1 /*-*- c++ -*-******************************************************** 
   2  * wxLwindow.h : a scrolled Window for displaying/entering rich text* 
   4  * (C) 1998, 1999 by Karsten Ballüder (karsten@phy.hw.ac.uk)        * 
   7  *******************************************************************/ 
   9 // ============================================================================ 
  11 // ============================================================================ 
  13 // ---------------------------------------------------------------------------- 
  15 // ---------------------------------------------------------------------------- 
  18 #   pragma implementation "wxlwindow.h" 
  21 #include <wx/wxprec.h> 
  32 #     include "gui/wxMenuDefs.h" 
  33 #     include "gui/wxMApp.h" 
  35 #   include "gui/wxlwindow.h" 
  36 #   include "gui/wxlparser.h" 
  38 #   include "MDialogs.h" 
  42 #       include <wx/msw/private.h> 
  45 #   include "wxlwindow.h" 
  46 #   include "wxlparser.h" 
  49 #include <wx/clipbrd.h> 
  50 #include <wx/textctrl.h> 
  51 #include <wx/dataobj.h> 
  53 #ifdef WXLAYOUT_USE_CARET 
  54 #   include <wx/caret.h> 
  55 #endif // WXLAYOUT_USE_CARET 
  59 // ---------------------------------------------------------------------------- 
  61 // ---------------------------------------------------------------------------- 
  64 #  define   WXLO_DEBUG(x)      wxLogDebug x 
  66 #  define WXLO_DEBUG(x) 
  69 // ---------------------------------------------------------------------------- 
  71 // ---------------------------------------------------------------------------- 
  73 /// offsets to put a nice frame around text 
  74 #define WXLO_XOFFSET   4 
  75 #define WXLO_YOFFSET   4 
  77 /// offset to the right and bottom for when to redraw scrollbars 
  78 #define   WXLO_ROFFSET   20 
  79 #define   WXLO_BOFFSET   20 
  81 /// scroll margins when selecting with the mouse 
  82 #define WXLO_SCROLLMARGIN_X   10 
  83 #define WXLO_SCROLLMARGIN_Y   10 
  85 /// the size of one scrollbar page in pixels 
  86 static const int X_SCROLL_PAGE 
= 10; 
  87 static const int Y_SCROLL_PAGE 
= 20; 
  89 // ---------------------------------------------------------------------------- 
  91 // ---------------------------------------------------------------------------- 
  93 BEGIN_EVENT_TABLE(wxLayoutWindow
,wxScrolledWindow
) 
  94    EVT_SIZE    (wxLayoutWindow::OnSize
) 
  96    EVT_PAINT    (wxLayoutWindow::OnPaint
) 
  98    EVT_CHAR     (wxLayoutWindow::OnChar
) 
  99    EVT_KEY_UP   (wxLayoutWindow::OnKeyUp
) 
 101    EVT_LEFT_DOWN(wxLayoutWindow::OnLeftMouseDown
) 
 102    EVT_LEFT_UP(wxLayoutWindow::OnLeftMouseUp
) 
 103    EVT_RIGHT_DOWN(wxLayoutWindow::OnRightMouseClick
) 
 104    EVT_LEFT_DCLICK(wxLayoutWindow::OnMouseDblClick
) 
 105    EVT_MIDDLE_DOWN(wxLayoutWindow::OnMiddleMouseDown
) 
 106    EVT_MOTION    (wxLayoutWindow::OnMouseMove
) 
 108    EVT_UPDATE_UI(WXLOWIN_MENU_UNDERLINE
, wxLayoutWindow::OnUpdateMenuUnderline
) 
 109    EVT_UPDATE_UI(WXLOWIN_MENU_BOLD
, wxLayoutWindow::OnUpdateMenuBold
) 
 110    EVT_UPDATE_UI(WXLOWIN_MENU_ITALICS
, wxLayoutWindow::OnUpdateMenuItalic
) 
 111    EVT_MENU_RANGE(WXLOWIN_MENU_FIRST
, WXLOWIN_MENU_LAST
, wxLayoutWindow::OnMenu
) 
 113    EVT_SET_FOCUS(wxLayoutWindow::OnSetFocus
) 
 114    EVT_KILL_FOCUS(wxLayoutWindow::OnKillFocus
) 
 116 //   EVT_IDLE(wxLayoutWindow::ResizeScrollbars) 
 119 // ---------------------------------------------------------------------------- 
 120 // function prototypes 
 121 // ---------------------------------------------------------------------------- 
 123 /// returns TRUE if keyCode is one of arrows/home/end/page{up|down} keys 
 124 static bool IsDirectionKey(long keyCode
); 
 126 // ============================================================================ 
 128 // ============================================================================ 
 130 #ifndef wxWANTS_CHARS 
 131 #   define wxWANTS_CHARS 0 
 134 // ---------------------------------------------------------------------------- 
 136 // ---------------------------------------------------------------------------- 
 138 wxLayoutWindow::wxLayoutWindow(wxWindow 
*parent
) 
 139               : wxScrolledWindow(parent
, -1, 
 140                                  wxDefaultPosition
, wxDefaultSize
, 
 141                                  wxHSCROLL 
| wxVSCROLL 
| 
 146    SetStatusBar(NULL
); // don't use statusbar 
 148    m_doSendEvents 
= false; 
 149    m_ViewStartX 
= 0; m_ViewStartY 
= 0; 
 150    m_DoPopupMenu 
= true; 
 151    m_PopupMenu 
= MakeFormatMenu(); 
 152    m_memDC 
= new wxMemoryDC
; 
 153    m_bitmap 
= new wxBitmap(4,4); 
 154    m_bitmapSize 
= wxPoint(4,4); 
 155    m_llist 
= new wxLayoutList(); 
 157    m_ScrollToCursor 
= false; 
 159    m_FocusFollowMode 
= false; 
 163    // no scrollbars initially 
 165    m_hasVScrollbar 
= false; 
 169 #ifdef WXLAYOUT_USE_CARET 
 170    // FIXME cursor size shouldn't be hardcoded 
 171    wxCaret 
*caret 
= new wxCaret(this, 2, 20); 
 173    m_llist
->SetCaret(caret
); 
 174 #endif // WXLAYOUT_USE_CARET 
 177    m_HandCursor 
= FALSE
; 
 178    m_CursorVisibility 
= -1; 
 179    SetCursor(wxCURSOR_IBEAM
); 
 182    // at least under Windows, this should be the default behaviour 
 183    m_AutoDeleteSelection 
= TRUE
; 
 186 wxLayoutWindow::~wxLayoutWindow() 
 188    delete m_memDC
; // deletes bitmap automatically (?) 
 192    SetBackgroundBitmap(NULL
); 
 196 wxLayoutWindow::Clear(int family
, 
 204    GetLayoutList()->Clear(family
,size
,style
,weight
,underline
,fg
,bg
); 
 205    SetBackgroundColour(GetLayoutList()->GetDefaultStyleInfo().GetBGColour()); 
 206    wxScrolledWindow::Clear(); 
 207    ResizeScrollbars(true); 
 211       m_CursorVisibility 
= 1; 
 213 #ifdef WXLAYOUT_USE_CARET 
 214    if ( m_CursorVisibility 
== 1 ) 
 216 #endif // WXLAYOUT_USE_CARET 
 218    RequestUpdate((wxRect 
*)NULL
); 
 221 void wxLayoutWindow::Refresh(bool eraseBackground
, const wxRect 
*rect
) 
 223    wxScrolledWindow::Refresh(eraseBackground
, rect
); 
 227 wxLayoutWindow::OnMouse(int eventId
, wxMouseEvent
& event
) 
 229    wxClientDC 
dc( this ); 
 231    if ( eventId 
!= WXLOWIN_MENU_MOUSEMOVE
 
 239    findPos
.x 
= dc
.DeviceToLogicalX(event
.GetX()); 
 240    findPos
.y 
= dc
.DeviceToLogicalY(event
.GetY()); 
 242    findPos
.x 
-= WXLO_XOFFSET
; 
 243    findPos
.y 
-= WXLO_YOFFSET
; 
 250    m_ClickPosition 
= wxPoint(event
.GetX(), event
.GetY()); 
 252    // Scroll the window if the mouse is at the end of it: 
 253    if(m_Selecting 
&& eventId 
== WXLOWIN_MENU_MOUSEMOVE
) 
 255       //WXLO_DEBUG(("selecting at : %d/%d", (int) event.GetX(), (int)event.GetY())); 
 257       ViewStart(&left
, &top
); 
 258       wxSize size 
= GetClientSize(); 
 261       if(event
.GetX() < WXLO_SCROLLMARGIN_X
) 
 262          xdelta 
= -(WXLO_SCROLLMARGIN_X
-event
.GetX()); 
 263       else if(event
.GetX() > size
.x
-WXLO_SCROLLMARGIN_X
) 
 264          xdelta 
= event
.GetX()-size
.x
+WXLO_SCROLLMARGIN_X
; 
 267       if(event
.GetY() < WXLO_SCROLLMARGIN_Y
) 
 268          ydelta 
= -(WXLO_SCROLLMARGIN_Y
-event
.GetY()); 
 269       else if(event
.GetY() > size
.y
-WXLO_SCROLLMARGIN_Y
) 
 270          ydelta 
= event
.GetY()-size
.y
+WXLO_SCROLLMARGIN_Y
; 
 274       //WXLO_DEBUG(("xdelta: %d", (int) xdelta)); 
 275       if(xdelta 
!= 0 || ydelta 
!= 0) 
 277          top  
+= ydelta
; if(top 
< 0) top 
= 0; 
 278          left 
+= xdelta
; if(left 
< 0) left 
= 0; 
 285    wxLayoutObject 
*obj 
= m_llist
->FindObjectScreen(dc
, findPos
, 
 287    wxLayoutObject::UserData 
*u 
= obj 
? obj
->GetUserData() : NULL
; 
 289    // has the mouse only been moved? 
 292    case WXLOWIN_MENU_MOUSEMOVE
: 
 294       // this variables is used to only erase the message in the status 
 295       // bar if we had put it there previously - otherwise empting status 
 296       // bar might be undesirable 
 297       static bool s_hasPutMessageInStatusBar 
= false; 
 299       // found is only true if we are really over an object, not just 
 301       if(found 
&& u 
&& ! m_Selecting
) 
 304             SetCursor(wxCURSOR_HAND
); 
 306          if(m_StatusBar 
&& m_StatusFieldLabel 
!= -1) 
 308             const wxString 
&label 
= u
->GetLabel(); 
 311                m_StatusBar
->SetStatusText(label
, 
 313                s_hasPutMessageInStatusBar 
= true; 
 320             SetCursor(wxCURSOR_IBEAM
); 
 321          m_HandCursor 
= FALSE
; 
 322          if( m_StatusBar 
&& m_StatusFieldLabel 
!= -1 && 
 323              s_hasPutMessageInStatusBar 
) 
 325             m_StatusBar
->SetStatusText("", m_StatusFieldLabel
); 
 331    if ( event
.LeftIsDown() ) 
 333       // m_Selecting might not be set if the button got pressed 
 334       // outside this window, so check for it: 
 337          m_llist
->ContinueSelection(cursorPos
, m_ClickPosition
); 
 338          RequestUpdate();  // TODO: we don't have to redraw everything! 
 349    case WXLOWIN_MENU_LDOWN
: 
 351       // always move cursor to mouse click: 
 352       m_llist
->MoveCursorTo(cursorPos
); 
 354       // clicking a mouse removes the selection 
 355       if ( m_llist
->HasSelection() ) 
 357          m_llist
->DiscardSelection(); 
 359          RequestUpdate();     // TODO: we don't have to redraw everything! 
 362       // Calculate where the top of the visible area is: 
 366       GetScrollPixelsPerUnit(&dx
, &dy
); 
 369       wxPoint 
offset(-x0
+WXLO_XOFFSET
, -y0
+WXLO_YOFFSET
); 
 371       if(m_CursorVisibility 
== -1) 
 372          m_CursorVisibility 
= 1; 
 373 #ifdef WXLAYOUT_USE_CARET 
 374       if ( m_CursorVisibility 
== 1 ) 
 376 #endif // WXLAYOUT_USE_CARET 
 378       if(m_CursorVisibility
) 
 380          // draw a thick cursor for editable windows with focus 
 381          m_llist
->DrawCursor(dc
, m_HaveFocus 
&& IsEditable(), offset
); 
 385       RequestUpdate(); // RequestUpdate suppresses flicker under GTK 
 389       m_llist
->StartSelection(wxPoint(-1, -1), m_ClickPosition
); 
 394    case WXLOWIN_MENU_LUP
: 
 397          m_llist
->EndSelection(); 
 400          RequestUpdate();     // TODO: we don't have to redraw everything! 
 404    case WXLOWIN_MENU_MDOWN
: 
 408    case WXLOWIN_MENU_DBLCLICK
: 
 409       // select a word under cursor 
 410       m_llist
->MoveCursorTo(cursorPos
); 
 411       m_llist
->MoveCursorWord(-1); 
 412       m_llist
->StartSelection(); 
 413       m_llist
->MoveCursorWord(1, false); 
 414       m_llist
->EndSelection(); 
 416       RequestUpdate();     // TODO: we don't have to redraw everything! 
 420    // notify about mouse events? 
 423       // only do the menu if activated, editable and not on a clickable object 
 424       if(eventId 
== WXLOWIN_MENU_RCLICK
 
 426          && (! obj 
|| u 
== NULL
)) 
 428          PopupMenu(m_PopupMenu
, m_ClickPosition
.x
, m_ClickPosition
.y
); 
 433       // find the object at this position 
 436          wxCommandEvent 
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, eventId
); 
 437          commandEvent
.SetEventObject( this ); 
 438          commandEvent
.SetClientData((char *)obj
); 
 439          GetEventHandler()->ProcessEvent(commandEvent
); 
 446 // ---------------------------------------------------------------------------- 
 447 // keyboard handling. 
 448 // ---------------------------------------------------------------------------- 
 451 wxLayoutWindow::OnChar(wxKeyEvent
& event
) 
 453    int keyCode 
= event
.KeyCode(); 
 454    bool ctrlDown 
= event
.ControlDown(); 
 456 #ifdef WXLAYOUT_DEBUG 
 457    if(keyCode 
== WXK_F1
) 
 464    // Force m_Selecting to be false if shift is no longer 
 465    // pressed. OnKeyUp() cannot catch all Shift-Up events. 
 466    if(m_Selecting 
&& !event
.ShiftDown()) 
 469       m_llist
->EndSelection(); 
 470       m_llist
->DiscardSelection(); //FIXME: correct? 
 473    // If we deleted the selection here, we must not execute the 
 474    // deletion in Delete/Backspace handling. 
 475    bool deletedSelection 
= false; 
 476    // pressing any non-arrow key optionally replaces the selection: 
 477    if(m_AutoDeleteSelection
 
 479       && m_llist
->HasSelection()  
 480       && ! IsDirectionKey(keyCode
) 
 481       && ! (event
.AltDown() || ctrlDown
) 
 484       m_llist
->DeleteSelection(); 
 485       deletedSelection 
= true; 
 489    // <Shift>+<arrow> starts selection 
 490    if ( IsDirectionKey(keyCode
) ) 
 492       // just continue the old selection 
 495          if( event
.ShiftDown() ) 
 496             m_llist
->ContinueSelection(); 
 499             m_llist
->DiscardSelection(); 
 503       else if( event
.ShiftDown() ) 
 506          m_llist
->StartSelection(); 
 510    // If needed, make cursor visible: 
 511    if(m_CursorVisibility 
== -1) 
 512       m_CursorVisibility 
= 1; 
 514    /* These two nested switches work like this: 
 515       The first one processes all non-editing keycodes, to move the 
 516       cursor, etc. It's default will process all keycodes causing 
 517       modifications to the buffer, but only if editing is allowed. 
 523          m_llist
->MoveCursorWord(1); 
 525          m_llist
->MoveCursorHorizontally(1); 
 529          m_llist
->MoveCursorWord(-1); 
 531          m_llist
->MoveCursorHorizontally(-1); 
 534       m_llist
->MoveCursorVertically(-1); 
 537       m_llist
->MoveCursorVertically(1); 
 540       m_llist
->MoveCursorVertically(-Y_SCROLL_PAGE
); 
 543       m_llist
->MoveCursorVertically(Y_SCROLL_PAGE
); 
 547          m_llist
->MoveCursorTo(wxPoint(0, 0)); 
 549          m_llist
->MoveCursorToBeginOfLine(); 
 553          m_llist
->MoveCursorToEnd(); 
 555          m_llist
->MoveCursorToEndOfLine(); 
 559       if(ctrlDown 
&& ! IsEditable()) 
 563             // this should work even in read-only mode 
 570          case 't': // search again 
 577       else if( IsEditable() ) 
 579          /* First, handle control keys */ 
 580          if(ctrlDown 
&& ! event
.AltDown()) 
 582             if(keyCode 
>= 'A' && keyCode 
<= 'Z') 
 583                keyCode 
= tolower(keyCode
); 
 590                if(! deletedSelection
) 
 592                   m_llist
->DeleteWord(); 
 597                if(! deletedSelection
) // already done 
 604                m_llist
->DeleteLines(1); 
 607             case 'h': // like backspace 
 608                if(m_llist
->MoveCursorHorizontally(-1)) 
 618             case 't': // search again 
 623                m_llist
->DeleteToBeginOfLine(); 
 627                m_llist
->DeleteToEndOfLine(); 
 636 #ifdef WXLAYOUT_DEBUG 
 638                m_llist
->SetFont(-1,-1,-1,-1,true);  // underlined 
 646          else if( event
.AltDown() && ! event
.ControlDown() ) 
 652                m_llist
->DeleteWord(); 
 660          else if ( ! event
.AltDown() && ! event
.ControlDown()) 
 665                if(event
.ShiftDown()) 
 669                if(event
.ShiftDown()) 
 672                   if(! deletedSelection
) 
 678             case WXK_BACK
: // backspace 
 679                if(! deletedSelection
) 
 680                   if(m_llist
->MoveCursorHorizontally(-1)) 
 688                   m_llist
->WrapLine(m_WrapMargin
); 
 689                m_llist
->LineBreak(); 
 694                if ( !event
.ShiftDown() ) 
 696                   // TODO should be configurable 
 697                   static const int tabSize 
= 8; 
 699                   CoordType x 
= m_llist
->GetCursorPos().x
; 
 700                   size_t numSpaces 
= tabSize 
- x 
% tabSize
; 
 701                   m_llist
->Insert(wxString(' ', numSpaces
)); 
 707                if((!(event
.ControlDown() || event
.AltDown() 
 709                      ///FIXME: wxGTK reports MetaDown always 
 713                   && (keyCode 
< 256 && keyCode 
>= 32) 
 716                   if(m_WrapMargin 
> 0 && isspace(keyCode
)) 
 717                      m_llist
->WrapLine(m_WrapMargin
); 
 718                   m_llist
->Insert((char)keyCode
); 
 729       // continue selection to the current (new) cursor position 
 730       m_llist
->ContinueSelection(); 
 733    // refresh the screen 
 734    RequestUpdate(m_llist
->GetUpdateRect()); 
 738 wxLayoutWindow::OnKeyUp(wxKeyEvent
& event
) 
 740    if ( event
.KeyCode() == WXK_SHIFT 
&& m_Selecting 
) 
 742       m_llist
->EndSelection(); 
 751 wxLayoutWindow::ScrollToCursor(void) 
 753    //is always needed to make sure we know where the cursor is 
 755    RequestUpdate(m_llist
->GetUpdateRect()); 
 757    int x0
,y0
,x1
,y1
, dx
, dy
; 
 759    // Calculate where the top of the visible area is: 
 761    GetScrollPixelsPerUnit(&dx
, &dy
); 
 764    WXLO_DEBUG(("ScrollToCursor: ViewStart is %d/%d", x0
, y0
)); 
 766    // Get the size of the visible window: 
 767    GetClientSize(&x1
, &y1
); 
 769    // Make sure that the scrollbars are at a position so that the cursor is 
 770    // visible if we are editing 
 771    WXLO_DEBUG(("m_ScrollToCursor = %d", (int) m_ScrollToCursor
)); 
 772    wxPoint cc 
= m_llist
->GetCursorScreenPos(); 
 774    // the cursor should be completely visible in both directions 
 775    wxPoint 
cs(m_llist
->GetCursorSize()); 
 778    if ( cc
.x 
< x0 
|| cc
.x 
>= x0 
+ x1 
- cs
.x 
) 
 785    if ( cc
.y 
< y0 
|| cc
.y 
>= y0 
+ y1 
- cs
.y 
) 
 792    if ( nx 
!= -1 || ny 
!= -1 ) 
 794       // set new view start 
 795       Scroll(nx 
== -1 ? -1 : (nx
+dx
-1)/dx
, ny 
== -1 ? -1 : (ny
+dy
-1)/dy
); 
 797       m_ScrollToCursor 
= false; 
 803 wxLayoutWindow::OnPaint( wxPaintEvent 
&WXUNUSED(event
)) 
 805    wxRect region 
= GetUpdateRegion().GetBox(); 
 806    InternalPaint(®ion
); 
 810 wxLayoutWindow::RequestUpdate(const wxRect 
*updateRect
) 
 813    // Calling Refresh() causes bad flicker under wxGTK!!! 
 814    InternalPaint(updateRect
); 
 816    // shouldn't specify the update rectangle if it doesn't include all the 
 817    // changed locations - otherwise, they won't be repainted at all because 
 818    // the system clips the display to the update rect 
 819    Refresh(FALSE
); //, updateRect); 
 824 wxLayoutWindow::InternalPaint(const wxRect 
*updateRect
) 
 826    wxPaintDC 
dc( this ); 
 829 #ifdef WXLAYOUT_USE_CARET 
 830    // hide the caret before drawing anything 
 832 #endif // WXLAYOUT_USE_CARET 
 834    int x0
,y0
,x1
,y1
, dx
, dy
; 
 836    // Calculate where the top of the visible area is: 
 838    GetScrollPixelsPerUnit(&dx
, &dy
); 
 841    // Get the size of the visible window: 
 842    GetClientSize(&x1
,&y1
); 
 848       WXLO_DEBUG(("Update rect: %ld,%ld / %ld,%ld", 
 849                   updateRect
->x
, updateRect
->y
, 
 850                   updateRect
->x
+updateRect
->width
, 
 851                   updateRect
->y
+updateRect
->height
)); 
 855       WXLO_DEBUG(("InternalPaint, isdirty, list size: %ld,%ld", 
 856                   (unsigned long) m_llist
->GetSize().x
, 
 857                   (unsigned long) m_llist
->GetSize().y
)); 
 858 //      m_llist->ForceTotalLayout(); 
 860       WXLO_DEBUG(("InternalPaint, isdirty, list size after layout: %ld,%ld", 
 861                   (unsigned long) m_llist
->GetSize().x
, 
 862                   (unsigned long) m_llist
->GetSize().y
)); 
 867    /* Check whether the window has grown, if so, we need to reallocate 
 868       the bitmap to be larger. */ 
 869    if(x1 
> m_bitmapSize
.x 
|| y1 
> m_bitmapSize
.y
) 
 871       wxASSERT(m_bitmapSize
.x 
> 0); 
 872       wxASSERT(m_bitmapSize
.y 
> 0); 
 874       m_memDC
->SelectObject(wxNullBitmap
); 
 876       m_bitmapSize 
= wxPoint(x1
,y1
); 
 877       m_bitmap 
= new wxBitmap(x1
,y1
); 
 878       m_memDC
->SelectObject(*m_bitmap
); 
 881    m_memDC
->SetDeviceOrigin(0,0); 
 882    m_memDC
->SetBackground(wxBrush(m_llist
->GetDefaultStyleInfo().GetBGColour(),wxSOLID
)); 
 883    m_memDC
->SetPen(wxPen(m_llist
->GetDefaultStyleInfo().GetBGColour(), 
 885    m_memDC
->SetLogicalFunction(wxCOPY
); 
 888    // fill the background with the background bitmap 
 893          w 
= m_BGbitmap
->GetWidth(), 
 894          h 
= m_BGbitmap
->GetHeight(); 
 895       for(y 
= 0; y 
< y1
; y
+=h
) 
 896          for(x 
= 0; x 
< x1
; x
+=w
) 
 897             m_memDC
->DrawBitmap(*m_BGbitmap
, x
, y
); 
 898       m_memDC
->SetBackgroundMode(wxTRANSPARENT
); 
 901    // This is the important bit: we tell the list to draw itself 
 905       WXLO_DEBUG(("Update rect: %ld,%ld / %ld,%ld", 
 906                   updateRect
->x
, updateRect
->y
, 
 907                   updateRect
->x
+updateRect
->width
, 
 908                   updateRect
->y
+updateRect
->height
)); 
 912    // Device origins on the memDC are suspect, we translate manually 
 913    // with the translate parameter of Draw(). 
 914    wxPoint 
offset(-x0
+WXLO_XOFFSET
,-y0
+WXLO_YOFFSET
); 
 915    m_llist
->Draw(*m_memDC
,offset
, y0
, y0
+y1
); 
 917    // We start calculating a new update rect before drawing the 
 918    // cursor, so that the cursor coordinates get included in the next 
 919    // update rectangle (although they are drawn on the memDC, this is 
 920    // needed to erase it): 
 921    m_llist
->InvalidateUpdateRect(); 
 922    if(m_CursorVisibility 
!= 0) 
 924       // draw a thick cursor for editable windows with focus 
 925       m_llist
->DrawCursor(*m_memDC
, 
 926                           m_HaveFocus 
&& IsEditable(), 
 930 // Now copy everything to the screen: 
 932    // This somehow doesn't work, but even the following bit with the 
 933    // whole rect at once is still a bit broken I think. 
 934    wxRegionIterator 
ri ( GetUpdateRegion() ); 
 938          WXLO_DEBUG(("UpdateRegion: %ld,%ld, %ld,%ld", 
 939                      ri
.GetX(),ri
.GetY(),ri
.GetW(),ri
.GetH())); 
 940          dc
.Blit(x0
+ri
.GetX(),y0
+ri
.GetY(),ri
.GetW(),ri
.GetH(), 
 941                  m_memDC
,ri
.GetX(),ri
.GetY(),wxCOPY
,FALSE
); 
 947       // FIXME: Trying to copy only the changed parts, but it does not seem 
 949 //      x0 = updateRect->x; y0 = updateRect->y; 
 950 //      if(updateRect->height < y1) 
 951 //         y1 = updateRect->height; 
 952 //      y1 += WXLO_YOFFSET; //FIXME might not be needed 
 953       dc
.Blit(x0
,y0
,x1
,y1
,m_memDC
,0,0,wxCOPY
,FALSE
); 
 956 #ifdef WXLAYOUT_USE_CARET 
 957    // show the caret back after everything is redrawn 
 959 #endif // WXLAYOUT_USE_CARET 
 963    if ( m_StatusBar 
&& m_StatusFieldCursor 
!= -1 ) 
 965       static wxPoint 
s_oldCursorPos(-1, -1); 
 967       wxPoint 
pos(m_llist
->GetCursorPos()); 
 969       // avoid unnecessary status bar refreshes 
 970       if ( pos 
!= s_oldCursorPos 
) 
 972          s_oldCursorPos 
= pos
; 
 975          label
.Printf(_("Ln:%d Col:%d"), pos
.y 
+ 1, pos
.x 
+ 1); 
 976          m_StatusBar
->SetStatusText(label
, m_StatusFieldCursor
); 
 982 wxLayoutWindow::OnSize(wxSizeEvent 
&event
) 
 990 // change the range and position of scrollbars 
 992 wxLayoutWindow::ResizeScrollbars(bool exact
) 
 997       wxClientDC 
dc( this ); 
 999 //      m_llist->ForceTotalLayout(); 
1000       m_llist
->Layout(dc
); 
1005    wxPoint max 
= m_llist
->GetSize(); 
1006    wxSize size 
= GetClientSize(); 
1008    WXLO_DEBUG(("ResizeScrollbars: max size = (%ld, %ld)", 
1009                (long int)max
.x
, (long int) max
.y
)); 
1011    // in the absence of scrollbars we should compare with the client size 
1012    if ( !m_hasHScrollbar 
) 
1013       m_maxx 
= size
.x
;// - WXLO_ROFFSET; 
1014    if ( !m_hasVScrollbar 
) 
1015       m_maxy 
= size
.y
;// - WXLO_BOFFSET; 
1017    // check if the text hasn't become too big 
1018    // TODO why do we set both at once? they're independent... 
1019    if( max
.x 
> m_maxx 
- WXLO_ROFFSET 
|| max
.y 
> m_maxy 
- WXLO_BOFFSET 
|| exact 
) 
1021       // text became too large 
1024          // add an extra bit to the sizes to avoid future updates 
1025          max
.x 
+= WXLO_ROFFSET
; 
1026          max
.y 
+= WXLO_BOFFSET
; 
1029       ViewStart(&m_ViewStartX
, &m_ViewStartY
); 
1030       SetScrollbars(X_SCROLL_PAGE
, Y_SCROLL_PAGE
, 
1031                     max
.x 
/ X_SCROLL_PAGE 
+ 1, max
.y 
/ Y_SCROLL_PAGE 
+ 1, 
1032                     m_ViewStartX
, m_ViewStartY
, 
1036       m_hasVScrollbar 
= true; 
1038       m_maxx 
= max
.x 
+ X_SCROLL_PAGE
; 
1039       m_maxy 
= max
.y 
+ Y_SCROLL_PAGE
; 
1042    //FIXME: this code is pretty broken, producing "arithmetic 
1043    //exception" crashes (div by 0??) 
1046       // check if the window hasn't become too big, thus making the scrollbars 
1050          // add an extra bit to the sizes to avoid future updates 
1051          max
.x 
-= WXLO_ROFFSET
; 
1052          max
.y 
-= WXLO_BOFFSET
; 
1055       if ( m_hasHScrollbar 
&& (max
.x 
< m_maxx
) ) 
1057          // remove the horizontal scrollbar 
1058          SetScrollbars(0, -1, 0, -1, 0, -1, true); 
1059          m_hasHScrollbar 
= false; 
1062       if ( m_hasVScrollbar 
&& (max
.y 
< m_maxy
) ) 
1064          // remove the vertical scrollbar 
1065          SetScrollbars(-1, 0, -1, 0, -1, 0, true); 
1066          m_hasVScrollbar 
= false; 
1072 // ---------------------------------------------------------------------------- 
1073 // clipboard operations 
1075 // ---------------------------------------------------------------------------- 
1078 wxLayoutWindow::Paste(bool primary
) 
1081    if (wxTheClipboard
->Open()) 
1085          wxTheClipboard
->UsePrimarySelection(); 
1087 #if wxUSE_PRIVATE_CLIPBOARD_FORMAT 
1088       wxLayoutDataObject wxldo
; 
1089       if (wxTheClipboard
->IsSupported( wxldo
.GetFormat() )) 
1091          wxTheClipboard
->GetData(&wxldo
); 
1094          //FIXME: missing functionality  m_llist->Insert(wxldo.GetList()); 
1099          wxTextDataObject data
; 
1100          if (wxTheClipboard
->IsSupported( data
.GetFormat() )) 
1102             wxTheClipboard
->GetData(&data
); 
1103             wxString text 
= data
.GetText(); 
1104             wxLayoutImportText( m_llist
, text
); 
1108       wxTheClipboard
->Close(); 
1113 wxLayoutWindow::Copy(bool invalidate
) 
1115    // Calling GetSelection() will automatically do an EndSelection() 
1116    // on the list, but we need to take a note of it, too: 
1119       m_Selecting 
= false; 
1120       m_llist
->EndSelection(); 
1123    wxLayoutDataObject wldo
; 
1124    wxLayoutList 
*llist 
= m_llist
->GetSelection(&wldo
, invalidate
); 
1127    // Export selection as text: 
1129    wxLayoutExportObject 
*exp
; 
1130    wxLayoutExportStatus 
status(llist
); 
1131    while((exp 
= wxLayoutExport( &status
, WXLO_EXPORT_AS_TEXT
)) != NULL
) 
1133       if(exp
->type 
== WXLO_EXPORT_TEXT
) 
1134          text 
<< *(exp
->content
.text
); 
1139    // The exporter always appends a newline, so we chop it off if it 
1142       size_t len 
= text
.Length(); 
1143       if(len 
> 2 && text
[len
-2] ==  '\r') // Windows 
1144          text 
= text
.Mid(0,len
-2); 
1145       else if(len 
> 1 && text
[len
-1] == '\n') 
1146          text 
= text
.Mid(0,len
-1); 
1150    if (wxTheClipboard
->Open()) 
1152       wxTextDataObject 
*data 
= new wxTextDataObject( text 
); 
1153       bool  rc 
= wxTheClipboard
->SetData( data 
); 
1154 #if wxUSE_PRIVATE_CLIPBOARD_FORMAT 
1155       rc 
|= wxTheClipboard
->AddData( &wldo 
); 
1157       wxTheClipboard
->Close(); 
1165 wxLayoutWindow::Cut(void) 
1167    if(Copy(false)) // do not invalidate selection after copy 
1169       m_llist
->DeleteSelection(); 
1177 // ---------------------------------------------------------------------------- 
1179 // ---------------------------------------------------------------------------- 
1183 wxLayoutWindow::Find(const wxString 
&needle
, 
1184                      wxPoint 
* fromWhere
, 
1185                      const wxString 
&configPath
) 
1189    if(needle
.Length() == 0) 
1191       if( ! MInputBox(&m_FindString
, 
1196           || strutil_isempty(m_FindString
)) 
1200       m_FindString 
= needle
; 
1202    if(fromWhere 
== NULL
) 
1203       found 
= m_llist
->FindText(m_FindString
, m_llist
->GetCursorPos()); 
1205       found 
= m_llist
->FindText(m_FindString
, *fromWhere
); 
1213       m_llist
->MoveCursorTo(found
); 
1223 wxLayoutWindow::FindAgain(void) 
1225    bool rc 
= Find(m_FindString
); 
1230 // ---------------------------------------------------------------------------- 
1232 // ---------------------------------------------------------------------------- 
1235 wxLayoutWindow::MakeFormatMenu() 
1237    wxMenu 
*m 
= new wxMenu(_("Layout Menu")); 
1239    m
->Append(WXLOWIN_MENU_LARGER   
,_("&Larger"),_("Switch to larger font."), false); 
1240    m
->Append(WXLOWIN_MENU_SMALLER  
,_("&Smaller"),_("Switch to smaller font."), false); 
1241    m
->AppendSeparator(); 
1242    m
->Append(WXLOWIN_MENU_UNDERLINE
, _("&Underline"),_("Underline mode."), true); 
1243    m
->Append(WXLOWIN_MENU_BOLD
, _("&Bold"),_("Bold mode."), true); 
1244    m
->Append(WXLOWIN_MENU_ITALICS
, _("&Italics"),_("Italics mode."), true); 
1245    m
->AppendSeparator(); 
1246    m
->Append(WXLOWIN_MENU_ROMAN     
,_("&Roman"),_("Switch to roman font."), false); 
1247    m
->Append(WXLOWIN_MENU_TYPEWRITER
,_("&Typewriter"),_("Switch to typewriter font."), false); 
1248    m
->Append(WXLOWIN_MENU_SANSSERIF 
,_("&Sans Serif"),_("Switch to sans serif font."), false); 
1253 void wxLayoutWindow::OnUpdateMenuUnderline(wxUpdateUIEvent
& event
) 
1255    event
.Check(m_llist
->IsFontUnderlined()); 
1258 void wxLayoutWindow::OnUpdateMenuBold(wxUpdateUIEvent
& event
) 
1260    event
.Check(m_llist
->IsFontBold()); 
1263 void wxLayoutWindow::OnUpdateMenuItalic(wxUpdateUIEvent
& event
) 
1265    event
.Check(m_llist
->IsFontItalic()); 
1268 void wxLayoutWindow::OnMenu(wxCommandEvent
& event
) 
1270    switch (event
.GetId()) 
1272    case WXLOWIN_MENU_LARGER
: 
1273       m_llist
->SetFontLarger(); RequestUpdate(); break; 
1274    case WXLOWIN_MENU_SMALLER
: 
1275       m_llist
->SetFontSmaller(); RequestUpdate(); break; 
1276    case WXLOWIN_MENU_UNDERLINE
: 
1277       m_llist
->ToggleFontUnderline(); RequestUpdate(); break; 
1278    case WXLOWIN_MENU_BOLD
: 
1279       m_llist
->ToggleFontWeight(); RequestUpdate(); break; 
1280    case WXLOWIN_MENU_ITALICS
: 
1281       m_llist
->ToggleFontItalics(); RequestUpdate(); break; 
1282    case WXLOWIN_MENU_ROMAN
: 
1283       m_llist
->SetFontFamily(wxROMAN
); RequestUpdate(); break; 
1284    case WXLOWIN_MENU_TYPEWRITER
: 
1285       m_llist
->SetFontFamily(wxFIXED
); RequestUpdate(); break; 
1286    case WXLOWIN_MENU_SANSSERIF
: 
1287       m_llist
->SetFontFamily(wxSWISS
); RequestUpdate(); break; 
1291 // ---------------------------------------------------------------------------- 
1293 // ---------------------------------------------------------------------------- 
1296 wxLayoutWindow::OnSetFocus(wxFocusEvent 
&ev
) 
1300    RequestUpdate(); // cursor must change 
1304 wxLayoutWindow::OnKillFocus(wxFocusEvent 
&ev
) 
1306    m_HaveFocus 
= false; 
1308    RequestUpdate();// cursor must change 
1311 // ---------------------------------------------------------------------------- 
1312 // private functions 
1313 // ---------------------------------------------------------------------------- 
1315 static bool IsDirectionKey(long keyCode
)