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:
parent
77ba5c12cb
commit
4ce3ebd3f5
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user