More table editing work: convert Excel spreadsheet to internal table format for more flexible paste options

This commit is contained in:
cmanske%netscape.com 1998-08-18 20:24:37 +00:00
Родитель f7c322de3f
Коммит 856a8dc5c8
5 изменённых файлов: 486 добавлений и 283 удалений

Просмотреть файл

@ -1024,6 +1024,11 @@ public:
// Uses m_ColumnLayoutData and m_RowLayoutData // Uses m_ColumnLayoutData and m_RowLayoutData
void FixupColumnsAndRows(); void FixupColumnsAndRows();
// Add empty cells to each row so table looks rectangular
// (each row has same number of "virtual" cells,
// compensating for effects of COLSPAN and ROWSPAN
void NormalizeCellsPerRow();
private: private:
ED_Color m_backgroundColor; ED_Color m_backgroundColor;
char* m_pBackgroundImage; char* m_pBackgroundImage;
@ -1175,12 +1180,12 @@ public:
CEditTableCellElement(CEditElement *pParent, PA_Tag *pTag, int16 csid); CEditTableCellElement(CEditElement *pParent, PA_Tag *pTag, int16 csid);
CEditTableCellElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer); CEditTableCellElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);
virtual ~CEditTableCellElement(); virtual ~CEditTableCellElement();
virtual XP_Bool IsTableCell(); virtual XP_Bool IsTableCell();
static CEditTableCellElement* Cast(CEditElement* pElement) { static CEditTableCellElement* Cast(CEditElement* pElement) {
return pElement && pElement->IsTableCell() ? (CEditTableCellElement*) pElement : 0; } return pElement && pElement->IsTableCell() ? (CEditTableCellElement*) pElement : 0; }
virtual EEditElementType GetElementType(); virtual EEditElementType GetElementType();
virtual ED_Alignment GetDefaultAlignment(); virtual ED_Alignment GetDefaultAlignment();
void Unlink();
XP_Bool IsTableData(); XP_Bool IsTableData();
@ -1267,6 +1272,8 @@ public:
int32 GetX() { return m_X; } int32 GetX() { return m_X; }
int32 GetY() { return m_Y; } int32 GetY() { return m_Y; }
void SetX(int32 x) { m_X = x; }
void SetY(int32 y) { m_Y = y; }
intn GetRow() { return m_iRow; } intn GetRow() { return m_iRow; }
int32 GetWidth() { return m_iWidthPixels; } int32 GetWidth() { return m_iWidthPixels; }
int32 GetHeight() { return m_iHeightPixels; } int32 GetHeight() { return m_iHeightPixels; }

Просмотреть файл

@ -13040,21 +13040,22 @@ NORMAL_PASTE:
// Delete table or cells in original location if moving within the doc // Delete table or cells in original location if moving within the doc
if( m_bDeleteTableAfterPasting ) if( m_bDeleteTableAfterPasting )
{ {
// The only way to end up here is if we are
// pasting as a new table
XP_ASSERT(m_pSelectedEdTable);
if( m_pSelectedEdTable ) if( m_pSelectedEdTable )
{ {
// Delete the entire table. // Delete the entire table.
// FALSE = don't reposition insert point // FALSE = don't reposition insert point
m_pSelectedEdTable->Delete(FALSE); m_pSelectedEdTable->Delete(FALSE);
} }
#if 0
else if( m_SelectedEdCells.Size() ) else if( m_SelectedEdCells.Size() )
{ {
// We must save and restore current insert point
// because DeleteSelectedCells will move it
// to the table whose cells are deleted
CEditInsertPoint ip;
GetInsertPoint(ip);
DeleteSelectedCells(); DeleteSelectedCells();
SetInsertPoint(ip);
} }
#endif
m_bDeleteTableAfterPasting = FALSE; m_bDeleteTableAfterPasting = FALSE;
} }
@ -13120,6 +13121,85 @@ BAD_CLIPBOARD:
#define EDT_PASTE_ROW(t) (t == ED_PASTE_ROW_ABOVE || t == ED_PASTE_ROW_BELOW) #define EDT_PASTE_ROW(t) (t == ED_PASTE_ROW_ABOVE || t == ED_PASTE_ROW_BELOW)
#define EDT_PASTE_COLUMN(t) (t == ED_PASTE_COLUMN_BEFORE || t == ED_PASTE_COLUMN_AFTER) #define EDT_PASTE_COLUMN(t) (t == ED_PASTE_COLUMN_BEFORE || t == ED_PASTE_COLUMN_AFTER)
#define END_OF_TABLE(pEle) (pEle == NULL || pEle->type == LO_LINEFEED)
// Returns TRUE if there is another row to process
// (pLoEle points to first cell on next row, and iRowY is updated)
static XP_Bool edt_SetSpecialSelectRow(MWContext *pMWContext, LO_Element*& pLoEle, int32 iCellsInRow,
int32 iFirstColX, int32& iRowY )
{
// Find first LO cell at appropriate column in each row to be marked
while(TRUE)
{
if( pLoEle && pLoEle->type == LO_CELL && pLoEle->lo_cell.x >= iFirstColX )
break;
pLoEle = pLoEle->lo_any.next;
}
if(END_OF_TABLE(pLoEle))
return FALSE;
XP_ASSERT(pLoEle && pLoEle->type == LO_CELL & iRowY == pLoEle->lo_cell.y );
for( intn i = 0; i< iCellsInRow; i++ )
{
if(END_OF_TABLE(pLoEle))
return FALSE;
LO_CellStruct *pLoCell = (LO_CellStruct*)pLoEle;
// Check if cell is in proper row
if( pLoCell->y != iRowY )
// We are in another row - get out
break;
// Mark cell with special selection
pLoCell->ele_attrmask |= LO_ELE_SELECTED_SPECIAL;
FE_DisplayEntireTableOrCell(pMWContext, pLoEle);
// Set flag in Editor's cell elements
CEditTableCellElement *pEdCell =
(CEditTableCellElement*)edt_GetTableElementFromLO_Element(pLoEle, LO_CELL);
if( pEdCell )
pEdCell->SetSpecialSelected(TRUE);
#ifdef DEBUG
else
XP_ASSERT(FALSE);
#endif
// Move to next table cell
pLoEle = pLoEle->lo_any.next;
while(!END_OF_TABLE(pLoEle))
{
if( pLoEle->type == LO_CELL )
break;
pLoEle = pLoEle->lo_any.next;
}
}
// Check if done because no more cells
if(END_OF_TABLE(pLoEle))
return FALSE;
// We are done marking cells in this row
// move to next row unless already there
if( pLoEle->type == LO_CELL && pLoEle->lo_cell.y == iRowY )
{
pLoEle = pLoEle->lo_any.next;
while(!END_OF_TABLE(pLoEle))
{
if( pLoEle->type == LO_CELL && pLoEle->lo_cell.y != iRowY )
break;
pLoEle = pLoEle->lo_any.next;
}
}
if(END_OF_TABLE(pLoEle))
return FALSE;
// If here, pLoEle should be first cell in next row to mark
XP_ASSERT(pLoEle && pLoEle->type == LO_CELL);
// Set top of next row
iRowY = pLoEle->lo_cell.y;
return TRUE;
}
void CEditBuffer::PasteTable( CEditTableCellElement *pCell, CEditTableElement *pSourceTable, ED_PasteType iPasteType ) void CEditBuffer::PasteTable( CEditTableCellElement *pCell, CEditTableElement *pSourceTable, ED_PasteType iPasteType )
{ {
@ -13136,32 +13216,67 @@ void CEditBuffer::PasteTable( CEditTableCellElement *pCell, CEditTableElement *p
XP_Bool bAfterCurrentCell = (iPasteType == ED_PASTE_COLUMN_AFTER || XP_Bool bAfterCurrentCell = (iPasteType == ED_PASTE_COLUMN_AFTER ||
iPasteType == ED_PASTE_ROW_BELOW ); iPasteType == ED_PASTE_ROW_BELOW );
// Note: These values counts COLSPAN and ROWSPAN,
// so this is number of "virtual" columns or rows
// [Be sure pasting routines can handle missing cells]
int32 X = pCell->GetX(); int32 X = pCell->GetX();
int32 Y = pCell->GetY(); int32 Y = pCell->GetY();
int32 iNewX = X + (bAfterCurrentCell ? pCell->GetFullWidth() : 0); // GetFullWidth is used in edtcmd.cpp routines int32 iNewX = X + (bAfterCurrentCell ? pCell->GetFullWidth() : 0);
int32 iNewY = Y + (bAfterCurrentCell ? pCell->GetHeight() : 0); int32 iNewY = Y + (bAfterCurrentCell ? pCell->GetHeight() : 0);
// Should always be 0 initially - lets test it // Should always be 0 initially - lets test it
XP_ASSERT(m_pCellForInsertPoint == NULL); XP_ASSERT(m_pCellForInsertPoint == NULL);
m_pCellForInsertPoint = NULL; m_pCellForInsertPoint = NULL;
if( iPasteType == ED_PASTE_REPLACE_CELLS ) // For now, ignore source layout when pasting over a user-selection
// When drag/dropping, lets assume we predicted the destination layout correctly
// TODO: ONLY IGNORE SOURCE LAYOUT FOR CERTAIN COPY TYPES?
XP_Bool bIgnoreSourceLayout = (m_pDragTableData == NULL);
if( iPasteType == ED_PASTE_REPLACE_CELLS ||
(pCell->IsSelected() && iPasteType == ED_PASTE_NORMAL) )
{ {
if( !m_pDragTableData ) if( !m_pDragTableData )
{
if( m_SelectedEdCells.Size() > 0 )
{ {
// We are pasting over current selected cells only // We are pasting over current selected cells only
// Set all selected cells to the "special selection" mode, // Set all selected cells to the "special selection" mode
// which happens with DragNDrop as well
DisplaySpecialCellSelection(); DisplaySpecialCellSelection();
} }
// Second param is bIgnoreSourceLayout else
// For now, use that when pasting over a user-selection {
// When drag/dropping, lets assume we predicted the destination layout correctly // Replace as if we were dragging -
// TODO: ONLY IGNORE SOURCE LAYOUT FOR CERTAIN COPY TYPES? // start special selection with current cell
bAllCellsPasted = pTable->ReplaceSpecialCells(pSourceTable, !m_pDragTableData, // and mark cells according to pattern in the source table
bIgnoreSourceLayout = FALSE;
CEditTableRowElement *pSourceRow = pSourceTable->GetFirstRow();
LO_Element *pLoEle = (LO_Element*)GetLoCell((CEditElement*)pCell);
if( !pLoEle || !pSourceRow )
return;
// This will get updated by edt_SetSpecialSelectRow
int32 iRowY = pLoEle->lo_cell.y;
while( pSourceRow )
{
// Count cells in each source row
// We DON'T want to pay attention to COLSPAN and ROWSPAN
int32 iCellsInRow = 0;
CEditElement *pSourceCell = pSourceRow->GetChild();
while( pSourceCell )
{
XP_ASSERT(pSourceCell->IsTableCell());
iCellsInRow++;
pSourceCell = pSourceCell->GetNextSibling();
}
// This code is shared with SetReplaceCellSelection(),
if( !edt_SetSpecialSelectRow(m_pContext, pLoEle, iCellsInRow,
pLoEle->lo_cell.x, iRowY) )
break;
pSourceRow = pSourceRow->GetNextRow();
}
}
}
bAllCellsPasted = pTable->ReplaceSpecialCells(pSourceTable, bIgnoreSourceLayout,
&m_pCellForInsertPoint); &m_pCellForInsertPoint);
} }
else if( EDT_PASTE_ROW(iPasteType) ) else if( EDT_PASTE_ROW(iPasteType) )
@ -13314,48 +13429,85 @@ EDT_ClipboardResult CEditBuffer::PasteTextAsTable(char *pText, ED_PasteType iPas
if( iRows == 0 || iCols == 0 ) if( iRows == 0 || iCols == 0 )
CountRowsAndColsInPasteText(pText, &iRows, &iCols); CountRowsAndColsInPasteText(pText, &iRows, &iCols);
if( iPasteType == ED_PASTE_NORMAL || iRows == 0 || iCols == 0 ) if( iPasteType == ED_PASTE_IMAGE || (iRows <= 0 || iCols <= 0) )
return EDT_COP_CLIPBOARD_BAD; return EDT_COP_CLIPBOARD_BAD;
XP_Bool bNewTable = (iPasteType == ED_PASTE_NORMAL); // We really shouldn't be called with this, but we know what to do with it
if( iPasteType == ED_PASTE_TEXT )
EDT_TableData *pData = bNewTable ? EDT_NewTableData() : GetTableData();
CEditTableElement *pDebugTable = NULL; // ******* DEBUG TEST ONLY
// TODO: REWRITE THIS -- BUILD A TABLE AND INSERT TEXT,
// THEN CALL CEditBuffer::PasteTable()
if( pData )
{ {
if( bNewTable ) // 2nd to last param = don't relayout, last param = we want to reduce
{ return PasteText(pText, FALSE, FALSE, TRUE, TRUE);
pData->iRows = iRows;
pData->iColumns = iCols;
InsertTable(pData);
EDT_FreeTableData(pData);
// We assume InsertTable will put caret in first cell of table
CEditInsertPoint ip;
GetTableInsertPoint(ip);
pDebugTable = ip.m_pElement->GetTableIgnoreSubdoc();
} }
CEditTableElement *pTable = m_pCurrent->GetTableIgnoreSubdoc();
CEditInsertPoint ip;
GetTableInsertPoint(ip);
int32 iRow = 0; // Decide default behavior:
if( iPasteType == ED_PASTE_NORMAL )
if( pTable )
{ {
CEditTableCellElement* pNextCell = bNewTable ? if( IsInsertPointInTable() )
pTable->GetFirstCell() : m_pCurrent->GetParentTableCell(); {
CEditTableCellElement *pCell = m_pCurrent->GetParentTableCell();
// Replace selected cells if trying to paste over them
if( pCell && pCell->IsSelected() )
iPasteType = ED_PASTE_REPLACE_CELLS;
else
// Use shape of source to decide to paste row vs. column
iPasteType = (iRows > iCols) ? ED_PASTE_ROW_ABOVE : ED_PASTE_COLUMN_BEFORE;
}
else
iPasteType = ED_PASTE_TABLE;
}
XP_Bool bInsertFullTable = (iPasteType == ED_PASTE_TABLE );
// Create a new table
// This will either be inserted whole or used as the
// source table for row/column pasting into existing table
CEditTableElement *pTable = NULL;
if( bInsertFullTable )
{
// Create table using default setting for new table
EDT_TableData *pData = EDT_NewTableData();
if( !pData )
return EDT_COP_CLIPBOARD_BAD;
pData->iColumns = iCols;
pData->iRows = iRows;
pTable = InsertTable(pData);
EDT_FreeTableData(pData);
}
else
{
XP_ASSERT( IsInsertPointInTable());
if( !IsInsertPointInTable() )
return EDT_COP_CLIPBOARD_BAD;
// Just a temporary table to hold text data
pTable = new CEditTableElement( iCols, iRows);
}
if( !pTable )
return EDT_COP_CLIPBOARD_BAD;
// Set default text in each cell
SetFillNewCellWithSpace();
pTable->FinishedLoad(this);
ClearFillNewCellWithSpace();
CEditTableCellElement* pFirstCell = pTable->GetFirstCell();
XP_ASSERT(pFirstCell);
CEditTableCellElement* pCell = pFirstCell;
intn iRow = 0;
intn iPrevRow = 0;
// This is actually used as the X value for
// each cell created. We know we have "normal"
// array of cells so this allows CountColumns() to work when pasting
int32 iCol = 0;
char *pCellText = pText; char *pCellText = pText;
XP_Bool bDone = FALSE;
XP_Bool bEndOfRow = FALSE; XP_Bool bEndOfRow = FALSE;
while(!bDone) while(TRUE)
{
// Get one token of text from the string
do
{ {
do {
char current = *pText; char current = *pText;
if( current == 9) if( current == 9)
{ {
@ -13387,55 +13539,66 @@ EDT_ClipboardResult CEditBuffer::PasteTextAsTable(char *pText, ED_PasteType iPas
pText++; pText++;
} while( pText ); } while( pText );
// We fake X,Y coordinates for cells so
// CountColumns() will work correctly when pasting
pCell->SetX(iCol++);
if( pCellText && *pCellText ) if( pCellText && *pCellText )
// 2nd to last param = don't relayout, last param = we want to reduce
PasteText(pCellText, FALSE, FALSE, FALSE, TRUE);
intn iNextRow = iRow;
// Move insert point to next cell, caret at end of contents
// If its in next row, iNextRow will be incremented
if( NextTableCell(TRUE, TRUE, &iNextRow) )
{ {
// Get the existing empty text element in the table
CEditTextElement *pText = pCell->GetFirstMostChild()->Text();
pText->SetText(pCellText);
}
if(bEndOfRow) if(bEndOfRow)
{ {
// Next item should go into next row
iRow++;
// If we have a short row (fewer tabs than maximum), // If we have a short row (fewer tabs than maximum),
// skip to first cell of next row // skip to first cell of next row
XP_Bool bGetNext = TRUE; while( pCell && iRow == iPrevRow )
while( bGetNext && iRow != iNextRow )
{ {
bGetNext = NextTableCell(TRUE, TRUE, &iNextRow); pCell = pCell->GetNextCellInTable(&iRow);
if(pCell)
pCell->SetX(iCol++);
} }
bEndOfRow = FALSE; bEndOfRow = FALSE;
iPrevRow = iRow;
iCol = 0;
} }
else if( iRow != iNextRow ) else
{ {
// Should never happen - we are in next row of new table, pCell = pCell->GetNextCellInTable(&iRow);
// but we're not at end of text "row" if( iRow != iPrevRow )
{
// We are in next row, but there's still text in source
// SHOULD NEVER HAPPEN if CountRowsAndColsInPasteText is working
XP_ASSERT(FALSE); XP_ASSERT(FALSE);
iPrevRow = iRow;
iCol = 0;
}
} }
// The next cell item starts at next character // The next cell item starts at next character
pCellText = pText; pCellText = pText;
if( *pText == '\0' ) if( !pCell || *pText == '\0' )
bDone = TRUE; break;
}
else
{
//TODO: We should probably test if any text is left
// here - there shouldn't be any
bDone = TRUE;
}
} //while(!bDone)
// Return insert point to the first cell }
SetInsertPoint(ip); //TODO: We should probably trigger the "Not all cells were pasted" message
// if there's still source text left to paste
if( bInsertFullTable )
{
// Set insert point inside first cell in table
SetTableInsertPoint(pFirstCell);
// Relayout the entire table // Relayout the entire table
Relayout(pTable, 0); Relayout(pTable, 0);
} }
else
{
// Insert rows or cols -- this will Relayout the table
pCell = m_pCurrent->GetParentTableCell();
PasteTable(pCell, pTable, iPasteType);
} }
return EDT_COP_OK; return EDT_COP_OK;
@ -15528,8 +15691,6 @@ void CEditBuffer::StartSpecialCellSelection(EDT_TableCellData *pCellData)
DisplaySpecialCellSelection( pEdCell, pCellData ); DisplaySpecialCellSelection( pEdCell, pCellData );
} }
#define END_OF_TABLE(pEle) (pEle == NULL || pEle->type == LO_LINEFEED)
void CEditBuffer::SetReplaceCellSelection() void CEditBuffer::SetReplaceCellSelection()
{ {
if( !m_pDragTableData || !m_pDragTableData->pDragOverCell || m_pDragTableData->iRows <= 0 || !m_pDragTableData->pCellsPerRow ) if( !m_pDragTableData || !m_pDragTableData->pDragOverCell || m_pDragTableData->iRows <= 0 || !m_pDragTableData->pCellsPerRow )
@ -15549,120 +15710,17 @@ void CEditBuffer::SetReplaceCellSelection()
// Reset replace cell // Reset replace cell
edt_pPrevReplaceCellSelected = m_pDragTableData->pDragOverCell; edt_pPrevReplaceCellSelected = m_pDragTableData->pDragOverCell;
// Copy array so we can decrement values as we mark cells LO_Element *pLoEle = m_pDragTableData->pDragOverCell;
int32 *pSourceCellsLeftInRow = (int32*)XP_ALLOC(m_pDragTableData->iRows * sizeof(int32)); // This will get updated by edt_SetSpecialSelectRow for each row
XP_ASSERT(pSourceCellsLeftInRow); int32 iRowY = pLoEle->lo_cell.y;
if(!pSourceCellsLeftInRow)
return;
intn i;
for( i = 0; i < m_pDragTableData->iRows; i++)
pSourceCellsLeftInRow[i] = m_pDragTableData->pCellsPerRow[i];
LO_Element *pEle = m_pDragTableData->pDragOverCell; for( intn iRow = 0; iRow < m_pDragTableData->iRows; iRow++ )
int32 iRowY = pEle->lo_cell.y;
int32 iFirstColX = pEle->lo_cell.x;
intn iRow;
for( iRow = 0; iRow < m_pDragTableData->iRows; iRow++ )
{ {
// Find first cell at appropriate column in each row to be marked // Most of the code is shared with section in PasteTable() for replace cell logic
while(TRUE) if( !edt_SetSpecialSelectRow(m_pContext, pLoEle, m_pDragTableData->pCellsPerRow[iRow],
{ pLoEle->lo_cell.x, iRowY) )
if( pEle && pEle->type == LO_CELL && pEle->lo_cell.x >= iFirstColX )
break; break;
pEle = pEle->lo_any.next;
} }
if(END_OF_TABLE(pEle))
break;
XP_ASSERT(pEle && pEle->type == LO_CELL & iRowY == pEle->lo_cell.y );
while(pSourceCellsLeftInRow[iRow])
{
if(END_OF_TABLE(pEle))
break;
LO_CellStruct *pLoCell = (LO_CellStruct*)pEle;
// Check if cell is in proper row
if( pLoCell->y != iRowY )
// We are in another row - get out
break;
#if 0
// Because we decided to ignore the COLSPAN and ROWSPAN of the source
// cells, and use that of the destination, we don't need to
// get fancy here. The following matches geometry of source to
// destination by considering COLSPAN and ROWSPAN
int32 iColSpan = lo_GetColSpan(pEle);
int32 iRowSpan = lo_GetRowSpan(pEle);
// Adjust for effect of rowspan on number of cells in following rows
if( iRowSpan > 1 )
{
for( intn j = 1; j < iRowSpan; j++ )
{
if( iColSpan < pSourceCellsLeftInRow[j] )
pSourceCellsLeftInRow[j] -= iColSpan;
else
pSourceCellsLeftInRow[j] = 0;
}
}
// Reduce the count of cells left to mark
if( iColSpan < pSourceCellsLeftInRow[iRow] )
pSourceCellsLeftInRow[iRow] -= iColSpan;
else
pSourceCellsLeftInRow[iRow] = 0;
#endif
// Because we decided to ignore the COLSPAN
pSourceCellsLeftInRow[iRow]--;
// Mark cell with special selection
pLoCell->ele_attrmask |= LO_ELE_SELECTED_SPECIAL;
FE_DisplayEntireTableOrCell(m_pContext, pEle);
// Set flag in Editor's cell elements
CEditTableCellElement *pEdCell =
(CEditTableCellElement*)edt_GetTableElementFromLO_Element(pEle, LO_CELL);
if( pEdCell )
pEdCell->SetSpecialSelected(TRUE);
#ifdef DEBUG
else
XP_ASSERT(FALSE);
#endif
// Move to next table cell
pEle = pEle->lo_any.next;
while(!END_OF_TABLE(pEle))
{
if( pEle->type == LO_CELL )
break;
pEle = pEle->lo_any.next;
}
}
// Check if done because no more cells
if(END_OF_TABLE(pEle))
break;
// We are done marking cells in this row
// move to next row unless already there
if( pEle->type == LO_CELL && pEle->lo_cell.y == iRowY )
{
pEle = pEle->lo_any.next;
while(!END_OF_TABLE(pEle))
{
if( pEle->type == LO_CELL && pEle->lo_cell.y != iRowY )
break;
pEle = pEle->lo_any.next;
}
}
if(END_OF_TABLE(pEle))
break;
// If here, pEle should be first cell in next row to mark
XP_ASSERT(pEle && pEle->type == LO_CELL);
// Get top of next row
iRowY = pEle->lo_cell.y;
}
XP_FREE(pSourceCellsLeftInRow);
} }
void CEditBuffer::ClearSpecialCellSelection(LO_Element *pDragOverCell) void CEditBuffer::ClearSpecialCellSelection(LO_Element *pDragOverCell)

Просмотреть файл

@ -1055,7 +1055,13 @@ CDeleteTableRowCommand::CDeleteTableRowCommand(CEditBuffer* pBuffer, intn rows,
pBuffer->m_pCellForInsertPoint = 0; pBuffer->m_pCellForInsertPoint = 0;
pTable->DeleteRows(Y, rows, &pBuffer->m_pCellForInsertPoint); pTable->DeleteRows(Y, rows, &pBuffer->m_pCellForInsertPoint);
pTable->FinishedLoad(pBuffer); pTable->FinishedLoad(pBuffer);
if( pBuffer->m_pCellForInsertPoint == NULL )
{
// Move to a safe location so Relayout() doesn't assert // Move to a safe location so Relayout() doesn't assert
CEditElement *pLeaf = pTable->FindPreviousElement(&CEditElement::FindLeafAll, 0 );
if( pLeaf )
pBuffer->SetInsertPoint(pLeaf->Leaf(), 0, pBuffer->m_bCurrentStickyAfter);
}
pBuffer->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET); pBuffer->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET);
} }
} }
@ -1129,7 +1135,13 @@ CDeleteTableColumnCommand::CDeleteTableColumnCommand(CEditBuffer* pBuffer, intn
// We don't save the table to undo any more // We don't save the table to undo any more
pTable->DeleteColumns(X, columns, &pBuffer->m_pCellForInsertPoint ); pTable->DeleteColumns(X, columns, &pBuffer->m_pCellForInsertPoint );
pTable->FinishedLoad(pBuffer); pTable->FinishedLoad(pBuffer);
if( pBuffer->m_pCellForInsertPoint == NULL )
{
// Move to a safe location so Relayout() doesn't assert // Move to a safe location so Relayout() doesn't assert
CEditElement *pLeaf = pTable->FindPreviousElement(&CEditElement::FindLeafAll, 0 );
if( pLeaf )
pBuffer->SetInsertPoint(pLeaf->Leaf(), 0, pBuffer->m_bCurrentStickyAfter);
}
pBuffer->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET); pBuffer->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET);
} }
} }
@ -1205,7 +1217,13 @@ CDeleteTableCellCommand::CDeleteTableCellCommand(CEditBuffer* pBuffer, intn colu
pBuffer->m_pCellForInsertPoint = 0; pBuffer->m_pCellForInsertPoint = 0;
pTableRow->DeleteCells(X, columns, &pBuffer->m_pCellForInsertPoint); pTableRow->DeleteCells(X, columns, &pBuffer->m_pCellForInsertPoint);
pTable->FinishedLoad(pBuffer); pTable->FinishedLoad(pBuffer);
if( pBuffer->m_pCellForInsertPoint == NULL )
{
// Move to a safe location so Relayout() doesn't assert // Move to a safe location so Relayout() doesn't assert
CEditElement *pLeaf = pTable->FindPreviousElement(&CEditElement::FindLeafAll, 0 );
if( pLeaf )
pBuffer->SetInsertPoint(pLeaf->Leaf(), 0, pBuffer->m_bCurrentStickyAfter);
}
pBuffer->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET); pBuffer->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET);
} }
} }

Просмотреть файл

@ -2718,6 +2718,90 @@ void CEditTableElement::FixupColumnsAndRows()
} }
} }
void CEditTableElement::NormalizeCellsPerRow()
{
CEditTableCellElement *pFirstCell = GetFirstCell();
CEditBuffer *pBuffer = GetEditBuffer();
if( !pFirstCell || !pBuffer )
return;
int32 iRows = CountRows();
int32 iArraySize = iRows * sizeof(int32);
int32 *pCellsPerRow = (int32*)XP_ALLOC(iArraySize);
if( !pCellsPerRow )
return;
XP_MEMSET( pCellsPerRow, 0, iArraySize );
CEditTableCellElement *pCell = pFirstCell;
intn iRow = 0;
intn iPrevRow = 0;
int32 iCellsInRow = 0;
while( pCell )
{
intn iColSpan = pCell->GetColSpan();
intn iRowSpan = pCell->GetRowSpan();
pCellsPerRow[iRow] += iColSpan;
// Fixup subsequent rows because of rowspan
if( iRowSpan > 1 )
{
for( intn j = 1; j < iRowSpan; j++ )
{
// We may overrun our array if table is "bad"
// because of a ROWSPAN value that exceeds actual
// number of rows. Just skip attempts to access a value too high
// since there is no row to use the "ExtraColumns" anyway.
if( iRow+j < iRows )
pCellsPerRow[iRow+j] += iColSpan;
else
// Fixup bad cell????
;//pCell->SetRowSpan(iRows-1);
}
}
pCell = pCell->GetNextCellInTable(&iRow);
}
XP_ASSERT((iRow+1) == iRows); // Safety check
// Find the maximum cells per row
intn iTotalRows = pCellsPerRow[0];
intn i;
for( i = 1; i < iRows; i++ )
{
if( pCellsPerRow[i] > iTotalRows )
iTotalRows = pCellsPerRow[i];
}
pBuffer->SetFillNewCellWithSpace();
CEditTableRowElement *pRow = GetFirstRow();
// Now go through rows and add cells as needed
for( i = 0; i < iRows; i++ )
{
XP_ASSERT(pRow);
if( !pRow )
break;
intn iExtraCells = iTotalRows - pCellsPerRow[i];
for( intn i = 0; i < iExtraCells; i++ )
{
CEditTableCellElement *pNewCell = new CEditTableCellElement();
if( pNewCell )
{
// What could we set this to to make CountColumns() work?
//pNewCell->SetX(????);
pNewCell->InsertAsLastChild(pRow);
pNewCell->FinishedLoad(pBuffer);
}
}
pRow = pRow->GetNextRow();
}
pBuffer->ClearFillNewCellWithSpace();
}
void CEditTableElement::InsertRows(int32 Y, int32 iNewY, intn number, void CEditTableElement::InsertRows(int32 Y, int32 iNewY, intn number,
CEditTableElement* pSourceTable, intn iStartColumn, CEditTableElement* pSourceTable, intn iStartColumn,
@ -2748,28 +2832,29 @@ void CEditTableElement::InsertRows(int32 Y, int32 iNewY, intn number,
CEditBuffer *pBuffer = GetEditBuffer(); CEditBuffer *pBuffer = GetEditBuffer();
XP_ASSERT(pBuffer); XP_ASSERT(pBuffer);
CEditTableCellElement *pCellForInsertPoint = NULL; CEditTableCellElement *pCellForInsertPoint = NULL;
if( pSourceTable ) int32 iCurrentColumns = GetColumns();
pCellForInsertPoint = pSourceTable->GetFirstCell(); int32 iTotalColumns = iCurrentColumns;
// Set to the total to simplify test padding the rows near end of function
int32 iSourceColumns = iTotalColumns;
if( pSourceTable && iStartColumn > 0 ) if( pSourceTable )
{ {
iSourceColumns = pSourceTable->CountColumns();
CEditTableRowElement *pSourceRow;
// If inserted rows will require new columns, // If inserted rows will require new columns,
// figure that out now and add the necessary columns // figure that out now and add the necessary columns
int32 iTotalColumns = iStartColumn + pSourceTable->CountColumns(); // Figure the total number of columns in table after inserting
if( iTotalColumns > GetColumns() ) iTotalColumns = max(iCurrentColumns, iStartColumn + pSourceTable->CountColumns());
{
CEditTableRowElement *pTableRow = GetFirstRow();
while( pTableRow )
{
pTableRow->PadRowWithEmptyCells(iTotalColumns);
pTableRow = (CEditTableRowElement*)pTableRow->GetNextSibling();
}
}
// Set flag to autoinsert space into new cells according to pref if( iStartColumn > 0 )
{
// Set flag to autoinsert space into
// the blank new cells we will insert here
pBuffer->SetFillNewCellWithSpace(); pBuffer->SetFillNewCellWithSpace();
//Simplest way to handle this is to add blank cells to the source data
CEditTableRowElement* pSourceRow = pSourceTable->GetFirstRow(); //Simplest way to handle offset insert column
// is to add blank cells to the source data
pSourceRow = pSourceTable->GetFirstRow();
while(pSourceRow) while(pSourceRow)
{ {
for( intn i = 0; i < iStartColumn; i++ ) for( intn i = 0; i < iStartColumn; i++ )
@ -2777,14 +2862,20 @@ void CEditTableElement::InsertRows(int32 Y, int32 iNewY, intn number,
CEditTableCellElement *pNewCell = new CEditTableCellElement(); CEditTableCellElement *pNewCell = new CEditTableCellElement();
if( pNewCell ) if( pNewCell )
{ {
// Fake the X value so CountColumns will work
pNewCell->SetX(iStartColumn - i);
pNewCell->InsertAsFirstChild(pSourceRow); pNewCell->InsertAsFirstChild(pSourceRow);
pNewCell->FinishedLoad(pBuffer); pNewCell->FinishedLoad(pBuffer);
} }
} }
pSourceRow = (CEditTableRowElement*)pSourceRow->GetNextSibling(); pSourceRow = (CEditTableRowElement*)pSourceRow->GetNextSibling();
} }
// Clear this so we don't touch empty cells
// from the paste source
pBuffer->ClearFillNewCellWithSpace(); pBuffer->ClearFillNewCellWithSpace();
} }
iSourceColumns += iStartColumn;
}
CEditTableCellElement *pCell = GetFirstCellInRow(Y, FALSE ); CEditTableCellElement *pCell = GetFirstCellInRow(Y, FALSE );
while( pCell ) while( pCell )
@ -2819,32 +2910,43 @@ void CEditTableElement::InsertRows(int32 Y, int32 iNewY, intn number,
pCell = GetNextCellInRow(); pCell = GetNextCellInRow();
} }
XP_ASSERT(iColumns == iCurrentColumns);
// Now insert the new rows (including iColumns new cells in each) // Now insert the new rows (including iColumns new cells in each)
for ( intn row = 0; row < number; row++ ) for ( intn row = 0; row < number; row++ )
{ {
CEditTableRowElement* pNewRow; CEditTableRowElement* pNewRow;
if ( pSourceTable ) { if ( pSourceTable )
{
pNewRow = pSourceTable->GetFirstRow(); pNewRow = pSourceTable->GetFirstRow();
pNewRow->Unlink(); pNewRow->Unlink();
} }
else { else
{
pNewRow = new CEditTableRowElement(iColumns); pNewRow = new CEditTableRowElement(iColumns);
if( !pNewRow ) if( !pNewRow )
break; break;
}
pBuffer->SetFillNewCellWithSpace();
// Set insert point to first cell in inserted rows // Set insert point to first cell in inserted rows
if( !pCellForInsertPoint ) if( !pCellForInsertPoint )
pCellForInsertPoint = pNewRow->GetFirstCell(); pCellForInsertPoint = pNewRow->GetFirstCell();
}
if( Y == iNewY ) if( Y == iNewY )
pNewRow->InsertBefore(pCurrentRow); pNewRow->InsertBefore(pCurrentRow);
else else
{
pNewRow->InsertAfter(pCurrentRow); pNewRow->InsertAfter(pCurrentRow);
pCurrentRow = pNewRow;
}
pNewRow->FinishedLoad(pBuffer); pNewRow->FinishedLoad(pBuffer);
} }
pBuffer->ClearFillNewCellWithSpace();
// if( (iSourceColumns < iTotalColumns) ||
// (iCurrentColumns < iTotalColumns) )
// Do this all the time?
NormalizeCellsPerRow();
if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL ) if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL )
*ppCellForInsertPoint = pCellForInsertPoint; *ppCellForInsertPoint = pCellForInsertPoint;
@ -2954,7 +3056,8 @@ void CEditTableElement::InsertColumns(int32 X, int32 iNewX, intn number,
if( pNextRow ) if( pNextRow )
{ {
// Fill in rest of row with empty cells // Fill in rest of row with empty cells
pNextRow->PadRowWithEmptyCells(iTotalColumns); // DOESN'T WORK WITH ROW NOT ALREADY LAYED OUT
//pNextRow->PadRowWithEmptyCells(iTotalColumns);
// Be sure any empty cells have required empty text elements // Be sure any empty cells have required empty text elements
pNextRow->FinishedLoad(pBuffer); pNextRow->FinishedLoad(pBuffer);
} }
@ -2963,6 +3066,7 @@ void CEditTableElement::InsertColumns(int32 X, int32 iNewX, intn number,
} }
} }
} }
NormalizeCellsPerRow();
INSERT_COLUMNS_END: INSERT_COLUMNS_END:
if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL ) if( ppCellForInsertPoint && *ppCellForInsertPoint == NULL )
@ -3072,7 +3176,6 @@ XP_Bool CEditTableElement::ReplaceSpecialCells(CEditTableElement *pSourceTable,
pSourceCell->Unlink(); pSourceCell->Unlink();
pSourceCell->InsertAfter(pReplaceCell); pSourceCell->InsertAfter(pReplaceCell);
// then delete the replace cell // then delete the replace cell
pReplaceCell->Unlink();
delete pReplaceCell; delete pReplaceCell;
// Use size data from the cell we replaced // Use size data from the cell we replaced
@ -4525,6 +4628,8 @@ intn CEditTableRowElement::AppendRow( CEditTableRowElement *pAppendRow, XP_Bool
return iColumnsAppended; return iColumnsAppended;
} }
// This only works for table rows already layed out
// (has gone through CEditBuffer::FixupTableData() )
void CEditTableRowElement::PadRowWithEmptyCells( intn iColumns ) void CEditTableRowElement::PadRowWithEmptyCells( intn iColumns )
{ {
CEditTableElement *pTable = (CEditTableElement*)GetParent(); CEditTableElement *pTable = (CEditTableElement*)GetParent();
@ -5057,6 +5162,16 @@ CEditTableCellElement::CEditTableCellElement(IStreamIn *pStreamIn, CEditBuffer *
PA_FreeTag( pTag ); PA_FreeTag( pTag );
} }
// We should never call Unlink for a table cell we are about
// to delete - it will get unlinked automatically.
// But in case someone does, we must unselect the cell here
// else it will fail to find the EditBuffer in the destructor
void CEditTableCellElement::Unlink(){
if( IsSelected() && GetEditBuffer() )
GetEditBuffer()->SelectCell(FALSE, NULL, this);
CEditElement::Unlink();
}
CEditTableCellElement::~CEditTableCellElement(){ CEditTableCellElement::~CEditTableCellElement(){
// Be sure any cell deleted is not selected // Be sure any cell deleted is not selected
if( IsSelected() && GetEditBuffer() ) if( IsSelected() && GetEditBuffer() )
@ -6092,7 +6207,12 @@ void CEditTableCellElement::SwitchLinkage(CEditTableRowElement *pParentRow)
// Switches back to saved parent and next pointers // Switches back to saved parent and next pointers
void CEditTableCellElement::RestoreLinkage() void CEditTableCellElement::RestoreLinkage()
{ {
Unlink(); // Important! Can't call our CEditTableCellElement::Unlink()
// because it will remove cell from selection list
// and the main purpose for using this is to
// restore selected cells temporarily moved
// for copying to clipboard
CEditElement::Unlink();
SetParent(m_pSaveParent); SetParent(m_pSaveParent);
SetNextSibling(m_pSaveNext); SetNextSibling(m_pSaveNext);
CEditElement *pParent = GetParent(); CEditElement *pParent = GetParent();

Просмотреть файл

@ -773,9 +773,9 @@ lo_DisplayElement(MWContext *context, LO_Element *tptr,
if (((LO_CellStruct*)tptr)->cell_inflow_layer) if (((LO_CellStruct*)tptr)->cell_inflow_layer)
break; break;
lo_DisplayCell(context, (LO_CellStruct *)tptr);
lo_DisplayCellContents(context, (LO_CellStruct *)tptr, lo_DisplayCellContents(context, (LO_CellStruct *)tptr,
base_x, base_y, x, y, width, height); base_x, base_y, x, y, width, height);
lo_DisplayCell(context, (LO_CellStruct *)tptr);
break; break;
case LO_SUBDOC: case LO_SUBDOC: