ImproveMathEquationEditor/Baseline AlignmentEquations/Adding formulas using highlighting

Back to Baseline alignment for equations

Description
After implementing code for retrieving baseline, we got to a bug when inserting formulas using highlighting. Writing text, highlighting it and inserting formula ( Insert -> Object -> Formula ) aligns it correctly, but doing this second time where the second formula has different nBaseline (usually different hight of the object is enough) aligns the second correctly, but misaligns the first.

Analysing the problem
We begun by heavy tracing. Clearly the baseline is retrieved from starmath, because the second equation is aligned by it. The problem is that the same value is applied to the first equation too. The reason is this:

When inserting a new equation SwWrtShell::InsertOLEObject is called to insert it. It needs to get SwFlyFrmFmt (format of the object) which happens in (going through few other methods) SwDoc::Insert which gets the format from pFrmFmt = GetFrmFmtFromPool( nId ); nId is set to represent formula. Next in SwDoc::Insert is retrieving baseline which takes SwFmtVertOrient from pFrmFmt, sets the baseline and puts it back. Now what happens is that in all the pFrmFmt for different formulas is the same SwAttrPool and getting SwFmtVertOrient gives back the same instance of SwFmtVertOrient, which means that changing it for the second equation, changes it for the first one.

What remains a mystery to me (hopefully not for long) is that if the SwAttrPool is the same for all equations, how come that after moving one of them it does not move all of them. I begun tracing to see that really once you start moving the equation (or you open edit and close it) the SwFmtVertOrient got by SwAttrSet (which is different for all equations) from SwAttrPool (which is the same for all equations) is different, therefore not all formulas are moved, but only one. (To clarify again the paragraph before... doing this in SwDoc::Insert leads to getting the same SwFmtVertOrient for all equations that has not yet been edited and thus aligning all of them at once (which looks like aligning their top to the same line))

Solution
It suddenly occurred to me that I'm stupid and that the problem might lie in retrieving the baseline in SwDoc::Insert, where the SwFlyFrmFmt is just being constructed but not yet finished. Therefore I moved retrieving the baseline to SwWrtShell::InsertOLEObject where it is more logical and what is more, it works fine. :) This way I also could reuse some code, calling a method that sets it also for already created formulas.

Description
When creating a formula, highlighting and inserting it, it doesn't behave as it should when adding borders to it. Formulas should be not resizable objects, ie adding border won't change the size of the object itself. But when you insert formula this way the object is resized, unlike when you add the formula normal way.

Analysing the problem
Method SwWrtShell::CalcAndSetScale takes care for non-resizable objects being not resized, ie it sets the size back to the original if something before resizes it (which happens when adding borders). Before resizing the object back, there is a code:

00771    SfxInPlaceClient* pCli = GetView.FindIPClient( xObj.GetObject, &GetView.GetEditWin ); 00772    if ( !pCli ) 00773    { 00774         if ( (embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY & nMisc) || bLinkingChart 00775             // TODO/LATER: ResizeOnPrinterChange 00776              //|| SVOBJ_MISCSTATUS_RESIZEONPRINTERCHANGE & xObj->GetMiscStatus 00777              ) 00778        { 00779             pCli = new SwOleClient( &GetView, &GetView.GetEditWin, xObj ); 00780        } 00781         else 00782            return; 00783    }

It tries to find IPClient. If there is no one it creates it in some case, but not in case of formulas, therefore the method is terminated, calling return.

Now if we want the method to work properly we have to ensure that IPClient is created. We have the option of creating it here, but we would have to check specifically for formula. Or we could create it when the object is created, which is IMHO nicer, because we could prevent other problems with this. The way it's created when inserting formula normally (not highlighting any text) is in SwWrtShell::InsertObject

00496            if( InsertOleObject( xObj ) && bActivate && bDoVerb ) 00497            { 00498                 SfxInPlaceClient* pClient = GetView.FindIPClient( xObj.GetObject, &GetView.GetEditWin ); 00499                if ( !pClient ) 00500                { 00501                     pClient = new SwOleClient( &GetView, &GetView.GetEditWin, xObj ); 00502                    SetCheckForOLEInCaption( TRUE ); 00503                } 00504 00505                 if ( xObj.GetViewAspect == embed::Aspects::MSOLE_ICON ) 00506                { 00507                     SwRect aArea = GetAnyCurRect( RECT_FLY_PRT_EMBEDDED, 0, xObj.GetObject ); 00508                    aArea.Pos += GetAnyCurRect( RECT_FLY_EMBEDDED, 0, xObj.GetObject ).Pos; 00509                    MapMode aMapMode( MAP_TWIP ); 00510                    Size aSize = xObj.GetSize( &aMapMode ); 00511                    aArea.Width( aSize.Width ); 00512                    aArea.Height( aSize.Height ); 00513                    RequestObjectResize( aArea, xObj.GetObject ); 00514                } 00515                 else 00516                    CalcAndSetScale( xObj ); 00517 00518                //#50270# Error brauchen wir nicht handeln, das erledigt das 00519                //DoVerb in der SfxViewShell 00520                pClient->DoVerb( SVVERB_SHOW ); 00521 00522                // TODO/LATER: set document name - should be done in Client 00523                //if ( !ERRCODE_TOERROR( nErr ) ) 00524                //    xIPObj->SetDocumentName( GetView.GetDocShell->GetTitle ); 00525            }

And the difference comes from InsertOleObject( xObj ) which returns false exactly for Starmath formulas that are created using highlighting. The idea is to create IPClient and call pClient->DoVerb( SVVERB_SHOW ); so that the object is activated.

Solution
At this place we can simply proceed so that also for Starmath formulas inserted using highlighting the IpClient will be created only the pClient->DoVerb will not be called and so the Starmath formula editor won't be triggered.

Description
If you copy-paste formula and then try to add borders to it, the formula is resized instead of keeping its object rectangle and adding borders onto it.

Analysing the problem
This problem is exactly the same as the one described above, that's why I added it here. Basically what happens is, adding borders to an object firstly resizes it, but then in SwWrtShell::CalcAndSetScale the original size is restored in the case of non-resizable objects (using RequestObjectResize). CalcAndSetScale needs InPlaceClient to set the scale and if it doesn't find it, it stops. Now if you copy a formula the InPlaceClient is not created, therefore CalcAndSetScale ends before it restores original size.

The code where it stops: 00771    SfxInPlaceClient* pCli = GetView.FindIPClient( xObj.GetObject, &GetView.GetEditWin ); 00772    if ( !pCli ) 00773    { 00774         if ( (embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY & nMisc) || bLinkingChart 00775             // TODO/LATER: ResizeOnPrinterChange 00776              //|| SVOBJ_MISCSTATUS_RESIZEONPRINTERCHANGE & xObj->GetMiscStatus 00777              ) 00778        { 00779             pCli = new SwOleClient( &GetView, &GetView.GetEditWin, xObj ); 00780        } 00781         else 00782            return; 00783    }

Now we have two options:

We can look at the code above and add a condition such that the SwOleClient would be created also in cases like this. The question here is which cases exactly. I would say that we probably need the IPClient for all non-resizable objects, because this method has to continue to set back the original size.

Or, we can deal with it as I did in the case above ( adding borders to formula created using highlighting), ie we can trace to find where the object is created and create IPClient for it at that place.
 * I already started some tracing, there are lots of cases that are dealt with when copying and somehow it doesn't use InsertOleObject (there is place where it could be, but it doesn't hit it in my case) when creating the new object, but I couldn't figure out where precisely it creates it.