1///////////////////////////////////////////////////////////////////////////// 2// Name: src/richtext/richeditctrl.cpp 3// Purpose: A rich edit control 4// Author: Julian Smart 5// Modified by: 6// Created: 2005-09-30 7// RCS-ID: $Id: richtextctrl.cpp 67025 2011-02-25 17:28:13Z JS $ 8// Copyright: (c) Julian Smart 9// Licence: wxWindows licence 10///////////////////////////////////////////////////////////////////////////// 11 12// For compilers that support precompilation, includes "wx.h". 13#include "wx/wxprec.h" 14 15#ifdef __BORLANDC__ 16 #pragma hdrstop 17#endif 18 19#if wxUSE_RICHTEXT 20 21#include "wx/richtext/richtextctrl.h" 22#include "wx/richtext/richtextstyles.h" 23 24#ifndef WX_PRECOMP 25 #include "wx/wx.h" 26 #include "wx/settings.h" 27#endif 28 29#include "wx/timer.h" 30#include "wx/textfile.h" 31#include "wx/ffile.h" 32#include "wx/filename.h" 33#include "wx/dcbuffer.h" 34#include "wx/arrimpl.cpp" 35#include "wx/fontenum.h" 36#include "wx/accel.h" 37 38// DLL options compatibility check: 39#include "wx/app.h" 40 41// Refresh the area affected by a selection change 42bool wxRichTextCtrlRefreshForSelectionChange(wxRichTextCtrl& ctrl, const wxRichTextRange& oldSelection, const wxRichTextRange& newSelection); 43 44WX_CHECK_BUILD_OPTIONS("wxRichTextCtrl") 45 46DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK) 47DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_MIDDLE_CLICK) 48DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RIGHT_CLICK) 49DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_DCLICK) 50DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RETURN) 51DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CHARACTER) 52DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_DELETE) 53 54DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING) 55DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED) 56DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_CHANGING) 57DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLESHEET_CHANGED) 58 59DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED) 60DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED) 61DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED) 62DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_SELECTION_CHANGED) 63DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET) 64 65#if wxRICHTEXT_USE_OWN_CARET 66 67class wxRichTextCaret; 68class wxRichTextCaretTimer: public wxTimer 69{ 70 public: 71 wxRichTextCaretTimer(wxRichTextCaret* caret) 72 { 73 m_caret = caret; 74 } 75 virtual void Notify(); 76 wxRichTextCaret* m_caret; 77}; 78 79/*! 80 * wxRichTextCaret 81 * 82 * This implements a non-flashing cursor in case there 83 * are platform-specific problems with the generic caret. 84 * wxRICHTEXT_USE_OWN_CARET is set in richtextbuffer.h. 85 */ 86 87class wxRichTextCaret: public wxCaret 88{ 89public: 90 // ctors 91 // ----- 92 // default - use Create() 93 wxRichTextCaret(): m_timer(this) { Init(); } 94 // creates a block caret associated with the given window 95 wxRichTextCaret(wxRichTextCtrl *window, int width, int height) 96 : wxCaret(window, width, height), m_timer(this) { Init(); m_richTextCtrl = window; } 97 wxRichTextCaret(wxRichTextCtrl *window, const wxSize& size) 98 : wxCaret(window, size), m_timer(this) { Init(); m_richTextCtrl = window; } 99 100 virtual ~wxRichTextCaret(); 101 102 // implementation 103 // -------------- 104 105 // called by wxWindow (not using the event tables) 106 virtual void OnSetFocus(); 107 virtual void OnKillFocus(); 108 109 // draw the caret on the given DC 110 void DoDraw(wxDC *dc); 111 112 // get the visible count 113 int GetVisibleCount() const { return m_countVisible; } 114 115 // delay repositioning 116 bool GetNeedsUpdate() const { return m_needsUpdate; } 117 void SetNeedsUpdate(bool needsUpdate = true ) { m_needsUpdate = needsUpdate; } 118 119 void Notify(); 120 121protected: 122 virtual void DoShow(); 123 virtual void DoHide(); 124 virtual void DoMove(); 125 virtual void DoSize(); 126 127 // refresh the caret 128 void Refresh(); 129 130private: 131 void Init(); 132 133 int m_xOld, 134 m_yOld; 135 bool m_hasFocus; // true => our window has focus 136 bool m_needsUpdate; // must be repositioned 137 bool m_flashOn; 138 wxRichTextCaretTimer m_timer; 139 wxRichTextCtrl* m_richTextCtrl; 140}; 141#endif 142 143IMPLEMENT_CLASS( wxRichTextCtrl, wxTextCtrlBase ) 144 145IMPLEMENT_CLASS( wxRichTextEvent, wxNotifyEvent ) 146 147BEGIN_EVENT_TABLE( wxRichTextCtrl, wxTextCtrlBase ) 148 EVT_PAINT(wxRichTextCtrl::OnPaint) 149 EVT_ERASE_BACKGROUND(wxRichTextCtrl::OnEraseBackground) 150 EVT_IDLE(wxRichTextCtrl::OnIdle) 151 EVT_SCROLLWIN(wxRichTextCtrl::OnScroll) 152 EVT_LEFT_DOWN(wxRichTextCtrl::OnLeftClick) 153 EVT_MOTION(wxRichTextCtrl::OnMoveMouse) 154 EVT_LEFT_UP(wxRichTextCtrl::OnLeftUp) 155 EVT_RIGHT_DOWN(wxRichTextCtrl::OnRightClick) 156 EVT_MIDDLE_DOWN(wxRichTextCtrl::OnMiddleClick) 157 EVT_LEFT_DCLICK(wxRichTextCtrl::OnLeftDClick) 158 EVT_CHAR(wxRichTextCtrl::OnChar) 159 EVT_KEY_DOWN(wxRichTextCtrl::OnChar) 160 EVT_SIZE(wxRichTextCtrl::OnSize) 161 EVT_SET_FOCUS(wxRichTextCtrl::OnSetFocus) 162 EVT_KILL_FOCUS(wxRichTextCtrl::OnKillFocus) 163 EVT_MOUSE_CAPTURE_LOST(wxRichTextCtrl::OnCaptureLost) 164 EVT_CONTEXT_MENU(wxRichTextCtrl::OnContextMenu) 165 166 EVT_MENU(wxID_UNDO, wxRichTextCtrl::OnUndo) 167 EVT_UPDATE_UI(wxID_UNDO, wxRichTextCtrl::OnUpdateUndo) 168 169 EVT_MENU(wxID_REDO, wxRichTextCtrl::OnRedo) 170 EVT_UPDATE_UI(wxID_REDO, wxRichTextCtrl::OnUpdateRedo) 171 172 EVT_MENU(wxID_COPY, wxRichTextCtrl::OnCopy) 173 EVT_UPDATE_UI(wxID_COPY, wxRichTextCtrl::OnUpdateCopy) 174 175 EVT_MENU(wxID_PASTE, wxRichTextCtrl::OnPaste) 176 EVT_UPDATE_UI(wxID_PASTE, wxRichTextCtrl::OnUpdatePaste) 177 178 EVT_MENU(wxID_CUT, wxRichTextCtrl::OnCut) 179 EVT_UPDATE_UI(wxID_CUT, wxRichTextCtrl::OnUpdateCut) 180 181 EVT_MENU(wxID_CLEAR, wxRichTextCtrl::OnClear) 182 EVT_UPDATE_UI(wxID_CLEAR, wxRichTextCtrl::OnUpdateClear) 183 184 EVT_MENU(wxID_SELECTALL, wxRichTextCtrl::OnSelectAll) 185 EVT_UPDATE_UI(wxID_SELECTALL, wxRichTextCtrl::OnUpdateSelectAll) 186END_EVENT_TABLE() 187 188/*! 189 * wxRichTextCtrl 190 */ 191 192wxArrayString wxRichTextCtrl::sm_availableFontNames; 193 194wxRichTextCtrl::wxRichTextCtrl() 195 : wxScrollHelper(this) 196{ 197 Init(); 198} 199 200wxRichTextCtrl::wxRichTextCtrl(wxWindow* parent, 201 wxWindowID id, 202 const wxString& value, 203 const wxPoint& pos, 204 const wxSize& size, 205 long style, 206 const wxValidator& validator, 207 const wxString& name) 208 : wxScrollHelper(this) 209{ 210 Init(); 211 Create(parent, id, value, pos, size, style, validator, name); 212} 213 214/// Creation 215bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, long style, 216 const wxValidator& validator, const wxString& name) 217{ 218 if ((style & wxBORDER_MASK) == wxBORDER_DEFAULT) 219#ifdef __WXMSW__ 220 style |= GetThemedBorderStyle(); 221#else 222 style |= wxBORDER_SUNKEN; 223#endif 224 225 if (!wxTextCtrlBase::Create(parent, id, pos, size, 226 style|wxFULL_REPAINT_ON_RESIZE, 227 validator, name)) 228 return false; 229 230 if (!GetFont().Ok()) 231 { 232 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); 233 } 234 235 // No physical scrolling, so we can preserve margins 236 EnableScrolling(false, false); 237 238 if (style & wxTE_READONLY) 239 SetEditable(false); 240 241 // The base attributes must all have default values 242 wxTextAttrEx attributes; 243 attributes.SetFont(GetFont()); 244 attributes.SetTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); 245 attributes.SetAlignment(wxTEXT_ALIGNMENT_LEFT); 246 attributes.SetLineSpacing(10); 247 attributes.SetParagraphSpacingAfter(10); 248 attributes.SetParagraphSpacingBefore(0); 249 250 SetBasicStyle(attributes); 251 252 // The default attributes will be merged with base attributes, so 253 // can be empty to begin with 254 wxTextAttrEx defaultAttributes; 255 SetDefaultStyle(defaultAttributes); 256 257 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); 258 SetBackgroundStyle(wxBG_STYLE_CUSTOM); 259 260 GetBuffer().Reset(); 261 GetBuffer().SetRichTextCtrl(this); 262 263#if wxRICHTEXT_USE_OWN_CARET 264 SetCaret(new wxRichTextCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16)); 265#else 266 SetCaret(new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16)); 267#endif 268 269 // Tell the sizers to use the given or best size 270 SetInitialSize(size); 271 272#if wxRICHTEXT_BUFFERED_PAINTING 273 // Create a buffer 274 RecreateBuffer(size); 275#endif 276 277 m_textCursor = wxCursor(wxCURSOR_IBEAM); 278 m_urlCursor = wxCursor(wxCURSOR_HAND); 279 280 SetCursor(m_textCursor); 281 282 if (!value.IsEmpty()) 283 SetValue(value); 284 285 GetBuffer().AddEventHandler(this); 286 287 // Accelerators 288 wxAcceleratorEntry entries[6]; 289 290 entries[0].Set(wxACCEL_CMD, (int) 'C', wxID_COPY); 291 entries[1].Set(wxACCEL_CMD, (int) 'X', wxID_CUT); 292 entries[2].Set(wxACCEL_CMD, (int) 'V', wxID_PASTE); 293 entries[3].Set(wxACCEL_CMD, (int) 'A', wxID_SELECTALL); 294 entries[4].Set(wxACCEL_CMD, (int) 'Z', wxID_UNDO); 295 entries[5].Set(wxACCEL_CMD, (int) 'Y', wxID_REDO); 296 297 wxAcceleratorTable accel(6, entries); 298 SetAcceleratorTable(accel); 299 300 m_contextMenu = new wxMenu; 301 m_contextMenu->Append(wxID_UNDO, _("&Undo")); 302 m_contextMenu->Append(wxID_REDO, _("&Redo")); 303 m_contextMenu->AppendSeparator(); 304 m_contextMenu->Append(wxID_CUT, _("Cu&t")); 305 m_contextMenu->Append(wxID_COPY, _("&Copy")); 306 m_contextMenu->Append(wxID_PASTE, _("&Paste")); 307 m_contextMenu->Append(wxID_CLEAR, _("&Delete")); 308 m_contextMenu->AppendSeparator(); 309 m_contextMenu->Append(wxID_SELECTALL, _("Select &All")); 310 311 return true; 312} 313 314wxRichTextCtrl::~wxRichTextCtrl() 315{ 316 GetBuffer().RemoveEventHandler(this); 317 318 delete m_contextMenu; 319} 320 321/// Member initialisation 322void wxRichTextCtrl::Init() 323{ 324 m_freezeCount = 0; 325 m_contextMenu = NULL; 326 m_caret = NULL; 327 m_caretPosition = -1; 328 m_selectionRange.SetRange(-2, -2); 329 m_selectionAnchor = -2; 330 m_editable = true; 331 m_caretAtLineStart = false; 332 m_dragging = false; 333 m_fullLayoutRequired = false; 334 m_fullLayoutTime = 0; 335 m_fullLayoutSavedPosition = 0; 336 m_delayedLayoutThreshold = wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD; 337 m_caretPositionForDefaultStyle = -2; 338} 339 340/// Call Freeze to prevent refresh 341void wxRichTextCtrl::Freeze() 342{ 343 m_freezeCount ++; 344} 345 346/// Call Thaw to refresh 347void wxRichTextCtrl::Thaw() 348{ 349 m_freezeCount --; 350 351 if (m_freezeCount == 0) 352 { 353 if (GetBuffer().GetDirty()) 354 LayoutContent(); 355 else 356 SetupScrollbars(); 357 358 Refresh(false); 359 } 360} 361 362/// Clear all text 363void wxRichTextCtrl::Clear() 364{ 365 m_buffer.ResetAndClearCommands(); 366 m_buffer.SetDirty(true); 367 m_caretPosition = -1; 368 m_caretPositionForDefaultStyle = -2; 369 m_caretAtLineStart = false; 370 m_selectionRange.SetRange(-2, -2); 371 372 Scroll(0,0); 373 374 if (m_freezeCount == 0) 375 { 376 LayoutContent(); 377 Refresh(false); 378 } 379 SendTextUpdatedEvent(); 380} 381 382/// Painting 383void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) 384{ 385#if !wxRICHTEXT_USE_OWN_CARET 386 if (GetCaret() && !IsFrozen()) 387 GetCaret()->Hide(); 388#endif 389 390 { 391#if wxRICHTEXT_BUFFERED_PAINTING 392 wxBufferedPaintDC dc(this, m_bufferBitmap); 393#else 394 wxPaintDC dc(this); 395#endif 396 PrepareDC(dc); 397 398 if (IsFrozen()) 399 { 400 return; 401 } 402 403 dc.SetFont(GetFont()); 404 405 // Paint the background 406 PaintBackground(dc); 407 408 // wxRect drawingArea(GetLogicalPoint(wxPoint(0, 0)), GetClientSize()); 409 410 wxRect drawingArea(GetUpdateRegion().GetBox()); 411 drawingArea.SetPosition(GetLogicalPoint(drawingArea.GetPosition())); 412 413 wxRect availableSpace(GetClientSize()); 414 if (GetBuffer().GetDirty()) 415 { 416 GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT); 417 GetBuffer().SetDirty(false); 418 SetupScrollbars(); 419 } 420 421 wxRect clipRect(availableSpace); 422 clipRect.x += GetBuffer().GetLeftMargin(); 423 clipRect.y += GetBuffer().GetTopMargin(); 424 clipRect.width -= (GetBuffer().GetLeftMargin() + GetBuffer().GetRightMargin()); 425 clipRect.height -= (GetBuffer().GetTopMargin() + GetBuffer().GetBottomMargin()); 426 clipRect.SetPosition(GetLogicalPoint(clipRect.GetPosition())); 427 dc.SetClippingRegion(clipRect); 428 429 GetBuffer().Draw(dc, GetBuffer().GetRange(), GetInternalSelectionRange(), drawingArea, 0 /* descent */, 0 /* flags */); 430 431 dc.DestroyClippingRegion(); 432 433#if wxRICHTEXT_USE_OWN_CARET 434 if (GetCaret()->IsVisible()) 435 { 436 ((wxRichTextCaret*) GetCaret())->DoDraw(& dc); 437 } 438#endif 439 440 } 441 442#if !wxRICHTEXT_USE_OWN_CARET 443 if (GetCaret()) 444 GetCaret()->Show(); 445 PositionCaret(); 446#endif 447} 448 449// Empty implementation, to prevent flicker 450void wxRichTextCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event)) 451{ 452} 453 454void wxRichTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event)) 455{ 456 if (GetCaret()) 457 { 458#if !wxRICHTEXT_USE_OWN_CARET 459 PositionCaret(); 460#endif 461 GetCaret()->Show(); 462 } 463 464#if defined(__WXGTK__) && !wxRICHTEXT_USE_OWN_CARET 465 // Work around dropouts when control is focused 466 if (!IsFrozen()) 467 { 468 Refresh(false); 469 } 470#endif 471} 472 473void wxRichTextCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event)) 474{ 475 if (GetCaret()) 476 GetCaret()->Hide(); 477 478#if defined(__WXGTK__) && !wxRICHTEXT_USE_OWN_CARET 479 // Work around dropouts when control is focused 480 if (!IsFrozen()) 481 { 482 Refresh(false); 483 } 484#endif 485} 486 487void wxRichTextCtrl::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event)) 488{ 489 m_dragging = false; 490} 491 492/// Left-click 493void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event) 494{ 495 SetFocus(); 496 497 wxClientDC dc(this); 498 PrepareDC(dc); 499 dc.SetFont(GetFont()); 500 501 long position = 0; 502 int hit = GetBuffer().HitTest(dc, event.GetLogicalPosition(dc), position); 503 504 if (hit != wxRICHTEXT_HITTEST_NONE) 505 { 506 m_dragStart = event.GetLogicalPosition(dc); 507 m_dragging = true; 508 CaptureMouse(); 509 510 bool caretAtLineStart = false; 511 512 if (hit & wxRICHTEXT_HITTEST_BEFORE) 513 { 514 // If we're at the start of a line (but not first in para) 515 // then we should keep the caret showing at the start of the line 516 // by showing the m_caretAtLineStart flag. 517 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position); 518 wxRichTextLine* line = GetBuffer().GetLineAtPosition(position); 519 520 if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position) 521 caretAtLineStart = true; 522 position --; 523 } 524 525 long oldCaretPos = m_caretPosition; 526 527 MoveCaret(position, caretAtLineStart); 528 SetDefaultStyleToCursorStyle(); 529 530 if (event.ShiftDown()) 531 { 532 if (m_selectionRange.GetStart() == -2) 533 ExtendSelection(oldCaretPos, m_caretPosition, wxRICHTEXT_SHIFT_DOWN); 534 else 535 ExtendSelection(m_caretPosition, m_caretPosition, wxRICHTEXT_SHIFT_DOWN); 536 } 537 else 538 SelectNone(); 539 } 540 541 event.Skip(); 542} 543 544/// Left-up 545void wxRichTextCtrl::OnLeftUp(wxMouseEvent& event) 546{ 547 if (m_dragging) 548 { 549 m_dragging = false; 550 if (GetCapture() == this) 551 ReleaseMouse(); 552 553 // See if we clicked on a URL 554 wxClientDC dc(this); 555 PrepareDC(dc); 556 dc.SetFont(GetFont()); 557 558 long position = 0; 559 wxPoint logicalPt = event.GetLogicalPosition(dc); 560 int hit = GetBuffer().HitTest(dc, logicalPt, position); 561 562 if ((hit != wxRICHTEXT_HITTEST_NONE) && !(hit & wxRICHTEXT_HITTEST_OUTSIDE)) 563 { 564 wxTextAttrEx attr; 565 if (GetStyle(position, attr)) 566 { 567 if (attr.HasFlag(wxTEXT_ATTR_URL)) 568 { 569 wxString urlTarget = attr.GetURL(); 570 if (!urlTarget.IsEmpty()) 571 { 572 wxMouseEvent mouseEvent(event); 573 574 long startPos = 0, endPos = 0; 575 wxRichTextObject* obj = GetBuffer().GetLeafObjectAtPosition(position); 576 if (obj) 577 { 578 startPos = obj->GetRange().GetStart(); 579 endPos = obj->GetRange().GetEnd(); 580 } 581 582 wxTextUrlEvent urlEvent(GetId(), mouseEvent, startPos, endPos); 583 InitCommandEvent(urlEvent); 584 585 urlEvent.SetString(urlTarget); 586 587 GetEventHandler()->ProcessEvent(urlEvent); 588 } 589 } 590 } 591 } 592 } 593} 594 595/// Left-click 596void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event) 597{ 598 wxClientDC dc(this); 599 PrepareDC(dc); 600 dc.SetFont(GetFont()); 601 602 long position = 0; 603 wxPoint logicalPt = event.GetLogicalPosition(dc); 604 int hit = GetBuffer().HitTest(dc, logicalPt, position); 605 606 // See if we need to change the cursor 607 608 { 609 if (hit != wxRICHTEXT_HITTEST_NONE && !(hit & wxRICHTEXT_HITTEST_OUTSIDE)) 610 { 611 wxTextAttrEx attr; 612 if (GetStyle(position, attr)) 613 { 614 if (attr.HasFlag(wxTEXT_ATTR_URL)) 615 { 616 SetCursor(m_urlCursor); 617 } 618 else if (!attr.HasFlag(wxTEXT_ATTR_URL)) 619 { 620 SetCursor(m_textCursor); 621 } 622 } 623 } 624 else 625 SetCursor(m_textCursor); 626 } 627 628 if (!event.Dragging()) 629 { 630 event.Skip(); 631 return; 632 } 633 634 if (m_dragging && hit != wxRICHTEXT_HITTEST_NONE) 635 { 636 // TODO: test closeness 637 638 bool caretAtLineStart = false; 639 640 if (hit & wxRICHTEXT_HITTEST_BEFORE) 641 { 642 // If we're at the start of a line (but not first in para) 643 // then we should keep the caret showing at the start of the line 644 // by showing the m_caretAtLineStart flag. 645 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position); 646 wxRichTextLine* line = GetBuffer().GetLineAtPosition(position); 647 648 if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position) 649 caretAtLineStart = true; 650 position --; 651 } 652 653 if (m_caretPosition != position) 654 { 655 ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN); 656 657 MoveCaret(position, caretAtLineStart); 658 SetDefaultStyleToCursorStyle(); 659 } 660 } 661} 662 663/// Right-click 664void wxRichTextCtrl::OnRightClick(wxMouseEvent& event) 665{ 666 SetFocus(); 667 event.Skip(); 668} 669 670/// Left-double-click 671void wxRichTextCtrl::OnLeftDClick(wxMouseEvent& event) 672{ 673 SelectWord(GetCaretPosition()+1); 674 event.Skip(); 675} 676 677/// Middle-click 678void wxRichTextCtrl::OnMiddleClick(wxMouseEvent& event) 679{ 680 event.Skip(); 681} 682 683/// Key press 684void wxRichTextCtrl::OnChar(wxKeyEvent& event) 685{ 686 int flags = 0; 687 if (event.CmdDown()) 688 flags |= wxRICHTEXT_CTRL_DOWN; 689 if (event.ShiftDown()) 690 flags |= wxRICHTEXT_SHIFT_DOWN; 691 if (event.AltDown()) 692 flags |= wxRICHTEXT_ALT_DOWN; 693 694 if (event.GetEventType() == wxEVT_KEY_DOWN) 695 { 696 if (event.GetKeyCode() == WXK_LEFT || 697 event.GetKeyCode() == WXK_RIGHT || 698 event.GetKeyCode() == WXK_UP || 699 event.GetKeyCode() == WXK_DOWN || 700 event.GetKeyCode() == WXK_HOME || 701 event.GetKeyCode() == WXK_PAGEUP || 702 event.GetKeyCode() == WXK_PAGEDOWN || 703 event.GetKeyCode() == WXK_END || 704 705 event.GetKeyCode() == WXK_NUMPAD_LEFT || 706 event.GetKeyCode() == WXK_NUMPAD_RIGHT || 707 event.GetKeyCode() == WXK_NUMPAD_UP || 708 event.GetKeyCode() == WXK_NUMPAD_DOWN || 709 event.GetKeyCode() == WXK_NUMPAD_HOME || 710 event.GetKeyCode() == WXK_NUMPAD_PAGEUP || 711 event.GetKeyCode() == WXK_NUMPAD_PAGEDOWN || 712 event.GetKeyCode() == WXK_NUMPAD_END) 713 { 714 KeyboardNavigate(event.GetKeyCode(), flags); 715 return; 716 } 717 718 long keycode = event.GetKeyCode(); 719 switch ( keycode ) 720 { 721 case WXK_ESCAPE: 722 case WXK_START: 723 case WXK_LBUTTON: 724 case WXK_RBUTTON: 725 case WXK_CANCEL: 726 case WXK_MBUTTON: 727 case WXK_CLEAR: 728 case WXK_SHIFT: 729 case WXK_ALT: 730 case WXK_CONTROL: 731 case WXK_MENU: 732 case WXK_PAUSE: 733 case WXK_CAPITAL: 734 case WXK_END: 735 case WXK_HOME: 736 case WXK_LEFT: 737 case WXK_UP: 738 case WXK_RIGHT: 739 case WXK_DOWN: 740 case WXK_SELECT: 741 case WXK_PRINT: 742 case WXK_EXECUTE: 743 case WXK_SNAPSHOT: 744 case WXK_INSERT: 745 case WXK_HELP: 746 case WXK_F1: 747 case WXK_F2: 748 case WXK_F3: 749 case WXK_F4: 750 case WXK_F5: 751 case WXK_F6: 752 case WXK_F7: 753 case WXK_F8: 754 case WXK_F9: 755 case WXK_F10: 756 case WXK_F11: 757 case WXK_F12: 758 case WXK_F13: 759 case WXK_F14: 760 case WXK_F15: 761 case WXK_F16: 762 case WXK_F17: 763 case WXK_F18: 764 case WXK_F19: 765 case WXK_F20: 766 case WXK_F21: 767 case WXK_F22: 768 case WXK_F23: 769 case WXK_F24: 770 case WXK_NUMLOCK: 771 case WXK_SCROLL: 772 case WXK_PAGEUP: 773 case WXK_PAGEDOWN: 774 case WXK_NUMPAD_F1: 775 case WXK_NUMPAD_F2: 776 case WXK_NUMPAD_F3: 777 case WXK_NUMPAD_F4: 778 case WXK_NUMPAD_HOME: 779 case WXK_NUMPAD_LEFT: 780 case WXK_NUMPAD_UP: 781 case WXK_NUMPAD_RIGHT: 782 case WXK_NUMPAD_DOWN: 783 case WXK_NUMPAD_PAGEUP: 784 case WXK_NUMPAD_PAGEDOWN: 785 case WXK_NUMPAD_END: 786 case WXK_NUMPAD_BEGIN: 787 case WXK_NUMPAD_INSERT: 788 case WXK_WINDOWS_LEFT: 789 { 790 return; 791 } 792 default: 793 { 794 } 795 } 796 797 // Must process this before translation, otherwise it's translated into a WXK_DELETE event. 798 if (event.CmdDown() && event.GetKeyCode() == WXK_BACK) 799 { 800 BeginBatchUndo(_("Delete Text")); 801 802 long newPos = m_caretPosition; 803 804 bool processed = DeleteSelectedContent(& newPos); 805 806 // Submit range in character positions, which are greater than caret positions, 807 // so subtract 1 for deleted character and add 1 for conversion to character position. 808 if (newPos > -1) 809 { 810 if (event.CmdDown()) 811 { 812 long pos = wxRichTextCtrl::FindNextWordPosition(-1); 813 if (pos < newPos) 814 { 815 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(pos+1, newPos), this); 816 processed = true; 817 } 818 } 819 820 if (!processed) 821 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(newPos, newPos), this); 822 } 823 824 EndBatchUndo(); 825 826 if (GetLastPosition() == -1) 827 { 828 GetBuffer().Reset(); 829 830 m_caretPosition = -1; 831 PositionCaret(); 832 SetDefaultStyleToCursorStyle(); 833 } 834 835 ScrollIntoView(m_caretPosition, WXK_LEFT); 836 837 wxRichTextEvent cmdEvent( 838 wxEVT_COMMAND_RICHTEXT_DELETE, 839 GetId()); 840 cmdEvent.SetEventObject(this); 841 cmdEvent.SetFlags(flags); 842 cmdEvent.SetPosition(m_caretPosition+1); 843 GetEventHandler()->ProcessEvent(cmdEvent); 844 845 Update(); 846 } 847 else 848 event.Skip(); 849 850 return; 851 } 852 853 // all the other keys modify the controls contents which shouldn't be 854 // possible if we're read-only 855 if ( !IsEditable() ) 856 { 857 event.Skip(); 858 return; 859 } 860 861 if (event.GetKeyCode() == WXK_RETURN) 862 { 863 BeginBatchUndo(_("Insert Text")); 864 865 long newPos = m_caretPosition; 866 867 DeleteSelectedContent(& newPos); 868 869 if (event.ShiftDown()) 870 { 871 wxString text; 872 text = wxRichTextLineBreakChar; 873 GetBuffer().InsertTextWithUndo(newPos+1, text, this); 874 m_caretAtLineStart = true; 875 PositionCaret(); 876 } 877 else 878 GetBuffer().InsertNewlineWithUndo(newPos+1, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE|wxRICHTEXT_INSERT_INTERACTIVE); 879 880 EndBatchUndo(); 881 SetDefaultStyleToCursorStyle(); 882 883 ScrollIntoView(m_caretPosition, WXK_RIGHT); 884 885 wxRichTextEvent cmdEvent( 886 wxEVT_COMMAND_RICHTEXT_RETURN, 887 GetId()); 888 cmdEvent.SetEventObject(this); 889 cmdEvent.SetFlags(flags); 890 cmdEvent.SetPosition(newPos+1); 891 892 if (!GetEventHandler()->ProcessEvent(cmdEvent)) 893 { 894 // Generate conventional event 895 wxCommandEvent textEvent(wxEVT_COMMAND_TEXT_ENTER, GetId()); 896 InitCommandEvent(textEvent); 897 898 GetEventHandler()->ProcessEvent(textEvent); 899 } 900 Update(); 901 } 902 else if (event.GetKeyCode() == WXK_BACK) 903 { 904 BeginBatchUndo(_("Delete Text")); 905 906 long newPos = m_caretPosition; 907 908 bool processed = DeleteSelectedContent(& newPos); 909 910 // Submit range in character positions, which are greater than caret positions, 911 // so subtract 1 for deleted character and add 1 for conversion to character position. 912 if (newPos > -1) 913 { 914 if (event.CmdDown()) 915 { 916 long pos = wxRichTextCtrl::FindNextWordPosition(-1); 917 if (pos < newPos) 918 { 919 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(pos+1, newPos), this); 920 processed = true; 921 } 922 } 923 924 if (!processed) 925 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(newPos, newPos), this); 926 } 927 928 EndBatchUndo(); 929 930 if (GetLastPosition() == -1) 931 { 932 GetBuffer().Reset(); 933 934 m_caretPosition = -1; 935 PositionCaret(); 936 SetDefaultStyleToCursorStyle(); 937 } 938 939 ScrollIntoView(m_caretPosition, WXK_LEFT); 940 941 wxRichTextEvent cmdEvent( 942 wxEVT_COMMAND_RICHTEXT_DELETE, 943 GetId()); 944 cmdEvent.SetEventObject(this); 945 cmdEvent.SetFlags(flags); 946 cmdEvent.SetPosition(m_caretPosition+1); 947 GetEventHandler()->ProcessEvent(cmdEvent); 948 949 Update(); 950 } 951 else if (event.GetKeyCode() == WXK_DELETE) 952 { 953 BeginBatchUndo(_("Delete Text")); 954 955 long newPos = m_caretPosition; 956 957 bool processed = DeleteSelectedContent(& newPos); 958 959 // Submit range in character positions, which are greater than caret positions, 960 if (newPos < GetBuffer().GetRange().GetEnd()+1) 961 { 962 if (event.CmdDown()) 963 { 964 long pos = wxRichTextCtrl::FindNextWordPosition(1); 965 if (pos != -1 && (pos > newPos)) 966 { 967 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(newPos+1, pos), this); 968 processed = true; 969 } 970 } 971 972 if (!processed && newPos < (GetLastPosition()-1)) 973 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(newPos+1, newPos+1), this); 974 } 975 976 EndBatchUndo(); 977 978 if (GetLastPosition() == -1) 979 { 980 GetBuffer().Reset(); 981 982 m_caretPosition = -1; 983 PositionCaret(); 984 SetDefaultStyleToCursorStyle(); 985 } 986 987 wxRichTextEvent cmdEvent( 988 wxEVT_COMMAND_RICHTEXT_DELETE, 989 GetId()); 990 cmdEvent.SetEventObject(this); 991 cmdEvent.SetFlags(flags); 992 cmdEvent.SetPosition(m_caretPosition+1); 993 GetEventHandler()->ProcessEvent(cmdEvent); 994 995 Update(); 996 } 997 else 998 { 999 long keycode = event.GetKeyCode(); 1000 switch ( keycode ) 1001 { 1002 case WXK_ESCAPE: 1003 { 1004 event.Skip(); 1005 return; 1006 } 1007 1008 default: 1009 { 1010#ifdef __WXMAC__ 1011 if (event.CmdDown()) 1012#else 1013 // Fixes AltGr+key with European input languages on Windows 1014 if ((event.CmdDown() && !event.AltDown()) || (event.AltDown() && !event.CmdDown())) 1015#endif 1016 { 1017 event.Skip(); 1018 return; 1019 } 1020 1021 wxRichTextEvent cmdEvent( 1022 wxEVT_COMMAND_RICHTEXT_CHARACTER, 1023 GetId()); 1024 cmdEvent.SetEventObject(this); 1025 cmdEvent.SetFlags(flags); 1026#if wxUSE_UNICODE 1027 cmdEvent.SetCharacter(event.GetUnicodeKey()); 1028#else 1029 cmdEvent.SetCharacter((wxChar) keycode); 1030#endif 1031 cmdEvent.SetPosition(m_caretPosition+1); 1032 1033 if (keycode == wxT('\t')) 1034 { 1035 // See if we need to promote or demote the selection or paragraph at the cursor 1036 // position, instead of inserting a tab. 1037 long pos = GetAdjustedCaretPosition(GetCaretPosition()); 1038 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos); 1039 if (para && para->GetRange().GetStart() == pos && para->GetAttributes().HasListStyleName()) 1040 { 1041 wxRichTextRange range; 1042 if (HasSelection()) 1043 range = GetSelectionRange(); 1044 else 1045 range = para->GetRange().FromInternal(); 1046 1047 int promoteBy = event.ShiftDown() ? 1 : -1; 1048 1049 PromoteList(promoteBy, range, NULL); 1050 1051 GetEventHandler()->ProcessEvent(cmdEvent); 1052 1053 return; 1054 } 1055 } 1056 1057 BeginBatchUndo(_("Insert Text")); 1058 1059 long newPos = m_caretPosition; 1060 DeleteSelectedContent(& newPos); 1061 1062#if wxUSE_UNICODE 1063 wxString str = event.GetUnicodeKey(); 1064#else 1065 wxString str = (wxChar) event.GetKeyCode(); 1066#endif 1067 GetBuffer().InsertTextWithUndo(newPos+1, str, this, 0); 1068 1069 EndBatchUndo(); 1070 1071 SetDefaultStyleToCursorStyle(); 1072 ScrollIntoView(m_caretPosition, WXK_RIGHT); 1073 1074 GetEventHandler()->ProcessEvent(cmdEvent); 1075 1076 Update(); 1077 } 1078 } 1079 } 1080} 1081 1082/// Delete content if there is a selection, e.g. when pressing a key. 1083bool wxRichTextCtrl::DeleteSelectedContent(long* newPos) 1084{ 1085 if (HasSelection()) 1086 { 1087 long pos = m_selectionRange.GetStart(); 1088 wxRichTextRange range = m_selectionRange; 1089 1090 // SelectAll causes more to be selected than doing it interactively, 1091 // and causes a new paragraph to be inserted. So for multiline buffers, 1092 // don't delete the final position. 1093 if (range.GetEnd() == GetLastPosition() && GetNumberOfLines() > 0) 1094 range.SetEnd(range.GetEnd()-1); 1095 1096 GetBuffer().DeleteRangeWithUndo(range, this); 1097 m_selectionRange.SetRange(-2, -2); 1098 1099 if (newPos) 1100 *newPos = pos-1; 1101 return true; 1102 } 1103 else 1104 return false; 1105} 1106 1107/// Keyboard navigation 1108 1109/* 1110 1111Left: left one character 1112Right: right one character 1113Up: up one line 1114Down: down one line 1115Ctrl-Left: left one word 1116Ctrl-Right: right one word 1117Ctrl-Up: previous paragraph start 1118Ctrl-Down: next start of paragraph 1119Home: start of line 1120End: end of line 1121Ctrl-Home: start of document 1122Ctrl-End: end of document 1123Page-Up: Up a screen 1124Page-Down: Down a screen 1125 1126Maybe: 1127 1128Ctrl-Alt-PgUp: Start of window 1129Ctrl-Alt-PgDn: End of window 1130F8: Start selection mode 1131Esc: End selection mode 1132 1133Adding Shift does the above but starts/extends selection. 1134 1135 1136 */ 1137 1138bool wxRichTextCtrl::KeyboardNavigate(int keyCode, int flags) 1139{ 1140 bool success = false; 1141 1142 if (keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT) 1143 { 1144 if (flags & wxRICHTEXT_CTRL_DOWN) 1145 success = WordRight(1, flags); 1146 else 1147 success = MoveRight(1, flags); 1148 } 1149 else if (keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT) 1150 { 1151 if (flags & wxRICHTEXT_CTRL_DOWN) 1152 success = WordLeft(1, flags); 1153 else 1154 success = MoveLeft(1, flags); 1155 } 1156 else if (keyCode == WXK_UP || keyCode == WXK_NUMPAD_UP) 1157 { 1158 if (flags & wxRICHTEXT_CTRL_DOWN) 1159 success = MoveToParagraphStart(flags); 1160 else 1161 success = MoveUp(1, flags); 1162 } 1163 else if (keyCode == WXK_DOWN || keyCode == WXK_NUMPAD_DOWN) 1164 { 1165 if (flags & wxRICHTEXT_CTRL_DOWN) 1166 success = MoveToParagraphEnd(flags); 1167 else 1168 success = MoveDown(1, flags); 1169 } 1170 else if (keyCode == WXK_PAGEUP || keyCode == WXK_NUMPAD_PAGEUP) 1171 { 1172 success = PageUp(1, flags); 1173 } 1174 else if (keyCode == WXK_PAGEDOWN || keyCode == WXK_NUMPAD_PAGEDOWN) 1175 { 1176 success = PageDown(1, flags); 1177 } 1178 else if (keyCode == WXK_HOME || keyCode == WXK_NUMPAD_HOME) 1179 { 1180 if (flags & wxRICHTEXT_CTRL_DOWN) 1181 success = MoveHome(flags); 1182 else 1183 success = MoveToLineStart(flags); 1184 } 1185 else if (keyCode == WXK_END || keyCode == WXK_NUMPAD_END) 1186 { 1187 if (flags & wxRICHTEXT_CTRL_DOWN) 1188 success = MoveEnd(flags); 1189 else 1190 success = MoveToLineEnd(flags); 1191 } 1192 1193 if (success) 1194 { 1195 ScrollIntoView(m_caretPosition, keyCode); 1196 SetDefaultStyleToCursorStyle(); 1197 } 1198 1199 return success; 1200} 1201 1202/// Extend the selection. Selections are in caret positions. 1203bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags) 1204{ 1205 if (flags & wxRICHTEXT_SHIFT_DOWN) 1206 { 1207 if (oldPos == newPos) 1208 return false; 1209 1210 wxRichTextRange oldSelection = m_selectionRange; 1211 1212 // If not currently selecting, start selecting 1213 if (m_selectionRange.GetStart() == -2) 1214 { 1215 m_selectionAnchor = oldPos; 1216 1217 if (oldPos > newPos) 1218 m_selectionRange.SetRange(newPos+1, oldPos); 1219 else 1220 m_selectionRange.SetRange(oldPos+1, newPos); 1221 } 1222 else 1223 { 1224 // Always ensure that the selection range start is greater than 1225 // the end. 1226 if (newPos > m_selectionAnchor) 1227 m_selectionRange.SetRange(m_selectionAnchor+1, newPos); 1228 else if (newPos == m_selectionAnchor) 1229 m_selectionRange = wxRichTextRange(-2, -2); 1230 else 1231 m_selectionRange.SetRange(newPos+1, m_selectionAnchor); 1232 } 1233 1234 wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange); 1235 1236 if (m_selectionRange.GetStart() > m_selectionRange.GetEnd()) 1237 { 1238 wxLogDebug(wxT("Strange selection range")); 1239 } 1240 1241 return true; 1242 } 1243 else 1244 return false; 1245} 1246 1247/// Scroll into view, returning true if we scrolled. 1248/// This takes a _caret_ position. 1249bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode) 1250{ 1251 wxRichTextLine* line = GetVisibleLineForCaretPosition(position); 1252 1253 if (!line) 1254 return false; 1255 1256 int ppuX, ppuY; 1257 GetScrollPixelsPerUnit(& ppuX, & ppuY); 1258 1259 int startXUnits, startYUnits; 1260 GetViewStart(& startXUnits, & startYUnits); 1261 int startY = startYUnits * ppuY; 1262 1263 int sx = 0, sy = 0; 1264 GetVirtualSize(& sx, & sy); 1265 int sxUnits = 0; 1266 int syUnits = 0; 1267 if (ppuY != 0) 1268 syUnits = sy/ppuY; 1269 1270 wxRect rect = line->GetRect(); 1271 1272 bool scrolled = false; 1273 1274 wxSize clientSize = GetClientSize(); 1275 clientSize.y -= GetBuffer().GetBottomMargin(); 1276 1277 if (GetWindowStyle() & wxRE_CENTRE_CARET) 1278 { 1279 int y = rect.y - GetClientSize().y/2; 1280 int yUnits = (int) (0.5 + ((float) y)/(float) ppuY); 1281 if (y >= 0 && (y + clientSize.y) < GetBuffer().GetCachedSize().y) 1282 { 1283 if (startYUnits != yUnits) 1284 { 1285 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits); 1286 scrolled = true; 1287 } 1288#if !wxRICHTEXT_USE_OWN_CARET 1289 if (scrolled) 1290#endif 1291 PositionCaret(); 1292 1293 return scrolled; 1294 } 1295 } 1296 1297 // Going down 1298 if (keyCode == WXK_DOWN || keyCode == WXK_NUMPAD_DOWN || 1299 keyCode == WXK_RIGHT || keyCode == WXK_NUMPAD_RIGHT || 1300 keyCode == WXK_END || keyCode == WXK_NUMPAD_END || 1301 keyCode == WXK_PAGEDOWN || keyCode == WXK_NUMPAD_PAGEDOWN) 1302 { 1303 if ((rect.y + rect.height) > (clientSize.y + startY)) 1304 { 1305 // Make it scroll so this item is at the bottom 1306 // of the window 1307 int y = rect.y - (clientSize.y - rect.height); 1308 int yUnits = (int) (0.5 + ((float) y)/(float) ppuY); 1309 1310 // If we're still off the screen, scroll another line down 1311 if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY))) 1312 yUnits ++; 1313 1314 if (startYUnits != yUnits) 1315 { 1316 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits); 1317 scrolled = true; 1318 } 1319 } 1320 else if (rect.y < (startY + GetBuffer().GetTopMargin())) 1321 { 1322 // Make it scroll so this item is at the top 1323 // of the window 1324 int y = rect.y - GetBuffer().GetTopMargin(); 1325 int yUnits = (int) (0.5 + ((float) y)/(float) ppuY); 1326 1327 if (startYUnits != yUnits) 1328 { 1329 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits); 1330 scrolled = true; 1331 } 1332 } 1333 } 1334 // Going up 1335 else if (keyCode == WXK_UP || keyCode == WXK_NUMPAD_UP || 1336 keyCode == WXK_LEFT || keyCode == WXK_NUMPAD_LEFT || 1337 keyCode == WXK_HOME || keyCode == WXK_NUMPAD_HOME || 1338 keyCode == WXK_PAGEUP || keyCode == WXK_NUMPAD_PAGEUP ) 1339 { 1340 if (rect.y < (startY + GetBuffer().GetBottomMargin())) 1341 { 1342 // Make it scroll so this item is at the top 1343 // of the window 1344 int y = rect.y - GetBuffer().GetTopMargin(); 1345 int yUnits = (int) (0.5 + ((float) y)/(float) ppuY); 1346 1347 if (startYUnits != yUnits) 1348 { 1349 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits); 1350 scrolled = true; 1351 } 1352 } 1353 else if ((rect.y + rect.height) > (clientSize.y + startY)) 1354 { 1355 // Make it scroll so this item is at the bottom 1356 // of the window 1357 int y = rect.y - (clientSize.y - rect.height); 1358 int yUnits = (int) (0.5 + ((float) y)/(float) ppuY); 1359 1360 // If we're still off the screen, scroll another line down 1361 if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY))) 1362 yUnits ++; 1363 1364 if (startYUnits != yUnits) 1365 { 1366 SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits); 1367 scrolled = true; 1368 } 1369 } 1370 } 1371 1372#if !wxRICHTEXT_USE_OWN_CARET 1373 if (scrolled) 1374#endif 1375 PositionCaret(); 1376 1377 return scrolled; 1378} 1379 1380/// Is the given position visible on the screen? 1381bool wxRichTextCtrl::IsPositionVisible(long pos) const 1382{ 1383 wxRichTextLine* line = GetVisibleLineForCaretPosition(pos-1); 1384 1385 if (!line) 1386 return false; 1387 1388 int ppuX, ppuY; 1389 GetScrollPixelsPerUnit(& ppuX, & ppuY); 1390 1391 int startX, startY; 1392 GetViewStart(& startX, & startY); 1393 startX = 0; 1394 startY = startY * ppuY; 1395 1396 wxRect rect = line->GetRect(); 1397 wxSize clientSize = GetClientSize(); 1398 clientSize.y -= GetBuffer().GetBottomMargin(); 1399 1400 return (rect.GetTop() >= (startY + GetBuffer().GetTopMargin())) && (rect.GetBottom() <= (startY + clientSize.y)); 1401} 1402 1403void wxRichTextCtrl::SetCaretPosition(long position, bool showAtLineStart) 1404{ 1405 m_caretPosition = position; 1406 m_caretAtLineStart = showAtLineStart; 1407} 1408 1409/// Move caret one visual step forward: this may mean setting a flag 1410/// and keeping the same position if we're going from the end of one line 1411/// to the start of the next, which may be the exact same caret position. 1412void wxRichTextCtrl::MoveCaretForward(long oldPosition) 1413{ 1414 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(oldPosition); 1415 1416 // Only do the check if we're not at the end of the paragraph (where things work OK 1417 // anyway) 1418 if (para && (oldPosition != para->GetRange().GetEnd() - 1)) 1419 { 1420 wxRichTextLine* line = GetBuffer().GetLineAtPosition(oldPosition); 1421 1422 if (line) 1423 { 1424 wxRichTextRange lineRange = line->GetAbsoluteRange(); 1425 1426 // We're at the end of a line. See whether we need to 1427 // stay at the same actual caret position but change visual 1428 // position, or not. 1429 if (oldPosition == lineRange.GetEnd()) 1430 { 1431 if (m_caretAtLineStart) 1432 { 1433 // We're already at the start of the line, so actually move on now. 1434 m_caretPosition = oldPosition + 1; 1435 m_caretAtLineStart = false; 1436 } 1437 else 1438 { 1439 // We're showing at the end of the line, so keep to 1440 // the same position but indicate that we're to show 1441 // at the start of the next line. 1442 m_caretPosition = oldPosition; 1443 m_caretAtLineStart = true; 1444 } 1445 SetDefaultStyleToCursorStyle(); 1446 return; 1447 } 1448 } 1449 } 1450 m_caretPosition ++; 1451 SetDefaultStyleToCursorStyle(); 1452} 1453 1454/// Move caret one visual step backward: this may mean setting a flag 1455/// and keeping the same position if we're going from the end of one line 1456/// to the start of the next, which may be the exact same caret position. 1457void wxRichTextCtrl::MoveCaretBack(long oldPosition) 1458{ 1459 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(oldPosition); 1460 1461 // Only do the check if we're not at the start of the paragraph (where things work OK 1462 // anyway) 1463 if (para && (oldPosition != para->GetRange().GetStart())) 1464 { 1465 wxRichTextLine* line = GetBuffer().GetLineAtPosition(oldPosition); 1466 1467 if (line) 1468 { 1469 wxRichTextRange lineRange = line->GetAbsoluteRange(); 1470 1471 // We're at the start of a line. See whether we need to 1472 // stay at the same actual caret position but change visual 1473 // position, or not. 1474 if (oldPosition == lineRange.GetStart()) 1475 { 1476 m_caretPosition = oldPosition-1; 1477 m_caretAtLineStart = true; 1478 return; 1479 } 1480 else if (oldPosition == lineRange.GetEnd()) 1481 { 1482 if (m_caretAtLineStart) 1483 { 1484 // We're at the start of the line, so keep the same caret position 1485 // but clear the start-of-line flag. 1486 m_caretPosition = oldPosition; 1487 m_caretAtLineStart = false; 1488 } 1489 else 1490 { 1491 // We're showing at the end of the line, so go back 1492 // to the previous character position. 1493 m_caretPosition = oldPosition - 1; 1494 } 1495 SetDefaultStyleToCursorStyle(); 1496 return; 1497 } 1498 } 1499 } 1500 m_caretPosition --; 1501 SetDefaultStyleToCursorStyle(); 1502} 1503 1504/// Move right 1505bool wxRichTextCtrl::MoveRight(int noPositions, int flags) 1506{ 1507 long endPos = GetBuffer().GetRange().GetEnd(); 1508 1509 if (m_caretPosition + noPositions < endPos) 1510 { 1511 long oldPos = m_caretPosition; 1512 long newPos = m_caretPosition + noPositions; 1513 1514 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags); 1515 if (!extendSel) 1516 SelectNone(); 1517 1518 // Determine by looking at oldPos and m_caretPosition whether 1519 // we moved from the end of a line to the start of the next line, in which case 1520 // we want to adjust the caret position such that it is positioned at the 1521 // start of the next line, rather than jumping past the first character of the 1522 // line. 1523 if (noPositions == 1 && !extendSel) 1524 MoveCaretForward(oldPos); 1525 else 1526 SetCaretPosition(newPos); 1527 1528 PositionCaret(); 1529 SetDefaultStyleToCursorStyle(); 1530 1531 return true; 1532 } 1533 else 1534 return false; 1535} 1536 1537/// Move left 1538bool wxRichTextCtrl::MoveLeft(int noPositions, int flags) 1539{ 1540 long startPos = -1; 1541 1542 if (m_caretPosition > startPos - noPositions + 1) 1543 { 1544 long oldPos = m_caretPosition; 1545 long newPos = m_caretPosition - noPositions; 1546 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags); 1547 if (!extendSel) 1548 SelectNone(); 1549 1550 if (noPositions == 1 && !extendSel) 1551 MoveCaretBack(oldPos); 1552 else 1553 SetCaretPosition(newPos); 1554 1555 PositionCaret(); 1556 SetDefaultStyleToCursorStyle(); 1557 1558 return true; 1559 } 1560 else 1561 return false; 1562} 1563 1564/// Move up 1565bool wxRichTextCtrl::MoveUp(int noLines, int flags) 1566{ 1567 return MoveDown(- noLines, flags); 1568} 1569 1570/// Move up 1571bool wxRichTextCtrl::MoveDown(int noLines, int flags) 1572{ 1573 if (!GetCaret()) 1574 return false; 1575 1576 long lineNumber = GetBuffer().GetVisibleLineNumber(m_caretPosition, true, m_caretAtLineStart); 1577 wxPoint pt = GetCaret()->GetPosition(); 1578 long newLine = lineNumber + noLines; 1579 1580 if (lineNumber != -1) 1581 { 1582 if (noLines > 0) 1583 { 1584 long lastLine = GetBuffer().GetVisibleLineNumber(GetBuffer().GetRange().GetEnd()); 1585 1586 if (newLine > lastLine) 1587 return false; 1588 } 1589 else 1590 { 1591 if (newLine < 0) 1592 return false; 1593 } 1594 } 1595 1596 wxRichTextLine* lineObj = GetBuffer().GetLineForVisibleLineNumber(newLine); 1597 if (lineObj) 1598 { 1599 pt.y = lineObj->GetAbsolutePosition().y + 2; 1600 } 1601 else 1602 return false; 1603 1604 long newPos = 0; 1605 wxClientDC dc(this); 1606 PrepareDC(dc); 1607 dc.SetFont(GetFont()); 1608 1609 int hitTest = GetBuffer().HitTest(dc, pt, newPos); 1610 1611 if (hitTest != wxRICHTEXT_HITTEST_NONE) 1612 { 1613 // If end of previous line, and hitTest is wxRICHTEXT_HITTEST_BEFORE, 1614 // we want to be at the end of the last line but with m_caretAtLineStart set to true, 1615 // so we view the caret at the start of the line. 1616 bool caretLineStart = false; 1617 if (hitTest & wxRICHTEXT_HITTEST_BEFORE) 1618 { 1619 wxRichTextLine* thisLine = GetBuffer().GetLineAtPosition(newPos-1); 1620 wxRichTextRange lineRange; 1621 if (thisLine) 1622 lineRange = thisLine->GetAbsoluteRange(); 1623 1624 if (thisLine && (newPos-1) == lineRange.GetEnd()) 1625 { 1626 newPos --; 1627 caretLineStart = true; 1628 } 1629 else 1630 { 1631 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(newPos); 1632 if (para && para->GetRange().GetStart() == newPos) 1633 newPos --; 1634 } 1635 } 1636 1637 long newSelEnd = newPos; 1638 1639 bool extendSel = ExtendSelection(m_caretPosition, newSelEnd, flags); 1640 if (!extendSel) 1641 SelectNone(); 1642 1643 SetCaretPosition(newPos, caretLineStart); 1644 PositionCaret(); 1645 SetDefaultStyleToCursorStyle(); 1646 1647 return true; 1648 } 1649 1650 return false; 1651} 1652 1653/// Move to the end of the paragraph 1654bool wxRichTextCtrl::MoveToParagraphEnd(int flags) 1655{ 1656 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(m_caretPosition, true); 1657 if (para) 1658 { 1659 long newPos = para->GetRange().GetEnd() - 1; 1660 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags); 1661 if (!extendSel) 1662 SelectNone(); 1663 1664 SetCaretPosition(newPos); 1665 PositionCaret(); 1666 SetDefaultStyleToCursorStyle(); 1667 1668 return true; 1669 } 1670 1671 return false; 1672} 1673 1674/// Move to the start of the paragraph 1675bool wxRichTextCtrl::MoveToParagraphStart(int flags) 1676{ 1677 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(m_caretPosition, true); 1678 if (para) 1679 { 1680 long newPos = para->GetRange().GetStart() - 1; 1681 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags); 1682 if (!extendSel) 1683 SelectNone(); 1684 1685 SetCaretPosition(newPos); 1686 PositionCaret(); 1687 SetDefaultStyleToCursorStyle(); 1688 1689 return true; 1690 } 1691 1692 return false; 1693} 1694 1695/// Move to the end of the line 1696bool wxRichTextCtrl::MoveToLineEnd(int flags) 1697{ 1698 wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition); 1699 1700 if (line) 1701 { 1702 wxRichTextRange lineRange = line->GetAbsoluteRange(); 1703 long newPos = lineRange.GetEnd(); 1704 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags); 1705 if (!extendSel) 1706 SelectNone(); 1707 1708 SetCaretPosition(newPos); 1709 PositionCaret(); 1710 SetDefaultStyleToCursorStyle(); 1711 1712 return true; 1713 } 1714 1715 return false; 1716} 1717 1718/// Move to the start of the line 1719bool wxRichTextCtrl::MoveToLineStart(int flags) 1720{ 1721 wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition); 1722 if (line) 1723 { 1724 wxRichTextRange lineRange = line->GetAbsoluteRange(); 1725 long newPos = lineRange.GetStart()-1; 1726 1727 bool extendSel = ExtendSelection(m_caretPosition, newPos, flags); 1728 if (!extendSel) 1729 SelectNone(); 1730 1731 wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(line); 1732 1733 SetCaretPosition(newPos, para->GetRange().GetStart() != lineRange.GetStart()); 1734 PositionCaret(); 1735 SetDefaultStyleToCursorStyle(); 1736 1737 return true; 1738 } 1739 1740 return false; 1741} 1742 1743/// Move to the start of the buffer 1744bool wxRichTextCtrl::MoveHome(int flags) 1745{ 1746 if (m_caretPosition != -1) 1747 { 1748 bool extendSel = ExtendSelection(m_caretPosition, -1, flags); 1749 if (!extendSel) 1750 SelectNone(); 1751 1752 SetCaretPosition(-1); 1753 PositionCaret(); 1754 SetDefaultStyleToCursorStyle(); 1755 1756 return true; 1757 } 1758 else 1759 return false; 1760} 1761 1762/// Move to the end of the buffer 1763bool wxRichTextCtrl::MoveEnd(int flags) 1764{ 1765 long endPos = GetBuffer().GetRange().GetEnd()-1; 1766 1767 if (m_caretPosition != endPos) 1768 { 1769 bool extendSel = ExtendSelection(m_caretPosition, endPos, flags); 1770 if (!extendSel) 1771 SelectNone(); 1772 1773 SetCaretPosition(endPos); 1774 PositionCaret(); 1775 SetDefaultStyleToCursorStyle(); 1776 1777 return true; 1778 } 1779 else 1780 return false; 1781} 1782 1783/// Move noPages pages up 1784bool wxRichTextCtrl::PageUp(int noPages, int flags) 1785{ 1786 return PageDown(- noPages, flags); 1787} 1788 1789/// Move noPages pages down 1790bool wxRichTextCtrl::PageDown(int noPages, int flags) 1791{ 1792 // Calculate which line occurs noPages * screen height further down. 1793 wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition); 1794 if (line) 1795 { 1796 wxSize clientSize = GetClientSize(); 1797 int newY = line->GetAbsolutePosition().y + noPages*clientSize.y; 1798 1799 wxRichTextLine* newLine = GetBuffer().GetLineAtYPosition(newY); 1800 if (newLine) 1801 { 1802 wxRichTextRange lineRange = newLine->GetAbsoluteRange(); 1803 long pos = lineRange.GetStart()-1; 1804 if (pos != m_caretPosition) 1805 { 1806 wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(newLine); 1807 1808 bool extendSel = ExtendSelection(m_caretPosition, pos, flags); 1809 if (!extendSel) 1810 SelectNone(); 1811 1812 SetCaretPosition(pos, para->GetRange().GetStart() != lineRange.GetStart()); 1813 PositionCaret(); 1814 SetDefaultStyleToCursorStyle(); 1815 1816 return true; 1817 } 1818 } 1819 } 1820 1821 return false; 1822} 1823 1824static bool wxRichTextCtrlIsWhitespace(const wxString& str) 1825{ 1826 return str == wxT(" ") || str == wxT("\t"); 1827} 1828 1829// Finds the caret position for the next word 1830long wxRichTextCtrl::FindNextWordPosition(int direction) const 1831{ 1832 long endPos = GetBuffer().GetRange().GetEnd(); 1833 1834 if (direction > 0) 1835 { 1836 long i = m_caretPosition+1+direction; // +1 for conversion to character pos 1837 1838 // First skip current text to space 1839 while (i < endPos && i > -1) 1840 { 1841 // i is in character, not caret positions 1842 wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i)); 1843 wxRichTextLine* line = GetBuffer().GetLineAtPosition(i, false); 1844 if (line && (i == line->GetAbsoluteRange().GetEnd())) 1845 { 1846 break; 1847 } 1848 else if (!wxRichTextCtrlIsWhitespace(text) && !text.empty()) 1849 i += direction; 1850 else 1851 { 1852 break; 1853 } 1854 } 1855 while (i < endPos && i > -1) 1856 { 1857 // i is in character, not caret positions 1858 wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i)); 1859 wxRichTextLine* line = GetBuffer().GetLineAtPosition(i, false); 1860 if (line && (i == line->GetAbsoluteRange().GetEnd())) 1861 return wxMax(-1, i); 1862 1863 if (text.empty()) // End of paragraph, or maybe an image 1864 return wxMax(-1, i - 1); 1865 else if (wxRichTextCtrlIsWhitespace(text) || text.empty()) 1866 i += direction; 1867 else 1868 { 1869 // Convert to caret position 1870 return wxMax(-1, i - 1); 1871 } 1872 } 1873 if (i >= endPos) 1874 return endPos-1; 1875 return i-1; 1876 } 1877 else 1878 { 1879 long i = m_caretPosition; 1880 1881 // First skip white space 1882 while (i < endPos && i > -1) 1883 { 1884 // i is in character, not caret positions 1885 wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i)); 1886 wxRichTextLine* line = GetBuffer().GetLineAtPosition(i, false); 1887 1888 if (text.empty() || (line && (i == line->GetAbsoluteRange().GetStart()))) // End of paragraph, or maybe an image 1889 break; 1890 else if (wxRichTextCtrlIsWhitespace(text) || text.empty()) 1891 i += direction; 1892 else 1893 break; 1894 } 1895 // Next skip current text to space 1896 while (i < endPos && i > -1) 1897 { 1898 // i is in character, not caret positions 1899 wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i)); 1900 wxRichTextLine* line = GetBuffer().GetLineAtPosition(i, false); 1901 if (line && line->GetAbsoluteRange().GetStart() == i) 1902 return i-1; 1903 1904 if (!wxRichTextCtrlIsWhitespace(text) /* && !text.empty() */) 1905 i += direction; 1906 else 1907 { 1908 return i; 1909 } 1910 } 1911 if (i < -1) 1912 return -1; 1913 return i; 1914 } 1915} 1916 1917/// Move n words left 1918bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags) 1919{ 1920 long pos = FindNextWordPosition(-1); 1921 if (pos != m_caretPosition) 1922 { 1923 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true); 1924 1925 bool extendSel = ExtendSelection(m_caretPosition, pos, flags); 1926 if (!extendSel) 1927 SelectNone(); 1928 1929 SetCaretPosition(pos, para->GetRange().GetStart() != pos); 1930 PositionCaret(); 1931 SetDefaultStyleToCursorStyle(); 1932 1933 return true; 1934 } 1935 1936 return false; 1937} 1938 1939/// Move n words right 1940bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags) 1941{ 1942 long pos = FindNextWordPosition(1); 1943 if (pos != m_caretPosition) 1944 { 1945 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true); 1946 1947 bool extendSel = ExtendSelection(m_caretPosition, pos, flags); 1948 if (!extendSel) 1949 SelectNone(); 1950 1951 SetCaretPosition(pos, para->GetRange().GetStart() != pos); 1952 PositionCaret(); 1953 SetDefaultStyleToCursorStyle(); 1954 1955 return true; 1956 } 1957 1958 return false; 1959} 1960 1961/// Sizing 1962void wxRichTextCtrl::OnSize(wxSizeEvent& event) 1963{ 1964 // Only do sizing optimization for large buffers 1965 if (GetBuffer().GetRange().GetEnd() > m_delayedLayoutThreshold) 1966 { 1967 m_fullLayoutRequired = true; 1968 m_fullLayoutTime = wxGetLocalTimeMillis(); 1969 m_fullLayoutSavedPosition = GetFirstVisiblePosition(); 1970 LayoutContent(true /* onlyVisibleRect */); 1971 } 1972 else 1973 GetBuffer().Invalidate(wxRICHTEXT_ALL); 1974 1975#if wxRICHTEXT_BUFFERED_PAINTING 1976 RecreateBuffer(); 1977#endif 1978 1979 event.Skip(); 1980} 1981 1982/// Idle-time processing 1983void wxRichTextCtrl::OnIdle(wxIdleEvent& event) 1984{ 1985#if wxRICHTEXT_USE_OWN_CARET 1986 if (((wxRichTextCaret*) GetCaret())->GetNeedsUpdate()) 1987 { 1988 ((wxRichTextCaret*) GetCaret())->SetNeedsUpdate(false); 1989 PositionCaret(); 1990 GetCaret()->Show(); 1991 } 1992#endif 1993 1994 const int layoutInterval = wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL; 1995 1996 if (m_fullLayoutRequired && (wxGetLocalTimeMillis() > (m_fullLayoutTime + layoutInterval))) 1997 { 1998 m_fullLayoutRequired = false; 1999 m_fullLayoutTime = 0; 2000 GetBuffer().Invalidate(wxRICHTEXT_ALL); 2001 ShowPosition(m_fullLayoutSavedPosition); 2002 Refresh(false); 2003 } 2004 2005 if (m_caretPositionForDefaultStyle != -2) 2006 { 2007 // If the caret position has changed, no longer reflect the default style 2008 // in the UI. 2009 if (GetCaretPosition() != m_caretPositionForDefaultStyle) 2010 m_caretPositionForDefaultStyle = -2; 2011 } 2012 2013 event.Skip(); 2014} 2015 2016/// Scrolling 2017void wxRichTextCtrl::OnScroll(wxScrollWinEvent& event) 2018{ 2019#if wxRICHTEXT_USE_OWN_CARET 2020 if (!((wxRichTextCaret*) GetCaret())->GetNeedsUpdate()) 2021 { 2022 GetCaret()->Hide(); 2023 ((wxRichTextCaret*) GetCaret())->SetNeedsUpdate(); 2024 } 2025#endif 2026 2027 event.Skip(); 2028} 2029 2030/// Set up scrollbars, e.g. after a resize 2031void wxRichTextCtrl::SetupScrollbars(bool atTop) 2032{ 2033 if (m_freezeCount) 2034 return; 2035 2036 if (GetBuffer().IsEmpty()) 2037 { 2038 SetScrollbars(0, 0, 0, 0, 0, 0); 2039 return; 2040 } 2041 2042 // TODO: reimplement scrolling so we scroll by line, not by fixed number 2043 // of pixels. See e.g. wxVScrolledWindow for ideas. 2044 int pixelsPerUnit = 5; 2045 wxSize clientSize = GetClientSize(); 2046 2047 int maxHeight = GetBuffer().GetCachedSize().y + GetBuffer().GetTopMargin(); 2048 2049 // Round up so we have at least maxHeight pixels 2050 int unitsY = (int) (((float)maxHeight/(float)pixelsPerUnit) + 0.5); 2051 2052 int startX = 0, startY = 0; 2053 if (!atTop) 2054 GetViewStart(& startX, & startY); 2055 2056 int maxPositionX = 0; // wxMax(sz.x - clientSize.x, 0); 2057 int maxPositionY = (int) ((((float)(wxMax((unitsY*pixelsPerUnit) - clientSize.y, 0)))/((float)pixelsPerUnit)) + 0.5); 2058 2059 int newStartX = wxMin(maxPositionX, startX); 2060 int newStartY = wxMin(maxPositionY, startY); 2061 2062 int oldPPUX, oldPPUY; 2063 int oldStartX, oldStartY; 2064 int oldVirtualSizeX = 0, oldVirtualSizeY = 0; 2065 GetScrollPixelsPerUnit(& oldPPUX, & oldPPUY); 2066 GetViewStart(& oldStartX, & oldStartY); 2067 GetVirtualSize(& oldVirtualSizeX, & oldVirtualSizeY); 2068 if (oldPPUY > 0) 2069 oldVirtualSizeY /= oldPPUY; 2070 2071 if (oldPPUX == 0 && oldPPUY == pixelsPerUnit && oldVirtualSizeY == unitsY && oldStartX == newStartX && oldStartY == newStartY) 2072 return; 2073 2074 // Move to previous scroll position if 2075 // possible 2076 SetScrollbars(0, pixelsPerUnit, 0, unitsY, newStartX, newStartY); 2077} 2078 2079/// Paint the background 2080void wxRichTextCtrl::PaintBackground(wxDC& dc) 2081{ 2082 wxColour backgroundColour = GetBackgroundColour(); 2083 if (!backgroundColour.Ok()) 2084 backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE); 2085 2086 // Clear the background 2087 dc.SetBrush(wxBrush(backgroundColour)); 2088 dc.SetPen(*wxTRANSPARENT_PEN); 2089 wxRect windowRect(GetClientSize()); 2090 windowRect.x -= 2; windowRect.y -= 2; 2091 windowRect.width += 4; windowRect.height += 4; 2092 2093 // We need to shift the rectangle to take into account 2094 // scrolling. Converting device to logical coordinates. 2095 CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y); 2096 dc.DrawRectangle(windowRect); 2097} 2098 2099#if wxRICHTEXT_BUFFERED_PAINTING 2100/// Recreate buffer bitmap if necessary 2101bool wxRichTextCtrl::RecreateBuffer(const wxSize& size) 2102{ 2103 wxSize sz = size; 2104 if (sz == wxDefaultSize) 2105 sz = GetClientSize(); 2106 2107 if (sz.x < 1 || sz.y < 1) 2108 return false; 2109 2110 if (!m_bufferBitmap.Ok() || m_bufferBitmap.GetWidth() < sz.x || m_bufferBitmap.GetHeight() < sz.y) 2111 m_bufferBitmap = wxBitmap(sz.x, sz.y); 2112 return m_bufferBitmap.Ok(); 2113} 2114#endif 2115 2116// ---------------------------------------------------------------------------- 2117// file IO functions 2118// ---------------------------------------------------------------------------- 2119 2120bool wxRichTextCtrl::DoLoadFile(const wxString& filename, int fileType) 2121{ 2122 bool success = GetBuffer().LoadFile(filename, fileType); 2123 if (success) 2124 m_filename = filename; 2125 2126 DiscardEdits(); 2127 SetInsertionPoint(0); 2128 LayoutContent(); 2129 PositionCaret(); 2130 SetupScrollbars(true); 2131 Refresh(false); 2132 SendTextUpdatedEvent(); 2133 2134 if (success) 2135 return true; 2136 else 2137 { 2138 wxLogError(_("File couldn't be loaded.")); 2139 2140 return false; 2141 } 2142} 2143 2144bool wxRichTextCtrl::DoSaveFile(const wxString& filename, int fileType) 2145{ 2146 if (GetBuffer().SaveFile(filename, fileType)) 2147 { 2148 m_filename = filename; 2149 2150 DiscardEdits(); 2151 2152 return true; 2153 } 2154 2155 wxLogError(_("The text couldn't be saved.")); 2156 2157 return false; 2158} 2159 2160// ---------------------------------------------------------------------------- 2161// wxRichTextCtrl specific functionality 2162// ---------------------------------------------------------------------------- 2163 2164/// Add a new paragraph of text to the end of the buffer 2165wxRichTextRange wxRichTextCtrl::AddParagraph(const wxString& text) 2166{ 2167 wxRichTextRange range = GetBuffer().AddParagraph(text); 2168 LayoutContent(); 2169 return range; 2170} 2171 2172/// Add an image 2173wxRichTextRange wxRichTextCtrl::AddImage(const wxImage& image) 2174{ 2175 wxRichTextRange range = GetBuffer().AddImage(image); 2176 LayoutContent(); 2177 return range; 2178} 2179 2180// ---------------------------------------------------------------------------- 2181// selection and ranges 2182// ---------------------------------------------------------------------------- 2183 2184void wxRichTextCtrl::SelectAll() 2185{ 2186 SetSelection(-1, -1); 2187} 2188 2189/// Select none 2190void wxRichTextCtrl::SelectNone() 2191{ 2192 if (!(GetSelectionRange() == wxRichTextRange(-2, -2))) 2193 { 2194 wxRichTextRange oldSelection = m_selectionRange; 2195 2196 m_selectionRange = wxRichTextRange(-2, -2); 2197 2198 wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange); 2199 } 2200 m_selectionAnchor = -2; 2201} 2202 2203static bool wxIsWordDelimiter(const wxString& text) 2204{ 2205 return !text.IsEmpty() && !wxIsalnum(text[0]); 2206} 2207 2208/// Select the word at the given character position 2209bool wxRichTextCtrl::SelectWord(long position) 2210{ 2211 if (position < 0 || position > GetBuffer().GetRange().GetEnd()) 2212 return false; 2213 2214 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position); 2215 if (!para) 2216 return false; 2217 2218 if (position == para->GetRange().GetEnd()) 2219 position --; 2220 2221 long positionStart = position; 2222 long positionEnd = position; 2223 2224 for (positionStart = position; positionStart >= para->GetRange().GetStart(); positionStart --) 2225 { 2226 wxString text = GetBuffer().GetTextForRange(wxRichTextRange(positionStart, positionStart)); 2227 if (wxIsWordDelimiter(text)) 2228 { 2229 positionStart ++; 2230 break; 2231 } 2232 } 2233 if (positionStart < para->GetRange().GetStart()) 2234 positionStart = para->GetRange().GetStart(); 2235 2236 for (positionEnd = position; positionEnd < para->GetRange().GetEnd(); positionEnd ++) 2237 { 2238 wxString text = GetBuffer().GetTextForRange(wxRichTextRange(positionEnd, positionEnd)); 2239 if (wxIsWordDelimiter(text)) 2240 { 2241 positionEnd --; 2242 break; 2243 } 2244 } 2245 if (positionEnd >= para->GetRange().GetEnd()) 2246 positionEnd = para->GetRange().GetEnd(); 2247 2248 if (positionEnd < positionStart) 2249 return false; 2250 2251 SetSelection(positionStart, positionEnd+1); 2252 2253 if (positionStart >= 0) 2254 { 2255 MoveCaret(positionStart-1, true); 2256 SetDefaultStyleToCursorStyle(); 2257 } 2258 2259 return true; 2260} 2261 2262wxString wxRichTextCtrl::GetStringSelection() const 2263{ 2264 long from, to; 2265 GetSelection(&from, &to); 2266 2267 return GetRange(from, to); 2268} 2269 2270// ---------------------------------------------------------------------------- 2271// hit testing 2272// ---------------------------------------------------------------------------- 2273 2274wxTextCtrlHitTestResult 2275wxRichTextCtrl::HitTest(const wxPoint& pt, wxTextCoord *x, wxTextCoord *y) const 2276{ 2277 // implement in terms of the other overload as the native ports typically 2278 // can get the position and not (x, y) pair directly (although wxUniv 2279 // directly gets x and y -- and so overrides this method as well) 2280 long pos; 2281 wxTextCtrlHitTestResult rc = HitTest(pt, &pos); 2282 2283 if ( rc != wxTE_HT_UNKNOWN ) 2284 { 2285 PositionToXY(pos, x, y); 2286 } 2287 2288 return rc; 2289} 2290 2291wxTextCtrlHitTestResult 2292wxRichTextCtrl::HitTest(const wxPoint& pt, 2293 long * pos) const 2294{ 2295 wxClientDC dc((wxRichTextCtrl*) this); 2296 ((wxRichTextCtrl*)this)->PrepareDC(dc); 2297 2298 // Buffer uses logical position (relative to start of buffer) 2299 // so convert 2300 wxPoint pt2 = GetLogicalPoint(pt); 2301 2302 int hit = ((wxRichTextCtrl*)this)->GetBuffer().HitTest(dc, pt2, *pos); 2303 2304 if ((hit & wxRICHTEXT_HITTEST_BEFORE) && (hit & wxRICHTEXT_HITTEST_OUTSIDE)) 2305 return wxTE_HT_BEFORE; 2306 else if ((hit & wxRICHTEXT_HITTEST_AFTER) && (hit & wxRICHTEXT_HITTEST_OUTSIDE)) 2307 return wxTE_HT_BEYOND; 2308 else if (hit & (wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_AFTER)) 2309 return wxTE_HT_ON_TEXT; 2310 2311 return wxTE_HT_UNKNOWN; 2312} 2313 2314// ---------------------------------------------------------------------------- 2315// set/get the controls text 2316// ---------------------------------------------------------------------------- 2317 2318wxString wxRichTextCtrl::GetValue() const 2319{ 2320 return GetBuffer().GetText(); 2321} 2322 2323wxString wxRichTextCtrl::GetRange(long from, long to) const 2324{ 2325 // Public API for range is different from internals 2326 return GetBuffer().GetTextForRange(wxRichTextRange(from, to-1)); 2327} 2328 2329void wxRichTextCtrl::DoSetValue(const wxString& value, int flags) 2330{ 2331 // Don't call Clear here, since it always sends a text updated event 2332 m_buffer.ResetAndClearCommands(); 2333 m_buffer.SetDirty(true); 2334 m_caretPosition = -1; 2335 m_caretPositionForDefaultStyle = -2; 2336 m_caretAtLineStart = false; 2337 m_selectionRange.SetRange(-2, -2); 2338 2339 Scroll(0,0); 2340 2341 if (m_freezeCount == 0) 2342 { 2343 LayoutContent(); 2344 Refresh(false); 2345 } 2346 2347 if (!value.IsEmpty()) 2348 { 2349 // Remove empty paragraph 2350 GetBuffer().Clear(); 2351 DoWriteText(value, flags); 2352 2353 // for compatibility, don't move the cursor when doing SetValue() 2354 SetInsertionPoint(0); 2355 } 2356 else 2357 { 2358 // still send an event for consistency 2359 if (flags & SetValue_SendEvent) 2360 SendTextUpdatedEvent(); 2361 } 2362 DiscardEdits(); 2363} 2364 2365void wxRichTextCtrl::WriteText(const wxString& value) 2366{ 2367 DoWriteText(value); 2368} 2369 2370void wxRichTextCtrl::DoWriteText(const wxString& value, int flags) 2371{ 2372 wxString valueUnix = wxTextFile::Translate(value, wxTextFileType_Unix); 2373 2374 GetBuffer().InsertTextWithUndo(m_caretPosition+1, valueUnix, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE); 2375 2376 if ( flags & SetValue_SendEvent ) 2377 SendTextUpdatedEvent(); 2378} 2379 2380void wxRichTextCtrl::AppendText(const wxString& text) 2381{ 2382 SetInsertionPointEnd(); 2383 2384 WriteText(text); 2385} 2386 2387/// Write an image at the current insertion point 2388bool wxRichTextCtrl::WriteImage(const wxImage& image, int bitmapType) 2389{ 2390 wxRichTextImageBlock imageBlock; 2391 2392 wxImage image2 = image; 2393 if (imageBlock.MakeImageBlock(image2, bitmapType)) 2394 return WriteImage(imageBlock); 2395 2396 return false; 2397} 2398 2399bool wxRichTextCtrl::WriteImage(const wxString& filename, int bitmapType) 2400{ 2401 wxRichTextImageBlock imageBlock; 2402 2403 wxImage image; 2404 if (imageBlock.MakeImageBlock(filename, bitmapType, image, false)) 2405 return WriteImage(imageBlock); 2406 2407 return false; 2408} 2409 2410bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock& imageBlock) 2411{ 2412 return GetBuffer().InsertImageWithUndo(m_caretPosition+1, imageBlock, this); 2413} 2414 2415bool wxRichTextCtrl::WriteImage(const wxBitmap& bitmap, int bitmapType) 2416{ 2417 if (bitmap.Ok()) 2418 { 2419 wxRichTextImageBlock imageBlock; 2420 2421 wxImage image = bitmap.ConvertToImage(); 2422 if (image.Ok() && imageBlock.MakeImageBlock(image, bitmapType)) 2423 return WriteImage(imageBlock); 2424 } 2425 2426 return false; 2427} 2428 2429/// Insert a newline (actually paragraph) at the current insertion point. 2430bool wxRichTextCtrl::Newline() 2431{ 2432 return GetBuffer().InsertNewlineWithUndo(m_caretPosition+1, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE); 2433} 2434 2435/// Insert a line break at the current insertion point. 2436bool wxRichTextCtrl::LineBreak() 2437{ 2438 wxString text; 2439 text = wxRichTextLineBreakChar; 2440 return GetBuffer().InsertTextWithUndo(m_caretPosition+1, text, this); 2441} 2442 2443// ---------------------------------------------------------------------------- 2444// Clipboard operations 2445// ---------------------------------------------------------------------------- 2446 2447void wxRichTextCtrl::Copy() 2448{ 2449 if (CanCopy()) 2450 { 2451 wxRichTextRange range = GetInternalSelectionRange(); 2452 GetBuffer().CopyToClipboard(range); 2453 } 2454} 2455 2456void wxRichTextCtrl::Cut() 2457{ 2458 if (CanCut()) 2459 { 2460 wxRichTextRange range = GetInternalSelectionRange(); 2461 GetBuffer().CopyToClipboard(range); 2462 2463 DeleteSelectedContent(); 2464 LayoutContent(); 2465 Refresh(false); 2466 } 2467} 2468 2469void wxRichTextCtrl::Paste() 2470{ 2471 if (CanPaste()) 2472 { 2473 BeginBatchUndo(_("Paste")); 2474 2475 long newPos = m_caretPosition; 2476 DeleteSelectedContent(& newPos); 2477 2478 GetBuffer().PasteFromClipboard(newPos); 2479 2480 EndBatchUndo(); 2481 } 2482} 2483 2484void wxRichTextCtrl::DeleteSelection() 2485{ 2486 if (CanDeleteSelection()) 2487 { 2488 DeleteSelectedContent(); 2489 } 2490} 2491 2492bool wxRichTextCtrl::HasSelection() const 2493{ 2494 return m_selectionRange.GetStart() != -2 && m_selectionRange.GetEnd() != -2; 2495} 2496 2497bool wxRichTextCtrl::CanCopy() const 2498{ 2499 // Can copy if there's a selection 2500 return HasSelection(); 2501} 2502 2503bool wxRichTextCtrl::CanCut() const 2504{ 2505 return HasSelection() && IsEditable(); 2506} 2507 2508bool wxRichTextCtrl::CanPaste() const 2509{ 2510 if ( !IsEditable() ) 2511 return false; 2512 2513 return GetBuffer().CanPasteFromClipboard(); 2514} 2515 2516bool wxRichTextCtrl::CanDeleteSelection() const 2517{ 2518 return HasSelection() && IsEditable(); 2519} 2520 2521 2522// ---------------------------------------------------------------------------- 2523// Accessors 2524// ---------------------------------------------------------------------------- 2525 2526void wxRichTextCtrl::SetContextMenu(wxMenu* menu) 2527{ 2528 if (m_contextMenu && m_contextMenu != menu) 2529 delete m_contextMenu; 2530 m_contextMenu = menu; 2531} 2532 2533void wxRichTextCtrl::SetEditable(bool editable) 2534{ 2535 m_editable = editable; 2536} 2537 2538void wxRichTextCtrl::SetInsertionPoint(long pos) 2539{ 2540 SelectNone(); 2541 2542 m_caretPosition = pos - 1; 2543 2544 PositionCaret(); 2545 2546 SetDefaultStyleToCursorStyle(); 2547} 2548 2549void wxRichTextCtrl::SetInsertionPointEnd() 2550{ 2551 long pos = GetLastPosition(); 2552 SetInsertionPoint(pos); 2553} 2554 2555long wxRichTextCtrl::GetInsertionPoint() const 2556{ 2557 return m_caretPosition+1; 2558} 2559 2560wxTextPos wxRichTextCtrl::GetLastPosition() const 2561{ 2562 return GetBuffer().GetRange().GetEnd(); 2563} 2564 2565// If the return values from and to are the same, there is no 2566// selection. 2567void wxRichTextCtrl::GetSelection(long* from, long* to) const 2568{ 2569 *from = m_selectionRange.GetStart(); 2570 *to = m_selectionRange.GetEnd(); 2571 if ((*to) != -1 && (*to) != -2) 2572 (*to) ++; 2573} 2574 2575bool wxRichTextCtrl::IsEditable() const 2576{ 2577 return m_editable; 2578} 2579 2580// ---------------------------------------------------------------------------- 2581// selection 2582// ---------------------------------------------------------------------------- 2583 2584void wxRichTextCtrl::SetSelection(long from, long to) 2585{ 2586 // if from and to are both -1, it means (in wxWidgets) that all text should 2587 // be selected. 2588 if ( (from == -1) && (to == -1) ) 2589 { 2590 from = 0; 2591 to = GetLastPosition()+1; 2592 } 2593 2594 DoSetSelection(from, to); 2595} 2596 2597void wxRichTextCtrl::DoSetSelection(long from, long to, bool WXUNUSED(scrollCaret)) 2598{ 2599 if (from == to) 2600 { 2601 SelectNone(); 2602 } 2603 else 2604 { 2605 wxRichTextRange oldSelection = m_selectionRange; 2606 m_selectionAnchor = from-1; 2607 m_selectionRange.SetRange(from, to-1); 2608 2609 m_caretPosition = wxMax(-1, to-1); 2610 2611 wxRichTextCtrlRefreshForSelectionChange(*this, oldSelection, m_selectionRange); 2612 PositionCaret(); 2613 } 2614} 2615 2616// ---------------------------------------------------------------------------- 2617// Editing 2618// ---------------------------------------------------------------------------- 2619 2620void wxRichTextCtrl::Replace(long WXUNUSED(from), long WXUNUSED(to), 2621 const wxString& value) 2622{ 2623 BeginBatchUndo(_("Replace")); 2624 2625 DeleteSelectedContent(); 2626 2627 DoWriteText(value, SetValue_SelectionOnly); 2628 2629 EndBatchUndo(); 2630} 2631 2632void wxRichTextCtrl::Remove(long from, long to) 2633{ 2634 SelectNone(); 2635 2636 GetBuffer().DeleteRangeWithUndo(wxRichTextRange(from, to-1), this); 2637 2638 LayoutContent(); 2639 if (!IsFrozen()) 2640 Refresh(false); 2641} 2642 2643bool wxRichTextCtrl::IsModified() const 2644{ 2645 return m_buffer.IsModified(); 2646} 2647 2648void wxRichTextCtrl::MarkDirty() 2649{ 2650 m_buffer.Modify(true); 2651} 2652 2653void wxRichTextCtrl::DiscardEdits() 2654{ 2655 m_caretPositionForDefaultStyle = -2; 2656 m_buffer.Modify(false); 2657 m_buffer.GetCommandProcessor()->ClearCommands(); 2658} 2659 2660int wxRichTextCtrl::GetNumberOfLines() const 2661{ 2662 return GetBuffer().GetParagraphCount(); 2663} 2664 2665// ---------------------------------------------------------------------------- 2666// Positions <-> coords 2667// ---------------------------------------------------------------------------- 2668 2669long wxRichTextCtrl::XYToPosition(long x, long y) const 2670{ 2671 return GetBuffer().XYToPosition(x, y); 2672} 2673 2674bool wxRichTextCtrl::PositionToXY(long pos, long *x, long *y) const 2675{ 2676 return GetBuffer().PositionToXY(pos, x, y); 2677} 2678 2679// ---------------------------------------------------------------------------- 2680// 2681// ---------------------------------------------------------------------------- 2682 2683void wxRichTextCtrl::ShowPosition(long pos) 2684{ 2685 if (!IsPositionVisible(pos)) 2686 ScrollIntoView(pos-1, WXK_DOWN); 2687} 2688 2689int wxRichTextCtrl::GetLineLength(long lineNo) const 2690{ 2691 return GetBuffer().GetParagraphLength(lineNo); 2692} 2693 2694wxString wxRichTextCtrl::GetLineText(long lineNo) const 2695{ 2696 return GetBuffer().GetParagraphText(lineNo); 2697} 2698 2699// ---------------------------------------------------------------------------- 2700// Undo/redo 2701// ---------------------------------------------------------------------------- 2702 2703void wxRichTextCtrl::Undo() 2704{ 2705 if (CanUndo()) 2706 { 2707 GetCommandProcessor()->Undo(); 2708 } 2709} 2710 2711void wxRichTextCtrl::Redo() 2712{ 2713 if (CanRedo()) 2714 { 2715 GetCommandProcessor()->Redo(); 2716 } 2717} 2718 2719bool wxRichTextCtrl::CanUndo() const 2720{ 2721 return GetCommandProcessor()->CanUndo() && IsEditable(); 2722} 2723 2724bool wxRichTextCtrl::CanRedo() const 2725{ 2726 return GetCommandProcessor()->CanRedo() && IsEditable(); 2727} 2728 2729// ---------------------------------------------------------------------------- 2730// implementation details 2731// ---------------------------------------------------------------------------- 2732 2733void wxRichTextCtrl::Command(wxCommandEvent& event) 2734{ 2735 SetValue(event.GetString()); 2736 GetEventHandler()->ProcessEvent(event); 2737} 2738 2739void wxRichTextCtrl::OnDropFiles(wxDropFilesEvent& event) 2740{ 2741 // By default, load the first file into the text window. 2742 if (event.GetNumberOfFiles() > 0) 2743 { 2744 LoadFile(event.GetFiles()[0]); 2745 } 2746} 2747 2748wxSize wxRichTextCtrl::DoGetBestSize() const 2749{ 2750 return wxSize(10, 10); 2751} 2752 2753// ---------------------------------------------------------------------------- 2754// standard handlers for standard edit menu events 2755// ---------------------------------------------------------------------------- 2756 2757void wxRichTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event)) 2758{ 2759 Cut(); 2760} 2761 2762void wxRichTextCtrl::OnClear(wxCommandEvent& WXUNUSED(event)) 2763{ 2764 DeleteSelection(); 2765} 2766 2767void wxRichTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event)) 2768{ 2769 Copy(); 2770} 2771 2772void wxRichTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event)) 2773{ 2774 Paste(); 2775} 2776 2777void wxRichTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event)) 2778{ 2779 Undo(); 2780} 2781 2782void wxRichTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event)) 2783{ 2784 Redo(); 2785} 2786 2787void wxRichTextCtrl::OnUpdateCut(wxUpdateUIEvent& event) 2788{ 2789 event.Enable( CanCut() ); 2790} 2791 2792void wxRichTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event) 2793{ 2794 event.Enable( CanCopy() ); 2795} 2796 2797void wxRichTextCtrl::OnUpdateClear(wxUpdateUIEvent& event) 2798{ 2799 event.Enable( CanDeleteSelection() ); 2800} 2801 2802void wxRichTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event) 2803{ 2804 event.Enable( CanPaste() ); 2805} 2806 2807void wxRichTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event) 2808{ 2809 event.Enable( CanUndo() ); 2810 event.SetText( GetCommandProcessor()->GetUndoMenuLabel() ); 2811} 2812 2813void wxRichTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event) 2814{ 2815 event.Enable( CanRedo() ); 2816 event.SetText( GetCommandProcessor()->GetRedoMenuLabel() ); 2817} 2818 2819void wxRichTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event)) 2820{ 2821 if (GetLastPosition() > 0) 2822 SelectAll(); 2823} 2824 2825void wxRichTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event) 2826{ 2827 event.Enable(GetLastPosition() > 0); 2828} 2829 2830void wxRichTextCtrl::OnContextMenu(wxContextMenuEvent& event) 2831{ 2832 if (event.GetEventObject() != this) 2833 { 2834 event.Skip(); 2835 return; 2836 } 2837 2838 if (m_contextMenu) 2839 PopupMenu(m_contextMenu); 2840 return; 2841} 2842 2843bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttrEx& style) 2844{ 2845 return GetBuffer().SetStyle(wxRichTextRange(start, end-1), style); 2846} 2847 2848bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttr& style) 2849{ 2850 return GetBuffer().SetStyle(wxRichTextRange(start, end-1), wxTextAttrEx(style)); 2851} 2852 2853bool wxRichTextCtrl::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style) 2854{ 2855 return GetBuffer().SetStyle(range.ToInternal(), style); 2856} 2857 2858// extended style setting operation with flags including: 2859// wxRICHTEXT_SETSTYLE_WITH_UNDO, wxRICHTEXT_SETSTYLE_OPTIMIZE, wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY. 2860// see richtextbuffer.h for more details. 2861bool wxRichTextCtrl::SetStyleEx(long start, long end, const wxTextAttrEx& style, int flags) 2862{ 2863 return GetBuffer().SetStyle(wxRichTextRange(start, end-1), style, flags); 2864} 2865 2866bool wxRichTextCtrl::SetStyleEx(const wxRichTextRange& range, const wxTextAttrEx& style, int flags) 2867{ 2868 return GetBuffer().SetStyle(range.ToInternal(), style, flags); 2869} 2870 2871bool wxRichTextCtrl::SetStyleEx(const wxRichTextRange& range, const wxRichTextAttr& style, int flags) 2872{ 2873 return GetBuffer().SetStyle(range.ToInternal(), style, flags); 2874} 2875 2876bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttrEx& style) 2877{ 2878 return GetBuffer().SetDefaultStyle(style); 2879} 2880 2881bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttr& style) 2882{ 2883 return GetBuffer().SetDefaultStyle(wxTextAttrEx(style)); 2884} 2885 2886const wxTextAttrEx& wxRichTextCtrl::GetDefaultStyleEx() const 2887{ 2888 return GetBuffer().GetDefaultStyle(); 2889} 2890 2891const wxTextAttr& wxRichTextCtrl::GetDefaultStyle() const 2892{ 2893 return GetBuffer().GetDefaultStyle(); 2894} 2895 2896bool wxRichTextCtrl::GetStyle(long position, wxTextAttr& style) 2897{ 2898 wxTextAttrEx attr(style); 2899 if (GetBuffer().GetStyle(position, attr)) 2900 { 2901 style = attr; 2902 return true; 2903 } 2904 else 2905 return false; 2906} 2907 2908bool wxRichTextCtrl::GetStyle(long position, wxTextAttrEx& style) 2909{ 2910 return GetBuffer().GetStyle(position, style); 2911} 2912 2913bool wxRichTextCtrl::GetStyle(long position, wxRichTextAttr& style) 2914{ 2915 return GetBuffer().GetStyle(position, style); 2916} 2917 2918// get the common set of styles for the range 2919bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style) 2920{ 2921 wxTextAttrEx styleEx; 2922 if (GetBuffer().GetStyleForRange(range.ToInternal(), styleEx)) 2923 { 2924 style = styleEx; 2925 return true; 2926 } 2927 else 2928 return false; 2929} 2930 2931bool wxRichTextCtrl::GetStyleForRange(const wxRichTextRange& range, wxTextAttrEx& style) 2932{ 2933 return GetBuffer().GetStyleForRange(range.ToInternal(), style); 2934} 2935 2936/// Get the content (uncombined) attributes for this position. 2937 2938bool wxRichTextCtrl::GetUncombinedStyle(long position, wxTextAttr& style) 2939{ 2940 wxTextAttrEx attr(style); 2941 if (GetBuffer().GetUncombinedStyle(position, attr)) 2942 { 2943 style = attr; 2944 return true; 2945 } 2946 else 2947 return false; 2948} 2949 2950bool wxRichTextCtrl::GetUncombinedStyle(long position, wxTextAttrEx& style) 2951{ 2952 return GetBuffer().GetUncombinedStyle(position, style); 2953} 2954 2955bool wxRichTextCtrl::GetUncombinedStyle(long position, wxRichTextAttr& style) 2956{ 2957 return GetBuffer().GetUncombinedStyle(position, style); 2958} 2959 2960/// Set font, and also the buffer attributes 2961bool wxRichTextCtrl::SetFont(const wxFont& font) 2962{ 2963 wxControl::SetFont(font); 2964 2965 wxTextAttrEx attr = GetBuffer().GetAttributes(); 2966 attr.SetFont(font); 2967 GetBuffer().SetBasicStyle(attr); 2968 2969 GetBuffer().Invalidate(wxRICHTEXT_ALL); 2970 Refresh(false); 2971 2972 return true; 2973} 2974 2975/// Transform logical to physical 2976wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) const 2977{ 2978 wxPoint pt; 2979 CalcScrolledPosition(ptLogical.x, ptLogical.y, & pt.x, & pt.y); 2980 2981 return pt; 2982} 2983 2984/// Transform physical to logical 2985wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical) const 2986{ 2987 wxPoint pt; 2988 CalcUnscrolledPosition(ptPhysical.x, ptPhysical.y, & pt.x, & pt.y); 2989 2990 return pt; 2991} 2992 2993/// Position the caret 2994void wxRichTextCtrl::PositionCaret() 2995{ 2996 if (!GetCaret()) 2997 return; 2998 2999 wxRect caretRect; 3000 if (GetCaretPositionForIndex(GetCaretPosition(), caretRect)) 3001 { 3002 wxPoint newPt = caretRect.GetPosition(); 3003 wxSize newSz = caretRect.GetSize(); 3004 wxPoint pt = GetPhysicalPoint(newPt); 3005 if (GetCaret()->GetPosition() != pt || GetCaret()->GetSize() != newSz) 3006 { 3007 //wxLogDebug(wxT("Positioning caret %d, %d"), pt.x, pt.y); 3008 GetCaret()->Hide(); 3009 if (GetCaret()->GetSize() != newSz) 3010 GetCaret()->SetSize(newSz); 3011 3012 int halfSize = newSz.y/2; 3013 // If the caret is beyond the margin, hide it by moving it out of the way 3014 if (((pt.y + halfSize) < GetBuffer().GetTopMargin()) || ((pt.y + halfSize) > (GetClientSize().y - GetBuffer().GetBottomMargin()))) 3015 pt.y = -200; 3016 3017 GetCaret()->Move(pt); 3018 GetCaret()->Show(); 3019 } 3020 } 3021} 3022 3023/// Get the caret height and position for the given character position 3024bool wxRichTextCtrl::GetCaretPositionForIndex(long position, wxRect& rect) 3025{ 3026 wxClientDC dc(this); 3027 dc.SetFont(GetFont()); 3028 3029 PrepareDC(dc); 3030 3031 wxPoint pt; 3032 int height = 0; 3033 3034 if (GetBuffer().FindPosition(dc, position, pt, & height, m_caretAtLineStart)) 3035 { 3036 // Caret height can't be zero 3037 if (height == 0) 3038 height = dc.GetCharHeight(); 3039 3040 rect = wxRect(pt, wxSize(wxRICHTEXT_DEFAULT_CARET_WIDTH, height)); 3041 return true; 3042 } 3043 3044 return false; 3045} 3046 3047/// Gets the line for the visible caret position. If the caret is 3048/// shown at the very end of the line, it means the next character is actually 3049/// on the following line. So let's get the line we're expecting to find 3050/// if this is the case. 3051wxRichTextLine* wxRichTextCtrl::GetVisibleLineForCaretPosition(long caretPosition) const 3052{ 3053 wxRichTextLine* line = GetBuffer().GetLineAtPosition(caretPosition, true); 3054 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPosition, true); 3055 if (line) 3056 { 3057 wxRichTextRange lineRange = line->GetAbsoluteRange(); 3058 if (caretPosition == lineRange.GetStart()-1 && 3059 (para->GetRange().GetStart() != lineRange.GetStart())) 3060 { 3061 if (!m_caretAtLineStart) 3062 line = GetBuffer().GetLineAtPosition(caretPosition-1, true); 3063 } 3064 } 3065 return line; 3066} 3067 3068 3069/// Move the caret to the given character position 3070bool wxRichTextCtrl::MoveCaret(long pos, bool showAtLineStart) 3071{ 3072 if (GetBuffer().GetDirty()) 3073 LayoutContent(); 3074 3075 if (pos <= GetBuffer().GetRange().GetEnd()) 3076 { 3077 SetCaretPosition(pos, showAtLineStart); 3078 3079 PositionCaret(); 3080 3081 return true; 3082 } 3083 else 3084 return false; 3085} 3086 3087/// Layout the buffer: which we must do before certain operations, such as 3088/// setting the caret position. 3089bool wxRichTextCtrl::LayoutContent(bool onlyVisibleRect) 3090{ 3091 if (GetBuffer().GetDirty() || onlyVisibleRect) 3092 { 3093 wxRect availableSpace(GetClientSize()); 3094 if (availableSpace.width == 0) 3095 availableSpace.width = 10; 3096 if (availableSpace.height == 0) 3097 availableSpace.height = 10; 3098 3099 int flags = wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT; 3100 if (onlyVisibleRect) 3101 { 3102 flags |= wxRICHTEXT_LAYOUT_SPECIFIED_RECT; 3103 availableSpace.SetPosition(GetLogicalPoint(wxPoint(0, 0))); 3104 } 3105 3106 wxClientDC dc(this); 3107 dc.SetFont(GetFont()); 3108 3109 PrepareDC(dc); 3110 3111 GetBuffer().Defragment(); 3112 GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation 3113 GetBuffer().Layout(dc, availableSpace, flags); 3114 GetBuffer().SetDirty(false); 3115 3116 if (!IsFrozen()) 3117 SetupScrollbars(); 3118 } 3119 3120 return true; 3121} 3122 3123/// Is all of the selection bold? 3124bool wxRichTextCtrl::IsSelectionBold() 3125{ 3126 if (HasSelection()) 3127 { 3128 wxRichTextAttr attr; 3129 wxRichTextRange range = GetSelectionRange(); 3130 attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT); 3131 attr.SetFontWeight(wxBOLD); 3132 3133 return HasCharacterAttributes(range, attr); 3134 } 3135 else 3136 { 3137 // If no selection, then we need to combine current style with default style 3138 // to see what the effect would be if we started typing. 3139 wxRichTextAttr attr; 3140 attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT); 3141 3142 long pos = GetAdjustedCaretPosition(GetCaretPosition()); 3143 if (GetStyle(pos, attr)) 3144 { 3145 if (IsDefaultStyleShowing()) 3146 wxRichTextApplyStyle(attr, GetDefaultStyleEx()); 3147 return attr.GetFontWeight() == wxBOLD; 3148 } 3149 } 3150 return false; 3151} 3152 3153/// Is all of the selection italics? 3154bool wxRichTextCtrl::IsSelectionItalics() 3155{ 3156 if (HasSelection()) 3157 { 3158 wxRichTextRange range = GetSelectionRange(); 3159 wxRichTextAttr attr; 3160 attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC); 3161 attr.SetFontStyle(wxITALIC); 3162 3163 return HasCharacterAttributes(range, attr); 3164 } 3165 else 3166 { 3167 // If no selection, then we need to combine current style with default style 3168 // to see what the effect would be if we started typing. 3169 wxRichTextAttr attr; 3170 attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC); 3171 3172 long pos = GetAdjustedCaretPosition(GetCaretPosition()); 3173 if (GetStyle(pos, attr)) 3174 { 3175 if (IsDefaultStyleShowing()) 3176 wxRichTextApplyStyle(attr, GetDefaultStyleEx()); 3177 return attr.GetFontStyle() == wxITALIC; 3178 } 3179 } 3180 return false; 3181} 3182 3183/// Is all of the selection underlined? 3184bool wxRichTextCtrl::IsSelectionUnderlined() 3185{ 3186 if (HasSelection()) 3187 { 3188 wxRichTextRange range = GetSelectionRange(); 3189 wxRichTextAttr attr; 3190 attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE); 3191 attr.SetFontUnderlined(true); 3192 3193 return HasCharacterAttributes(range, attr); 3194 } 3195 else 3196 { 3197 // If no selection, then we need to combine current style with default style 3198 // to see what the effect would be if we started typing. 3199 wxRichTextAttr attr; 3200 attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE); 3201 long pos = GetAdjustedCaretPosition(GetCaretPosition()); 3202 3203 if (GetStyle(pos, attr)) 3204 { 3205 if (IsDefaultStyleShowing()) 3206 wxRichTextApplyStyle(attr, GetDefaultStyleEx()); 3207 return attr.GetFontUnderlined(); 3208 } 3209 } 3210 return false; 3211} 3212 3213/// Apply bold to the selection 3214bool wxRichTextCtrl::ApplyBoldToSelection() 3215{ 3216 wxRichTextAttr attr; 3217 attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT); 3218 attr.SetFontWeight(IsSelectionBold() ? wxNORMAL : wxBOLD); 3219 3220 if (HasSelection()) 3221 return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY); 3222 else 3223 { 3224 wxRichTextAttr current = GetDefaultStyleEx(); 3225 current.Apply(attr); 3226 SetAndShowDefaultStyle(current); 3227 } 3228 return true; 3229} 3230 3231/// Apply italic to the selection 3232bool wxRichTextCtrl::ApplyItalicToSelection() 3233{ 3234 wxRichTextAttr attr; 3235 attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC); 3236 attr.SetFontStyle(IsSelectionItalics() ? wxNORMAL : wxITALIC); 3237 3238 if (HasSelection()) 3239 return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY); 3240 else 3241 { 3242 wxRichTextAttr current = GetDefaultStyleEx(); 3243 current.Apply(attr); 3244 SetAndShowDefaultStyle(current); 3245 } 3246 return true; 3247} 3248 3249/// Apply underline to the selection 3250bool wxRichTextCtrl::ApplyUnderlineToSelection() 3251{ 3252 wxRichTextAttr attr; 3253 attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE); 3254 attr.SetFontUnderlined(!IsSelectionUnderlined()); 3255 3256 if (HasSelection()) 3257 return SetStyleEx(GetSelectionRange(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY); 3258 else 3259 { 3260 wxRichTextAttr current = GetDefaultStyleEx(); 3261 current.Apply(attr); 3262 SetAndShowDefaultStyle(current); 3263 } 3264 return true; 3265} 3266 3267/// Is all of the selection aligned according to the specified flag? 3268bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment) 3269{ 3270 wxRichTextRange range; 3271 if (HasSelection()) 3272 range = GetSelectionRange(); 3273 else 3274 range = wxRichTextRange(GetCaretPosition()+1, GetCaretPosition()+2); 3275 3276 wxRichTextAttr attr; 3277 attr.SetAlignment(alignment); 3278 3279 return HasParagraphAttributes(range, attr); 3280} 3281 3282/// Apply alignment to the selection 3283bool wxRichTextCtrl::ApplyAlignmentToSelection(wxTextAttrAlignment alignment) 3284{ 3285 wxRichTextAttr attr; 3286 attr.SetAlignment(alignment); 3287 if (HasSelection()) 3288 return SetStyle(GetSelectionRange(), attr); 3289 else 3290 { 3291 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(GetCaretPosition()+1); 3292 if (para) 3293 return SetStyleEx(para->GetRange().FromInternal(), attr, wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY); 3294 } 3295 return true; 3296} 3297 3298/// Apply a named style to the selection 3299bool wxRichTextCtrl::ApplyStyle(wxRichTextStyleDefinition* def) 3300{ 3301 // Flags are defined within each definition, so only certain 3302 // attributes are applied. 3303 wxRichTextAttr attr(GetStyleSheet() ? def->GetStyleMergedWithBase(GetStyleSheet()) : def->GetStyle()); 3304 3305 int flags = wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_OPTIMIZE|wxRICHTEXT_SETSTYLE_RESET; 3306 3307 if (def->IsKindOf(CLASSINFO(wxRichTextListStyleDefinition))) 3308 { 3309 flags |= wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY; 3310 3311 wxRichTextRange range; 3312 3313 if (HasSelection()) 3314 range = GetSelectionRange(); 3315 else 3316 { 3317 long pos = GetAdjustedCaretPosition(GetCaretPosition()); 3318 range = wxRichTextRange(pos, pos+1); 3319 } 3320 3321 return SetListStyle(range, (wxRichTextListStyleDefinition*) def, flags); 3322 } 3323 3324 bool isPara = false; 3325 3326 // Make sure the attr has the style name 3327 if (def->IsKindOf(CLASSINFO(wxRichTextParagraphStyleDefinition))) 3328 { 3329 isPara = true; 3330 attr.SetParagraphStyleName(def->GetName()); 3331 3332 // If applying a paragraph style, we only want the paragraph nodes to adopt these 3333 // attributes, and not the leaf nodes. This will allow the content (e.g. text) 3334 // to change its style independently. 3335 flags |= wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY; 3336 } 3337 else 3338 attr.SetCharacterStyleName(def->GetName()); 3339 3340 if (HasSelection()) 3341 return SetStyleEx(GetSelectionRange(), attr, flags); 3342 else 3343 { 3344 wxRichTextAttr current = GetDefaultStyleEx(); 3345 wxRichTextAttr defaultStyle(attr); 3346 if (isPara) 3347 { 3348 // Don't apply extra character styles since they are already implied 3349 // in the paragraph style 3350 defaultStyle.SetFlags(defaultStyle.GetFlags() & ~wxTEXT_ATTR_CHARACTER); 3351 } 3352 current.Apply(defaultStyle); 3353 SetAndShowDefaultStyle(current); 3354 3355 // If it's a paragraph style, we want to apply the style to the 3356 // current paragraph even if we didn't select any text. 3357 if (isPara) 3358 { 3359 long pos = GetAdjustedCaretPosition(GetCaretPosition()); 3360 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos); 3361 if (para) 3362 { 3363 return SetStyleEx(para->GetRange().FromInternal(), attr, flags); 3364 } 3365 } 3366 return true; 3367 } 3368} 3369 3370/// Apply the style sheet to the buffer, for example if the styles have changed. 3371bool wxRichTextCtrl::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet) 3372{ 3373 if (!styleSheet) 3374 styleSheet = GetBuffer().GetStyleSheet(); 3375 if (!styleSheet) 3376 return false; 3377 3378 if (GetBuffer().ApplyStyleSheet(styleSheet)) 3379 { 3380 GetBuffer().Invalidate(wxRICHTEXT_ALL); 3381 Refresh(false); 3382 return true; 3383 } 3384 else 3385 return false; 3386} 3387 3388/// Sets the default style to the style under the cursor 3389bool wxRichTextCtrl::SetDefaultStyleToCursorStyle() 3390{ 3391 wxTextAttrEx attr; 3392 attr.SetFlags(wxTEXT_ATTR_CHARACTER); 3393 3394 // If at the start of a paragraph, use the next position. 3395 long pos = GetAdjustedCaretPosition(GetCaretPosition()); 3396 3397 if (GetUncombinedStyle(pos, attr)) 3398 { 3399 SetDefaultStyle(attr); 3400 return true; 3401 } 3402 3403 return false; 3404} 3405 3406/// Returns the first visible position in the current view 3407long wxRichTextCtrl::GetFirstVisiblePosition() const 3408{ 3409 wxRichTextLine* line = GetBuffer().GetLineAtYPosition(GetLogicalPoint(wxPoint(0, 0)).y); 3410 if (line) 3411 return line->GetAbsoluteRange().GetStart(); 3412 else 3413 return 0; 3414} 3415 3416/// Get the first visible point in the window 3417wxPoint wxRichTextCtrl::GetFirstVisiblePoint() const 3418{ 3419 int ppuX, ppuY; 3420 int startXUnits, startYUnits; 3421 3422 GetScrollPixelsPerUnit(& ppuX, & ppuY); 3423 GetViewStart(& startXUnits, & startYUnits); 3424 3425 return wxPoint(startXUnits * ppuX, startYUnits * ppuY); 3426} 3427 3428/// The adjusted caret position is the character position adjusted to take 3429/// into account whether we're at the start of a paragraph, in which case 3430/// style information should be taken from the next position, not current one. 3431long wxRichTextCtrl::GetAdjustedCaretPosition(long caretPos) const 3432{ 3433 wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPos+1); 3434 3435 if (para && (caretPos+1 == para->GetRange().GetStart())) 3436 caretPos ++; 3437 return caretPos; 3438} 3439 3440/// Get/set the selection range in character positions. -1, -1 means no selection. 3441/// The range is in API convention, i.e. a single character selection is denoted 3442/// by (n, n+1) 3443wxRichTextRange wxRichTextCtrl::GetSelectionRange() const 3444{ 3445 wxRichTextRange range = GetInternalSelectionRange(); 3446 if (range != wxRichTextRange(-2,-2) && range != wxRichTextRange(-1,-1)) 3447 range.SetEnd(range.GetEnd() + 1); 3448 return range; 3449} 3450 3451void wxRichTextCtrl::SetSelectionRange(const wxRichTextRange& range) 3452{ 3453 SetSelection(range.GetStart(), range.GetEnd()); 3454} 3455 3456/// Set list style 3457bool wxRichTextCtrl::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel) 3458{ 3459 return GetBuffer().SetListStyle(range.ToInternal(), def, flags, startFrom, specifiedLevel); 3460} 3461 3462bool wxRichTextCtrl::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel) 3463{ 3464 return GetBuffer().SetListStyle(range.ToInternal(), defName, flags, startFrom, specifiedLevel); 3465} 3466 3467/// Clear list for given range 3468bool wxRichTextCtrl::ClearListStyle(const wxRichTextRange& range, int flags) 3469{ 3470 return GetBuffer().ClearListStyle(range.ToInternal(), flags); 3471} 3472 3473/// Number/renumber any list elements in the given range 3474bool wxRichTextCtrl::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel) 3475{ 3476 return GetBuffer().NumberList(range.ToInternal(), def, flags, startFrom, specifiedLevel); 3477} 3478 3479bool wxRichTextCtrl::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel) 3480{ 3481 return GetBuffer().NumberList(range.ToInternal(), defName, flags, startFrom, specifiedLevel); 3482} 3483 3484/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1 3485bool wxRichTextCtrl::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel) 3486{ 3487 return GetBuffer().PromoteList(promoteBy, range.ToInternal(), def, flags, specifiedLevel); 3488} 3489 3490bool wxRichTextCtrl::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel) 3491{ 3492 return GetBuffer().PromoteList(promoteBy, range.ToInternal(), defName, flags, specifiedLevel); 3493} 3494 3495/// Deletes the content in the given range 3496bool wxRichTextCtrl::Delete(const wxRichTextRange& range) 3497{ 3498 return GetBuffer().DeleteRangeWithUndo(range.ToInternal(), this); 3499} 3500 3501const wxArrayString& wxRichTextCtrl::GetAvailableFontNames() 3502{ 3503 if (sm_availableFontNames.GetCount() == 0) 3504 { 3505 sm_availableFontNames = wxFontEnumerator::GetFacenames(); 3506 sm_availableFontNames.Sort(); 3507 } 3508 return sm_availableFontNames; 3509} 3510 3511void wxRichTextCtrl::ClearAvailableFontNames() 3512{ 3513 sm_availableFontNames.Clear(); 3514} 3515 3516// Refresh the area affected by a selection change 3517bool wxRichTextCtrlRefreshForSelectionChange(wxRichTextCtrl& ctrl, const wxRichTextRange& oldSelection, const wxRichTextRange& newSelection) 3518{ 3519 // Calculate the refresh rectangle - just the affected lines 3520 long firstPos, lastPos; 3521 if (oldSelection.GetStart() == -2 && newSelection.GetStart() != -2) 3522 { 3523 firstPos = newSelection.GetStart(); 3524 lastPos = newSelection.GetEnd(); 3525 } 3526 else if (oldSelection.GetStart() != -2 && newSelection.GetStart() == -2) 3527 { 3528 firstPos = oldSelection.GetStart(); 3529 lastPos = oldSelection.GetEnd(); 3530 } 3531 else if (oldSelection.GetStart() == -2 && newSelection.GetStart() == -2) 3532 { 3533 return false; 3534 } 3535 else 3536 { 3537 firstPos = wxMin(oldSelection.GetStart(), newSelection.GetStart()); 3538 lastPos = wxMax(oldSelection.GetEnd(), newSelection.GetEnd()); 3539 } 3540 3541 wxRichTextLine* firstLine = ctrl.GetBuffer().GetLineAtPosition(firstPos); 3542 wxRichTextLine* lastLine = ctrl.GetBuffer().GetLineAtPosition(lastPos); 3543 3544 if (firstLine && lastLine) 3545 { 3546 wxSize clientSize = ctrl.GetClientSize(); 3547 wxPoint pt1 = ctrl.GetPhysicalPoint(firstLine->GetAbsolutePosition()); 3548 wxPoint pt2 = ctrl.GetPhysicalPoint(lastLine->GetAbsolutePosition()) + wxPoint(0, lastLine->GetSize().y); 3549 3550 pt1.x = 0; 3551 pt1.y = wxMax(0, pt1.y); 3552 pt2.x = 0; 3553 pt2.y = wxMin(clientSize.y, pt2.y); 3554 3555 wxRect rect(pt1, wxSize(clientSize.x, pt2.y - pt1.y)); 3556 ctrl.RefreshRect(rect, false); 3557 } 3558 else 3559 ctrl.Refresh(false); 3560 3561 return true; 3562} 3563 3564#if wxRICHTEXT_USE_OWN_CARET 3565 3566// ---------------------------------------------------------------------------- 3567// initialization and destruction 3568// ---------------------------------------------------------------------------- 3569 3570void wxRichTextCaret::Init() 3571{ 3572 m_hasFocus = true; 3573 3574 m_xOld = 3575 m_yOld = -1; 3576 m_richTextCtrl = NULL; 3577 m_needsUpdate = false; 3578 m_flashOn = true; 3579} 3580 3581wxRichTextCaret::~wxRichTextCaret() 3582{ 3583 if (m_timer.IsRunning()) 3584 m_timer.Stop(); 3585} 3586 3587// ---------------------------------------------------------------------------- 3588// showing/hiding/moving the caret (base class interface) 3589// ---------------------------------------------------------------------------- 3590 3591void wxRichTextCaret::DoShow() 3592{ 3593 m_flashOn = true; 3594 3595 if (!m_timer.IsRunning()) 3596 m_timer.Start(GetBlinkTime()); 3597 3598 Refresh(); 3599} 3600 3601void wxRichTextCaret::DoHide() 3602{ 3603 if (m_timer.IsRunning()) 3604 m_timer.Stop(); 3605 3606 Refresh(); 3607} 3608 3609void wxRichTextCaret::DoMove() 3610{ 3611 if (IsVisible()) 3612 { 3613 Refresh(); 3614 3615 if (m_xOld != -1 && m_yOld != -1) 3616 { 3617 if (m_richTextCtrl) 3618 { 3619 wxRect rect(GetPosition(), GetSize()); 3620 m_richTextCtrl->RefreshRect(rect, false); 3621 } 3622 } 3623 } 3624 3625 m_xOld = m_x; 3626 m_yOld = m_y; 3627} 3628 3629void wxRichTextCaret::DoSize() 3630{ 3631 int countVisible = m_countVisible; 3632 if (countVisible > 0) 3633 { 3634 m_countVisible = 0; 3635 DoHide(); 3636 } 3637 3638 if (countVisible > 0) 3639 { 3640 m_countVisible = countVisible; 3641 DoShow(); 3642 } 3643} 3644 3645// ---------------------------------------------------------------------------- 3646// handling the focus 3647// ---------------------------------------------------------------------------- 3648 3649void wxRichTextCaret::OnSetFocus() 3650{ 3651 m_hasFocus = true; 3652 3653 if ( IsVisible() ) 3654 Refresh(); 3655} 3656 3657void wxRichTextCaret::OnKillFocus() 3658{ 3659 m_hasFocus = false; 3660} 3661 3662// ---------------------------------------------------------------------------- 3663// drawing the caret 3664// ---------------------------------------------------------------------------- 3665 3666void wxRichTextCaret::Refresh() 3667{ 3668 if (m_richTextCtrl) 3669 { 3670 wxRect rect(GetPosition(), GetSize()); 3671 m_richTextCtrl->RefreshRect(rect, false); 3672 } 3673} 3674 3675void wxRichTextCaret::DoDraw(wxDC *dc) 3676{ 3677 dc->SetPen( *wxBLACK_PEN ); 3678 3679 dc->SetBrush(*(m_hasFocus ? wxBLACK_BRUSH : wxTRANSPARENT_BRUSH)); 3680 dc->SetPen(*wxBLACK_PEN); 3681 3682 wxPoint pt(m_x, m_y); 3683 3684 if (m_richTextCtrl) 3685 { 3686 pt = m_richTextCtrl->GetLogicalPoint(pt); 3687 } 3688 if (IsVisible() && m_flashOn) 3689 dc->DrawRectangle(pt.x, pt.y, m_width, m_height); 3690} 3691 3692void wxRichTextCaret::Notify() 3693{ 3694 m_flashOn = !m_flashOn; 3695 Refresh(); 3696} 3697 3698void wxRichTextCaretTimer::Notify() 3699{ 3700 m_caret->Notify(); 3701} 3702 3703#endif 3704 // wxRICHTEXT_USE_OWN_CARET 3705#endif 3706 // wxUSE_RICHTEXT 3707