git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41562 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2777 lines
73 KiB
C++
2777 lines
73 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
||
// Name: src/richtext/richeditctrl.cpp
|
||
// Purpose: A rich edit control
|
||
// Author: Julian Smart
|
||
// Modified by:
|
||
// Created: 2005-09-30
|
||
// RCS-ID: $Id$
|
||
// Copyright: (c) Julian Smart
|
||
// Licence: wxWindows licence
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
|
||
// For compilers that support precompilation, includes "wx.h".
|
||
#include "wx/wxprec.h"
|
||
|
||
#ifdef __BORLANDC__
|
||
#pragma hdrstop
|
||
#endif
|
||
|
||
#if wxUSE_RICHTEXT
|
||
|
||
#include "wx/richtext/richtextctrl.h"
|
||
#include "wx/richtext/richtextstyles.h"
|
||
|
||
#ifndef WX_PRECOMP
|
||
#include "wx/wx.h"
|
||
#include "wx/settings.h"
|
||
#endif
|
||
|
||
#include "wx/textfile.h"
|
||
#include "wx/ffile.h"
|
||
#include "wx/filename.h"
|
||
#include "wx/dcbuffer.h"
|
||
#include "wx/arrimpl.cpp"
|
||
|
||
// DLL options compatibility check:
|
||
#include "wx/app.h"
|
||
WX_CHECK_BUILD_OPTIONS("wxRichTextCtrl")
|
||
|
||
DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_ITEM_SELECTED)
|
||
DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_ITEM_DESELECTED)
|
||
DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK)
|
||
DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_MIDDLE_CLICK)
|
||
DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RIGHT_CLICK)
|
||
DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_DCLICK)
|
||
DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RETURN)
|
||
|
||
#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
|
||
IMPLEMENT_CLASS( wxRichTextCtrl, wxControl )
|
||
#else
|
||
IMPLEMENT_CLASS( wxRichTextCtrl, wxScrolledWindow )
|
||
#endif
|
||
|
||
IMPLEMENT_CLASS( wxRichTextEvent, wxNotifyEvent )
|
||
|
||
#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
|
||
BEGIN_EVENT_TABLE( wxRichTextCtrl, wxControl )
|
||
#else
|
||
BEGIN_EVENT_TABLE( wxRichTextCtrl, wxScrolledWindow )
|
||
#endif
|
||
EVT_PAINT(wxRichTextCtrl::OnPaint)
|
||
EVT_ERASE_BACKGROUND(wxRichTextCtrl::OnEraseBackground)
|
||
EVT_IDLE(wxRichTextCtrl::OnIdle)
|
||
EVT_SCROLLWIN(wxRichTextCtrl::OnScroll)
|
||
EVT_LEFT_DOWN(wxRichTextCtrl::OnLeftClick)
|
||
EVT_MOTION(wxRichTextCtrl::OnMoveMouse)
|
||
EVT_LEFT_UP(wxRichTextCtrl::OnLeftUp)
|
||
EVT_RIGHT_DOWN(wxRichTextCtrl::OnRightClick)
|
||
EVT_MIDDLE_DOWN(wxRichTextCtrl::OnMiddleClick)
|
||
EVT_LEFT_DCLICK(wxRichTextCtrl::OnLeftDClick)
|
||
EVT_CHAR(wxRichTextCtrl::OnChar)
|
||
EVT_SIZE(wxRichTextCtrl::OnSize)
|
||
EVT_SET_FOCUS(wxRichTextCtrl::OnSetFocus)
|
||
EVT_KILL_FOCUS(wxRichTextCtrl::OnKillFocus)
|
||
EVT_CONTEXT_MENU(wxRichTextCtrl::OnContextMenu)
|
||
|
||
EVT_MENU(wxID_UNDO, wxRichTextCtrl::OnUndo)
|
||
EVT_UPDATE_UI(wxID_UNDO, wxRichTextCtrl::OnUpdateUndo)
|
||
|
||
EVT_MENU(wxID_REDO, wxRichTextCtrl::OnRedo)
|
||
EVT_UPDATE_UI(wxID_REDO, wxRichTextCtrl::OnUpdateRedo)
|
||
|
||
EVT_MENU(wxID_COPY, wxRichTextCtrl::OnCopy)
|
||
EVT_UPDATE_UI(wxID_COPY, wxRichTextCtrl::OnUpdateCopy)
|
||
|
||
EVT_MENU(wxID_PASTE, wxRichTextCtrl::OnPaste)
|
||
EVT_UPDATE_UI(wxID_PASTE, wxRichTextCtrl::OnUpdatePaste)
|
||
|
||
EVT_MENU(wxID_CUT, wxRichTextCtrl::OnCut)
|
||
EVT_UPDATE_UI(wxID_CUT, wxRichTextCtrl::OnUpdateCut)
|
||
|
||
EVT_MENU(wxID_CLEAR, wxRichTextCtrl::OnClear)
|
||
EVT_UPDATE_UI(wxID_CLEAR, wxRichTextCtrl::OnUpdateClear)
|
||
|
||
EVT_MENU(wxID_SELECTALL, wxRichTextCtrl::OnSelectAll)
|
||
EVT_UPDATE_UI(wxID_SELECTALL, wxRichTextCtrl::OnUpdateSelectAll)
|
||
END_EVENT_TABLE()
|
||
|
||
/*!
|
||
* wxRichTextCtrl
|
||
*/
|
||
|
||
wxRichTextCtrl::wxRichTextCtrl()
|
||
#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
|
||
: wxScrollHelper(this)
|
||
#endif
|
||
{
|
||
Init();
|
||
}
|
||
|
||
wxRichTextCtrl::wxRichTextCtrl( wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, long style)
|
||
#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
|
||
: wxScrollHelper(this)
|
||
#endif
|
||
{
|
||
Init();
|
||
Create(parent, id, value, pos, size, style);
|
||
}
|
||
|
||
/// Creation
|
||
bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, long style)
|
||
{
|
||
#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
|
||
if (!wxTextCtrlBase::Create(parent, id, pos, size, style|wxFULL_REPAINT_ON_RESIZE
|
||
))
|
||
return false;
|
||
#else
|
||
if (!wxScrolledWindow::Create(parent, id, pos, size, style|wxFULL_REPAINT_ON_RESIZE
|
||
))
|
||
return false;
|
||
#endif
|
||
|
||
if (!GetFont().Ok())
|
||
{
|
||
SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||
}
|
||
|
||
GetBuffer().SetRichTextCtrl(this);
|
||
|
||
wxTextAttrEx attributes;
|
||
attributes.SetFont(GetFont());
|
||
attributes.SetTextColour(*wxBLACK);
|
||
attributes.SetBackgroundColour(*wxWHITE);
|
||
attributes.SetAlignment(wxTEXT_ALIGNMENT_LEFT);
|
||
attributes.SetFlags(wxTEXT_ATTR_ALL);
|
||
SetBasicStyle(attributes);
|
||
|
||
// The default attributes will be merged with base attributes, so
|
||
// can be empty to begin with
|
||
wxTextAttrEx defaultAttributes;
|
||
SetDefaultStyle(defaultAttributes);
|
||
|
||
SetBackgroundColour(*wxWHITE);
|
||
SetBackgroundStyle(wxBG_STYLE_CUSTOM);
|
||
|
||
// Tell the sizers to use the given or best size
|
||
SetBestFittingSize(size);
|
||
|
||
// Create a buffer
|
||
RecreateBuffer(size);
|
||
|
||
SetCursor(wxCursor(wxCURSOR_IBEAM));
|
||
|
||
if (!value.IsEmpty())
|
||
SetValue(value);
|
||
|
||
return true;
|
||
}
|
||
|
||
wxRichTextCtrl::~wxRichTextCtrl()
|
||
{
|
||
delete m_contextMenu;
|
||
}
|
||
|
||
/// Member initialisation
|
||
void wxRichTextCtrl::Init()
|
||
{
|
||
m_freezeCount = 0;
|
||
m_contextMenu = NULL;
|
||
m_caret = NULL;
|
||
m_caretPosition = -1;
|
||
m_selectionRange.SetRange(-2, -2);
|
||
m_selectionAnchor = -2;
|
||
m_editable = true;
|
||
m_caretAtLineStart = false;
|
||
m_dragging = false;
|
||
m_fullLayoutRequired = false;
|
||
m_fullLayoutTime = 0;
|
||
m_fullLayoutSavedPosition = 0;
|
||
m_delayedLayoutThreshold = wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD;
|
||
m_caretPositionForDefaultStyle = -2;
|
||
}
|
||
|
||
/// Call Freeze to prevent refresh
|
||
void wxRichTextCtrl::Freeze()
|
||
{
|
||
m_freezeCount ++;
|
||
}
|
||
|
||
/// Call Thaw to refresh
|
||
void wxRichTextCtrl::Thaw()
|
||
{
|
||
m_freezeCount --;
|
||
|
||
if (m_freezeCount == 0)
|
||
{
|
||
SetupScrollbars();
|
||
Refresh(false);
|
||
}
|
||
}
|
||
|
||
/// Clear all text
|
||
void wxRichTextCtrl::Clear()
|
||
{
|
||
m_buffer.Reset();
|
||
m_buffer.SetDirty(true);
|
||
m_caretPosition = -1;
|
||
m_caretPositionForDefaultStyle = -2;
|
||
m_caretAtLineStart = false;
|
||
m_selectionRange.SetRange(-2, -2);
|
||
|
||
SetScrollbars(0, 0, 0, 0, 0, 0);
|
||
|
||
if (m_freezeCount == 0)
|
||
{
|
||
SetupScrollbars();
|
||
Refresh(false);
|
||
}
|
||
SendUpdateEvent();
|
||
}
|
||
|
||
/// Painting
|
||
void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
|
||
{
|
||
if (GetCaret())
|
||
GetCaret()->Hide();
|
||
|
||
{
|
||
wxBufferedPaintDC dc(this, m_bufferBitmap);
|
||
//wxLogDebug(wxT("OnPaint"));
|
||
|
||
PrepareDC(dc);
|
||
|
||
if (m_freezeCount > 0)
|
||
return;
|
||
|
||
dc.SetFont(GetFont());
|
||
|
||
// Paint the background
|
||
PaintBackground(dc);
|
||
|
||
wxRegion dirtyRegion = GetUpdateRegion();
|
||
|
||
wxRect drawingArea(GetLogicalPoint(wxPoint(0, 0)), GetClientSize());
|
||
wxRect availableSpace(GetClientSize());
|
||
if (GetBuffer().GetDirty())
|
||
{
|
||
GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
|
||
GetBuffer().SetDirty(false);
|
||
SetupScrollbars();
|
||
}
|
||
|
||
GetBuffer().Draw(dc, GetBuffer().GetRange(), GetInternalSelectionRange(), drawingArea, 0 /* descent */, 0 /* flags */);
|
||
}
|
||
|
||
if (GetCaret())
|
||
GetCaret()->Show();
|
||
|
||
PositionCaret();
|
||
}
|
||
|
||
// Empty implementation, to prevent flicker
|
||
void wxRichTextCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
|
||
{
|
||
}
|
||
|
||
void wxRichTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
|
||
{
|
||
wxCaret* caret = new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16);
|
||
SetCaret(caret);
|
||
caret->Show();
|
||
PositionCaret();
|
||
|
||
if (!IsFrozen())
|
||
Refresh(false);
|
||
}
|
||
|
||
void wxRichTextCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event))
|
||
{
|
||
SetCaret(NULL);
|
||
|
||
if (!IsFrozen())
|
||
Refresh(false);
|
||
}
|
||
|
||
/// Left-click
|
||
void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event)
|
||
{
|
||
SetFocus();
|
||
|
||
wxClientDC dc(this);
|
||
PrepareDC(dc);
|
||
dc.SetFont(GetFont());
|
||
|
||
long position = 0;
|
||
int hit = GetBuffer().HitTest(dc, event.GetLogicalPosition(dc), position);
|
||
|
||
if (hit != wxRICHTEXT_HITTEST_NONE)
|
||
{
|
||
m_dragStart = event.GetLogicalPosition(dc);
|
||
m_dragging = true;
|
||
CaptureMouse();
|
||
|
||
SelectNone();
|
||
|
||
bool caretAtLineStart = false;
|
||
|
||
if (hit & wxRICHTEXT_HITTEST_BEFORE)
|
||
{
|
||
// If we're at the start of a line (but not first in para)
|
||
// then we should keep the caret showing at the start of the line
|
||
// by showing the m_caretAtLineStart flag.
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
|
||
wxRichTextLine* line = GetBuffer().GetLineAtPosition(position);
|
||
|
||
if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position)
|
||
caretAtLineStart = true;
|
||
position --;
|
||
}
|
||
|
||
MoveCaret(position, caretAtLineStart);
|
||
SetDefaultStyleToCursorStyle();
|
||
}
|
||
|
||
event.Skip();
|
||
}
|
||
|
||
/// Left-up
|
||
void wxRichTextCtrl::OnLeftUp(wxMouseEvent& WXUNUSED(event))
|
||
{
|
||
if (m_dragging)
|
||
{
|
||
m_dragging = false;
|
||
if (GetCapture() == this)
|
||
ReleaseMouse();
|
||
}
|
||
}
|
||
|
||
/// Left-click
|
||
void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event)
|
||
{
|
||
if (!event.Dragging())
|
||
{
|
||
event.Skip();
|
||
return;
|
||
}
|
||
|
||
wxClientDC dc(this);
|
||
PrepareDC(dc);
|
||
dc.SetFont(GetFont());
|
||
|
||
long position = 0;
|
||
wxPoint logicalPt = event.GetLogicalPosition(dc);
|
||
int hit = GetBuffer().HitTest(dc, logicalPt, position);
|
||
|
||
if (m_dragging && hit != wxRICHTEXT_HITTEST_NONE)
|
||
{
|
||
// TODO: test closeness
|
||
|
||
bool caretAtLineStart = false;
|
||
|
||
if (hit & wxRICHTEXT_HITTEST_BEFORE)
|
||
{
|
||
// If we're at the start of a line (but not first in para)
|
||
// then we should keep the caret showing at the start of the line
|
||
// by showing the m_caretAtLineStart flag.
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
|
||
wxRichTextLine* line = GetBuffer().GetLineAtPosition(position);
|
||
|
||
if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position)
|
||
caretAtLineStart = true;
|
||
position --;
|
||
}
|
||
|
||
if (m_caretPosition != position)
|
||
{
|
||
bool extendSel = ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN);
|
||
|
||
MoveCaret(position, caretAtLineStart);
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Right-click
|
||
void wxRichTextCtrl::OnRightClick(wxMouseEvent& event)
|
||
{
|
||
SetFocus();
|
||
event.Skip();
|
||
}
|
||
|
||
/// Left-double-click
|
||
void wxRichTextCtrl::OnLeftDClick(wxMouseEvent& event)
|
||
{
|
||
SelectWord(GetCaretPosition()+1);
|
||
event.Skip();
|
||
}
|
||
|
||
/// Middle-click
|
||
void wxRichTextCtrl::OnMiddleClick(wxMouseEvent& event)
|
||
{
|
||
event.Skip();
|
||
}
|
||
|
||
/// Key press
|
||
void wxRichTextCtrl::OnChar(wxKeyEvent& event)
|
||
{
|
||
int flags = 0;
|
||
if (event.ControlDown())
|
||
flags |= wxRICHTEXT_CTRL_DOWN;
|
||
if (event.ShiftDown())
|
||
flags |= wxRICHTEXT_SHIFT_DOWN;
|
||
if (event.AltDown())
|
||
flags |= wxRICHTEXT_ALT_DOWN;
|
||
|
||
if (event.GetKeyCode() == WXK_LEFT ||
|
||
event.GetKeyCode() == WXK_RIGHT ||
|
||
event.GetKeyCode() == WXK_UP ||
|
||
event.GetKeyCode() == WXK_DOWN ||
|
||
event.GetKeyCode() == WXK_HOME ||
|
||
event.GetKeyCode() == WXK_PAGEUP ||
|
||
event.GetKeyCode() == WXK_PAGEDOWN ||
|
||
event.GetKeyCode() == WXK_END)
|
||
{
|
||
KeyboardNavigate(event.GetKeyCode(), flags);
|
||
return;
|
||
}
|
||
|
||
// all the other keys modify the controls contents which shouldn't be
|
||
// possible if we're read-only
|
||
if ( !IsEditable() )
|
||
{
|
||
event.Skip();
|
||
return;
|
||
}
|
||
|
||
if (event.GetKeyCode() == WXK_RETURN)
|
||
{
|
||
BeginBatchUndo(_("Insert Text"));
|
||
|
||
long newPos = m_caretPosition;
|
||
|
||
DeleteSelectedContent(& newPos);
|
||
|
||
GetBuffer().InsertNewlineWithUndo(newPos+1, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
|
||
|
||
wxRichTextEvent cmdEvent(
|
||
wxEVT_COMMAND_RICHTEXT_RETURN,
|
||
GetId());
|
||
cmdEvent.SetEventObject(this);
|
||
cmdEvent.SetFlags(flags);
|
||
GetEventHandler()->ProcessEvent(cmdEvent);
|
||
|
||
EndBatchUndo();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
ScrollIntoView(m_caretPosition, WXK_RIGHT);
|
||
}
|
||
else if (event.GetKeyCode() == WXK_BACK)
|
||
{
|
||
BeginBatchUndo(_("Delete Text"));
|
||
|
||
// Submit range in character positions, which are greater than caret positions,
|
||
// so subtract 1 for deleted character and add 1 for conversion to character position.
|
||
if (m_caretPosition > -1 && !HasSelection())
|
||
{
|
||
GetBuffer().DeleteRangeWithUndo(wxRichTextRange(m_caretPosition, m_caretPosition),
|
||
m_caretPosition, // Current caret position
|
||
m_caretPosition-1, // New caret position
|
||
this);
|
||
}
|
||
else
|
||
DeleteSelectedContent();
|
||
|
||
EndBatchUndo();
|
||
|
||
// Shouldn't this be in Do()?
|
||
if (GetLastPosition() == -1)
|
||
{
|
||
GetBuffer().Reset();
|
||
|
||
m_caretPosition = -1;
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
}
|
||
|
||
ScrollIntoView(m_caretPosition, WXK_LEFT);
|
||
}
|
||
else if (event.GetKeyCode() == WXK_DELETE)
|
||
{
|
||
BeginBatchUndo(_("Delete Text"));
|
||
|
||
// Submit range in character positions, which are greater than caret positions,
|
||
if (m_caretPosition < GetBuffer().GetRange().GetEnd()+1 && !HasSelection())
|
||
{
|
||
GetBuffer().DeleteRangeWithUndo(wxRichTextRange(m_caretPosition+1, m_caretPosition+1),
|
||
m_caretPosition, // Current caret position
|
||
m_caretPosition+1, // New caret position
|
||
this);
|
||
}
|
||
else
|
||
DeleteSelectedContent();
|
||
|
||
EndBatchUndo();
|
||
|
||
// Shouldn't this be in Do()?
|
||
if (GetLastPosition() == -1)
|
||
{
|
||
GetBuffer().Reset();
|
||
|
||
m_caretPosition = -1;
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
BeginBatchUndo(_("Insert Text"));
|
||
|
||
long newPos = m_caretPosition;
|
||
DeleteSelectedContent(& newPos);
|
||
|
||
wxString str = (wxChar) event.GetKeyCode();
|
||
GetBuffer().InsertTextWithUndo(newPos+1, str, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
|
||
|
||
EndBatchUndo();
|
||
|
||
SetDefaultStyleToCursorStyle();
|
||
ScrollIntoView(m_caretPosition, WXK_RIGHT);
|
||
}
|
||
}
|
||
|
||
/// Delete content if there is a selection, e.g. when pressing a key.
|
||
bool wxRichTextCtrl::DeleteSelectedContent(long* newPos)
|
||
{
|
||
if (HasSelection())
|
||
{
|
||
long pos = m_selectionRange.GetStart();
|
||
GetBuffer().DeleteRangeWithUndo(m_selectionRange,
|
||
m_caretPosition, // Current caret position
|
||
pos, // New caret position
|
||
this);
|
||
m_selectionRange.SetRange(-2, -2);
|
||
|
||
if (newPos)
|
||
*newPos = pos-1;
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
/// Keyboard navigation
|
||
|
||
/*
|
||
|
||
Left: left one character
|
||
Right: right one character
|
||
Up: up one line
|
||
Down: down one line
|
||
Ctrl-Left: left one word
|
||
Ctrl-Right: right one word
|
||
Ctrl-Up: previous paragraph start
|
||
Ctrl-Down: next start of paragraph
|
||
Home: start of line
|
||
End: end of line
|
||
Ctrl-Home: start of document
|
||
Ctrl-End: end of document
|
||
Page-Up: Up a screen
|
||
Page-Down: Down a screen
|
||
|
||
Maybe:
|
||
|
||
Ctrl-Alt-PgUp: Start of window
|
||
Ctrl-Alt-PgDn: End of window
|
||
F8: Start selection mode
|
||
Esc: End selection mode
|
||
|
||
Adding Shift does the above but starts/extends selection.
|
||
|
||
|
||
*/
|
||
|
||
bool wxRichTextCtrl::KeyboardNavigate(int keyCode, int flags)
|
||
{
|
||
bool success = false;
|
||
|
||
if (keyCode == WXK_RIGHT)
|
||
{
|
||
if (flags & wxRICHTEXT_CTRL_DOWN)
|
||
success = WordRight(1, flags);
|
||
else
|
||
success = MoveRight(1, flags);
|
||
}
|
||
else if (keyCode == WXK_LEFT)
|
||
{
|
||
if (flags & wxRICHTEXT_CTRL_DOWN)
|
||
success = WordLeft(1, flags);
|
||
else
|
||
success = MoveLeft(1, flags);
|
||
}
|
||
else if (keyCode == WXK_UP)
|
||
{
|
||
if (flags & wxRICHTEXT_CTRL_DOWN)
|
||
success = MoveToParagraphStart(flags);
|
||
else
|
||
success = MoveUp(1, flags);
|
||
}
|
||
else if (keyCode == WXK_DOWN)
|
||
{
|
||
if (flags & wxRICHTEXT_CTRL_DOWN)
|
||
success = MoveToParagraphEnd(flags);
|
||
else
|
||
success = MoveDown(1, flags);
|
||
}
|
||
else if (keyCode == WXK_PAGEUP)
|
||
{
|
||
success = PageUp(1, flags);
|
||
}
|
||
else if (keyCode == WXK_PAGEDOWN)
|
||
{
|
||
success = PageDown(1, flags);
|
||
}
|
||
else if (keyCode == WXK_HOME)
|
||
{
|
||
if (flags & wxRICHTEXT_CTRL_DOWN)
|
||
success = MoveHome(flags);
|
||
else
|
||
success = MoveToLineStart(flags);
|
||
}
|
||
else if (keyCode == WXK_END)
|
||
{
|
||
if (flags & wxRICHTEXT_CTRL_DOWN)
|
||
success = MoveEnd(flags);
|
||
else
|
||
success = MoveToLineEnd(flags);
|
||
}
|
||
|
||
if (success)
|
||
{
|
||
ScrollIntoView(m_caretPosition, keyCode);
|
||
SetDefaultStyleToCursorStyle();
|
||
}
|
||
|
||
return success;
|
||
}
|
||
|
||
/// Extend the selection. Selections are in caret positions.
|
||
bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags)
|
||
{
|
||
if (flags & wxRICHTEXT_SHIFT_DOWN)
|
||
{
|
||
// If not currently selecting, start selecting
|
||
if (m_selectionRange.GetStart() == -2)
|
||
{
|
||
m_selectionAnchor = oldPos;
|
||
|
||
if (oldPos > newPos)
|
||
m_selectionRange.SetRange(newPos+1, oldPos);
|
||
else
|
||
m_selectionRange.SetRange(oldPos+1, newPos);
|
||
}
|
||
else
|
||
{
|
||
// Always ensure that the selection range start is greater than
|
||
// the end.
|
||
if (newPos > m_selectionAnchor)
|
||
m_selectionRange.SetRange(m_selectionAnchor+1, newPos);
|
||
else
|
||
m_selectionRange.SetRange(newPos+1, m_selectionAnchor);
|
||
}
|
||
|
||
if (m_selectionRange.GetStart() > m_selectionRange.GetEnd())
|
||
{
|
||
wxLogDebug(wxT("Strange selection range"));
|
||
}
|
||
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
/// Scroll into view, returning true if we scrolled.
|
||
/// This takes a _caret_ position.
|
||
bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode)
|
||
{
|
||
wxRichTextLine* line = GetVisibleLineForCaretPosition(position);
|
||
|
||
if (!line)
|
||
return false;
|
||
|
||
int ppuX, ppuY;
|
||
GetScrollPixelsPerUnit(& ppuX, & ppuY);
|
||
|
||
int startXUnits, startYUnits;
|
||
GetViewStart(& startXUnits, & startYUnits);
|
||
int startY = startYUnits * ppuY;
|
||
|
||
int sx = 0, sy = 0;
|
||
GetVirtualSize(& sx, & sy);
|
||
int sxUnits = 0;
|
||
int syUnits = 0;
|
||
if (ppuY != 0)
|
||
syUnits = sy/ppuY;
|
||
|
||
wxRect rect = line->GetRect();
|
||
|
||
bool scrolled = false;
|
||
|
||
wxSize clientSize = GetClientSize();
|
||
|
||
// Going down
|
||
if (keyCode == WXK_DOWN || keyCode == WXK_RIGHT || keyCode == WXK_END || keyCode == WXK_PAGEDOWN)
|
||
{
|
||
if ((rect.y + rect.height) > (clientSize.y + startY))
|
||
{
|
||
// Make it scroll so this item is at the bottom
|
||
// of the window
|
||
int y = rect.y - (clientSize.y - rect.height);
|
||
int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
|
||
|
||
// If we're still off the screen, scroll another line down
|
||
if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY)))
|
||
yUnits ++;
|
||
|
||
if (startYUnits != yUnits)
|
||
{
|
||
SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
|
||
scrolled = true;
|
||
}
|
||
}
|
||
else if (rect.y < startY)
|
||
{
|
||
// Make it scroll so this item is at the top
|
||
// of the window
|
||
int y = rect.y ;
|
||
int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
|
||
|
||
if (startYUnits != yUnits)
|
||
{
|
||
SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
|
||
scrolled = true;
|
||
}
|
||
}
|
||
}
|
||
// Going up
|
||
else if (keyCode == WXK_UP || keyCode == WXK_LEFT || keyCode == WXK_HOME || keyCode == WXK_PAGEUP )
|
||
{
|
||
if (rect.y < startY)
|
||
{
|
||
// Make it scroll so this item is at the top
|
||
// of the window
|
||
int y = rect.y ;
|
||
int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
|
||
|
||
if (startYUnits != yUnits)
|
||
{
|
||
SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
|
||
scrolled = true;
|
||
}
|
||
}
|
||
else if ((rect.y + rect.height) > (clientSize.y + startY))
|
||
{
|
||
// Make it scroll so this item is at the bottom
|
||
// of the window
|
||
int y = rect.y - (clientSize.y - rect.height);
|
||
int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
|
||
|
||
// If we're still off the screen, scroll another line down
|
||
if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY)))
|
||
yUnits ++;
|
||
|
||
if (startYUnits != yUnits)
|
||
{
|
||
SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
|
||
scrolled = true;
|
||
}
|
||
}
|
||
}
|
||
PositionCaret();
|
||
|
||
return scrolled;
|
||
}
|
||
|
||
/// Is the given position visible on the screen?
|
||
bool wxRichTextCtrl::IsPositionVisible(long pos) const
|
||
{
|
||
wxRichTextLine* line = GetVisibleLineForCaretPosition(pos-1);
|
||
|
||
if (!line)
|
||
return false;
|
||
|
||
int ppuX, ppuY;
|
||
GetScrollPixelsPerUnit(& ppuX, & ppuY);
|
||
|
||
int startX, startY;
|
||
GetViewStart(& startX, & startY);
|
||
startX = 0;
|
||
startY = startY * ppuY;
|
||
|
||
int sx = 0, sy = 0;
|
||
GetVirtualSize(& sx, & sy);
|
||
sx = 0;
|
||
if (ppuY != 0)
|
||
sy = sy/ppuY;
|
||
|
||
wxRect rect = line->GetRect();
|
||
|
||
wxSize clientSize = GetClientSize();
|
||
|
||
return !(((rect.y + rect.height) > (clientSize.y + startY)) || rect.y < startY);
|
||
}
|
||
|
||
void wxRichTextCtrl::SetCaretPosition(long position, bool showAtLineStart)
|
||
{
|
||
m_caretPosition = position;
|
||
m_caretAtLineStart = showAtLineStart;
|
||
}
|
||
|
||
/// Move caret one visual step forward: this may mean setting a flag
|
||
/// and keeping the same position if we're going from the end of one line
|
||
/// to the start of the next, which may be the exact same caret position.
|
||
void wxRichTextCtrl::MoveCaretForward(long oldPosition)
|
||
{
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(oldPosition);
|
||
|
||
// Only do the check if we're not at the end of the paragraph (where things work OK
|
||
// anyway)
|
||
if (para && (oldPosition != para->GetRange().GetEnd() - 1))
|
||
{
|
||
wxRichTextLine* line = GetBuffer().GetLineAtPosition(oldPosition);
|
||
|
||
if (line)
|
||
{
|
||
wxRichTextRange lineRange = line->GetAbsoluteRange();
|
||
|
||
// We're at the end of a line. See whether we need to
|
||
// stay at the same actual caret position but change visual
|
||
// position, or not.
|
||
if (oldPosition == lineRange.GetEnd())
|
||
{
|
||
if (m_caretAtLineStart)
|
||
{
|
||
// We're already at the start of the line, so actually move on now.
|
||
m_caretPosition = oldPosition + 1;
|
||
m_caretAtLineStart = false;
|
||
}
|
||
else
|
||
{
|
||
// We're showing at the end of the line, so keep to
|
||
// the same position but indicate that we're to show
|
||
// at the start of the next line.
|
||
m_caretPosition = oldPosition;
|
||
m_caretAtLineStart = true;
|
||
}
|
||
SetDefaultStyleToCursorStyle();
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
m_caretPosition ++;
|
||
SetDefaultStyleToCursorStyle();
|
||
}
|
||
|
||
/// Move caret one visual step backward: this may mean setting a flag
|
||
/// and keeping the same position if we're going from the end of one line
|
||
/// to the start of the next, which may be the exact same caret position.
|
||
void wxRichTextCtrl::MoveCaretBack(long oldPosition)
|
||
{
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(oldPosition);
|
||
|
||
// Only do the check if we're not at the start of the paragraph (where things work OK
|
||
// anyway)
|
||
if (para && (oldPosition != para->GetRange().GetStart()))
|
||
{
|
||
wxRichTextLine* line = GetBuffer().GetLineAtPosition(oldPosition);
|
||
|
||
if (line)
|
||
{
|
||
wxRichTextRange lineRange = line->GetAbsoluteRange();
|
||
|
||
// We're at the start of a line. See whether we need to
|
||
// stay at the same actual caret position but change visual
|
||
// position, or not.
|
||
if (oldPosition == lineRange.GetStart())
|
||
{
|
||
m_caretPosition = oldPosition-1;
|
||
m_caretAtLineStart = true;
|
||
return;
|
||
}
|
||
else if (oldPosition == lineRange.GetEnd())
|
||
{
|
||
if (m_caretAtLineStart)
|
||
{
|
||
// We're at the start of the line, so keep the same caret position
|
||
// but clear the start-of-line flag.
|
||
m_caretPosition = oldPosition;
|
||
m_caretAtLineStart = false;
|
||
}
|
||
else
|
||
{
|
||
// We're showing at the end of the line, so go back
|
||
// to the previous character position.
|
||
m_caretPosition = oldPosition - 1;
|
||
}
|
||
SetDefaultStyleToCursorStyle();
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
m_caretPosition --;
|
||
SetDefaultStyleToCursorStyle();
|
||
}
|
||
|
||
/// Move right
|
||
bool wxRichTextCtrl::MoveRight(int noPositions, int flags)
|
||
{
|
||
long endPos = GetBuffer().GetRange().GetEnd();
|
||
|
||
if (m_caretPosition + noPositions < endPos)
|
||
{
|
||
long oldPos = m_caretPosition;
|
||
long newPos = m_caretPosition + noPositions;
|
||
|
||
bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
// Determine by looking at oldPos and m_caretPosition whether
|
||
// we moved from the end of a line to the start of the next line, in which case
|
||
// we want to adjust the caret position such that it is positioned at the
|
||
// start of the next line, rather than jumping past the first character of the
|
||
// line.
|
||
if (noPositions == 1 && !extendSel)
|
||
MoveCaretForward(oldPos);
|
||
else
|
||
SetCaretPosition(newPos);
|
||
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
/// Move left
|
||
bool wxRichTextCtrl::MoveLeft(int noPositions, int flags)
|
||
{
|
||
long startPos = -1;
|
||
|
||
if (m_caretPosition > startPos - noPositions + 1)
|
||
{
|
||
long oldPos = m_caretPosition;
|
||
long newPos = m_caretPosition - noPositions;
|
||
bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
if (noPositions == 1 && !extendSel)
|
||
MoveCaretBack(oldPos);
|
||
else
|
||
SetCaretPosition(newPos);
|
||
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
/// Move up
|
||
bool wxRichTextCtrl::MoveUp(int noLines, int flags)
|
||
{
|
||
return MoveDown(- noLines, flags);
|
||
}
|
||
|
||
/// Move up
|
||
bool wxRichTextCtrl::MoveDown(int noLines, int flags)
|
||
{
|
||
if (!GetCaret())
|
||
return false;
|
||
|
||
long lineNumber = GetBuffer().GetVisibleLineNumber(m_caretPosition, true, m_caretAtLineStart);
|
||
wxPoint pt = GetCaret()->GetPosition();
|
||
long newLine = lineNumber + noLines;
|
||
|
||
if (lineNumber != -1)
|
||
{
|
||
if (noLines > 0)
|
||
{
|
||
long lastLine = GetBuffer().GetVisibleLineNumber(GetBuffer().GetRange().GetEnd());
|
||
|
||
if (newLine > lastLine)
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (newLine < 0)
|
||
return false;
|
||
}
|
||
}
|
||
|
||
wxRichTextLine* lineObj = GetBuffer().GetLineForVisibleLineNumber(newLine);
|
||
if (lineObj)
|
||
{
|
||
pt.y = lineObj->GetAbsolutePosition().y + 2;
|
||
}
|
||
else
|
||
return false;
|
||
|
||
long newPos = 0;
|
||
wxClientDC dc(this);
|
||
PrepareDC(dc);
|
||
dc.SetFont(GetFont());
|
||
|
||
int hitTest = GetBuffer().HitTest(dc, pt, newPos);
|
||
|
||
if (hitTest != wxRICHTEXT_HITTEST_NONE)
|
||
{
|
||
// If end of previous line, and hitTest is wxRICHTEXT_HITTEST_BEFORE,
|
||
// we want to be at the end of the last line but with m_caretAtLineStart set to true,
|
||
// so we view the caret at the start of the line.
|
||
bool caretLineStart = false;
|
||
if (hitTest == wxRICHTEXT_HITTEST_BEFORE)
|
||
{
|
||
wxRichTextLine* thisLine = GetBuffer().GetLineAtPosition(newPos-1);
|
||
wxRichTextRange lineRange;
|
||
if (thisLine)
|
||
lineRange = thisLine->GetAbsoluteRange();
|
||
|
||
if (thisLine && (newPos-1) == lineRange.GetEnd())
|
||
{
|
||
newPos --;
|
||
caretLineStart = true;
|
||
}
|
||
else
|
||
{
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(newPos);
|
||
if (para && para->GetRange().GetStart() == newPos)
|
||
newPos --;
|
||
}
|
||
}
|
||
|
||
long newSelEnd = newPos;
|
||
|
||
bool extendSel = ExtendSelection(m_caretPosition, newSelEnd, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
SetCaretPosition(newPos, caretLineStart);
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// Move to the end of the paragraph
|
||
bool wxRichTextCtrl::MoveToParagraphEnd(int flags)
|
||
{
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(m_caretPosition, true);
|
||
if (para)
|
||
{
|
||
long newPos = para->GetRange().GetEnd() - 1;
|
||
bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
SetCaretPosition(newPos);
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// Move to the start of the paragraph
|
||
bool wxRichTextCtrl::MoveToParagraphStart(int flags)
|
||
{
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(m_caretPosition, true);
|
||
if (para)
|
||
{
|
||
long newPos = para->GetRange().GetStart() - 1;
|
||
bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
SetCaretPosition(newPos);
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// Move to the end of the line
|
||
bool wxRichTextCtrl::MoveToLineEnd(int flags)
|
||
{
|
||
wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
|
||
|
||
if (line)
|
||
{
|
||
wxRichTextRange lineRange = line->GetAbsoluteRange();
|
||
long newPos = lineRange.GetEnd();
|
||
bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
SetCaretPosition(newPos);
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// Move to the start of the line
|
||
bool wxRichTextCtrl::MoveToLineStart(int flags)
|
||
{
|
||
wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
|
||
if (line)
|
||
{
|
||
wxRichTextRange lineRange = line->GetAbsoluteRange();
|
||
long newPos = lineRange.GetStart()-1;
|
||
|
||
bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(line);
|
||
|
||
SetCaretPosition(newPos, para->GetRange().GetStart() != lineRange.GetStart());
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// Move to the start of the buffer
|
||
bool wxRichTextCtrl::MoveHome(int flags)
|
||
{
|
||
if (m_caretPosition != -1)
|
||
{
|
||
bool extendSel = ExtendSelection(m_caretPosition, -1, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
SetCaretPosition(-1);
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
/// Move to the end of the buffer
|
||
bool wxRichTextCtrl::MoveEnd(int flags)
|
||
{
|
||
long endPos = GetBuffer().GetRange().GetEnd()-1;
|
||
|
||
if (m_caretPosition != endPos)
|
||
{
|
||
bool extendSel = ExtendSelection(m_caretPosition, endPos, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
SetCaretPosition(endPos);
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
/// Move noPages pages up
|
||
bool wxRichTextCtrl::PageUp(int noPages, int flags)
|
||
{
|
||
return PageDown(- noPages, flags);
|
||
}
|
||
|
||
/// Move noPages pages down
|
||
bool wxRichTextCtrl::PageDown(int noPages, int flags)
|
||
{
|
||
// Calculate which line occurs noPages * screen height further down.
|
||
wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
|
||
if (line)
|
||
{
|
||
wxSize clientSize = GetClientSize();
|
||
int newY = line->GetAbsolutePosition().y + noPages*clientSize.y;
|
||
|
||
wxRichTextLine* newLine = GetBuffer().GetLineAtYPosition(newY);
|
||
if (newLine)
|
||
{
|
||
wxRichTextRange lineRange = newLine->GetAbsoluteRange();
|
||
long pos = lineRange.GetStart()-1;
|
||
if (pos != m_caretPosition)
|
||
{
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(newLine);
|
||
|
||
bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
SetCaretPosition(pos, para->GetRange().GetStart() != lineRange.GetStart());
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// Finds the caret position for the next word
|
||
long wxRichTextCtrl::FindNextWordPosition(int direction) const
|
||
{
|
||
long endPos = GetBuffer().GetRange().GetEnd();
|
||
|
||
if (direction > 0)
|
||
{
|
||
long i = m_caretPosition+1+direction; // +1 for conversion to character pos
|
||
|
||
// First skip current text to space
|
||
while (i < endPos && i > -1)
|
||
{
|
||
// i is in character, not caret positions
|
||
wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
|
||
if (text != wxT(" ") && !text.empty())
|
||
i += direction;
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
while (i < endPos && i > -1)
|
||
{
|
||
// i is in character, not caret positions
|
||
wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
|
||
if (text.empty()) // End of paragraph, or maybe an image
|
||
return wxMax(-1, i - 1);
|
||
else if (text == wxT(" ") || text.empty())
|
||
i += direction;
|
||
else
|
||
{
|
||
// Convert to caret position
|
||
return wxMax(-1, i - 1);
|
||
}
|
||
}
|
||
if (i >= endPos)
|
||
return endPos-1;
|
||
return i-1;
|
||
}
|
||
else
|
||
{
|
||
long i = m_caretPosition;
|
||
|
||
// First skip white space
|
||
while (i < endPos && i > -1)
|
||
{
|
||
// i is in character, not caret positions
|
||
wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
|
||
if (text.empty()) // End of paragraph, or maybe an image
|
||
break;
|
||
else if (text == wxT(" ") || text.empty())
|
||
i += direction;
|
||
else
|
||
break;
|
||
}
|
||
// Next skip current text to space
|
||
while (i < endPos && i > -1)
|
||
{
|
||
// i is in character, not caret positions
|
||
wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
|
||
if (text != wxT(" ") /* && !text.empty() */)
|
||
i += direction;
|
||
else
|
||
{
|
||
return i;
|
||
}
|
||
}
|
||
if (i < -1)
|
||
return -1;
|
||
return i;
|
||
}
|
||
}
|
||
|
||
/// Move n words left
|
||
bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags)
|
||
{
|
||
long pos = FindNextWordPosition(-1);
|
||
if (pos != m_caretPosition)
|
||
{
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true);
|
||
|
||
bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
SetCaretPosition(pos, para->GetRange().GetStart() != pos);
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// Move n words right
|
||
bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags)
|
||
{
|
||
long pos = FindNextWordPosition(1);
|
||
if (pos != m_caretPosition)
|
||
{
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true);
|
||
|
||
bool extendSel = ExtendSelection(m_caretPosition, pos, flags);
|
||
if (!extendSel)
|
||
SelectNone();
|
||
|
||
SetCaretPosition(pos, para->GetRange().GetStart() != pos);
|
||
PositionCaret();
|
||
SetDefaultStyleToCursorStyle();
|
||
|
||
if (extendSel)
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// Sizing
|
||
void wxRichTextCtrl::OnSize(wxSizeEvent& event)
|
||
{
|
||
// Only do sizing optimization for large buffers
|
||
if (GetBuffer().GetRange().GetEnd() > m_delayedLayoutThreshold)
|
||
{
|
||
m_fullLayoutRequired = true;
|
||
m_fullLayoutTime = wxGetLocalTimeMillis();
|
||
m_fullLayoutSavedPosition = GetFirstVisiblePosition();
|
||
LayoutContent(true /* onlyVisibleRect */);
|
||
}
|
||
else
|
||
GetBuffer().Invalidate(wxRICHTEXT_ALL);
|
||
|
||
RecreateBuffer();
|
||
|
||
event.Skip();
|
||
}
|
||
|
||
|
||
/// Idle-time processing
|
||
void wxRichTextCtrl::OnIdle(wxIdleEvent& event)
|
||
{
|
||
const int layoutInterval = wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL;
|
||
|
||
if (m_fullLayoutRequired && (wxGetLocalTimeMillis() > (m_fullLayoutTime + layoutInterval)))
|
||
{
|
||
m_fullLayoutRequired = false;
|
||
m_fullLayoutTime = 0;
|
||
GetBuffer().Invalidate(wxRICHTEXT_ALL);
|
||
ShowPosition(m_fullLayoutSavedPosition);
|
||
Refresh(false);
|
||
}
|
||
|
||
if (m_caretPositionForDefaultStyle != -2)
|
||
{
|
||
// If the caret position has changed, no longer reflect the default style
|
||
// in the UI.
|
||
if (GetCaretPosition() != m_caretPositionForDefaultStyle)
|
||
m_caretPositionForDefaultStyle = -2;
|
||
}
|
||
|
||
event.Skip();
|
||
}
|
||
|
||
/// Scrolling
|
||
void wxRichTextCtrl::OnScroll(wxScrollWinEvent& event)
|
||
{
|
||
// Not used
|
||
event.Skip();
|
||
}
|
||
|
||
/// Set up scrollbars, e.g. after a resize
|
||
void wxRichTextCtrl::SetupScrollbars(bool atTop)
|
||
{
|
||
if (m_freezeCount)
|
||
return;
|
||
|
||
if (GetBuffer().IsEmpty())
|
||
{
|
||
SetScrollbars(0, 0, 0, 0, 0, 0);
|
||
return;
|
||
}
|
||
|
||
// TODO: reimplement scrolling so we scroll by line, not by fixed number
|
||
// of pixels. See e.g. wxVScrolledWindow for ideas.
|
||
int pixelsPerUnit = 5;
|
||
wxSize clientSize = GetClientSize();
|
||
|
||
int maxHeight = GetBuffer().GetCachedSize().y;
|
||
|
||
// Round up so we have at least maxHeight pixels
|
||
int unitsY = (int) (((float)maxHeight/(float)pixelsPerUnit) + 0.5);
|
||
|
||
int startX = 0, startY = 0;
|
||
if (!atTop)
|
||
GetViewStart(& startX, & startY);
|
||
|
||
int maxPositionX = 0; // wxMax(sz.x - clientSize.x, 0);
|
||
int maxPositionY = (int) ((((float)(wxMax((unitsY*pixelsPerUnit) - clientSize.y, 0)))/((float)pixelsPerUnit)) + 0.5);
|
||
|
||
// Move to previous scroll position if
|
||
// possible
|
||
SetScrollbars(0, pixelsPerUnit,
|
||
0, unitsY,
|
||
wxMin(maxPositionX, startX), wxMin(maxPositionY, startY));
|
||
}
|
||
|
||
/// Paint the background
|
||
void wxRichTextCtrl::PaintBackground(wxDC& dc)
|
||
{
|
||
wxColour backgroundColour = GetBackgroundColour();
|
||
if (!backgroundColour.Ok())
|
||
backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
|
||
|
||
// Clear the background
|
||
dc.SetBrush(wxBrush(backgroundColour));
|
||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||
wxRect windowRect(GetClientSize());
|
||
windowRect.x -= 2; windowRect.y -= 2;
|
||
windowRect.width += 4; windowRect.height += 4;
|
||
|
||
// We need to shift the rectangle to take into account
|
||
// scrolling. Converting device to logical coordinates.
|
||
CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y);
|
||
dc.DrawRectangle(windowRect);
|
||
}
|
||
|
||
/// Recreate buffer bitmap if necessary
|
||
bool wxRichTextCtrl::RecreateBuffer(const wxSize& size)
|
||
{
|
||
wxSize sz = size;
|
||
if (sz == wxDefaultSize)
|
||
sz = GetClientSize();
|
||
|
||
if (sz.x < 1 || sz.y < 1)
|
||
return false;
|
||
|
||
if (!m_bufferBitmap.Ok() || m_bufferBitmap.GetWidth() < sz.x || m_bufferBitmap.GetHeight() < sz.y)
|
||
m_bufferBitmap = wxBitmap(sz.x, sz.y);
|
||
return m_bufferBitmap.Ok();
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// file IO functions
|
||
// ----------------------------------------------------------------------------
|
||
|
||
#if !wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
|
||
bool wxRichTextCtrl::LoadFile(const wxString& filename, int fileType)
|
||
{
|
||
return DoLoadFile(filename, fileType);
|
||
}
|
||
|
||
bool wxRichTextCtrl::SaveFile(const wxString& filename, int fileType)
|
||
{
|
||
wxString filenameToUse = filename.empty() ? m_filename : filename;
|
||
if ( filenameToUse.empty() )
|
||
{
|
||
// what kind of message to give? is it an error or a program bug?
|
||
wxLogDebug(wxT("Can't save textctrl to file without filename."));
|
||
|
||
return false;
|
||
}
|
||
|
||
return DoSaveFile(filenameToUse, fileType);
|
||
}
|
||
#endif
|
||
|
||
bool wxRichTextCtrl::DoLoadFile(const wxString& filename, int fileType)
|
||
{
|
||
bool success = GetBuffer().LoadFile(filename, fileType);
|
||
if (success)
|
||
m_filename = filename;
|
||
|
||
DiscardEdits();
|
||
SetInsertionPoint(0);
|
||
LayoutContent();
|
||
PositionCaret();
|
||
SetupScrollbars(true);
|
||
Refresh(false);
|
||
SendUpdateEvent();
|
||
|
||
if (success)
|
||
return true;
|
||
else
|
||
{
|
||
wxLogError(_("File couldn't be loaded."));
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
bool wxRichTextCtrl::DoSaveFile(const wxString& filename, int fileType)
|
||
{
|
||
if (GetBuffer().SaveFile(filename, fileType))
|
||
{
|
||
m_filename = filename;
|
||
|
||
DiscardEdits();
|
||
|
||
return true;
|
||
}
|
||
|
||
wxLogError(_("The text couldn't be saved."));
|
||
|
||
return false;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// wxRichTextCtrl specific functionality
|
||
// ----------------------------------------------------------------------------
|
||
|
||
/// Add a new paragraph of text to the end of the buffer
|
||
wxRichTextRange wxRichTextCtrl::AddParagraph(const wxString& text)
|
||
{
|
||
return GetBuffer().AddParagraph(text);
|
||
}
|
||
|
||
/// Add an image
|
||
wxRichTextRange wxRichTextCtrl::AddImage(const wxImage& image)
|
||
{
|
||
return GetBuffer().AddImage(image);
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// selection and ranges
|
||
// ----------------------------------------------------------------------------
|
||
|
||
void wxRichTextCtrl::SelectAll()
|
||
{
|
||
SetSelection(0, GetLastPosition()+1);
|
||
m_selectionAnchor = -1;
|
||
}
|
||
|
||
/// Select none
|
||
void wxRichTextCtrl::SelectNone()
|
||
{
|
||
if (!(GetSelectionRange() == wxRichTextRange(-2, -2)))
|
||
SetSelection(-2, -2);
|
||
m_selectionAnchor = -2;
|
||
}
|
||
|
||
static bool wxIsWordDelimiter(const wxString& text)
|
||
{
|
||
static wxString delimiters = wxT(" ,.:;!?-\"'~<7E>$%^&*()_+-={}[]@#<>/\\|");
|
||
return !text.IsEmpty() && delimiters.Find(text) != wxNOT_FOUND;
|
||
}
|
||
|
||
/// Select the word at the given character position
|
||
bool wxRichTextCtrl::SelectWord(long position)
|
||
{
|
||
if (position < 0 || position > GetBuffer().GetRange().GetEnd())
|
||
return false;
|
||
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
|
||
if (!para)
|
||
return false;
|
||
|
||
long positionStart = position;
|
||
long positionEnd = position;
|
||
|
||
for (positionStart = position; positionStart >= para->GetRange().GetStart(); positionStart --)
|
||
{
|
||
wxString text = GetBuffer().GetTextForRange(wxRichTextRange(positionStart, positionStart));
|
||
if (wxIsWordDelimiter(text))
|
||
{
|
||
positionStart ++;
|
||
break;
|
||
}
|
||
}
|
||
if (positionStart < para->GetRange().GetStart())
|
||
positionStart = para->GetRange().GetStart();
|
||
|
||
for (positionEnd = position; positionEnd < para->GetRange().GetEnd(); positionEnd ++)
|
||
{
|
||
wxString text = GetBuffer().GetTextForRange(wxRichTextRange(positionEnd, positionEnd));
|
||
if (wxIsWordDelimiter(text))
|
||
{
|
||
positionEnd --;
|
||
break;
|
||
}
|
||
}
|
||
if (positionEnd >= para->GetRange().GetEnd())
|
||
positionEnd = para->GetRange().GetEnd();
|
||
|
||
SetSelection(positionStart, positionEnd+1);
|
||
|
||
if (positionStart >= 0)
|
||
{
|
||
MoveCaret(positionStart-1, true);
|
||
SetDefaultStyleToCursorStyle();
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
wxString wxRichTextCtrl::GetStringSelection() const
|
||
{
|
||
long from, to;
|
||
GetSelection(&from, &to);
|
||
|
||
return GetRange(from, to);
|
||
}
|
||
|
||
// do the window-specific processing after processing the update event
|
||
#if !wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
|
||
void wxRichTextCtrl::DoUpdateWindowUI(wxUpdateUIEvent& event)
|
||
{
|
||
// call inherited
|
||
wxWindowBase::DoUpdateWindowUI(event);
|
||
|
||
// update text
|
||
if ( event.GetSetText() )
|
||
{
|
||
if ( event.GetText() != GetValue() )
|
||
SetValue(event.GetText());
|
||
}
|
||
}
|
||
#endif // !wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// hit testing
|
||
// ----------------------------------------------------------------------------
|
||
|
||
wxTextCtrlHitTestResult
|
||
wxRichTextCtrl::HitTest(const wxPoint& pt, wxTextCoord *x, wxTextCoord *y) const
|
||
{
|
||
// implement in terms of the other overload as the native ports typically
|
||
// can get the position and not (x, y) pair directly (although wxUniv
|
||
// directly gets x and y -- and so overrides this method as well)
|
||
long pos;
|
||
wxTextCtrlHitTestResult rc = HitTest(pt, &pos);
|
||
|
||
if ( rc != wxTE_HT_UNKNOWN )
|
||
{
|
||
PositionToXY(pos, x, y);
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
|
||
wxTextCtrlHitTestResult
|
||
wxRichTextCtrl::HitTest(const wxPoint& pt,
|
||
long * pos) const
|
||
{
|
||
wxClientDC dc((wxRichTextCtrl*) this);
|
||
((wxRichTextCtrl*)this)->PrepareDC(dc);
|
||
|
||
// Buffer uses logical position (relative to start of buffer)
|
||
// so convert
|
||
wxPoint pt2 = GetLogicalPoint(pt);
|
||
|
||
int hit = ((wxRichTextCtrl*)this)->GetBuffer().HitTest(dc, pt2, *pos);
|
||
|
||
switch ( hit )
|
||
{
|
||
case wxRICHTEXT_HITTEST_BEFORE:
|
||
return wxTE_HT_BEFORE;
|
||
|
||
case wxRICHTEXT_HITTEST_AFTER:
|
||
return wxTE_HT_BEYOND;
|
||
|
||
case wxRICHTEXT_HITTEST_ON:
|
||
return wxTE_HT_ON_TEXT;
|
||
}
|
||
|
||
return wxTE_HT_UNKNOWN;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// set/get the controls text
|
||
// ----------------------------------------------------------------------------
|
||
|
||
wxString wxRichTextCtrl::GetValue() const
|
||
{
|
||
return GetBuffer().GetText();
|
||
}
|
||
|
||
wxString wxRichTextCtrl::GetRange(long from, long to) const
|
||
{
|
||
// Public API for range is different from internals
|
||
return GetBuffer().GetTextForRange(wxRichTextRange(from, to-1));
|
||
}
|
||
|
||
void wxRichTextCtrl::SetValue(const wxString& value)
|
||
{
|
||
Clear();
|
||
|
||
// if the text is long enough, it's faster to just set it instead of first
|
||
// comparing it with the old one (chances are that it will be different
|
||
// anyhow, this comparison is there to avoid flicker for small single-line
|
||
// edit controls mostly)
|
||
if ( (value.length() > 0x400) || (value != GetValue()) )
|
||
{
|
||
DoWriteText(value, false /* not selection only */);
|
||
|
||
// for compatibility, don't move the cursor when doing SetValue()
|
||
SetInsertionPoint(0);
|
||
}
|
||
else // same text
|
||
{
|
||
// still send an event for consistency
|
||
SendUpdateEvent();
|
||
}
|
||
|
||
// we should reset the modified flag even if the value didn't really change
|
||
|
||
// mark the control as being not dirty - we changed its text, not the
|
||
// user
|
||
DiscardEdits();
|
||
}
|
||
|
||
void wxRichTextCtrl::WriteText(const wxString& value)
|
||
{
|
||
DoWriteText(value);
|
||
}
|
||
|
||
void wxRichTextCtrl::DoWriteText(const wxString& value, bool WXUNUSED(selectionOnly))
|
||
{
|
||
wxString valueUnix = wxTextFile::Translate(value, wxTextFileType_Unix);
|
||
|
||
GetBuffer().InsertTextWithUndo(m_caretPosition+1, valueUnix, this);
|
||
}
|
||
|
||
void wxRichTextCtrl::AppendText(const wxString& text)
|
||
{
|
||
SetInsertionPointEnd();
|
||
|
||
WriteText(text);
|
||
}
|
||
|
||
/// Write an image at the current insertion point
|
||
bool wxRichTextCtrl::WriteImage(const wxImage& image, int bitmapType)
|
||
{
|
||
wxRichTextImageBlock imageBlock;
|
||
|
||
wxImage image2 = image;
|
||
if (imageBlock.MakeImageBlock(image2, bitmapType))
|
||
return WriteImage(imageBlock);
|
||
|
||
return false;
|
||
}
|
||
|
||
bool wxRichTextCtrl::WriteImage(const wxString& filename, int bitmapType)
|
||
{
|
||
wxRichTextImageBlock imageBlock;
|
||
|
||
wxImage image;
|
||
if (imageBlock.MakeImageBlock(filename, bitmapType, image, false))
|
||
return WriteImage(imageBlock);
|
||
|
||
return false;
|
||
}
|
||
|
||
bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock& imageBlock)
|
||
{
|
||
return GetBuffer().InsertImageWithUndo(m_caretPosition+1, imageBlock, this);
|
||
}
|
||
|
||
bool wxRichTextCtrl::WriteImage(const wxBitmap& bitmap, int bitmapType)
|
||
{
|
||
if (bitmap.Ok())
|
||
{
|
||
wxRichTextImageBlock imageBlock;
|
||
|
||
wxImage image = bitmap.ConvertToImage();
|
||
if (image.Ok() && imageBlock.MakeImageBlock(image, bitmapType))
|
||
return WriteImage(imageBlock);
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// Insert a newline (actually paragraph) at the current insertion point.
|
||
bool wxRichTextCtrl::Newline()
|
||
{
|
||
return GetBuffer().InsertNewlineWithUndo(m_caretPosition+1, this);
|
||
}
|
||
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Clipboard operations
|
||
// ----------------------------------------------------------------------------
|
||
|
||
void wxRichTextCtrl::Copy()
|
||
{
|
||
if (CanCopy())
|
||
{
|
||
wxRichTextRange range = GetInternalSelectionRange();
|
||
GetBuffer().CopyToClipboard(range);
|
||
}
|
||
}
|
||
|
||
void wxRichTextCtrl::Cut()
|
||
{
|
||
if (CanCut())
|
||
{
|
||
wxRichTextRange range = GetInternalSelectionRange();
|
||
GetBuffer().CopyToClipboard(range);
|
||
|
||
DeleteSelectedContent();
|
||
LayoutContent();
|
||
Refresh(false);
|
||
}
|
||
}
|
||
|
||
void wxRichTextCtrl::Paste()
|
||
{
|
||
if (CanPaste())
|
||
{
|
||
BeginBatchUndo(_("Paste"));
|
||
|
||
long newPos = m_caretPosition;
|
||
DeleteSelectedContent(& newPos);
|
||
|
||
GetBuffer().PasteFromClipboard(newPos);
|
||
|
||
EndBatchUndo();
|
||
}
|
||
}
|
||
|
||
void wxRichTextCtrl::DeleteSelection()
|
||
{
|
||
if (CanDeleteSelection())
|
||
{
|
||
DeleteSelectedContent();
|
||
}
|
||
}
|
||
|
||
bool wxRichTextCtrl::HasSelection() const
|
||
{
|
||
return m_selectionRange.GetStart() != -2 && m_selectionRange.GetEnd() != -2;
|
||
}
|
||
|
||
bool wxRichTextCtrl::CanCopy() const
|
||
{
|
||
// Can copy if there's a selection
|
||
return HasSelection();
|
||
}
|
||
|
||
bool wxRichTextCtrl::CanCut() const
|
||
{
|
||
return HasSelection() && IsEditable();
|
||
}
|
||
|
||
bool wxRichTextCtrl::CanPaste() const
|
||
{
|
||
if ( !IsEditable() )
|
||
return false;
|
||
|
||
return GetBuffer().CanPasteFromClipboard();
|
||
}
|
||
|
||
bool wxRichTextCtrl::CanDeleteSelection() const
|
||
{
|
||
return HasSelection() && IsEditable();
|
||
}
|
||
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Accessors
|
||
// ----------------------------------------------------------------------------
|
||
|
||
void wxRichTextCtrl::SetEditable(bool editable)
|
||
{
|
||
m_editable = editable;
|
||
}
|
||
|
||
void wxRichTextCtrl::SetInsertionPoint(long pos)
|
||
{
|
||
SelectNone();
|
||
|
||
m_caretPosition = pos - 1;
|
||
}
|
||
|
||
void wxRichTextCtrl::SetInsertionPointEnd()
|
||
{
|
||
long pos = GetLastPosition();
|
||
SetInsertionPoint(pos);
|
||
}
|
||
|
||
long wxRichTextCtrl::GetInsertionPoint() const
|
||
{
|
||
return m_caretPosition+1;
|
||
}
|
||
|
||
wxTextPos wxRichTextCtrl::GetLastPosition() const
|
||
{
|
||
return GetBuffer().GetRange().GetEnd();
|
||
}
|
||
|
||
// If the return values from and to are the same, there is no
|
||
// selection.
|
||
void wxRichTextCtrl::GetSelection(long* from, long* to) const
|
||
{
|
||
*from = m_selectionRange.GetStart();
|
||
*to = m_selectionRange.GetEnd();
|
||
if ((*to) != -1 && (*to) != -2)
|
||
(*to) ++;
|
||
}
|
||
|
||
bool wxRichTextCtrl::IsEditable() const
|
||
{
|
||
return m_editable;
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// selection
|
||
// ----------------------------------------------------------------------------
|
||
|
||
void wxRichTextCtrl::SetSelection(long from, long to)
|
||
{
|
||
// if from and to are both -1, it means (in wxWidgets) that all text should
|
||
// be selected.
|
||
if ( (from == -1) && (to == -1) )
|
||
{
|
||
from = 0;
|
||
to = GetLastPosition()+1;
|
||
}
|
||
|
||
DoSetSelection(from, to);
|
||
}
|
||
|
||
void wxRichTextCtrl::DoSetSelection(long from, long to, bool WXUNUSED(scrollCaret))
|
||
{
|
||
m_selectionAnchor = from;
|
||
m_selectionRange.SetRange(from, to-1);
|
||
|
||
Refresh(false);
|
||
PositionCaret();
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Editing
|
||
// ----------------------------------------------------------------------------
|
||
|
||
void wxRichTextCtrl::Replace(long WXUNUSED(from), long WXUNUSED(to), const wxString& value)
|
||
{
|
||
BeginBatchUndo(_("Replace"));
|
||
|
||
DeleteSelectedContent();
|
||
|
||
DoWriteText(value, true /* selection only */);
|
||
|
||
EndBatchUndo();
|
||
}
|
||
|
||
void wxRichTextCtrl::Remove(long from, long to)
|
||
{
|
||
SelectNone();
|
||
|
||
GetBuffer().DeleteRangeWithUndo(wxRichTextRange(from, to),
|
||
m_caretPosition, // Current caret position
|
||
from, // New caret position
|
||
this);
|
||
|
||
LayoutContent();
|
||
if (!IsFrozen())
|
||
Refresh(false);
|
||
}
|
||
|
||
bool wxRichTextCtrl::IsModified() const
|
||
{
|
||
return m_buffer.IsModified();
|
||
}
|
||
|
||
void wxRichTextCtrl::MarkDirty()
|
||
{
|
||
m_buffer.Modify(true);
|
||
}
|
||
|
||
void wxRichTextCtrl::DiscardEdits()
|
||
{
|
||
m_caretPositionForDefaultStyle = -2;
|
||
m_buffer.Modify(false);
|
||
m_buffer.GetCommandProcessor()->ClearCommands();
|
||
}
|
||
|
||
int wxRichTextCtrl::GetNumberOfLines() const
|
||
{
|
||
return GetBuffer().GetParagraphCount();
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Positions <-> coords
|
||
// ----------------------------------------------------------------------------
|
||
|
||
long wxRichTextCtrl::XYToPosition(long x, long y) const
|
||
{
|
||
return GetBuffer().XYToPosition(x, y);
|
||
}
|
||
|
||
bool wxRichTextCtrl::PositionToXY(long pos, long *x, long *y) const
|
||
{
|
||
return GetBuffer().PositionToXY(pos, x, y);
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
//
|
||
// ----------------------------------------------------------------------------
|
||
|
||
void wxRichTextCtrl::ShowPosition(long pos)
|
||
{
|
||
if (!IsPositionVisible(pos))
|
||
ScrollIntoView(pos-1, WXK_DOWN);
|
||
}
|
||
|
||
int wxRichTextCtrl::GetLineLength(long lineNo) const
|
||
{
|
||
return GetBuffer().GetParagraphLength(lineNo);
|
||
}
|
||
|
||
wxString wxRichTextCtrl::GetLineText(long lineNo) const
|
||
{
|
||
return GetBuffer().GetParagraphText(lineNo);
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// Undo/redo
|
||
// ----------------------------------------------------------------------------
|
||
|
||
void wxRichTextCtrl::Undo()
|
||
{
|
||
if (CanUndo())
|
||
{
|
||
GetCommandProcessor()->Undo();
|
||
}
|
||
}
|
||
|
||
void wxRichTextCtrl::Redo()
|
||
{
|
||
if (CanRedo())
|
||
{
|
||
GetCommandProcessor()->Redo();
|
||
}
|
||
}
|
||
|
||
bool wxRichTextCtrl::CanUndo() const
|
||
{
|
||
return GetCommandProcessor()->CanUndo();
|
||
}
|
||
|
||
bool wxRichTextCtrl::CanRedo() const
|
||
{
|
||
return GetCommandProcessor()->CanRedo();
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// implementation details
|
||
// ----------------------------------------------------------------------------
|
||
|
||
void wxRichTextCtrl::Command(wxCommandEvent& event)
|
||
{
|
||
SetValue(event.GetString());
|
||
GetEventHandler()->ProcessEvent(event);
|
||
}
|
||
|
||
void wxRichTextCtrl::OnDropFiles(wxDropFilesEvent& event)
|
||
{
|
||
// By default, load the first file into the text window.
|
||
if (event.GetNumberOfFiles() > 0)
|
||
{
|
||
LoadFile(event.GetFiles()[0]);
|
||
}
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// text control event processing
|
||
// ----------------------------------------------------------------------------
|
||
|
||
bool wxRichTextCtrl::SendUpdateEvent()
|
||
{
|
||
wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
|
||
InitCommandEvent(event);
|
||
|
||
return GetEventHandler()->ProcessEvent(event);
|
||
}
|
||
|
||
void wxRichTextCtrl::InitCommandEvent(wxCommandEvent& event) const
|
||
{
|
||
event.SetEventObject((wxControlBase *)this); // const_cast
|
||
|
||
switch ( m_clientDataType )
|
||
{
|
||
case wxClientData_Void:
|
||
event.SetClientData(GetClientData());
|
||
break;
|
||
|
||
case wxClientData_Object:
|
||
event.SetClientObject(GetClientObject());
|
||
break;
|
||
|
||
case wxClientData_None:
|
||
// nothing to do
|
||
;
|
||
}
|
||
}
|
||
|
||
|
||
wxSize wxRichTextCtrl::DoGetBestSize() const
|
||
{
|
||
return wxSize(10, 10);
|
||
}
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// standard handlers for standard edit menu events
|
||
// ----------------------------------------------------------------------------
|
||
|
||
void wxRichTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
|
||
{
|
||
Cut();
|
||
}
|
||
|
||
void wxRichTextCtrl::OnClear(wxCommandEvent& WXUNUSED(event))
|
||
{
|
||
DeleteSelection();
|
||
}
|
||
|
||
void wxRichTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
|
||
{
|
||
Copy();
|
||
}
|
||
|
||
void wxRichTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
|
||
{
|
||
Paste();
|
||
}
|
||
|
||
void wxRichTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
|
||
{
|
||
Undo();
|
||
}
|
||
|
||
void wxRichTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
|
||
{
|
||
Redo();
|
||
}
|
||
|
||
void wxRichTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
|
||
{
|
||
event.Enable( CanCut() );
|
||
}
|
||
|
||
void wxRichTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
|
||
{
|
||
event.Enable( CanCopy() );
|
||
}
|
||
|
||
void wxRichTextCtrl::OnUpdateClear(wxUpdateUIEvent& event)
|
||
{
|
||
event.Enable( CanDeleteSelection() );
|
||
}
|
||
|
||
void wxRichTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
|
||
{
|
||
event.Enable( CanPaste() );
|
||
}
|
||
|
||
void wxRichTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
|
||
{
|
||
event.Enable( CanUndo() );
|
||
event.SetText( GetCommandProcessor()->GetUndoMenuLabel() );
|
||
}
|
||
|
||
void wxRichTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
|
||
{
|
||
event.Enable( CanRedo() );
|
||
event.SetText( GetCommandProcessor()->GetRedoMenuLabel() );
|
||
}
|
||
|
||
void wxRichTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
|
||
{
|
||
SelectAll();
|
||
}
|
||
|
||
void wxRichTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
|
||
{
|
||
event.Enable(GetLastPosition() > 0);
|
||
}
|
||
|
||
void wxRichTextCtrl::OnContextMenu(wxContextMenuEvent& WXUNUSED(event))
|
||
{
|
||
if (!m_contextMenu)
|
||
{
|
||
m_contextMenu = new wxMenu;
|
||
m_contextMenu->Append(wxID_UNDO, _("&Undo"));
|
||
m_contextMenu->Append(wxID_REDO, _("&Redo"));
|
||
m_contextMenu->AppendSeparator();
|
||
m_contextMenu->Append(wxID_CUT, _("Cu&t"));
|
||
m_contextMenu->Append(wxID_COPY, _("&Copy"));
|
||
m_contextMenu->Append(wxID_PASTE, _("&Paste"));
|
||
m_contextMenu->Append(wxID_CLEAR, _("&Delete"));
|
||
m_contextMenu->AppendSeparator();
|
||
m_contextMenu->Append(wxID_SELECTALL, _("Select &All"));
|
||
}
|
||
PopupMenu(m_contextMenu);
|
||
return;
|
||
}
|
||
|
||
bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttrEx& style)
|
||
{
|
||
return GetBuffer().SetStyle(wxRichTextRange(start, end-1), style);
|
||
}
|
||
|
||
bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
|
||
{
|
||
return GetBuffer().SetStyle(wxRichTextRange(start, end-1), wxTextAttrEx(style));
|
||
}
|
||
|
||
bool wxRichTextCtrl::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style)
|
||
{
|
||
return GetBuffer().SetStyle(range.ToInternal(), style);
|
||
}
|
||
|
||
bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttrEx& style)
|
||
{
|
||
return GetBuffer().SetDefaultStyle(style);
|
||
}
|
||
|
||
bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttr& style)
|
||
{
|
||
return GetBuffer().SetDefaultStyle(wxTextAttrEx(style));
|
||
}
|
||
|
||
const wxTextAttrEx& wxRichTextCtrl::GetDefaultStyleEx() const
|
||
{
|
||
return GetBuffer().GetDefaultStyle();
|
||
}
|
||
|
||
const wxTextAttr& wxRichTextCtrl::GetDefaultStyle() const
|
||
{
|
||
return GetBuffer().GetDefaultStyle();
|
||
}
|
||
|
||
bool wxRichTextCtrl::GetStyle(long position, wxTextAttr& style)
|
||
{
|
||
wxTextAttrEx attr(style);
|
||
if (GetBuffer().GetStyle(position, attr))
|
||
{
|
||
style = attr;
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
bool wxRichTextCtrl::GetStyle(long position, wxTextAttrEx& style)
|
||
{
|
||
return GetBuffer().GetStyle(position, style);
|
||
}
|
||
|
||
bool wxRichTextCtrl::GetStyle(long position, wxRichTextAttr& style)
|
||
{
|
||
return GetBuffer().GetStyle(position, style);
|
||
}
|
||
|
||
/// Get the content (uncombined) attributes for this position.
|
||
|
||
bool wxRichTextCtrl::GetUncombinedStyle(long position, wxTextAttr& style)
|
||
{
|
||
wxTextAttrEx attr(style);
|
||
if (GetBuffer().GetUncombinedStyle(position, attr))
|
||
{
|
||
style = attr;
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
bool wxRichTextCtrl::GetUncombinedStyle(long position, wxTextAttrEx& style)
|
||
{
|
||
return GetBuffer().GetUncombinedStyle(position, style);
|
||
}
|
||
|
||
bool wxRichTextCtrl::GetUncombinedStyle(long position, wxRichTextAttr& style)
|
||
{
|
||
return GetBuffer().GetUncombinedStyle(position, style);
|
||
}
|
||
|
||
/// Set font, and also the buffer attributes
|
||
bool wxRichTextCtrl::SetFont(const wxFont& font)
|
||
{
|
||
#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
|
||
wxControl::SetFont(font);
|
||
#else
|
||
wxScrolledWindow::SetFont(font);
|
||
#endif
|
||
|
||
wxTextAttrEx attr = GetBuffer().GetAttributes();
|
||
attr.SetFont(font);
|
||
GetBuffer().SetBasicStyle(attr);
|
||
|
||
#if !wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
|
||
// Don't set the default style, since it will be inherited from
|
||
// the basic style.
|
||
GetBuffer().SetDefaultStyle(attr);
|
||
#endif
|
||
|
||
GetBuffer().Invalidate(wxRICHTEXT_ALL);
|
||
Refresh(false);
|
||
|
||
return true;
|
||
}
|
||
|
||
/// Transform logical to physical
|
||
wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) const
|
||
{
|
||
wxPoint pt;
|
||
CalcScrolledPosition(ptLogical.x, ptLogical.y, & pt.x, & pt.y);
|
||
|
||
return pt;
|
||
}
|
||
|
||
/// Transform physical to logical
|
||
wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical) const
|
||
{
|
||
wxPoint pt;
|
||
CalcUnscrolledPosition(ptPhysical.x, ptPhysical.y, & pt.x, & pt.y);
|
||
|
||
return pt;
|
||
}
|
||
|
||
/// Position the caret
|
||
void wxRichTextCtrl::PositionCaret()
|
||
{
|
||
if (!GetCaret())
|
||
return;
|
||
|
||
//wxLogDebug(wxT("PositionCaret"));
|
||
|
||
wxRect caretRect;
|
||
if (GetCaretPositionForIndex(GetCaretPosition(), caretRect))
|
||
{
|
||
wxPoint originalPt = caretRect.GetPosition();
|
||
wxPoint pt = GetPhysicalPoint(originalPt);
|
||
if (GetCaret()->GetPosition() != pt)
|
||
{
|
||
GetCaret()->Move(pt);
|
||
GetCaret()->SetSize(caretRect.GetSize());
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Get the caret height and position for the given character position
|
||
bool wxRichTextCtrl::GetCaretPositionForIndex(long position, wxRect& rect)
|
||
{
|
||
wxClientDC dc(this);
|
||
dc.SetFont(GetFont());
|
||
|
||
PrepareDC(dc);
|
||
|
||
wxPoint pt;
|
||
int height = 0;
|
||
|
||
if (GetBuffer().FindPosition(dc, position, pt, & height, m_caretAtLineStart))
|
||
{
|
||
rect = wxRect(pt, wxSize(wxRICHTEXT_DEFAULT_CARET_WIDTH, height));
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// Gets the line for the visible caret position. If the caret is
|
||
/// shown at the very end of the line, it means the next character is actually
|
||
/// on the following line. So let's get the line we're expecting to find
|
||
/// if this is the case.
|
||
wxRichTextLine* wxRichTextCtrl::GetVisibleLineForCaretPosition(long caretPosition) const
|
||
{
|
||
wxRichTextLine* line = GetBuffer().GetLineAtPosition(caretPosition, true);
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPosition, true);
|
||
if (line)
|
||
{
|
||
wxRichTextRange lineRange = line->GetAbsoluteRange();
|
||
if (caretPosition == lineRange.GetStart()-1 &&
|
||
(para->GetRange().GetStart() != lineRange.GetStart()))
|
||
{
|
||
if (!m_caretAtLineStart)
|
||
line = GetBuffer().GetLineAtPosition(caretPosition-1, true);
|
||
}
|
||
}
|
||
return line;
|
||
}
|
||
|
||
|
||
/// Move the caret to the given character position
|
||
bool wxRichTextCtrl::MoveCaret(long pos, bool showAtLineStart)
|
||
{
|
||
if (GetBuffer().GetDirty())
|
||
LayoutContent();
|
||
|
||
if (pos <= GetBuffer().GetRange().GetEnd())
|
||
{
|
||
SetCaretPosition(pos, showAtLineStart);
|
||
|
||
PositionCaret();
|
||
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
/// Layout the buffer: which we must do before certain operations, such as
|
||
/// setting the caret position.
|
||
bool wxRichTextCtrl::LayoutContent(bool onlyVisibleRect)
|
||
{
|
||
if (GetBuffer().GetDirty() || onlyVisibleRect)
|
||
{
|
||
wxRect availableSpace(GetClientSize());
|
||
if (availableSpace.width == 0)
|
||
availableSpace.width = 10;
|
||
if (availableSpace.height == 0)
|
||
availableSpace.height = 10;
|
||
|
||
int flags = wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT;
|
||
if (onlyVisibleRect)
|
||
{
|
||
flags |= wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
|
||
availableSpace.SetPosition(GetLogicalPoint(wxPoint(0, 0)));
|
||
}
|
||
|
||
wxClientDC dc(this);
|
||
dc.SetFont(GetFont());
|
||
|
||
PrepareDC(dc);
|
||
|
||
GetBuffer().Defragment();
|
||
GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation
|
||
GetBuffer().Layout(dc, availableSpace, flags);
|
||
GetBuffer().SetDirty(false);
|
||
|
||
if (!IsFrozen())
|
||
SetupScrollbars();
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// Is all of the selection bold?
|
||
bool wxRichTextCtrl::IsSelectionBold()
|
||
{
|
||
if (HasSelection())
|
||
{
|
||
wxRichTextAttr attr;
|
||
wxRichTextRange range = GetInternalSelectionRange();
|
||
attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
|
||
attr.SetFontWeight(wxBOLD);
|
||
|
||
return HasCharacterAttributes(range, attr);
|
||
}
|
||
else
|
||
{
|
||
// If no selection, then we need to combine current style with default style
|
||
// to see what the effect would be if we started typing.
|
||
wxRichTextAttr attr;
|
||
attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
|
||
|
||
long pos = GetAdjustedCaretPosition(GetCaretPosition());
|
||
if (GetStyle(pos, attr))
|
||
{
|
||
if (IsDefaultStyleShowing())
|
||
wxRichTextApplyStyle(attr, GetDefaultStyleEx());
|
||
return attr.GetFontWeight() == wxBOLD;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// Is all of the selection italics?
|
||
bool wxRichTextCtrl::IsSelectionItalics()
|
||
{
|
||
if (HasSelection())
|
||
{
|
||
wxRichTextRange range = GetInternalSelectionRange();
|
||
wxRichTextAttr attr;
|
||
attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
|
||
attr.SetFontStyle(wxITALIC);
|
||
|
||
return HasCharacterAttributes(range, attr);
|
||
}
|
||
else
|
||
{
|
||
// If no selection, then we need to combine current style with default style
|
||
// to see what the effect would be if we started typing.
|
||
wxRichTextAttr attr;
|
||
attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
|
||
|
||
long pos = GetAdjustedCaretPosition(GetCaretPosition());
|
||
if (GetStyle(pos, attr))
|
||
{
|
||
if (IsDefaultStyleShowing())
|
||
wxRichTextApplyStyle(attr, GetDefaultStyleEx());
|
||
return attr.GetFontStyle() == wxITALIC;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// Is all of the selection underlined?
|
||
bool wxRichTextCtrl::IsSelectionUnderlined()
|
||
{
|
||
if (HasSelection())
|
||
{
|
||
wxRichTextRange range = GetInternalSelectionRange();
|
||
wxRichTextAttr attr;
|
||
attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
|
||
attr.SetFontUnderlined(true);
|
||
|
||
return HasCharacterAttributes(range, attr);
|
||
}
|
||
else
|
||
{
|
||
// If no selection, then we need to combine current style with default style
|
||
// to see what the effect would be if we started typing.
|
||
wxRichTextAttr attr;
|
||
attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
|
||
long pos = GetAdjustedCaretPosition(GetCaretPosition());
|
||
|
||
if (GetStyle(pos, attr))
|
||
{
|
||
if (IsDefaultStyleShowing())
|
||
wxRichTextApplyStyle(attr, GetDefaultStyleEx());
|
||
return attr.GetFontUnderlined();
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// Apply bold to the selection
|
||
bool wxRichTextCtrl::ApplyBoldToSelection()
|
||
{
|
||
wxRichTextAttr attr;
|
||
attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
|
||
attr.SetFontWeight(IsSelectionBold() ? wxNORMAL : wxBOLD);
|
||
|
||
if (HasSelection())
|
||
return SetStyle(GetSelectionRange(), attr);
|
||
else
|
||
SetAndShowDefaultStyle(attr);
|
||
return true;
|
||
}
|
||
|
||
/// Apply italic to the selection
|
||
bool wxRichTextCtrl::ApplyItalicToSelection()
|
||
{
|
||
wxRichTextAttr attr;
|
||
attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
|
||
attr.SetFontStyle(IsSelectionItalics() ? wxNORMAL : wxITALIC);
|
||
|
||
if (HasSelection())
|
||
return SetStyle(GetSelectionRange(), attr);
|
||
else
|
||
SetAndShowDefaultStyle(attr);
|
||
return true;
|
||
}
|
||
|
||
/// Apply underline to the selection
|
||
bool wxRichTextCtrl::ApplyUnderlineToSelection()
|
||
{
|
||
wxRichTextAttr attr;
|
||
attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
|
||
attr.SetFontUnderlined(!IsSelectionUnderlined());
|
||
|
||
if (HasSelection())
|
||
return SetStyle(GetSelectionRange(), attr);
|
||
else
|
||
SetAndShowDefaultStyle(attr);
|
||
return true;
|
||
}
|
||
|
||
/// Is all of the selection aligned according to the specified flag?
|
||
bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment)
|
||
{
|
||
wxRichTextRange range;
|
||
if (HasSelection())
|
||
range = GetInternalSelectionRange();
|
||
else
|
||
range = wxRichTextRange(GetCaretPosition()+1, GetCaretPosition()+1);
|
||
|
||
wxRichTextAttr attr;
|
||
attr.SetAlignment(alignment);
|
||
|
||
return HasParagraphAttributes(range, attr);
|
||
}
|
||
|
||
/// Apply alignment to the selection
|
||
bool wxRichTextCtrl::ApplyAlignmentToSelection(wxTextAttrAlignment alignment)
|
||
{
|
||
wxRichTextAttr attr;
|
||
attr.SetAlignment(alignment);
|
||
if (HasSelection())
|
||
return SetStyle(GetSelectionRange(), attr);
|
||
else
|
||
{
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(GetCaretPosition()+1);
|
||
if (para)
|
||
return SetStyle(para->GetRange().FromInternal(), attr);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/// Apply a named style to the selection
|
||
void wxRichTextCtrl::ApplyStyle(wxRichTextStyleDefinition* def)
|
||
{
|
||
// Flags are defined within each definition, so only certain
|
||
// attributes are applied.
|
||
wxRichTextAttr attr(def->GetStyle());
|
||
|
||
// Make sure the attr has the style name
|
||
if (def->IsKindOf(CLASSINFO(wxRichTextParagraphStyleDefinition)))
|
||
attr.SetParagraphStyleName(def->GetName());
|
||
else
|
||
attr.SetCharacterStyleName(def->GetName());
|
||
|
||
if (HasSelection())
|
||
SetStyle(GetSelectionRange(), attr);
|
||
else
|
||
SetAndShowDefaultStyle(attr);
|
||
}
|
||
|
||
/// Apply the style sheet to the buffer, for example if the styles have changed.
|
||
bool wxRichTextCtrl::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
|
||
{
|
||
if (!styleSheet)
|
||
styleSheet = GetBuffer().GetStyleSheet();
|
||
if (!styleSheet)
|
||
return false;
|
||
|
||
if (GetBuffer().ApplyStyleSheet(styleSheet))
|
||
{
|
||
GetBuffer().Invalidate(wxRICHTEXT_ALL);
|
||
Refresh(false);
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
/// Sets the default style to the style under the cursor
|
||
bool wxRichTextCtrl::SetDefaultStyleToCursorStyle()
|
||
{
|
||
wxTextAttrEx attr;
|
||
attr.SetFlags(wxTEXT_ATTR_CHARACTER);
|
||
|
||
// If at the start of a paragraph, use the next position.
|
||
long pos = GetAdjustedCaretPosition(GetCaretPosition());
|
||
|
||
#if wxRICHTEXT_USE_DYNAMIC_STYLES
|
||
if (GetUncombinedStyle(pos, attr))
|
||
#else
|
||
if (GetStyle(pos, attr))
|
||
#endif
|
||
{
|
||
SetDefaultStyle(attr);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// Returns the first visible position in the current view
|
||
long wxRichTextCtrl::GetFirstVisiblePosition() const
|
||
{
|
||
wxRichTextLine* line = GetBuffer().GetLineAtYPosition(GetLogicalPoint(wxPoint(0, 0)).y);
|
||
if (line)
|
||
return line->GetAbsoluteRange().GetStart();
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
/// The adjusted caret position is the character position adjusted to take
|
||
/// into account whether we're at the start of a paragraph, in which case
|
||
/// style information should be taken from the next position, not current one.
|
||
long wxRichTextCtrl::GetAdjustedCaretPosition(long caretPos) const
|
||
{
|
||
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPos+1);
|
||
|
||
if (para && (caretPos+1 == para->GetRange().GetStart()))
|
||
caretPos ++;
|
||
return caretPos;
|
||
}
|
||
|
||
/// Get/set the selection range in character positions. -1, -1 means no selection.
|
||
/// The range is in API convention, i.e. a single character selection is denoted
|
||
/// by (n, n+1)
|
||
wxRichTextRange wxRichTextCtrl::GetSelectionRange() const
|
||
{
|
||
wxRichTextRange range = GetInternalSelectionRange();
|
||
if (range != wxRichTextRange(-2,-2) && range != wxRichTextRange(-1,-1))
|
||
range.SetEnd(range.GetEnd() + 1);
|
||
return range;
|
||
}
|
||
|
||
void wxRichTextCtrl::SetSelectionRange(const wxRichTextRange& range)
|
||
{
|
||
wxRichTextRange range1(range);
|
||
if (range1 != wxRichTextRange(-2,-2) && range1 != wxRichTextRange(-1,-1) )
|
||
range1.SetEnd(range1.GetEnd() - 1);
|
||
|
||
wxASSERT( range1.GetStart() > range1.GetEnd() );
|
||
|
||
SetInternalSelectionRange(range1);
|
||
}
|
||
|
||
#endif
|
||
// wxUSE_RICHTEXT
|