]> git.saurik.com Git - wxWidgets.git/commitdiff
Added continuation bullet style for supporting multiple paragraphs in a list item
authorJulian Smart <julian@anthemion.co.uk>
Sun, 15 Jul 2012 06:42:15 +0000 (06:42 +0000)
committerJulian Smart <julian@anthemion.co.uk>
Sun, 15 Jul 2012 06:42:15 +0000 (06:42 +0000)
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

include/wx/richtext/richtextctrl.h
include/wx/textctrl.h
interface/wx/richtext/richtextctrl.h
interface/wx/textctrl.h
src/richtext/richtextbuffer.cpp
src/richtext/richtextctrl.cpp

index 47130eff11bedace8704062e1aa7f93ff40aa838..9cc8364f6dbfade3901cda0ad975145b24bc7491 100644 (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.
     */
index 57dfeb79b43d0468369f9cb4288d775ce9cd3763..bf611d72abe756c017967e5699bdae2358d52a57 100644 (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
 };
 
 /*!
index e0ce27ddedf48a119de445369744cdb4cfb841ac..9462805f2d5cfe3831adf3922cf7776bbe3a0eca 100644 (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.
     */
index 7909e59ee91236ac9efe0a65a1c7347bef7ba6cc..6da42a394e07625c1022d3a5d80b76d04965bc10 100644 (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
 };
 
 /**
index b0054947250981a5f4befc0d27f35ad4c4d8d044..ff4505b10ccfc2a64b80bbc6023208c7308add68 100644 (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)
         {
index 7a92cc4399020ec7b4ce58b6c2a0bda708e5f892..1b0a084f8672e23003a54f606a3117332b29003d 100644 (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())
-            {
-                return;
-            }
-
-            if (HasSelection() && !CanDeleteRange(* GetFocusObject(), GetSelectionRange()))
-            {
+            if (!ProcessBackKey(event, flags))
                 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)
 {