X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/fca66f598520911aea221c043ed05e4f0a9eb1c3..62795f413a7222863b4aee76c08764071f94bd87:/src/richtext/richtextbuffer.cpp diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index 230a8a4731..be804ef4d0 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -662,7 +662,7 @@ int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale) // Draw the borders and background for the given rectangle and attributes. // Width and height are taken to be the outer margin size, not the content. -bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags) +bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags, wxRichTextObject* obj) { // Assume boxRect is the area around the content wxRect marginRect = boxRect; @@ -692,12 +692,24 @@ bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, con if (flags & wxRICHTEXT_DRAW_GUIDELINES) { - wxRichTextAttr editBorderAttr = attr; + wxRichTextAttr editBorderAttr; // TODO: make guideline colour configurable editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY); editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS); editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID); + if (obj) + { + wxRichTextCell* cell = wxDynamicCast(obj, wxRichTextCell); + if (cell) + { + // This ensures that thin lines drawn by adjacent cells (left and above) + // don't get overwritten by the guidelines. + editBorderAttr.GetTextBoxAttr().GetBorder().GetLeft().Reset(); + editBorderAttr.GetTextBoxAttr().GetBorder().GetTop().Reset(); + } + } + DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags); } @@ -769,7 +781,7 @@ bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTe wxBrush brush(col); dc.SetPen(pen); dc.SetBrush(brush); - dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height); + dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height + 1); } } @@ -826,7 +838,7 @@ bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTe wxBrush brush(col); dc.SetPen(pen); dc.SetBrush(brush); - dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom); + dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom + 1, rect.width, borderBottom); } } @@ -1840,14 +1852,14 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& cont context.ApplyVirtualAttributes(attr, this); int flags = style; - if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer())) + if (selection.IsValid() && GetParentContainer() != this && selection.GetContainer() == this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer())) flags |= wxRICHTEXT_DRAW_SELECTED; // Don't draw guidelines if at top level int theseFlags = flags; if (!GetParent()) theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES; - DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags); + DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags, this); if (wxRichTextBuffer::GetFloatingLayoutMode()) DrawFloats(dc, context, range, selection, rect, descent, style); @@ -7711,6 +7723,39 @@ wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuf return obj; } +bool wxRichTextParagraphLayoutBox::SetObjectPropertiesWithUndo(wxRichTextObject& obj, const wxRichTextProperties& properties, wxRichTextObject* objToSet) +{ + wxRichTextBuffer* buffer = GetBuffer(); + wxCHECK_MSG(buffer, false, wxT("Invalid buffer")); + wxRichTextCtrl* rtc = buffer->GetRichTextCtrl(); + wxCHECK_MSG(rtc, false, wxT("Invalid rtc")); + + wxRichTextAction* action = NULL; + wxRichTextObject* clone = NULL; + + // The object on which to set properties will usually be 'obj', but use objToSet if it's valid. + // This is necessary e.g. on setting a wxRichTextCell's properties, when obj will be the parent table + if (objToSet == NULL) + objToSet = &obj; + + if (rtc->SuppressingUndo()) + objToSet->SetProperties(properties); + else + { + clone = obj.Clone(); + objToSet->SetProperties(properties); + + // The 'true' parameter in the next line says "Ignore first time"; otherwise the objects are prematurely switched + action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_OBJECT, buffer, obj.GetParentContainer(), rtc, true); + action->SetOldAndNewObjects(& obj, clone); + action->SetPosition(obj.GetRange().GetStart()); + action->SetRange(obj.GetRange()); + buffer->SubmitAction(action); + } + + return true; +} + /// Get the style that is appropriate for a new paragraph at this position. /// If the previous paragraph has a paragraph style name, look up the next-paragraph /// style. @@ -7867,11 +7912,14 @@ bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action) if (BatchingUndo() && m_batchedCommand && !SuppressingUndo()) { - wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName()); - cmd->AddAction(action); - cmd->Do(); - cmd->GetActions().Clear(); - delete cmd; + if (!action->GetIgnoreFirstTime()) + { + wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName()); + cmd->AddAction(action); + cmd->Do(); + cmd->GetActions().Clear(); + delete cmd; + } m_batchedCommand->AddAction(action); } @@ -7881,7 +7929,14 @@ bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action) cmd->AddAction(action); // Only store it if we're not suppressing undo. - return GetCommandProcessor()->Submit(cmd, !SuppressingUndo()); + if (!action->GetIgnoreFirstTime()) + { + return GetCommandProcessor()->Submit(cmd, !SuppressingUndo()); + } + else if (!SuppressingUndo()) + { + GetCommandProcessor()->Store(cmd); // Just store it, without Do()ing anything + } } return true; @@ -9227,6 +9282,22 @@ bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxR return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style); } +int wxRichTextCell::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) +{ + int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags); + if (ret != wxRICHTEXT_HITTEST_NONE) + { + return ret; + } + else + { + textPosition = m_ownRange.GetEnd()-1; + *obj = this; + *contextObj = this; + return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE; + } +} + /// Copy void wxRichTextCell::Copy(const wxRichTextCell& obj) { @@ -9275,18 +9346,20 @@ bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) else caption = _("Cell Properties"); + // We don't want position and floating controls for a cell. + wxRichTextSizePage::ShowPositionControls(false); + wxRichTextSizePage::ShowFloatingControls(false); + wxRichTextSizePage::ShowAlignmentControls(true); + wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption); cellDlg.SetAttributes(attr); - wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage); - if (sizePage) - { - // We don't want position and floating controls for a cell. - sizePage->ShowPositionControls(false); - sizePage->ShowFloatingControls(false); - } + bool ok = (cellDlg.ShowModal() == wxID_OK); + + wxRichTextSizePage::ShowPositionControls(true); + wxRichTextSizePage::ShowFloatingControls(true); - if (cellDlg.ShowModal() == wxID_OK) + if (ok) { if (multipleCells) { @@ -9343,12 +9416,47 @@ wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent // Draws the object. bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style) { - return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style); -} + wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style); -WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray); -WX_DEFINE_OBJARRAY(wxRichTextRectArray); + int colCount = GetColumnCount(); + int rowCount = GetRowCount(); + int col, row; + for (col = 0; col < colCount; col++) + { + for (row = 0; row < rowCount; row++) + { + if (row == 0 || row == (rowCount-1) || col == 0 || col == (colCount-1)) + { + wxRichTextCell* cell = GetCell(row, col); + if (cell && cell->IsShown() && !cell->GetRange().IsOutside(range)) + { + wxRect childRect(cell->GetPosition(), cell->GetCachedSize()); + wxRichTextAttr attr(cell->GetAttributes()); + if (row != 0) + attr.GetTextBoxAttr().GetBorder().GetTop().Reset(); + if (row != (rowCount-1)) + attr.GetTextBoxAttr().GetBorder().GetBottom().Reset(); + if (col != 0) + attr.GetTextBoxAttr().GetBorder().GetLeft().Reset(); + if (col != (colCount-1)) + attr.GetTextBoxAttr().GetBorder().GetRight().Reset(); + + if (attr.GetTextBoxAttr().GetBorder().IsValid()) + { + wxRect boxRect(cell->GetPosition(), cell->GetCachedSize()); + wxRect marginRect = boxRect; + wxRect contentRect, borderRect, paddingRect, outlineRect; + cell->GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); + cell->DrawBorder(dc, GetBuffer(), attr.GetTextBoxAttr().GetBorder(), borderRect); + } + } + } + } + } + + return true; +} // Helper function for Layout() that clears the space needed by a cell with rowspan > 1 int GetRowspanDisplacement(const wxRichTextTable* table, int row, int col, int paddingX, const wxArrayInt& colWidths) @@ -10269,6 +10377,9 @@ bool wxRichTextTable::CreateTable(int rows, int cols) { ClearTable(); + wxRichTextAttr cellattr; + cellattr.SetTextColour(GetBasicStyle().GetTextColour()); + m_rowCount = rows; m_colCount = cols; @@ -10281,6 +10392,8 @@ bool wxRichTextTable::CreateTable(int rows, int cols) for (j = 0; j < cols; j++) { wxRichTextCell* cell = new wxRichTextCell; + cell->GetAttributes() = cellattr; + AppendChild(cell); cell->AddParagraph(wxEmptyString); @@ -10436,6 +10549,23 @@ wxPosition wxRichTextTable::GetFocusedCell() const return position; } +int wxRichTextTable::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags) +{ + for (int row = 0; row < GetRowCount(); ++row) + { + for (int col = 0; col < GetColumnCount(); ++col) + { + wxRichTextCell* cell = GetCell(row, col); + if (cell->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags) != wxRICHTEXT_HITTEST_NONE) + { + return cell->HitTest(dc, context, pt, textPosition, obj, contextObj, flags); + } + } + } + + return wxRICHTEXT_HITTEST_NONE; +} + bool wxRichTextTable::DeleteRows(int startRow, int noRows) { wxASSERT((startRow + noRows) <= m_rowCount); @@ -10447,24 +10577,6 @@ bool wxRichTextTable::DeleteRows(int startRow, int noRows) 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 (!rtc->SuppressingUndo()) @@ -10472,7 +10584,7 @@ bool wxRichTextTable::DeleteRows(int startRow, int noRows) // 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, rtc); + action = new wxRichTextAction(NULL, _("Delete Row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc); action->SetObject(this); action->SetPosition(GetRange().GetStart()); } @@ -10515,24 +10627,6 @@ bool wxRichTextTable::DeleteColumns(int startCol, int noCols) 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 (!rtc->SuppressingUndo()) @@ -10540,7 +10634,7 @@ bool wxRichTextTable::DeleteColumns(int startCol, int noCols) // 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, rtc); + action = new wxRichTextAction(NULL, _("Delete Column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc); action->SetObject(this); action->SetPosition(GetRange().GetStart()); } @@ -10585,16 +10679,21 @@ bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& at wxRichTextBuffer* buffer = GetBuffer(); wxRichTextAction* action = NULL; wxRichTextTable* clone = NULL; + if (!buffer->GetRichTextCtrl()->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, _("Add row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl()); + action = new wxRichTextAction(NULL, _("Add Row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl()); action->SetObject(this); action->SetPosition(GetRange().GetStart()); } + wxRichTextAttr cellattr = attr; + if (!cellattr.GetTextColour().IsOk()) + cellattr.SetTextColour(buffer->GetBasicStyle().GetTextColour()); + int i, j; for (i = 0; i < noRows; i++) { @@ -10614,7 +10713,7 @@ bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& at for (j = 0; j < m_colCount; j++) { wxRichTextCell* cell = new wxRichTextCell; - cell->GetAttributes() = attr; + cell->GetAttributes() = cellattr; AppendChild(cell); cell->AddParagraph(wxEmptyString); @@ -10643,16 +10742,21 @@ bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& wxRichTextBuffer* buffer = GetBuffer(); wxRichTextAction* action = NULL; wxRichTextTable* clone = NULL; + if (!buffer->GetRichTextCtrl()->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, _("Add column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl()); + action = new wxRichTextAction(NULL, _("Add Column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl()); action->SetObject(this); action->SetPosition(GetRange().GetStart()); } + wxRichTextAttr cellattr = attr; + if (!cellattr.GetTextColour().IsOk()) + cellattr.SetTextColour(buffer->GetBasicStyle().GetTextColour()); + int i, j; for (i = 0; i < m_rowCount; i++) { @@ -10660,7 +10764,7 @@ bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& for (j = 0; j < noCols; j++) { wxRichTextCell* cell = new wxRichTextCell; - cell->GetAttributes() = attr; + cell->GetAttributes() = cellattr; AppendChild(cell); cell->AddParagraph(wxEmptyString); @@ -10699,6 +10803,90 @@ bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer) return false; } +bool wxRichTextTableBlock::ComputeBlockForSelection(wxRichTextTable* table, wxRichTextCtrl* ctrl, bool requireCellSelection) +{ + if (!ctrl) + return false; + + ColStart() = 0; + ColEnd() = table->GetColumnCount()-1; + RowStart() = 0; + RowEnd() = table->GetRowCount()-1; + + wxRichTextSelection selection = ctrl->GetSelection(); + if (selection.IsValid() && selection.GetContainer() == table) + { + // Start with an invalid block and increase. + wxRichTextTableBlock selBlock(-1, -1, -1, -1); + wxRichTextRangeArray ranges = selection.GetRanges(); + int row, col; + for (row = 0; row < table->GetRowCount(); row++) + { + for (col = 0; col < table->GetColumnCount(); col++) + { + if (selection.WithinSelection(table->GetCell(row, col)->GetRange().GetStart())) + { + if (selBlock.ColStart() == -1) + selBlock.ColStart() = col; + if (selBlock.ColEnd() == -1) + selBlock.ColEnd() = col; + if (col < selBlock.ColStart()) + selBlock.ColStart() = col; + if (col > selBlock.ColEnd()) + selBlock.ColEnd() = col; + + if (selBlock.RowStart() == -1) + selBlock.RowStart() = row; + if (selBlock.RowEnd() == -1) + selBlock.RowEnd() = row; + if (row < selBlock.RowStart()) + selBlock.RowStart() = row; + if (row > selBlock.RowEnd()) + selBlock.RowEnd() = row; + } + } + } + + if (selBlock.RowStart() != -1 && selBlock.RowEnd() != -1 && selBlock.ColStart() != -1 && selBlock.ColEnd() != -1) + (*this) = selBlock; + } + else + { + // See if a whole cell's contents is selected, in which case we can treat the cell as selected. + // wxRTC lacks the ability to select a single cell. + wxRichTextCell* cell = wxDynamicCast(ctrl->GetFocusObject(), wxRichTextCell); + if (cell && (!requireCellSelection || (ctrl->HasSelection() && ctrl->GetSelectionRange() == cell->GetOwnRange()))) + { + int row, col; + if (table->GetCellRowColumnPosition(cell->GetRange().GetStart(), row, col)) + { + RowStart() = row; + RowEnd() = row; + ColStart() = col; + ColEnd() = col; + } + } + } + + return true; +} + +// Does this block represent the whole table? +bool wxRichTextTableBlock::IsWholeTable(wxRichTextTable* table) const +{ + return (ColStart() == 0 && RowStart() == 0 && ColEnd() == (table->GetColumnCount()-1) && RowEnd() == (table->GetRowCount()-1)); +} + +// Returns the cell focused in the table, if any +wxRichTextCell* wxRichTextTableBlock::GetFocusedCell(wxRichTextCtrl* ctrl) +{ + if (!ctrl) + return NULL; + + wxRichTextCell* cell = wxDynamicCast(ctrl->GetFocusObject(), wxRichTextCell); + return cell; +} + /* * Module to initialise and clean up handlers */ @@ -11081,6 +11269,12 @@ bool wxRichTextAction::Do() } } + // We can't rely on the current focus-object remaining valid, if it's e.g. a table's cell. + // And we can't cope with this in the calling code: a user may later click in the cell + // before deciding to Undo() or Redo(). So play safe and set focus to the buffer. + if (m_ctrl) + m_ctrl->SetFocusObject(m_buffer, false); + // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object, // Layout() would stop prematurely at the top level. // Invalidate the whole buffer if there were floating objects @@ -11089,7 +11283,7 @@ bool wxRichTextAction::Do() else container->InvalidateHierarchy(GetRange()); - UpdateAppearance(GetPosition()); + UpdateAppearance(GetPosition(), true); // TODO: send new kind of modification event @@ -13818,9 +14012,8 @@ void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAtt } WX_DEFINE_OBJARRAY(wxRichTextVariantArray); - -// JACS 2013-01-27 WX_DEFINE_OBJARRAY(wxRichTextAttrArray); +WX_DEFINE_OBJARRAY(wxRichTextRectArray); IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)