virtual wxRichTextObject* Clone() const { return new wxRichTextParagraphLayoutBox(*this); }
+ /**
+ Prepares the content just before insertion (or after buffer reset).
+ Currently is only called if undo mode is on.
+ */
+ virtual void PrepareContent(wxRichTextParagraphLayoutBox& container);
+
/**
Insert fragment into this box at the given position. If partialParagraph is true,
it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
*/
virtual wxString GetPropertiesMenuLabel(wxRichTextObject* obj) { return obj->GetPropertiesMenuLabel(); }
+ /**
+ Prepares the content just before insertion (or after buffer reset). Called by the same function in wxRichTextBuffer.
+ Currently is only called if undo mode is on.
+ */
+ virtual void PrepareContent(wxRichTextParagraphLayoutBox& WXUNUSED(container)) {}
+
+ /**
+ Can we delete this range?
+ Sends an event to the control.
+ */
+ virtual bool CanDeleteRange(wxRichTextParagraphLayoutBox& container, const wxRichTextRange& range) const;
+
+ /**
+ Can we insert content at this position?
+ Sends an event to the control.
+ */
+ virtual bool CanInsertContent(wxRichTextParagraphLayoutBox& container, long pos) const;
+
// Command handlers
/**
virtual wxRichTextObject* Clone() const { return new wxRichTextParagraphLayoutBox(*this); }
+ /**
+ Prepares the content just before insertion (or after buffer reset).
+ Currently is only called if undo mode is on.
+ */
+ virtual void PrepareContent(wxRichTextParagraphLayoutBox& container);
+
/**
Insert fragment into this box at the given position. If partialParagraph is true,
it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
*/
virtual wxString GetPropertiesMenuLabel(wxRichTextObject* obj);
+ /**
+ Prepares the content just before insertion (or after buffer reset). Called by the same function in wxRichTextBuffer.
+ Currently is only called if undo mode is on.
+ */
+ virtual void PrepareContent(wxRichTextParagraphLayoutBox& WXUNUSED(container)) {}
+
+ /**
+ Can we delete this range?
+ Sends an event to the control.
+ */
+ virtual bool CanDeleteRange(wxRichTextParagraphLayoutBox& container, const wxRichTextRange& range) const;
+
+ /**
+ Can we insert content at this position?
+ Sends an event to the control.
+ */
+ virtual bool CanInsertContent(wxRichTextParagraphLayoutBox& container, long pos) const;
+
// Command handlers
/**
// private classes
// ----------------------------------------------------------------------------
+// Define a new application type, each program should derive a class from wxApp
+class MyRichTextCtrl: public wxRichTextCtrl
+{
+public:
+ MyRichTextCtrl( wxWindow* parent, wxWindowID id = -1, const wxString& value = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
+ long style = wxRE_MULTILINE, const wxValidator& validator = wxDefaultValidator, const wxString& name = wxTextCtrlNameStr):
+ wxRichTextCtrl(parent, id, value, pos, size, style, validator, name)
+ {
+ m_lockId = 0;
+ m_locked = false;
+ }
+
+ void SetLockId(long id) { m_lockId = id; }
+ long GetLockId() const { return m_lockId; }
+
+ void BeginLock() { m_lockId ++; m_locked = true; }
+ void EndLock() { m_locked = false; }
+ bool IsLocked() const { return m_locked; }
+
+ static void SetEnhancedDrawingHandler();
+
+ /**
+ Prepares the content just before insertion (or after buffer reset). Called by the same function in wxRichTextBuffer.
+ Currently is only called if undo mode is on.
+ */
+ virtual void PrepareContent(wxRichTextParagraphLayoutBox& container);
+
+ /**
+ Can we delete this range?
+ Sends an event to the control.
+ */
+ virtual bool CanDeleteRange(wxRichTextParagraphLayoutBox& container, const wxRichTextRange& range) const;
+
+ /**
+ Can we insert content at this position?
+ Sends an event to the control.
+ */
+ virtual bool CanInsertContent(wxRichTextParagraphLayoutBox& container, long pos) const;
+
+ long m_lockId;
+ bool m_locked;
+};
+
// Define a new application type, each program should derive a class from wxApp
class MyApp : public wxApp
{
// any class wishing to process wxWidgets events must use this macro
DECLARE_EVENT_TABLE()
- wxRichTextCtrl* m_richTextCtrl;
+ MyRichTextCtrl* m_richTextCtrl;
};
// ----------------------------------------------------------------------------
CreateStyles();
+ MyRichTextCtrl::SetEnhancedDrawingHandler();
+
// Add extra handlers (plain text is automatically added)
wxRichTextBuffer::AddHandler(new wxRichTextXMLHandler);
wxRichTextBuffer::AddHandler(new wxRichTextHTMLHandler);
wxFont boldFont = wxFont(12, wxROMAN, wxNORMAL, wxBOLD);
wxFont italicFont = wxFont(12, wxROMAN, wxITALIC, wxNORMAL);
- m_richTextCtrl = new wxRichTextCtrl(splitter, ID_RICHTEXT_CTRL, wxEmptyString, wxDefaultPosition, wxSize(200, 200), wxVSCROLL|wxHSCROLL|wxWANTS_CHARS);
+ m_richTextCtrl = new MyRichTextCtrl(splitter, ID_RICHTEXT_CTRL, wxEmptyString, wxDefaultPosition, wxSize(200, 200), wxVSCROLL|wxHSCROLL|wxWANTS_CHARS);
wxFont font(12, wxROMAN, wxNORMAL, wxNORMAL);
m_richTextCtrl->SetFont(font);
// Write text
void MyFrame::WriteInitialText()
{
- wxRichTextCtrl& r = *m_richTextCtrl;
+ MyRichTextCtrl& r = *m_richTextCtrl;
r.SetDefaultStyle(wxRichTextAttr());
+ // Add some locked content first - needs Undo to be enabled
+ {
+ r.BeginLock();
+ r.WriteText(wxString(wxT("This is a locked object.")));
+ r.EndLock();
+
+ r.WriteText(wxString(wxT(" This is unlocked text. ")));
+
+ r.BeginLock();
+ r.WriteText(wxString(wxT("More locked content.")));
+ r.EndLock();
+ r.Newline();
+
+ // Flush the Undo buffer
+ r.GetCommandProcessor()->ClearCommands();
+ }
+
r.BeginSuppressUndo();
r.Freeze();
// wxGetApp().GetPrinting()->PageSetup();
}
+
+void MyRichTextCtrl::PrepareContent(wxRichTextParagraphLayoutBox& container)
+{
+ if (IsLocked())
+ {
+ // Lock all content that's about to be added to the control
+ wxRichTextObjectList::compatibility_iterator node = container.GetChildren().GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ if (para)
+ {
+ wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
+ while (childNode)
+ {
+ wxRichTextObject* obj = childNode->GetData();
+ obj->GetProperties().SetProperty(wxT("Lock"), m_lockId);
+
+ childNode = childNode->GetNext();
+ }
+ }
+ node = node->GetNext();
+ }
+ }
+}
+
+bool MyRichTextCtrl::CanDeleteRange(wxRichTextParagraphLayoutBox& container, const wxRichTextRange& range) const
+{
+ long i;
+ for (i = range.GetStart(); i < range.GetEnd(); i++)
+ {
+ wxRichTextObject* obj = container.GetLeafObjectAtPosition(i);
+ if (obj && obj->GetProperties().HasProperty(wxT("Lock")))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool MyRichTextCtrl::CanInsertContent(wxRichTextParagraphLayoutBox& container, long pos) const
+{
+ wxRichTextObject* child1 = container.GetLeafObjectAtPosition(pos);
+ wxRichTextObject* child2 = container.GetLeafObjectAtPosition(pos-1);
+
+ long lock1 = -1, lock2 = -1;
+
+ if (child1 && child1->GetProperties().HasProperty(wxT("Lock")))
+ lock1 = child1->GetProperties().GetPropertyLong(wxT("Lock"));
+ if (child2 && child2->GetProperties().HasProperty(wxT("Lock")))
+ lock2 = child2->GetProperties().GetPropertyLong(wxT("Lock"));
+
+ if (lock1 != -1 && lock1 == lock2)
+ return false;
+
+ // Don't allow insertion before a locked object if it's at the beginning of the buffer.
+ if (pos == 0 && lock1 != -1)
+ return false;
+
+ return true;
+}
+
+
+class wxRichTextEnhancedDrawingHandler: public wxRichTextDrawingHandler
+{
+public:
+ wxRichTextEnhancedDrawingHandler()
+ {
+ SetName(wxT("enhanceddrawing"));
+ m_lockBackgroundColour = wxColour(220, 220, 220);
+ }
+
+ /**
+ Returns @true if this object has virtual attributes that we can provide.
+ */
+ virtual bool HasVirtualAttributes(wxRichTextObject* obj) const;
+
+ /**
+ Provides virtual attributes that we can provide.
+ */
+ virtual bool GetVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const;
+
+ wxColour m_lockBackgroundColour;
+};
+
+bool wxRichTextEnhancedDrawingHandler::HasVirtualAttributes(wxRichTextObject* obj) const
+{
+ return obj->GetProperties().HasProperty(wxT("Lock"));
+}
+
+bool wxRichTextEnhancedDrawingHandler::GetVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
+{
+ if (obj->GetProperties().HasProperty(wxT("Lock")))
+ {
+ attr.SetBackgroundColour(m_lockBackgroundColour);
+ return true;
+ }
+ return false;
+}
+
+void MyRichTextCtrl::SetEnhancedDrawingHandler()
+{
+ wxRichTextBuffer::AddDrawingHandler(new wxRichTextEnhancedDrawingHandler);
+}
return foundCount == matchingCount && foundCount != 0;
}
+void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
+{
+ wxRichTextBuffer* buffer = GetBuffer();
+ if (buffer && buffer->GetRichTextCtrl())
+ buffer->GetRichTextCtrl()->PrepareContent(container);
+}
+
+
/// Set character or paragraph properties
bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
{
AddParagraph(wxEmptyString);
+ PrepareContent(*this);
+
InvalidateHierarchy(wxRICHTEXT_ALL);
}
/// Submit immediately, or delay according to whether collapsing is on
bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
{
+ if (action && !action->GetNewParagraphs().IsEmpty())
+ PrepareContent(action->GetNewParagraphs());
+
if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
{
wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
// Must process this before translation, otherwise it's translated into a WXK_DELETE event.
if (event.CmdDown() && event.GetKeyCode() == WXK_BACK)
{
+ if (!IsEditable())
+ {
+ return;
+ }
+
+ if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
+ {
+ return;
+ }
+
BeginBatchUndo(_("Delete Text"));
long newPos = m_caretPosition;
bool processed = DeleteSelectedContent(& newPos);
+ int deletions = 0;
+ if (processed)
+ deletions ++;
+
// 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 (newPos > -1)
long pos = wxRichTextCtrl::FindNextWordPosition(-1);
if (pos < newPos)
{
- GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(pos+1, newPos), this, & GetBuffer());
+ wxRichTextRange range(pos+1, newPos);
+ if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
+ {
+ GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
+ deletions ++;
+ }
processed = true;
}
}
if (!processed)
- GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(newPos, newPos), this, & GetBuffer());
+ {
+ wxRichTextRange range(newPos, newPos);
+ if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
+ {
+ GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
+ deletions ++;
+ }
+ }
}
EndBatchUndo();
ScrollIntoView(m_caretPosition, WXK_LEFT);
- wxRichTextEvent cmdEvent(
- wxEVT_COMMAND_RICHTEXT_DELETE,
- GetId());
- cmdEvent.SetEventObject(this);
- cmdEvent.SetFlags(flags);
- cmdEvent.SetPosition(m_caretPosition+1);
- cmdEvent.SetContainer(GetFocusObject());
- GetEventHandler()->ProcessEvent(cmdEvent);
+ if (deletions > 0)
+ {
+ wxRichTextEvent cmdEvent(
+ wxEVT_COMMAND_RICHTEXT_DELETE,
+ GetId());
+ cmdEvent.SetEventObject(this);
+ cmdEvent.SetFlags(flags);
+ cmdEvent.SetPosition(m_caretPosition+1);
+ cmdEvent.SetContainer(GetFocusObject());
+ GetEventHandler()->ProcessEvent(cmdEvent);
+ }
Update();
}
if (event.GetKeyCode() == WXK_RETURN)
{
- BeginBatchUndo(_("Insert Text"));
+ if (!CanInsertContent(* GetFocusObject(), m_caretPosition+1))
+ return;
long newPos = m_caretPosition;
+ if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
+ {
+ return;
+ }
+
+ BeginBatchUndo(_("Insert Text"));
+
DeleteSelectedContent(& newPos);
if (event.ShiftDown())
}
else if (event.GetKeyCode() == WXK_BACK)
{
- BeginBatchUndo(_("Delete Text"));
-
long newPos = m_caretPosition;
+ if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
+ {
+ return;
+ }
+
+ BeginBatchUndo(_("Delete Text"));
+
bool processed = DeleteSelectedContent(& newPos);
+ int deletions = 0;
+ if (processed)
+ deletions ++;
+
// 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 (newPos > -1)
long pos = wxRichTextCtrl::FindNextWordPosition(-1);
if (pos < newPos)
{
- GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(pos+1, newPos), this, & GetBuffer());
+ wxRichTextRange range(pos+1, newPos);
+ if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
+ {
+ GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
+ deletions ++;
+ }
processed = true;
}
}
if (!processed)
- GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(newPos, newPos), this, & GetBuffer());
+ {
+ wxRichTextRange range(newPos, newPos);
+ if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
+ {
+ GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
+ deletions ++;
+ }
+ }
}
EndBatchUndo();
ScrollIntoView(m_caretPosition, WXK_LEFT);
- wxRichTextEvent cmdEvent(
- wxEVT_COMMAND_RICHTEXT_DELETE,
- GetId());
- cmdEvent.SetEventObject(this);
- cmdEvent.SetFlags(flags);
- cmdEvent.SetPosition(m_caretPosition+1);
- cmdEvent.SetContainer(GetFocusObject());
- GetEventHandler()->ProcessEvent(cmdEvent);
+ if (deletions > 0)
+ {
+ wxRichTextEvent cmdEvent(
+ wxEVT_COMMAND_RICHTEXT_DELETE,
+ GetId());
+ cmdEvent.SetEventObject(this);
+ cmdEvent.SetFlags(flags);
+ cmdEvent.SetPosition(m_caretPosition+1);
+ cmdEvent.SetContainer(GetFocusObject());
+ GetEventHandler()->ProcessEvent(cmdEvent);
+ }
Update();
}
else if (event.GetKeyCode() == WXK_DELETE)
{
- BeginBatchUndo(_("Delete Text"));
-
long newPos = m_caretPosition;
+ if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
+ {
+ return;
+ }
+
+ BeginBatchUndo(_("Delete Text"));
+
bool processed = DeleteSelectedContent(& newPos);
+ int deletions = 0;
+ if (processed)
+ deletions ++;
+
// Submit range in character positions, which are greater than caret positions,
if (newPos < GetFocusObject()->GetOwnRange().GetEnd()+1)
{
long pos = wxRichTextCtrl::FindNextWordPosition(1);
if (pos != -1 && (pos > newPos))
{
- GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(newPos+1, pos), this, & GetBuffer());
+ wxRichTextRange range(newPos+1, pos);
+ if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
+ {
+ GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
+ deletions ++;
+ }
processed = true;
}
}
if (!processed && newPos < (GetLastPosition()-1))
- GetFocusObject()->DeleteRangeWithUndo(wxRichTextRange(newPos+1, newPos+1), this, & GetBuffer());
+ {
+ wxRichTextRange range(newPos+1, newPos+1);
+ if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
+ {
+ GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
+ deletions ++;
+ }
+ }
}
EndBatchUndo();
ScrollIntoView(m_caretPosition, WXK_LEFT);
- wxRichTextEvent cmdEvent(
- wxEVT_COMMAND_RICHTEXT_DELETE,
- GetId());
- cmdEvent.SetEventObject(this);
- cmdEvent.SetFlags(flags);
- cmdEvent.SetPosition(m_caretPosition+1);
- cmdEvent.SetContainer(GetFocusObject());
- GetEventHandler()->ProcessEvent(cmdEvent);
+ if (deletions > 0)
+ {
+ wxRichTextEvent cmdEvent(
+ wxEVT_COMMAND_RICHTEXT_DELETE,
+ GetId());
+ cmdEvent.SetEventObject(this);
+ cmdEvent.SetFlags(flags);
+ cmdEvent.SetPosition(m_caretPosition+1);
+ cmdEvent.SetContainer(GetFocusObject());
+ GetEventHandler()->ProcessEvent(cmdEvent);
+ }
Update();
}
}
}
+ if (!CanInsertContent(* GetFocusObject(), m_caretPosition+1))
+ return;
+
+ if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
+ return;
+
BeginBatchUndo(_("Insert Text"));
long newPos = m_caretPosition;
bool wxRichTextCtrl::CanCut() const
{
- return HasSelection() && IsEditable();
+ return CanDeleteSelection();
}
bool wxRichTextCtrl::CanPaste() const
{
- if ( !IsEditable() )
+ if ( !IsEditable() || !GetFocusObject() || !CanInsertContent(* GetFocusObject(), m_caretPosition+1))
return false;
return GetBuffer().CanPasteFromClipboard();
bool wxRichTextCtrl::CanDeleteSelection() const
{
- return HasSelection() && IsEditable();
+ return HasSelection() && IsEditable() && CanDeleteRange(* GetFocusObject(), GetSelectionRange());
}
}
#endif // wxUSE_DRAG_AND_DROP
+bool wxRichTextCtrl::CanDeleteRange(wxRichTextParagraphLayoutBox& WXUNUSED(container), const wxRichTextRange& WXUNUSED(range)) const
+{
+ return true;
+}
+
+bool wxRichTextCtrl::CanInsertContent(wxRichTextParagraphLayoutBox& WXUNUSED(container), long WXUNUSED(pos)) const
+{
+ return true;
+}
+
#if wxRICHTEXT_USE_OWN_CARET