1 /*-*- c++ -*-******************************************************** 
   2  * wxLwindow.h : a scrolled Window for displaying/entering rich text* 
   4  * (C) 1998, 1999 by Karsten Ballüder (Ballueder@usa.net)           *  
   7  *******************************************************************/ 
  10 #   pragma implementation "wxlwindow.h" 
  13 #include "wx/wxprec.h" 
  23 #     include "gui/wxMenuDefs.h" 
  24 #     include "gui/wxMApp.h" 
  26 #   include "gui/wxlwindow.h" 
  27 #   include "gui/wxlparser.h" 
  36 #   include "wxlwindow.h" 
  37 #   include "wxlparser.h" 
  40 #include <wx/clipbrd.h> 
  41 #include <wx/textctrl.h> 
  42 #include <wx/dataobj.h> 
  47 #  define   WXLO_DEBUG(x)      wxLogDebug x 
  49 #  define WXLO_DEBUG(x) 
  52 /// offsets to put a nice frame around text 
  53 #define WXLO_XOFFSET   4 
  54 #define WXLO_YOFFSET   4 
  56 /// offset to the right and bottom for when to redraw scrollbars 
  57 #define   WXLO_ROFFSET   20 
  58 #define   WXLO_BOFFSET   20 
  60 BEGIN_EVENT_TABLE(wxLayoutWindow
,wxScrolledWindow
) 
  61    EVT_PAINT    (wxLayoutWindow::OnPaint
) 
  62    EVT_CHAR     (wxLayoutWindow::OnChar
) 
  63    EVT_KEY_UP   (wxLayoutWindow::OnKeyUp
) 
  64    EVT_LEFT_DOWN(wxLayoutWindow::OnLeftMouseClick
) 
  65    EVT_RIGHT_DOWN(wxLayoutWindow::OnRightMouseClick
) 
  66    EVT_LEFT_DCLICK(wxLayoutWindow::OnMouseDblClick
) 
  67    EVT_MOTION    (wxLayoutWindow::OnMouseMove
) 
  68    EVT_MENU_RANGE(WXLOWIN_MENU_FIRST
, WXLOWIN_MENU_LAST
, wxLayoutWindow::OnMenu
) 
  69    EVT_SET_FOCUS(wxLayoutWindow::OnSetFocus
) 
  70    EVT_KILL_FOCUS(wxLayoutWindow::OnKillFocus
) 
  73 wxLayoutWindow::wxLayoutWindow(wxWindow 
*parent
) 
  74    : wxScrolledWindow(parent
, -1, wxDefaultPosition
, wxDefaultSize
, 
  75                       wxHSCROLL 
| wxVSCROLL 
| wxBORDER
) 
  79    m_doSendEvents 
= false; 
  80    m_ViewStartX 
= 0; m_ViewStartY 
= 0; 
  82    m_PopupMenu 
= MakeFormatMenu(); 
  83    m_memDC 
= new wxMemoryDC
; 
  84    m_bitmap 
= new wxBitmap(4,4); 
  85    m_bitmapSize 
= wxPoint(4,4); 
  86    m_llist 
= new wxLayoutList(); 
  88    m_ScrollToCursor 
= false; 
  90    wxPoint max 
= m_llist
->GetSize(); 
  91    SetScrollbars(10, 20 /*lineHeight*/, max
.x
/10+1, max
.y
/20+1); 
  92    EnableScrolling(true,true); 
  93    m_maxx 
= max
.x
; m_maxy 
= max
.y
; 
  95    SetCursor(wxCURSOR_IBEAM
); 
  99 wxLayoutWindow::~wxLayoutWindow() 
 101    delete m_memDC
; // deletes bitmap automatically (?) 
 105    SetBackgroundBitmap(NULL
); 
 109 wxLayoutWindow::Clear(int family
, 
 117    GetLayoutList()->Clear(family
,size
,style
,weight
,underline
,fg
,bg
); 
 118    SetBackgroundColour(GetLayoutList()->GetDefaults()->GetBGColour()); 
 119    ResizeScrollbars(true); 
 124    r
.x 
= r
.y 
= 0; GetSize(&w
,&h
); 
 132 wxLayoutWindow::MSWGetDlgCode() 
 134    // if we don't return this, we won't get OnChar() events for TABs and ENTER 
 135    return DLGC_WANTCHARS 
| DLGC_WANTARROWS 
| DLGC_WANTMESSAGE
; 
 140 wxLayoutWindow::OnMouse(int eventId
, wxMouseEvent
& event
) 
 142    wxPaintDC 
dc( this ); 
 147    findPos
.x 
= dc
.DeviceToLogicalX(event
.GetX()); 
 148    findPos
.y 
= dc
.DeviceToLogicalY(event
.GetY()); 
 150    findPos
.x 
-= WXLO_XOFFSET
; 
 151    findPos
.y 
-= WXLO_YOFFSET
; 
 153    if(findPos
.x 
< 0) findPos
.x 
= 0; 
 154    if(findPos
.y 
< 0) findPos
.y 
= 0; 
 156    m_ClickPosition 
= wxPoint(event
.GetX(), event
.GetY()); 
 160    wxLayoutObject 
*obj 
= m_llist
->FindObjectScreen(dc
, findPos
, 
 163    //has the mouse only been moved? 
 164    if(eventId 
== WXLOWIN_MENU_MOUSEMOVE
) 
 166       // found is only true if we are really over an object, not just 
 168       if(found 
&& obj 
&& obj
->GetUserData() != NULL
) 
 171             SetCursor(wxCURSOR_HAND
); 
 177             SetCursor(wxCURSOR_IBEAM
); 
 178          m_HandCursor 
= FALSE
; 
 183    // always move cursor to mouse click: 
 184    if(obj 
&& eventId 
== WXLOWIN_MENU_LCLICK
) 
 186       m_llist
->MoveCursorTo(cursorPos
); 
 188       Refresh(FALSE
); // DoPaint suppresses flicker under GTK 
 190    if(!m_doSendEvents
) // nothing to do 
 193    // only do the menu if activated, editable and not on a clickable object 
 194    if(eventId 
== WXLOWIN_MENU_RCLICK
 
 196       && (! obj 
|| (obj 
&& obj
->GetUserData() == NULL
)) 
 199       PopupMenu(m_PopupMenu
, m_ClickPosition
.x
, m_ClickPosition
.y
); 
 202    // find the object at this position 
 205       wxCommandEvent 
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, eventId
); 
 206       commandEvent
.SetEventObject( this ); 
 207       commandEvent
.SetClientData((char *)obj
); 
 208       GetEventHandler()->ProcessEvent(commandEvent
); 
 213  * Some simple keyboard handling. 
 216 wxLayoutWindow::OnChar(wxKeyEvent
& event
) 
 218    int keyCode 
= event
.KeyCode(); 
 220 #ifdef WXLAYOUT_DEBUG 
 221    if(keyCode 
== WXK_F1
) 
 228    if(! m_Selecting 
&& event
.ShiftDown()) 
 241          m_llist
->StartSelection(); 
 248    /* These two nested switches work like this: 
 249       The first one processes all non-editing keycodes, to move the 
 250       cursor, etc. It's default will process all keycodes causing 
 251       modifications to the buffer, but only if editing is allowed. 
 256       m_llist
->MoveCursorHorizontally(1); 
 259       m_llist
->MoveCursorHorizontally(-1); 
 262       m_llist
->MoveCursorVertically(-1); 
 265       m_llist
->MoveCursorVertically(1); 
 268       m_llist
->MoveCursorVertically(-20); 
 271       m_llist
->MoveCursorVertically(20); 
 274       m_llist
->MoveCursorToBeginOfLine(); 
 277       m_llist
->MoveCursorToEndOfLine(); 
 280       if(keyCode 
== 'c' && event
.ControlDown()) 
 284          /* First, handle control keys */ 
 285          if(event
.ControlDown() && ! event
.AltDown()) 
 294                m_llist
->DeleteLines(1); 
 296             case 'h': // like backspace 
 297                if(m_llist
->MoveCursorHorizontally(-1)) m_llist
->Delete(1); 
 300                m_llist
->DeleteToBeginOfLine(); 
 303                m_llist
->DeleteToEndOfLine(); 
 308 #ifdef WXLAYOUT_DEBUG 
 310                m_llist
->SetFont(-1,-1,-1,-1,true);  // underlined 
 318          else if( event
.AltDown() && ! event
.ControlDown() ) 
 324                m_llist
->DeleteWord(); 
 331          else if ( ! event
.AltDown() && ! event
.ControlDown()) 
 336                if(event
.ShiftDown()) 
 342             case WXK_BACK
: // backspace 
 343                if(m_llist
->MoveCursorHorizontally(-1)) m_llist
->Delete(1); 
 347                   m_llist
->WrapLine(m_WrapMargin
); 
 348                m_llist
->LineBreak(); 
 351                if((!(event
.ControlDown() || event
.AltDown() || event
.MetaDown())) 
 352                   && (keyCode 
< 256 && keyCode 
>= 32) 
 357                   if(m_WrapMargin 
> 0 && isspace(keyCode
)) 
 358                      m_llist
->WrapLine(m_WrapMargin
); 
 359                   m_llist
->Insert(tmp
); 
 370       if(event
.ShiftDown()) 
 371          m_llist
->ContinueSelection(); 
 374          m_llist
->EndSelection(); 
 380    wxRect r 
= *m_llist
->GetUpdateRect(); 
 385 wxLayoutWindow::OnKeyUp(wxKeyEvent
& event
) 
 387    if(event
.KeyCode() == WXK_SHIFT 
&& m_llist
->IsSelecting()) 
 388       m_llist
->EndSelection(); 
 394 wxLayoutWindow::ScrollToCursor(void) 
 396    wxClientDC 
dc( this ); 
 399    int x0
,y0
,x1
,y1
, dx
, dy
; 
 401    // Calculate where the top of the visible area is: 
 403    GetScrollPixelsPerUnit(&dx
, &dy
); 
 406    WXLO_DEBUG(("ScrollToCursor: ViewStart is %d/%d", x0
, y0
)); 
 408    // Get the size of the visible window: 
 409    GetClientSize(&x1
,&y1
); 
 412    // As we have the values anyway, use them to avoid unnecessary 
 413    // scrollbar updates. 
 414    if(x1 
> m_maxx
) m_maxx 
= x1
;   
 415    if(y1 
> m_maxy
) m_maxy 
= y1
; 
 416    /* Make sure that the scrollbars are at a position so that the 
 417       cursor is visible if we are editing. */ 
 418       /** Scroll so that cursor is visible! */ 
 419    WXLO_DEBUG(("m_ScrollToCursor = %d", (int) m_ScrollToCursor
)); 
 420    wxPoint cc 
= m_llist
->GetCursorScreenPos(*m_memDC
); 
 421    if(cc
.x 
< x0 
|| cc
.y 
< y0
 
 422       || cc
.x 
>= x0
+(9*x1
)/10 || cc
.y 
>= y0
+(9*y1
/10))  // (9*x)/10 ==  90% 
 425       nx 
= cc
.x 
- x1
/2; if(nx 
< 0) nx 
= 0; 
 426       ny 
= cc
.y 
- y1
/2; if(ny 
< 0) ny 
= 0; 
 427       Scroll(nx
/dx
,ny
/dy
); // new view start 
 429       m_ScrollToCursor 
= false; // avoid recursion 
 434 wxLayoutWindow::OnPaint( wxPaintEvent 
&WXUNUSED(event
))  // or: OnDraw(wxDC& dc) 
 436    wxRect region 
= GetUpdateRegion().GetBox(); 
 437    InternalPaint(& region
); 
 441 wxLayoutWindow::DoPaint(const wxRect 
*updateRect
) 
 444    InternalPaint(updateRect
); 
 446    Refresh(FALSE
, updateRect
); // Causes bad flicker under wxGTK!!! 
 451 wxLayoutWindow::InternalPaint(const wxRect 
*updateRect
) 
 453    wxPaintDC 
dc( this ); 
 456    int x0
,y0
,x1
,y1
, dx
, dy
; 
 458    // Calculate where the top of the visible area is: 
 460    GetScrollPixelsPerUnit(&dx
, &dy
); 
 463    // Get the size of the visible window: 
 464    GetClientSize(&x1
,&y1
); 
 467    // As we have the values anyway, use them to avoid unnecessary 
 468    // scrollbar updates. 
 469    if(x1 
> m_maxx
) m_maxx 
= x1
;   
 470    if(y1 
> m_maxy
) m_maxy 
= y1
; 
 472    WXLO_DEBUG(("Update rect: %ld,%ld / %ld,%ld", 
 473                updateRect
->x
, updateRect
->y
, 
 474                updateRect
->x
+updateRect
->width
, 
 475                updateRect
->y
+updateRect
->height
)); 
 482    /* Check whether the window has grown, if so, we need to reallocate  
 483       the bitmap to be larger. */ 
 484    if(x1 
> m_bitmapSize
.x 
|| y1 
> m_bitmapSize
.y
) 
 486       wxASSERT(m_bitmapSize
.x 
> 0); 
 487       wxASSERT(m_bitmapSize
.y 
> 0); 
 489       m_memDC
->SelectObject(wxNullBitmap
); 
 491       m_bitmapSize 
= wxPoint(x1
,y1
); 
 492       m_bitmap 
= new wxBitmap(x1
,y1
); 
 493       m_memDC
->SelectObject(*m_bitmap
); 
 496    m_memDC
->SetDeviceOrigin(0,0); 
 497    m_memDC
->SetBrush(wxBrush(m_llist
->GetDefaults()->GetBGColour(),wxSOLID
)); 
 498    m_memDC
->SetPen(wxPen(m_llist
->GetDefaults()->GetBGColour(), 
 500    m_memDC
->SetLogicalFunction(wxCOPY
); 
 502    /* Either fill the background with the background bitmap, or clear 
 508          w 
= m_BGbitmap
->GetWidth(), 
 509          h 
= m_BGbitmap
->GetHeight(); 
 510       for(y 
= 0; y 
< y1
; y
+=h
) 
 511          for(x 
= 0; x 
< x1
; x
+=w
) 
 512             m_memDC
->DrawBitmap(*m_BGbitmap
, x
, y
); 
 513       m_memDC
->SetBackgroundMode(wxTRANSPARENT
); 
 517       // clear the background: (must not be done if we use the update rectangle!) 
 518       m_memDC
->SetBackgroundMode(wxSOLID
); 
 519       m_memDC
->DrawRectangle(0,0,x1
, y1
); 
 523    /* This is the important bit: we tell the list to draw itself: */ 
 525    WXLO_DEBUG(("Update rect: %ld,%ld / %ld,%ld", 
 526                updateRect
->x
, updateRect
->y
, 
 527                updateRect
->x
+updateRect
->width
, 
 528                updateRect
->y
+updateRect
->height
));  
 531    // Device origins on the memDC are suspect, we translate manually 
 532    // with the translate parameter of Draw(). 
 533    wxPoint 
offset(-x0
+WXLO_XOFFSET
,-y0
+WXLO_YOFFSET
); 
 534    m_llist
->Draw(*m_memDC
,offset
, y0
, y0
+y1
); 
 536    // We start calculating a new update rect before drawing the 
 537    // cursor, so that the cursor coordinates get included in the next 
 538    // update rectangle (although they are drawn on the memDC, this is 
 539    // needed to erase it): 
 540    m_llist
->InvalidateUpdateRect();  
 541    m_llist
->DrawCursor(*m_memDC
, 
 542                        m_HaveFocus 
&& IsEditable(), // draw a thick 
 543                        // cursor for editable windows with focus 
 546 // Now copy everything to the screen: 
 548    // This somehow doesn't work, but even the following bit with the 
 549    // whole rect at once is still a bit broken I think. 
 550    wxRegionIterator 
ri ( GetUpdateRegion() ); 
 554          WXLO_DEBUG(("UpdateRegion: %ld,%ld, %ld,%ld", 
 555                      ri
.GetX(),ri
.GetY(),ri
.GetW(),ri
.GetH())); 
 556          dc
.Blit(x0
+ri
.GetX(),y0
+ri
.GetY(),ri
.GetW(),ri
.GetH(), 
 557                  m_memDC
,ri
.GetX(),ri
.GetY(),wxCOPY
,FALSE
); 
 563       // FIXME: Trying to copy only the changed parts, but it does not seem 
 565 //      x0 = updateRect->x; y0 = updateRect->y; 
 566 //      if(updateRect->height < y1) 
 567 //         y1 = updateRect->height; 
 568 //      y1 += WXLO_YOFFSET; //FIXME might not be needed 
 569       dc
.Blit(x0
,y0
,x1
,y1
,m_memDC
,0,0,wxCOPY
,FALSE
); 
 573    m_ScrollToCursor 
= false; 
 576 // change the range and position of scrollbars 
 578 wxLayoutWindow::ResizeScrollbars(bool exact
) 
 580    wxPoint max 
= m_llist
->GetSize(); 
 582    WXLO_DEBUG(("ResizeScrollbars: GetSize: %ld, %ld", (long int)max
.x
,  
 584    if(max
.x 
> m_maxx 
|| max
.y 
> m_maxy
 
 585       || max
.x 
> m_maxx
-WXLO_ROFFSET 
|| max
.y 
> m_maxy
-WXLO_BOFFSET
 
 590          // add an extra bit to the sizes to avoid future updates 
 591          max
.x 
= max
.x
+WXLO_ROFFSET
;   
 592          max
.y 
= max
.y
+WXLO_BOFFSET
; 
 594       ViewStart(&m_ViewStartX
, &m_ViewStartY
); 
 595       SetScrollbars(10, 20, max
.x
/10+1,max
.y
/20+1,m_ViewStartX
,m_ViewStartY
,true); 
 596       m_maxx 
= max
.x
; m_maxy 
= max
.y
; 
 601 wxLayoutWindow::Paste(void) 
 605    if (wxTheClipboard
->Open()) 
 607       wxTextDataObject data
; 
 608       if (wxTheClipboard
->IsSupported( data
.GetFormat() )) 
 610          wxTheClipboard
->GetData(&data
); 
 611          text 
+= data
.GetText(); 
 613       wxTheClipboard
->Close(); 
 616    /* My attempt to get the primary selection, but it does not 
 618    if(text
.Length() == 0) 
 620       wxTextCtrl 
tmp_tctrl(this,-1); 
 622       text 
+= tmp_tctrl
.GetValue(); 
 625    wxLayoutImportText( m_llist
, text
); 
 629 wxLayoutWindow::Copy(void) 
 631    // Calling GetSelection() will automatically do an EndSelection() 
 632    // on the list, but we need to take a note of it, too: 
 636       m_llist
->EndSelection(); 
 638    wxLayoutList 
*llist 
= m_llist
->GetSelection(); 
 643    wxLayoutExportObject 
*export
; 
 644    wxLayoutExportStatus 
status(llist
); 
 645    while((export 
= wxLayoutExport( &status
, WXLO_EXPORT_AS_TEXT
)) != NULL
) 
 647       if(export
->type 
== WXLO_EXPORT_TEXT
) 
 648          text 
<< *(export
->content
.text
); 
 653    // The exporter always appends a newline, so we chop it off if it 
 656       size_t len 
= text
.Length(); 
 657       if(len 
> 2 && text
[len
-2] ==  '\r') // Windows 
 658          text 
= text
.Mid(0,len
-2); 
 659       else if(len 
> 1 && text
[len
-1] == '\n') 
 660          text 
= text
.Mid(0,len
-1); 
 664    if (wxTheClipboard
->Open()) 
 666       wxTextDataObject 
*data 
= new wxTextDataObject( text 
); 
 667       bool  rc 
= wxTheClipboard
->SetData( data 
); 
 668       wxTheClipboard
->Close(); 
 675 wxLayoutWindow::Cut(void) 
 679       m_llist
->DeleteSelection(); 
 686 wxLayoutWindow::Find(const wxString 
&needle
, 
 691    if(fromWhere 
== NULL
) 
 692       found 
= m_llist
->FindText(needle
, m_llist
->GetCursorPos()); 
 694       found 
= m_llist
->FindText(needle
, *fromWhere
); 
 702       m_llist
->MoveCursorTo(found
); 
 710 wxLayoutWindow::MakeFormatMenu() 
 712    wxMenu 
*m 
= new wxMenu(_("Layout Menu")); 
 714    m
->Append(WXLOWIN_MENU_LARGER   
,_("&Larger"),_("Switch to larger font."), false); 
 715    m
->Append(WXLOWIN_MENU_SMALLER  
,_("&Smaller"),_("Switch to smaller font."), false); 
 716    m
->AppendSeparator(); 
 717    m
->Append(WXLOWIN_MENU_UNDERLINE_ON
, _("&Underline on"),_("Activate underline mode."), false); 
 718    m
->Append(WXLOWIN_MENU_UNDERLINE_OFF
,_("&Underline off"),_("Deactivate underline mode."), false); 
 719    m
->Append(WXLOWIN_MENU_BOLD_ON      
,_("&Bold on"),_("Activate bold mode."), false); 
 720    m
->Append(WXLOWIN_MENU_BOLD_OFF     
,_("&Bold off"),_("Deactivate bold mode."), false); 
 721    m
->Append(WXLOWIN_MENU_ITALICS_ON   
,_("&Italics on"),_("Activate italics mode."), false); 
 722    m
->Append(WXLOWIN_MENU_ITALICS_OFF  
,_("&Italics off"),_("Deactivate italics mode."), false); 
 723    m
->AppendSeparator(); 
 724    m
->Append(WXLOWIN_MENU_ROMAN     
,_("&Roman"),_("Switch to roman font."), false); 
 725    m
->Append(WXLOWIN_MENU_TYPEWRITER
,_("&Typewriter"),_("Switch to typewriter font."), false); 
 726    m
->Append(WXLOWIN_MENU_SANSSERIF 
,_("&Sans Serif"),_("Switch to sans serif font."), false); 
 730 void wxLayoutWindow::OnMenu(wxCommandEvent
& event
) 
 732    switch (event
.GetId()) 
 734    case WXLOWIN_MENU_LARGER
: 
 735       m_llist
->SetFontLarger(); break; 
 736    case WXLOWIN_MENU_SMALLER
: 
 737       m_llist
->SetFontSmaller(); break; 
 738    case WXLOWIN_MENU_UNDERLINE_ON
: 
 739       m_llist
->SetFontUnderline(true); break; 
 740    case WXLOWIN_MENU_UNDERLINE_OFF
: 
 741       m_llist
->SetFontUnderline(false); break; 
 742    case WXLOWIN_MENU_BOLD_ON
: 
 743       m_llist
->SetFontWeight(wxBOLD
); break; 
 744    case WXLOWIN_MENU_BOLD_OFF
: 
 745       m_llist
->SetFontWeight(wxNORMAL
); break; 
 746    case WXLOWIN_MENU_ITALICS_ON
: 
 747       m_llist
->SetFontStyle(wxITALIC
); break; 
 748    case WXLOWIN_MENU_ITALICS_OFF
: 
 749       m_llist
->SetFontStyle(wxNORMAL
); break; 
 750    case WXLOWIN_MENU_ROMAN
: 
 751       m_llist
->SetFontFamily(wxROMAN
); break; 
 752    case WXLOWIN_MENU_TYPEWRITER
: 
 753       m_llist
->SetFontFamily(wxFIXED
); break; 
 754    case WXLOWIN_MENU_SANSSERIF
: 
 755       m_llist
->SetFontFamily(wxSWISS
); break; 
 760 wxLayoutWindow::OnSetFocus(wxFocusEvent 
&ev
) 
 763 //FIXME: need argument   DoPaint(); // to repaint the cursor 
 767 wxLayoutWindow::OnKillFocus(wxFocusEvent 
&ev
) 
 770 //FIXME: need argument   DoPaint(); // to repaint the cursor