ImproveMathEquationEditor/Baseline AlignmentEquations/Editing equation from Writer

Back to Baseline alignment for equations

Setting size of letters in formula from Writer
This feature was suggested to me by Eric and basically what it should do, is that after highlighting text together with formula and setting the size of text, the size of letters in formula should be adjusted too.

Outline
What we need to do is:


 * Take the code executed when resizing text
 * Find a suitable place in the code to put the changes in
 * Find out which objects are selected
 * Change size of letters in the formula
 * Rearrange the formula and retrieve the baseline to align it

What do we want to change
First impulse was to change the size of formula when the size of text is changed, but then I was thinking whether we want to change something else with the text also. So I made a quick review what other font properties could be changed, but nothing exciting:

font name - in Math we can set fonts of different things, eg variables, functions (we can do it for all fonts if we wanted) font size - in Math it's base size font weight (bold) - don't want to do it (the formula would look really strange) italic (typeface) - don't want to do it (italics are usually always variables) underline (typeface) - can't do it as font in Math and definitely don't want to also align left/center/right - not sure whether you want to change, because if not centred fractions look strange

Code executed when resizing
Using few breakpoints at suitable places we can trace the code-flow:

Dispatcher creates the request and sends it to SfxShell::CallExec which is after that in the case of resizing text handled by SwBaseShell::ExecTxtCtrl. This handles cases like change of font, posture, weight and fontheight. Then it calls SwEditShell::SetAttr with the SfxItemSet that stores the change. This calls SwDoc::InsertItemSet which calls a function lcl_InsAttr. I spent a lot of time trying to figure out where precisely are the changes put in some SfxItemSet that stores format of a text, but didn't find it out, it's not needed anyway.

Get selected objects
Now our first proper task is to find references to objects that are selected by the cursor (we want svt::EmbeddedObjectRef). We can call SwFEShell::GetCrsr to get class SwPaM which stores the cursor position and the selected region. Firstly I imagined that I can go through all the positions in the selected region, ask for SwNode and if it's SwOleNode I can retrieve the value. After heavy tracing I started to be confused, I found out that the nodes are not in the right order, but nodes with objects are first and then there are text nodes. Also comparing two SwPosition does it by comparing node orders, which if it's true must cause a real chaos, because the nodes are not sorted as they should be.

I tried to write few times a function that would go through all nodes between the two positions (of selected region), but the SwOleNode always appeared to be outside of the region between the selected text nodes, although the formula really was inside the selected (highlighted) text.

Then I tried to use methods like SwFEShell::IsObjectSelected which gets SdrMarkList with marked objects. This seems not to work for formulas, simply there are no items in the list if I have only formulas selected. Apparently the MarkList is used for some other objects.

Now I'm reviewing SwPaM::Find which has a few definitions implemented to find different things in selected region. Hopefully this will be fruitful.
 * Sadly, the way Find methods are searching is precisely going through all nodes from the one where is the start position of cursor till the one where ends the selection of the text. Which means it misses all SwOleNode, because they are stored before all text nodes in the node array. This is really confusing way of storing the nodes, because now this way we can't find out whether some ole node lies inside the selected text or not.

So I have to find a new way how to say whether the object is selected or not. The whole system looks strange...

After a long tracing I found out that the actual position of the object is stored in SfxItemSet of its SwFmt. So all that is needed to be done, is take all SwOleNode and search through all of them for which ones are in the selection. It's not nice that we have to look at each one, but there is no other way to do this, because the position is stored just in the SfxItemSet. But maybe the ole nodes are sorted according to position, if so, we could find which ones are selected in logarithmic time. Anyway if someone inserted so many objects in the document that searching through all of them would be time consuming, I think that the document would be too big to store anywhere, so this is not an issue.

Change size in the formula
My investigation yielded very pleasant result which is that you just need to set it using SmModel::_setPropertyValues (well, you call PropertySetHelper::setPropertyValue which is parent of SmModel). There is property called BaseFontHight which has to be changed.

Rearranging the formula
This is done automatically when a property value is set.

Suitable place for code
I'll try to contact someone who understands Writer better, but meanwhile I decided to implement the method that would change the size of the formula to SwFEShell and it will be called by SwBaseShell::ExecTxtCtrl which handles the case of editing text height. I was thinking of some other, more general implementation that could be used later, for instance sending the request to change text hight not just to SwBaseShell, but to all shells of OLE objects in selection. But then, this might be unnecessary generalization.

Setting size of the formula when inserted
Once we can change the size of formula by highlighting and setting the size, it would be nice if the size would be set automatically when inserting formula according to the text hight. For this the only thing that we need is to get to the font hight at the position we are inserting the formula.

SwCharFmt
First thing is to check Character_Styles_and_Automatic_Character_Styles where is some diagram about styles, so I tried to get to the SfxItemSet and get SvxFontHeightItem from there.

the code went something like:

const SwPosition *pPos = (((SwOLENode&)rNd).GetFrm)->GetUpper->GetFmt->GetAnchor.GetCntntAnchor; //this gets the position at which is the formula const SwNode &rNode = pPos->nNode.GetNode; const SwIndex &rIdx = pPos->nContent;

const SwTxtAttr *pTxtAttr = ((SwTxtNode&)rNode).GetTxtAttr( rIdx, RES_TXTATR_CHARFMT ); const SwCharFmt *pCharFmt = pTxtAttr->GetCharFmt.GetCharFmt; pHItem = &pCharFmt->GetSize;

but here the RES_TXT_CHARFMT couldn't be find, but I could see that RES_TXTATR_AUTOFMT is there, so I got that one instead. But then getting SwCharFmt failed because pTxtAttr->GetCharFmt got SwFmtCharFmt, but this didn't have pRegisteredIn correct which is essential to get to SwCharFmt. So I thought I have to go through SwFmtAutoFmt, but I didn't know how to use it.

SwFmtColl
Tracing around I saw that SwFmtColl should store the value. But there most of methods that are getting it uses SwCntntNode::GetFmtColl which gets it, but it looks that it's the same for whole node, which means that it can't store the hight, as the hight can be different at different places of the same text node.

FontSizeBox
Then I tried to see how the FontSizeBox gets the value that it displays, it certainly has to get it from the position the cursor is at. But it was retrieved from some cache and I really couldn't track, how and what set it there. I just tried to put breakpoint on ctor of SvxFontHeightItem, thinking that something can use it to pass on the value, but it wasn't hit.

SwTxtNode::GetAttr
Finally I found precisely what I wanted SwTxtNode::GetAttr gives the format and you can specify in which area or at which position. I found it putting breakpoint to SfxItemSet::Get if the nWhich is RES_CHRATR_FONTSIZE and backtracking for the caller. I tried this before, but I used conditional breakpoint, which somehow slowed down the gdb to unbearable speed. I don't know how it evaluates the condition, but as the SfxItemSet::Get is called very many times, it couldn't handle it. So this time, I just redefined the method with a condition I wanted, ie I typed if "(nWhich == RES_CHRATR_FONTSIZE) int i=1;" and I could place a breakpoint at the int i=1.

FOREACHPAM
In one of my methods I was writing I use FOREACHPAM_START and FOREACHPAM_END, I was getting strange errors. Then I found out that this macros are defined two times, obviously in specific file just once, but I thought I'm using the other definition. Just type FOREACHPAM_START into grok and you'll see. Not sure whether the best idea to have macros defined twice so someone can mess it up...