+ if (currentLevel == -1)
+ currentLevel = thisLevel;
+
+ // Same level as before, do nothing except increment level's number afterwards
+ if (currentLevel == thisLevel)
+ {
+ }
+ // A deeper level: start renumbering all levels after current level
+ else if (thisLevel > currentLevel)
+ {
+ for (i = currentLevel+1; i <= thisLevel; i++)
+ {
+ levels[i] = 0;
+ }
+ currentLevel = thisLevel;
+ }
+ else if (thisLevel < currentLevel)
+ {
+ currentLevel = thisLevel;
+ }
+
+ // Use the current numbering if -1 and we have a bullet number already
+ if (levels[currentLevel] == -1)
+ {
+ if (newPara->GetAttributes().HasBulletNumber())
+ levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
+ else
+ levels[currentLevel] = 1;
+ }
+ else
+ {
+ levels[currentLevel] ++;
+ }
+
+ newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
+
+ // Create the bullet text if an outline list
+ if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
+ {
+ wxString text;
+ for (i = 0; i <= currentLevel; i++)
+ {
+ if (!text.IsEmpty())
+ text += wxT(".");
+ text += wxString::Format(wxT("%d"), levels[i]);
+ }
+ newPara->GetAttributes().SetBulletText(text);
+ }
+ }
+ }
+ }
+
+ node = node->GetNext();
+ }
+
+ // Do action, or delay it until end of batch.
+ if (haveControl && withUndo)
+ GetRichTextCtrl()->GetBuffer().SubmitAction(action);
+
+ return true;
+}
+
+bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
+{
+ if (GetStyleSheet())
+ {
+ wxRichTextListStyleDefinition* def = NULL;
+ if (!defName.IsEmpty())
+ def = GetStyleSheet()->FindListStyle(defName);
+ return NumberList(range, def, flags, startFrom, specifiedLevel);
+ }
+ return false;
+}
+
+/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
+bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
+{
+ // TODO
+ // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
+ // to NumberList with a flag indicating promotion is required within one of the ranges.
+ // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
+ // a paragraph that either has no list style, or has one that is different or whose indentation is less.
+ // We start renumbering from the para after that different para we found. We specify that the numbering of that
+ // list position will start from 1.
+ // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
+ // We can end the renumbering at this point.
+
+ // For now, only renumber within the promotion range.
+
+ return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
+}
+
+bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
+{
+ if (GetStyleSheet())
+ {
+ wxRichTextListStyleDefinition* def = NULL;
+ if (!defName.IsEmpty())
+ def = GetStyleSheet()->FindListStyle(defName);
+ return PromoteList(promoteBy, range, def, flags, specifiedLevel);
+ }
+ return false;
+}
+
+/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
+/// position of the paragraph that it had to start looking from.
+bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxTextAttr& attr) const
+{
+ if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
+ return false;
+
+ wxRichTextStyleSheet* styleSheet = GetStyleSheet();
+ if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
+ {
+ wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
+ if (def)
+ {
+ // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
+ // int thisLevel = def->FindLevelForIndent(thisIndent);
+
+ bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
+
+ attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
+ if (previousParagraph->GetAttributes().HasBulletName())
+ attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
+ attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
+ attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
+
+ int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
+ attr.SetBulletNumber(nextNumber);
+
+ if (isOutline)
+ {
+ wxString text = previousParagraph->GetAttributes().GetBulletText();
+ if (!text.IsEmpty())
+ {
+ int pos = text.Find(wxT('.'), true);
+ if (pos != wxNOT_FOUND)
+ {
+ text = text.Mid(0, text.Length() - pos - 1);
+ }
+ else
+ text = wxEmptyString;
+ if (!text.IsEmpty())
+ text += wxT(".");
+ text += wxString::Format(wxT("%d"), nextNumber);
+ attr.SetBulletText(text);
+ }
+ }
+
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ return false;
+}
+
+/*!
+ * wxRichTextParagraph
+ * This object represents a single paragraph (or in a straight text editor, a line).
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextBox)
+
+wxArrayInt wxRichTextParagraph::sm_defaultTabs;
+
+wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxTextAttr* style):
+ wxRichTextBox(parent)
+{
+ if (style)
+ SetAttributes(*style);
+}
+
+wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxTextAttr* paraStyle, wxTextAttr* charStyle):
+ wxRichTextBox(parent)
+{
+ if (paraStyle)
+ SetAttributes(*paraStyle);
+
+ AppendChild(new wxRichTextPlainText(text, this, charStyle));
+}
+
+wxRichTextParagraph::~wxRichTextParagraph()
+{
+ ClearLines();
+}
+
+/// Draw the item
+bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int WXUNUSED(descent), int style)
+{
+ wxTextAttr attr = GetCombinedAttributes();
+
+ // Draw the bullet, if any
+ if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
+ {
+ if (attr.GetLeftSubIndent() != 0)
+ {
+ int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
+ int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
+
+ wxTextAttr bulletAttr(GetCombinedAttributes());
+
+ // Combine with the font of the first piece of content, if one is specified
+ if (GetChildren().GetCount() > 0)
+ {
+ wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
+ if (firstObj->GetAttributes().HasFont())
+ {
+ wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
+ }
+ }
+
+ // Get line height from first line, if any
+ wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : (wxRichTextLine*) NULL;
+
+ wxPoint linePos;
+ int lineHeight wxDUMMY_INITIALIZE(0);
+ if (line)
+ {
+ lineHeight = line->GetSize().y;
+ linePos = line->GetPosition() + GetPosition();
+ }
+ else
+ {
+ wxFont font;
+ if (bulletAttr.HasFont() && GetBuffer())
+ font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
+ else
+ font = (*wxNORMAL_FONT);
+
+ wxCheckSetFont(dc, font);
+
+ lineHeight = dc.GetCharHeight();
+ linePos = GetPosition();
+ linePos.y += spaceBeforePara;
+ }
+
+ wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
+
+ if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
+ {
+ if (wxRichTextBuffer::GetRenderer())
+ wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
+ }
+ else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
+ {
+ if (wxRichTextBuffer::GetRenderer())
+ wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
+ }
+ else
+ {
+ wxString bulletText = GetBulletText();
+
+ if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
+ wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
+ }
+ }
+ }
+
+ // Draw the range for each line, one object at a time.
+
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
+ while (node)
+ {
+ wxRichTextLine* line = node->GetData();
+ wxRichTextRange lineRange = line->GetAbsoluteRange();
+
+ // Lines are specified relative to the paragraph
+
+ wxPoint linePosition = line->GetPosition() + GetPosition();
+
+ // Don't draw if off the screen
+ if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
+ {
+ wxPoint objectPosition = linePosition;
+ int maxDescent = line->GetDescent();
+
+ // Loop through objects until we get to the one within range
+ wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
+
+ int i = 0;
+ while (node2)
+ {
+ wxRichTextObject* child = node2->GetData();
+
+ if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
+ {
+ // Draw this part of the line at the correct position
+ wxRichTextRange objectRange(child->GetRange());
+ objectRange.LimitTo(lineRange);
+
+ wxSize objectSize;
+#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
+ if (i < (int) line->GetObjectSizes().GetCount())
+ {
+ objectSize.x = line->GetObjectSizes()[(size_t) i];
+ }
+ else
+#endif
+ {
+ int descent = 0;
+ child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition);
+ }
+
+ // Use the child object's width, but the whole line's height
+ wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
+ child->Draw(dc, objectRange, selectionRange, childRect, maxDescent, style);
+
+ objectPosition.x += objectSize.x;
+ i ++;
+ }
+ else if (child->GetRange().GetStart() > lineRange.GetEnd())
+ // Can break out of inner loop now since we've passed this line's range
+ break;
+
+ node2 = node2->GetNext();
+ }
+ }
+
+ node = node->GetNext();
+ }
+
+ return true;
+}
+
+// Get the range width using partial extents calculated for the whole paragraph.
+static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
+{
+ wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
+
+ if (partialExtents.GetCount() < (size_t) range.GetLength())
+ return 0;
+
+ int leftMostPos = 0;
+ if (range.GetStart() - para.GetRange().GetStart() > 0)
+ leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
+
+ int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
+
+ int w = rightMostPos - leftMostPos;
+
+ return w;
+}
+
+/// Lay the item out
+bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
+{
+ wxTextAttr attr = GetCombinedAttributes();
+
+ // ClearLines();
+
+ // Increase the size of the paragraph due to spacing
+ int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
+ int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
+ int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
+ int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
+ int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
+
+ int lineSpacing = 0;
+
+ // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
+ if (attr.GetLineSpacing() != 10 && GetBuffer())
+ {
+ wxFont font(GetBuffer()->GetFontTable().FindFont(attr));
+ wxCheckSetFont(dc, font);
+ lineSpacing = (ConvertTenthsMMToPixels(dc, dc.GetCharHeight()) * attr.GetLineSpacing())/10;
+ }
+
+ // Available space for text on each line differs.
+ int availableTextSpaceFirstLine = rect.GetWidth() - leftIndent - rightIndent;
+
+ // Bullets start the text at the same position as subsequent lines
+ if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
+ availableTextSpaceFirstLine -= leftSubIndent;
+
+ int availableTextSpaceSubsequentLines = rect.GetWidth() - leftIndent - rightIndent - leftSubIndent;
+
+ // Start position for each line relative to the paragraph
+ int startPositionFirstLine = leftIndent;
+ int startPositionSubsequentLines = leftIndent + leftSubIndent;
+
+ // If we have a bullet in this paragraph, the start position for the first line's text
+ // is actually leftIndent + leftSubIndent.
+ if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
+ startPositionFirstLine = startPositionSubsequentLines;
+
+ long lastEndPos = GetRange().GetStart()-1;
+ long lastCompletedEndPos = lastEndPos;
+
+ int currentWidth = 0;
+ SetPosition(rect.GetPosition());
+
+ wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
+ int lineHeight = 0;
+ int maxWidth = 0;
+ int maxDescent = 0;
+
+ int lineCount = 0;
+
+ wxRichTextObjectList::compatibility_iterator node;
+
+#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
+ wxUnusedVar(style);
+ wxArrayInt partialExtents;
+
+ wxSize paraSize;
+ int paraDescent;
+
+ // This calculates the partial text extents
+ GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
+#else
+ node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+
+ child->SetCachedSize(wxDefaultSize);
+ child->Layout(dc, rect, style);
+
+ node = node->GetNext();
+ }
+
+#endif
+
+ // Split up lines
+
+ // We may need to go back to a previous child, in which case create the new line,
+ // find the child corresponding to the start position of the string, and
+ // continue.
+
+ node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+
+ if (child->GetRange().GetLength() == 0)
+ {
+ node = node->GetNext();
+ continue;
+ }