X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7547d64ec0fce0faac0488765316f235b4b760a5..820b49cdbf6fa6e6aac6bbb66e84ea9f5f5f745d:/samples/richedit/wxlwindow.cpp diff --git a/samples/richedit/wxlwindow.cpp b/samples/richedit/wxlwindow.cpp index 98239d5184..b8158f2e5b 100644 --- a/samples/richedit/wxlwindow.cpp +++ b/samples/richedit/wxlwindow.cpp @@ -1,14 +1,14 @@ /*-*- c++ -*-******************************************************** * wxLwindow.h : a scrolled Window for displaying/entering rich text* * * - * (C) 1998, 1999 by Karsten Ballüder (Ballueder@usa.net) * + * (C) 1998-2000 by Karsten Ballüder (ballueder@gmx.net) * * * * $Id$ *******************************************************************/ -// =========================================================================== +// ============================================================================ // declarations -// =========================================================================== +// ============================================================================ // ---------------------------------------------------------------------------- // headers @@ -34,6 +34,9 @@ # endif // USE_PCH # include "gui/wxlwindow.h" # include "gui/wxlparser.h" + +# include "MDialogs.h" +# include "strutil.h" #else # ifdef __WXMSW__ # include @@ -53,16 +56,24 @@ #include + // ---------------------------------------------------------------------------- // macros // ---------------------------------------------------------------------------- -#ifdef WXLAYOUT_DEBUG +#ifdef DEBUG # define WXLO_DEBUG(x) wxLogDebug x #else # define WXLO_DEBUG(x) #endif +// for profiling in debug mode: +WXLO_TIMER_DEFINE(UpdateTimer); +WXLO_TIMER_DEFINE(BlitTimer); +WXLO_TIMER_DEFINE(LayoutTimer); +WXLO_TIMER_DEFINE(TmpTimer); +WXLO_TIMER_DEFINE(DrawTimer); + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -75,23 +86,33 @@ #define WXLO_ROFFSET 20 #define WXLO_BOFFSET 20 +/// scroll margins when selecting with the mouse +#define WXLO_SCROLLMARGIN_X 10 +#define WXLO_SCROLLMARGIN_Y 10 + /// 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_SIZE (wxLayoutWindow::OnSize) + EVT_PAINT (wxLayoutWindow::OnPaint) EVT_CHAR (wxLayoutWindow::OnChar) EVT_KEY_UP (wxLayoutWindow::OnKeyUp) - EVT_LEFT_DOWN(wxLayoutWindow::OnLeftMouseClick) + EVT_LEFT_DOWN(wxLayoutWindow::OnLeftMouseDown) + EVT_LEFT_UP(wxLayoutWindow::OnLeftMouseUp) EVT_RIGHT_DOWN(wxLayoutWindow::OnRightMouseClick) EVT_LEFT_DCLICK(wxLayoutWindow::OnMouseDblClick) + EVT_MIDDLE_DOWN(wxLayoutWindow::OnMiddleMouseDown) EVT_MOTION (wxLayoutWindow::OnMouseMove) EVT_UPDATE_UI(WXLOWIN_MENU_UNDERLINE, wxLayoutWindow::OnUpdateMenuUnderline) @@ -101,23 +122,23 @@ BEGIN_EVENT_TABLE(wxLayoutWindow,wxScrolledWindow) EVT_SET_FOCUS(wxLayoutWindow::OnSetFocus) EVT_KILL_FOCUS(wxLayoutWindow::OnKillFocus) + +// EVT_IDLE(wxLayoutWindow::ResizeScrollbars) END_EVENT_TABLE() -// =========================================================================== -// implementation -// =========================================================================== +// ---------------------------------------------------------------------------- +// function prototypes +// ---------------------------------------------------------------------------- -/* LEAVE IT HERE UNTIL WXGTK WORKS AGAIN!!! */ -#ifdef __WXGTK__ -/// allows me to compare to wxPoints -static bool operator != (wxPoint const &p1, wxPoint const &p2) -{ - return p1.x != p2.x || p1.y != p2.y; -} -#endif // __WXGTK__ +/// returns TRUE if keyCode is one of arrows/home/end/page{up|down} keys +static bool IsDirectionKey(long keyCode); + +// ============================================================================ +// implementation +// ============================================================================ #ifndef wxWANTS_CHARS - #define wxWANTS_CHARS 0 +# define wxWANTS_CHARS 0 #endif // ---------------------------------------------------------------------------- @@ -129,7 +150,8 @@ wxLayoutWindow::wxLayoutWindow(wxWindow *parent) wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL | wxBORDER | - wxWANTS_CHARS) + wxWANTS_CHARS), + m_llist(NULL) { SetStatusBar(NULL); // don't use statusbar m_Editable = false; @@ -141,16 +163,18 @@ wxLayoutWindow::wxLayoutWindow(wxWindow *parent) m_bitmap = new wxBitmap(4,4); m_bitmapSize = wxPoint(4,4); m_llist = new wxLayoutList(); - m_BGbitmap = NULL; m_ScrollToCursor = false; +#ifndef __WXMSW__ + m_FocusFollowMode = false; +#endif + SetWordWrap(false); SetWrapMargin(0); - wxPoint max = m_llist->GetSize(); - 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; + + // no scrollbars initially + m_hasHScrollbar = + m_hasVScrollbar = false; + m_Selecting = false; #ifdef WXLAYOUT_USE_CARET @@ -158,13 +182,16 @@ wxLayoutWindow::wxLayoutWindow(wxWindow *parent) wxCaret *caret = new wxCaret(this, 2, 20); SetCaret(caret); m_llist->SetCaret(caret); - caret->Show(); #endif // WXLAYOUT_USE_CARET + m_HaveFocus = FALSE; m_HandCursor = FALSE; m_CursorVisibility = -1; SetCursor(wxCURSOR_IBEAM); SetDirty(); + + // at least under Windows, this should be the default behaviour + m_AutoDeleteSelection = TRUE; } wxLayoutWindow::~wxLayoutWindow() @@ -187,11 +214,24 @@ wxLayoutWindow::Clear(int family, { GetLayoutList()->Clear(family,size,style,weight,underline,fg,bg); SetBackgroundColour(GetLayoutList()->GetDefaultStyleInfo().GetBGColour()); + wxScrolledWindow::Clear(); ResizeScrollbars(true); SetDirty(); - SetModified(false); - wxScrolledWindow::Clear(); - DoPaint((wxRect *)NULL); + SetModified(FALSE); + if ( m_Editable ) + m_CursorVisibility = 1; + +#ifdef WXLAYOUT_USE_CARET + if ( m_CursorVisibility == 1 ) + GetCaret()->Show(); +#endif // WXLAYOUT_USE_CARET + + RequestUpdate((wxRect *)NULL); +} + +void wxLayoutWindow::Refresh(bool eraseBackground, const wxRect *rect) +{ + wxScrolledWindow::Refresh(eraseBackground, rect); } void @@ -199,11 +239,13 @@ wxLayoutWindow::OnMouse(int eventId, wxMouseEvent& event) { wxClientDC dc( this ); PrepareDC( dc ); - if ( eventId != WXLOWIN_MENU_MOUSEMOVE ) - { - // moving the mouse in a window shouldn't give it the focus! + if ( (eventId != WXLOWIN_MENU_MOUSEMOVE +#ifndef __WXMSW__ + || m_FocusFollowMode +#endif + ) && (wxWindow::FindFocus() != this) + ) SetFocus(); - } wxPoint findPos; findPos.x = dc.DeviceToLogicalX(event.GetX()); @@ -212,20 +254,60 @@ wxLayoutWindow::OnMouse(int eventId, wxMouseEvent& event) findPos.x -= WXLO_XOFFSET; findPos.y -= WXLO_YOFFSET; - if(findPos.x < 0) findPos.x = 0; - if(findPos.y < 0) findPos.y = 0; + if(findPos.x < 0) + findPos.x = 0; + if(findPos.y < 0) + findPos.y = 0; m_ClickPosition = wxPoint(event.GetX(), event.GetY()); + // Scroll the window if the mouse is at the end of it: + if(m_Selecting && eventId == WXLOWIN_MENU_MOUSEMOVE) + { + //WXLO_DEBUG(("selecting at : %d/%d", (int) event.GetX(), (int)event.GetY())); + int left, top; + GetViewStart(&left, &top); + wxSize size = GetClientSize(); + int xdelta, ydelta; + + if(event.GetX() < WXLO_SCROLLMARGIN_X) + xdelta = -(WXLO_SCROLLMARGIN_X-event.GetX()); + else if(event.GetX() > size.x-WXLO_SCROLLMARGIN_X) + xdelta = event.GetX()-size.x+WXLO_SCROLLMARGIN_X; + else + xdelta = 0; + if(event.GetY() < WXLO_SCROLLMARGIN_Y) + ydelta = -(WXLO_SCROLLMARGIN_Y-event.GetY()); + else if(event.GetY() > size.y-WXLO_SCROLLMARGIN_Y) + ydelta = event.GetY()-size.y+WXLO_SCROLLMARGIN_Y; + else + ydelta = 0; + + //WXLO_DEBUG(("xdelta: %d", (int) xdelta)); + if(xdelta != 0 || ydelta != 0) + { + top += ydelta; if(top < 0) top = 0; + left += xdelta; if(left < 0) left = 0; + Scroll(left, top); + } + } + wxPoint cursorPos; bool found; wxLayoutObject *obj = m_llist->FindObjectScreen(dc, findPos, &cursorPos, &found); wxLayoutObject::UserData *u = obj ? obj->GetUserData() : NULL; - //has the mouse only been moved? - if(eventId == WXLOWIN_MENU_MOUSEMOVE) + // has the mouse only been moved? + switch ( eventId ) + { + case WXLOWIN_MENU_MOUSEMOVE: { + // this variables is used to only erase the message in the status + // bar if we had put it there previously - otherwise empting status + // bar might be undesirable + static bool s_hasPutMessageInStatusBar = false; + // found is only true if we are really over an object, not just // behind it if(found && u && ! m_Selecting) @@ -237,8 +319,11 @@ wxLayoutWindow::OnMouse(int eventId, wxMouseEvent& event) { const wxString &label = u->GetLabel(); if(label.Length()) + { m_StatusBar->SetStatusText(label, m_StatusFieldLabel); + s_hasPutMessageInStatusBar = true; + } } } else @@ -246,37 +331,34 @@ wxLayoutWindow::OnMouse(int eventId, wxMouseEvent& event) if(m_HandCursor) SetCursor(wxCURSOR_IBEAM); m_HandCursor = FALSE; - if(m_StatusBar && m_StatusFieldLabel != -1) - m_StatusBar->SetStatusText("", m_StatusFieldLabel); - } - if(event.LeftIsDown()) - { - if(! m_Selecting) + if( m_StatusBar && m_StatusFieldLabel != -1 && + s_hasPutMessageInStatusBar ) { - m_llist->StartSelection(wxPoint(-1, -1), m_ClickPosition); - m_Selecting = true; - DoPaint(); // TODO: we don't have to redraw everything! - } - else - { - m_llist->ContinueSelection(cursorPos, m_ClickPosition); - DoPaint(); // TODO: we don't have to redraw everything! + m_StatusBar->SetStatusText("", m_StatusFieldLabel); } } - if(m_Selecting && ! event.LeftIsDown()) - { - m_llist->EndSelection(cursorPos, m_ClickPosition); - m_Selecting = false; - DoPaint(); // TODO: we don't have to redraw everything! - } + } - if ( u ) + // selecting? + if ( event.LeftIsDown() ) + { + // m_Selecting might not be set if the button got pressed + // outside this window, so check for it: + if( m_Selecting ) { - u->DecRef(); - u = NULL; + m_llist->ContinueSelection(cursorPos, m_ClickPosition); + RequestUpdate(); // TODO: we don't have to redraw everything! } } - else if(eventId == WXLOWIN_MENU_LCLICK) + + if ( u ) + { + u->DecRef(); + u = NULL; + } + break; + + case WXLOWIN_MENU_LDOWN: { // always move cursor to mouse click: m_llist->MoveCursorTo(cursorPos); @@ -285,12 +367,13 @@ wxLayoutWindow::OnMouse(int eventId, wxMouseEvent& event) if ( m_llist->HasSelection() ) { m_llist->DiscardSelection(); - DoPaint(); // TODO: we don't have to redraw everything! + m_Selecting = false; + RequestUpdate(); // TODO: we don't have to redraw everything! } - + // Calculate where the top of the visible area is: int x0, y0; - ViewStart(&x0,&y0); + GetViewStart(&x0,&y0); int dx, dy; GetScrollPixelsPerUnit(&dx, &dy); x0 *= dx; y0 *= dy; @@ -299,22 +382,53 @@ wxLayoutWindow::OnMouse(int eventId, wxMouseEvent& event) if(m_CursorVisibility == -1) m_CursorVisibility = 1; +#ifdef WXLAYOUT_USE_CARET + if ( m_CursorVisibility == 1 ) + GetCaret()->Show(); +#endif // WXLAYOUT_USE_CARET - if(m_CursorVisibility != 0) + if(m_CursorVisibility) { - // draw a thick cursor for editable windows with focus + // draw a thick cursor for editable windows with focus m_llist->DrawCursor(dc, m_HaveFocus && IsEditable(), offset); } - // 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(); // DoPaint suppresses flicker under GTK + RequestUpdate(); // RequestUpdate suppresses flicker under GTK #endif // wxGTK + + // start selection + m_llist->StartSelection(wxPoint(-1, -1), m_ClickPosition); + m_Selecting = true; + } + break; + + case WXLOWIN_MENU_LUP: + if ( m_Selecting ) + { + // end selection at the cursor position corresponding to the + // current mouse position, but don´t move cursor there. + m_llist->EndSelection(cursorPos,m_ClickPosition); + m_Selecting = false; + + RequestUpdate(); // TODO: we don't have to redraw everything! + } + break; + + case WXLOWIN_MENU_MDOWN: + Paste(TRUE); + break; + + case WXLOWIN_MENU_DBLCLICK: + // select a word under cursor + m_llist->MoveCursorTo(cursorPos); + m_llist->MoveCursorWord(-1); + m_llist->StartSelection(); + m_llist->MoveCursorWord(1, false); + m_llist->EndSelection(); + m_Selecting = false; + RequestUpdate(); // TODO: we don't have to redraw everything! + break; } // notify about mouse events? @@ -340,17 +454,18 @@ wxLayoutWindow::OnMouse(int eventId, wxMouseEvent& event) } } - if( u ) - u->DecRef(); + if( u ) u->DecRef(); } -/* - * Some simple keyboard handling. - */ +// ---------------------------------------------------------------------------- +// keyboard handling. +// ---------------------------------------------------------------------------- + void wxLayoutWindow::OnChar(wxKeyEvent& event) { int keyCode = event.KeyCode(); + bool ctrlDown = event.ControlDown(); #ifdef WXLAYOUT_DEBUG if(keyCode == WXK_F1) @@ -360,26 +475,50 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) } #endif - if(! m_Selecting && event.ShiftDown()) + // Force m_Selecting to be false if shift is no longer + // pressed. OnKeyUp() cannot catch all Shift-Up events. + if(m_Selecting && !event.ShiftDown()) + { + m_Selecting = false; + m_llist->EndSelection(); + m_llist->DiscardSelection(); //FIXME: correct? + } + + // If we deleted the selection here, we must not execute the + // deletion in Delete/Backspace handling. + bool deletedSelection = false; + // pressing any non-arrow key optionally replaces the selection: + if(m_AutoDeleteSelection + && IsEditable() + && !m_Selecting + && m_llist->HasSelection() + && ! IsDirectionKey(keyCode) + && ! (event.AltDown() || ctrlDown) + ) { - switch(keyCode) + m_llist->DeleteSelection(); + deletedSelection = true; + SetDirty(); + } + + // + starts selection + if ( IsDirectionKey(keyCode) ) + { + // just continue the old selection + if ( m_Selecting && event.ShiftDown() ) + m_llist->ContinueSelection(); + else { - case WXK_UP: - case WXK_DOWN: - case WXK_RIGHT: - case WXK_LEFT: - case WXK_PRIOR: - case WXK_NEXT: - case WXK_HOME: - case WXK_END: - m_Selecting = true; - m_llist->StartSelection(); - break; - default: - ; + m_llist->DiscardSelection(); + m_Selecting = false; + if( event.ShiftDown() ) + { + m_Selecting = true; + m_llist->StartSelection(); + } } } - + // If needed, make cursor visible: if(m_CursorVisibility == -1) m_CursorVisibility = 1; @@ -389,7 +528,6 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) cursor, etc. It's default will process all keycodes causing modifications to the buffer, but only if editing is allowed. */ - bool ctrlDown = event.ControlDown(); switch(keyCode) { case WXK_RIGHT: @@ -424,56 +562,109 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) break; case WXK_END: if ( ctrlDown ) - m_llist->MoveCursorTo(m_llist->GetSize()); + m_llist->MoveCursorToEnd(); else m_llist->MoveCursorToEndOfLine(); break; - default: - if(keyCode == 'c' && ctrlDown) - { - // this should work even in read-only mode - Copy(); - } + + if(ctrlDown && ! IsEditable()) + switch(keyCode) + { + case 'c': + // this should work even in read-only mode + Copy(TRUE, TRUE); + break; + case 's': // search + Find(""); + break; + case 't': // search again + FindAgain(); + break; + default: + // we don't handle it, maybe an accelerator? + event.Skip(); + ; + } else if( IsEditable() ) { /* First, handle control keys */ - if(event.ControlDown() && ! event.AltDown()) + if(ctrlDown && ! event.AltDown()) { + if(keyCode >= 'A' && keyCode <= 'Z') + keyCode = tolower(keyCode); switch(keyCode) { case WXK_INSERT: Copy(); break; case WXK_DELETE : + if(! deletedSelection) + { + m_llist->DeleteWord(); + SetDirty(); + } + break; case 'd': - m_llist->Delete(1); + if(! deletedSelection) // already done + { + m_llist->Delete(1); + SetDirty(); + } break; case 'y': m_llist->DeleteLines(1); + SetDirty(); break; case 'h': // like backspace - if(m_llist->MoveCursorHorizontally(-1)) m_llist->Delete(1); + if(m_llist->MoveCursorHorizontally(-1)) + { + m_llist->Delete(1); + SetDirty(); + } + break; + case 's': // search + Find(""); + break; + case 't': // search again + FindAgain(); break; case 'u': m_llist->DeleteToBeginOfLine(); + SetDirty(); break; case 'k': m_llist->DeleteToEndOfLine(); + SetDirty(); + break; + case 'c': + Copy(TRUE, TRUE); break; case 'v': - Paste(); + Paste( TRUE ); break; case 'x': Cut(); break; + case 'w': + if(m_WrapMargin > 0) + m_llist->WrapLine(m_WrapMargin); + break; + case 'q': + if(m_WrapMargin > 0) + m_llist->WrapAll(m_WrapMargin); + break; #ifdef WXLAYOUT_DEBUG case WXK_F1: m_llist->SetFont(-1,-1,-1,-1,true); // underlined break; + case 'l': + Refresh(TRUE); + break; #endif default: - ; + // we don't handle it, maybe an accelerator? + event.Skip(); } } // ALT only: @@ -484,9 +675,11 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) case WXK_DELETE: case 'd': m_llist->DeleteWord(); + SetDirty(); break; default: - ; + // we don't handle it, maybe an accelerator? + event.Skip(); } } // no control keys: @@ -502,60 +695,87 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) if(event.ShiftDown()) Cut(); else - m_llist->Delete(1); + if(! deletedSelection) + { + m_llist->Delete(1); + SetDirty(); + } break; case WXK_BACK: // backspace - if(m_llist->MoveCursorHorizontally(-1)) m_llist->Delete(1); + if(! deletedSelection) + if(m_llist->MoveCursorHorizontally(-1)) + { + m_llist->Delete(1); + SetDirty(); + } break; case WXK_RETURN: - if(m_WrapMargin > 0) + if(m_DoWordWrap && + m_WrapMargin > 0 + && m_llist->GetCursorPos().x > m_WrapMargin) m_llist->WrapLine(m_WrapMargin); m_llist->LineBreak(); + SetDirty(); break; + + case WXK_TAB: + if ( !event.ShiftDown() ) + { + // TODO should be configurable + static const int tabSize = 8; + + CoordType x = m_llist->GetCursorPos().x; + size_t numSpaces = tabSize - x % tabSize; + m_llist->Insert(wxString(' ', numSpaces)); + SetDirty(); + } + break; + default: - if((!(event.ControlDown() || event.AltDown() || event.MetaDown())) + if((!(event.ControlDown() || event.AltDown() + )) && (keyCode < 256 && keyCode >= 32) ) { - if(m_WrapMargin > 0 && isspace(keyCode)) + if(m_DoWordWrap + && m_WrapMargin > 0 + && m_llist->GetCursorPos().x > m_WrapMargin + && isspace(keyCode)) m_llist->WrapLine(m_WrapMargin); m_llist->Insert((char)keyCode); + SetDirty(); } + else + // we don't handle it, maybe an accelerator? + event.Skip(); break; } } - SetDirty(); - SetModified(); }// if(IsEditable()) + else + // we don't handle it, maybe an accelerator? + event.Skip(); }// first switch() - if(m_Selecting) + + if ( m_Selecting ) { - if(event.ShiftDown()) - m_llist->ContinueSelection(); - else - { - m_llist->EndSelection(); - m_Selecting = false; - } + // continue selection to the current (new) cursor position + m_llist->ContinueSelection(); } - - // we must call ResizeScrollbars() before ScrollToCursor(), otherwise the - // ne cursor position might be outside the current scrolllbar range - ResizeScrollbars(); ScrollToCursor(); - // refresh the screen - DoPaint(m_llist->GetUpdateRect()); + RequestUpdate(m_llist->GetUpdateRect()); } void wxLayoutWindow::OnKeyUp(wxKeyEvent& event) { - if(event.KeyCode() == WXK_SHIFT && m_llist->IsSelecting()) + if ( event.KeyCode() == WXK_SHIFT && m_Selecting ) { m_llist->EndSelection(); m_Selecting = false; } + event.Skip(); } @@ -563,37 +783,29 @@ wxLayoutWindow::OnKeyUp(wxKeyEvent& event) void wxLayoutWindow::ScrollToCursor(void) { - wxClientDC dc( this ); - PrepareDC( dc ); + //is always needed to make sure we know where the cursor is + //if(IsDirty()) + //RequestUpdate(m_llist->GetUpdateRect()); + + + ResizeScrollbars(); int x0,y0,x1,y1, dx, dy; // Calculate where the top of the visible area is: - ViewStart(&x0,&y0); + GetViewStart(&x0,&y0); GetScrollPixelsPerUnit(&dx, &dy); x0 *= dx; y0 *= dy; - WXLO_DEBUG(("ScrollToCursor: ViewStart is %d/%d", x0, y0)); + WXLO_DEBUG(("ScrollToCursor: GetViewStart is %d/%d", x0, y0)); // Get the size of the visible window: - GetClientSize(&x1,&y1); - - // 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; -#endif // 0 + GetClientSize(&x1, &y1); // 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(dc); + wxPoint cc = m_llist->GetCursorScreenPos(); // the cursor should be completely visible in both directions wxPoint cs(m_llist->GetCursorSize()); @@ -613,13 +825,13 @@ wxLayoutWindow::ScrollToCursor(void) ny = 0; } - if ( nx != -1 || ny != -1 ) + 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; + RequestUpdate(); } } @@ -631,21 +843,23 @@ wxLayoutWindow::OnPaint( wxPaintEvent &WXUNUSED(event)) } void -wxLayoutWindow::DoPaint(const wxRect *updateRect) +wxLayoutWindow::RequestUpdate(const wxRect *updateRect) { #ifdef __WXGTK__ + // Calling Refresh() causes bad flicker under wxGTK!!! InternalPaint(updateRect); -#else // Causes bad flicker under wxGTK!!! +#else + // shouldn't specify the update rectangle if it doesn't include all the + // changed locations - otherwise, they won't be repainted at all because + // the system clips the display to the update rect Refresh(FALSE); //, updateRect); - - if ( !::UpdateWindow(GetHwnd()) ) - wxLogLastError("UpdateWindow"); #endif } void wxLayoutWindow::InternalPaint(const wxRect *updateRect) { + wxPaintDC dc( this ); PrepareDC( dc ); @@ -657,7 +871,7 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) int x0,y0,x1,y1, dx, dy; // Calculate where the top of the visible area is: - ViewStart(&x0,&y0); + GetViewStart(&x0,&y0); GetScrollPixelsPerUnit(&dx, &dy); x0 *= dx; y0 *= dy; @@ -673,11 +887,10 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) updateRect->x+updateRect->width, updateRect->y+updateRect->height)); } - if(IsDirty()) - { - m_llist->Layout(dc); - ResizeScrollbars(); - } + + ResizeScrollbars(true); + + WXLO_TIMER_START(TmpTimer); /* Check whether the window has grown, if so, we need to reallocate the bitmap to be larger. */ if(x1 > m_bitmapSize.x || y1 > m_bitmapSize.y) @@ -693,12 +906,13 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) } m_memDC->SetDeviceOrigin(0,0); - m_memDC->SetBrush(wxBrush(m_llist->GetDefaultStyleInfo().GetBGColour(),wxSOLID)); + m_memDC->SetBackground(wxBrush(m_llist->GetDefaultStyleInfo().GetBGColour(),wxSOLID)); m_memDC->SetPen(wxPen(m_llist->GetDefaultStyleInfo().GetBGColour(), 0,wxTRANSPARENT)); m_memDC->SetLogicalFunction(wxCOPY); m_memDC->Clear(); - + WXLO_TIMER_STOP(TmpTimer); + // fill the background with the background bitmap if(m_BGbitmap) { @@ -733,7 +947,7 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) // update rectangle (although they are drawn on the memDC, this is // needed to erase it): m_llist->InvalidateUpdateRect(); - if(m_CursorVisibility != 0) + if(m_CursorVisibility == 1) { // draw a thick cursor for editable windows with focus m_llist->DrawCursor(*m_memDC, @@ -741,6 +955,7 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) offset); } + WXLO_TIMER_START(BlitTimer); // Now copy everything to the screen: #if 0 // This somehow doesn't work, but even the following bit with the @@ -766,14 +981,15 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) // y1 += WXLO_YOFFSET; //FIXME might not be needed dc.Blit(x0,y0,x1,y1,m_memDC,0,0,wxCOPY,FALSE); } + WXLO_TIMER_STOP(BlitTimer); + #ifdef WXLAYOUT_USE_CARET // show the caret back after everything is redrawn - m_caret->Show(); + GetCaret()->Show(); #endif // WXLAYOUT_USE_CARET ResetDirty(); - m_ScrollToCursor = false; if ( m_StatusBar && m_StatusFieldCursor != -1 ) { @@ -791,19 +1007,67 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) m_StatusBar->SetStatusText(label, m_StatusFieldCursor); } } + + WXLO_TIMER_PRINT(LayoutTimer); + WXLO_TIMER_PRINT(BlitTimer); + WXLO_TIMER_PRINT(TmpTimer); +} + +void +wxLayoutWindow::OnSize(wxSizeEvent &event) +{ + if ( m_llist ) + ResizeScrollbars(); + + event.Skip(); } -// change the range and position of scrollbars +/* +Change the range and position of scrollbars. Has evolved into a +generic Update function which will at some time later cause a repaint +as needed. +*/ + void wxLayoutWindow::ResizeScrollbars(bool exact) { + wxClientDC dc( this ); + PrepareDC( dc ); +// m_llist->ForceTotalLayout(); + + if(! IsDirty()) + { + // we are laying out just the minimum, but always up to the + // cursor line, so the cursor position is updated. + m_llist->Layout(dc, 0); + return; + } + WXLO_TIMER_START(LayoutTimer); + m_llist->Layout(dc, -1); + WXLO_TIMER_STOP(LayoutTimer); + ResetDirty(); + wxPoint max = m_llist->GetSize(); + wxSize size = GetClientSize(); 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 ) + // in the absence of scrollbars we should compare with the client size + if ( !m_hasHScrollbar ) + m_maxx = size.x;// - WXLO_ROFFSET; + if ( !m_hasVScrollbar ) + m_maxy = size.y;// - WXLO_BOFFSET; + + // check if the text hasn't become too big + // TODO why do we set both at once? they're independent... + if( max.x > m_maxx - WXLO_ROFFSET + || max.y > m_maxy - WXLO_BOFFSET + || (max.x < m_maxx - X_SCROLL_PAGE) + || (max.y < m_maxy - Y_SCROLL_PAGE) + || exact ) { + // text became too large if ( !exact ) { // add an extra bit to the sizes to avoid future updates @@ -811,60 +1075,107 @@ wxLayoutWindow::ResizeScrollbars(bool exact) max.y += WXLO_BOFFSET; } - ViewStart(&m_ViewStartX, &m_ViewStartY); - SetScrollbars(X_SCROLL_PAGE, Y_SCROLL_PAGE, - max.x / X_SCROLL_PAGE + 1, max.y / Y_SCROLL_PAGE + 1, - m_ViewStartX, m_ViewStartY, - true); - + bool done = FALSE; + if(max.x < X_SCROLL_PAGE && m_hasHScrollbar) + { + SetScrollbars(0,-1,0,-1,0,-1,true); + m_hasHScrollbar = FALSE; + done = TRUE; + } + if(max.y < Y_SCROLL_PAGE && m_hasVScrollbar) + { + SetScrollbars(-1,0,-1,0,-1,0,true); + m_hasVScrollbar = FALSE; + done = TRUE; + } + if(! done && +// (max.x > X_SCROLL_PAGE || max.y > Y_SCROLL_PAGE) + (max.x > size.x - X_SCROLL_PAGE|| max.y > size.y - Y_SCROLL_PAGE) + ) + { + GetViewStart(&m_ViewStartX, &m_ViewStartY); + SetScrollbars(X_SCROLL_PAGE, + Y_SCROLL_PAGE, + max.x / X_SCROLL_PAGE + 2, + max.y / Y_SCROLL_PAGE + 2, + m_ViewStartX, + m_ViewStartY, + true); + m_hasHScrollbar = + m_hasVScrollbar = true; +// ScrollToCursor(); + } + m_maxx = max.x + X_SCROLL_PAGE; m_maxy = max.y + Y_SCROLL_PAGE; } } +// ---------------------------------------------------------------------------- +// +// clipboard operations +// +// ---------------------------------------------------------------------------- + void -wxLayoutWindow::Paste(void) +wxLayoutWindow::Paste(bool usePrivate, bool primary) { + // this only has an effect under X11: + wxTheClipboard->UsePrimarySelection(primary); // Read some text if (wxTheClipboard->Open()) { -#if wxUSE_PRIVATE_CLIPBOARD_FORMAT - wxLayoutDataObject wxldo; - if (wxTheClipboard->IsSupported( wxldo.GetFormat() )) + if(usePrivate) { - wxTheClipboard->GetData(&wxldo); + wxLayoutDataObject wxldo; + if (wxTheClipboard->IsSupported( wxldo.GetFormat() )) { + if(wxTheClipboard->GetData(wxldo)) + { + wxTheClipboard->Close(); + wxString str = wxldo.GetLayoutData(); + m_llist->Read(str); + SetDirty(); + RequestUpdate(); + return; + } } - //FIXME: missing functionality m_llist->Insert(wxldo.GetList()); } - else -#endif + wxTextDataObject data; + if (wxTheClipboard->IsSupported( data.GetFormat() ) + && wxTheClipboard->GetData(data) ) + { + wxTheClipboard->Close(); + wxString text = data.GetText(); + wxLayoutImportText( m_llist, text); + SetDirty(); + RequestUpdate(); + return; + } + } + // if everything failed we can still try the primary: + wxTheClipboard->Close(); + if(! primary) // not tried before + { + wxTheClipboard->UsePrimarySelection(); + if (wxTheClipboard->Open()) { wxTextDataObject data; - if (wxTheClipboard->IsSupported( data.GetFormat() )) + if (wxTheClipboard->IsSupported( data.GetFormat() ) + && wxTheClipboard->GetData(data) ) { - wxTheClipboard->GetData(&data); wxString text = data.GetText(); wxLayoutImportText( m_llist, text); + SetDirty(); + RequestUpdate(); } + wxTheClipboard->Close(); } - wxTheClipboard->Close(); - } - -#if 0 - /* My attempt to get the primary selection, but it does not - work. :-( */ - if(text.Length() == 0) - { - wxTextCtrl tmp_tctrl(this,-1); - tmp_tctrl.Paste(); - text += tmp_tctrl.GetValue(); } -#endif } bool -wxLayoutWindow::Copy(bool invalidate) +wxLayoutWindow::Copy(bool invalidate, bool privateFormat, bool primary) { // Calling GetSelection() will automatically do an EndSelection() // on the list, but we need to take a note of it, too: @@ -874,19 +1185,19 @@ wxLayoutWindow::Copy(bool invalidate) m_llist->EndSelection(); } - wxLayoutDataObject wldo; - wxLayoutList *llist = m_llist->GetSelection(&wldo, invalidate); + wxLayoutDataObject *wldo = new wxLayoutDataObject; + wxLayoutList *llist = m_llist->GetSelection(wldo, invalidate); if(! llist) return FALSE; // Export selection as text: wxString text; - wxLayoutExportObject *export; + wxLayoutExportObject *exp; wxLayoutExportStatus status(llist); - while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_TEXT)) != NULL) + while((exp = wxLayoutExport( &status, WXLO_EXPORT_AS_TEXT)) != NULL) { - if(export->type == WXLO_EXPORT_TEXT) - text << *(export->content.text); - delete export; + if(exp->type == WXLO_EXPORT_TEXT) + text << *(exp->content.text); + delete exp; } delete llist; @@ -900,42 +1211,79 @@ wxLayoutWindow::Copy(bool invalidate) text = text.Mid(0,len-1); } +#if 0 +if(! primary) // always copy as text-only to primary selection + { + wxTheClipboard->UsePrimarySelection(); + if (wxTheClipboard->Open()) + { + wxTextDataObject *data = new wxTextDataObject( text ); + wxTheClipboard->SetData( data ); + wxTheClipboard->Close(); + } + } +#endif + wxTheClipboard->UsePrimarySelection(primary); if (wxTheClipboard->Open()) { wxTextDataObject *data = new wxTextDataObject( text ); - bool rc = wxTheClipboard->SetData( data ); -#if wxUSE_PRIVATE_CLIPBOARD_FORMAT - rc |= wxTheClipboard->AddData( &wldo ); -#endif + bool rc; + + rc = wxTheClipboard->SetData( data ); + if(privateFormat) + rc |= wxTheClipboard->SetData( wldo ); wxTheClipboard->Close(); return rc; } - + else + delete wldo; + return FALSE; } bool -wxLayoutWindow::Cut(void) +wxLayoutWindow::Cut(bool privateFormat, bool usePrimary) { - if(Copy(false)) // do not invalidate selection after copy + if(Copy(false, privateFormat, usePrimary)) // do not invalidate selection after copy { m_llist->DeleteSelection(); + SetDirty(); return TRUE; } else return FALSE; } + +// ---------------------------------------------------------------------------- +// searching +// ---------------------------------------------------------------------------- + bool wxLayoutWindow::Find(const wxString &needle, - wxPoint * fromWhere) + wxPoint * fromWhere, + const wxString &configPath) { +#ifdef M_BASEDIR wxPoint found; - + + if(needle.Length() == 0) + { + if( ! MInputBox(&m_FindString, + _("Find text"), + _(" Find:"), + this, + configPath, "") + || strutil_isempty(m_FindString)) + return true; + } + else + m_FindString = needle; + if(fromWhere == NULL) - found = m_llist->FindText(needle, m_llist->GetCursorPos()); + found = m_llist->FindText(m_FindString, m_llist->GetCursorPos()); else - found = m_llist->FindText(needle, *fromWhere); + found = m_llist->FindText(m_FindString, *fromWhere); if(found.x != -1) { if(fromWhere) @@ -945,11 +1293,25 @@ wxLayoutWindow::Find(const wxString &needle, } m_llist->MoveCursorTo(found); ScrollToCursor(); + RequestUpdate(); return true; } +#endif return false; } + +bool +wxLayoutWindow::FindAgain(void) +{ + bool rc = Find(m_FindString); + return rc; +} + +// ---------------------------------------------------------------------------- +// popup menu stuff +// ---------------------------------------------------------------------------- + wxMenu * wxLayoutWindow::MakeFormatMenu() { @@ -989,36 +1351,34 @@ void wxLayoutWindow::OnMenu(wxCommandEvent& event) switch (event.GetId()) { case WXLOWIN_MENU_LARGER: - m_llist->SetFontLarger(); - break; + m_llist->SetFontLarger(); RequestUpdate(); break; case WXLOWIN_MENU_SMALLER: - m_llist->SetFontSmaller(); - break; - + m_llist->SetFontSmaller(); RequestUpdate(); break; case WXLOWIN_MENU_UNDERLINE: - m_llist->ToggleFontUnderline(); - break; + m_llist->ToggleFontUnderline(); RequestUpdate(); break; case WXLOWIN_MENU_BOLD: - m_llist->ToggleFontWeight(); - break; + m_llist->ToggleFontWeight(); RequestUpdate(); break; case WXLOWIN_MENU_ITALICS: - m_llist->ToggleFontItalics(); - break; - + m_llist->ToggleFontItalics(); RequestUpdate(); break; case WXLOWIN_MENU_ROMAN: - m_llist->SetFontFamily(wxROMAN); break; + m_llist->SetFontFamily(wxROMAN); RequestUpdate(); break; case WXLOWIN_MENU_TYPEWRITER: - m_llist->SetFontFamily(wxFIXED); break; + m_llist->SetFontFamily(wxFIXED); RequestUpdate(); break; case WXLOWIN_MENU_SANSSERIF: - m_llist->SetFontFamily(wxSWISS); break; + m_llist->SetFontFamily(wxSWISS); RequestUpdate(); break; } } +// ---------------------------------------------------------------------------- +// focus +// ---------------------------------------------------------------------------- + void wxLayoutWindow::OnSetFocus(wxFocusEvent &ev) { m_HaveFocus = true; ev.Skip(); + RequestUpdate(); // cursor must change } void @@ -1026,4 +1386,28 @@ wxLayoutWindow::OnKillFocus(wxFocusEvent &ev) { m_HaveFocus = false; ev.Skip(); + RequestUpdate();// cursor must change +} + +// ---------------------------------------------------------------------------- +// private functions +// ---------------------------------------------------------------------------- + +static bool IsDirectionKey(long keyCode) +{ + switch(keyCode) + { + case WXK_UP: + case WXK_DOWN: + case WXK_RIGHT: + case WXK_LEFT: + case WXK_PRIOR: + case WXK_NEXT: + case WXK_HOME: + case WXK_END: + return true; + + default: + return false; + } }