X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0a6ec346d56158e98ee91e2661821940a825e899..fb8d7eb7a880f1f2e32d8830f9c5e12b2536e05f:/src/richtext/richtextbuffer.cpp diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index dfcc768eea..4847d40e1d 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -10241,21 +10241,64 @@ bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const w return true; } +wxPosition wxRichTextTable::GetFocusedCell() const +{ + wxPosition position(-1, -1); + const wxRichTextObject* focus = GetBuffer()->GetRichTextCtrl()->GetFocusObject(); + + for (int row = 0; row < GetRowCount(); ++row) + { + for (int col = 0; col < GetColumnCount(); ++col) + { + if (GetCell(row, col) == focus) + { + position.SetRow(row); + position.SetCol(col); + return position; + } + } + } + + return position; +} + bool wxRichTextTable::DeleteRows(int startRow, int noRows) { wxASSERT((startRow + noRows) <= m_rowCount); if ((startRow + noRows) > m_rowCount) return false; + wxCHECK_MSG(noRows != m_rowCount, false, "Trying to delete all the cells in a table"); + wxRichTextBuffer* buffer = GetBuffer(); + wxRichTextCtrl* rtc = buffer->GetRichTextCtrl(); + + wxPosition position = GetFocusedCell(); + int focusCol = position.GetCol(); + int focusRow = position.GetRow(); + if (focusRow >= startRow && focusRow < (startRow+noRows)) + { + // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object + if ((startRow + noRows) < m_rowCount) + { + // There are more rows after the one(s) to be deleted, so set focus in the first of them + rtc->SetFocusObject(GetCell(startRow + noRows, focusCol)); + } + else + { + // Otherwise set focus in the preceding row + rtc->SetFocusObject(GetCell(startRow - 1, focusCol)); + } + } + wxRichTextAction* action = NULL; wxRichTextTable* clone = NULL; - if (!buffer->GetRichTextCtrl()->SuppressingUndo()) + if (!rtc->SuppressingUndo()) { // Create a clone containing the current state of the table. It will be used to Undo the action clone = wxStaticCast(this->Clone(), wxRichTextTable); clone->SetParent(GetParent()); - action = new wxRichTextAction(NULL, _("Delete row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl()); + action = new wxRichTextAction(NULL, _("Delete row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc); action->SetObject(this); action->SetPosition(GetRange().GetStart()); } @@ -10277,7 +10320,7 @@ bool wxRichTextTable::DeleteRows(int startRow, int noRows) m_rowCount = m_rowCount - noRows; - if (!buffer->GetRichTextCtrl()->SuppressingUndo()) + if (!rtc->SuppressingUndo()) { buffer->SubmitAction(action); // Finally store the original-state clone; doing so earlier would cause various failures @@ -10293,15 +10336,37 @@ bool wxRichTextTable::DeleteColumns(int startCol, int noCols) if ((startCol + noCols) > m_colCount) return false; + wxCHECK_MSG(noCols != m_colCount, false, "Trying to delete all the cells in a table"); + wxRichTextBuffer* buffer = GetBuffer(); + wxRichTextCtrl* rtc = buffer->GetRichTextCtrl(); + + wxPosition position = GetFocusedCell(); + int focusCol = position.GetCol(); + int focusRow = position.GetRow(); + if (focusCol >= startCol && focusCol < (startCol+noCols)) + { + // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object + if ((startCol + noCols) < m_colCount) + { + // There are more columns after the one(s) to be deleted, so set focus in the first of them + rtc->SetFocusObject(GetCell(focusRow, startCol + noCols)); + } + else + { + // Otherwise set focus in the preceding column + rtc->SetFocusObject(GetCell(focusRow, startCol - 1)); + } + } + wxRichTextAction* action = NULL; wxRichTextTable* clone = NULL; - if (!buffer->GetRichTextCtrl()->SuppressingUndo()) + if (!rtc->SuppressingUndo()) { // Create a clone containing the current state of the table. It will be used to Undo the action clone = wxStaticCast(this->Clone(), wxRichTextTable); clone->SetParent(GetParent()); - action = new wxRichTextAction(NULL, _("Delete column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl()); + action = new wxRichTextAction(NULL, _("Delete column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc); action->SetObject(this); action->SetPosition(GetRange().GetStart()); } @@ -10327,7 +10392,7 @@ bool wxRichTextTable::DeleteColumns(int startCol, int noCols) m_rowCount = 0; m_colCount = m_colCount - noCols; - if (!buffer->GetRichTextCtrl()->SuppressingUndo()) + if (!rtc->SuppressingUndo()) { buffer->SubmitAction(action); // Finally store the original-state clone; doing so earlier would cause various failures @@ -10821,15 +10886,18 @@ bool wxRichTextAction::Do() wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); if (obj && m_object && m_ctrl) { - // If the cloned object is unparented it will cause layout asserts later - // An alternative (would it always be valid?) could be to do: m_object->SetParent(obj->GetParent()) - wxCHECK_MSG(m_object->GetParent(), false, "The stored object must have a valid parent"); - // The plan is to swap the current object with the stored, previous-state, clone // We can't get 'node' from the containing buffer (as it doesn't directly store objects) // so use the parent paragraph wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph); wxCHECK_MSG(para, false, "Invalid parent paragraph"); + + // The stored object, m_object, may have a stale parent paragraph. This would cause + // a crash during layout, so use obj's parent para, which should be the correct one. + // (An alternative would be to return the parent too from m_objectAddress.GetObject(), + // or to set obj's parent there before returning) + m_object->SetParent(para); + wxRichTextObjectList::compatibility_iterator node = para->GetChildren().Find(obj); if (node) { @@ -11019,13 +11087,16 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent // find the first line which is being drawn at the same position as it was // before. Since we're talking about a simple insertion, we can assume // that the rest of the window does not need to be redrawn. + long pos = GetRange().GetStart(); - wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition()); + wxRichTextParagraph* para = container->GetParagraphAtPosition(pos, false /* is not caret pos */); // Since we support floating layout, we should redraw the whole para instead of just // the first line touching the invalid range. if (para) { - firstY = para->GetPosition().y; + // In case something was drawn above the paragraph, + // such as a line break, allow a little extra. + firstY = para->GetPosition().y - 4; } wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para); @@ -11073,7 +11144,7 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent // Stop, we're now the same as we were foundEnd = true; - lastY = pt.y; + lastY = pt.y + line->GetSize().y; node2 = wxRichTextLineList::compatibility_iterator(); node = wxRichTextObjectList::compatibility_iterator();