ImproveMathEquationEditor/Baseline AlignmentEquations/Adding formulas using highlighting

From Wiki.ooo4kids.org

Jump to: navigation, search


Adding formulas using highlighting

Back to Baseline alignment for equations

Misalignment

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.

Adding borders

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.

Adding Borders to copied formula

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.
Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox