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; 
  91 #define wxUSE_PRIVATE_CLIPBOARD_FORMAT 0 
  93 // ---------------------------------------------------------------------------- 
  95 // ---------------------------------------------------------------------------- 
  97 BEGIN_EVENT_TABLE(wxLayoutWindow
,wxScrolledWindow
) 
  98    EVT_SIZE    (wxLayoutWindow::OnSize
) 
 100    EVT_PAINT    (wxLayoutWindow::OnPaint
) 
 102    EVT_CHAR     (wxLayoutWindow::OnChar
) 
 103    EVT_KEY_UP   (wxLayoutWindow::OnKeyUp
) 
 105    EVT_LEFT_DOWN(wxLayoutWindow::OnLeftMouseDown
) 
 106    EVT_LEFT_UP(wxLayoutWindow::OnLeftMouseUp
) 
 107    EVT_RIGHT_DOWN(wxLayoutWindow::OnRightMouseClick
) 
 108    EVT_LEFT_DCLICK(wxLayoutWindow::OnMouseDblClick
) 
 109    EVT_MIDDLE_DOWN(wxLayoutWindow::OnMiddleMouseDown
) 
 110    EVT_MOTION    (wxLayoutWindow::OnMouseMove
) 
 112    EVT_UPDATE_UI(WXLOWIN_MENU_UNDERLINE
, wxLayoutWindow::OnUpdateMenuUnderline
) 
 113    EVT_UPDATE_UI(WXLOWIN_MENU_BOLD
, wxLayoutWindow::OnUpdateMenuBold
) 
 114    EVT_UPDATE_UI(WXLOWIN_MENU_ITALICS
, wxLayoutWindow::OnUpdateMenuItalic
) 
 115    EVT_MENU_RANGE(WXLOWIN_MENU_FIRST
, WXLOWIN_MENU_LAST
, wxLayoutWindow::OnMenu
) 
 117    EVT_SET_FOCUS(wxLayoutWindow::OnSetFocus
) 
 118    EVT_KILL_FOCUS(wxLayoutWindow::OnKillFocus
) 
 120 //   EVT_IDLE(wxLayoutWindow::ResizeScrollbars) 
 123 // ---------------------------------------------------------------------------- 
 124 // function prototypes 
 125 // ---------------------------------------------------------------------------- 
 127 /// returns TRUE if keyCode is one of arrows/home/end/page{up|down} keys 
 128 static bool IsDirectionKey(long keyCode
); 
 130 // ============================================================================ 
 132 // ============================================================================ 
 134 #ifndef wxWANTS_CHARS 
 135 #   define wxWANTS_CHARS 0 
 138 // ---------------------------------------------------------------------------- 
 140 // ---------------------------------------------------------------------------- 
 142 wxLayoutWindow::wxLayoutWindow(wxWindow 
*parent
) 
 143               : wxScrolledWindow(parent
, -1, 
 144                                  wxDefaultPosition
, wxDefaultSize
, 
 145                                  wxHSCROLL 
| wxVSCROLL 
| 
 150    SetStatusBar(NULL
); // don't use statusbar 
 152    m_doSendEvents 
= false; 
 153    m_ViewStartX 
= 0; m_ViewStartY 
= 0; 
 154    m_DoPopupMenu 
= true; 
 155    m_PopupMenu 
= MakeFormatMenu(); 
 156    m_memDC 
= new wxMemoryDC
; 
 157    m_bitmap 
= new wxBitmap(4,4); 
 158    m_bitmapSize 
= wxPoint(4,4); 
 159    m_llist 
= new wxLayoutList(); 
 161    m_ScrollToCursor 
= false; 
 163    m_FocusFollowMode 
= false; 
 167    // no scrollbars initially 
 169    m_hasVScrollbar 
= false; 
 173 #ifdef WXLAYOUT_USE_CARET 
 174    // FIXME cursor size shouldn't be hardcoded 
 175    wxCaret 
*caret 
= new wxCaret(this, 2, 20); 
 177    m_llist
->SetCaret(caret
); 
 178 #endif // WXLAYOUT_USE_CARET 
 181    m_HandCursor 
= FALSE
; 
 182    m_CursorVisibility 
= -1; 
 183    SetCursor(wxCURSOR_IBEAM
); 
 186    // at least under Windows, this should be the default behaviour 
 187    m_AutoDeleteSelection 
= TRUE
; 
 190 wxLayoutWindow::~wxLayoutWindow() 
 192    delete m_memDC
; // deletes bitmap automatically (?) 
 196    SetBackgroundBitmap(NULL
); 
 200 wxLayoutWindow::Clear(int family
, 
 208    GetLayoutList()->Clear(family
,size
,style
,weight
,underline
,fg
,bg
); 
 209    SetBackgroundColour(GetLayoutList()->GetDefaultStyleInfo().GetBGColour()); 
 210    wxScrolledWindow::Clear(); 
 211    ResizeScrollbars(true); 
 215       m_CursorVisibility 
= 1; 
 217 #ifdef WXLAYOUT_USE_CARET 
 218    if ( m_CursorVisibility 
== 1 ) 
 220 #endif // WXLAYOUT_USE_CARET 
 222    RequestUpdate((wxRect 
*)NULL
); 
 225 void wxLayoutWindow::Refresh(bool eraseBackground
, const wxRect 
*rect
) 
 227    wxScrolledWindow::Refresh(eraseBackground
, rect
); 
 231 wxLayoutWindow::OnMouse(int eventId
, wxMouseEvent
& event
) 
 233    wxClientDC 
dc( this ); 
 235    if ( eventId 
!= WXLOWIN_MENU_MOUSEMOVE
 
 243    findPos
.x 
= dc
.DeviceToLogicalX(event
.GetX()); 
 244    findPos
.y 
= dc
.DeviceToLogicalY(event
.GetY()); 
 246    findPos
.x 
-= WXLO_XOFFSET
; 
 247    findPos
.y 
-= WXLO_YOFFSET
; 
 254    m_ClickPosition 
= wxPoint(event
.GetX(), event
.GetY()); 
 256    // Scroll the window if the mouse is at the end of it: 
 257    if(m_Selecting 
&& eventId 
== WXLOWIN_MENU_MOUSEMOVE
) 
 259       //WXLO_DEBUG(("selecting at : %d/%d", (int) event.GetX(), (int)event.GetY())); 
 261       ViewStart(&left
, &top
); 
 262       wxSize size 
= GetClientSize(); 
 265       if(event
.GetX() < WXLO_SCROLLMARGIN_X
) 
 266          xdelta 
= -(WXLO_SCROLLMARGIN_X
-event
.GetX()); 
 267       else if(event
.GetX() > size
.x
-WXLO_SCROLLMARGIN_X
) 
 268          xdelta 
= event
.GetX()-size
.x
+WXLO_SCROLLMARGIN_X
; 
 271       if(event
.GetY() < WXLO_SCROLLMARGIN_Y
) 
 272          ydelta 
= -(WXLO_SCROLLMARGIN_Y
-event
.GetY()); 
 273       else if(event
.GetY() > size
.y
-WXLO_SCROLLMARGIN_Y
) 
 274          ydelta 
= event
.GetY()-size
.y
+WXLO_SCROLLMARGIN_Y
; 
 278       //WXLO_DEBUG(("xdelta: %d", (int) xdelta)); 
 279       if(xdelta 
!= 0 || ydelta 
!= 0) 
 281          top  
+= ydelta
; if(top 
< 0) top 
= 0; 
 282          left 
+= xdelta
; if(left 
< 0) left 
= 0; 
 289    wxLayoutObject 
*obj 
= m_llist
->FindObjectScreen(dc
, findPos
, 
 291    wxLayoutObject::UserData 
*u 
= obj 
? obj
->GetUserData() : NULL
; 
 293    // has the mouse only been moved? 
 296    case WXLOWIN_MENU_MOUSEMOVE
: 
 298       // this variables is used to only erase the message in the status 
 299       // bar if we had put it there previously - otherwise empting status 
 300       // bar might be undesirable 
 301       static bool s_hasPutMessageInStatusBar 
= false; 
 303       // found is only true if we are really over an object, not just 
 305       if(found 
&& u 
&& ! m_Selecting
) 
 308             SetCursor(wxCURSOR_HAND
); 
 310          if(m_StatusBar 
&& m_StatusFieldLabel 
!= -1) 
 312             const wxString 
&label 
= u
->GetLabel(); 
 315                m_StatusBar
->SetStatusText(label
, 
 317                s_hasPutMessageInStatusBar 
= true; 
 324             SetCursor(wxCURSOR_IBEAM
); 
 325          m_HandCursor 
= FALSE
; 
 326          if( m_StatusBar 
&& m_StatusFieldLabel 
!= -1 && 
 327              s_hasPutMessageInStatusBar 
) 
 329             m_StatusBar
->SetStatusText("", m_StatusFieldLabel
); 
 335    if ( event
.LeftIsDown() ) 
 337       // m_Selecting might not be set if the button got pressed 
 338       // outside this window, so check for it: 
 341          m_llist
->ContinueSelection(cursorPos
, m_ClickPosition
); 
 342          RequestUpdate();  // TODO: we don't have to redraw everything! 
 353    case WXLOWIN_MENU_LDOWN
: 
 355       // always move cursor to mouse click: 
 356       m_llist
->MoveCursorTo(cursorPos
); 
 358       // clicking a mouse removes the selection 
 359       if ( m_llist
->HasSelection() ) 
 361          m_llist
->DiscardSelection(); 
 363          RequestUpdate();     // TODO: we don't have to redraw everything! 
 366       // Calculate where the top of the visible area is: 
 370       GetScrollPixelsPerUnit(&dx
, &dy
); 
 373       wxPoint 
offset(-x0
+WXLO_XOFFSET
, -y0
+WXLO_YOFFSET
); 
 375       if(m_CursorVisibility 
== -1) 
 376          m_CursorVisibility 
= 1; 
 377 #ifdef WXLAYOUT_USE_CARET 
 378       if ( m_CursorVisibility 
== 1 ) 
 380 #endif // WXLAYOUT_USE_CARET 
 382       if(m_CursorVisibility
) 
 384          // draw a thick cursor for editable windows with focus 
 385          m_llist
->DrawCursor(dc
, m_HaveFocus 
&& IsEditable(), offset
); 
 389       RequestUpdate(); // RequestUpdate suppresses flicker under GTK 
 393       m_llist
->StartSelection(wxPoint(-1, -1), m_ClickPosition
); 
 398    case WXLOWIN_MENU_LUP
: 
 401          // end selection at the cursor position corresponding to the 
 402          // current mouse position, but don´t move cursor there. 
 403          m_llist
->EndSelection(cursorPos
,m_ClickPosition
); 
 406          RequestUpdate();     // TODO: we don't have to redraw everything! 
 410    case WXLOWIN_MENU_MDOWN
: 
 414    case WXLOWIN_MENU_DBLCLICK
: 
 415       // select a word under cursor 
 416       m_llist
->MoveCursorTo(cursorPos
); 
 417       m_llist
->MoveCursorWord(-1); 
 418       m_llist
->StartSelection(); 
 419       m_llist
->MoveCursorWord(1, false); 
 420       m_llist
->EndSelection(); 
 422       RequestUpdate();     // TODO: we don't have to redraw everything! 
 426    // notify about mouse events? 
 429       // only do the menu if activated, editable and not on a clickable object 
 430       if(eventId 
== WXLOWIN_MENU_RCLICK
 
 432          && (! obj 
|| u 
== NULL
)) 
 434          PopupMenu(m_PopupMenu
, m_ClickPosition
.x
, m_ClickPosition
.y
); 
 439       // find the object at this position 
 442          wxCommandEvent 
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, eventId
); 
 443          commandEvent
.SetEventObject( this ); 
 444          commandEvent
.SetClientData((char *)obj
); 
 445          GetEventHandler()->ProcessEvent(commandEvent
); 
 452 // ---------------------------------------------------------------------------- 
 453 // keyboard handling. 
 454 // ---------------------------------------------------------------------------- 
 457 wxLayoutWindow::OnChar(wxKeyEvent
& event
) 
 459    int keyCode 
= event
.KeyCode(); 
 460    bool ctrlDown 
= event
.ControlDown(); 
 462 #ifdef WXLAYOUT_DEBUG 
 463    if(keyCode 
== WXK_F1
) 
 470    // Force m_Selecting to be false if shift is no longer 
 471    // pressed. OnKeyUp() cannot catch all Shift-Up events. 
 472    if(m_Selecting 
&& !event
.ShiftDown()) 
 475       m_llist
->EndSelection(); 
 476       m_llist
->DiscardSelection(); //FIXME: correct? 
 479    // If we deleted the selection here, we must not execute the 
 480    // deletion in Delete/Backspace handling. 
 481    bool deletedSelection 
= false; 
 482    // pressing any non-arrow key optionally replaces the selection: 
 483    if(m_AutoDeleteSelection
 
 485       && m_llist
->HasSelection()  
 486       && ! IsDirectionKey(keyCode
) 
 487       && ! (event
.AltDown() || ctrlDown
) 
 490       m_llist
->DeleteSelection(); 
 491       deletedSelection 
= true; 
 495    // <Shift>+<arrow> starts selection 
 496    if ( IsDirectionKey(keyCode
) ) 
 498       // just continue the old selection 
 501          if( event
.ShiftDown() ) 
 502             m_llist
->ContinueSelection(); 
 505             m_llist
->DiscardSelection(); 
 509       else if( event
.ShiftDown() ) 
 512          m_llist
->StartSelection(); 
 516    // If needed, make cursor visible: 
 517    if(m_CursorVisibility 
== -1) 
 518       m_CursorVisibility 
= 1; 
 520    /* These two nested switches work like this: 
 521       The first one processes all non-editing keycodes, to move the 
 522       cursor, etc. It's default will process all keycodes causing 
 523       modifications to the buffer, but only if editing is allowed. 
 529          m_llist
->MoveCursorWord(1); 
 531          m_llist
->MoveCursorHorizontally(1); 
 535          m_llist
->MoveCursorWord(-1); 
 537          m_llist
->MoveCursorHorizontally(-1); 
 540       m_llist
->MoveCursorVertically(-1); 
 543       m_llist
->MoveCursorVertically(1); 
 546       m_llist
->MoveCursorVertically(-Y_SCROLL_PAGE
); 
 549       m_llist
->MoveCursorVertically(Y_SCROLL_PAGE
); 
 553          m_llist
->MoveCursorTo(wxPoint(0, 0)); 
 555          m_llist
->MoveCursorToBeginOfLine(); 
 559          m_llist
->MoveCursorToEnd(); 
 561          m_llist
->MoveCursorToEndOfLine(); 
 565       if(ctrlDown 
&& ! IsEditable()) 
 569             // this should work even in read-only mode 
 575          case 't': // search again 
 579             // we don't handle it, maybe an accelerator? 
 583       else if( IsEditable() ) 
 585          /* First, handle control keys */ 
 586          if(ctrlDown 
&& ! event
.AltDown()) 
 588             if(keyCode 
>= 'A' && keyCode 
<= 'Z') 
 589                keyCode 
= tolower(keyCode
); 
 596                if(! deletedSelection
) 
 598                   m_llist
->DeleteWord(); 
 603                if(! deletedSelection
) // already done 
 610                m_llist
->DeleteLines(1); 
 613             case 'h': // like backspace 
 614                if(m_llist
->MoveCursorHorizontally(-1)) 
 623             case 't': // search again 
 627                m_llist
->DeleteToBeginOfLine(); 
 631                m_llist
->DeleteToEndOfLine(); 
 640 #ifdef WXLAYOUT_DEBUG 
 642                m_llist
->SetFont(-1,-1,-1,-1,true);  // underlined 
 646             // we don't handle it, maybe an accelerator? 
 651          else if( event
.AltDown() && ! event
.ControlDown() ) 
 657                m_llist
->DeleteWord(); 
 661                // we don't handle it, maybe an accelerator? 
 666          else if ( ! event
.AltDown() && ! event
.ControlDown()) 
 671                if(event
.ShiftDown()) 
 675                if(event
.ShiftDown()) 
 678                   if(! deletedSelection
) 
 684             case WXK_BACK
: // backspace 
 685                if(! deletedSelection
) 
 686                   if(m_llist
->MoveCursorHorizontally(-1)) 
 694                   m_llist
->WrapLine(m_WrapMargin
); 
 695                m_llist
->LineBreak(); 
 700                if ( !event
.ShiftDown() ) 
 702                   // TODO should be configurable 
 703                   static const int tabSize 
= 8; 
 705                   CoordType x 
= m_llist
->GetCursorPos().x
; 
 706                   size_t numSpaces 
= tabSize 
- x 
% tabSize
; 
 707                   m_llist
->Insert(wxString(' ', numSpaces
)); 
 713                if((!(event
.ControlDown() || event
.AltDown() 
 715                      ///FIXME: wxGTK reports MetaDown always 
 719                   && (keyCode 
< 256 && keyCode 
>= 32) 
 722                   if(m_WrapMargin 
> 0 && isspace(keyCode
)) 
 723                      m_llist
->WrapLine(m_WrapMargin
); 
 724                   m_llist
->Insert((char)keyCode
); 
 728                   // we don't handle it, maybe an accelerator? 
 735          // we don't handle it, maybe an accelerator? 
 741       // continue selection to the current (new) cursor position 
 742       m_llist
->ContinueSelection(); 
 745    // refresh the screen 
 746    RequestUpdate(m_llist
->GetUpdateRect()); 
 750 wxLayoutWindow::OnKeyUp(wxKeyEvent
& event
) 
 752    if ( event
.KeyCode() == WXK_SHIFT 
&& m_Selecting 
) 
 754       m_llist
->EndSelection(); 
 763 wxLayoutWindow::ScrollToCursor(void) 
 765    //is always needed to make sure we know where the cursor is 
 767    RequestUpdate(m_llist
->GetUpdateRect()); 
 769    int x0
,y0
,x1
,y1
, dx
, dy
; 
 771    // Calculate where the top of the visible area is: 
 773    GetScrollPixelsPerUnit(&dx
, &dy
); 
 776    WXLO_DEBUG(("ScrollToCursor: ViewStart is %d/%d", x0
, y0
)); 
 778    // Get the size of the visible window: 
 779    GetClientSize(&x1
, &y1
); 
 781    // Make sure that the scrollbars are at a position so that the cursor is 
 782    // visible if we are editing 
 783    WXLO_DEBUG(("m_ScrollToCursor = %d", (int) m_ScrollToCursor
)); 
 784    wxPoint cc 
= m_llist
->GetCursorScreenPos(); 
 786    // the cursor should be completely visible in both directions 
 787    wxPoint 
cs(m_llist
->GetCursorSize()); 
 790    if ( cc
.x 
< x0 
|| cc
.x 
>= x0 
+ x1 
- cs
.x 
) 
 797    if ( cc
.y 
< y0 
|| cc
.y 
>= y0 
+ y1 
- cs
.y 
) 
 804    if ( nx 
!= -1 || ny 
!= -1 ) 
 806       // set new view start 
 807       Scroll(nx 
== -1 ? -1 : (nx
+dx
-1)/dx
, ny 
== -1 ? -1 : (ny
+dy
-1)/dy
); 
 809       m_ScrollToCursor 
= false; 
 815 wxLayoutWindow::OnPaint( wxPaintEvent 
&WXUNUSED(event
)) 
 817    wxRect region 
= GetUpdateRegion().GetBox(); 
 818    InternalPaint(®ion
); 
 822 wxLayoutWindow::RequestUpdate(const wxRect 
*updateRect
) 
 825    // Calling Refresh() causes bad flicker under wxGTK!!! 
 826    InternalPaint(updateRect
); 
 828    // shouldn't specify the update rectangle if it doesn't include all the 
 829    // changed locations - otherwise, they won't be repainted at all because 
 830    // the system clips the display to the update rect 
 831    Refresh(FALSE
); //, updateRect); 
 836 wxLayoutWindow::InternalPaint(const wxRect 
*updateRect
) 
 838    wxPaintDC 
dc( this ); 
 841 #ifdef WXLAYOUT_USE_CARET 
 842    // hide the caret before drawing anything 
 844 #endif // WXLAYOUT_USE_CARET 
 846    int x0
,y0
,x1
,y1
, dx
, dy
; 
 848    // Calculate where the top of the visible area is: 
 850    GetScrollPixelsPerUnit(&dx
, &dy
); 
 853    // Get the size of the visible window: 
 854    GetClientSize(&x1
,&y1
); 
 860       WXLO_DEBUG(("Update rect: %ld,%ld / %ld,%ld", 
 861                   updateRect
->x
, updateRect
->y
, 
 862                   updateRect
->x
+updateRect
->width
, 
 863                   updateRect
->y
+updateRect
->height
)); 
 867       WXLO_DEBUG(("InternalPaint, isdirty, list size: %ld,%ld", 
 868                   (unsigned long) m_llist
->GetSize().x
, 
 869                   (unsigned long) m_llist
->GetSize().y
)); 
 870 //      m_llist->ForceTotalLayout(); 
 872       WXLO_DEBUG(("InternalPaint, isdirty, list size after layout: %ld,%ld", 
 873                   (unsigned long) m_llist
->GetSize().x
, 
 874                   (unsigned long) m_llist
->GetSize().y
)); 
 879    /* Check whether the window has grown, if so, we need to reallocate 
 880       the bitmap to be larger. */ 
 881    if(x1 
> m_bitmapSize
.x 
|| y1 
> m_bitmapSize
.y
) 
 883       wxASSERT(m_bitmapSize
.x 
> 0); 
 884       wxASSERT(m_bitmapSize
.y 
> 0); 
 886       m_memDC
->SelectObject(wxNullBitmap
); 
 888       m_bitmapSize 
= wxPoint(x1
,y1
); 
 889       m_bitmap 
= new wxBitmap(x1
,y1
); 
 890       m_memDC
->SelectObject(*m_bitmap
); 
 893    m_memDC
->SetDeviceOrigin(0,0); 
 894    m_memDC
->SetBackground(wxBrush(m_llist
->GetDefaultStyleInfo().GetBGColour(),wxSOLID
)); 
 895    m_memDC
->SetPen(wxPen(m_llist
->GetDefaultStyleInfo().GetBGColour(), 
 897    m_memDC
->SetLogicalFunction(wxCOPY
); 
 900    // fill the background with the background bitmap 
 905          w 
= m_BGbitmap
->GetWidth(), 
 906          h 
= m_BGbitmap
->GetHeight(); 
 907       for(y 
= 0; y 
< y1
; y
+=h
) 
 908          for(x 
= 0; x 
< x1
; x
+=w
) 
 909             m_memDC
->DrawBitmap(*m_BGbitmap
, x
, y
); 
 910       m_memDC
->SetBackgroundMode(wxTRANSPARENT
); 
 913    // This is the important bit: we tell the list to draw itself 
 917       WXLO_DEBUG(("Update rect: %ld,%ld / %ld,%ld", 
 918                   updateRect
->x
, updateRect
->y
, 
 919                   updateRect
->x
+updateRect
->width
, 
 920                   updateRect
->y
+updateRect
->height
)); 
 924    // Device origins on the memDC are suspect, we translate manually 
 925    // with the translate parameter of Draw(). 
 926    wxPoint 
offset(-x0
+WXLO_XOFFSET
,-y0
+WXLO_YOFFSET
); 
 927    m_llist
->Draw(*m_memDC
,offset
, y0
, y0
+y1
); 
 929    // We start calculating a new update rect before drawing the 
 930    // cursor, so that the cursor coordinates get included in the next 
 931    // update rectangle (although they are drawn on the memDC, this is 
 932    // needed to erase it): 
 933    m_llist
->InvalidateUpdateRect(); 
 934    if(m_CursorVisibility 
!= 0) 
 936       // draw a thick cursor for editable windows with focus 
 937       m_llist
->DrawCursor(*m_memDC
, 
 938                           m_HaveFocus 
&& IsEditable(), 
 942 // Now copy everything to the screen: 
 944    // This somehow doesn't work, but even the following bit with the 
 945    // whole rect at once is still a bit broken I think. 
 946    wxRegionIterator 
ri ( GetUpdateRegion() ); 
 950          WXLO_DEBUG(("UpdateRegion: %ld,%ld, %ld,%ld", 
 951                      ri
.GetX(),ri
.GetY(),ri
.GetW(),ri
.GetH())); 
 952          dc
.Blit(x0
+ri
.GetX(),y0
+ri
.GetY(),ri
.GetW(),ri
.GetH(), 
 953                  m_memDC
,ri
.GetX(),ri
.GetY(),wxCOPY
,FALSE
); 
 959       // FIXME: Trying to copy only the changed parts, but it does not seem 
 961 //      x0 = updateRect->x; y0 = updateRect->y; 
 962 //      if(updateRect->height < y1) 
 963 //         y1 = updateRect->height; 
 964 //      y1 += WXLO_YOFFSET; //FIXME might not be needed 
 965       dc
.Blit(x0
,y0
,x1
,y1
,m_memDC
,0,0,wxCOPY
,FALSE
); 
 968 #ifdef WXLAYOUT_USE_CARET 
 969    // show the caret back after everything is redrawn 
 971 #endif // WXLAYOUT_USE_CARET 
 975    if ( m_StatusBar 
&& m_StatusFieldCursor 
!= -1 ) 
 977       static wxPoint 
s_oldCursorPos(-1, -1); 
 979       wxPoint 
pos(m_llist
->GetCursorPos()); 
 981       // avoid unnecessary status bar refreshes 
 982       if ( pos 
!= s_oldCursorPos 
) 
 984          s_oldCursorPos 
= pos
; 
 987          label
.Printf(_("Ln:%d Col:%d"), pos
.y 
+ 1, pos
.x 
+ 1); 
 988          m_StatusBar
->SetStatusText(label
, m_StatusFieldCursor
); 
 994 wxLayoutWindow::OnSize(wxSizeEvent 
&event
) 
1002 // change the range and position of scrollbars 
1004 wxLayoutWindow::ResizeScrollbars(bool exact
) 
1009       wxClientDC 
dc( this ); 
1011 //      m_llist->ForceTotalLayout(); 
1012       m_llist
->Layout(dc
); 
1017    wxPoint max 
= m_llist
->GetSize(); 
1018    wxSize size 
= GetClientSize(); 
1020    WXLO_DEBUG(("ResizeScrollbars: max size = (%ld, %ld)", 
1021                (long int)max
.x
, (long int) max
.y
)); 
1023    // in the absence of scrollbars we should compare with the client size 
1024    if ( !m_hasHScrollbar 
) 
1025       m_maxx 
= size
.x
;// - WXLO_ROFFSET; 
1026    if ( !m_hasVScrollbar 
) 
1027       m_maxy 
= size
.y
;// - WXLO_BOFFSET; 
1029    // check if the text hasn't become too big 
1030    // TODO why do we set both at once? they're independent... 
1031    if( max
.x 
> m_maxx 
- WXLO_ROFFSET 
|| max
.y 
> m_maxy 
- WXLO_BOFFSET 
|| exact 
) 
1033       // text became too large 
1036          // add an extra bit to the sizes to avoid future updates 
1037          max
.x 
+= WXLO_ROFFSET
; 
1038          max
.y 
+= WXLO_BOFFSET
; 
1041       ViewStart(&m_ViewStartX
, &m_ViewStartY
); 
1042       SetScrollbars(X_SCROLL_PAGE
, Y_SCROLL_PAGE
, 
1043                     max
.x 
/ X_SCROLL_PAGE 
+ 1, max
.y 
/ Y_SCROLL_PAGE 
+ 1, 
1044                     m_ViewStartX
, m_ViewStartY
, 
1048       m_hasVScrollbar 
= true; 
1050       m_maxx 
= max
.x 
+ X_SCROLL_PAGE
; 
1051       m_maxy 
= max
.y 
+ Y_SCROLL_PAGE
; 
1054    //FIXME: this code is pretty broken, producing "arithmetic 
1055    //exception" crashes (div by 0??) 
1058       // check if the window hasn't become too big, thus making the scrollbars 
1062          // add an extra bit to the sizes to avoid future updates 
1063          max
.x 
-= WXLO_ROFFSET
; 
1064          max
.y 
-= WXLO_BOFFSET
; 
1067       if ( m_hasHScrollbar 
&& (max
.x 
< m_maxx
) ) 
1069          // remove the horizontal scrollbar 
1070          SetScrollbars(0, -1, 0, -1, 0, -1, true); 
1071          m_hasHScrollbar 
= false; 
1074       if ( m_hasVScrollbar 
&& (max
.y 
< m_maxy
) ) 
1076          // remove the vertical scrollbar 
1077          SetScrollbars(-1, 0, -1, 0, -1, 0, true); 
1078          m_hasVScrollbar 
= false; 
1084 // ---------------------------------------------------------------------------- 
1086 // clipboard operations 
1088 // ---------------------------------------------------------------------------- 
1091 wxLayoutWindow::Paste(bool primary
) 
1094    if (wxTheClipboard
->Open()) 
1098          wxTheClipboard
->UsePrimarySelection(); 
1100 #if wxUSE_PRIVATE_CLIPBOARD_FORMAT 
1101       wxLayoutDataObject wxldo
; 
1102       if (wxTheClipboard
->IsSupported( wxldo
.GetFormat() )) 
1104          wxTheClipboard
->GetData(wxldo
); 
1107          //FIXME: missing functionality  m_llist->Insert(wxldo.GetList()); 
1112          wxTextDataObject data
; 
1113          if (wxTheClipboard
->IsSupported( data
.GetFormat() ) 
1114              && wxTheClipboard
->GetData(data
) ) 
1116             wxString text 
= data
.GetText(); 
1117             wxLayoutImportText( m_llist
, text
); 
1121       wxTheClipboard
->Close(); 
1126 wxLayoutWindow::Copy(bool invalidate
) 
1128    // Calling GetSelection() will automatically do an EndSelection() 
1129    // on the list, but we need to take a note of it, too: 
1132       m_Selecting 
= false; 
1133       m_llist
->EndSelection(); 
1136    wxLayoutDataObject wldo
; 
1137    wxLayoutList 
*llist 
= m_llist
->GetSelection(&wldo
, invalidate
); 
1140    // Export selection as text: 
1142    wxLayoutExportObject 
*exp
; 
1143    wxLayoutExportStatus 
status(llist
); 
1144    while((exp 
= wxLayoutExport( &status
, WXLO_EXPORT_AS_TEXT
)) != NULL
) 
1146       if(exp
->type 
== WXLO_EXPORT_TEXT
) 
1147          text 
<< *(exp
->content
.text
); 
1152    // The exporter always appends a newline, so we chop it off if it 
1155       size_t len 
= text
.Length(); 
1156       if(len 
> 2 && text
[len
-2] ==  '\r') // Windows 
1157          text 
= text
.Mid(0,len
-2); 
1158       else if(len 
> 1 && text
[len
-1] == '\n') 
1159          text 
= text
.Mid(0,len
-1); 
1162    if (wxTheClipboard
->Open()) 
1164       wxTextDataObject 
*data 
= new wxTextDataObject( text 
); 
1165       bool  rc 
= wxTheClipboard
->SetData( data 
); 
1166 #if wxUSE_PRIVATE_CLIPBOARD_FORMAT 
1167       rc 
|= wxTheClipboard
->AddData( &wldo 
); 
1169       wxTheClipboard
->Close(); 
1177 wxLayoutWindow::Cut(void) 
1179    if(Copy(false)) // do not invalidate selection after copy 
1181       m_llist
->DeleteSelection(); 
1189 // ---------------------------------------------------------------------------- 
1191 // ---------------------------------------------------------------------------- 
1194 wxLayoutWindow::Find(const wxString 
&needle
, 
1195                      wxPoint 
* fromWhere
, 
1196                      const wxString 
&configPath
) 
1201    if(needle
.Length() == 0) 
1203       if( ! MInputBox(&m_FindString
, 
1208           || strutil_isempty(m_FindString
)) 
1212       m_FindString 
= needle
; 
1214    if(fromWhere 
== NULL
) 
1215       found 
= m_llist
->FindText(m_FindString
, m_llist
->GetCursorPos()); 
1217       found 
= m_llist
->FindText(m_FindString
, *fromWhere
); 
1225       m_llist
->MoveCursorTo(found
); 
1236 wxLayoutWindow::FindAgain(void) 
1238    bool rc 
= Find(m_FindString
); 
1242 // ---------------------------------------------------------------------------- 
1244 // ---------------------------------------------------------------------------- 
1247 wxLayoutWindow::MakeFormatMenu() 
1249    wxMenu 
*m 
= new wxMenu(_("Layout Menu")); 
1251    m
->Append(WXLOWIN_MENU_LARGER   
,_("&Larger"),_("Switch to larger font."), false); 
1252    m
->Append(WXLOWIN_MENU_SMALLER  
,_("&Smaller"),_("Switch to smaller font."), false); 
1253    m
->AppendSeparator(); 
1254    m
->Append(WXLOWIN_MENU_UNDERLINE
, _("&Underline"),_("Underline mode."), true); 
1255    m
->Append(WXLOWIN_MENU_BOLD
, _("&Bold"),_("Bold mode."), true); 
1256    m
->Append(WXLOWIN_MENU_ITALICS
, _("&Italics"),_("Italics mode."), true); 
1257    m
->AppendSeparator(); 
1258    m
->Append(WXLOWIN_MENU_ROMAN     
,_("&Roman"),_("Switch to roman font."), false); 
1259    m
->Append(WXLOWIN_MENU_TYPEWRITER
,_("&Typewriter"),_("Switch to typewriter font."), false); 
1260    m
->Append(WXLOWIN_MENU_SANSSERIF 
,_("&Sans Serif"),_("Switch to sans serif font."), false); 
1265 void wxLayoutWindow::OnUpdateMenuUnderline(wxUpdateUIEvent
& event
) 
1267    event
.Check(m_llist
->IsFontUnderlined()); 
1270 void wxLayoutWindow::OnUpdateMenuBold(wxUpdateUIEvent
& event
) 
1272    event
.Check(m_llist
->IsFontBold()); 
1275 void wxLayoutWindow::OnUpdateMenuItalic(wxUpdateUIEvent
& event
) 
1277    event
.Check(m_llist
->IsFontItalic()); 
1280 void wxLayoutWindow::OnMenu(wxCommandEvent
& event
) 
1282    switch (event
.GetId()) 
1284    case WXLOWIN_MENU_LARGER
: 
1285       m_llist
->SetFontLarger(); RequestUpdate(); break; 
1286    case WXLOWIN_MENU_SMALLER
: 
1287       m_llist
->SetFontSmaller(); RequestUpdate(); break; 
1288    case WXLOWIN_MENU_UNDERLINE
: 
1289       m_llist
->ToggleFontUnderline(); RequestUpdate(); break; 
1290    case WXLOWIN_MENU_BOLD
: 
1291       m_llist
->ToggleFontWeight(); RequestUpdate(); break; 
1292    case WXLOWIN_MENU_ITALICS
: 
1293       m_llist
->ToggleFontItalics(); RequestUpdate(); break; 
1294    case WXLOWIN_MENU_ROMAN
: 
1295       m_llist
->SetFontFamily(wxROMAN
); RequestUpdate(); break; 
1296    case WXLOWIN_MENU_TYPEWRITER
: 
1297       m_llist
->SetFontFamily(wxFIXED
); RequestUpdate(); break; 
1298    case WXLOWIN_MENU_SANSSERIF
: 
1299       m_llist
->SetFontFamily(wxSWISS
); RequestUpdate(); break; 
1303 // ---------------------------------------------------------------------------- 
1305 // ---------------------------------------------------------------------------- 
1308 wxLayoutWindow::OnSetFocus(wxFocusEvent 
&ev
) 
1312    RequestUpdate(); // cursor must change 
1316 wxLayoutWindow::OnKillFocus(wxFocusEvent 
&ev
) 
1318    m_HaveFocus 
= false; 
1320    RequestUpdate();// cursor must change 
1323 // ---------------------------------------------------------------------------- 
1324 // private functions 
1325 // ---------------------------------------------------------------------------- 
1327 static bool IsDirectionKey(long keyCode
)