[Libreoffice-commits] .: 8 commits - readme.md starmath/inc starmath/sdi starmath/source

Michael Meeks mmeeks at kemper.freedesktop.org
Wed Oct 27 03:05:20 PDT 2010


 readme.md                    |   72 +
 starmath/inc/caret.hxx       |  471 ++++++++
 starmath/inc/cursor.hxx      |  450 +++++++
 starmath/inc/document.hxx    |   21 
 starmath/inc/edit.hxx        |    2 
 starmath/inc/node.hxx        |  521 +++++++-
 starmath/inc/parse.hxx       |   23 
 starmath/inc/starmath.hrc    |    4 
 starmath/inc/view.hxx        |   51 
 starmath/inc/visitors.hxx    |  495 ++++++++
 starmath/sdi/smath.sdi       |    2 
 starmath/sdi/smslots.sdi     |   14 
 starmath/source/caret.cxx    |   59 +
 starmath/source/cursor.cxx   | 1617 +++++++++++++++++++++++++++
 starmath/source/dialog.cxx   |    4 
 starmath/source/document.cxx |   44 
 starmath/source/edit.cxx     |   56 
 starmath/source/makefile.mk  |   59 -
 starmath/source/node.cxx     |  426 ++++---
 starmath/source/parse.cxx    |   86 +
 starmath/source/view.cxx     |  214 ++-
 starmath/source/visitors.cxx | 2528 +++++++++++++++++++++++++++++++++++++++++++
 22 files changed, 6843 insertions(+), 376 deletions(-)

New commits:
commit 816ad5ae7f3c32b20b6421541577fdf613e2aa58
Merge: 047ad40... f1b82f5...
Author: Michael Meeks <michael.meeks at novell.com>
Date:   Wed Oct 27 10:56:35 2010 +0100

    Merge branch 'feature/editable-formulae'

commit f1b82f5c544e8e34ae46f3fabf284a113fc482ba
Author: Michael Meeks <michael.meeks at novell.com>
Date:   Sat Oct 23 17:52:33 2010 +0100

    re-instate the nasty old editing paths as a run-time conditional

diff --git a/starmath/inc/edit.hxx b/starmath/inc/edit.hxx
index cd0c6cb..7249cc3 100644
--- a/starmath/inc/edit.hxx
+++ b/starmath/inc/edit.hxx
@@ -65,13 +65,15 @@ class SmEditWindow : public Window, public DropTargetHelper
     ScrollBar	   *pHScrollBar,
                    *pVScrollBar;
     ScrollBarBox   *pScrollBox;
-    Timer			aModifyTimer;
+    Timer			aModifyTimer,
+                    aCursorMoveTimer;
     ESelection		aOldSelection;
 
     virtual void KeyInput(const KeyEvent& rKEvt);
     virtual void Command(const CommandEvent& rCEvt);
     DECL_LINK(MenuSelectHdl, Menu *);
     DECL_LINK(ModifyTimerHdl, Timer *);
+    DECL_LINK(CursorMoveTimerHdl, Timer *);
 
     virtual void DataChanged( const DataChangedEvent& );
     virtual void Resize();
@@ -133,6 +135,8 @@ public:
     void ApplyColorConfigValues( const svtools::ColorConfig &rColorCfg );
 
     BOOL                HandleWheelCommands( const CommandEvent &rCEvt );
+    bool                IsInlineEditEnabled();
+    void                StartCursorMove();
 
     // for Accessibility
     virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > CreateAccessible();
diff --git a/starmath/inc/node.hxx b/starmath/inc/node.hxx
index f6a33a6..7f7981d 100644
--- a/starmath/inc/node.hxx
+++ b/starmath/inc/node.hxx
@@ -34,9 +34,10 @@
 #include <iostream>
 #include <stdio.h>
 
-//My special assert macro
-//TODO: replace this with DBG_ASSERT when this patch moves to production, can be done using search/replace
-#define j_assert(cond, msg)     do{                                                             \
+#define j_assert(cond, msg) DBG_ASSERT(cond, msg)
+#if 0
+// easier to read assert macro
+    do{                                                             \
                                     if(!(cond))                                                 \
                                     {                                                           \
                                         std::cerr<<"Failed assertion: "<<msg<<", at line ";     \
@@ -48,7 +49,9 @@
                                         fprintf(stderr, "%d in %s\n", __LINE__, f + 1);         \
                                     }                                                           \
                                 } while(false)
-//TODO: Comment out below to disable dumpasdot
+#endif
+
+// Comment out below to disable dumpasdot
 #define DEBUG_ENABLE_DUMPASDOT
 
 #include "parse.hxx"
diff --git a/starmath/inc/view.hxx b/starmath/inc/view.hxx
index 67e83ac..f867e4a 100644
--- a/starmath/inc/view.hxx
+++ b/starmath/inc/view.hxx
@@ -51,8 +51,23 @@ class SmPrintUIOptions;
 
 class SmGraphicWindow : public ScrollableWindow
 {
-    Point			aFormulaDrawPos;
+    Point     aFormulaDrawPos;
 
+    // old style editing pieces
+    Rectangle aCursorRect;
+    bool      bIsCursorVisible;
+public:
+    BOOL IsCursorVisible() const { return bIsCursorVisible; }
+    void ShowCursor(BOOL bShow);
+    const SmNode * SetCursorPos(USHORT nRow, USHORT nCol);
+protected:
+    void		SetIsCursorVisible(BOOL bVis) { bIsCursorVisible = bVis; }
+    using   Window::SetCursor;
+    void        SetCursor(const SmNode *pNode);
+    void 		SetCursor(const Rectangle &rRect);
+    bool        IsInlineEditEnabled() const;
+
+private:
     ::com::sun::star::uno::Reference<
         ::com::sun::star::accessibility::XAccessible >  xAccessible;
     SmGraphicAccessible *                                       pAccessible;
@@ -298,6 +313,9 @@ public:
     void SetInsertIntoEditWindow(BOOL bEditWindowHadFocusLast = TRUE){
         bInsertIntoEditWindow = bEditWindowHadFocusLast;
     }
+    bool IsInlineEditEnabled() const {
+        return false;
+    }
 };
 
 #endif
diff --git a/starmath/source/document.cxx b/starmath/source/document.cxx
index 7029228..d2d893f 100644
--- a/starmath/source/document.cxx
+++ b/starmath/source/document.cxx
@@ -98,6 +98,7 @@
 #include <sfx2/sfxsids.hrc>
 #include <svx/svxids.hrc>
 #include "cursor.hxx"
+#include "visitors.hxx"
 
 using namespace ::com::sun::star;
 using namespace ::com::sun::star::accessibility;
@@ -435,8 +436,6 @@ SfxItemPool& SmDocShell::GetEditEngineItemPool()
     OSL_ENSURE( pEditEngineItemPool, "EditEngineItemPool missing" );
     return *pEditEngineItemPool;
 }
-//TODO: Move to the top of the file...
-#include "visitors.hxx"
 
 void SmDocShell::DrawFormula(OutputDevice &rDev, Point &rPosition, BOOL bDrawSelection)
 {
@@ -530,8 +529,7 @@ Size SmDocShell::GetSize()
 }
 
 void SmDocShell::InvalidateCursor(){
-    if(pCursor)
-        delete pCursor;
+    delete pCursor;
     pCursor = NULL;
 }
 
diff --git a/starmath/source/edit.cxx b/starmath/source/edit.cxx
index 3974065..a9a1ad2 100644
--- a/starmath/source/edit.cxx
+++ b/starmath/source/edit.cxx
@@ -96,6 +96,11 @@ void SmGetLeftSelectionPart(const ESelection aSel,
     }
 }
 
+bool SmEditWindow::IsInlineEditEnabled()
+{
+    return GetView()->IsInlineEditEnabled();
+}
+
 ////////////////////////////////////////
 
 SmEditWindow::SmEditWindow( SmCmdBoxWindow &rMyCmdBoxWin ) :
@@ -122,6 +127,12 @@ SmEditWindow::SmEditWindow( SmCmdBoxWindow &rMyCmdBoxWin ) :
     aModifyTimer.SetTimeoutHdl(LINK(this, SmEditWindow, ModifyTimerHdl));
     aModifyTimer.SetTimeout(500);
 
+    if (!IsInlineEditEnabled())
+    {
+        aCursorMoveTimer.SetTimeoutHdl(LINK(this, SmEditWindow, CursorMoveTimerHdl));
+        aCursorMoveTimer.SetTimeout(500);
+    }
+
     // if not called explicitly the this edit window within the
     // command window will just show an empty gray panel.
     Show();
@@ -132,6 +143,7 @@ SmEditWindow::~SmEditWindow()
 {
     aModifyTimer.Stop();
 
+    StartCursorMove();
 
     // #112565# clean up of classes used for accessibility
     // must be done before EditView (and thus EditEngine) is no longer
@@ -156,6 +168,12 @@ SmEditWindow::~SmEditWindow()
     delete pScrollBox;
 }
 
+void SmEditWindow::StartCursorMove()
+{
+    if (!IsInlineEditEnabled())
+        aCursorMoveTimer.Stop();
+}
+
 void SmEditWindow::InvalidateSlots()
 {
     SfxBindings& rBind = GetView()->GetViewFrame()->GetBindings();
@@ -253,6 +271,35 @@ IMPL_LINK( SmEditWindow, ModifyTimerHdl, Timer *, EMPTYARG /*pTimer*/ )
     return 0;
 }
 
+IMPL_LINK(SmEditWindow, CursorMoveTimerHdl, Timer *, EMPTYARG /*pTimer*/)
+    // every once in a while check cursor position (selection) of edit
+    // window and if it has changed (try to) set the formula-cursor
+    // according to that.
+{
+    if (IsInlineEditEnabled())
+        return 0;
+
+    ESelection aNewSelection(GetSelection());
+
+    if (!aNewSelection.IsEqual(aOldSelection))
+    {
+        SmViewShell *pView = rCmdBox.GetView();
+        if (pView)
+        {
+            // get row and column to look for
+            USHORT  nRow, nCol;
+            SmGetLeftSelectionPart(aNewSelection, nRow, nCol);
+            nRow++;
+            nCol++;
+            pView->GetGraphicWindow().SetCursorPos(nRow, nCol);
+            aOldSelection = aNewSelection;
+        }
+    }
+    aCursorMoveTimer.Stop();
+
+    return 0;
+}
+
 void SmEditWindow::Resize()
 {
     if (!pEditView)
@@ -286,6 +333,8 @@ void SmEditWindow::MouseButtonUp(const MouseEvent &rEvt)
     else
         Window::MouseButtonUp (rEvt);
 
+    if (!IsInlineEditEnabled())
+        CursorMoveTimerHdl(&aCursorMoveTimer);
     InvalidateSlots();
 }
 
@@ -390,6 +439,8 @@ void SmEditWindow::KeyInput(const KeyEvent& rKEvt)
     }
     else
     {
+        StartCursorMove();
+
         if (!pEditView)
             CreateEditView();
         if ( !pEditView->PostKeyEvent(rKEvt) )
@@ -616,7 +667,7 @@ void SmEditWindow::GetFocus()
         pEditEngine->SetStatusEventHdl( LINK(this, SmEditWindow, EditStatusHdl) );
 
     //Let SmViewShell know we got focus
-    if(GetView())
+    if(GetView() && IsInlineEditEnabled())
         GetView()->SetInsertIntoEditWindow(TRUE);
 }
 
@@ -700,7 +751,7 @@ void SmEditWindow::InsertCommand(USHORT nCommand)
         }
 
         aModifyTimer.Start();
-
+        StartCursorMove();
         GrabFocus();
     }
 }
@@ -887,6 +938,7 @@ void SmEditWindow::InsertText(const String& Text)
     {
         pEditView->InsertText(Text);
         aModifyTimer.Start();
+        StartCursorMove();
     }
 }
 
@@ -904,6 +956,11 @@ void SmEditWindow::Flush()
                     new SfxStringItem(SID_TEXT, GetText()), 0L);
         }
     }
+    if (aCursorMoveTimer.IsActive())
+    {
+        aCursorMoveTimer.Stop();
+        CursorMoveTimerHdl(&aCursorMoveTimer);
+    }
 }
 
 
diff --git a/starmath/source/view.cxx b/starmath/source/view.cxx
index f06244b..7e98170 100644
--- a/starmath/source/view.cxx
+++ b/starmath/source/view.cxx
@@ -169,16 +169,50 @@ void SmGraphicWindow::MouseButtonDown(const MouseEvent& rMEvt)
         Point  aPos (PixelToLogic(rMEvt.GetPosPixel())
                      - GetFormulaDrawPos());
 
-        const SmNode* pTree = pViewShell->GetDoc()->GetFormulaTree();
-
+        const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree();
+        if (!pTree)
+            return;
+
+        if (IsInlineEditEnabled()) {
+            // if it was clicked inside the formula then get the appropriate node
+            if (pTree->OrientedDist(aPos) <= 0)
+                pViewShell->GetDoc()->GetCursor().MoveTo(this, aPos, !rMEvt.IsShift());
+            return;
+        }
+        const SmNode *pNode = 0;
         // if it was clicked inside the formula then get the appropriate node
         if (pTree->OrientedDist(aPos) <= 0)
-            pViewShell->GetDoc()->GetCursor().MoveTo(this, aPos, !rMEvt.IsShift());
+            pNode = pTree->FindRectClosestTo(aPos);
+
+        if (pNode)
+        {	SmEditWindow  *pEdit = pViewShell->GetEditWindow();
+            const SmToken  aToken (pNode->GetToken());
+
+            // set selection to the beginning of the token
+            ESelection  aSel (aToken.nRow - 1, aToken.nCol - 1);
+
+            if (rMEvt.GetClicks() != 1 || aToken.eType == TPLACE)
+                aSel.nEndPos = aSel.nEndPos + sal::static_int_cast< USHORT >(aToken.aText.Len());
+
+            pEdit->SetSelection(aSel);
+            SetCursor(pNode);
+
+            // allow for immediate editing and
+            //! implicitly synchronize the cursor position mark in this window
+            pEdit->GrabFocus();
+        }
     }
 }
 
+bool SmGraphicWindow::IsInlineEditEnabled() const
+{
+    return pViewShell->GetEditWindow()->IsInlineEditEnabled();
+}
+
 void SmGraphicWindow::GetFocus()
 {
+    if (!IsInlineEditEnabled())
+        return;
     pViewShell->GetEditWindow()->Flush();
     //Let view shell know what insertions should be done in visual editor
     pViewShell->SetInsertIntoEditWindow(FALSE);
@@ -197,6 +231,80 @@ void SmGraphicWindow::LoseFocus()
     }
 }
 
+void SmGraphicWindow::ShowCursor(BOOL bShow)
+    // shows or hides the formula-cursor depending on 'bShow' is TRUE or not
+{
+    if (IsInlineEditEnabled())
+        return;
+
+    BOOL  bInvert = bShow != IsCursorVisible();
+
+    if (bInvert)
+        InvertTracking(aCursorRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW);
+
+    SetIsCursorVisible(bShow);
+}
+
+
+void SmGraphicWindow::SetCursor(const SmNode *pNode)
+{
+    if (IsInlineEditEnabled())
+        return;
+
+    const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree();
+
+    // get appropriate rectangle
+    Point aOffset (pNode->GetTopLeft() - pTree->GetTopLeft()),
+          aTLPos  (GetFormulaDrawPos() + aOffset);
+    aTLPos.X() -= pNode->GetItalicLeftSpace();
+    Size  aSize   (pNode->GetItalicSize());
+    Point aBRPos  (aTLPos.X() + aSize.Width(), aTLPos.Y() + aSize.Height());
+
+    SetCursor(Rectangle(aTLPos, aSize));
+}
+
+void SmGraphicWindow::SetCursor(const Rectangle &rRect)
+    // sets cursor to new position (rectangle) 'rRect'.
+    // The old cursor will be removed, and the new one will be shown if
+    // that is activated in the ConfigItem
+{
+    if (IsInlineEditEnabled())
+        return;
+
+    SmModule *pp = SM_MOD();
+
+    if (IsCursorVisible())
+        ShowCursor(FALSE);		// clean up remainings of old cursor
+    aCursorRect = rRect;
+    if (pp->GetConfig()->IsShowFormulaCursor())
+        ShowCursor(TRUE);		// draw new cursor
+}
+
+const SmNode * SmGraphicWindow::SetCursorPos(USHORT nRow, USHORT nCol)
+    // looks for a VISIBLE node in the formula tree with it's token at
+    // (or around) the position 'nRow', 'nCol' in the edit window
+    // (row and column numbering starts with 1 there!).
+    // If there is such a node the formula-cursor is set to cover that nodes
+    // rectangle. If not the formula-cursor will be hidden.
+    // In any case the search result is being returned.
+{
+    if (IsInlineEditEnabled())
+        return NULL;
+
+    // find visible node with token at nRow, nCol
+    const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree(),
+                 *pNode = 0;
+    if (pTree)
+        pNode = pTree->FindTokenAt(nRow, nCol);
+
+    if (pNode)
+        SetCursor(pNode);
+    else
+        ShowCursor(FALSE);
+
+    return pNode;
+}
+
 void SmGraphicWindow::Paint(const Rectangle&)
 {
     OSL_ENSURE(pViewShell, "Sm : NULL pointer");
@@ -207,9 +315,27 @@ void SmGraphicWindow::Paint(const Rectangle&)
     rDoc.DrawFormula(*this, aPoint, TRUE);	//! modifies aPoint to be the topleft
                                 //! corner of the formula
     SetFormulaDrawPos(aPoint);
-    //Draw cursor if any...
-    if(pViewShell->GetDoc()->HasCursor())
-        pViewShell->GetDoc()->GetCursor().Draw(*this, aPoint);
+    if(IsInlineEditEnabled()) {
+        //Draw cursor if any...
+        if(pViewShell->GetDoc()->HasCursor())
+            pViewShell->GetDoc()->GetCursor().Draw(*this, aPoint);
+    } else {
+    SetIsCursorVisible(FALSE);	// (old) cursor must be drawn again
+
+    const SmEditWindow *pEdit = pViewShell->GetEditWindow();
+    if (pEdit)
+    {	// get new position for formula-cursor (for possible altered formula)
+        USHORT	nRow, nCol;
+        SmGetLeftSelectionPart(pEdit->GetSelection(), nRow, nCol);
+        nRow++;
+        nCol++;
+        const SmNode *pFound = SetCursorPos(nRow, nCol);
+
+        SmModule  *pp = SM_MOD();
+        if (pFound && pp->GetConfig()->IsShowFormulaCursor())
+            ShowCursor(TRUE);
+    }
+    }
 }
 
 
@@ -223,6 +349,11 @@ void SmGraphicWindow::SetTotalSize ()
 
 void SmGraphicWindow::KeyInput(const KeyEvent& rKEvt)
 {
+    if (!IsInlineEditEnabled()) {
+        if (! (GetView() && GetView()->KeyInput(rKEvt)) )
+            ScrollableWindow::KeyInput(rKEvt);
+        return;
+    }
     USHORT nCode = rKEvt.GetKeyCode().GetCode();
     SmCursor& rCursor = pViewShell->GetDoc()->GetCursor();
     switch(nCode)
@@ -1360,8 +1491,8 @@ void SmViewShell::Execute(SfxRequest& rReq)
                 bVal = !pp->GetConfig()->IsShowFormulaCursor();
 
             pp->GetConfig()->SetShowFormulaCursor(bVal);
-            //GetGraphicWindow().ShowCursor(bVal);
-            //TODO Consider disabling this option!!!
+            if (!IsInlineEditEnabled())
+                GetGraphicWindow().ShowCursor(bVal);
             break;
         }
         case SID_DRAW:
@@ -1506,9 +1637,9 @@ void SmViewShell::Execute(SfxRequest& rReq)
             const SfxInt16Item& rItem =
                 (const SfxInt16Item&)rReq.GetArgs()->Get(SID_INSERTCOMMAND);
 
-            if (pWin && bInsertIntoEditWindow)
+            if (pWin && (bInsertIntoEditWindow || !IsInlineEditEnabled()))
                 pWin->InsertCommand(rItem.GetValue());
-            if (GetDoc() && !bInsertIntoEditWindow) {
+            if (IsInlineEditEnabled() && (GetDoc() && !bInsertIntoEditWindow)) {
                 GetDoc()->GetCursor().InsertCommand(rItem.GetValue());
                 GetGraphicWindow().GrabFocus();
             }
@@ -1520,9 +1651,9 @@ void SmViewShell::Execute(SfxRequest& rReq)
             const SfxStringItem& rItem =
                 (const SfxStringItem&)rReq.GetArgs()->Get(SID_INSERTSYMBOL);
 
-            if (pWin && bInsertIntoEditWindow)
+            if (pWin && (bInsertIntoEditWindow || !IsInlineEditEnabled()))
                 pWin->InsertText(rItem.GetValue());
-            if(GetDoc() && !bInsertIntoEditWindow)
+            if (IsInlineEditEnabled() && (GetDoc() && !bInsertIntoEditWindow))
                 GetDoc()->GetCursor().InsertSpecial(rItem.GetValue());
             break;
         }
commit 01f4b50072f4d06aad01d3a310e9b6e946988129
Author: Jonas Finnemann Jensen <jopsen at gmail.com>
Date:   Sat Oct 23 16:39:57 2010 +0100

    Use the right license header

diff --git a/starmath/inc/caret.hxx b/starmath/inc/caret.hxx
index ae0f0fc..df8abaf 100644
--- a/starmath/inc/caret.hxx
+++ b/starmath/inc/caret.hxx
@@ -1,3 +1,29 @@
+/*
+ * Version: MPL 1.1 / GPLv3+ / LGPLv3+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Initial Developer of the Original Code is
+ *       Jonas Finnemann Jensen <jopsen at gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2010 the
+ * Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Jonas Finnemann Jensen <jopsen at gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
+ * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
+ * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
+ * instead of those above.
+ */
 #ifndef CARET_H
 #define CARET_H
 
diff --git a/starmath/inc/cursor.hxx b/starmath/inc/cursor.hxx
index d78aad7..fab86a4 100644
--- a/starmath/inc/cursor.hxx
+++ b/starmath/inc/cursor.hxx
@@ -1,3 +1,29 @@
+/*
+ * Version: MPL 1.1 / GPLv3+ / LGPLv3+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Initial Developer of the Original Code is
+ *       Jonas Finnemann Jensen <jopsen at gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2010 the
+ * Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Jonas Finnemann Jensen <jopsen at gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
+ * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
+ * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
+ * instead of those above.
+ */
 #ifndef SMCURSOR_H
 #define SMCURSOR_H
 
diff --git a/starmath/inc/visitors.hxx b/starmath/inc/visitors.hxx
index bb4ee15..d63bc13 100644
--- a/starmath/inc/visitors.hxx
+++ b/starmath/inc/visitors.hxx
@@ -1,3 +1,29 @@
+/*
+ * Version: MPL 1.1 / GPLv3+ / LGPLv3+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Initial Developer of the Original Code is
+ *       Jonas Finnemann Jensen <jopsen at gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2010 the
+ * Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Jonas Finnemann Jensen <jopsen at gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
+ * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
+ * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
+ * instead of those above.
+ */
 #ifndef SMVISITORS_H
 #define SMVISITORS_H
 
diff --git a/starmath/source/caret.cxx b/starmath/source/caret.cxx
index 24374be..128c427 100644
--- a/starmath/source/caret.cxx
+++ b/starmath/source/caret.cxx
@@ -1,3 +1,29 @@
+/*
+ * Version: MPL 1.1 / GPLv3+ / LGPLv3+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Initial Developer of the Original Code is
+ *       Jonas Finnemann Jensen <jopsen at gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2010 the
+ * Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Jonas Finnemann Jensen <jopsen at gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
+ * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
+ * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
+ * instead of those above.
+ */
 #include "caret.hxx"
 
 /////////////////////////////// SmCaretPosGraph ////////////////////////////////
diff --git a/starmath/source/cursor.cxx b/starmath/source/cursor.cxx
index 3b5022c..5613c3b 100644
--- a/starmath/source/cursor.cxx
+++ b/starmath/source/cursor.cxx
@@ -1,3 +1,29 @@
+/*
+ * Version: MPL 1.1 / GPLv3+ / LGPLv3+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Initial Developer of the Original Code is
+ *       Jonas Finnemann Jensen <jopsen at gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2010 the
+ * Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Jonas Finnemann Jensen <jopsen at gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
+ * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
+ * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
+ * instead of those above.
+ */
 #include "cursor.hxx"
 #include "parse.hxx"
 #include "visitors.hxx"
diff --git a/starmath/source/visitors.cxx b/starmath/source/visitors.cxx
index f7418c0..dc5dd38 100644
--- a/starmath/source/visitors.cxx
+++ b/starmath/source/visitors.cxx
@@ -1,3 +1,29 @@
+/*
+ * Version: MPL 1.1 / GPLv3+ / LGPLv3+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Initial Developer of the Original Code is
+ *       Jonas Finnemann Jensen <jopsen at gmail.com>
+ * Portions created by the Initial Developer are Copyright (C) 2010 the
+ * Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Jonas Finnemann Jensen <jopsen at gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
+ * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
+ * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
+ * instead of those above.
+ */
 #include "visitors.hxx"
 #include "cursor.hxx"
 
commit 87a7f1813a40d73fadb430985d5601d1128fd057
Author: Michael Meeks <michael.meeks at novell.com>
Date:   Sat Oct 23 16:29:59 2010 +0100

    undo merging issue

diff --git a/starmath/inc/node.hxx b/starmath/inc/node.hxx
index 04a954e..f6a33a6 100644
--- a/starmath/inc/node.hxx
+++ b/starmath/inc/node.hxx
@@ -114,8 +114,12 @@ class SmNode : public SmRect
     SmNodeType		eType;
     SmScaleMode		eScaleMode;
     RectHorAlign	eRectHorAlign;
-    USHORT		nFlags, nAttributes;
-    BOOL		bIsPhantom, bIsDebug;
+    USHORT			nFlags,
+                    nAttributes;
+    BOOL			bIsPhantom,
+                    bIsDebug;
+
+    BOOL            bIsSelected;
 
 protected:
     SmNode(SmNodeType eNodeType, const SmToken &rNodeToken);
commit 2a51f7826f84c929aa82c9e1cbe37456bc9d70b9
Author: Michael Meeks <michael.meeks at novell.com>
Date:   Sat Oct 23 16:20:40 2010 +0100

    cleanup leading tabs etc.

diff --git a/starmath/inc/visitors.hxx b/starmath/inc/visitors.hxx
index aeed3f6..bb4ee15 100644
--- a/starmath/inc/visitors.hxx
+++ b/starmath/inc/visitors.hxx
@@ -298,7 +298,7 @@ private:
 class SmCaretPosGraphBuildingVisitor : public SmVisitor
 {
 public:
-	/** Builds a caret position graph for pRootNode */
+    /** Builds a caret position graph for pRootNode */
     SmCaretPosGraphBuildingVisitor( SmNode* pRootNode );
     void Visit( SmTableNode* pNode );
     void Visit( SmBraceNode* pNode );
diff --git a/starmath/source/makefile.mk b/starmath/source/makefile.mk
index ab8f39c..b211903 100644
--- a/starmath/source/makefile.mk
+++ b/starmath/source/makefile.mk
@@ -45,43 +45,43 @@ ENABLE_EXCEPTIONS=TRUE
 SMDLL=TRUE
 
 SRS2NAME =smres
-SRC2FILES = smres.src   \
-            symbol.src	\
+SRC2FILES = smres.src    \
+            symbol.src   \
             commands.src
 
-SLO1FILES =  \
+SLO1FILES = \
         $(SLO)$/accessibility.obj \
-        $(SLO)$/edit.obj \
+        $(SLO)$/edit.obj          \
         $(SLO)$/eqnolefilehdr.obj \
-        $(SLO)$/mathmlexport.obj \
-        $(SLO)$/mathmlimport.obj \
-        $(SLO)$/symbol.obj \
-        $(SLO)$/types.obj \
-        $(SLO)$/unodoc.obj \
-        $(SLO)$/action.obj \
-        $(SLO)$/cfgitem.obj \
-        $(SLO)$/config.obj \
-        $(SLO)$/dialog.obj \
-        $(SLO)$/document.obj \
-        $(SLO)$/format.obj \
-        $(SLO)$/mathtype.obj \
-        $(SLO)$/node.obj \
-        $(SLO)$/visitors.obj \
-		$(SLO)$/caret.obj \
-		$(SLO)$/cursor.obj \
-        $(SLO)$/parse.obj \
-        $(SLO)$/register.obj \
-        $(SLO)$/smdll.obj \
-        $(SLO)$/toolbox.obj \
-        $(SLO)$/typemap.obj \
-        $(SLO)$/smmod.obj \
-        $(SLO)$/utility.obj \
-        $(SLO)$/rect.obj \
-        $(SLO)$/unomodel.obj \
+        $(SLO)$/mathmlexport.obj  \
+        $(SLO)$/mathmlimport.obj  \
+        $(SLO)$/symbol.obj        \
+        $(SLO)$/types.obj         \
+        $(SLO)$/unodoc.obj        \
+        $(SLO)$/action.obj        \
+        $(SLO)$/cfgitem.obj       \
+        $(SLO)$/config.obj        \
+        $(SLO)$/dialog.obj        \
+        $(SLO)$/document.obj      \
+        $(SLO)$/format.obj        \
+        $(SLO)$/mathtype.obj      \
+        $(SLO)$/node.obj          \
+        $(SLO)$/visitors.obj      \
+        $(SLO)$/caret.obj         \
+        $(SLO)$/cursor.obj        \
+        $(SLO)$/parse.obj         \
+        $(SLO)$/register.obj      \
+        $(SLO)$/smdll.obj         \
+        $(SLO)$/toolbox.obj       \
+        $(SLO)$/typemap.obj       \
+        $(SLO)$/smmod.obj         \
+        $(SLO)$/utility.obj       \
+        $(SLO)$/rect.obj          \
+        $(SLO)$/unomodel.obj      \
         $(SLO)$/view.obj
 
-SLO2FILES =  \
-        $(SLO)$/register.obj  \
+SLO2FILES = \
+        $(SLO)$/register.obj \
         $(SLO)$/detreg.obj
 
 SLOFILES = \
commit 2f7504f41dcd179e73728827d52f91de30815f2c
Merge: a8e7635... 2d415c9...
Author: Michael Meeks <michael.meeks at novell.com>
Date:   Sat Oct 23 16:20:23 2010 +0100

    Merge branch 'formula' into intformulae
    
    Conflicts:
    	starmath/inc/node.hxx
    	starmath/source/edit.cxx
    	starmath/source/node.cxx
    	starmath/source/view.cxx

diff --cc starmath/inc/document.hxx
index c9896d4,2bcb97a..6cf3896
--- a/starmath/inc/document.hxx
+++ b/starmath/inc/document.hxx
@@@ -221,8 -227,17 +229,17 @@@ public
  
      virtual void SetVisArea (const Rectangle & rVisArea);
      virtual void SetModified(BOOL bModified);
+ 
+     /** Get a cursor for modifying this document
+      * @remarks Don't store this reference, a new cursor may be made...
+      */
+     SmCursor&   GetCursor();
+     /** True, if cursor have previously been requested and thus
+      * has some sort of position.
+      */
+     BOOL        HasCursor() { return pCursor != NULL; }
  };
  
 -
  #endif
  
 +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --cc starmath/inc/node.hxx
index 8c8213b,d5af727..04a954e
--- a/starmath/inc/node.hxx
+++ b/starmath/inc/node.hxx
@@@ -29,7 -28,29 +29,27 @@@
  #ifndef NODE_HXX
  #define NODE_HXX
  
 -
  #include <vector>
+ #include <fstream>
+ #include <iostream>
+ #include <stdio.h>
+ 
+ //My special assert macro
+ //TODO: replace this with DBG_ASSERT when this patch moves to production, can be done using search/replace
+ #define j_assert(cond, msg)     do{                                                             \
+                                     if(!(cond))                                                 \
+                                     {                                                           \
+                                         std::cerr<<"Failed assertion: "<<msg<<", at line ";     \
+                                         char* f = (char*)__FILE__;                              \
+                                         f += strlen(f);                                         \
+                                         do f--; while(*f != '/');                               \
+                                         do f--; while(*f != '/');                               \
+                                         do f--; while(*f != '/');                               \
+                                         fprintf(stderr, "%d in %s\n", __LINE__, f + 1);         \
+                                     }                                                           \
+                                 } while(false)
+ //TODO: Comment out below to disable dumpasdot
+ #define DEBUG_ENABLE_DUMPASDOT
  
  #include "parse.hxx"
  #include "types.hxx"
@@@ -92,8 -115,12 +114,9 @@@ class SmNode : public SmRec
      SmNodeType		eType;
      SmScaleMode		eScaleMode;
      RectHorAlign	eRectHorAlign;
 -    USHORT			nFlags,
 -                    nAttributes;
 -    BOOL			bIsPhantom,
 -                    bIsDebug;
 +    USHORT		nFlags, nAttributes;
 +    BOOL		bIsPhantom, bIsDebug;
+ 
 -    BOOL            bIsSelected;
  protected:
      SmNode(SmNodeType eNodeType, const SmToken &rNodeToken);
  
@@@ -267,9 -429,10 +425,9 @@@ public
  #ifdef SM_RECT_DEBUG
      using   SmRect::Draw;
  #endif
-     virtual void Draw(OutputDevice &rDev, const Point &rPosition) const;
  
 -
      void CreateTextFromNode(String &rText);
+     void Accept(SmVisitor* pVisitor);
  };
  
  
diff --cc starmath/source/document.cxx
index 7cf2c52,f3b3521..7029228
--- a/starmath/source/document.cxx
+++ b/starmath/source/document.cxx
@@@ -430,12 -431,13 +432,13 @@@ SfxItemPool& SmDocShell::GetEditEngineI
  
      if (!pEditEngineItemPool)
          GetEditEngine();
 -    DBG_ASSERT( pEditEngineItemPool, "EditEngineItemPool missing" );
 +    OSL_ENSURE( pEditEngineItemPool, "EditEngineItemPool missing" );
      return *pEditEngineItemPool;
  }
+ //TODO: Move to the top of the file...
+ #include "visitors.hxx"
  
- 
- void SmDocShell::Draw(OutputDevice &rDev, Point &rPosition)
+ void SmDocShell::DrawFormula(OutputDevice &rDev, Point &rPosition, BOOL bDrawSelection)
  {
      RTL_LOGFILE_CONTEXT( aLog, "starmath: SmDocShell::Draw" );
  
diff --cc starmath/source/edit.cxx
index a6ae06b,b6cf80b..3974065
--- a/starmath/source/edit.cxx
+++ b/starmath/source/edit.cxx
@@@ -426,11 -389,7 +390,6 @@@ void SmEditWindow::KeyInput(const KeyEv
      }
      else
      {
-         // Timer neu starten, um den Handler (auch bei laengeren Eingaben)
-         // moeglichst nur einmal am Ende aufzurufen.
-         aCursorMoveTimer.Start();
- 
-         OSL_ENSURE( pEditView, "EditView missing (NULL pointer)" );
 -        DBG_ASSERT( pEditView, "EditView missing (NULL pointer)" );
          if (!pEditView)
              CreateEditView();
          if ( !pEditView->PostKeyEvent(rKEvt) )
diff --cc starmath/source/node.cxx
index c6f7dcd,fddbde1..9af5027
mode 100644,100755..100644
--- a/starmath/source/node.cxx
+++ b/starmath/source/node.cxx
@@@ -3029,6 -2993,122 +2994,123 @@@ void SmBlankNode::Arrange(const OutputD
      SetWidth(nSpace);
  }
  
+ /**************************************************************************/
+ //Implementation of all accept methods for SmVisitor
+ 
+ void SmNode::Accept(SmVisitor*){
+     //This method is only implemented to avoid making SmNode abstract because an
+     //obscure copy constructor is used... I can't find it's implementation, and
+     //don't want to figure out how to fix it... If you want to, just delete this
+     //method, making SmNode abstract, and see where you can an problem with that.
+     j_assert(false, "SmNode should not be visitable!");
+ }
+ 
+ void SmTableNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmBraceNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmOperNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmAlignNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmAttributNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmFontNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmUnHorNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmBinHorNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmBinVerNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmSubSupNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmMatrixNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmPlaceNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmTextNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmSpecialNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
  
+ void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmBlankNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmErrorNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmLineNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmExpressionNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmRootNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmRectangleNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
+ 
+ void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
+     pVisitor->Visit(this);
+ }
  
 +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --cc starmath/source/parse.cxx
index d2536e2,0ef0898..e77d3be
mode 100644,100755..100644
--- a/starmath/source/parse.cxx
+++ b/starmath/source/parse.cxx
diff --cc starmath/source/view.cxx
index 5182d1b,31622bb..f06244b
mode 100644,100755..100644
--- a/starmath/source/view.cxx
+++ b/starmath/source/view.cxx
@@@ -230,72 -196,9 +197,9 @@@ void SmGraphicWindow::LoseFocus(
      }
  }
  
- void SmGraphicWindow::ShowCursor(BOOL bShow)
-     // shows or hides the formula-cursor depending on 'bShow' is TRUE or not
- {
-     BOOL  bInvert = bShow != IsCursorVisible();
- 
-     if (bInvert)
-         InvertTracking(aCursorRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW);
- 
-     SetIsCursorVisible(bShow);
- }
- 
- 
- void SmGraphicWindow::SetCursor(const SmNode *pNode)
- {
-     const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree();
- 
-     // get appropriate rectangle
-     Point aOffset (pNode->GetTopLeft() - pTree->GetTopLeft()),
-           aTLPos  (GetFormulaDrawPos() + aOffset);
-     aTLPos.X() -= pNode->GetItalicLeftSpace();
-     Size  aSize   (pNode->GetItalicSize());
-     Point aBRPos  (aTLPos.X() + aSize.Width(), aTLPos.Y() + aSize.Height());
- 
-     SetCursor(Rectangle(aTLPos, aSize));
- }
- 
- void SmGraphicWindow::SetCursor(const Rectangle &rRect)
-     // sets cursor to new position (rectangle) 'rRect'.
-     // The old cursor will be removed, and the new one will be shown if
-     // that is activated in the ConfigItem
- {
-     SmModule *pp = SM_MOD();
- 
-     if (IsCursorVisible())
-         ShowCursor(FALSE);		// clean up remainings of old cursor
-     aCursorRect = rRect;
-     if (pp->GetConfig()->IsShowFormulaCursor())
-         ShowCursor(TRUE);		// draw new cursor
- }
- 
- const SmNode * SmGraphicWindow::SetCursorPos(USHORT nRow, USHORT nCol)
-     // looks for a VISIBLE node in the formula tree with it's token at
-     // (or around) the position 'nRow', 'nCol' in the edit window
-     // (row and column numbering starts with 1 there!).
-     // If there is such a node the formula-cursor is set to cover that nodes
-     // rectangle. If not the formula-cursor will be hidden.
-     // In any case the search result is being returned.
- {
-     // find visible node with token at nRow, nCol
-     const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree(),
-                  *pNode = 0;
-     if (pTree)
-         pNode = pTree->FindTokenAt(nRow, nCol);
- 
-     if (pNode)
-         SetCursor(pNode);
-     else
-         ShowCursor(FALSE);
- 
-     return pNode;
- }
- 
- 
  void SmGraphicWindow::Paint(const Rectangle&)
  {
 -    DBG_ASSERT(pViewShell, "Sm : NULL pointer");
 +    OSL_ENSURE(pViewShell, "Sm : NULL pointer");
  
      SmDocShell &rDoc = *pViewShell->GetDoc();
      Point aPoint;
commit 2d415c9854fb6111ac924ee95044c7e1a931433c
Author: Jonas Finnemann Jensen <jopsen at gmail.com>
Date:   Sun Oct 3 16:16:52 2010 +0200

    Fixed issue with moving in/out of binom and stack.
    
    Rewrote handling of SmTableNode in SmCaretPosGraphBuildingVisitor,
    so that the special case where NTABLE is used as root node is handled
    in the constructor, and NSTACK and NBINOM is handled in the visiting
    method.

diff --git a/readme.md b/readme.md
index fc62851..16301fc 100644
--- a/readme.md
+++ b/readme.md
@@ -38,20 +38,19 @@ Project Status
 Easy
 ----
 1. Draw a non-blinking line under the the visual line that the caret is in, in `SmCaretDrawingVisitor`.
-2. `SmGraphicWindow::KeyInput` relies on comparison of `sal_Char`, a more generic way must be available for CTRL+c
+2. `SmGraphicWindow::KeyInput` relies on comparison of `sal_Char`, a better way must be available for CTRL+c
 3. Code style (missing spaces, linebreaks and a few renames)
 4. More documentation
 5. Replace `j_assert` with `DBG_ASSERT`
 
 Medium
 ------
-1. Caret can move into or out of binom, make this work again... (Probably a visitor issue).
-2. `SmCursor::InsertCol()` method for added columns to matrices should be implemented.
-3. `SmCursor` should support deletion of lines, rows, cols and sub-/superscripts.
-4. `SmCursor::InsertSubSup()` should wrap the body in a `SmBraceNode` if the body is an `SmOperNode`, `SmBinVerNode`, etc.
-5. Make caret in visual editor blink.
-6. Don't draw visual editor caret, when `SmGraphicWindow` doesn't have focus.
-7. When OpenOffice Math runs in standalone mode it centers the current formula, this is not nice for visual editing.
+1. `SmCursor::InsertCol()` method for added columns to matrices should be implemented.
+2. `SmCursor` should support deletion of lines, rows, cols and sub-/superscripts.
+3. `SmCursor::InsertSubSup()` should wrap the body in a `SmBraceNode` if the body is an `SmOperNode`, `SmBinVerNode`, etc.
+4. Make caret in visual editor blink.
+5. Don't draw visual editor caret, when `SmGraphicWindow` doesn't have focus.
+6. When OpenOffice Math runs in standalone mode it centers the current formula, this is not nice for visual editing.
 
 Complex
 -------
diff --git a/starmath/inc/visitors.hxx b/starmath/inc/visitors.hxx
index e2be49d..aeed3f6 100644
--- a/starmath/inc/visitors.hxx
+++ b/starmath/inc/visitors.hxx
@@ -276,31 +276,30 @@ private:
 /////////////////////////////// SmCaretPosGraphBuildingVisitor ////////////////////////////////
 
 
-/** A visitor for building a SmCaretPosGraph */
+/** A visitor for building a SmCaretPosGraph
+ *
+ * Visit invariant:
+ * Each pNode, except SmExpressionNode, SmBinHorNode and a few others, constitues an entry
+ * in a line. Consider the line entry "H", this entry creates one carat position, here
+ * denoted by | in "H|".
+ *
+ * Parameter variables:
+ *  The following variables are used to transfer parameters in to calls and results out
+ *  of calls.
+ *      pRightMost : SmCaretPosGraphEntry*
+ *
+ * Prior to a Visit call:
+ *  pRightMost: A pointer to right most position infront of the current line entry.
+ *
+ * After a Visit call:
+ *  pRightMost: A pointer to the right most position in the called line entry, if no there's
+ *              no caret positions in called line entry don't change this variable.
+ */
 class SmCaretPosGraphBuildingVisitor : public SmVisitor
 {
 public:
-    SmCaretPosGraphBuildingVisitor( ){
-        pRightMost  = NULL;
-        pGraph = new SmCaretPosGraph( );
-    }
-    /* Visit invariant:
-     * Each pNode, except SmExpressionNode, SmBinHorNode and a few others, constitues an entry
-     * in a line. Consider the line entry "H", this entry creates one carat position, here
-     * denoted by | in "H|".
-     *
-     * Parameter variables:
-     *  The following variables are used to transfer parameters in to calls and results out
-     *  of calls.
-     *      pRightMost : SmCaretPosGraphEntry*
-     *
-     * Prior to a Visit call:
-     *  pRightMost: A pointer to right most position infront of the current line entry.
-     *
-     * After a Visit call:
-     *  pRightMost: A pointer to the right most position in the called line entry, if no there's
-     *              no caret positions in called line entry don't change this variable.
-     */
+	/** Builds a caret position graph for pRootNode */
+    SmCaretPosGraphBuildingVisitor( SmNode* pRootNode );
     void Visit( SmTableNode* pNode );
     void Visit( SmBraceNode* pNode );
     void Visit( SmBracebodyNode* pNode );
diff --git a/starmath/source/cursor.cxx b/starmath/source/cursor.cxx
index 9637889..3b5022c 100644
--- a/starmath/source/cursor.cxx
+++ b/starmath/source/cursor.cxx
@@ -113,9 +113,7 @@ void SmCursor::BuildGraph(){
     pGraph = NULL;
 
     //Build the new graph
-    SmCaretPosGraphBuildingVisitor builder;
-    pTree->Accept(&builder);
-    pGraph = builder.Graph();
+    pGraph = SmCaretPosGraphBuildingVisitor(pTree).Graph();
 
     //Restore anchor and position pointers
     if(_anchor.IsValid() || _position.IsValid()){
diff --git a/starmath/source/visitors.cxx b/starmath/source/visitors.cxx
index f39825f..f7418c0 100644
--- a/starmath/source/visitors.cxx
+++ b/starmath/source/visitors.cxx
@@ -949,36 +949,65 @@ void SmSetSelectionVisitor::Visit( SmFontNode* pNode )
 
 /////////////////////////////// SmCaretPosGraphBuildingVisitor ////////////////////////////////
 
+SmCaretPosGraphBuildingVisitor::SmCaretPosGraphBuildingVisitor( SmNode* pRootNode ){
+    pRightMost  = NULL;
+    pGraph = new SmCaretPosGraph( );
+    //pRootNode should always be a table
+    j_assert( pRootNode->GetType( ) == NTABLE, "pRootNode must be a table node");
+    //Handle the special case where NTABLE is used a rootnode
+    if( pRootNode->GetType( ) == NTABLE ){
+        //Children are SmLineNodes
+        //Or so I thought... Aparently, the children can be instances of SmExpression
+        //especially if there's a error in the formula... So he we go, a simple work around.
+        SmNodeIterator it( pRootNode );
+        while( it.Next( ) ){
+            //There's a special invariant between this method and the Visit( SmLineNode* )
+            //Usually pRightMost may not be NULL, to avoid this pRightMost should here be
+            //set to a new SmCaretPos infront of it.Current( ), however, if it.Current( ) is
+            //an instance of SmLineNode we let SmLineNode create this position infront of
+            //the visual line.
+            //The argument for doing this is that we now don't have to worry about SmLineNode
+            //being a visual line composition node. Thus, no need for yet another special case
+            //in SmCursor::IsLineCompositionNode and everywhere this method is used.
+            if( it->GetType( ) != NLINE )
+                pRightMost = pGraph->Add( SmCaretPos( it.Current( ), 0 ) );
+            it->Accept( this );
+        }
+    }else
+        pRootNode->Accept(this);
+}
 
-//Needs special care:
-void SmCaretPosGraphBuildingVisitor::Visit( SmTableNode* pNode )
-{
-    //Children are SmLineNodes
-    //Or so I thought... Aparently, the children can be instances of SmExpression
-    //especially if there's a error in the formula... So he we go, a simple work around.
+void SmCaretPosGraphBuildingVisitor::Visit( SmLineNode* pNode ){
+    pRightMost = NULL;
     SmNodeIterator it( pNode );
     while( it.Next( ) ){
-        //There's a special invariant between this method and the Visit( SmLineNode* )
-        //Usually pRightMost may not be NULL, to avoid this pRightMost should here be
-        //set to a new SmCaretPos infront of it.Current( ), however, if it.Current( ) is
-        //an instance of SmLineNode we let SmLineNode create this position infront of
-        //the visual line.
-        //The argument for doing this is that we now don't have to worry about SmLineNode
-        //being a visual line composition node. Thus no need for yet another special case
-        //in SmCursor::IsLineCompositionNode and everywhere this method is used.
-        if( it->GetType( ) != NLINE )
+        if( !pRightMost )
             pRightMost = pGraph->Add( SmCaretPos( it.Current( ), 0 ) );
         it->Accept( this );
     }
 }
-void SmCaretPosGraphBuildingVisitor::Visit( SmLineNode* pNode ){
-    pRightMost = NULL;
+
+/** Build SmCaretPosGraph for SmTableNode
+ * This method covers cases where SmTableNode is used in a binom or stack,
+ * the special case where it is used as root node for the entire formula is
+ * handled in the constructor.
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmTableNode* pNode ){
+    SmCaretPosGraphEntry *left  = pRightMost,
+                         *right = pGraph->Add( SmCaretPos( pNode, 1) );
+    BOOL bIsFirst = TRUE;
     SmNodeIterator it( pNode );
-    while( it.Next( ) ){
-        if( !pRightMost )
-            pRightMost = pGraph->Add( SmCaretPos( it.Current( ), 0 ) );
+    while( it.Next() ){
+        pRightMost = pGraph->Add( SmCaretPos( it.Current(), 0 ), left);
+        if(bIsFirst)
+            left->SetRight(pRightMost);
         it->Accept( this );
+        pRightMost->SetRight(right);
+        if(bIsFirst)
+            right->SetLeft(pRightMost);
+        bIsFirst = FALSE;
     }
+    pRightMost = right;
 }
 
 /** Build SmCaretPosGraph for SmSubSupNode
@@ -1305,10 +1334,10 @@ void SmCaretPosGraphBuildingVisitor::Visit( SmBinVerNode* pNode )
     SmNode  *pNum   = pNode->GetSubNode( 0 ),
             *pDenom = pNode->GetSubNode( 2 );
 
-    SmCaretPosGraphEntry  *left,
-                        *right,
-                        *numLeft,
-                        *denomLeft;
+    SmCaretPosGraphEntry *left,
+                         *right,
+                         *numLeft,
+                         *denomLeft;
 
     //Set left
     left = pRightMost;
commit 11ddbed837eab61d2ff1dcbbf4c978c037a58ac9
Author: Jonas Finnemann Jensen <jopsen at gmail.com>
Date:   Fri Oct 1 22:04:50 2010 +0200

    Integrated the visual formula editor patch
    
    Ported the most recent version of the visual formula editor patch,
    to LibreOffice. This patch is not finished yet, see README for more
    information.

diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..fc62851
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,73 @@
+Visual Formula Editor for LibreOffice Math
+==========================================
+
+This repository facilitates development of a visual formula editor
+for LibreOffice/OpenOffice Math. This work was started by me (Jonas
+ Finnemann Jensen) during GSoC 2010 with Go-OO. During development
+this repository will be merged with libreoffice/writer on a random
+basis, but will hopefully at some point be merged back into
+LibreOffice.
+
+Description of the GSoC project that started this, be found
+[here](http://www.freedesktop.org/wiki/Software/ooo-build/SummerOfCode/2010/Jonas).
+Please note that this page will not be updated futher, but it does
+contain relevant information about the project and discusses the
+implementation strategy. I might also post progress updates on
+[my blog](http://jopsen.dk/blog/category/computer/openoffice/).
+But the project status will be documented and updated in this file, see below.
+
+Videos of the patch in action can be found at:
+
+ * [Development Preview 3 (13th of August)](http://www.youtube.com/watch?v=3foNqKYAlYY)
+ * [Development Preview 2 (26th of July)](http://www.youtube.com/watch?v=tELPgJIC1sg)
+ * [Development Preview 1 (21th of July)](http://www.youtube.com/watch?v=W8yXyDiIQPc)
+
+(All videos can also be found [here](http://jopsen.dk/downloads/GSoC2010/Videos/) better quality and various formats).
+
+If you're interested in more technical details the source (that I've
+written) is extensively documented with doxygen comments. You're also
+welcome to [contact me](http://jopsen.dk/blog/about/) with any
+questions, or wish to help hacking. My email is <jopsen at gmail.com>
+and I'm jopsen on #LibreOffice (if I'm online), there's also a finite
+set of [other options](http://jopsen.dk/blog/about/).
+
+Project Status
+==============
+*This is basically my todo list, so don't be surprised if things are not immediately obvious.*
+
+Easy
+----
+1. Draw a non-blinking line under the the visual line that the caret is in, in `SmCaretDrawingVisitor`.
+2. `SmGraphicWindow::KeyInput` relies on comparison of `sal_Char`, a more generic way must be available for CTRL+c
+3. Code style (missing spaces, linebreaks and a few renames)
+4. More documentation
+5. Replace `j_assert` with `DBG_ASSERT`
+
+Medium
+------
+1. Caret can move into or out of binom, make this work again... (Probably a visitor issue).
+2. `SmCursor::InsertCol()` method for added columns to matrices should be implemented.
+3. `SmCursor` should support deletion of lines, rows, cols and sub-/superscripts.
+4. `SmCursor::InsertSubSup()` should wrap the body in a `SmBraceNode` if the body is an `SmOperNode`, `SmBinVerNode`, etc.
+5. Make caret in visual editor blink.
+6. Don't draw visual editor caret, when `SmGraphicWindow` doesn't have focus.
+7. When OpenOffice Math runs in standalone mode it centers the current formula, this is not nice for visual editing.
+
+Complex
+-------
+1. `SmAlignNode` and `SmFontNode` are ignored by visual editor, figure out how these should work.
+2. Solve the flickering issue when drawing formulas (See e-mail)
+3. Make " a shortcut for creating an `SmTextNode` with `FNT_TEXT`, also check that `SmNodeToTextVisitor` supports this.
+4. `parse.cxx` merges multiple blanks into one `SmBlankNode`, the visual editor doesn't...
+
+Complex and non-essential
+-------------------------
+1. Global clipboard integration
+2. Support undo/redo with `UndoManager` integration
+3. Consider improving GUI for "Formula Elements"-dialog, most buttons work with visual editor
+4. Consider allowing users to enter commands in visual editor, by prefixing the command...
+5. Optimize things, for instance `SmCursor::AnnotateSelection()` is called way too many places...
+6. Improve handling of `MoveUp` and `MoveDown` in `SmCursor::Move`, `SmCaretPos2LineVisitor` might need improvement.
+
+Items are organized by complexity and importance, and I'm not sure everything needs to be addressed
+before release, but it constitutes a list of things I can work on.
diff --git a/starmath/inc/caret.hxx b/starmath/inc/caret.hxx
new file mode 100644
index 0000000..ae0f0fc
--- /dev/null
+++ b/starmath/inc/caret.hxx
@@ -0,0 +1,445 @@
+#ifndef CARET_H
+#define CARET_H
+
+#include "node.hxx"
+
+/** Representation of caret position with an equantion */
+struct SmCaretPos{
+    SmCaretPos(SmNode* selectedNode = NULL, int iIndex = 0) {
+        pSelectedNode = selectedNode;
+        Index = iIndex;
+    }
+    /** Selected node */
+    SmNode* pSelectedNode;
+    /** Index within the selected node
+     *
+     * 0: Position infront of a node
+     * 1: Position after a node or after first char in SmTextNode
+     * n: Position after n char in SmTextNode
+     *
+     * Notice how there's special cases for SmTextNode.
+     */
+    //TODO: Special cases for SmBlankNode is needed
+    //TODO: Consider forgetting about the todo above... As it's really unpleasent.
+    int Index;
+    /** True, if this is a valid caret position */
+    bool IsValid() { return pSelectedNode != NULL; }
+    bool operator!=(SmCaretPos pos) const {
+        return pos.pSelectedNode != pSelectedNode || Index != pos.Index;
+    }
+    bool operator==(SmCaretPos pos) const {
+        return pos.pSelectedNode == pSelectedNode && Index == pos.Index;
+    }
+    /** Get the caret position after pNode, regardless of pNode
+     *
+     * Gets the caret position following pNode, this is SmCaretPos(pNode, 1).
+     * Unless pNode is an instance of SmTextNode, then the index is the text length.
+     */
+    static SmCaretPos GetPosAfter(SmNode* pNode) {
+        if(pNode && pNode->GetType() == NTEXT)
+            return SmCaretPos(pNode, ((SmTextNode*)pNode)->GetText().Len());
+        return SmCaretPos(pNode, 1);
+    }
+};
+
+/** A line that represents a caret */
+class SmCaretLine{
+public:
+    SmCaretLine(long left = 0, long top = 0, long height = 0) {
+        _top = top;
+        _left = left;
+        _height = height;
+    }
+    long GetTop() const {return _top;}
+    long GetLeft() const {return _left;}
+    long GetHeight() const {return _height;}
+    long SquaredDistanceX(SmCaretLine line) const{
+        return (GetLeft() - line.GetLeft()) * (GetLeft() - line.GetLeft());
+    }
+    long SquaredDistanceX(Point pos) const{
+        return (GetLeft() - pos.X()) * (GetLeft() - pos.X());
+    }
+    long SquaredDistanceY(SmCaretLine line) const{
+        long d = GetTop() - line.GetTop();
+        if(d < 0)
+            d = (d * -1) - GetHeight();
+        else
+            d = d - line.GetHeight();
+        if(d < 0)
+            return 0;
+        return d * d;
+    }
+    long SquaredDistanceY(Point pos) const{
+        long d = GetTop() - pos.Y();
+        if(d < 0)
+            d = (d * -1) - GetHeight();
+        if(d < 0)
+            return 0;
+        return d * d;
+    }
+private:
+    long _top;
+    long _left;
+    long _height;
+};
+
+/////////////////////////////// SmCaretPosGraph////////////////////////////////
+
+/** An entry in SmCaretPosGraph */
+struct SmCaretPosGraphEntry{
+    SmCaretPosGraphEntry(SmCaretPos pos = SmCaretPos(),
+                       SmCaretPosGraphEntry* left = NULL,
+                       SmCaretPosGraphEntry* right = NULL){
+        CaretPos = pos;
+        Left = left;
+        Right = right;
+    }
+    /** Caret position */
+    SmCaretPos CaretPos;
+    /** Entry to the left visually */
+    SmCaretPosGraphEntry* Left;
+    /** Entry to the right visually */
+    SmCaretPosGraphEntry* Right;
+    void SetRight(SmCaretPosGraphEntry* right){
+        Right = right;
+    }
+    void SetLeft(SmCaretPosGraphEntry* left){
+        Left = left;
+    }
+};
+
+/** Define SmCaretPosGraph to be less than one page 4096 */
+#define SmCaretPosGraphSize   255
+
+class SmCaretPosGraph;
+
+/** Iterator for SmCaretPosGraph */
+class SmCaretPosGraphIterator{
+public:
+    SmCaretPosGraphIterator(SmCaretPosGraph* graph){
+        pGraph = graph;
+        nOffset = 0;
+        pEntry = NULL;
+    }
+    /** Get the next entry, NULL if none */
+    SmCaretPosGraphEntry* Next();
+    /** Get the current entry, NULL if none */
+    SmCaretPosGraphEntry* Current(){
+        return pEntry;
+    }
+    /** Get the current entry, NULL if none */
+    SmCaretPosGraphEntry* operator->(){
+        return pEntry;
+    }
+private:
+    /** Next entry to return */
+    int nOffset;
+    /** Current graph */
+    SmCaretPosGraph* pGraph;
+    /** Current entry */
+    SmCaretPosGraphEntry* pEntry;
+};
+
+
+/** A graph over all caret positions
+ * @remarks Graphs can only grow, entries cannot be removed!
+ */
+class SmCaretPosGraph{
+public:
+    SmCaretPosGraph(){
+        pNext = NULL;
+        nOffset = 0;
+    }
+    ~SmCaretPosGraph();
+    SmCaretPosGraphEntry* Add(SmCaretPosGraphEntry entry);
+    SmCaretPosGraphEntry* Add(SmCaretPos pos,
+                            SmCaretPosGraphEntry* left = NULL,
+                            SmCaretPosGraphEntry* right = NULL){
+        j_assert(pos.Index >= 0, "Index shouldn't be -1!");
+        return Add(SmCaretPosGraphEntry(pos, left, right));
+    }
+    /** Get an iterator for this graph */
+    SmCaretPosGraphIterator GetIterator(){
+        return SmCaretPosGraphIterator(this);
+    }
+    friend class SmCaretPosGraphIterator;
+private:
+    /** Next graph, to be used when this graph is full */
+    SmCaretPosGraph* pNext;
+    /** Next free entry in graph */
+    int nOffset;
+    /** Entries in this graph segment */
+    SmCaretPosGraphEntry Graph[SmCaretPosGraphSize];
+};
+
+/** \page visual_formula_editing Visual Formula Editing
+ * A visual formula editor allows users to easily edit formulas without having to learn and
+ * use complicated commands. A visual formula editor is a WYSIWYG editor. For OpenOffice Math
+ * this essentially means that you can click on the formula image, to get a caret, which you
+ * can move with arrow keys, and use to modify the formula by entering text, clicking buttons
+ * or using shortcuts.
+ *
+ * \subsection formula_trees Formula Trees
+ * A formula in OpenOffice Math is a tree of nodes, take for instance the formula
+ * "A + {B cdot C} over D", it looks like this
+ * \f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$. The tree for this formula
+ * looks like this:
+ *
+ * \dot
+ * digraph {
+ * labelloc = "t";
+ * label= "Equation: \"A  + {B  cdot C} over D\"";
+ * size = "9,9";
+ * n0 [label="SmTableNode (1)"];
+ * n0 -> n1 [label="0"];
+ * n1 [label="SmLineNode (2)"];
+ * n1 -> n2 [label="0"];
+ * n2 [label="SmExpressionNode (3)"];
+ * n2 -> n3 [label="0"];
+ * n3 [label="SmBinHorNode (4)"];
+ * n3 -> n4 [label="0"];
+ * n4 [label="SmTextNode: A (5)"];
+ * n3 -> n5 [label="1"];
+ * n5 [label="SmMathSymbolNode:  (6)"];
+ * n3 -> n6 [label="2"];
+ * n6 [label="SmBinVerNode (7)"];
+ * n6 -> n7 [label="0"];
+ * n7 [label="SmExpressionNode (8)"];
+ * n7 -> n8 [label="0"];
+ * n8 [label="SmBinHorNode (9)"];
+ * n8 -> n9 [label="0"];
+ * n9 [label="SmTextNode: B (10)"];
+ * n8 -> n10 [label="1"];
+ * n10 [label="SmMathSymbolNode: â‹… (11)"];
+ * n8 -> n11 [label="2"];
+ * n11 [label="SmTextNode: C (12)"];
+ * n6 -> n12 [label="1"];
+ * n12 [label="SmRectangleNode (13)"];
+ * n6 -> n13 [label="2"];
+ * n13 [label="SmTextNode: D (14)"];
+ * }
+ * \enddot
+ *
+ * The vertices are nodes, their label says what kind of node and the number in parentheses is
+ *  the identifier of the node (In practices a pointer is used instead of the id). The direction
+ *  of the edges tells which node is parent and which is child. The label of the edges are the
+ *  child node index number, given to SmNode::GetSubNode() of the parent to get the child node.
+ *
+ *
+ * \subsection visual_lines Visual Lines
+ *
+ * Inorder to do caret movement in visual lines, we need a definition of caret position and
+ * visual line. In a tree such as the above there are three visual lines. There's the outer most
+ * line, with entries such as
+ * \f$\mbox{A}\f$, \f$ + \f$ and \f$ \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$. Then there's
+ *  the numerator line of the fraction it has entries \f$ \mbox{B} \f$, \f$ \cdot \f$ and \f$ \mbox{C} \f$.
+ *  And last by not least there's the denominator line of the fraction it's only entry is \f$ \mbox{D} \f$.
+ *
+ * For visual editing it should be possible to place a caret on both sides of any line entry,
+ * consider a line entry a character or construction that in a line is treated as a character.
+ *  Imagine the caret is placed to the right of the plus sign (id: 6), now if user presses
+ * backspace this should delete the plus sign (id: 6), and if the user presses delete this
+ * should delete the entire fraction (id: 7). This is because the caret is in the outer most
+ * line where the fraction is considered a line entry.
+ *
+ * However, inorder to prevent users from accidentally deleting large subtrees, just because
+ * they logically placed there caret a in the wrong line, require that complex constructions
+ * such as a fraction is selected before it is deleted. Thus in this case it wouldn't be
+ * deleted, but only selected and then deleted if the user hit delete again. Anyway, this is
+ * slightly off topic for now.
+ *
+ * Important about visual lines is that they don't always have an SmExpressionNode as root
+ * and the entries in a visual line is all the nodes of a subtree ordered left to right that
+ *  isn't either an SmExpressionNode, SmBinHorNode or SmUnHorNode.
+ *
+ *
+ * \subsection caret_positions Caret Positions
+ *
+ * A caret position in OpenOffice Math is representated by an instance of SmCaretPos.
+ * That is a caret position is a node and an index related to this node. For most nodes the
+ * index 0, means caret is infront of this node, the index 1 means caret is after this node.
+ * For SmTextNode the index is the caret position after the specified number of characters,
+ * imagine an SmTextNode with the number 1337. The index 3 in such SmTextNode would mean a
+ * caret placed right before 7, e.g. "133|7".
+ *
+ * For SmExpressionNode, SmBinHorNode and SmUnHorNode the only legal index is 0, which means
+ *  infront of the node. Actually the index 0 may only because for the first caret position
+ * in a visual line. From the example above, consider the following subtree that constitutes
+ * a visual line:
+ *
+ * \dot
+ * digraph {
+ * labelloc = "t";
+ * label= "Subtree that constitutes a visual line";
+ * size = "7,5";
+ * n7 [label="SmExpressionNode (8)"];
+ * n7 -> n8 [label="0"];
+ * n8 [label="SmBinHorNode (9)"];
+ * n8 -> n9 [label="0"];
+ * n9 [label="SmTextNode: B (10)"];
+ * n8 -> n10 [label="1"];
+ * n10 [label="SmMathSymbolNode: â‹… (11)"];
+ * n8 -> n11 [label="2"];
+ * n11 [label="SmTextNode: C (12)"];
+ * }
+ * \enddot
+ * Here the caret positions are:
+ *
+ * <TABLE>
+ * <TR><TD><B>Caret position:</B></TD><TD><B>Example:</B></TD>
+ * </TR><TR>
+ *     <TD>{id: 8, index: 0}</TD>
+ *     <TD>\f$ \mid \mbox{C} \cdot \mbox{C} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 10, index: 1}</TD>
+ *     <TD>\f$ \mbox{C} \mid \cdot \mbox{C} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 11, index: 1}</TD>
+ *     <TD>\f$ \mbox{C} \cdot \mid \mbox{C} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 12, index: 1}</TD>
+ *     <TD>\f$ \mbox{C} \cdot \mbox{C} \mid \f$</TD>
+ * </TR><TR>
+ * </TABLE>
+ *
+ * Where \f$ \mid \f$ is used to denote caret position.
+ *
+ * With these exceptions included in the definition the id and index: {id: 11, index: 0} does
+ * \b not constitute a caret position in the given context. Note the method
+ * SmCaretPos::IsValid() does not check if this invariant holds true, but code in SmCaret,
+ * SmSetSelectionVisitor and other places depends on this invariant to hold.
+ *
+ *
+ * \subsection caret_movement Caret Movement
+ *
+ * As the placement of caret positions depends very much on the context within which a node
+ * appears it is not trivial to find all caret positions and determine which follows which.
+ * In OpenOffice Math this is done by the SmCaretPosGraphBuildingVisitor. This visitor builds
+ * graph (an instnce of SmCaretPosGraph) over the caret positions. For details on how this
+ * graph is build, and how new methods should be implemented see SmCaretPosGraphBuildingVisitor.
+ *
+ * The result of the SmCaretPosGraphBuildingVisitor is a graph over the caret positions in a
+ * formula, representated by an instance of SmCaretPosGraph. Each entry (instances of SmCaretPosGraphEntry)
+ * has a pointer to the entry to the left and right of itself. This way we can easily find
+ * the caret position to a right or left of a given caret position. Note each caret position
+ * only appears once in this graph.
+ *
+ * When searching for a caret position after a left click on the formula this map is also used.
+ * We simply iterate over all entries, uses the SmCaretPos2LineVisitor to find a line for each
+ * caret position. Then the distance from the click to the line is computed and we choose the
+ * caret position closest to the click.
+ *
+ * For up and down movement, we also iterator over all caret positions and use SmCaretPos2LineVisitor
+ * to find a line for each caret position. Then we compute the distance from the current
+ * caret position to every other caret position and chooses the one closest that is either
+ * above or below the current caret position, depending on wether we're doing up or down movement.
+ *
+ * This result of this approach to caret movement is that we have logically predictable
+ * movement for left and right, whilst leftclick, up and down movement depends on the sizes
+ * and placement of all node and may be less logically predictable. This solution also means
+ * that we only have one complex visitor generating the graph, imagine the nightmare if we
+ * had a visitor for movement in each direction.
+ *
+ * Making up and down movement independent of node sizes and placement wouldn't necessarily
+ * be a good thing either. Consider the formula \f$ \frac{1+2+3+4+5}{6} \f$, if the caret is
+ * placed as displayed here: \f$ \frac{1+2+3+4+5}{6 \mid} \f$, up movement should move to right
+ * after "3": \f$ \frac{1+2+3|+4+5}{6} \f$. However, such a move depends on the sizes and placement
+ * of all nodes in the fraction.
+ *
+ *
+ * \subsubsection caretpos_graph_example Example of Caret Position Graph
+ *
+ * If we consider the formula
+ * \f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$ from \ref formula_trees.
+ * It has the following caret positions:
+ *
+ * <TABLE>
+ * <TR>
+ *     <TD><B>Caret position:</B></TD>
+ *     <TD><B>Example:</B></TD>
+ * </TR><TR>
+ *     <TD>{id: 3, index: 0}</TD>
+ *     <TD>\f$ \mid\mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 5, index: 1}</TD>
+ *     <TD>\f$ \mbox{A}\mid + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 6, index: 1}</TD>
+ *     <TD>\f$ \mbox{A} + \mid \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 8, index: 0}</TD>
+ *     <TD>\f$ \mbox{A} + \frac{ \mid \mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 10, index: 1}</TD>
+ *     <TD>\f$ \mbox{A} + \frac{\mbox{B} \mid \cdot \mbox{C}}{\mbox{D}} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 11, index: 1}</TD>
+ *     <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mid \mbox{C}}{\mbox{D}} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 12, index: 1}</TD>
+ *     <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C} \mid}{\mbox{D}} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 14, index: 0}</TD>
+ *     <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mid \mbox{D}} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 14, index: 1}</TD>
+ *     <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D} \mid} \f$</TD>
+ * </TR><TR>
+ *     <TD>{id: 7, index: 1}</TD>
+ *     <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \mid \f$</TD>
+ * </TR>
+ * </TABLE>
+ *
+ * Below is a directed graph over the caret postions and how you can move between them.
+ * \dot
+ * digraph {
+ *     labelloc = "t";
+ *     label= "Caret Position Graph";
+ *     size = "4,6";
+ *     p0 [label = "{id: 3, index: 0}"];
+ *     p0 -> p1 [fontsize = 10.0, label = "right"];
+ *     p1 [label = "{id: 5, index: 1}"];
+ *     p1 -> p0 [fontsize = 10.0, label = "left"];
+ *     p1 -> p2 [fontsize = 10.0, label = "right"];
+ *     p2 [label = "{id: 6, index: 1}"];
+ *     p2 -> p1 [fontsize = 10.0, label = "left"];
+ *     p2 -> p3 [fontsize = 10.0, label = "right"];
+ *     p3 [label = "{id: 8, index: 0}"];
+ *     p3 -> p2 [fontsize = 10.0, label = "left"];
+ *     p3 -> p4 [fontsize = 10.0, label = "right"];
+ *     p4 [label = "{id: 10, index: 1}"];
+ *     p4 -> p3 [fontsize = 10.0, label = "left"];
+ *     p4 -> p5 [fontsize = 10.0, label = "right"];
+ *     p5 [label = "{id: 11, index: 1}"];
+ *     p5 -> p4 [fontsize = 10.0, label = "left"];
+ *     p5 -> p6 [fontsize = 10.0, label = "right"];
+ *     p6 [label = "{id: 12, index: 1}"];
+ *     p6 -> p5 [fontsize = 10.0, label = "left"];
+ *     p6 -> p9 [fontsize = 10.0, label = "right"];
+ *     p7 [label = "{id: 14, index: 0}"];
+ *     p7 -> p2 [fontsize = 10.0, label = "left"];
+ *     p7 -> p8 [fontsize = 10.0, label = "right"];
+ *     p8 [label = "{id: 14, index: 1}"];
+ *     p8 -> p7 [fontsize = 10.0, label = "left"];
+ *     p8 -> p9 [fontsize = 10.0, label = "right"];
+ *     p9 [label = "{id: 7, index: 1}"];
+ *     p9 -> p6 [fontsize = 10.0, label = "left"];
+ * }
+ * \enddot
+ */
+
+/* TODO: Write documentation about the following keywords:
+ *
+ * Visual Selections:
+ *  - Show images
+ *  - Talk about how the visitor does this
+ *
+ * Modifying a Visual Line:
+ *  - Find top most non-compo of the line (e.g. The subtree that constitutes a line)
+ *  - Make the line into a list
+ *  - Edit the list, add/remove/modify nodes
+ *  - Parse the list back into a subtree
+ *  - Insert the new subtree where the old was taken
+ */
+
+#endif /* CARET_H */
diff --git a/starmath/inc/cursor.hxx b/starmath/inc/cursor.hxx
new file mode 100644
index 0000000..d78aad7
--- /dev/null
+++ b/starmath/inc/cursor.hxx
@@ -0,0 +1,424 @@
+#ifndef SMCURSOR_H
+#define SMCURSOR_H
+
+#include "node.hxx"
+#include "caret.hxx"
+
+/** Factor to multiple the squared horizontical distance with
+ * Used for Up and Down movement.
+ */
+#define HORIZONTICAL_DISTANCE_FACTOR        10
+
+/** Enum of direction for movement */
+enum SmMovementDirection{
+    MoveUp,
+    MoveDown,
+    MoveLeft,
+    MoveRight
+};
+
+/** Enum of elements that can inserted into a formula */
+enum SmFormulaElement{
+    BlankElement,
+    FactorialElement,
+    PlusElement,
+    MinusElement,
+    CDotElement,
+    EqualElement,
+    LessThanElement,
+    GreaterThanElement
+};
+
+/** Bracket types that can be inserted */
+enum SmBracketType {
+    /** None brackets, left command "none" */
+    NoneBrackets,
+    /** Round brackets, left command "(" */
+    RoundBrackets,
+    /**Square brackets, left command "[" */
+    SquareBrackets,
+    /** Double square brackets, left command "ldbracket" */
+    DoubleSquareBrackets,
+    /** Line brackets, left command "lline" */
+    LineBrackets,
+    /** Double line brackets, left command "ldline" */
+    DoubleLineBrackets,
+    /** Curly brackets, left command "lbrace" */
+    CurlyBrackets,
+    /** Angle brackets, left command "langle" */
+    AngleBrackets,
+    /** Ceiling brackets, left command "lceil" */
+    CeilBrackets,
+    /** Floor brackets, left command "lfloor" */
+    FloorBrackets
+};
+
+/** A list of nodes */
+typedef std::list<SmNode*> SmNodeList;
+
+class SmDocShell;
+
+/** Formula cursor
+ *
+ * This class is used to represent a cursor in a formula, which can be used to manipulate
+ * an formula programmatically.
+ * @remarks This class is a very intimite friend of SmDocShell.
+ */
+class SmCursor{
+public:
+    SmCursor(SmNode* tree, SmDocShell* pShell){
+        //Initialize members
+        pTree           = tree;
+        anchor          = NULL;
+        position        = NULL;
+        pGraph          = NULL;
+        pDocShell       = pShell;
+        pClipboard      = NULL;
+        nEditSections   = 0;
+        //Build graph
+        BuildGraph();
+    }
+
+    ~SmCursor(){
+        SetClipboard();
+        if(pGraph)
+            delete pGraph;
+        pGraph = NULL;
+    }
+
+    /** Gets the anchor */
+    SmCaretPos GetAnchor(){ return anchor->CaretPos; }
+
+    /** Get position */
+    SmCaretPos GetPosition() { return position->CaretPos; }
+
+    /** True, if the cursor has a selection */
+    bool HasSelection() { return anchor != position; }
+
+    /** Move the position of this cursor */
+    void Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor = true);
+
+    /** Move to the caret position closet to a given point */
+    void MoveTo(OutputDevice* pDev, Point pos, bool bMoveAnchor = true);
+
+    /** Delete the current selection or do nothing */
+    void Delete();
+
+    /** Insert text at the current position */
+    void InsertText(XubString aString);
+
+    /** Insert an element into the formula */
+    void InsertElement(SmFormulaElement element);
+
+    /** Insert a command specified in commands.src*/
+    void InsertCommand(USHORT nCommand);
+
+    /** Insert command text translated into line entries at position
+     *
+     * Note: This method uses the parser to translate a command text into a
+     * tree, then it copies line entries from this tree into the current tree.
+     * Will not work for commands such as newline or ##, if position is in a matrix.
+     * This will work for stuff like "A intersection B". But stuff spaning multiple lines
+     * or dependent on the context which position is placed in will not work!
+     */
+    void InsertCommandText(String aCommandText);
+
+    /** Insert a special node created from aString
+     *
+     * Used for handling insert request from the "catalog" dialog.
+     * The provided string should be formatet as the desired command: %phi
+     * Note: this method ONLY supports commands defined in Math.xcu
+     *
+     * For more complex expressions use InsertCommandText, this method doesn't
+     * use SmParser, this means that it's faster, but not as strong.
+     */
+    void InsertSpecial(XubString aString);
+
+    /** Create sub-/super script
+     *
+     * If there's a selection, it will be move into the appropriate sub-/super scription
+     * of the node infront of it. If there's no node infront of position (or the selection),
+     * a sub-/super scription of a new SmPlaceNode will be made.
+     *
+     * If there's is an existing subscription of the node, the caret will be moved into it,
+     * and any selection will replace it.
+     */
+    void InsertSubSup(SmSubSup eSubSup);
+
+    /** Create a limit on an SmOperNode
+     *
+     * This this method only work if the caret is inside an SmOperNode, or to the right of one.
+     * Notice also that this method ignores any selection made.
+     *
+     * @param bMoveCaret If true that caret will be moved into the limit.
+     *
+     * @returns True, if the caret was in a context where this operation was possible.
+     */
+    BOOL InsertLimit(SmSubSup eSubSup, BOOL bMoveCaret = TRUE);
+
+    /** Insert a new row or newline
+     *
+     * Inserts a new row if position is in an matrix or stack command.
+     * Otherwise a newline is inserted if we're in a toplevel line.
+     *
+     * @returns True, if a new row/line could be inserted.
+     *
+     * @remarks If the caret is placed in a subline of a command that doesn't support
+     *          this operator the method returns FALSE, and doesn't do anything.
+     */
+    BOOL InsertRow();
+
+    /** Insert a fraction, use selection as numerator */
+    void InsertFraction();
+
+    /** Create brackets around current selection, or new SmPlaceNode */
+    void InsertBrackets(SmBracketType eBracketType);
+
+    /** Copy the current selection */
+    void Copy();
+    /** Cut the current selection */
+    void Cut(){
+        Copy();
+        Delete();
+    }
+    /** Paste the clipboard */
+    void Paste();
+
+    /** Returns true if more than one node is selected
+     *
+     * This method is used for implementing backspace and delete.
+     * If one of these causes a complex selection, e.g. a node with
+     * subnodes or similar, this should not be deleted imidiately.
+     */
+    bool HasComplexSelection();
+
+    /** Finds the topmost node in a visual line
+     *
+     * If MoveUpIfSelected is true, this will move up to the parent line
+     * if the parent of the current line is selected.
+     */
+    static SmNode* FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected = false);
+
+    /** Draw the caret */
+    void Draw(OutputDevice& pDev, Point Offset);
+
+private:
+    friend class SmDocShell;
+
+    SmCaretPosGraphEntry    *anchor,
+                            *position;
+    /** Formula tree */
+    SmNode* pTree;
+    /** Owner of the formula tree */
+    SmDocShell* pDocShell;
+    /** Graph over caret position in the current tree */
+    SmCaretPosGraph* pGraph;
+    /** Clipboard holder */
+    SmNodeList* pClipboard;
+
+    /** Returns a node that is selected, if any could be found */
+    SmNode* FindSelectedNode(SmNode* pNode);
+
+    /** Is this one of the nodes used to compose a line
+     *
+     * These are SmExpression, SmBinHorNode, SmUnHorNode etc.
+     */
+    static bool IsLineCompositionNode(SmNode* pNode);
+
+    /** Count number of selected nodes, excluding line composition nodes
+     *
+     * Note this function doesn't count line composition nodes and it
+     * does count all subnodes as well as the owner nodes.
+     *
+     * Used by SmCursor::HasComplexSelection()
+     */
+    int CountSelectedNodes(SmNode* pNode);
+
+    /** Convert a visual line to a list
+     *
+     * Note this method will delete all the nodes that will no longer be needed.
+     * that includes pLine!
+     * This method also deletes SmErrorNode's as they're just meta info in the line.
+     */
+    static SmNodeList* LineToList(SmStructureNode* pLine, SmNodeList* pList = new SmNodeList());
+
+    /** Clone a visual line to a list
+     *
+     * Doesn't clone SmErrorNode's these are ignored, as they are context dependent metadata.
+     */
+    static SmNodeList* CloneLineToList(SmStructureNode* pLine,
+                                       bool bOnlyIfSelected = false,
+                                       SmNodeList* pList = new SmNodeList());
+
+    /** Build pGraph over caret positions */
+    void BuildGraph();
+
+    /** Insert new nodes in the tree after position */
+    void InsertNodes(SmNodeList* pNewNodes);
+
+    /** tries to set position to a specific SmCaretPos
+     *
+     * @returns false on failure to find the position in pGraph.
+     */
+    bool SetCaretPosition(SmCaretPos pos, bool moveAnchor = false);
+
+    /** Set selected on nodes of the tree */
+    void AnnotateSelection();
+
+    /** Set the clipboard, and release current clipboard
+     *
+     * Call this method with NULL to reset the clipboard
+     * @remarks: This method takes ownership of pList.
+     */
+    void SetClipboard(SmNodeList* pList = NULL);
+
+    /** Clone list of nodes (creates a deep clone) */
+    static SmNodeList* CloneList(SmNodeList* pList);
+
+    /** Find an iterator pointing to the node in pLineList following aCaretPos
+     *
+     * If aCaretPos::pSelectedNode cannot be found it is assumed that it's infront of pLineList,
+     * thus not an element in pLineList. In this case this method returns an iterator to the
+     * first element in pLineList.
+     *
+     * If the current position is inside an SmTextNode, this node will be split in two, for this
+     * reason you should beaware that iterators to elements in pLineList may be invalidated, and
+     * that you should call PatchLineList() with this iterator if no action is taken.
+     */
+    static SmNodeList::iterator FindPositionInLineList(SmNodeList* pLineList, SmCaretPos aCaretPos);
+
+    /** Patch a line list after modification, merge SmTextNode, remove SmPlaceNode etc.
+     *
+     * @param pLineList The line list to patch
+     * @param aIter     Iterator pointing to the element that needs to be patched with it's previous.
+     *
+     * When the list is patched text nodes before and after aIter will be merged.
+     * If there's an, in the context, inappropriate SmPlaceNode before or after aIter it will also be
+     * removed.
+     *
+     * @returns A caret position equivalent to one selecting the node before aIter, the method returns
+     *          an invalid SmCaretPos to indicate placement infront of the line.
+     */
+     static SmCaretPos PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter);
+
+    /** Take selected nodes from a list
+     *
+     * Puts the selected nodes into pSelectedNodes, or if pSelectedNodes is NULL deletes
+     * the selected nodes.
+     * Note: If there's a selection inside an SmTextNode this node will be split, and it
+     * will not be merged when the selection have been taken. Use PatchLineList on the
+     * iterator returns to fix this.
+     *
+     * @returns An iterator pointing to the element following the selection taken.
+     */
+    static SmNodeList::iterator TakeSelectedNodesFromList(SmNodeList *pLineList,
+                                                         SmNodeList *pSelectedNodes = NULL);
+
+    /** Create an instance of SmMathSymbolNode usable for brackets */
+    static SmNode *CreateBracket(SmBracketType eBracketType, BOOL bIsLeft);
+
+    /** The number of times BeginEdit have been called
+     * Used to allow nesting of BeginEdit() and EndEdit() sections
+     */
+    int nEditSections;
+    /** Holds data for BeginEdit() and EndEdit() */
+    BOOL bIsEnabledSetModifiedSmDocShell;
+    /** Begin edit section where the tree will be modified */
+    void BeginEdit();
+    /** End edit section where the tree will be modified */
+    void EndEdit();
+    /** Finish editing
+     *
+     * Finishes editing by parsing pLineList and inserting back into pParent at nParentIndex.
+     * This method also rebuilts the graph, annotates the selection, sets caret position and
+     * Calls EndEdit.
+     *
+     * @remarks Please note that this method will delete pLineList, as the elements are taken.
+     *
+     * @param pLineList     List the constitutes the edited line.
+     * @param pParent       Parent to which the line should be inserted.
+     * @param nParentIndex  Index in parent where the line should be inserted.
+     * @param PosAfterEdit  Caret position to look for after rebuilding graph.
+     * @param pStartLine    Line to take first position in, if PosAfterEdit cannot be found,
+     *                      leave it NULL for pLineList.
+     */
+    void FinishEdit(SmNodeList* pLineList,
+                    SmStructureNode* pParent,
+                    int nParentIndex,
+                    SmCaretPos PosAfterEdit,
+                    SmNode* pStartLine = NULL);
+    /** Request the formula is repainted */
+    void RequestRepaint();
+};
+
+/** Minimalistic recursive decent SmNodeList parser
+ *
+ * This parser is used to take a list of nodes that constitues a line
+ * and parse them to a tree of SmBinHorNode, SmUnHorNode and SmExpression.
+ *
+ * Please note, this will not handle all kinds of nodes, only nodes that
+ * constitues and entry in a line.
+ *
+ * Below is an EBNF representation of the grammar used for this parser:
+ * \code
+ * Expression   -> Relation*
+ * Relation     -> Sum [(=|<|>|...) Sum]*
+ * Sum          -> Product [(+|-) Product]*
+ * Product      -> Factor [(*|/) Factor]*
+ * Factor       -> [+|-|-+|...]* Factor | Postfix
+ * Postfix      -> node [!]*
+ * \endcode
+ */
+class SmNodeListParser{
+public:
+    /** Create an instance of SmNodeListParser */
+    SmNodeListParser(){
+        pList = NULL;
+    }
+    /** Parse a list of nodes to an expression
+     *
+     * If bDeleteErrorNodes is true, old error nodes will be deleted.
+     */
+    SmNode* Parse(SmNodeList* list, bool bDeleteErrorNodes = true);
+    /** True, if the token is an operator */
+    static BOOL IsOperator(const SmToken &token);
+    /** True, if the token is a relation operator */
+    static BOOL IsRelationOperator(const SmToken &token);
+    /** True, if the token is a sum operator */
+    static BOOL IsSumOperator(const SmToken &token);
+    /** True, if the token is a product operator */
+    static BOOL IsProductOperator(const SmToken &token);
+    /** True, if the token is a unary operator */
+    static BOOL IsUnaryOperator(const SmToken &token);
+    /** True, if the token is a postfix operator */
+    static BOOL IsPostfixOperator(const SmToken &token);
+private:
+    SmNodeList* pList;
+    /** Get the current terminal */
+    SmNode* Terminal(){
+        if(pList->size() > 0)
+            return pList->front();
+        return NULL;
+    }
+    /** Move to next terminal */
+    SmNode* Next(){
+        pList->pop_front();
+        return Terminal();
+    }
+    /** Take the current terminal */
+    SmNode* Take(){
+        SmNode* pRetVal = Terminal();
+        Next();
+        return pRetVal;
+    }
+    SmNode* Expression();
+    SmNode* Relation();
+    SmNode* Sum();
+    SmNode* Product();
+    SmNode* Factor();
+    SmNode* Postfix();
+    SmNode* Error();
+};
+
+
+#endif /* SMCURSOR_H */
diff --git a/starmath/inc/document.hxx b/starmath/inc/document.hxx
index fb2aa9d..2bcb97a 100644
--- a/starmath/inc/document.hxx
+++ b/starmath/inc/document.hxx
@@ -46,6 +46,7 @@ class SmNode;
 class SfxMenuBarManager;
 class SfxPrinter;
 class Printer;
+class SmCursor;
 
 #define HINT_DATACHANGED	1004
 
@@ -106,6 +107,7 @@ class SmDocShell : public SfxObjectShell, public SfxListener
 {
     friend class SmPrinterAccess;
     friend class SmModel;
+    friend class SmCursor;
 
     String				aText;
     SmFormat   			aFormat;
@@ -123,6 +125,7 @@ class SmDocShell : public SfxObjectShell, public SfxListener
                         nBottomBorder;
     USHORT				nModifyCount;
     BOOL				bIsFormulaArranged;
+    SmCursor           *pCursor;
 
 
 
@@ -158,10 +161,15 @@ class SmDocShell : public SfxObjectShell, public SfxListener
     OutputDevice*       GetRefDev();
 
     BOOL				IsFormulaArranged() const { return bIsFormulaArranged; }
-    void				SetFormulaArranged(BOOL bVal) { bIsFormulaArranged = bVal; }
+    void		        SetFormulaArranged(BOOL bVal) { bIsFormulaArranged = bVal; }
 
     virtual BOOL		ConvertFrom(SfxMedium &rMedium);
 
+    /** Called whenever the formula is changed
+     * Deletes the current cursor
+     */
+    void                InvalidateCursor();
+
 public:
     TYPEINFO();
     SFX_DECL_INTERFACE(SFX_INTERFACE_SMA_START+1)
@@ -205,7 +213,7 @@ public:
     EditEngine & 	GetEditEngine();
     SfxItemPool &	GetEditEngineItemPool();
 
-    void		Draw(OutputDevice &rDev, Point &rPosition);
+    void		DrawFormula(OutputDevice &rDev, Point &rPosition, BOOL bDrawSelection = FALSE);
     Size		GetSize();
 
     void        Repaint();
@@ -219,6 +227,15 @@ public:
 
     virtual void SetVisArea (const Rectangle & rVisArea);
     virtual void SetModified(BOOL bModified);
+
+    /** Get a cursor for modifying this document
+     * @remarks Don't store this reference, a new cursor may be made...
+     */
+    SmCursor&   GetCursor();
+    /** True, if cursor have previously been requested and thus
+     * has some sort of position.
+     */
+    BOOL        HasCursor() { return pCursor != NULL; }
 };
 
 
diff --git a/starmath/inc/edit.hxx b/starmath/inc/edit.hxx
index 084501e..b8a7a90 100644
--- a/starmath/inc/edit.hxx
+++ b/starmath/inc/edit.hxx
@@ -68,15 +68,13 @@ class SmEditWindow : public Window, public DropTargetHelper
     ScrollBar	   *pHScrollBar,
                    *pVScrollBar;
     ScrollBarBox   *pScrollBox;
-    Timer			aModifyTimer,
-                    aCursorMoveTimer;
+    Timer			aModifyTimer;
     ESelection		aOldSelection;
 
     virtual void KeyInput(const KeyEvent& rKEvt);
     virtual void Command(const CommandEvent& rCEvt);
     DECL_LINK(MenuSelectHdl, Menu *);
     DECL_LINK(ModifyTimerHdl, Timer *);
-    DECL_LINK(CursorMoveTimerHdl, Timer *);
 
     virtual void DataChanged( const DataChangedEvent& );
     virtual void Resize();
diff --git a/starmath/inc/node.hxx b/starmath/inc/node.hxx
index 156a3c1..d5af727 100644
--- a/starmath/inc/node.hxx
+++ b/starmath/inc/node.hxx
@@ -1,7 +1,7 @@
 /*************************************************************************
  *
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- * 
+ *
  * Copyright 2000, 2010 Oracle and/or its affiliates.
  *
  * OpenOffice.org - a multi-platform office productivity suite
@@ -31,6 +31,26 @@
 
 
 #include <vector>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+
+//My special assert macro
+//TODO: replace this with DBG_ASSERT when this patch moves to production, can be done using search/replace
+#define j_assert(cond, msg)     do{                                                             \
+                                    if(!(cond))                                                 \
+                                    {                                                           \
+                                        std::cerr<<"Failed assertion: "<<msg<<", at line ";     \
+                                        char* f = (char*)__FILE__;                              \
+                                        f += strlen(f);                                         \
+                                        do f--; while(*f != '/');                               \
+                                        do f--; while(*f != '/');                               \
+                                        do f--; while(*f != '/');                               \
+                                        fprintf(stderr, "%d in %s\n", __LINE__, f + 1);         \
+                                    }                                                           \
+                                } while(false)
+//TODO: Comment out below to disable dumpasdot
+#define DEBUG_ENABLE_DUMPASDOT
 
 #include "parse.hxx"
 #include "types.hxx"
@@ -41,6 +61,7 @@
 #define ATTR_BOLD		0x0001
 #define ATTR_ITALIC		0x0002
 
+
 #define FNTSIZ_ABSOLUT	1
 #define FNTSIZ_PLUS 	2
 #define FNTSIZ_MINUS	3
@@ -59,6 +80,7 @@
 
 extern SmFormat	*pActiveFormat;
 
+class SmVisitor;
 class SmDocShell;
 class SmNode;
 class SmStructureNode;
@@ -77,7 +99,7 @@ enum SmNodeType
     NATTRIBUT,		NFONT,			NUNHOR,			NBINHOR,		NBINVER,
     NBINDIAGONAL,	NSUBSUP,		NMATRIX,		NPLACE,			NTEXT,
     NSPECIAL,		NGLYPH_SPECIAL,	NMATH,			NBLANK,			NERROR,
-    NLINE,          NEXPRESSION,    NPOLYLINE,      NROOT,          NROOTSYMBOL,    
+    NLINE,          NEXPRESSION,    NPOLYLINE,      NROOT,          NROOTSYMBOL,
     NRECTANGLE,     NVERTICAL_BRACE
 };
 
@@ -97,6 +119,8 @@ class SmNode : public SmRect
                     nAttributes;
     BOOL			bIsPhantom,
                     bIsDebug;
+
+    BOOL            bIsSelected;
 protected:
     SmNode(SmNodeType eNodeType, const SmToken &rNodeToken);
 
@@ -160,7 +184,6 @@ public:
 #ifdef SM_RECT_DEBUG
     using   SmRect::Draw;
 #endif
-    virtual void Draw(OutputDevice &rDev, const Point &rPosition) const;
 
     virtual void    GetAccessibleText( String &rText ) const;
     sal_Int32       GetAccessibleIndex() const { return nAccIndex; }
@@ -180,12 +203,128 @@ public:
 
     const SmNode *	FindTokenAt(USHORT nRow, USHORT nCol) const;
     const SmNode *	FindRectClosestTo(const Point &rPoint) const;
-};
 
+    /** Accept a visitor
+     * Calls the method for this class on the visitor
+     */
+    virtual void Accept(SmVisitor* pVisitor);
+
+    /** True if this node is selected */
+    BOOL IsSelected() const {return bIsSelected;}
+    void SetSelected(BOOL Selected = true) {bIsSelected = Selected;}
+
+#ifdef DEBUG_ENABLE_DUMPASDOT
+    /** The tree as dot graph for graphviz, usable for debugging
+     * Convert the output to a image using $ dot graph.gv -Tpng > graph.png
+     */
+    inline void DumpAsDot(std::ostream &out, String* label = NULL) const{
+        int id = 0;
+        DumpAsDot(out, label, -1, id, -1);
+    }
+#endif /* DEBUG_ENABLE_DUMPASDOT */
+
+    /** Get the parent node of this node */
+    SmStructureNode* GetParent(){ return aParentNode; }
+    /** Set the parent node */
+    void SetParent(SmStructureNode* parent){
+        aParentNode = parent;
+    }
+
+    /** Get the index of a child node
+     *
+     * Returns -1, if pSubNode isn't a subnode of this.
+     */
+    int IndexOfSubNode(SmNode* pSubNode){
+        USHORT nSize = GetNumSubNodes();
+        for(USHORT i = 0; i < nSize; i++)
+            if(pSubNode == GetSubNode(i))
+                return i;
+        return -1;
+    }
+    /** Set the token for this node */
+    void SetToken(SmToken& token){
+        aNodeToken = token;
+    }
+protected:
+    /** Sets parent on children of this node */
+    void ClaimPaternity(){
+        SmNode* pNode;
+        USHORT	nSize = GetNumSubNodes();
+        for (USHORT i = 0;	i < nSize;	i++)
+            if (NULL != (pNode = GetSubNode(i)))
+                pNode->SetParent((SmStructureNode*)this); //Cast is valid if we have children
+    }
+private:
+    SmStructureNode* aParentNode;
+    void DumpAsDot(std::ostream &out, String* label, int number, int& id, int parent) const;
+};
 
 ////////////////////////////////////////////////////////////////////////////////
 
+/** A simple auxiliary iterator class for SmNode
+ *
+ * Example of iteration over children of pMyNode:
+ * \code
+ *  //Node to iterate over:
+ *  SmNode* pMyNode = 0;// A pointer from somewhere
+ *  //The iterator:
+ *  SmNodeIterator it(pMyNode);
+ *  //The iteration:
+ *  while(it.Next()) {
+ *      it->SetSelected(true);
+ *  }
+ * \endcode
+ */
+class SmNodeIterator{
+public:
+    SmNodeIterator(SmNode* node, bool bReverse = false){
+        pNode = node;
+        nSize = pNode->GetNumSubNodes();
+        nIndex = 0;
+        pChildNode = NULL;
+        bIsReverse = bReverse;
+    }
+    /** Get the subnode or NULL if none */
+    SmNode* Next(){
+        while(!bIsReverse && nIndex < nSize){
+            if(NULL != (pChildNode = pNode->GetSubNode(nIndex++)))
+                return pChildNode;
+        }
+        while(bIsReverse && nSize > 0){
+            if(NULL != (pChildNode = pNode->GetSubNode((nSize--)-1)))
+                return pChildNode;
+        }
+        pChildNode = NULL;
+        return NULL;
+    }
+    /** Get the current child node, NULL if none */
+    SmNode* Current(){
+        return pChildNode;
+    }
+    /** Get the current child node, NULL if none */
+    SmNode* operator->(){
+        return pChildNode;
+    }
+private:
+    /** Current child */
+    SmNode* pChildNode;
+    /** Node whos children we're iterating over */
+    SmNode* pNode;
+    /** Size of the node */
+    USHORT nSize;
+    /** Current index in the node */
+    USHORT nIndex;
+    /** Move reverse */
+    bool bIsReverse;
+};
 
+////////////////////////////////////////////////////////////////////////////////
+
+/** Abstract baseclass for all composite node
+ *
+ * Subclasses of this class can have subnodes. Nodes that doesn't derivate from
+ * this class does not have subnodes.
+ */
 class SmStructureNode : public SmNode
 {
     SmNodeArray  aSubNodes;
@@ -212,12 +351,29 @@ public:
     virtual SmStructureNode & operator = ( const SmStructureNode &rNode );
 
     virtual void  GetAccessibleText( String &rText ) const;
+
+    void SetSubNode(USHORT nIndex, SmNode* pNode){
+        int size = aSubNodes.size();
+        if(size <= nIndex){
+            //Resize subnodes array
+            aSubNodes.resize(nIndex + 1);
+            //Set new slots to NULL
+            for(int i = size; i < nIndex+1; i++)
+                aSubNodes[i] = NULL;
+        }
+        aSubNodes[nIndex] = pNode;
+        ClaimPaternity();
+    }
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 
-
+/** Abstract base class for all visible node
+ *
+ * Nodes that doesn't derivate from this class doesn't draw anything, but their
+ * children.
+ */
 class SmVisibleNode : public SmNode
 {
 protected:
@@ -252,7 +408,10 @@ public:
 
 ////////////////////////////////////////////////////////////////////////////////
 
-
+/** Draws a rectangle
+ *
+ * Used for drawing the line in the OVER and OVERSTRIKE commands.
+ */
 class SmRectangleNode : public SmGraphicNode
 {
     Size  aToSize;
@@ -270,15 +429,19 @@ public:
 #ifdef SM_RECT_DEBUG
     using   SmRect::Draw;
 #endif
-    virtual void Draw(OutputDevice &rDev, const Point &rPosition) const;
-    
+
+
     void CreateTextFromNode(String &rText);
+    void Accept(SmVisitor* pVisitor);
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 
-
+/** Polygon line node
+ *
+ * Used to draw the slash of the WIDESLASH command by SmBinDiagonalNode.
+ */
 class SmPolyLineNode : public SmGraphicNode
 {
     Polygon		aPoly;
@@ -289,6 +452,8 @@ public:

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list