[Libreoffice-commits] core.git: 3 commits - officecfg/registry sc/inc sc/Library_sc.mk sc/qa sc/sdi sc/source

Kohei Yoshida kohei.yoshida at collabora.com
Thu Oct 2 21:00:36 PDT 2014


 officecfg/registry/data/org/openoffice/Office/UI/CalcCommands.xcu |    8 
 sc/Library_sc.mk                                                  |    1 
 sc/inc/cellvalues.hxx                                             |   60 ++
 sc/inc/column.hxx                                                 |    7 
 sc/inc/document.hxx                                               |    5 
 sc/inc/formulacell.hxx                                            |    1 
 sc/inc/globstr.hrc                                                |    3 
 sc/inc/sc.hrc                                                     |    5 
 sc/inc/table.hxx                                                  |    8 
 sc/qa/unit/ucalc.cxx                                              |  234 ++++++++++
 sc/qa/unit/ucalc.hxx                                              |    5 
 sc/sdi/cellsh.sdi                                                 |    2 
 sc/sdi/scalc.sdi                                                  |   27 +
 sc/source/core/data/cellvalues.cxx                                |  202 ++++++++
 sc/source/core/data/column4.cxx                                   |  149 ++++++
 sc/source/core/data/document10.cxx                                |   41 +
 sc/source/core/data/formulacell.cxx                               |    9 
 sc/source/core/data/table7.cxx                                    |   21 
 sc/source/ui/docshell/docfunc.cxx                                 |   34 +
 sc/source/ui/inc/docfunc.hxx                                      |    2 
 sc/source/ui/inc/undoconvert.hxx                                  |   37 +
 sc/source/ui/inc/viewfunc.hxx                                     |    1 
 sc/source/ui/src/globstr.src                                      |    5 
 sc/source/ui/undo/undoconvert.cxx                                 |   51 ++
 sc/source/ui/view/cellsh1.cxx                                     |    5 
 sc/source/ui/view/viewfun2.cxx                                    |   11 
 26 files changed, 931 insertions(+), 3 deletions(-)

New commits:
commit 8cd605901532c9408422059bb94e35f92088b18e
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Thu Oct 2 21:14:01 2014 -0400

    Another test case for this feature.
    
    Change-Id: Ia406baec2867605bf515dac0ba090500e2bbd1ce

diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index c7c81b8..415cca5 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -6124,6 +6124,81 @@ void Test::testFormulaToValue()
     m_pDoc->DeleteTab(0);
 }
 
+void Test::testFormulaToValue2()
+{
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
+    FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
+
+    m_pDoc->InsertTab(0, "Test");
+
+    const char* aData[][2] = {
+        { "=1", "=ISFORMULA(RC[-1])" },
+        { "=2", "=ISFORMULA(RC[-1])" },
+        {  "3", "=ISFORMULA(RC[-1])" },
+        { "=4", "=ISFORMULA(RC[-1])" },
+        { "=5", "=ISFORMULA(RC[-1])" },
+    };
+
+    // Insert data into B2:C6.
+    ScAddress aPos(1,1,0); // B2
+    ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
+    CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
+
+    {
+        // Expected output table content.  0 = empty cell
+        const char* aOutputCheck[][2] = {
+            { "1", "TRUE" },
+            { "2", "TRUE" },
+            { "3", "FALSE" },
+            { "4", "TRUE" },
+            { "5", "TRUE" },
+        };
+
+        bool bSuccess = checkOutput<2>(m_pDoc, aDataRange, aOutputCheck, "Initial value");
+        CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
+    }
+
+    // Convert B3:B5 to a value.
+    ScDocFunc& rFunc = getDocShell().GetDocFunc();
+    ScRange aConvRange(1,2,0,1,4,0); // B3:B5
+    rFunc.ConvertFormulaToValue(aConvRange, true, false);
+
+    {
+        // Expected output table content.  0 = empty cell
+        const char* aOutputCheck[][2] = {
+            { "1", "TRUE" },
+            { "2", "FALSE" },
+            { "3", "FALSE" },
+            { "4", "FALSE" },
+            { "5", "TRUE" },
+        };
+
+        bool bSuccess = checkOutput<2>(m_pDoc, aDataRange, aOutputCheck, "Initial value");
+        CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
+    }
+
+    // Undo and check.
+    SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
+    CPPUNIT_ASSERT(pUndoMgr);
+    pUndoMgr->Undo();
+
+    {
+        // Expected output table content.  0 = empty cell
+        const char* aOutputCheck[][2] = {
+            { "1", "TRUE" },
+            { "2", "TRUE" },
+            { "3", "FALSE" },
+            { "4", "TRUE" },
+            { "5", "TRUE" },
+        };
+
+        bool bSuccess = checkOutput<2>(m_pDoc, aDataRange, aOutputCheck, "Initial value");
+        CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
+    }
+
+    m_pDoc->DeleteTab(0);
+}
+
 void Test::testMixData()
 {
     m_pDoc->InsertTab(0, "Test");
diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx
index e59378c..259d0a2 100644
--- a/sc/qa/unit/ucalc.hxx
+++ b/sc/qa/unit/ucalc.hxx
@@ -388,6 +388,7 @@ public:
     void testTransliterateText();
 
     void testFormulaToValue();
+    void testFormulaToValue2();
 
     void testColumnFindEditCells();
 
@@ -572,6 +573,7 @@ public:
     CPPUNIT_TEST(testDeleteContents);
     CPPUNIT_TEST(testTransliterateText);
     CPPUNIT_TEST(testFormulaToValue);
+    CPPUNIT_TEST(testFormulaToValue2);
     CPPUNIT_TEST(testColumnFindEditCells);
     CPPUNIT_TEST_SUITE_END();
 
commit 1b1e50ccc02b833f2ca8ef85a47dd2e8c28a479b
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Fri Sep 26 21:26:33 2014 -0400

    Write the first test for this formula-to-value feature.
    
    Change-Id: I545328a1c126b3bfeee8c1f662c5eb9a471d33ca

diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index efcfc8e..c7c81b8 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -5965,6 +5965,165 @@ void Test::testTransliterateText()
     m_pDoc->DeleteTab(0);
 }
 
+void Test::testFormulaToValue()
+{
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
+    FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
+
+    m_pDoc->InsertTab(0, "Test");
+
+    const char* aData[][3] = {
+        { "=1", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
+        { "=2", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
+        { "=3", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
+        { "=4", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
+        { "=5", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
+        { "=6", "=RC[-1]*2", "=ISFORMULA(RC[-1])" },
+    };
+
+    ScAddress aPos(1,2,0); // B3
+    ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
+    CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
+
+    {
+        // Expected output table content.  0 = empty cell
+        const char* aOutputCheck[][3] = {
+            { "1",  "2", "TRUE" },
+            { "2",  "4", "TRUE" },
+            { "3",  "6", "TRUE" },
+            { "4",  "8", "TRUE" },
+            { "5", "10", "TRUE" },
+            { "6", "12", "TRUE" },
+        };
+
+        bool bSuccess = checkOutput<3>(m_pDoc, aDataRange, aOutputCheck, "Initial value");
+        CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
+    }
+
+    // Convert B5:C6 to static values, and check the result.
+    ScDocFunc& rFunc = getDocShell().GetDocFunc();
+    ScRange aConvRange(1,4,0,2,5,0); // B5:C6
+    rFunc.ConvertFormulaToValue(aConvRange, true, false);
+
+    {
+        // Expected output table content.  0 = empty cell
+        const char* aOutputCheck[][3] = {
+            { "1",  "2",  "TRUE" },
+            { "2",  "4",  "TRUE" },
+            { "3",  "6", "FALSE" },
+            { "4",  "8", "FALSE" },
+            { "5", "10",  "TRUE" },
+            { "6", "12",  "TRUE" },
+        };
+
+        bool bSuccess = checkOutput<3>(m_pDoc, aDataRange, aOutputCheck, "Converted");
+        CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
+    }
+
+    // Make sure that B3:B4 and B7:B8 are formula cells.
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,2,0)) == CELLTYPE_FORMULA);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,3,0)) == CELLTYPE_FORMULA);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,6,0)) == CELLTYPE_FORMULA);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,7,0)) == CELLTYPE_FORMULA);
+
+    // Make sure that B5:C6 are numeric cells.
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,4,0)) == CELLTYPE_VALUE);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,5,0)) == CELLTYPE_VALUE);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(2,4,0)) == CELLTYPE_VALUE);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(2,5,0)) == CELLTYPE_VALUE);
+
+    // Make sure that formula cells in C3:C4 and C7:C8 are grouped.
+    const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
+    CPPUNIT_ASSERT(pFC);
+    CPPUNIT_ASSERT(pFC->GetSharedTopRow() == 2);
+    CPPUNIT_ASSERT(pFC->GetSharedLength() == 2);
+    pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
+    CPPUNIT_ASSERT(pFC);
+    CPPUNIT_ASSERT(pFC->GetSharedTopRow() == 6);
+    CPPUNIT_ASSERT(pFC->GetSharedLength() == 2);
+
+    // Undo and check.
+    SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
+    CPPUNIT_ASSERT(pUndoMgr);
+    pUndoMgr->Undo();
+
+    {
+        // Expected output table content.  0 = empty cell
+        const char* aOutputCheck[][3] = {
+            { "1",  "2", "TRUE" },
+            { "2",  "4", "TRUE" },
+            { "3",  "6", "TRUE" },
+            { "4",  "8", "TRUE" },
+            { "5", "10", "TRUE" },
+            { "6", "12", "TRUE" },
+        };
+
+        bool bSuccess = checkOutput<3>(m_pDoc, aDataRange, aOutputCheck, "After undo");
+        CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
+    }
+
+    // B3:B8 should all be (ungrouped) formula cells.
+    for (SCROW i = 2; i <= 7; ++i)
+    {
+        pFC = m_pDoc->GetFormulaCell(ScAddress(1,i,0));
+        CPPUNIT_ASSERT(pFC);
+        CPPUNIT_ASSERT(!pFC->IsShared());
+    }
+
+    // C3:C8 should be shared formula cells.
+    pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
+    CPPUNIT_ASSERT(pFC->GetSharedTopRow() == 2);
+    CPPUNIT_ASSERT(pFC->GetSharedLength() == 6);
+
+    // Redo and check.
+    pUndoMgr->Redo();
+    {
+        // Expected output table content.  0 = empty cell
+        const char* aOutputCheck[][3] = {
+            { "1",  "2",  "TRUE" },
+            { "2",  "4",  "TRUE" },
+            { "3",  "6", "FALSE" },
+            { "4",  "8", "FALSE" },
+            { "5", "10",  "TRUE" },
+            { "6", "12",  "TRUE" },
+        };
+
+        bool bSuccess = checkOutput<3>(m_pDoc, aDataRange, aOutputCheck, "Converted");
+        CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
+    }
+
+    // Make sure that B3:B4 and B7:B8 are formula cells.
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,2,0)) == CELLTYPE_FORMULA);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,3,0)) == CELLTYPE_FORMULA);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,6,0)) == CELLTYPE_FORMULA);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,7,0)) == CELLTYPE_FORMULA);
+
+    // Make sure that B5:C6 are numeric cells.
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,4,0)) == CELLTYPE_VALUE);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(1,5,0)) == CELLTYPE_VALUE);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(2,4,0)) == CELLTYPE_VALUE);
+    CPPUNIT_ASSERT(m_pDoc->GetCellType(ScAddress(2,5,0)) == CELLTYPE_VALUE);
+
+    // Make sure that formula cells in C3:C4 and C7:C8 are grouped.
+    pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
+    CPPUNIT_ASSERT(pFC);
+    CPPUNIT_ASSERT(pFC->GetSharedTopRow() == 2);
+    CPPUNIT_ASSERT(pFC->GetSharedLength() == 2);
+    pFC = m_pDoc->GetFormulaCell(ScAddress(2,6,0));
+    CPPUNIT_ASSERT(pFC);
+    CPPUNIT_ASSERT(pFC->GetSharedTopRow() == 6);
+    CPPUNIT_ASSERT(pFC->GetSharedLength() == 2);
+
+    // Undo again and make sure the recovered formulas in C5:C6 still track B5:B6.
+    pUndoMgr->Undo();
+    m_pDoc->SetValue(ScAddress(1,4,0), 10);
+    m_pDoc->SetValue(ScAddress(1,5,0), 11);
+    CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
+    CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(2,5,0)));
+
+    m_pDoc->DeleteTab(0);
+}
+
 void Test::testMixData()
 {
     m_pDoc->InsertTab(0, "Test");
diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx
index e968f6c..e59378c 100644
--- a/sc/qa/unit/ucalc.hxx
+++ b/sc/qa/unit/ucalc.hxx
@@ -387,6 +387,8 @@ public:
     void testDeleteContents();
     void testTransliterateText();
 
+    void testFormulaToValue();
+
     void testColumnFindEditCells();
 
     CPPUNIT_TEST_SUITE(Test);
@@ -569,6 +571,7 @@ public:
     CPPUNIT_TEST(testImportStream);
     CPPUNIT_TEST(testDeleteContents);
     CPPUNIT_TEST(testTransliterateText);
+    CPPUNIT_TEST(testFormulaToValue);
     CPPUNIT_TEST(testColumnFindEditCells);
     CPPUNIT_TEST_SUITE_END();
 
commit dd617c0bbe4906e8d6e589289f42415738f00006
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Tue Sep 16 19:50:19 2014 -0400

    New feature to allow converting formula cells to static values.
    
    You used to have to convert formula cells to static values by copying
    them and pasting them as values onto the original place.  Why not just
    allow converting them in place?
    
    This is something I've always wanted to implement.

diff --git a/officecfg/registry/data/org/openoffice/Office/UI/CalcCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/CalcCommands.xcu
index a0f7e86..d81f35b 100644
--- a/officecfg/registry/data/org/openoffice/Office/UI/CalcCommands.xcu
+++ b/officecfg/registry/data/org/openoffice/Office/UI/CalcCommands.xcu
@@ -1709,6 +1709,14 @@
           <value>1</value>
         </prop>
       </node>
+      <node oor:name=".uno:ConvertFormulaToValue" oor:op="replace">
+        <prop oor:name="Label" oor:type="xs:string">
+          <value xml:lang="en-US">Convert Formula to Value</value>
+        </prop>
+        <prop oor:name="Properties" oor:type="xs:int">
+          <value>1</value>
+        </prop>
+      </node>
     </node>
     <node oor:name="Popups">
       <node oor:name=".uno:AuditMenu" oor:op="replace">
diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index 639946a..55988a1 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -514,6 +514,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
     sc/source/ui/undo/undoblk3 \
     sc/source/ui/undo/undocell \
     sc/source/ui/undo/undocell2 \
+    sc/source/ui/undo/undoconvert \
     sc/source/ui/undo/undodat \
     sc/source/ui/undo/undodraw \
     sc/source/ui/undo/undoolk \
diff --git a/sc/inc/cellvalues.hxx b/sc/inc/cellvalues.hxx
index 4f4c50f..b545fcd 100644
--- a/sc/inc/cellvalues.hxx
+++ b/sc/inc/cellvalues.hxx
@@ -11,13 +11,29 @@
 #define INCLUDED_SC_INC_CELLVALUES_HXX
 
 #include "address.hxx"
+#include <global.hxx>
 
 class ScColumn;
 
+namespace svl {
+
+class SharedString;
+
+}
+
 namespace sc {
 
 struct CellValuesImpl;
 
+struct CellValueSpan
+{
+    SCROW mnRow1;
+    SCROW mnRow2;
+    CellType meType;
+
+    CellValueSpan( SCROW nRow1, SCROW nRow2, CellType eType );
+};
+
 /**
  * Think of this as a mini-ScColumn like storage that only stores cell
  * values in a column.
@@ -45,16 +61,60 @@ public:
 
     void transferTo( ScColumn& rCol, SCROW nRow );
     void copyTo( ScColumn& rCol, SCROW nRow ) const;
+    void swapNonEmpty( ScColumn& rCol );
 
     void assign( const std::vector<double>& rVals );
 
     size_t size() const;
 
+    void reset( size_t nSize );
+    void setValue( size_t nRow, double fVal );
+    void setValue( size_t nRow, const svl::SharedString& rStr );
+
+    void swap( CellValues& r );
+
+    std::vector<CellValueSpan> getNonEmptySpans() const;
+
 private:
     void copyCellsTo( ScColumn& rCol, SCROW nRow ) const;
     void copyCellTextAttrsTo( ScColumn& rCol, SCROW nRow ) const;
 };
 
+/**
+ * Stores cell values for multiple tables.
+ */
+class TableValues
+{
+    struct Impl;
+
+    Impl* mpImpl;
+
+    TableValues( const TableValues& ); // disabled
+    TableValues& operator= ( const TableValues& ); // disabled
+
+public:
+
+    TableValues();
+    TableValues( const ScRange& rRange );
+    ~TableValues();
+
+    const ScRange& getRange() const;
+
+    /**
+     * Swap the entire column.
+     */
+    void swap( SCTAB nTab, SCCOL nCol, CellValues& rColValue );
+
+    /**
+     * Swap non-empty blocks with the column storage.
+     */
+    void swapNonEmpty( SCTAB nTab, SCCOL nCol, ScColumn& rCol );
+
+    std::vector<CellValueSpan> getNonEmptySpans( SCTAB nTab, SCCOL nCol ) const;
+
+    void swap( TableValues& rOther );
+};
+
 }
 
 #endif
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 893ef13..c694513 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -60,6 +60,7 @@ class EditTextIterator;
 struct NoteEntry;
 class DocumentStreamAccess;
 class CellValues;
+class TableValues;
 struct RowSpan;
 class RowHeightContext;
 class CompileFormulaContext;
@@ -153,6 +154,7 @@ friend class sc::SingleColumnSpanSet;
 friend class sc::ColumnSpanSet;
 friend class sc::EditTextIterator;
 friend class sc::CellValues;
+friend class sc::TableValues;
 
     ScColumn(const ScColumn&); // disabled
     ScColumn& operator= (const ScColumn&); // disabled
@@ -601,6 +603,11 @@ public:
 
     void TransferCellValuesTo( SCROW nRow, size_t nLen, sc::CellValues& rDest );
     void CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc );
+    void ConvertFormulaToValue(
+        sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, sc::TableValues* pUndo );
+
+    void SwapNonEmpty(
+        sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt );
 
 #if DEBUG_COLUMN_STORAGE
     void DumpFormulaGroups() const;
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 1d14b9f..41fcffb 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -75,6 +75,7 @@ struct FormulaGroupContext;
 class DocumentStreamAccess;
 class DocumentLinkManager;
 class CellValues;
+class TableValues;
 class RowHeightContext;
 struct SetFormulaDirtyContext;
 class RefMovedHint;
@@ -2110,6 +2111,10 @@ public:
     void SetCalcConfig( const ScCalcConfig& rConfig );
     const ScCalcConfig& GetCalcConfig() const { return maCalcConfig; }
 
+    void ConvertFormulaToValue( const ScRange& rRange, sc::TableValues* pUndo );
+
+    void SwapNonEmpty( sc::TableValues& rValues );
+
 private:
 
     /**
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index b765ff0..475bea3 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -300,6 +300,7 @@ public:
     sal_uInt16      GetRawError();  // don't interpret, just return code or result error
     bool GetErrorOrValue( sal_uInt16& rErr, double& rVal );
     sc::FormulaResultValue GetResult();
+    sc::FormulaResultValue GetResult() const;
     sal_uInt8       GetMatrixFlag() const { return cMatrixFlag;}
     ScTokenArray* GetCode() { return pCode;}
     const ScTokenArray* GetCode() const { return pCode;}
diff --git a/sc/inc/globstr.hrc b/sc/inc/globstr.hrc
index 0b2a660..eab98cc 100644
--- a/sc/inc/globstr.hrc
+++ b/sc/inc/globstr.hrc
@@ -693,8 +693,9 @@
 #define STR_PRINT_PREVIEW_EMPTY_RANGE   530
 
 #define STR_UNDO_CONDFORMAT             531
+#define STR_UNDO_FORMULA_TO_VALUE       532
 
-#define SC_GLOBSTR_STR_COUNT            532     /**< the count of permanently resident strings */
+#define SC_GLOBSTR_STR_COUNT            533     /**< the count of permanently resident strings */
 
 #endif
 
diff --git a/sc/inc/sc.hrc b/sc/inc/sc.hrc
index 420d149..2d7d228 100644
--- a/sc/inc/sc.hrc
+++ b/sc/inc/sc.hrc
@@ -275,8 +275,9 @@
 
 // functions
 
-#define SID_OPEN_CALC           (SC_FUNCTION_START + 4)
-#define FILE_MENU_END           (SC_FUNCTION_START + 20)
+#define SID_OPEN_CALC                   (SC_FUNCTION_START + 4)
+#define SID_CONVERT_FORMULA_TO_VALUE    (SC_FUNCTION_START + 5)
+#define FILE_MENU_END                   (SC_FUNCTION_START + 20)
 
 #define EDIT_MENU_START         (FILE_MENU_END)
 #define FID_DELETE_CELL         (EDIT_MENU_START + 2)
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index fb5d6f9..e0c6e21 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -69,6 +69,7 @@ struct RefUpdateMoveTabContext;
 struct NoteEntry;
 class DocumentStreamAccess;
 class CellValues;
+class TableValues;
 class RowHeightContext;
 class CompileFormulaContext;
 struct SetFormulaDirtyContext;
@@ -934,6 +935,13 @@ public:
     void TransferCellValuesTo( SCCOL nCol, SCROW nRow, size_t nLen, sc::CellValues& rDest );
     void CopyCellValuesFrom( SCCOL nCol, SCROW nRow, const sc::CellValues& rSrc );
 
+    void ConvertFormulaToValue(
+        sc::EndListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+        sc::TableValues* pUndo );
+
+    void SwapNonEmpty(
+        sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt );
+
 #if DEBUG_COLUMN_STORAGE
     void DumpFormulaGroups( SCCOL nCol ) const;
 #endif
diff --git a/sc/sdi/cellsh.sdi b/sc/sdi/cellsh.sdi
index 8447768..9bc9444 100644
--- a/sc/sdi/cellsh.sdi
+++ b/sc/sdi/cellsh.sdi
@@ -218,6 +218,8 @@ interface CellSelection
     SID_HANGUL_HANJA_CONVERSION [ ExecMethod = ExecuteEdit; StateMethod = GetState; ]
         SID_CHINESE_CONVERSION          [ ExecMethod = ExecuteEdit; StateMethod = GetState; ]
 
+    SID_CONVERT_FORMULA_TO_VALUE [ ExecMethod = ExecuteEdit; StateMethod = GetBlockState; ]
+
     SID_TRANSLITERATE_SENTENCE_CASE [ ExecMethod = ExecuteTrans; StateMethod = GetBlockState; ]
     SID_TRANSLITERATE_TITLE_CASE    [ ExecMethod = ExecuteTrans; StateMethod = GetBlockState; ]
     SID_TRANSLITERATE_TOGGLE_CASE   [ ExecMethod = ExecuteTrans; StateMethod = GetBlockState; ]
diff --git a/sc/sdi/scalc.sdi b/sc/sdi/scalc.sdi
index 1835459..f9c6cc7 100644
--- a/sc/sdi/scalc.sdi
+++ b/sc/sdi/scalc.sdi
@@ -8753,6 +8753,33 @@ SfxVoidItem OpenFromCalc SID_OPEN_CALC
     GroupId = GID_OPTIONS;
 ]
 
+SfxVoidItem ConvertFormulaToValue SID_CONVERT_FORMULA_TO_VALUE
+()
+[
+    /* flags: */
+    AutoUpdate = FALSE,
+    Cachable = Cachable,
+    FastCall = FALSE,
+    HasCoreId = FALSE,
+    HasDialog = FALSE,
+    ReadOnlyDoc = TRUE,
+    Toggle = FALSE,
+    Container = FALSE,
+    RecordAbsolute = FALSE,
+    RecordPerSet;
+    Synchron;
+
+    /* status: */
+    SlotType = SfxStringItem
+
+    /* config: */
+    AccelConfig = TRUE,
+    MenuConfig = TRUE,
+    StatusBarConfig = FALSE,
+    ToolBoxConfig = FALSE,
+    GroupId = GID_OPTIONS;
+]
+
 SfxVoidItem ShowDetail SID_OUTLINE_SHOW
 ()
 [
diff --git a/sc/source/core/data/cellvalues.cxx b/sc/source/core/data/cellvalues.cxx
index 083b4d1..fb38ed5 100644
--- a/sc/source/core/data/cellvalues.cxx
+++ b/sc/source/core/data/cellvalues.cxx
@@ -16,10 +16,44 @@
 
 namespace sc {
 
+namespace {
+
+struct BlockPos
+{
+    size_t mnStart;
+    size_t mnEnd;
+};
+
+CellType toCellType( mdds::mtv::element_t eType )
+{
+    switch (eType)
+    {
+        case element_type_numeric:
+            return CELLTYPE_VALUE;
+        case element_type_string:
+            return CELLTYPE_STRING;
+        case element_type_edittext:
+            return CELLTYPE_EDIT;
+        case element_type_formula:
+            return CELLTYPE_FORMULA;
+        default:
+            ;
+    }
+
+    return CELLTYPE_NONE;
+}
+
+}
+
+CellValueSpan::CellValueSpan( SCROW nRow1, SCROW nRow2, CellType eType ) :
+    mnRow1(nRow1), mnRow2(nRow2), meType(eType) {}
+
 struct CellValuesImpl : boost::noncopyable
 {
     CellStoreType maCells;
     CellTextAttrStoreType maCellTextAttrs;
+    CellStoreType::iterator miCellPos;
+    CellTextAttrStoreType::iterator miAttrPos;
 };
 
 CellValues::CellValues() :
@@ -53,6 +87,34 @@ void CellValues::copyTo( ScColumn& rCol, SCROW nRow ) const
     copyCellTextAttrsTo(rCol, nRow);
 }
 
+void CellValues::swapNonEmpty( ScColumn& rCol )
+{
+    std::vector<BlockPos> aBlocksToSwap;
+
+    {
+        // Go through static value blocks and record their positions and sizes.
+        sc::CellStoreType::const_iterator it = mpImpl->maCells.begin(), itEnd = mpImpl->maCells.end();
+        for (; it != itEnd; ++it)
+        {
+            if (it->type == sc::element_type_empty)
+                continue;
+
+            BlockPos aPos;
+            aPos.mnStart = it->position;
+            aPos.mnEnd = aPos.mnStart + it->size - 1;
+            aBlocksToSwap.push_back(aPos);
+        }
+    }
+
+    // Do the swapping.  The undo storage will store the replaced formula cells after this.
+    std::vector<BlockPos>::const_iterator it = aBlocksToSwap.begin(), itEnd = aBlocksToSwap.end();
+    for (; it != itEnd; ++it)
+    {
+        rCol.maCells.swap(it->mnStart, it->mnEnd, mpImpl->maCells, it->mnStart);
+        rCol.maCellTextAttrs.swap(it->mnStart, it->mnEnd, mpImpl->maCellTextAttrs, it->mnStart);
+    }
+}
+
 void CellValues::assign( const std::vector<double>& rVals )
 {
     mpImpl->maCells.resize(rVals.size());
@@ -70,6 +132,51 @@ size_t CellValues::size() const
     return mpImpl->maCells.size();
 }
 
+void CellValues::reset( size_t nSize )
+{
+    mpImpl->maCells.clear();
+    mpImpl->maCells.resize(nSize);
+    mpImpl->maCellTextAttrs.clear();
+    mpImpl->maCellTextAttrs.resize(nSize);
+
+    mpImpl->miCellPos = mpImpl->maCells.begin();
+    mpImpl->miAttrPos = mpImpl->maCellTextAttrs.begin();
+}
+
+void CellValues::setValue( size_t nRow, double fVal )
+{
+    mpImpl->miCellPos = mpImpl->maCells.set(mpImpl->miCellPos, nRow, fVal);
+    mpImpl->miAttrPos = mpImpl->maCellTextAttrs.set(mpImpl->miAttrPos, nRow, sc::CellTextAttr());
+}
+
+void CellValues::setValue( size_t nRow, const svl::SharedString& rStr )
+{
+    mpImpl->miCellPos = mpImpl->maCells.set(mpImpl->miCellPos, nRow, rStr);
+    mpImpl->miAttrPos = mpImpl->maCellTextAttrs.set(mpImpl->miAttrPos, nRow, sc::CellTextAttr());
+}
+
+void CellValues::swap( CellValues& r )
+{
+    std::swap(mpImpl, r.mpImpl);
+}
+
+std::vector<CellValueSpan> CellValues::getNonEmptySpans() const
+{
+    std::vector<CellValueSpan> aRet;
+    CellStoreType::const_iterator it = mpImpl->maCells.begin(), itEnd = mpImpl->maCells.end();
+    for (; it != itEnd; ++it)
+    {
+        if (it->type != element_type_empty)
+        {
+            // Record this span.
+            size_t nRow1 = it->position;
+            size_t nRow2 = nRow1 + it->size - 1;
+            aRet.push_back(CellValueSpan(nRow1, nRow2, toCellType(it->type)));
+        }
+    }
+    return aRet;
+}
+
 void CellValues::copyCellsTo( ScColumn& rCol, SCROW nRow ) const
 {
     CellStoreType& rDest = rCol.maCells;
@@ -167,6 +274,101 @@ void CellValues::copyCellTextAttrsTo( ScColumn& rCol, SCROW nRow ) const
     }
 }
 
+typedef boost::ptr_vector<CellValues> TableType;
+typedef boost::ptr_vector<TableType> TablesType;
+
+struct TableValues::Impl
+{
+    ScRange maRange;
+    TablesType maTables;
+
+    Impl( const ScRange& rRange ) : maRange(rRange)
+    {
+        size_t nTabs = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1;
+        size_t nCols = rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+
+        for (size_t nTab = 0; nTab < nTabs; ++nTab)
+        {
+            maTables.push_back(new TableType);
+            TableType& rTab = maTables.back();
+            for (size_t nCol = 0; nCol < nCols; ++nCol)
+                rTab.push_back(new CellValues);
+        }
+    }
+
+    CellValues* getCellValues( SCTAB nTab, SCCOL nCol )
+    {
+        if (nTab < maRange.aStart.Tab() || maRange.aEnd.Tab() < nTab)
+            // sheet index out of bound.
+            return NULL;
+
+        if (nCol < maRange.aStart.Col() || maRange.aEnd.Col() < nCol)
+            // column index out of bound.
+            return NULL;
+
+        size_t nTabOffset = nTab - maRange.aStart.Tab();
+        if (nTabOffset >= maTables.size())
+            return NULL;
+
+        TableType& rTab = maTables[nTab-maRange.aStart.Tab()];
+
+        size_t nColOffset = nCol - maRange.aStart.Col();
+        if (nColOffset >= rTab.size())
+            return NULL;
+
+        return &rTab[nColOffset];
+    }
+};
+
+TableValues::TableValues() :
+    mpImpl(new Impl(ScRange(ScAddress::INITIALIZE_INVALID))) {}
+
+TableValues::TableValues( const ScRange& rRange ) :
+    mpImpl(new Impl(rRange)) {}
+
+TableValues::~TableValues()
+{
+    delete mpImpl;
+}
+
+const ScRange& TableValues::getRange() const
+{
+    return mpImpl->maRange;
+}
+
+void TableValues::swap( SCTAB nTab, SCCOL nCol, CellValues& rColValue )
+{
+    CellValues* pCol = mpImpl->getCellValues(nTab, nCol);
+    if (!pCol)
+        return;
+
+    pCol->swap(rColValue);
+}
+
+void TableValues::swapNonEmpty( SCTAB nTab, SCCOL nCol, ScColumn& rCol )
+{
+    CellValues* pCol = mpImpl->getCellValues(nTab, nCol);
+    if (!pCol)
+        return;
+
+    pCol->swapNonEmpty(rCol);
+}
+
+std::vector<CellValueSpan> TableValues::getNonEmptySpans( SCTAB nTab, SCCOL nCol ) const
+{
+    std::vector<CellValueSpan> aRet;
+    CellValues* pCol = mpImpl->getCellValues(nTab, nCol);
+    if (pCol)
+        aRet = pCol->getNonEmptySpans();
+
+    return aRet;
+}
+
+void TableValues::swap( TableValues& rOther )
+{
+    std::swap(mpImpl, rOther.mpImpl);
+}
+
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 3be6857..c0c1fde 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -322,6 +322,155 @@ void ScColumn::CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc )
     BroadcastCells(aRows, SC_HINT_DATACHANGED);
 }
 
+namespace {
+
+class ConvertFormulaToValueHandler
+{
+    SCTAB mnTab;
+    SCCOL mnCol;
+    sc::CellValues maResValues;
+    bool mbModified;
+
+public:
+    ConvertFormulaToValueHandler( SCTAB nTab, SCCOL nCol ) :
+        mnTab(nTab),
+        mnCol(nCol),
+        mbModified(false)
+    {
+        maResValues.reset(MAXROWCOUNT);
+    }
+
+    void operator() ( size_t nRow, const ScFormulaCell* pCell )
+    {
+        sc::FormulaResultValue aRes = pCell->GetResult();
+        switch (aRes.meType)
+        {
+            case sc::FormulaResultValue::Value:
+                maResValues.setValue(nRow, aRes.mfValue);
+            break;
+            case sc::FormulaResultValue::String:
+                maResValues.setValue(nRow, aRes.maString);
+            break;
+            case sc::FormulaResultValue::Error:
+            case sc::FormulaResultValue::Invalid:
+            default:
+                maResValues.setValue(nRow, svl::SharedString::getEmptyString());
+        }
+
+        mbModified = true;
+    }
+
+    bool isModified() const { return mbModified; }
+
+    sc::CellValues& getResValues() { return maResValues; }
+};
+
+}
+
+void ScColumn::ConvertFormulaToValue(
+    sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, sc::TableValues* pUndo )
+{
+    if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2)
+        return;
+
+    std::vector<SCROW> aBounds;
+    aBounds.push_back(nRow1);
+    if (nRow2 < MAXROW-1)
+        aBounds.push_back(nRow2+1);
+
+    // Split formula cell groups at top and bottom boundaries (if applicable).
+    sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
+
+    // Parse all formulas within the range and store their results into temporary storage.
+    ConvertFormulaToValueHandler aFunc(nTab, nCol);
+    sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+    if (!aFunc.isModified())
+        // No formula cells encountered.
+        return;
+
+    DetachFormulaCells(rCxt, nRow1, nRow2);
+
+    // Undo storage to hold static values which will get swapped to the cell storage later.
+    sc::CellValues aUndoCells;
+    aFunc.getResValues().swap(aUndoCells);
+    aUndoCells.swapNonEmpty(*this);
+    if (pUndo)
+        pUndo->swap(nTab, nCol, aUndoCells);
+}
+
+namespace {
+
+class StartListeningHandler
+{
+    sc::StartListeningContext& mrCxt;
+
+public:
+    StartListeningHandler( sc::StartListeningContext& rCxt ) :
+        mrCxt(rCxt) {}
+
+    void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+    {
+        pCell->StartListeningTo(mrCxt);
+    }
+};
+
+class EndListeningHandler
+{
+    sc::EndListeningContext& mrCxt;
+
+public:
+    EndListeningHandler( sc::EndListeningContext& rCxt ) :
+        mrCxt(rCxt) {}
+
+    void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+    {
+        pCell->EndListeningTo(mrCxt);
+    }
+};
+
+}
+
+void ScColumn::SwapNonEmpty(
+    sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt )
+{
+    const ScRange& rRange = rValues.getRange();
+    std::vector<SCROW> aBounds;
+    aBounds.push_back(rRange.aStart.Row());
+    if (rRange.aEnd.Row() < MAXROW-1)
+        aBounds.push_back(rRange.aEnd.Row()+1);
+
+    // Split formula cell groups at top and bottom boundaries (if applicable).
+    sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
+    std::vector<sc::CellValueSpan> aSpans = rValues.getNonEmptySpans(nTab, nCol);
+
+    // Detach formula cells within the spans (if any).
+    EndListeningHandler aEndLisFunc(rEndCxt);
+    std::vector<sc::CellValueSpan>::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
+    sc::CellStoreType::iterator itPos = maCells.begin();
+    for (; it != itEnd; ++it)
+    {
+        SCROW nRow1 = it->mnRow1;
+        SCROW nRow2 = it->mnRow2;
+        itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aEndLisFunc);
+    }
+
+    rValues.swapNonEmpty(nTab, nCol, *this);
+    RegroupFormulaCells();
+
+    // Attach formula cells within the spans (if any).
+    StartListeningHandler aStartLisFunc(rStartCxt);
+    it = aSpans.begin();
+    itPos = maCells.begin();
+    for (; it != itEnd; ++it)
+    {
+        SCROW nRow1 = it->mnRow1;
+        SCROW nRow2 = it->mnRow2;
+        itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aStartLisFunc);
+    }
+
+    CellStorageModified();
+}
+
 void ScColumn::DeleteRanges( const std::vector<sc::RowSpan>& rRanges, InsertDeleteFlags nDelFlag, bool bBroadcast )
 {
     std::vector<sc::RowSpan>::const_iterator itSpan = rRanges.begin(), itSpanEnd = rRanges.end();
diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx
index 51b85ec..183e8e5 100644
--- a/sc/source/core/data/document10.cxx
+++ b/sc/source/core/data/document10.cxx
@@ -18,6 +18,7 @@
 #include <tokenstringcontext.hxx>
 #include <poolhelp.hxx>
 #include <bcaslot.hxx>
+#include <cellvalues.hxx>
 
 #include "dociter.hxx"
 #include "patattr.hxx"
@@ -302,6 +303,46 @@ void ScDocument::SetCalcConfig( const ScCalcConfig& rConfig )
     maCalcConfig = rConfig;
 }
 
+void ScDocument::ConvertFormulaToValue( const ScRange& rRange, sc::TableValues* pUndo )
+{
+    sc::EndListeningContext aCxt(*this);
+
+    for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+    {
+        ScTable* pTab = FetchTable(nTab);
+        if (!pTab)
+            continue;
+
+        pTab->ConvertFormulaToValue(
+            aCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(),
+            pUndo);
+    }
+
+    aCxt.purgeEmptyBroadcasters();
+}
+
+void ScDocument::SwapNonEmpty( sc::TableValues& rValues )
+{
+    const ScRange& rRange = rValues.getRange();
+    if (!rRange.IsValid())
+        return;
+
+    boost::shared_ptr<sc::ColumnBlockPositionSet> pPosSet(new sc::ColumnBlockPositionSet(*this));
+    sc::StartListeningContext aStartCxt(*this, pPosSet);
+    sc::EndListeningContext aEndCxt(*this, pPosSet);
+
+    for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+    {
+        ScTable* pTab = FetchTable(nTab);
+        if (!pTab)
+            continue;
+
+        pTab->SwapNonEmpty(rValues, aStartCxt, aEndCxt);
+    }
+
+    aEndCxt.purgeEmptyBroadcasters();
+}
+
 void ScDocument::PreprocessRangeNameUpdate()
 {
     sc::EndListeningContext aEndListenCxt(*this);
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index d10b9d4..8cd9598 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -2513,6 +2513,15 @@ sc::FormulaResultValue ScFormulaCell::GetResult()
     return aResult.GetResult();
 }
 
+sc::FormulaResultValue ScFormulaCell::GetResult() const
+{
+    sal_uInt16 nErr = pCode->GetCodeError();
+    if (nErr)
+        return sc::FormulaResultValue(nErr);
+
+    return aResult.GetResult();
+}
+
 bool ScFormulaCell::HasOneReference( ScRange& r ) const
 {
     pCode->Reset();
diff --git a/sc/source/core/data/table7.cxx b/sc/source/core/data/table7.cxx
index 407a8ba..2104b6f 100644
--- a/sc/source/core/data/table7.cxx
+++ b/sc/source/core/data/table7.cxx
@@ -14,6 +14,7 @@
 #include <bcaslot.hxx>
 #include <segmenttree.hxx>
 #include <sharedformula.hxx>
+#include <cellvalues.hxx>
 
 bool ScTable::IsMerged( SCCOL nCol, SCROW nRow ) const
 {
@@ -82,6 +83,26 @@ void ScTable::CopyCellValuesFrom( SCCOL nCol, SCROW nRow, const sc::CellValues&
     aCol[nCol].CopyCellValuesFrom(nRow, rSrc);
 }
 
+void ScTable::ConvertFormulaToValue(
+    sc::EndListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+    sc::TableValues* pUndo )
+{
+    if (!ValidCol(nCol1) || !ValidCol(nCol2) || nCol1 > nCol2)
+        return;
+
+    for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+        aCol[nCol].ConvertFormulaToValue(rCxt, nRow1, nRow2, pUndo);
+}
+
+void ScTable::SwapNonEmpty(
+    sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt )
+{
+    const ScRange& rRange = rValues.getRange();
+    assert(rRange.IsValid());
+    for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+        aCol[nCol].SwapNonEmpty(rValues, rStartCxt, rEndCxt);
+}
+
 void ScTable::PreprocessRangeNameUpdate(
     sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
 {
diff --git a/sc/source/ui/docshell/docfunc.cxx b/sc/source/ui/docshell/docfunc.cxx
index c20992a..f3a6245 100644
--- a/sc/source/ui/docshell/docfunc.cxx
+++ b/sc/source/ui/docshell/docfunc.cxx
@@ -84,6 +84,8 @@
 #include "cellvalue.hxx"
 #include "tokenarray.hxx"
 #include <rowheightcontext.hxx>
+#include <cellvalues.hxx>
+#include <undoconvert.hxx>
 
 #include <memory>
 #include <utility>
@@ -5384,6 +5386,38 @@ void ScDocFunc::SetConditionalFormatList( ScConditionalFormatList* pList, SCTAB
     SfxGetpApp()->Broadcast(SfxSimpleHint(SC_HINT_AREAS_CHANGED));
 }
 
+void ScDocFunc::ConvertFormulaToValue( const ScRange& rRange, bool bRecord, bool bInteraction )
+{
+    ScDocShellModificator aModificator(rDocShell);
+    ScDocument& rDoc = rDocShell.GetDocument();
+    if (!rDoc.IsUndoEnabled())
+        bRecord = false;
+
+    ScEditableTester aTester(&rDoc, rRange);
+    if (!aTester.IsEditable())
+    {
+        if (bInteraction)
+            rDocShell.ErrorMessage(aTester.GetMessageId());
+        return;
+    }
+
+    sc::TableValues aUndoVals(rRange);
+    sc::TableValues* pUndoVals = bRecord ? &aUndoVals : NULL;
+
+    rDoc.ConvertFormulaToValue(rRange, pUndoVals);
+
+    if (bRecord && pUndoVals)
+    {
+        rDocShell.GetUndoManager()->AddUndoAction(
+            new sc::UndoFormulaToValue(&rDocShell, *pUndoVals));
+    }
+
+    rDocShell.PostPaint(rRange, PAINT_GRID);
+    rDocShell.PostDataChanged();
+    rDoc.BroadcastCells(rRange, SC_HINT_DATACHANGED);
+    aModificator.SetDocumentModified();
+}
+
 void ScDocFunc::EnterListAction( sal_uInt16 nNameResId )
 {
     OUString aUndo( ScGlobal::GetRscString( nNameResId ) );
diff --git a/sc/source/ui/inc/docfunc.hxx b/sc/source/ui/inc/docfunc.hxx
index 37bf4aa..ebd7ac3 100644
--- a/sc/source/ui/inc/docfunc.hxx
+++ b/sc/source/ui/inc/docfunc.hxx
@@ -222,6 +222,8 @@ public:
      * @param nTab the tab to which the conditional format list belongs
      */
     void SetConditionalFormatList( ScConditionalFormatList* pList, SCTAB nTab );
+
+    void ConvertFormulaToValue( const ScRange& rRange, bool bRecord, bool bInteraction );
 };
 
 class ScDocFuncDirect : public ScDocFunc
diff --git a/sc/source/ui/inc/undoconvert.hxx b/sc/source/ui/inc/undoconvert.hxx
new file mode 100644
index 0000000..99ce97f
--- /dev/null
+++ b/sc/source/ui/inc/undoconvert.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SC_UNDOCONVERT_HXX
+#define INCLUDED_SC_UNDOCONVERT_HXX
+
+#include <undobase.hxx>
+#include <cellvalues.hxx>
+
+namespace sc {
+
+class UndoFormulaToValue : public ScSimpleUndo
+{
+    TableValues maUndoValues;
+
+public:
+    UndoFormulaToValue( ScDocShell* pDocSh, TableValues& rUndoValues );
+
+    virtual OUString GetComment() const SAL_OVERRIDE;
+    virtual void Undo() SAL_OVERRIDE;
+    virtual void Redo() SAL_OVERRIDE;
+
+private:
+    void Execute();
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/viewfunc.hxx b/sc/source/ui/inc/viewfunc.hxx
index cd664cd..70c7e0f 100644
--- a/sc/source/ui/inc/viewfunc.hxx
+++ b/sc/source/ui/inc/viewfunc.hxx
@@ -240,6 +240,7 @@ public:
     void            FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
                                 SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount, bool bRecord = true );
     void            FillCrossDblClick();
+    void            ConvertFormulaToValue();
 
     void            TransliterateText( sal_Int32 nType );
 
diff --git a/sc/source/ui/src/globstr.src b/sc/source/ui/src/globstr.src
index 3d167fd..985644a 100644
--- a/sc/source/ui/src/globstr.src
+++ b/sc/source/ui/src/globstr.src
@@ -2076,6 +2076,11 @@ Resource RID_GLOBSTR
     {
         Text [ en-US ] = "Conditional Format";
     };
+
+    String STR_UNDO_FORMULA_TO_VALUE
+    {
+        Text [ en-US ] = "Convert Formula To Value";
+    };
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undoconvert.cxx b/sc/source/ui/undo/undoconvert.cxx
new file mode 100644
index 0000000..995ab8f
--- /dev/null
+++ b/sc/source/ui/undo/undoconvert.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <undoconvert.hxx>
+#include <globstr.hrc>
+#include <undoutil.hxx>
+
+namespace sc {
+
+UndoFormulaToValue::UndoFormulaToValue( ScDocShell* pDocSh, TableValues& rUndoValues ) :
+    ScSimpleUndo(pDocSh)
+{
+    maUndoValues.swap(rUndoValues);
+}
+
+OUString UndoFormulaToValue::GetComment() const
+{
+    return ScGlobal::GetRscString(STR_UNDO_FORMULA_TO_VALUE);
+}
+
+void UndoFormulaToValue::Undo()
+{
+    Execute();
+}
+
+void UndoFormulaToValue::Redo()
+{
+    Execute();
+}
+
+void UndoFormulaToValue::Execute()
+{
+    ScDocument& rDoc = pDocShell->GetDocument();
+    rDoc.SwapNonEmpty(maUndoValues);
+
+    ScUndoUtil::MarkSimpleBlock(pDocShell, maUndoValues.getRange());
+
+    pDocShell->PostPaint(maUndoValues.getRange(), PAINT_GRID);
+    pDocShell->PostDataChanged();
+    rDoc.BroadcastCells(maUndoValues.getRange(), SC_HINT_DATACHANGED);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh1.cxx b/sc/source/ui/view/cellsh1.cxx
index bb9ce1a..511d4b7 100644
--- a/sc/source/ui/view/cellsh1.cxx
+++ b/sc/source/ui/view/cellsh1.cxx
@@ -1754,6 +1754,11 @@ void ScCellShell::ExecuteEdit( SfxRequest& rReq )
             }
             break;
 
+        case SID_CONVERT_FORMULA_TO_VALUE:
+        {
+            pTabViewShell->ConvertFormulaToValue();
+        }
+        break;
         case SID_THESAURUS:
             pTabViewShell->DoThesaurus();
             break;
diff --git a/sc/source/ui/view/viewfun2.cxx b/sc/source/ui/view/viewfun2.cxx
index df5fb97..1f395fc 100644
--- a/sc/source/ui/view/viewfun2.cxx
+++ b/sc/source/ui/view/viewfun2.cxx
@@ -1414,6 +1414,17 @@ void ScViewFunc::FillCrossDblClick()
     }
 }
 
+void ScViewFunc::ConvertFormulaToValue()
+{
+    ScRange aRange;
+    GetViewData().GetSimpleArea(aRange);
+    aRange.Justify();
+
+    ScDocShell* pDocSh = GetViewData().GetDocShell();
+    pDocSh->GetDocFunc().ConvertFormulaToValue(aRange, true, true);
+    pDocSh->PostPaint(aRange, PAINT_GRID);
+}
+
 void ScViewFunc::TransliterateText( sal_Int32 nType )
 {
     ScMarkData aFuncMark = GetViewData().GetMarkData();


More information about the Libreoffice-commits mailing list