// Author: Julian Smart
// Modified by:
// Created: 2005-09-30
-// RCS-ID: $Id$
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// 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;
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);
}
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);
}
}
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);
}
}
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);
/// Submit command to insert the given text
bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
{
- return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
+ if (ctrl)
+ return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
+ else
+ return wxRichTextParagraphLayoutBox::InsertTextWithUndo(this, pos, text, ctrl, flags);
}
/// Submit command to insert the given text
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.
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);
}
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;
return wildcard;
}
+#if wxUSE_FFILE && wxUSE_STREAMS
/// Load a file
bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
{
else
return false;
}
+#endif // wxUSE_FFILE && wxUSE_STREAMS
+#if wxUSE_STREAMS
/// Load from a stream
bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
{
else
return false;
}
+#endif // wxUSE_STREAMS
/// Copy the range to the clipboard
bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
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)
{
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)
{
// 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)
const int colCount = table->GetColumnCount();
wxArrayInt rowTops;
rowTops.Add(0, rowCount+1);
- for (int row = 0; row < rowCount; ++row)
+ int row;
+ for (row = 0; row < rowCount; ++row)
{
for (int column = 0; column < colCount; ++column)
{
bool needsRelay = false;
- int row, col;
for (row = 0; row < rowCount-1; ++row) // -1 as the bottom row can't rowspan
{
- for (col = 0; col < colCount; ++col)
+ for (int col = 0; col < colCount; ++col)
{
wxRichTextCell* cell = table->GetCell(row, col);
if (cell && cell->IsShown())
// There were overflowing rowspanning cells, so layout yet again to make the increased row depths show
for (row = 0; row < rowCount; ++row)
{
- for (col = 0; col < colCount; ++col)
+ for (int col = 0; col < colCount; ++col)
{
wxRichTextCell* cell = table->GetCell(row, col);
if (cell && cell->IsShown())
wxRichTextAttr attr(GetAttributes());
context.ApplyVirtualAttributes(attr, this);
+ bool tableHasPercentWidth = (attr.GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE);
// If we have no fixed table size, and assuming we're not pushed for
// space, then we don't have to try to stretch the table to fit the contents.
- bool stretchToFitTableWidth = false;
-
+ bool stretchToFitTableWidth = tableHasPercentWidth;
+
int tableWidth = rect.width;
- if (attr.GetTextBoxAttr().GetWidth().IsValid())
+ if (attr.GetTextBoxAttr().GetWidth().IsValid() && !tableHasPercentWidth)
{
tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
{
ClearTable();
+ wxRichTextAttr cellattr;
+ cellattr.SetTextColour(GetBasicStyle().GetTextColour());
+
m_rowCount = rows;
m_colCount = cols;
for (j = 0; j < cols; j++)
{
wxRichTextCell* cell = new wxRichTextCell;
+ cell->GetAttributes() = cellattr;
+
AppendChild(cell);
cell->AddParagraph(wxEmptyString);
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);
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())
// 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());
}
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())
// 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());
}
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++)
{
for (j = 0; j < m_colCount; j++)
{
wxRichTextCell* cell = new wxRichTextCell;
- cell->GetAttributes() = attr;
+ cell->GetAttributes() = cellattr;
AppendChild(cell);
cell->AddParagraph(wxEmptyString);
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++)
{
for (j = 0; j < noCols; j++)
{
wxRichTextCell* cell = new wxRichTextCell;
- cell->GetAttributes() = attr;
+ cell->GetAttributes() = cellattr;
AppendChild(cell);
cell->AddParagraph(wxEmptyString);
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
*/
}
}
+ // 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
else
container->InvalidateHierarchy(GetRange());
- UpdateAppearance(GetPosition());
+ UpdateAppearance(GetPosition(), true);
// TODO: send new kind of modification event
}
WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
-
-// JACS 2013-01-27
WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
+WX_DEFINE_OBJARRAY(wxRichTextRectArray);
IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)