Added continuation bullet style for supporting multiple paragraphs in a list item

The user can 'delete' the bullet to create a continuation paragraph


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72096 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Julian Smart 2012-07-15 06:42:15 +00:00
parent 77ba5c12cb
commit 4ce3ebd3f5
6 changed files with 243 additions and 154 deletions

View File

@ -2144,6 +2144,17 @@ public:
// Implementation
/**
Processes the back key.
*/
virtual bool ProcessBackKey(wxKeyEvent& event, int flags);
/**
Given a character position at which there is a list style, find the range
encompassing the same list style by looking backwards and forwards.
*/
virtual wxRichTextRange FindRangeForList(long pos, bool& isNumberedList);
/**
Sets up the caret for the given position and container, after a mouse click.
*/

View File

@ -243,7 +243,9 @@ enum wxTextAttrBulletStyle
wxTEXT_ATTR_BULLET_STYLE_ALIGN_LEFT = 0x00000000,
wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT = 0x00001000,
wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE = 0x00002000
wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE = 0x00002000,
wxTEXT_ATTR_BULLET_STYLE_CONTINUATION = 0x00004000
};
/*!

View File

@ -2103,6 +2103,17 @@ public:
// Implementation
/**
Processes the back key.
*/
virtual bool ProcessBackKey(wxKeyEvent& event, int flags);
/**
Given a character position at which there is a list style, find the range
encompassing the same list style by looking backwards and forwards.
*/
virtual wxRichTextRange FindRangeForList(long pos, bool& isNumberedList);
/**
Sets up the caret for the given position and container, after a mouse click.
*/

View File

@ -179,7 +179,9 @@ enum wxTextAttrBulletStyle
wxTEXT_ATTR_BULLET_STYLE_ALIGN_LEFT = 0x00000000,
wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT = 0x00001000,
wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE = 0x00002000
wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE = 0x00002000,
wxTEXT_ATTR_BULLET_STYLE_CONTINUATION = 0x00004000
};
/**

View File

@ -3985,12 +3985,19 @@ bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wx
wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
// Now we need to do numbering
if (renumber)
// Preserve the existing list item continuation bullet style, if any
if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
else
{
newPara->GetAttributes().SetBulletNumber(n);
}
// Now we need to do numbering
if (renumber)
{
newPara->GetAttributes().SetBulletNumber(n);
}
n ++;
n ++;
}
}
else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
{
@ -4163,6 +4170,10 @@ bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, co
wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
// Preserve the existing list item continuation bullet style, if any
if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
// OK, we've (re)applied the style, now let's get the numbering right.
if (currentLevel == -1)
@ -4196,7 +4207,8 @@ bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, co
}
else
{
levels[currentLevel] ++;
if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
levels[currentLevel] ++;
}
newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
@ -4275,6 +4287,22 @@ bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRa
/// position of the paragraph that it had to start looking from.
bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
{
// Search for a paragraph that isn't a continuation paragraph (no bullet)
while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
{
wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
if (node)
{
node = node->GetPrevious();
if (node)
previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
else
previousParagraph = NULL;
}
else
previousParagraph = NULL;
}
if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
return false;
@ -4374,7 +4402,7 @@ bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons
DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
// Draw the bullet, if any
if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
{
if (attr.GetLeftSubIndent() != 0)
{

View File

@ -1130,82 +1130,8 @@ 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())
{
if (!ProcessBackKey(event, flags))
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)
{
if (event.CmdDown())
{
long pos = wxRichTextCtrl::FindNextWordPosition(-1);
if (pos < newPos)
{
wxRichTextRange range(pos+1, newPos);
if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
{
GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
deletions ++;
}
processed = true;
}
}
if (!processed)
{
wxRichTextRange range(newPos, newPos);
if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
{
GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
deletions ++;
}
}
}
EndBatchUndo();
if (GetLastPosition() == -1)
{
GetFocusObject()->Reset();
m_caretPosition = -1;
PositionCaret();
SetDefaultStyleToCursorStyle();
}
ScrollIntoView(m_caretPosition, WXK_LEFT);
// Always send this event; wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED will be sent only if there is an actual deletion.
{
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
event.Skip();
@ -1248,6 +1174,14 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
else
GetFocusObject()->InsertNewlineWithUndo(& GetBuffer(), newPos+1, this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE|wxRICHTEXT_INSERT_INTERACTIVE);
// Automatically renumber list
bool isNumberedList = false;
wxRichTextRange numberedListRange = FindRangeForList(newPos+1, isNumberedList);
if (isNumberedList && numberedListRange != wxRichTextRange(-1, -1))
{
NumberList(numberedListRange, NULL, wxRICHTEXT_SETSTYLE_RENUMBER|wxRICHTEXT_SETSTYLE_WITH_UNDO);
}
EndBatchUndo();
SetDefaultStyleToCursorStyle();
@ -1273,77 +1207,7 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
}
else if (event.GetKeyCode() == WXK_BACK)
{
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)
{
if (event.CmdDown())
{
long pos = wxRichTextCtrl::FindNextWordPosition(-1);
if (pos < newPos)
{
wxRichTextRange range(pos+1, newPos);
if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
{
GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
deletions ++;
}
processed = true;
}
}
if (!processed)
{
wxRichTextRange range(newPos, newPos);
if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
{
GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
deletions ++;
}
}
}
EndBatchUndo();
if (GetLastPosition() == -1)
{
GetFocusObject()->Reset();
m_caretPosition = -1;
PositionCaret();
SetDefaultStyleToCursorStyle();
}
ScrollIntoView(m_caretPosition, WXK_LEFT);
// Always send this event; wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED will be sent only if there is an actual deletion.
{
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();
ProcessBackKey(event, flags);
}
else if (event.GetKeyCode() == WXK_DELETE)
{
@ -1530,6 +1394,122 @@ bool wxRichTextCtrl::ProcessMouseMovement(wxRichTextParagraphLayoutBox* containe
return false;
}
// Processes the back key
bool wxRichTextCtrl::ProcessBackKey(wxKeyEvent& event, int flags)
{
if (!IsEditable())
{
return false;
}
if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
{
return false;
}
wxRichTextParagraph* para = GetFocusObject()->GetParagraphAtPosition(m_caretPosition, true);
// If we're at the start of a list item with a bullet, let's 'delete' the bullet, i.e.
// make it a continuation paragraph.
if (!HasSelection() && para && ((m_caretPosition+1) == para->GetRange().GetStart()) &&
para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
{
wxRichTextParagraph* newPara = wxDynamicCast(para->Clone(), wxRichTextParagraph);
newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle() | wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
wxRichTextAction* action = new wxRichTextAction(NULL, _("Remove Bullet"), wxRICHTEXT_CHANGE_STYLE, & GetBuffer(), GetFocusObject(), this);
action->SetRange(newPara->GetRange());
action->SetPosition(GetCaretPosition());
action->GetNewParagraphs().AppendChild(newPara);
// Also store the old ones for Undo
action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
GetBuffer().Invalidate(para->GetRange());
GetBuffer().SubmitAction(action);
// Automatically renumber list
bool isNumberedList = false;
wxRichTextRange numberedListRange = FindRangeForList(m_caretPosition, isNumberedList);
if (isNumberedList && numberedListRange != wxRichTextRange(-1, -1))
{
NumberList(numberedListRange, NULL, wxRICHTEXT_SETSTYLE_RENUMBER|wxRICHTEXT_SETSTYLE_WITH_UNDO);
}
Update();
}
else
{
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)
{
if (event.CmdDown())
{
long pos = wxRichTextCtrl::FindNextWordPosition(-1);
if (pos < newPos)
{
wxRichTextRange range(pos+1, newPos);
if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
{
GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
deletions ++;
}
processed = true;
}
}
if (!processed)
{
wxRichTextRange range(newPos, newPos);
if (CanDeleteRange(* GetFocusObject(), range.FromInternal()))
{
GetFocusObject()->DeleteRangeWithUndo(range, this, & GetBuffer());
deletions ++;
}
}
}
EndBatchUndo();
if (GetLastPosition() == -1)
{
GetFocusObject()->Reset();
m_caretPosition = -1;
PositionCaret();
SetDefaultStyleToCursorStyle();
}
ScrollIntoView(m_caretPosition, WXK_LEFT);
// Always send this event; wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED will be sent only if there is an actual deletion.
{
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();
}
return true;
}
/// Delete content if there is a selection, e.g. when pressing a key.
bool wxRichTextCtrl::DeleteSelectedContent(long* newPos)
{
@ -4345,6 +4325,61 @@ bool wxRichTextCtrl::PromoteList(int promoteBy, const wxRichTextRange& range, co
return GetFocusObject()->PromoteList(promoteBy, range.ToInternal(), defName, flags, specifiedLevel);
}
// Given a character position at which there is a list style, find the range
// encompassing the same list style by looking backwards and forwards.
wxRichTextRange wxRichTextCtrl::FindRangeForList(long pos, bool& isNumberedList)
{
wxRichTextParagraphLayoutBox* focusObject = GetFocusObject();
wxRichTextRange range = wxRichTextRange(-1, -1);
wxRichTextParagraph* para = focusObject->GetParagraphAtPosition(pos);
if (!para || !para->GetAttributes().HasListStyleName())
return range;
else
{
wxString listStyle = para->GetAttributes().GetListStyleName();
range = para->GetRange();
isNumberedList = para->GetAttributes().HasBulletNumber();
// Search back
wxRichTextObjectList::compatibility_iterator initialNode = focusObject->GetChildren().Find(para);
if (initialNode)
{
wxRichTextObjectList::compatibility_iterator startNode = initialNode->GetPrevious();
while (startNode)
{
wxRichTextParagraph* p = wxDynamicCast(startNode->GetData(), wxRichTextParagraph);
if (p)
{
if (!p->GetAttributes().HasListStyleName() || p->GetAttributes().GetListStyleName() != listStyle)
break;
else
range.SetStart(p->GetRange().GetStart());
}
startNode = startNode->GetPrevious();
}
// Search forward
wxRichTextObjectList::compatibility_iterator endNode = initialNode->GetNext();
while (endNode)
{
wxRichTextParagraph* p = wxDynamicCast(endNode->GetData(), wxRichTextParagraph);
if (p)
{
if (!p->GetAttributes().HasListStyleName() || p->GetAttributes().GetListStyleName() != listStyle)
break;
else
range.SetEnd(p->GetRange().GetEnd());
}
endNode = endNode->GetNext();
}
}
}
return range;
}
/// Deletes the content in the given range
bool wxRichTextCtrl::Delete(const wxRichTextRange& range)
{