#include "wx/wxprec.h"
#ifdef __BORLANDC__
- #pragma hdrstop
+ #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"
+ #include "wx/menu.h"
+ #include "wx/intl.h"
+ #include "wx/log.h"
+ #include "wx/stopwatch.h"
#endif
#include "wx/textfile.h"
#include "wx/ffile.h"
-#include "wx/settings.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)
Init();
}
-wxRichTextCtrl::wxRichTextCtrl( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
+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, pos, size, style);
+ Create(parent, id, value, pos, size, style);
}
/// Creation
-bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
+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
SetCursor(wxCursor(wxCURSOR_IBEAM));
+ if (!value.IsEmpty())
+ SetValue(value);
+
return true;
}
m_fullLayoutTime = 0;
m_fullLayoutSavedPosition = 0;
m_delayedLayoutThreshold = wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD;
+ m_caretPositionForDefaultStyle = -2;
}
/// Call Freeze to prevent refresh
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();
SetupScrollbars();
}
- GetBuffer().Draw(dc, GetBuffer().GetRange(), GetSelectionRange(), drawingArea, 0 /* descent */, 0 /* flags */);
+ GetBuffer().Draw(dc, GetBuffer().GetRange(), GetInternalSelectionRange(), drawingArea, 0 /* descent */, 0 /* flags */);
}
if (GetCaret())
int ppuX, ppuY;
GetScrollPixelsPerUnit(& ppuX, & ppuY);
- int startX, startY;
- GetViewStart(& startX, & startY);
- startX = 0;
- startY = startY * ppuY;
+ int startXUnits, startYUnits;
+ GetViewStart(& startXUnits, & startYUnits);
+ int startY = startYUnits * ppuY;
int sx = 0, sy = 0;
GetVirtualSize(& sx, & sy);
- sx = 0;
+ int sxUnits = 0;
+ int syUnits = 0;
if (ppuY != 0)
- sy = sy/ppuY;
+ syUnits = sy/ppuY;
wxRect rect = line->GetRect();
// Make it scroll so this item is at the bottom
// of the window
int y = rect.y - (clientSize.y - rect.height);
- y = (int) (0.5 + y/ppuY);
+ int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
- if (startY != y)
+ // 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, sx, sy, 0, y);
+ SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
scrolled = true;
}
}
// Make it scroll so this item is at the top
// of the window
int y = rect.y ;
- y = (int) (0.5 + y/ppuY);
+ int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
- if (startY != y)
+ if (startYUnits != yUnits)
{
- SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
+ SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
scrolled = true;
}
}
// Make it scroll so this item is at the top
// of the window
int y = rect.y ;
- y = (int) (0.5 + y/ppuY);
+ int yUnits = (int) (0.5 + ((float) y)/(float) ppuY);
- if (startY != y)
+ if (startYUnits != yUnits)
{
- SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
+ SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
scrolled = true;
}
}
// Make it scroll so this item is at the bottom
// of the window
int y = rect.y - (clientSize.y - rect.height);
- y = (int) (0.5 + y/ppuY);
+ 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 (startY != y)
+ if (startYUnits != yUnits)
{
- SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
+ SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits);
scrolled = true;
}
}
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();
}
// TODO: reimplement scrolling so we scroll by line, not by fixed number
// of pixels. See e.g. wxVScrolledWindow for ideas.
- int pixelsPerUnit = 5; // 10;
+ int pixelsPerUnit = 5;
wxSize clientSize = GetClientSize();
int maxHeight = GetBuffer().GetCachedSize().y;
- int unitsY = maxHeight/pixelsPerUnit;
+ // 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 = (wxMax(maxHeight - clientSize.y, 0))/pixelsPerUnit;
+ int maxPositionY = (int) ((((float)(wxMax((unitsY*pixelsPerUnit) - clientSize.y, 0)))/((float)pixelsPerUnit)) + 0.5);
// Move to previous scroll position if
// possible
// file IO functions
// ----------------------------------------------------------------------------
-bool wxRichTextCtrl::LoadFile(const wxString& filename, int type)
+#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)
{
- bool success = GetBuffer().LoadFile(filename, type);
+ 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;
}
}
-bool wxRichTextCtrl::SaveFile(const wxString& filename, int type)
+bool wxRichTextCtrl::DoSaveFile(const wxString& filename, int fileType)
{
- wxString filenameToUse = filename.empty() ? m_filename : filename;
- if ( filenameToUse.empty() )
+ if (GetBuffer().SaveFile(filename, fileType))
{
- // 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;
- }
-
- if (GetBuffer().SaveFile(filenameToUse, type))
- {
- m_filename = filenameToUse;
+ m_filename = filename;
DiscardEdits();
return true;
-
}
wxLogError(_("The text couldn't be saved."));
void wxRichTextCtrl::SelectAll()
{
- SetSelection(0, GetLastPosition());
+ SetSelection(0, GetLastPosition()+1);
m_selectionAnchor = -1;
}
wxClientDC dc((wxRichTextCtrl*) this);
((wxRichTextCtrl*)this)->PrepareDC(dc);
- int hit = ((wxRichTextCtrl*)this)->GetBuffer().HitTest(dc, pt, *pos);
+ // 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 )
{
wxString wxRichTextCtrl::GetRange(long from, long to) const
{
- return GetBuffer().GetTextForRange(wxRichTextRange(from, to));
+ // Public API for range is different from internals
+ return GetBuffer().GetTextForRange(wxRichTextRange(from, to-1));
}
void wxRichTextCtrl::SetValue(const wxString& value)
{
*from = m_selectionRange.GetStart();
*to = m_selectionRange.GetEnd();
+ if ((*to) != -1 && (*to) != -2)
+ (*to) ++;
}
bool wxRichTextCtrl::IsEditable() const
if ( (from == -1) && (to == -1) )
{
from = 0;
- to = GetLastPosition();
+ to = GetLastPosition()+1;
}
DoSetSelection(from, to);
void wxRichTextCtrl::DoSetSelection(long from, long to, bool WXUNUSED(scrollCaret))
{
m_selectionAnchor = from;
- m_selectionRange.SetRange(from, to);
+ m_selectionRange.SetRange(from, to-1);
Refresh(false);
PositionCaret();
}
void wxRichTextCtrl::DiscardEdits()
{
+ m_caretPositionForDefaultStyle = -2;
m_buffer.Modify(false);
m_buffer.GetCommandProcessor()->ClearCommands();
}
}
// ----------------------------------------------------------------------------
-// implemenation details
+// implementation details
// ----------------------------------------------------------------------------
-void wxRichTextCtrl::Command(wxCommandEvent & event)
+void wxRichTextCtrl::Command(wxCommandEvent& event)
{
SetValue(event.GetString());
GetEventHandler()->ProcessEvent(event);
bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttrEx& style)
{
- return GetBuffer().SetStyle(wxRichTextRange(start, end), 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, 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();
}
-bool wxRichTextCtrl::GetStyle(long position, wxTextAttrEx& style) const
+const wxTextAttr& wxRichTextCtrl::GetDefaultStyle() const
+{
+ return GetBuffer().GetDefaultStyle();
+}
+
+bool wxRichTextCtrl::GetStyle(long position, wxTextAttr& style)
+{
+ wxTextAttrEx attr;
+ 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) const
+bool wxRichTextCtrl::GetStyle(long position, wxRichTextAttr& style)
{
return GetBuffer().GetStyle(position, style);
}
}
/// Is all of the selection bold?
-bool wxRichTextCtrl::IsSelectionBold() const
+bool wxRichTextCtrl::IsSelectionBold()
{
if (HasSelection())
{
wxRichTextAttr attr;
- wxRichTextRange range = GetSelectionRange();
+ wxRichTextRange range = GetInternalSelectionRange();
attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
attr.SetFontWeight(wxBOLD);
// to see what the effect would be if we started typing.
wxRichTextAttr attr;
attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
- if (GetStyle(GetCaretPosition()+1, attr))
+
+ long pos = GetAdjustedCaretPosition(GetCaretPosition());
+ if (GetStyle(pos, attr))
{
- wxRichTextApplyStyle(attr, GetDefaultStyleEx());
+ if (IsDefaultStyleShowing())
+ wxRichTextApplyStyle(attr, GetDefaultStyleEx());
return attr.GetFontWeight() == wxBOLD;
}
}
}
/// Is all of the selection italics?
-bool wxRichTextCtrl::IsSelectionItalics() const
+bool wxRichTextCtrl::IsSelectionItalics()
{
if (HasSelection())
{
- wxRichTextRange range = GetSelectionRange();
+ wxRichTextRange range = GetInternalSelectionRange();
wxRichTextAttr attr;
attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
attr.SetFontStyle(wxITALIC);
// to see what the effect would be if we started typing.
wxRichTextAttr attr;
attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
- if (GetStyle(GetCaretPosition()+1, attr))
+
+ long pos = GetAdjustedCaretPosition(GetCaretPosition());
+ if (GetStyle(pos, attr))
{
- wxRichTextApplyStyle(attr, GetDefaultStyleEx());
+ if (IsDefaultStyleShowing())
+ wxRichTextApplyStyle(attr, GetDefaultStyleEx());
return attr.GetFontStyle() == wxITALIC;
}
}
}
/// Is all of the selection underlined?
-bool wxRichTextCtrl::IsSelectionUnderlined() const
+bool wxRichTextCtrl::IsSelectionUnderlined()
{
if (HasSelection())
{
- wxRichTextRange range = GetSelectionRange();
+ wxRichTextRange range = GetInternalSelectionRange();
wxRichTextAttr attr;
attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
attr.SetFontUnderlined(true);
// to see what the effect would be if we started typing.
wxRichTextAttr attr;
attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
- if (GetStyle(GetCaretPosition()+1, attr))
+
+ long pos = GetAdjustedCaretPosition(GetCaretPosition());
+ if (GetStyle(pos, attr))
{
- wxRichTextApplyStyle(attr, GetDefaultStyleEx());
+ if (IsDefaultStyleShowing())
+ wxRichTextApplyStyle(attr, GetDefaultStyleEx());
return attr.GetFontUnderlined();
}
}
if (HasSelection())
return SetStyle(GetSelectionRange(), attr);
else
- SetDefaultStyle(attr);
+ SetAndShowDefaultStyle(attr);
return true;
}
if (HasSelection())
return SetStyle(GetSelectionRange(), attr);
else
- SetDefaultStyle(attr);
+ SetAndShowDefaultStyle(attr);
return true;
}
if (HasSelection())
return SetStyle(GetSelectionRange(), attr);
else
- SetDefaultStyle(attr);
+ SetAndShowDefaultStyle(attr);
return true;
}
/// Is all of the selection aligned according to the specified flag?
-bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment) const
+bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment)
{
if (HasSelection())
{
- wxRichTextRange range = GetSelectionRange();
+ wxRichTextRange range = GetInternalSelectionRange();
wxRichTextAttr attr;
attr.SetAlignment(alignment);
{
wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(GetCaretPosition()+1);
if (para)
- return SetStyle(para->GetRange(), attr);
+ 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);
+}
+
/// Sets the default style to the style under the cursor
bool wxRichTextCtrl::SetDefaultStyleToCursorStyle()
{
wxTextAttrEx attr;
attr.SetFlags(wxTEXT_ATTR_CHARACTER);
- if (GetStyle(GetCaretPosition(), attr))
+ // If at the start of a paragraph, use the next position.
+ long pos = GetAdjustedCaretPosition(GetCaretPosition());
+
+ if (GetStyle(pos, attr))
{
SetDefaultStyle(attr);
return true;
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