ImproveMathEquationEditor/Baseline AlignmentEquations/Adding formulas using highlighting
From Wiki.ooo4kids.org
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
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.