Commits

Anonymous committed 1bea201

calcdatatables: expand DataTable data area without inserting cells if possible

* If expansion area is not empty or total row under data area, insert cells
with shift down.
* If expansion area is empty and no total row under data area, copy only
attributes instead of inserting a cell area.
+ Use ScDocument::UpdateGrow() in this case.
+ Added ScDBData::UpdateGrow() and ScDataTableFormat::UpdateGrow()
+ Undo/Redo
/ Additional benefit of not shifting cells is that less references have to
be updated if referenced data is present further down. Also less data to
be remembered in Undo.

  • Participants
  • Parent commits 070e6d7

Comments (0)

Files changed (22)

File sc/inc/attarray.hxx

 						bool bSafe, INT16 nStripFlags, bool bStripAllDataTables,
                         ScDataTableMap* pDataTables );
 
+    void    CopyRow( SCROW nFromRow, SCROW nToStartRow, SCROW nToEndRow );
+
 	void	DeleteHardAttr( SCROW nStartRow, SCROW nEndRow );
 
 //UNUSED2008-05  void    ConvertFontsAfterLoad();     // old binary file format

File sc/inc/column.hxx

 								const ScPatternAttr& rPatAttr, BOOL bPutToPool = FALSE );
 	void		ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
 							const ScPatternAttr& rPattern, short nNewType );
+    void        CopyPatternRow( SCROW nFromRow, SCROW nToStartRow, SCROW nToEndRow );
 
 	void		ApplyStyle( SCROW nRow, const ScStyleSheet& rStyle );
 	void		ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, const ScStyleSheet& rStyle );

File sc/inc/datatablestyle.hxx

                                         { return mbHeaderRow ? maRange.aStart.Row() + 1 : maRange.aStart.Row(); }
         SCROW                       GetLastDataRow() const
                                         { return mbTotalRow ? maRange.aEnd.Row() - 1 : maRange.aEnd.Row(); }
-        bool                        IsExpandBottomRight( const ScRange& rRange, SCsCOL nDx, SCsROW nDy ) const;
+        bool                        IsExpandBottomRight( UpdateRefMode eUpdateRefMode,
+                                                         const ScRange& rRange, SCsCOL nDx, SCsROW nDy ) const;
 
         void                        InvalidateStyleSheetPointers();
         bool                        IsStyleSheetUsed( const ScStyleSheet& rStyle, bool bGatherAllStyles ) const;
                                                      UpdateRefMode eUpdateRefMode,
                                                      const ScRange& rRange,
                                                      SCsCOL nDx, SCsROW nDy, SCsTAB nDz );
+        void                        UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY );
         void                        UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos );
         void                        RenameCellStyle( const String& rOld, const String& rNew );
                                     /// Is maRange completely covered by rRange?
         /// @returns a vector of X/Y-grown ranges.
         ::std::vector<ScRange>      UpdateReference( UpdateRefMode eUpdateRefMode, const ScRange& rRange,
                                                      SCsCOL nDx, SCsROW nDy, SCsTAB nDz );
+        void                        UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY );
         void                        UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos );
         void                        RenameCellStyle( const String& rOld, const String& rNew );
         bool                        IsStyleSheetUsed( const ScStyleSheet& rStyle, bool bGatherAllStyles ) const;

File sc/inc/dbcolect.hxx

 								SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
 								SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
 								SCsCOL nDx, SCsROW nDy, SCsTAB nDz );
+    void    UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY );
 	void	UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos );
 
 	ScDBData* FindIndex(USHORT nIndex);

File sc/inc/document.hxx

 											SCCOL nEndCol, SCROW nEndRow, SCTAB nTab,
 											const ScPatternAttr& rAttr );
 	SC_DLLPUBLIC void			ApplyPatternIfNumberformatIncompatible(
-							const ScRange& rRange, const ScMarkData& rMark,
-							const ScPatternAttr& rPattern, short nNewType );
+                                    const ScRange& rRange, const ScMarkData& rMark,
+                                    const ScPatternAttr& rPattern, short nNewType );
+    void                        CopyPatternRow( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol,
+                                                SCROW nFromRow, SCROW nToStartRow, SCROW nToEndRow );
 
 	void			ApplyStyle( SCCOL nCol, SCROW nRow, SCTAB nTab,
 								const ScStyleSheet& rStyle);

File sc/inc/table.hxx

 	void		SetPattern( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr, BOOL bPutToPool = FALSE );
 	void		ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
 							const ScPatternAttr& rPattern, short nNewType );
+    void        CopyPatternRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nFromRow, SCROW nToStartRow, SCROW nToEndRow );
 
 	void		ApplyStyle( SCCOL nCol, SCROW nRow, const ScStyleSheet& rStyle );
 	void		ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, const ScStyleSheet& rStyle );

File sc/source/core/data/attarray.cxx

 }
 
 
+void ScAttrArray::CopyRow( SCROW nFromRow, SCROW nToStartRow, SCROW nToEndRow )
+{
+    SCROW nStartRow, nEndRow;
+	const ScPatternAttr* pPattern = GetPatternRange( nStartRow, nEndRow, nFromRow);
+    if (!pPattern || nToEndRow <= nEndRow)
+        return;
+    SetPatternArea( nToStartRow, nToEndRow, pPattern);
+}
+
+
 SCsROW ScAttrArray::SearchStyle( SCsROW nRow, const ScStyleSheet* pSearchStyle,
 									BOOL bUp, ScMarkArray* pMarkArray )
 {

File sc/source/core/data/cell2.cxx

 #include "editutil.hxx"
 #include "chgtrack.hxx"
 #include "externalrefmgr.hxx"
+#include "dbcolect.hxx"
 
 using namespace formula;
 
                 bRefChanged = TRUE;
             }
         }
+        else    // DB area, ScDocument::UpdateGrow() now also calls ScDBCollection::UpdateGrow()
+        {
+            const ScDBData* pDBData = pDocument->GetDBCollection()->FindIndex( t->GetIndex());
+            if (pDBData && pDBData->IsModified())
+                bRefChanged = TRUE;
+        }
     }
 
     if (pShared)            // Shared Formula gegen echte Formel austauschen

File sc/source/core/data/column.cxx

 }
 
 
+void ScColumn::CopyPatternRow( SCROW nFromRow, SCROW nToStartRow, SCROW nToEndRow )
+{
+    pAttrArray->CopyRow( nFromRow, nToStartRow, nToEndRow);
+}
+
+
 void ScColumn::ApplyAttr( SCROW nRow, const SfxPoolItem& rAttr )
 {
 	//	um nur ein neues SetItem zu erzeugen, brauchen wir keinen SfxItemPoolCache.

File sc/source/core/data/datatablestyle.cxx

 }
 
 
-bool ScDataTableFormat::IsExpandBottomRight( const ScRange& rRange, SCsCOL nDx, SCsROW nDy ) const
+bool ScDataTableFormat::IsExpandBottomRight( UpdateRefMode eUpdateRefMode, 
+        const ScRange& rRange, SCsCOL nDx, SCsROW nDy ) const
 {
-    return ((nDx > 0 && rRange.aStart.Col() == maRange.aEnd.Col() + 1 && 
+    return (eUpdateRefMode == URM_INSDEL &&
+            (nDx > 0 && rRange.aStart.Col() == maRange.aEnd.Col() + 1 && 
                 rRange.aStart.Row() <= maRange.aStart.Row() && 
                 maRange.aEnd.Row() <= rRange.aEnd.Row()) ||
             (nDy > 0 && rRange.aStart.Row() == GetLastDataRow() + 1 && 
         UpdateRefMode eUpdateRefMode, const ScRange& rRange,
         SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
 {
-    bool bExpandBottomRight = IsExpandBottomRight( rRange, nDx, nDy);
+    bool bExpandBottomRight = IsExpandBottomRight( eUpdateRefMode, rRange, nDx, nDy);
     if (bExpandBottomRight)
     {
         ScRange aOldRange( maRange);
 }
 
 
+void ScDataTableFormat::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+    ScRefUpdate::UpdateGrow( rArea, nGrowX, nGrowY, maRange);
+}
+
+
 void ScDataTableFormat::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos )
 {
     DBG_ASSERT( maRange.aStart.Tab() == maRange.aEnd.Tab(), "ScDataTableFormat::UpdateMoveTab: range not on one sheet");
 }
 
 
+void ScDataTableFormatList::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+	USHORT nCount = Count();
+	for (USHORT i=0; i < nCount; i++)
+		(*this)[i]->UpdateGrow( rArea, nGrowX, nGrowY);
+}
+
+
 void ScDataTableFormatList::RenameCellStyle( const String& rOld, const String& rNew )
 {
     // ScDocShell::NotifyStyle() fires even upon creation of a style for "" -> 

File sc/source/core/data/documen3.cxx

             // use the DataTables' IsExpandBottomRight(). To call
             // ScDocument::UpdateGrow() for specific ranges do this after 
             // regular UpdateReference() to not update those ranges twice if 
-            // ExpandRefs is active.
+            // ExpandRefs is active. Note also that the ScDBData and 
+            // ScRangeName bModified flags are reset if an area didn't grow.
             ::std::vector<ScRange> aGrownRanges( 
                     pDataTableFormatList->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz));
             for (::std::vector<ScRange>::const_iterator it( aGrownRanges.begin());
                     it != aGrownRanges.end(); ++it)
-                /* FIXME: pass pUndoDoc? */
                 UpdateGrow( *it, nDx, nDy);
         }
 
 
 void ScDocument::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
 {
-	//!	pDBCollection
-	//!	pPivotCollection
+    //!	pPivotCollection
 	//!	UpdateChartRef
 
+    if (pDBCollection)
+        pDBCollection->UpdateGrow( rArea, nGrowX, nGrowY);
+    // If updating the DB areas is removed, ScFormulaCell::UpdateGrow() should 
+    // be adapted to not react on ScDBData::IsModified()
+
+    if (pDataTableFormatList)
+        pDataTableFormatList->UpdateGrow( rArea, nGrowX, nGrowY);
+
 	pRangeName->UpdateGrow( rArea, nGrowX, nGrowY );
 
 	for (SCTAB i=0; i<=MAXTAB && pTab[i]; i++)

File sc/source/core/data/document.cxx

 }
 
 
+void ScDocument::CopyPatternRow( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol, 
+        SCROW nFromRow, SCROW nToStartRow, SCROW nToEndRow )
+{
+	if (ValidTab(nTab) && (pTab[nTab]))
+        pTab[nTab]->CopyPatternRow( nStartCol, nEndCol, nFromRow, nToStartRow, nToEndRow);
+}
+
+
 void ScDocument::ApplyStyle( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScStyleSheet& rStyle)
 {
 	if (VALIDTAB(nTab))
         {
             aCopyMark.ResetMark();
             aDataTableRange = pDataTable->GetTableRange();
-            if (aDataTableRange.aEnd.Row() < MAXROW)
-                // Do not wrap to top row, position under last data row 
-                // instead.
-                aDataTableRange.aEnd.SetRow( pDataTable->GetLastDataRow() + 1);
+            SCROW nLastDataRow = pDataTable->GetLastDataRow();
+            if (rRow == nLastDataRow && aDataTableRange.aEnd.Row() < MAXROW)
+                // Do not wrap to top row, position under last data row instead.
+                aDataTableRange.aEnd.SetRow( nLastDataRow + 1);
             aCopyMark.SetMarkArea( aDataTableRange);
             bMarked = TRUE;
         }
     if (ValidTab(nTab) && pTab[nTab])
         pTab[nTab]->GetNextPos( rCol, rRow, nMovX, nMovY, bMarked, bUnprotected, aCopyMark );
 
-    return (pDataTable && rRow == aDataTableRange.aEnd.Row()) ? pDataTable : NULL;
+    return (pDataTable && rRow == aDataTableRange.aEnd.Row() && rCol == aDataTableRange.aStart.Col()) ?
+        pDataTable : NULL;
 }
 
 //

File sc/source/core/data/table2.cxx

 }
 
 
+void ScTable::CopyPatternRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nFromRow, SCROW nToStartRow, SCROW nToEndRow )
+{
+    if (ValidColRow( nStartCol, nFromRow) && ValidCol( nEndCol) && ValidRow( nToStartRow) && ValidRow( nToEndRow))
+        for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
+            aCol[nCol].CopyPatternRow( nFromRow, nToStartRow, nToEndRow);
+}
+
+
 void ScTable::ApplyAttr( SCCOL nCol, SCROW nRow, const SfxPoolItem& rAttr )
 {
 	if (ValidColRow(nCol,nRow))

File sc/source/core/tool/dbcolect.cxx

 								SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
 								SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
 {
-	for (USHORT i=0; i<nCount; i++)
-	{
-		SCCOL theCol1;
-		SCROW theRow1;
-		SCTAB theTab1;
-		SCCOL theCol2;
-		SCROW theRow2;
-		SCTAB theTab2;
+    bool bTestGrow = (eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0));
+    for (USHORT i=0; i<nCount; i++)
+    {
+        SCCOL theCol1, theCol2;
+        SCROW theRow1, theRow2;
+        SCTAB theTab1, theTab2;
         ScDBData* pData = static_cast<ScDBData*>(pItems[i]);
-		pData->GetArea( theTab1, theCol1, theRow1, theCol2, theRow2 );
-		theTab2 = theTab1;
+        pData->GetArea( theTab1, theCol1, theRow1, theCol2, theRow2 );
+        theTab2 = theTab1;
 
         bool bExpandBottomRight = false;
-        if (nDx > 0 || nDy > 0)
+        if (bTestGrow)
         {
             sal_uInt32 nDataTable = pData->GetDataTable();
             if (nDataTable)
             {
                 const ScDataTableFormat* pDTab = pDoc->GetDataTableFormat( nDataTable);
                 if (pDTab)
-                    bExpandBottomRight = pDTab->IsExpandBottomRight(
+                    bExpandBottomRight = pDTab->IsExpandBottomRight( eUpdateRefMode,
                             ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2), nDx, nDy);
             }
         }
                     nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz, 
                     theCol1,theRow1,theTab1, theCol2,theRow2,theTab2) != UR_NOTHING;
 
-		if (bDoUpdate)
-			pData->MoveTo( theTab1, theCol1, theRow1, theCol2, theRow2 );
+        if (bDoUpdate)
+            pData->MoveTo( theTab1, theCol1, theRow1, theCol2, theRow2 );
 
-		ScRange aAdvSource;
-		if ( pData->GetAdvancedQuerySource(aAdvSource) )
-		{
-			aAdvSource.GetVars( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
-			if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
-										nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
-										theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ) )
-			{
-				aAdvSource.aStart.Set( theCol1,theRow1,theTab1 );
-				aAdvSource.aEnd.Set( theCol2,theRow2,theTab2 );
-				pData->SetAdvancedQuerySource( &aAdvSource );
+        ScRange aAdvSource;
+        if ( pData->GetAdvancedQuerySource(aAdvSource) )
+        {
+            aAdvSource.GetVars( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
+            if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
+                                        nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
+                                        theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ) )
+            {
+                aAdvSource.aStart.Set( theCol1,theRow1,theTab1 );
+                aAdvSource.aEnd.Set( theCol2,theRow2,theTab2 );
+                pData->SetAdvancedQuerySource( &aAdvSource );
 
-				bDoUpdate = TRUE;		// DBData is modified
-			}
-		}
+                bDoUpdate = TRUE;       // DBData is modified
+            }
+        }
 
-		pData->SetModified(bDoUpdate);
+        pData->SetModified(bDoUpdate);
 
-		//!		Testen, ob mitten aus dem Bereich geloescht/eingefuegt wurde !!!
-	}
+        //!     Testen, ob mitten aus dem Bereich geloescht/eingefuegt wurde !!!
+    }
+}
+
+
+void ScDBCollection::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+    for (USHORT i=0; i<nCount; i++)
+    {
+        SCCOL theCol1, theCol2;
+        SCROW theRow1, theRow2;
+        SCTAB theTab1, theTab2;
+        ScDBData* pData = static_cast<ScDBData*>(pItems[i]);
+        pData->GetArea( theTab1, theCol1, theRow1, theCol2, theRow2 );
+        theTab2 = theTab1;
+
+        ScRange aRange( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
+        bool bDoUpdate = ScRefUpdate::UpdateGrow( rArea, nGrowX, nGrowY, aRange) != UR_NOTHING;
+        if (bDoUpdate)
+        {
+            aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
+            pData->MoveTo( theTab1, theCol1, theRow1, theCol2, theRow2 );
+        }
+
+        // Growing query sources probably is not desirable.
+
+        pData->SetModified( bDoUpdate);
+
+    }
 }
 
 

File sc/source/core/tool/rangenam.cxx

 		}
 	}
 
-	bModified = bChanged;			// muss direkt hinterher ausgewertet werden
+    bModified = bChanged;       // must be evaluated immediately after
 }
 
 BOOL ScRangeData::operator== (const ScRangeData& rData) const		// fuer Undo

File sc/source/ui/docshell/docfunc.cxx

 #include "uiitems.hxx"
 #include "undoblk.hxx"
 #include "undocell.hxx"
+#include "undodat.hxx"
 #include "undodraw.hxx"
 #include "undotab.hxx"
 #include "waitoff.hxx"
 #include "tabprotection.hxx"
 #include "clipparam.hxx"
 #include "externalrefmgr.hxx"
+#include "datatablestyle.hxx"
 
 #include <memory>
 #include <basic/basmgr.hxx>
 
 //------------------------------------------------------------------------
 
-//	Zeile ueber dem Range painten (fuer Linien nach AdjustRowHeight)
-
-void lcl_PaintAbove( ScDocShell& rDocShell, const ScRange& rRange )
-{
-	SCROW nRow = rRange.aStart.Row();
-	if ( nRow > 0 )
-	{
-		SCTAB nTab = rRange.aStart.Tab();	//! alle?
-		--nRow;
-		rDocShell.PostPaint( ScRange(0,nRow,nTab, MAXCOL,nRow,nTab), PAINT_GRID );
-	}
-}
-
-//------------------------------------------------------------------------
-
 BOOL ScDocFunc::AdjustRowHeight( const ScRange& rRange, BOOL bPaint )
 {
 	ScDocument* pDoc = rDocShell.GetDocument();
 	if (!AdjustRowHeight( aExtendedRange ))
 		rDocShell.PostPaint( aExtendedRange, PAINT_GRID, nExtFlags );
 	else if (nExtFlags & SC_PF_LINES)
-		lcl_PaintAbove( rDocShell, aExtendedRange );	// fuer Linien ueber dem Bereich
+		rDocShell.PostPaintAbove( aExtendedRange );	// fuer Linien ueber dem Bereich
 
 //	rDocShell.UpdateOle(GetViewData());		//! an der View?
 	aModificator.SetDocumentModified();
 	if (!AdjustRowHeight( aMultiRange ))
 		rDocShell.PostPaint( aMultiRange, PAINT_GRID, nExtFlags );
 	else if (nExtFlags & SC_PF_LINES)
-		lcl_PaintAbove( rDocShell, aMultiRange );	// fuer Linien ueber dem Bereich
+		rDocShell.PostPaintAbove( aMultiRange );	// fuer Linien ueber dem Bereich
 
 	aModificator.SetDocumentModified();
 
 }
 
 
+bool ScDocFunc::CopyPatternRow( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol,
+        SCROW nFromRow, SCROW nToStartRow, SCROW nToEndRow,
+        bool bRecord, ScDocument* pUndoDocPassed )
+{
+    ScDocument* pDoc = rDocShell.GetDocument();
+    if (bRecord && !pDoc->IsUndoEnabled())
+        bRecord = false;
+
+    const USHORT nAttrFlags = IDF_ATTRIB | IDF_NODTFORMAT;
+    ScDocument* pUndoDoc = NULL;
+    if (bRecord)
+    {
+        if (pUndoDocPassed)
+            pUndoDoc = pUndoDocPassed;
+        else
+        {
+            pUndoDoc = new ScDocument( SCDOCMODE_UNDO);
+            pUndoDoc->InitUndo( pDoc, nTab, nTab);
+        }
+        pDoc->CopyToDocument( nStartCol, nToStartRow, nTab, nEndCol, nToEndRow,
+                nTab, nAttrFlags, false, pUndoDoc);
+        if (pUndoDoc && !pUndoDocPassed)
+        {
+            DBG_ERRORFILE( "ScDocFunc::CopyPatternRow: standalone UndoAction not implemented");
+            delete pUndoDoc;
+            /* TODO: implement undo action */
+#if 0
+            rDocShell.GetUndoManager()->AddUndoAction(
+                    new ScUndoCopyPatternRow( &rDocShell, pUndoDoc));
+#endif
+        }
+    }
+
+    ScDocShellModificator aModificator( rDocShell );
+
+    ScRange aAttrRange( nStartCol, nToStartRow, nTab, nEndCol, nToEndRow, nTab);
+    USHORT nExtFlags = 0;
+    rDocShell.UpdatePaintExt( nExtFlags, aAttrRange);
+    pDoc->DeleteAreaTab( aAttrRange, nAttrFlags);
+    pDoc->CopyPatternRow( nTab, nStartCol, nEndCol, nFromRow, nToStartRow, nToEndRow);
+    rDocShell.UpdatePaintExt( nExtFlags, aAttrRange);
+	if (!AdjustRowHeight( aAttrRange))
+		rDocShell.PostPaint( aAttrRange, PAINT_GRID, nExtFlags);
+	else if (nExtFlags & SC_PF_LINES)
+		rDocShell.PostPaintAbove( aAttrRange);     // for lines above
+
+    aModificator.SetDocumentModified();
+    return true;
+}
+
+
+bool ScDocFunc::GrowDataTable( const ScDataTableFormat& rDataTable, SCROW nGrowY, bool bRecord )
+{
+    DBG_ASSERT( nGrowY >= 0, "ScDocFunc::GrowDataTable: shrink not implemented");
+    if (nGrowY <= 0)
+        return false;
+
+    ScDocument* pDoc = rDocShell.GetDocument();
+    if (bRecord && !pDoc->IsUndoEnabled())
+        bRecord = false;
+
+    // Do not use the reference, the DataTable range is altered in UpdateGrow()!
+    ScRange aRange( rDataTable.GetTableRange());
+    SCTAB nTab = aRange.aStart.Tab();
+
+    ScDocument* pUndoDoc = NULL;
+    if (bRecord)
+    {
+        pUndoDoc = new ScDocument( SCDOCMODE_UNDO);
+        pUndoDoc->InitUndo( pDoc, nTab, nTab);
+    }
+
+    ScDocShellModificator aModificator( rDocShell );
+    SCROW nEndRow = aRange.aEnd.Row();
+    CopyPatternRow( nTab, aRange.aStart.Col(), aRange.aEnd.Col(), nEndRow,
+            nEndRow + 1, nEndRow + nGrowY, bRecord, pUndoDoc);
+    pDoc->UpdateGrow( aRange, 0, nGrowY);
+    aModificator.SetDocumentModified();
+
+    if (pUndoDoc)
+        rDocShell.GetUndoManager()->AddUndoAction( new ScUndoGrowDataTable( 
+                    &rDocShell, aRange, nGrowY, pUndoDoc));
+
+    SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
+    SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_AREAS_CHANGED ) );
+    return true;
+}
+
+
 BOOL ScDocFunc::ApplyStyle( const ScMarkData& rMark, const String& rStyleName,
 									BOOL bRecord, BOOL bApi )
 {
 	if (!AdjustRowHeight( aMultiRange ))
 		rDocShell.PostPaint( aMultiRange, PAINT_GRID, nExtFlags );
 	else if (nExtFlags & SC_PF_LINES)
-		lcl_PaintAbove( rDocShell, aMultiRange );	// fuer Linien ueber dem Bereich
+		rDocShell.PostPaintAbove( aMultiRange );	// fuer Linien ueber dem Bereich
 
 	aModificator.SetDocumentModified();
 
 	        {
 		        //	paint only what is not done by AdjustRowHeight
 		        if (nExtFlags & SC_PF_LINES)
-			        lcl_PaintAbove( rDocShell, ScRange( nPaintStartX, nPaintStartY, i, nPaintEndX, nPaintEndY, i+nScenarioCount) );
+			        rDocShell.PostPaintAbove( ScRange( nPaintStartX, nPaintStartY, i, nPaintEndX, nPaintEndY, i+nScenarioCount) );
 		        if (nPaintFlags & PAINT_TOP)
 			        rDocShell.PostPaint( nPaintStartX, nPaintStartY, i, nPaintEndX, nPaintEndY, i+nScenarioCount, PAINT_TOP );
 	        }

File sc/source/ui/docshell/docsh3.cxx

 	PostPaint( 0,0,0, MAXCOL,MAXROW,MAXTAB, PAINT_EXTRAS );
 }
 
+void ScDocShell::PostPaintAbove( const ScRange& rRange )
+{
+	SCROW nRow = rRange.aStart.Row();
+	if ( nRow > 0 )
+	{
+		SCTAB nTab = rRange.aStart.Tab();	//! all?
+		--nRow;
+		PostPaint( ScRange(0,nRow,nTab, MAXCOL,nRow,nTab), PAINT_GRID );
+	}
+}
+
 void ScDocShell::UpdatePaintExt( USHORT& rExtFlags, const ScRange& rRange )
 {
 	if ( ( rExtFlags & SC_PF_LINES ) == 0 && aDocument.HasAttrib( rRange, HASATTR_PAINTEXT ) )

File sc/source/ui/inc/docfunc.hxx

 class ScTokenArray;
 struct ScTabOpParam;
 class ScTableProtection;
+class ScDataTableFormat;
 
 // ---------------------------------------------------------------------------
 
 	BOOL			ApplyStyle( const ScMarkData& rMark, const String& rStyleName,
 									BOOL bRecord, BOOL bApi );
 
+    bool            CopyPatternRow( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol,
+                                    SCROW nFromRow, SCROW nToStartRow, SCROW nToEndRow,
+                                    bool bRecord, ScDocument* pUndoDoc = NULL );
+
+    /** Grow the DataTable range by appending nGrowY rows.
+        Does NOT insert cells, only attributes are copied. Use only if the 
+        target range is empty. The DataTable should not contain a total row or 
+        the data in the total row needs to be moved separately.
+     */
+    bool            GrowDataTable( const ScDataTableFormat& rDataTable, SCROW nGrowY, bool bRecord );
+
 	BOOL			InsertCells( const ScRange& rRange,const ScMarkData* pTabMark,
                                  InsCellCmd eCmd, BOOL bRecord, BOOL bApi,
 									BOOL bPartOfPaste = FALSE );

File sc/source/ui/inc/docsh.hxx

 	void            PostPaintCell( const ScAddress& rPos );
 	void            PostPaintGridAll();
 	void            PostPaintExtras();
+					/** Paint row above range, for lines after AdjustRowHeight 
+						if (nExtFlags & SC_PF_LINES) */
+	void 			PostPaintAbove( const ScRange& rRange );
 
 	void            PostDataChanged();
 

File sc/source/ui/inc/undodat.hxx

 };
 
 
+class ScUndoGrowDataTable: public ScSimpleUndo
+{
+public:
+                    TYPEINFO();
+                    ScUndoGrowDataTable( ScDocShell* pNewDocShell,
+                            const ScRange& rOldRange, SCROW nGrowY,
+                            ScDocument* pUndoDoc );
+    virtual         ~ScUndoGrowDataTable();
+
+    virtual void    Undo();
+    virtual void    Redo();
+    virtual void    Repeat(SfxRepeatTarget& rTarget);
+    virtual BOOL    CanRepeat(SfxRepeatTarget& rTarget) const;
+
+    virtual String  GetComment() const;
+
+private:
+
+    void            DoChange( bool bUndo );
+
+    ScRange     maOldRange;
+    SCROW       mnGrowY;
+    ScDocument *mpUndoDoc;
+    ScDocument *mpRedoDoc;
+};
 
 
 #endif
-

File sc/source/ui/undo/undodat.cxx

 TYPEINIT1(ScUndoDataPilot,          ScSimpleUndo);
 TYPEINIT1(ScUndoConsolidate,        ScSimpleUndo);
 TYPEINIT1(ScUndoChartData,          ScSimpleUndo);
+TYPEINIT1(ScUndoGrowDataTable,      ScSimpleUndo);
 
 // -----------------------------------------------------------------------
 
 }
 
 
+// ===========================================================================
 
+ScUndoGrowDataTable::ScUndoGrowDataTable( ScDocShell* pNewDocShell,
+        const ScRange& rOldRange, SCROW nGrowY, ScDocument* pUndoDoc )
+    :
+    ScSimpleUndo( pNewDocShell ),
+    maOldRange( rOldRange ),
+    mnGrowY( nGrowY ),
+    mpUndoDoc( pUndoDoc ),
+    mpRedoDoc( NULL )
+{
+}
 
+__EXPORT ScUndoGrowDataTable::~ScUndoGrowDataTable()
+{
+    delete mpUndoDoc;
+    delete mpRedoDoc;
+}
 
+String __EXPORT ScUndoGrowDataTable::GetComment() const
+{   // "Expand data table"
+    //return ScGlobal::GetRscString( STR_UNDO_EXPAND_DATATABLE );
+    /* FIXME DataTable: add resource */
+    return String( RTL_CONSTASCII_USTRINGPARAM( "Expand data table"));
+}
 
+void ScUndoGrowDataTable::DoChange( bool bUndo )
+{
+    ScDocument* pDoc = pDocShell->GetDocument();
+    ScRange aAttrRange( maOldRange);
+    aAttrRange.aStart.SetRow( maOldRange.aEnd.Row() + 1);
+    aAttrRange.aEnd.SetRow( maOldRange.aEnd.Row() + mnGrowY);
+    const USHORT nAttrFlags = IDF_ATTRIB | IDF_NODTFORMAT;
+
+    if (bUndo && !mpRedoDoc)
+    {
+        mpRedoDoc = new ScDocument( SCDOCMODE_UNDO);
+        mpRedoDoc->InitUndo( pDoc, aAttrRange.aStart.Tab(), aAttrRange.aStart.Tab());
+        pDoc->CopyToDocument( aAttrRange, nAttrFlags, false, mpRedoDoc);
+    }
+
+    USHORT nExtFlags = 0;
+    pDocShell->UpdatePaintExt( nExtFlags, aAttrRange);
+    BOOL bOldAutoCalc = pDoc->GetAutoCalc();
+    pDoc->SetAutoCalc( FALSE );         // avoid multiple recalculations
+    if (bUndo)
+    {
+        pDoc->DeleteAreaTab( aAttrRange, nAttrFlags);
+        mpUndoDoc->CopyToDocument( aAttrRange, nAttrFlags, false, pDoc);
+        ScRange aRange( maOldRange);
+        aRange.aEnd.SetRow( maOldRange.aEnd.Row() + mnGrowY);
+        pDoc->UpdateGrow( aRange, 0, -mnGrowY);
+    }
+    else
+    {
+        DBG_ASSERT( mpRedoDoc, "ScUndoGrowDataTable::Redo: mpRedoDoc==NULL");
+        pDoc->DeleteAreaTab( aAttrRange, nAttrFlags);
+        mpRedoDoc->CopyToDocument( aAttrRange, nAttrFlags, false, pDoc);
+        pDoc->UpdateGrow( maOldRange, 0, mnGrowY);
+    }
+    pDoc->SetAutoCalc( bOldAutoCalc );
+
+    pDocShell->UpdatePaintExt( nExtFlags, aAttrRange);
+    if (!pDocShell->AdjustRowHeight( aAttrRange.aStart.Row(), 
+                aAttrRange.aEnd.Row(), aAttrRange.aStart.Tab()))
+        pDocShell->PostPaint( aAttrRange, PAINT_GRID, nExtFlags);
+    else if (nExtFlags & SC_PF_LINES)
+        pDocShell->PostPaintAbove( aAttrRange);     // for lines above
+
+    SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
+    SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_AREAS_CHANGED ) );
+}
+
+void __EXPORT ScUndoGrowDataTable::Undo()
+{
+    BeginUndo();
+    DoChange( true);
+    EndUndo();
+}
+
+void __EXPORT ScUndoGrowDataTable::Redo()
+{
+    BeginRedo();
+    DoChange( false);
+    EndRedo();
+}
+
+void __EXPORT ScUndoGrowDataTable::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+BOOL __EXPORT ScUndoGrowDataTable::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+    return FALSE;
+}

File sc/source/ui/view/tabview3.cxx

 #include "client.hxx"
 #include "tabprotection.hxx"
 #include "datatablestyle.hxx"
+#include "docfunc.hxx"
 
 #include <com/sun/star/chart2/data/HighlightedRange.hpp>
 
 	const ScDataTableFormat* pDTab = pDoc->GetNextPos( nNewX,nNewY, nTab, nMove,0, bMarked,TRUE, rMark, true );
     if (pDTab)
     {
-        // Pointer is returned only if the new position is on the row following 
-        // the last data row, no need to check again.
-        ScMarkData aOldMark( rMark);
+        // Pointer is returned only if the new position is in the first column 
+        // of the row following the last data row, no need to check again.
         ScRange aRange( pDTab->GetTableRange());
-        aRange.aStart.SetRow( nNewY);
-        aRange.aEnd.SetRow( nNewY);
-        rMark.ResetMark();
-        rMark.SetMarkArea( aRange);
-/* TODO DataTable: the non-inserting approach needs quite some work */
-#if 0
+        SCROW nOldEndRow = aRange.aEnd.Row();
         // Insert new DataTable cells only if the range is not empty or is a 
         // total row, else copy only attributes to be less invasive on existing 
-        // data.
-        if (nNewY <= pDTab->GetTableRange().aEnd.Row() ||
+        // data below the DataTable.
+        /* TODO: This could be further enhanced to also "move" a total row, 
+         * adapting formulas to include the new row. */
+        if (nNewY <= nOldEndRow ||
                 !pDoc->IsBlockEmpty( aRange.aStart.Tab(), aRange.aStart.Col(), nNewY, aRange.aEnd.Col(), nNewY))
+        {
+            ScMarkData aOldMark( rMark);
+            rMark.ResetMark();
+            aRange.aStart.SetRow( nNewY);
+            aRange.aEnd.SetRow( nNewY);
+            rMark.SetMarkArea( aRange);
             aViewData.GetViewShell()->InsertCells( INS_CELLSDOWN);
+            rMark = aOldMark;
+        }
         else
-        {
-            /* FIXME DataTable: copy attributes (may include formats, 
-             * conditional, ...) from last data row instead of setting only 
-             * DataTable */
-            SfxUInt32Item aItem( ATTR_DATATABLE, pDTab->GetKey());
-            aViewData.GetViewShell()->ApplyAttr( aItem);
-            aRange = pDTab->GetTableRange();
-            aRange.aEnd.SetRow( nNewY);
-            /* FIXME DataTable: obtain non-const pointer from document */
-            const_cast<ScDataTableFormat*>(pDTab)->SetTableRange( aRange);
-            /* FIXME DataTable: call UpdateGrow() for references */
-            /* FIXME DataTable: new range and grow need Undo */
-        }
-#else
-        aViewData.GetViewShell()->InsertCells( INS_CELLSDOWN);
-#endif
-        rMark = aOldMark;
+            aViewData.GetDocShell()->GetDocFunc().GrowDataTable( *pDTab, 1, true);
     }
 
 	SCCOL nTabCol = aViewData.GetTabStartCol();