Added further API for intercepting deletion and content insertion

Added simple implementation of locked objects to sample


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@70465 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Julian Smart
2012-01-25 15:10:09 +00:00
parent eccdb55165
commit cc2aecdef5
7 changed files with 369 additions and 41 deletions

View File

@@ -3231,6 +3231,12 @@ public:
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

View File

@@ -1658,6 +1658,24 @@ public:
*/
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
/**

View File

@@ -3111,6 +3111,12 @@ public:
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

View File

@@ -1617,6 +1617,24 @@ public:
*/
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
/**

View File

@@ -89,6 +89,49 @@
// 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
{
@@ -205,7 +248,7 @@ private:
// any class wishing to process wxWidgets events must use this macro
DECLARE_EVENT_TABLE()
wxRichTextCtrl* m_richTextCtrl;
MyRichTextCtrl* m_richTextCtrl;
};
// ----------------------------------------------------------------------------
@@ -387,6 +430,8 @@ bool MyApp::OnInit()
CreateStyles();
MyRichTextCtrl::SetEnhancedDrawingHandler();
// Add extra handlers (plain text is automatically added)
wxRichTextBuffer::AddHandler(new wxRichTextXMLHandler);
wxRichTextBuffer::AddHandler(new wxRichTextHTMLHandler);
@@ -736,7 +781,7 @@ MyFrame::MyFrame(const wxString& title, wxWindowID id, const wxPoint& pos,
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);
@@ -773,10 +818,27 @@ MyFrame::MyFrame(const wxString& title, wxWindowID id, const wxPoint& pos,
// 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();
@@ -1807,3 +1869,107 @@ void MyFrame::OnPageSetup(wxCommandEvent& WXUNUSED(event))
// 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);
}

View File

@@ -3524,6 +3524,14 @@ bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange&
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)
{
@@ -3697,6 +3705,8 @@ void wxRichTextParagraphLayoutBox::Reset()
AddParagraph(wxEmptyString);
PrepareContent(*this);
InvalidateHierarchy(wxRICHTEXT_ALL);
}
@@ -7201,6 +7211,9 @@ bool wxRichTextBuffer::EndBatchUndo()
/// 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());

View File

@@ -1113,12 +1113,26 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
// 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)
@@ -1128,13 +1142,25 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
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();
@@ -1150,6 +1176,8 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
ScrollIntoView(m_caretPosition, WXK_LEFT);
if (deletions > 0)
{
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_DELETE,
GetId());
@@ -1158,6 +1186,7 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
cmdEvent.SetPosition(m_caretPosition+1);
cmdEvent.SetContainer(GetFocusObject());
GetEventHandler()->ProcessEvent(cmdEvent);
}
Update();
}
@@ -1177,10 +1206,18 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
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())
@@ -1219,12 +1256,21 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
}
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)
@@ -1234,13 +1280,25 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
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();
@@ -1256,6 +1314,8 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
ScrollIntoView(m_caretPosition, WXK_LEFT);
if (deletions > 0)
{
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_DELETE,
GetId());
@@ -1264,17 +1324,27 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
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)
{
@@ -1283,13 +1353,25 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
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();
@@ -1305,6 +1387,8 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
ScrollIntoView(m_caretPosition, WXK_LEFT);
if (deletions > 0)
{
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_DELETE,
GetId());
@@ -1313,6 +1397,7 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
cmdEvent.SetPosition(m_caretPosition+1);
cmdEvent.SetContainer(GetFocusObject());
GetEventHandler()->ProcessEvent(cmdEvent);
}
Update();
}
@@ -1377,6 +1462,12 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
}
}
if (!CanInsertContent(* GetFocusObject(), m_caretPosition+1))
return;
if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
return;
BeginBatchUndo(_("Insert Text"));
long newPos = m_caretPosition;
@@ -3018,12 +3109,12 @@ bool wxRichTextCtrl::CanCopy() const
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();
@@ -3031,7 +3122,7 @@ bool wxRichTextCtrl::CanPaste() const
bool wxRichTextCtrl::CanDeleteSelection() const
{
return HasSelection() && IsEditable();
return HasSelection() && IsEditable() && CanDeleteRange(* GetFocusObject(), GetSelectionRange());
}
@@ -4460,6 +4551,16 @@ bool wxRichTextDropSource::GiveFeedback(wxDragResult WXUNUSED(effect))
}
#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