Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*
3 : : * Version: MPL 1.1 / GPLv3+ / LGPLv3+
4 : : *
5 : : * The contents of this file are subject to the Mozilla Public License Version
6 : : * 1.1 (the "License"); you may not use this file except in compliance with
7 : : * the License. You may obtain a copy of the License at
8 : : * http://www.mozilla.org/MPL/
9 : : *
10 : : * Software distributed under the License is distributed on an "AS IS" basis,
11 : : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : : * for the specific language governing rights and limitations under the
13 : : * License.
14 : : *
15 : : * The Initial Developer of the Original Code is
16 : : * Jonas Finnemann Jensen <jopsen@gmail.com>
17 : : * Portions created by the Initial Developer are Copyright (C) 2010 the
18 : : * Initial Developer. All Rights Reserved.
19 : : *
20 : : * Contributor(s): Jonas Finnemann Jensen <jopsen@gmail.com>
21 : : *
22 : : * Alternatively, the contents of this file may be used under the terms of
23 : : * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
24 : : * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
25 : : * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
26 : : * instead of those above.
27 : : */
28 : : #include "cursor.hxx"
29 : : #include "parse.hxx"
30 : : #include "visitors.hxx"
31 : : #include "document.hxx"
32 : : #include "view.hxx"
33 : : #include "accessibility.hxx"
34 : : #include <comphelper/string.hxx>
35 : :
36 : 0 : void SmCursor::Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor){
37 : 0 : SmCaretPosGraphEntry* NewPos = NULL;
38 [ # # # # ]: 0 : switch(direction){
39 : : case MoveLeft:
40 : : {
41 : 0 : NewPos = position->Left;
42 : : OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
43 : 0 : }break;
44 : : case MoveRight:
45 : : {
46 : 0 : NewPos = position->Right;
47 : : OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
48 : 0 : }break;
49 : : case MoveUp:
50 : : //Implementation is practically identical to MoveDown, except for a single if statement
51 : : //so I've implemented them together and added a direction == MoveDown to the if statements.
52 : : case MoveDown:
53 : : {
54 [ # # ]: 0 : SmCaretLine from_line = SmCaretPos2LineVisitor(pDev, position->CaretPos).GetResult(),
55 : 0 : best_line, //Best approximated line found so far
56 : 0 : curr_line; //Current line
57 : 0 : long dbp_sq = 0; //Distance squared to best line
58 : 0 : SmCaretPosGraphIterator it = pGraph->GetIterator();
59 [ # # ][ # # ]: 0 : while(it.Next()){
60 : : //Reject it if it's the current position
61 [ # # ]: 0 : if(it->CaretPos == position->CaretPos) continue;
62 : : //Compute caret line
63 [ # # ]: 0 : curr_line = SmCaretPos2LineVisitor(pDev, it->CaretPos).GetResult();
64 : : //Reject anything above if we're moving down
65 [ # # ][ # # ]: 0 : if(curr_line.GetTop() <= from_line.GetTop() && direction == MoveDown) continue;
[ # # ]
66 : : //Reject anything below if we're moving up
67 [ # # ][ # # ]: 0 : if(curr_line.GetTop() + curr_line.GetHeight() >= from_line.GetTop() + from_line.GetHeight()
[ # # ]
68 : 0 : && direction == MoveUp) continue;
69 : : //Compare if it to what we have, if we have anything yet
70 [ # # ]: 0 : if(NewPos){
71 : : //Compute distance to current line squared, multiplied with a horizontial factor
72 : 0 : long dp_sq = curr_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
73 : 0 : curr_line.SquaredDistanceY(from_line);
74 : : //Discard current line if best line is closer
75 [ # # ]: 0 : if(dbp_sq <= dp_sq) continue;
76 : : }
77 : : //Take current line as the best
78 : 0 : best_line = curr_line;
79 : 0 : NewPos = it.Current();
80 : : //Update distance to best line
81 : 0 : dbp_sq = best_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
82 : 0 : best_line.SquaredDistanceY(from_line);
83 : : }
84 : 0 : }break;
85 : : default:
86 : : OSL_FAIL("Movement direction not supported!");
87 : : }
88 [ # # ]: 0 : if(NewPos){
89 : 0 : position = NewPos;
90 [ # # ]: 0 : if(bMoveAnchor)
91 : 0 : anchor = NewPos;
92 : 0 : RequestRepaint();
93 : : }
94 : 0 : }
95 : :
96 : 0 : void SmCursor::MoveTo(OutputDevice* pDev, Point pos, bool bMoveAnchor){
97 : 0 : SmCaretLine best_line, //Best line found so far, when iterating
98 : 0 : curr_line; //Current line, when iterating
99 : 0 : SmCaretPosGraphEntry* NewPos = NULL;
100 : 0 : long dp_sq = 0, //Distance to current line squared
101 : 0 : dbp_sq = 1; //Distance to best line squared
102 : 0 : SmCaretPosGraphIterator it = pGraph->GetIterator();
103 [ # # ][ # # ]: 0 : while(it.Next()){
104 : : OSL_ENSURE(it->CaretPos.IsValid(), "The caret position graph may not have invalid positions!");
105 : : //Compute current line
106 [ # # ]: 0 : curr_line = SmCaretPos2LineVisitor(pDev, it->CaretPos).GetResult();
107 : : //If we have a position compare to it
108 [ # # ]: 0 : if(NewPos){
109 : : //Compute squared distance to current line
110 : 0 : dp_sq = curr_line.SquaredDistanceX(pos) + curr_line.SquaredDistanceY(pos);
111 : : //If best line is closer, reject current line
112 [ # # ]: 0 : if(dbp_sq <= dp_sq) continue;
113 : : }
114 : : //Accept current position as the best
115 : 0 : best_line = curr_line;
116 : 0 : NewPos = it.Current();
117 : : //Update distance to best line
118 : 0 : dbp_sq = best_line.SquaredDistanceX(pos) + best_line.SquaredDistanceY(pos);
119 : : }
120 [ # # ]: 0 : if(NewPos){
121 : 0 : position = NewPos;
122 [ # # ]: 0 : if(bMoveAnchor)
123 : 0 : anchor = NewPos;
124 [ # # ]: 0 : RequestRepaint();
125 : : }
126 : 0 : }
127 : :
128 : 0 : void SmCursor::BuildGraph(){
129 : : //Save the current anchor and position
130 : 0 : SmCaretPos _anchor, _position;
131 : : //Release pGraph if allocated
132 [ # # ]: 0 : if(pGraph){
133 [ # # ]: 0 : if(anchor)
134 : 0 : _anchor = anchor->CaretPos;
135 [ # # ]: 0 : if(position)
136 : 0 : _position = position->CaretPos;
137 [ # # ][ # # ]: 0 : delete pGraph;
138 : : //Reset anchor and position as they point into an old graph
139 : 0 : anchor = NULL;
140 : 0 : position = NULL;
141 : : }
142 : 0 : pGraph = NULL;
143 : :
144 : : //Build the new graph
145 [ # # ]: 0 : pGraph = SmCaretPosGraphBuildingVisitor(pTree).Graph();
146 : :
147 : : //Restore anchor and position pointers
148 [ # # ][ # # ]: 0 : if(_anchor.IsValid() || _position.IsValid()){
[ # # ]
149 : 0 : SmCaretPosGraphIterator it = pGraph->GetIterator();
150 [ # # ][ # # ]: 0 : while(it.Next()){
151 [ # # ]: 0 : if(_anchor == it->CaretPos)
152 : 0 : anchor = it.Current();
153 [ # # ]: 0 : if(_position == it->CaretPos)
154 : 0 : position = it.Current();
155 : : }
156 : : }
157 : : //Set position and anchor to first caret position
158 : 0 : SmCaretPosGraphIterator it = pGraph->GetIterator();
159 [ # # ]: 0 : if(!position)
160 [ # # ]: 0 : position = it.Next();
161 [ # # ]: 0 : if(!anchor)
162 : 0 : anchor = position;
163 : :
164 : : OSL_ENSURE(position->CaretPos.IsValid(), "Position must be valid");
165 : : OSL_ENSURE(anchor->CaretPos.IsValid(), "Anchor must be valid");
166 : 0 : }
167 : :
168 : 0 : bool SmCursor::SetCaretPosition(SmCaretPos pos, bool moveAnchor){
169 : 0 : SmCaretPosGraphIterator it = pGraph->GetIterator();
170 [ # # ][ # # ]: 0 : while(it.Next()){
171 [ # # ]: 0 : if(it->CaretPos == pos){
172 : 0 : position = it.Current();
173 [ # # ]: 0 : if(moveAnchor)
174 : 0 : anchor = it.Current();
175 : 0 : return true;
176 : : }
177 : : }
178 : 0 : return false;
179 : : }
180 : :
181 : 0 : void SmCursor::AnnotateSelection(){
182 : : //TODO: Manage a state, reset it upon modification and optimize this call
183 : 0 : SmSetSelectionVisitor(anchor->CaretPos, position->CaretPos, pTree);
184 : 0 : }
185 : :
186 : 0 : void SmCursor::Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible){
187 : 0 : SmCaretDrawingVisitor(pDev, GetPosition(), Offset, isCaretVisible);
188 : 0 : }
189 : :
190 : 0 : void SmCursor::DeletePrev(OutputDevice* pDev){
191 : : //Delete only a selection if there's a selection
192 [ # # ]: 0 : if(HasSelection()){
193 [ # # ]: 0 : Delete();
194 : 0 : return;
195 : : }
196 : :
197 [ # # ]: 0 : SmNode* pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode);
198 : 0 : SmStructureNode* pLineParent = pLine->GetParent();
199 [ # # ]: 0 : int nLineOffset = pLineParent->IndexOfSubNode(pLine);
200 : :
201 : : //If we're in front of a node who's parent is a TABLE
202 [ # # ][ # # ]: 0 : if(pLineParent->GetType() == NTABLE && position->CaretPos.Index == 0 && nLineOffset > 0){
[ # # ][ # # ]
203 : : //Now we can merge with nLineOffset - 1
204 [ # # ]: 0 : BeginEdit();
205 : : //Line to merge things into, so we can delete pLine
206 [ # # ]: 0 : SmNode* pMergeLine = pLineParent->GetSubNode(nLineOffset-1);
207 : : OSL_ENSURE(pMergeLine, "pMergeLine cannot be NULL!");
208 : : //Convert first line to list
209 [ # # ][ # # ]: 0 : SmNodeList *pLineList = NodeToList(pMergeLine);
[ # # ]
210 : : //Find iterator to patch
211 : 0 : SmNodeList::iterator patchPoint = pLineList->end();
212 : 0 : --patchPoint;
213 : : //Convert second line to list
214 [ # # ]: 0 : NodeToList(pLine, pLineList);
215 : : //Patch the line list
216 : 0 : ++patchPoint;
217 [ # # ]: 0 : SmCaretPos PosAfterDelete = PatchLineList(pLineList, patchPoint);
218 : : //Parse the line
219 [ # # ]: 0 : pLine = SmNodeListParser().Parse(pLineList);
220 [ # # ]: 0 : delete pLineList;
221 [ # # ]: 0 : pLineParent->SetSubNode(nLineOffset-1, pLine);
222 : : //Delete the removed line slot
223 [ # # ][ # # ]: 0 : SmNodeArray lines(pLineParent->GetNumSubNodes()-1);
224 [ # # ][ # # ]: 0 : for(int i = 0; i < pLineParent->GetNumSubNodes(); i++){
225 [ # # ]: 0 : if(i < nLineOffset)
226 [ # # ]: 0 : lines[i] = pLineParent->GetSubNode(i);
227 [ # # ]: 0 : else if(i > nLineOffset)
228 [ # # ]: 0 : lines[i-1] = pLineParent->GetSubNode(i);
229 : : }
230 [ # # ]: 0 : pLineParent->SetSubNodes(lines);
231 : : //Rebuild graph
232 : 0 : anchor = NULL;
233 : 0 : position = NULL;
234 [ # # ]: 0 : BuildGraph();
235 [ # # ]: 0 : AnnotateSelection();
236 : : //Set caret position
237 [ # # ][ # # ]: 0 : if(!SetCaretPosition(PosAfterDelete, true))
238 [ # # ]: 0 : SetCaretPosition(SmCaretPos(pLine, 0), true);
239 : : //Finish editing
240 [ # # ]: 0 : EndEdit();
241 : :
242 : : //TODO: If we're in an empty (sub/super/*) script
243 : : /*}else if(pLineParent->GetType() == NSUBSUP &&
244 : : nLineOffset != 0 &&
245 : : pLine->GetType() == NEXPRESSION &&
246 : : pLine->GetNumSubNodes() == 0){
247 : : //There's a (sub/super) script we can delete
248 : : //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != NEXPRESSION
249 : : //TODO: Handle case where we delete a limit
250 : : */
251 : :
252 : : //Else move select, and delete if not complex
253 : : }else{
254 [ # # ]: 0 : this->Move(pDev, MoveLeft, false);
255 [ # # ][ # # ]: 0 : if(!this->HasComplexSelection())
256 [ # # ]: 0 : Delete();
257 : : }
258 : : }
259 : :
260 : 0 : void SmCursor::Delete(){
261 : : //Return if we don't have a selection to delete
262 [ # # ]: 0 : if(!HasSelection())
263 : 0 : return;
264 : :
265 : : //Enter edit setion
266 [ # # ]: 0 : BeginEdit();
267 : :
268 : : //Set selected on nodes
269 [ # # ]: 0 : AnnotateSelection();
270 : :
271 : : //Find an arbitrary selected node
272 [ # # ]: 0 : SmNode* pSNode = FindSelectedNode(pTree);
273 : : OSL_ENSURE(pSNode != NULL, "There must be a selection when HasSelection is true!");
274 : :
275 : : //Find the topmost node of the line that holds the selection
276 [ # # ]: 0 : SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
277 : : OSL_ENSURE(pLine != pTree, "Shouldn't be able to select the entire tree");
278 : :
279 : : //Get the parent of the line
280 : 0 : SmStructureNode* pLineParent = pLine->GetParent();
281 : : //Find line offset in parent
282 [ # # ]: 0 : int nLineOffset = pLineParent->IndexOfSubNode(pLine);
283 : : OSL_ENSURE(nLineOffset != -1, "pLine must be a child of it's parent!");
284 : :
285 : : //Position after delete
286 : 0 : SmCaretPos PosAfterDelete;
287 : :
288 [ # # ][ # # ]: 0 : SmNodeList* pLineList = NodeToList(pLine);
[ # # ]
289 : :
290 : : //Take the selected nodes and delete them...
291 [ # # ]: 0 : SmNodeList::iterator patchIt = TakeSelectedNodesFromList(pLineList);
292 : :
293 : : //Get teh position to set after delete
294 [ # # ]: 0 : PosAfterDelete = PatchLineList(pLineList, patchIt);
295 : :
296 : : //Finish editing
297 [ # # ]: 0 : FinishEdit(pLineList, pLineParent, nLineOffset, PosAfterDelete);
298 : : }
299 : :
300 : 0 : void SmCursor::InsertNodes(SmNodeList* pNewNodes){
301 [ # # ]: 0 : if(pNewNodes->empty()){
302 [ # # ]: 0 : delete pNewNodes;
303 : 0 : return;
304 : : }
305 : :
306 : : //Begin edit section
307 [ # # ]: 0 : BeginEdit();
308 : :
309 : : //Position after insert should be after pNewNode
310 [ # # ]: 0 : SmCaretPos PosAfterInsert = SmCaretPos(pNewNodes->back(), 1);
311 : :
312 : : //Get the current position
313 : 0 : const SmCaretPos pos = position->CaretPos;
314 : :
315 : : //Find top most of line that holds position
316 [ # # ]: 0 : SmNode* pLine = FindTopMostNodeInLine(pos.pSelectedNode, false);
317 : :
318 : : //Find line parent and line index in parent
319 : 0 : SmStructureNode* pLineParent = pLine->GetParent();
320 [ # # ]: 0 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
321 : : OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
322 : :
323 : : //Convert line to list
324 [ # # ][ # # ]: 0 : SmNodeList* pLineList = NodeToList(pLine);
[ # # ]
325 : :
326 : : //Find iterator for place to insert nodes
327 [ # # ]: 0 : SmNodeList::iterator it = FindPositionInLineList(pLineList, pos);
328 : :
329 : : //Insert all new nodes
330 : 0 : SmNodeList::iterator newIt,
331 : 0 : patchIt = it, // (pointless default value, fixes compiler warnings)
332 : 0 : insIt;
333 [ # # ]: 0 : for(newIt = pNewNodes->begin(); newIt != pNewNodes->end(); ++newIt){
334 [ # # ]: 0 : insIt = pLineList->insert(it, *newIt);
335 [ # # ]: 0 : if(newIt == pNewNodes->begin())
336 : 0 : patchIt = insIt;
337 [ # # ]: 0 : if((*newIt)->GetType() == NTEXT)
338 : 0 : PosAfterInsert = SmCaretPos(*newIt, ((SmTextNode*)*newIt)->GetText().Len());
339 : : else
340 : 0 : PosAfterInsert = SmCaretPos(*newIt, 1);
341 : : }
342 : : //Patch the places we've changed stuff
343 [ # # ]: 0 : PatchLineList(pLineList, patchIt);
344 [ # # ]: 0 : PosAfterInsert = PatchLineList(pLineList, it);
345 : : //Release list, we've taken the nodes
346 [ # # ]: 0 : delete pNewNodes;
347 : 0 : pNewNodes = NULL;
348 : :
349 : : //Finish editing
350 [ # # ]: 0 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
351 : : }
352 : :
353 : 0 : SmNodeList::iterator SmCursor::FindPositionInLineList(SmNodeList* pLineList, SmCaretPos aCaretPos) {
354 : : //Find iterator for position
355 : 0 : SmNodeList::iterator it;
356 [ # # ]: 0 : for(it = pLineList->begin(); it != pLineList->end(); ++it){
357 [ # # ]: 0 : if(*it == aCaretPos.pSelectedNode){
358 [ # # ]: 0 : if((*it)->GetType() == NTEXT){
359 : : //Split textnode if needed
360 [ # # ]: 0 : if(aCaretPos.Index > 0){
361 : 0 : SmTextNode* pText = (SmTextNode*)aCaretPos.pSelectedNode;
362 [ # # ]: 0 : XubString str1 = pText->GetText().Copy(0, aCaretPos.Index);
363 [ # # ]: 0 : XubString str2 = pText->GetText().Copy(aCaretPos.Index);
364 [ # # ]: 0 : pText->ChangeText(str1);
365 : 0 : ++it;
366 : : //Insert str2 as new text node
367 [ # # ]: 0 : if(str2.Len() > 0){
368 [ # # ][ # # ]: 0 : SmTextNode* pNewText = new SmTextNode(pText->GetToken(), pText->GetFontDesc());
369 [ # # ]: 0 : pNewText->ChangeText(str2);
370 [ # # ]: 0 : it = pLineList->insert(it, pNewText);
371 [ # # ][ # # ]: 0 : }
372 : : }
373 : : }else
374 : 0 : ++it;
375 : : //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
376 : 0 : return it;
377 : :
378 : : }
379 : : }
380 : : //If we didn't find pSelectedNode, it must be because the caret is in front of the line
381 : 0 : return pLineList->begin();
382 : : }
383 : :
384 : 0 : SmCaretPos SmCursor::PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter) {
385 : : //The nodes we should consider merging
386 : 0 : SmNode *prev = NULL,
387 : 0 : *next = NULL;
388 [ # # ]: 0 : if(aIter != pLineList->end())
389 : 0 : next = *aIter;
390 [ # # ]: 0 : if(aIter != pLineList->begin()) {
391 : 0 : --aIter;
392 : 0 : prev = *aIter;
393 : 0 : ++aIter;
394 : : }
395 : :
396 : : //Check if there's textnodes to merge
397 [ # # ][ # # : 0 : if( prev &&
# # # # #
# # # ]
[ # # ]
398 : : next &&
399 : 0 : prev->GetType() == NTEXT &&
400 : 0 : next->GetType() == NTEXT &&
401 : 0 : ( prev->GetToken().eType != TNUMBER ||
402 : 0 : next->GetToken().eType == TNUMBER) ){
403 : 0 : SmTextNode *pText = (SmTextNode*)prev,
404 : 0 : *pOldN = (SmTextNode*)next;
405 : 0 : SmCaretPos retval(pText, pText->GetText().Len());
406 [ # # ]: 0 : String newText;
407 [ # # ]: 0 : newText += pText->GetText();
408 [ # # ]: 0 : newText += pOldN->GetText();
409 [ # # ]: 0 : pText->ChangeText(newText);
410 [ # # ][ # # ]: 0 : delete pOldN;
411 [ # # ]: 0 : pLineList->erase(aIter);
412 [ # # ]: 0 : return retval;
413 : : }
414 : :
415 : : //Check if there's a SmPlaceNode to remove:
416 [ # # ][ # # ]: 0 : if(prev && next && prev->GetType() == NPLACE && !SmNodeListParser::IsOperator(next->GetToken())){
[ # # ][ # # ]
[ # # ]
417 : 0 : --aIter;
418 : 0 : aIter = pLineList->erase(aIter);
419 [ # # ]: 0 : delete prev;
420 : : //Return caret pos in front of aIter
421 [ # # ]: 0 : if(aIter != pLineList->begin())
422 : 0 : --aIter; //Thus find node before aIter
423 [ # # ]: 0 : if(aIter == pLineList->begin())
424 : 0 : return SmCaretPos();
425 [ # # ]: 0 : if((*aIter)->GetType() == NTEXT)
426 : 0 : return SmCaretPos(*aIter, ((SmTextNode*)*aIter)->GetText().Len());
427 : 0 : return SmCaretPos(*aIter, 1);
428 : : }
429 [ # # ][ # # ]: 0 : if(prev && next && next->GetType() == NPLACE && !SmNodeListParser::IsOperator(prev->GetToken())){
[ # # ][ # # ]
[ # # ]
430 : 0 : aIter = pLineList->erase(aIter);
431 [ # # ]: 0 : delete next;
432 [ # # ]: 0 : if(prev->GetType() == NTEXT)
433 : 0 : return SmCaretPos(prev, ((SmTextNode*)prev)->GetText().Len());
434 : 0 : return SmCaretPos(prev, 1);
435 : : }
436 : :
437 : : //If we didn't do anything return
438 [ # # ]: 0 : if(!prev) //return an invalid to indicate we're in front of line
439 : 0 : return SmCaretPos();
440 [ # # ]: 0 : if(prev->GetType() == NTEXT)
441 : 0 : return SmCaretPos(prev, ((SmTextNode*)prev)->GetText().Len());
442 : 0 : return SmCaretPos(prev, 1);
443 : : }
444 : :
445 : 0 : SmNodeList::iterator SmCursor::TakeSelectedNodesFromList(SmNodeList *pLineList,
446 : : SmNodeList *pSelectedNodes) {
447 : 0 : SmNodeList::iterator retval;
448 : 0 : SmNodeList::iterator it = pLineList->begin();
449 [ # # ]: 0 : while(it != pLineList->end()){
450 [ # # ]: 0 : if((*it)->IsSelected()){
451 : : //Split text nodes
452 [ # # ]: 0 : if((*it)->GetType() == NTEXT) {
453 : 0 : SmTextNode* pText = (SmTextNode*)*it;
454 [ # # ]: 0 : String aText = pText->GetText();
455 : : //Start and lengths of the segments, 2 is the selected segment
456 : 0 : int start2 = pText->GetSelectionStart(),
457 : 0 : start3 = pText->GetSelectionEnd(),
458 : 0 : len1 = start2 - 0,
459 : 0 : len2 = start3 - start2,
460 : 0 : len3 = aText.Len() - start3;
461 [ # # ]: 0 : SmToken aToken = pText->GetToken();
462 : 0 : sal_uInt16 eFontDesc = pText->GetFontDesc();
463 : : //If we need make segment 1
464 [ # # ]: 0 : if(len1 > 0) {
465 : 0 : int start1 = 0;
466 [ # # ]: 0 : String str = aText.Copy(start1, len1);
467 [ # # ]: 0 : pText->ChangeText(str);
468 [ # # ]: 0 : ++it;
469 : : } else {//Remove it if not needed
470 [ # # ]: 0 : it = pLineList->erase(it);
471 [ # # ][ # # ]: 0 : delete pText;
472 : : }
473 : : //Set retval to be right after the selection
474 : 0 : retval = it;
475 : : //if we need make segment 3
476 [ # # ]: 0 : if(len3 > 0) {
477 [ # # ]: 0 : String str = aText.Copy(start3, len3);
478 [ # # ][ # # ]: 0 : SmTextNode* pSeg3 = new SmTextNode(aToken, eFontDesc);
479 [ # # ]: 0 : pSeg3->ChangeText(str);
480 [ # # ][ # # ]: 0 : retval = pLineList->insert(it, pSeg3);
481 : : }
482 : : //If we need to save the selected text
483 [ # # ][ # # ]: 0 : if(pSelectedNodes && len2 > 0) {
484 [ # # ]: 0 : String str = aText.Copy(start2, len2);
485 [ # # ][ # # ]: 0 : SmTextNode* pSeg2 = new SmTextNode(aToken, eFontDesc);
486 [ # # ]: 0 : pSeg2->ChangeText(str);
487 [ # # ][ # # ]: 0 : pSelectedNodes->push_back(pSeg2);
488 [ # # ][ # # ]: 0 : }
489 : : } else { //if it's not textnode
490 : 0 : SmNode* pNode = *it;
491 [ # # ]: 0 : retval = it = pLineList->erase(it);
492 [ # # ]: 0 : if(pSelectedNodes)
493 [ # # ]: 0 : pSelectedNodes->push_back(pNode);
494 : : else
495 [ # # ][ # # ]: 0 : delete pNode;
496 : : }
497 : : } else
498 : 0 : ++it;
499 : : }
500 : 0 : return retval;
501 : : }
502 : :
503 : 0 : void SmCursor::InsertSubSup(SmSubSup eSubSup) {
504 [ # # ]: 0 : AnnotateSelection();
505 : :
506 : : //Find line
507 : : SmNode *pLine;
508 [ # # ]: 0 : if(HasSelection()) {
509 [ # # ]: 0 : SmNode *pSNode = FindSelectedNode(pTree);
510 : : OSL_ENSURE(pSNode != NULL, "There must be a selected node when HasSelection is true!");
511 [ # # ]: 0 : pLine = FindTopMostNodeInLine(pSNode, sal_True);
512 : : } else
513 [ # # ]: 0 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, sal_False);
514 : :
515 : : //Find Parent and offset in parent
516 : 0 : SmStructureNode *pLineParent = pLine->GetParent();
517 [ # # ]: 0 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
518 : : OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
519 : :
520 : : //TODO: Consider handling special cases where parent is an SmOperNode,
521 : : // Maybe this method should be able to add limits to an SmOperNode...
522 : :
523 : : //We begin modifying the tree here
524 [ # # ]: 0 : BeginEdit();
525 : :
526 : : //Convert line to list
527 [ # # ][ # # ]: 0 : SmNodeList* pLineList = NodeToList(pLine);
[ # # ]
528 : :
529 : : //Take the selection, and/or find iterator for current position
530 [ # # ][ # # ]: 0 : SmNodeList* pSelectedNodesList = new SmNodeList();
531 : 0 : SmNodeList::iterator it;
532 [ # # ]: 0 : if(HasSelection())
533 [ # # ]: 0 : it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
534 : : else
535 [ # # ]: 0 : it = FindPositionInLineList(pLineList, position->CaretPos);
536 : :
537 : : //Find node that this should be applied to
538 : : SmNode* pSubject;
539 : 0 : bool bPatchLine = pSelectedNodesList->size() > 0; //If the line should be patched later
540 [ # # ]: 0 : if(it != pLineList->begin()) {
541 : 0 : --it;
542 : 0 : pSubject = *it;
543 : 0 : ++it;
544 : : } else {
545 : : //Create a new place node
546 [ # # ][ # # ]: 0 : pSubject = new SmPlaceNode();
547 [ # # ]: 0 : pSubject->Prepare(pDocShell->GetFormat(), *pDocShell);
548 [ # # ]: 0 : it = pLineList->insert(it, pSubject);
549 : 0 : ++it;
550 : 0 : bPatchLine = true; //We've modified the line it should be patched later.
551 : : }
552 : :
553 : : //Wrap the subject in a SmSubSupNode
554 : : SmSubSupNode* pSubSup;
555 [ # # ]: 0 : if(pSubject->GetType() != NSUBSUP){
556 [ # # ]: 0 : SmToken token;
557 : 0 : token.nGroup = TGPOWER;
558 [ # # ][ # # ]: 0 : pSubSup = new SmSubSupNode(token);
559 [ # # ]: 0 : pSubSup->SetBody(pSubject);
560 : 0 : *(--it) = pSubSup;
561 [ # # ]: 0 : ++it;
562 : : }else
563 : 0 : pSubSup = (SmSubSupNode*)pSubject;
564 : : //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
565 : : //and it pointer to the element following pSubSup in pLineList.
566 : 0 : pSubject = NULL;
567 : :
568 : : //Patch the line if we noted that was needed previously
569 [ # # ]: 0 : if(bPatchLine)
570 [ # # ]: 0 : PatchLineList(pLineList, it);
571 : :
572 : : //Convert existing, if any, sub-/superscript line to list
573 [ # # ]: 0 : SmNode *pScriptLine = pSubSup->GetSubSup(eSubSup);
574 [ # # ][ # # ]: 0 : SmNodeList* pScriptLineList = NodeToList(pScriptLine);
[ # # ]
575 : :
576 : : //Add selection to pScriptLineList
577 : 0 : unsigned int nOldSize = pScriptLineList->size();
578 [ # # ]: 0 : pScriptLineList->insert(pScriptLineList->end(), pSelectedNodesList->begin(), pSelectedNodesList->end());
579 [ # # ]: 0 : delete pSelectedNodesList;
580 : 0 : pSelectedNodesList = NULL;
581 : :
582 : : //Patch pScriptLineList if needed
583 [ # # ][ # # ]: 0 : if(0 < nOldSize && nOldSize < pScriptLineList->size()) {
[ # # ]
584 : 0 : SmNodeList::iterator iPatchPoint = pScriptLineList->begin();
585 [ # # ]: 0 : std::advance(iPatchPoint, nOldSize);
586 [ # # ]: 0 : PatchLineList(pScriptLineList, iPatchPoint);
587 : : }
588 : :
589 : : //Find caret pos, that should be used after sub-/superscription.
590 : 0 : SmCaretPos PosAfterScript; //Leave invalid for first position
591 [ # # ]: 0 : if(pScriptLineList->size() > 0)
592 [ # # ]: 0 : PosAfterScript = SmCaretPos::GetPosAfter(pScriptLineList->back());
593 : :
594 : : //Parse pScriptLineList
595 [ # # ]: 0 : pScriptLine = SmNodeListParser().Parse(pScriptLineList);
596 [ # # ]: 0 : delete pScriptLineList;
597 : 0 : pScriptLineList = NULL;
598 : :
599 : : //Insert pScriptLine back into the tree
600 [ # # ]: 0 : pSubSup->SetSubSup(eSubSup, pScriptLine);
601 : :
602 : : //Finish editing
603 [ # # ]: 0 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterScript, pScriptLine);
604 : 0 : }
605 : :
606 : 0 : bool SmCursor::InsertLimit(SmSubSup eSubSup, bool bMoveCaret) {
607 : : //Find a subject to set limits on
608 : 0 : SmOperNode *pSubject = NULL;
609 : : //Check if pSelectedNode might be a subject
610 [ # # ]: 0 : if(position->CaretPos.pSelectedNode->GetType() == NOPER)
611 : 0 : pSubject = (SmOperNode*)position->CaretPos.pSelectedNode;
612 : : else {
613 : : //If not, check if parent of the current line is a SmOperNode
614 [ # # ]: 0 : SmNode *pLineNode = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, sal_False);
615 [ # # ][ # # ]: 0 : if(pLineNode->GetParent() && pLineNode->GetParent()->GetType() == NOPER)
[ # # ]
616 : 0 : pSubject = (SmOperNode*)pLineNode->GetParent();
617 : : }
618 : :
619 : : //Abort operation if we're not in the appropriate context
620 [ # # ]: 0 : if(!pSubject)
621 : 0 : return false;
622 : :
623 [ # # ]: 0 : BeginEdit();
624 : :
625 : : //Find the sub sup node
626 : 0 : SmSubSupNode *pSubSup = NULL;
627 : : //Check if there's already one there...
628 [ # # ][ # # ]: 0 : if(pSubject->GetSubNode(0)->GetType() == NSUBSUP)
629 [ # # ]: 0 : pSubSup = (SmSubSupNode*)pSubject->GetSubNode(0);
630 : : else { //if not create a new SmSubSupNode
631 [ # # ]: 0 : SmToken token;
632 : 0 : token.nGroup = TGLIMIT;
633 [ # # ][ # # ]: 0 : pSubSup = new SmSubSupNode(token);
634 : : //Set it's body
635 [ # # ][ # # ]: 0 : pSubSup->SetBody(pSubject->GetSubNode(0));
636 : : //Replace the operation of the SmOperNode
637 [ # # ][ # # ]: 0 : pSubject->SetSubNode(0, pSubSup);
638 : : }
639 : :
640 : : //Create the limit, if needed
641 : 0 : SmCaretPos PosAfterLimit;
642 : 0 : SmNode *pLine = NULL;
643 [ # # ][ # # ]: 0 : if(!pSubSup->GetSubSup(eSubSup)){
644 [ # # ][ # # ]: 0 : pLine = new SmPlaceNode();
645 [ # # ]: 0 : pSubSup->SetSubSup(eSubSup, pLine);
646 : 0 : PosAfterLimit = SmCaretPos(pLine, 1);
647 : : //If it's already there... let's move the caret
648 [ # # ]: 0 : } else if(bMoveCaret){
649 [ # # ]: 0 : pLine = pSubSup->GetSubSup(eSubSup);
650 [ # # ][ # # ]: 0 : SmNodeList* pLineList = NodeToList(pLine);
[ # # ]
651 [ # # ]: 0 : if(pLineList->size() > 0)
652 [ # # ]: 0 : PosAfterLimit = SmCaretPos::GetPosAfter(pLineList->back());
653 [ # # ]: 0 : pLine = SmNodeListParser().Parse(pLineList);
654 [ # # ]: 0 : delete pLineList;
655 [ # # ]: 0 : pSubSup->SetSubSup(eSubSup, pLine);
656 : : }
657 : :
658 : : //Rebuild graph of caret positions
659 [ # # ]: 0 : BuildGraph();
660 [ # # ]: 0 : AnnotateSelection();
661 : :
662 : : //Set caret position
663 [ # # ]: 0 : if(bMoveCaret)
664 [ # # ][ # # ]: 0 : if(!SetCaretPosition(PosAfterLimit, true))
665 [ # # ]: 0 : SetCaretPosition(SmCaretPos(pLine, 0), true);
666 : :
667 [ # # ]: 0 : EndEdit();
668 : :
669 : 0 : return true;
670 : : }
671 : :
672 : 0 : void SmCursor::InsertBrackets(SmBracketType eBracketType) {
673 [ # # ]: 0 : BeginEdit();
674 : :
675 [ # # ]: 0 : AnnotateSelection();
676 : :
677 : : //Find line
678 : : SmNode *pLine;
679 [ # # ]: 0 : if(HasSelection()) {
680 [ # # ]: 0 : SmNode *pSNode = FindSelectedNode(pTree);
681 : : OSL_ENSURE(pSNode != NULL, "There must be a selected node if HasSelection()");
682 [ # # ]: 0 : pLine = FindTopMostNodeInLine(pSNode, sal_True);
683 : : } else
684 [ # # ]: 0 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, sal_False);
685 : :
686 : : //Find parent and offset in parent
687 : 0 : SmStructureNode *pLineParent = pLine->GetParent();
688 [ # # ]: 0 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
689 : : OSL_ENSURE( nParentIndex != -1, "pLine must be a subnode of pLineParent!");
690 : :
691 : : //Convert line to list
692 [ # # ][ # # ]: 0 : SmNodeList *pLineList = NodeToList(pLine);
[ # # ]
693 : :
694 : : //Take the selection, and/or find iterator for current position
695 [ # # ][ # # ]: 0 : SmNodeList *pSelectedNodesList = new SmNodeList();
696 : 0 : SmNodeList::iterator it;
697 [ # # ]: 0 : if(HasSelection())
698 [ # # ]: 0 : it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
699 : : else
700 [ # # ]: 0 : it = FindPositionInLineList(pLineList, position->CaretPos);
701 : :
702 : : //If there's no selected nodes, create a place node
703 : : SmNode *pBodyNode;
704 : 0 : SmCaretPos PosAfterInsert;
705 [ # # ]: 0 : if(pSelectedNodesList->empty()) {
706 [ # # ][ # # ]: 0 : pBodyNode = new SmPlaceNode();
707 : 0 : PosAfterInsert = SmCaretPos(pBodyNode, 1);
708 : : } else
709 [ # # ]: 0 : pBodyNode = SmNodeListParser().Parse(pSelectedNodesList);
710 : :
711 [ # # ]: 0 : delete pSelectedNodesList;
712 : :
713 : : //Create SmBraceNode
714 [ # # ]: 0 : SmToken aTok(TLEFT, '\0', "left", 0, 5);
715 [ # # ][ # # ]: 0 : SmBraceNode *pBrace = new SmBraceNode(aTok);
716 : 0 : pBrace->SetScaleMode(SCALE_HEIGHT);
717 [ # # ]: 0 : SmNode *pLeft = CreateBracket(eBracketType, true),
718 [ # # ]: 0 : *pRight = CreateBracket(eBracketType, false);
719 [ # # ][ # # ]: 0 : SmBracebodyNode *pBody = new SmBracebodyNode(SmToken());
[ # # ][ # # ]
720 [ # # ]: 0 : pBody->SetSubNodes(pBodyNode, NULL);
721 [ # # ]: 0 : pBrace->SetSubNodes(pLeft, pBody, pRight);
722 [ # # ]: 0 : pBrace->Prepare(pDocShell->GetFormat(), *pDocShell);
723 : :
724 : : //Insert into line
725 [ # # ]: 0 : pLineList->insert(it, pBrace);
726 : : //Patch line (I think this is good enough)
727 [ # # ]: 0 : SmCaretPos pAfter = PatchLineList(pLineList, it);
728 [ # # ]: 0 : if( !PosAfterInsert.IsValid() )
729 : 0 : PosAfterInsert = pAfter;
730 : :
731 : : //Finish editing
732 [ # # ][ # # ]: 0 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
733 : 0 : }
734 : :
735 : 0 : SmNode *SmCursor::CreateBracket(SmBracketType eBracketType, bool bIsLeft) {
736 [ # # ]: 0 : SmToken aTok;
737 [ # # ]: 0 : if(bIsLeft){
738 [ # # # # : 0 : switch(eBracketType){
# # # # #
# # ]
739 : : case NoneBrackets:
740 [ # # ][ # # ]: 0 : aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0);
[ # # ]
741 : 0 : break;
742 : : case RoundBrackets:
743 [ # # ][ # # ]: 0 : aTok = SmToken(TLPARENT, MS_LPARENT, "(", TGLBRACES, 5);
[ # # ]
744 : 0 : break;
745 : : case SquareBrackets:
746 [ # # ][ # # ]: 0 : aTok = SmToken(TLBRACKET, MS_LBRACKET, "[", TGLBRACES, 5);
[ # # ]
747 : 0 : break;
748 : : case DoubleSquareBrackets:
749 [ # # ][ # # ]: 0 : aTok = SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TGLBRACES, 5);
[ # # ]
750 : 0 : break;
751 : : case LineBrackets:
752 [ # # ][ # # ]: 0 : aTok = SmToken(TLLINE, MS_LINE, "lline", TGLBRACES, 5);
[ # # ]
753 : 0 : break;
754 : : case DoubleLineBrackets:
755 [ # # ][ # # ]: 0 : aTok = SmToken(TLDLINE, MS_DLINE, "ldline", TGLBRACES, 5);
[ # # ]
756 : 0 : break;
757 : : case CurlyBrackets:
758 [ # # ][ # # ]: 0 : aTok = SmToken(TLBRACE, MS_LBRACE, "lbrace", TGLBRACES, 5);
[ # # ]
759 : 0 : break;
760 : : case AngleBrackets:
761 [ # # ][ # # ]: 0 : aTok = SmToken(TLANGLE, MS_LANGLE, "langle", TGLBRACES, 5);
[ # # ]
762 : 0 : break;
763 : : case CeilBrackets:
764 [ # # ][ # # ]: 0 : aTok = SmToken(TLCEIL, MS_LCEIL, "lceil", TGLBRACES, 5);
[ # # ]
765 : 0 : break;
766 : : case FloorBrackets:
767 [ # # ][ # # ]: 0 : aTok = SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TGLBRACES, 5);
[ # # ]
768 : 0 : break;
769 : : }
770 : : } else {
771 [ # # # # : 0 : switch(eBracketType) {
# # # # #
# # ]
772 : : case NoneBrackets:
773 [ # # ][ # # ]: 0 : aTok = SmToken(TNONE, '\0', "none", TGLBRACES | TGRBRACES, 0);
[ # # ]
774 : 0 : break;
775 : : case RoundBrackets:
776 [ # # ][ # # ]: 0 : aTok = SmToken(TRPARENT, MS_RPARENT, ")", TGRBRACES, 5);
[ # # ]
777 : 0 : break;
778 : : case SquareBrackets:
779 [ # # ][ # # ]: 0 : aTok = SmToken(TRBRACKET, MS_RBRACKET, "]", TGRBRACES, 5);
[ # # ]
780 : 0 : break;
781 : : case DoubleSquareBrackets:
782 [ # # ][ # # ]: 0 : aTok = SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TGRBRACES, 5);
[ # # ]
783 : 0 : break;
784 : : case LineBrackets:
785 [ # # ][ # # ]: 0 : aTok = SmToken(TRLINE, MS_LINE, "rline", TGRBRACES, 5);
[ # # ]
786 : 0 : break;
787 : : case DoubleLineBrackets:
788 [ # # ][ # # ]: 0 : aTok = SmToken(TRDLINE, MS_DLINE, "rdline", TGRBRACES, 5);
[ # # ]
789 : 0 : break;
790 : : case CurlyBrackets:
791 [ # # ][ # # ]: 0 : aTok = SmToken(TRBRACE, MS_RBRACE, "rbrace", TGRBRACES, 5);
[ # # ]
792 : 0 : break;
793 : : case AngleBrackets:
794 [ # # ][ # # ]: 0 : aTok = SmToken(TRANGLE, MS_RANGLE, "rangle", TGRBRACES, 5);
[ # # ]
795 : 0 : break;
796 : : case CeilBrackets:
797 [ # # ][ # # ]: 0 : aTok = SmToken(TRCEIL, MS_RCEIL, "rceil", TGRBRACES, 5);
[ # # ]
798 : 0 : break;
799 : : case FloorBrackets:
800 [ # # ][ # # ]: 0 : aTok = SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TGRBRACES, 5);
[ # # ]
801 : 0 : break;
802 : : }
803 : : }
804 [ # # ][ # # ]: 0 : SmNode* pRetVal = new SmMathSymbolNode(aTok);
805 : 0 : pRetVal->SetScaleMode(SCALE_HEIGHT);
806 [ # # ]: 0 : return pRetVal;
807 : : }
808 : :
809 : 0 : bool SmCursor::InsertRow() {
810 [ # # ]: 0 : AnnotateSelection();
811 : :
812 : : //Find line
813 : : SmNode *pLine;
814 [ # # ]: 0 : if(HasSelection()) {
815 [ # # ]: 0 : SmNode *pSNode = FindSelectedNode(pTree);
816 : : OSL_ENSURE(pSNode != NULL, "There must be a selected node if HasSelection()");
817 [ # # ]: 0 : pLine = FindTopMostNodeInLine(pSNode, sal_True);
818 : : } else
819 [ # # ]: 0 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, sal_False);
820 : :
821 : : //Find parent and offset in parent
822 : 0 : SmStructureNode *pLineParent = pLine->GetParent();
823 [ # # ]: 0 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
824 : : OSL_ENSURE( nParentIndex != -1, "pLine must be a subnode of pLineParent!");
825 : :
826 : : //Discover the context of this command
827 : 0 : SmTableNode *pTable = NULL;
828 : 0 : SmMatrixNode *pMatrix = NULL;
829 : 0 : int nTableIndex = nParentIndex;
830 [ # # ]: 0 : if(pLineParent->GetType() == NTABLE)
831 : 0 : pTable = (SmTableNode*)pLineParent;
832 : : //If it's warped in a SmLineNode, we can still insert a newline
833 [ # # # # : 0 : else if(pLineParent->GetType() == NLINE &&
# # ][ # # ]
834 : 0 : pLineParent->GetParent() &&
835 : 0 : pLineParent->GetParent()->GetType() == NTABLE) {
836 : : //NOTE: This hack might give problems if we stop ignoring SmAlignNode
837 : 0 : pTable = (SmTableNode*)pLineParent->GetParent();
838 [ # # ]: 0 : nTableIndex = pTable->IndexOfSubNode(pLineParent);
839 : : OSL_ENSURE(nTableIndex != -1, "pLineParent must be a child of its parent!");
840 : : }
841 [ # # ]: 0 : if(pLineParent->GetType() == NMATRIX)
842 : 0 : pMatrix = (SmMatrixNode*)pLineParent;
843 : :
844 : : //If we're not in a context that supports InsertRow, return sal_False
845 [ # # ][ # # ]: 0 : if(!pTable && !pMatrix)
846 : 0 : return false;
847 : :
848 : : //Now we start editing
849 [ # # ]: 0 : BeginEdit();
850 : :
851 : : //Convert line to list
852 [ # # ][ # # ]: 0 : SmNodeList *pLineList = NodeToList(pLine);
[ # # ]
853 : :
854 : : //Find position in line
855 : 0 : SmNodeList::iterator it;
856 [ # # ]: 0 : if(HasSelection()) {
857 : : //Take the selected nodes and delete them...
858 [ # # ]: 0 : it = TakeSelectedNodesFromList(pLineList);
859 : : } else
860 [ # # ]: 0 : it = FindPositionInLineList(pLineList, position->CaretPos);
861 : :
862 : : //New caret position after inserting the newline/row in whatever context
863 : 0 : SmCaretPos PosAfterInsert;
864 : :
865 : : //If we're in the context of a table
866 [ # # ]: 0 : if(pTable) {
867 [ # # ][ # # ]: 0 : SmNodeList *pNewLineList = new SmNodeList();
868 : : //Move elements from pLineList to pNewLineList
869 [ # # ]: 0 : pNewLineList->splice(pNewLineList->begin(), *pLineList, it, pLineList->end());
870 : : //Make sure it is valid again
871 : 0 : it = pLineList->end();
872 [ # # ]: 0 : if(it != pLineList->begin())
873 : 0 : --it;
874 [ # # ]: 0 : if(pNewLineList->empty())
875 [ # # ][ # # ]: 0 : pNewLineList->push_front(new SmPlaceNode());
[ # # ]
876 : : //Parse new line
877 [ # # ]: 0 : SmNode *pNewLine = SmNodeListParser().Parse(pNewLineList);
878 [ # # ]: 0 : delete pNewLineList;
879 : : //Wrap pNewLine in SmLineNode if needed
880 [ # # ]: 0 : if(pLineParent->GetType() == NLINE) {
881 [ # # ][ # # ]: 0 : SmLineNode *pNewLineNode = new SmLineNode(SmToken(TNEWLINE, '\0', "newline"));
[ # # ][ # # ]
882 [ # # ]: 0 : pNewLineNode->SetSubNodes(pNewLine, NULL);
883 : 0 : pNewLine = pNewLineNode;
884 : : }
885 : : //Get position
886 : 0 : PosAfterInsert = SmCaretPos(pNewLine, 0);
887 : : //Move other nodes if needed
888 [ # # ][ # # ]: 0 : for( int i = pTable->GetNumSubNodes(); i > nTableIndex + 1; i--)
889 [ # # ][ # # ]: 0 : pTable->SetSubNode(i, pTable->GetSubNode(i-1));
890 : :
891 : : //Insert new line
892 [ # # ]: 0 : pTable->SetSubNode(nTableIndex + 1, pNewLine);
893 : :
894 : : //Check if we need to change token type:
895 [ # # ][ # # ]: 0 : if(pTable->GetNumSubNodes() > 2 && pTable->GetToken().eType == TBINOM) {
[ # # ][ # # ]
896 [ # # ]: 0 : SmToken tok = pTable->GetToken();
897 : 0 : tok.eType = TSTACK;
898 [ # # ][ # # ]: 0 : pTable->SetToken(tok);
899 : : }
900 : : }
901 : : //If we're in the context of a matrix
902 [ # # ]: 0 : else if(pMatrix) {
903 : : //Find position after insert and patch the list
904 [ # # ]: 0 : PosAfterInsert = PatchLineList(pLineList, it);
905 : : //Move other children
906 : 0 : sal_uInt16 rows = pMatrix->GetNumRows();
907 : 0 : sal_uInt16 cols = pMatrix->GetNumCols();
908 : 0 : int nRowStart = (nParentIndex - nParentIndex % cols) + cols;
909 [ # # ][ # # ]: 0 : for( int i = pMatrix->GetNumSubNodes() + cols - 1; i >= nRowStart + cols; i--)
910 [ # # ][ # # ]: 0 : pMatrix->SetSubNode(i, pMatrix->GetSubNode(i - cols));
911 [ # # ]: 0 : for( int i = nRowStart; i < nRowStart + cols; i++) {
912 [ # # ][ # # ]: 0 : SmPlaceNode *pNewLine = new SmPlaceNode();
913 [ # # ]: 0 : if(i == nParentIndex + cols)
914 : 0 : PosAfterInsert = SmCaretPos(pNewLine, 0);
915 [ # # ]: 0 : pMatrix->SetSubNode(i, pNewLine);
916 : : }
917 [ # # ]: 0 : pMatrix->SetRowCol(rows + 1, cols);
918 : : } else
919 : : OSL_FAIL("We must be either the context of a table or matrix!");
920 : :
921 : : //Finish editing
922 [ # # ]: 0 : FinishEdit(pLineList, pLineParent, nParentIndex, PosAfterInsert);
923 : : //FinishEdit is actually used to handle siturations where parent is an instance of
924 : : //SmSubSupNode. In this case parent should always be a table or matrix, however, for
925 : : //code reuse we just use FinishEdit() here too.
926 : 0 : return true;
927 : : }
928 : :
929 : 0 : void SmCursor::InsertFraction() {
930 [ # # ]: 0 : AnnotateSelection();
931 : :
932 : : //Find line
933 : : SmNode *pLine;
934 [ # # ]: 0 : if(HasSelection()) {
935 [ # # ]: 0 : SmNode *pSNode = FindSelectedNode(pTree);
936 : : OSL_ENSURE(pSNode != NULL, "There must be a selected node when HasSelection is true!");
937 [ # # ]: 0 : pLine = FindTopMostNodeInLine(pSNode, sal_True);
938 : : } else
939 [ # # ]: 0 : pLine = FindTopMostNodeInLine(position->CaretPos.pSelectedNode, sal_False);
940 : :
941 : : //Find Parent and offset in parent
942 : 0 : SmStructureNode *pLineParent = pLine->GetParent();
943 [ # # ]: 0 : int nParentIndex = pLineParent->IndexOfSubNode(pLine);
944 : : OSL_ENSURE(nParentIndex != -1, "pLine must be a subnode of pLineParent!");
945 : :
946 : : //We begin modifying the tree here
947 [ # # ]: 0 : BeginEdit();
948 : :
949 : : //Convert line to list
950 [ # # ][ # # ]: 0 : SmNodeList* pLineList = NodeToList(pLine);
[ # # ]
951 : :
952 : : //Take the selection, and/or find iterator for current position
953 [ # # ][ # # ]: 0 : SmNodeList* pSelectedNodesList = new SmNodeList();
954 : 0 : SmNodeList::iterator it;
955 [ # # ]: 0 : if(HasSelection())
956 [ # # ]: 0 : it = TakeSelectedNodesFromList(pLineList, pSelectedNodesList);
957 : : else
958 [ # # ]: 0 : it = FindPositionInLineList(pLineList, position->CaretPos);
959 : :
960 : : //Create pNum, and pDenom
961 : 0 : bool bEmptyFraction = pSelectedNodesList->empty();
962 : : SmNode *pNum = bEmptyFraction
963 : 0 : ? new SmPlaceNode()
964 [ # # ][ # # ]: 0 : : SmNodeListParser().Parse(pSelectedNodesList);
[ # # ][ # # ]
[ # # ][ # # ]
965 [ # # ][ # # ]: 0 : SmNode *pDenom = new SmPlaceNode();
966 [ # # ]: 0 : delete pSelectedNodesList;
967 : 0 : pSelectedNodesList = NULL;
968 : :
969 : : //Create new fraction
970 [ # # ][ # # ]: 0 : SmBinVerNode *pFrac = new SmBinVerNode(SmToken(TOVER, '\0', "over", TGPRODUCT, 0));
[ # # ][ # # ]
971 [ # # ][ # # ]: 0 : SmNode *pRect = new SmRectangleNode(SmToken());
[ # # ][ # # ]
972 [ # # ]: 0 : pFrac->SetSubNodes(pNum, pRect, pDenom);
973 : :
974 : : //Insert in pLineList
975 [ # # ]: 0 : SmNodeList::iterator patchIt = pLineList->insert(it, pFrac);
976 [ # # ]: 0 : PatchLineList(pLineList, patchIt);
977 [ # # ]: 0 : PatchLineList(pLineList, it);
978 : :
979 : : //Finish editing
980 [ # # ]: 0 : SmNode *pSelectedNode = bEmptyFraction ? pNum : pDenom;
981 [ # # ]: 0 : FinishEdit(pLineList, pLineParent, nParentIndex, SmCaretPos(pSelectedNode, 1));
982 : 0 : }
983 : :
984 : 0 : void SmCursor::InsertText(rtl::OUString aString)
985 : : {
986 [ # # ]: 0 : BeginEdit();
987 : :
988 [ # # ]: 0 : Delete();
989 : :
990 [ # # ]: 0 : SmToken token;
991 : 0 : token.eType = TIDENT;
992 : 0 : token.cMathChar = '\0';
993 : 0 : token.nGroup = 0;
994 : 0 : token.nLevel = 5;
995 [ # # ]: 0 : token.aText = aString;
996 : :
997 [ # # ][ # # ]: 0 : SmTextNode* pText = new SmTextNode(token, FNT_VARIABLE);
998 : :
999 : : //Prepare the new node
1000 [ # # ]: 0 : pText->Prepare(pDocShell->GetFormat(), *pDocShell);
1001 [ # # ]: 0 : pText->AdjustFontDesc();
1002 : :
1003 [ # # ][ # # ]: 0 : SmNodeList* pList = new SmNodeList();
1004 [ # # ]: 0 : pList->push_front(pText);
1005 [ # # ]: 0 : InsertNodes(pList);
1006 : :
1007 [ # # ][ # # ]: 0 : EndEdit();
1008 : 0 : }
1009 : :
1010 : 0 : void SmCursor::InsertElement(SmFormulaElement element){
1011 [ # # ]: 0 : BeginEdit();
1012 : :
1013 [ # # ]: 0 : Delete();
1014 : :
1015 : : //Create new node
1016 : 0 : SmNode* pNewNode = NULL;
1017 [ # # # # : 0 : switch(element){
# # # # #
# ]
1018 : : case BlankElement:
1019 : : {
1020 [ # # ]: 0 : SmToken token;
1021 : 0 : token.nGroup = TGBLANK;
1022 [ # # ]: 0 : token.aText.AssignAscii("~");
1023 [ # # ][ # # ]: 0 : pNewNode = new SmBlankNode(token);
[ # # ]
1024 : 0 : }break;
1025 : : case FactorialElement:
1026 : : {
1027 [ # # ]: 0 : SmToken token(TFACT, MS_FACT, "fact", TGUNOPER, 5);
1028 [ # # ][ # # ]: 0 : pNewNode = new SmMathSymbolNode(token);
[ # # ]
1029 : 0 : }break;
1030 : : case PlusElement:
1031 : : {
1032 [ # # ]: 0 : SmToken token;
1033 : 0 : token.eType = TPLUS;
1034 : 0 : token.cMathChar = MS_PLUS;
1035 : 0 : token.nGroup = TGUNOPER | TGSUM;
1036 : 0 : token.nLevel = 5;
1037 [ # # ]: 0 : token.aText.AssignAscii("+");
1038 [ # # ][ # # ]: 0 : pNewNode = new SmMathSymbolNode(token);
[ # # ]
1039 : 0 : }break;
1040 : : case MinusElement:
1041 : : {
1042 [ # # ]: 0 : SmToken token;
1043 : 0 : token.eType = TMINUS;
1044 : 0 : token.cMathChar = MS_MINUS;
1045 : 0 : token.nGroup = TGUNOPER | TGSUM;
1046 : 0 : token.nLevel = 5;
1047 [ # # ]: 0 : token.aText.AssignAscii("-");
1048 [ # # ][ # # ]: 0 : pNewNode = new SmMathSymbolNode(token);
[ # # ]
1049 : 0 : }break;
1050 : : case CDotElement:
1051 : : {
1052 [ # # ]: 0 : SmToken token;
1053 : 0 : token.eType = TCDOT;
1054 : 0 : token.cMathChar = MS_CDOT;
1055 : 0 : token.nGroup = TGPRODUCT;
1056 [ # # ]: 0 : token.aText.AssignAscii("cdot");
1057 [ # # ][ # # ]: 0 : pNewNode = new SmMathSymbolNode(token);
[ # # ]
1058 : 0 : }break;
1059 : : case EqualElement:
1060 : : {
1061 [ # # ]: 0 : SmToken token;
1062 : 0 : token.eType = TASSIGN;
1063 : 0 : token.cMathChar = MS_ASSIGN;
1064 : 0 : token.nGroup = TGRELATION;
1065 [ # # ]: 0 : token.aText.AssignAscii("=");
1066 [ # # ][ # # ]: 0 : pNewNode = new SmMathSymbolNode(token);
[ # # ]
1067 : 0 : }break;
1068 : : case LessThanElement:
1069 : : {
1070 [ # # ]: 0 : SmToken token;
1071 : 0 : token.eType = TLT;
1072 : 0 : token.cMathChar = MS_LT;
1073 : 0 : token.nGroup = TGRELATION;
1074 [ # # ]: 0 : token.aText.AssignAscii("<");
1075 [ # # ][ # # ]: 0 : pNewNode = new SmMathSymbolNode(token);
[ # # ]
1076 : 0 : }break;
1077 : : case GreaterThanElement:
1078 : : {
1079 [ # # ]: 0 : SmToken token;
1080 : 0 : token.eType = TGT;
1081 : 0 : token.cMathChar = MS_GT;
1082 : 0 : token.nGroup = TGRELATION;
1083 [ # # ]: 0 : token.aText.AssignAscii(">");
1084 [ # # ][ # # ]: 0 : pNewNode = new SmMathSymbolNode(token);
[ # # ]
1085 : 0 : }break;
1086 : : case PercentElement:
1087 : : {
1088 [ # # ]: 0 : SmToken token;
1089 : 0 : token.eType = TTEXT;
1090 : 0 : token.cMathChar = MS_PERCENT;
1091 : 0 : token.nGroup = 0;
1092 [ # # ]: 0 : token.aText.AssignAscii("\"%\"");
1093 [ # # ][ # # ]: 0 : pNewNode = new SmMathSymbolNode(token);
[ # # ]
1094 : 0 : }break;
1095 : : default:
1096 : : OSL_FAIL("Element unknown!");
1097 : : }
1098 : : OSL_ENSURE(pNewNode != NULL, "No new node was created!");
1099 [ # # ]: 0 : if(!pNewNode)
1100 : 0 : return;
1101 : :
1102 : : //Prepare the new node
1103 [ # # ]: 0 : pNewNode->Prepare(pDocShell->GetFormat(), *pDocShell);
1104 : :
1105 : : //Insert new node
1106 [ # # ][ # # ]: 0 : SmNodeList* pList = new SmNodeList();
1107 [ # # ]: 0 : pList->push_front(pNewNode);
1108 [ # # ]: 0 : InsertNodes(pList);
1109 : :
1110 [ # # ]: 0 : EndEdit();
1111 : : }
1112 : :
1113 : 0 : void SmCursor::InsertSpecial(rtl::OUString aString)
1114 : : {
1115 [ # # ]: 0 : BeginEdit();
1116 [ # # ]: 0 : Delete();
1117 : :
1118 [ # # ]: 0 : aString = comphelper::string::strip(aString, ' ');
1119 : :
1120 : : //Create instance of special node
1121 [ # # ]: 0 : SmToken token;
1122 : 0 : token.eType = TSPECIAL;
1123 : 0 : token.cMathChar = '\0';
1124 : 0 : token.nGroup = 0;
1125 : 0 : token.nLevel = 5;
1126 [ # # ]: 0 : token.aText = aString;
1127 [ # # ][ # # ]: 0 : SmSpecialNode* pSpecial = new SmSpecialNode(token);
1128 : :
1129 : : //Prepare the special node
1130 [ # # ]: 0 : pSpecial->Prepare(pDocShell->GetFormat(), *pDocShell);
1131 : :
1132 : : //Insert the node
1133 [ # # ][ # # ]: 0 : SmNodeList* pList = new SmNodeList();
1134 [ # # ]: 0 : pList->push_front(pSpecial);
1135 [ # # ]: 0 : InsertNodes(pList);
1136 : :
1137 [ # # ][ # # ]: 0 : EndEdit();
1138 : 0 : }
1139 : :
1140 : 0 : void SmCursor::InsertCommand(sal_uInt16 nCommand) {
1141 [ # # # # : 0 : switch(nCommand){
# ]
1142 : : case RID_NEWLINE:
1143 : 0 : InsertRow();
1144 : 0 : break;
1145 : : case RID_FROMX:
1146 : 0 : InsertLimit(CSUB, true);
1147 : 0 : break;
1148 : : case RID_TOX:
1149 : 0 : InsertLimit(CSUP, true);
1150 : 0 : break;
1151 : : case RID_FROMXTOY:
1152 [ # # ]: 0 : if(InsertLimit(CSUB, true))
1153 : 0 : InsertLimit(CSUP, true);
1154 : 0 : break;
1155 : : default:
1156 [ # # ][ # # ]: 0 : InsertCommandText(SmResId(nCommand));
[ # # ]
1157 : 0 : break;
1158 : : }
1159 : 0 : }
1160 : :
1161 : 0 : void SmCursor::InsertCommandText(XubString aCommandText) {
1162 : : //Parse the the sub expression
1163 [ # # ][ # # ]: 0 : SmNode* pSubExpr = SmParser().ParseExpression(aCommandText);
[ # # ]
1164 : :
1165 : : //Prepare the subtree
1166 [ # # ]: 0 : pSubExpr->Prepare(pDocShell->GetFormat(), *pDocShell);
1167 : :
1168 : : //Convert subtree to list
1169 [ # # ][ # # ]: 0 : SmNodeList* pLineList = NodeToList(pSubExpr);
[ # # ]
1170 : :
1171 [ # # ]: 0 : BeginEdit();
1172 : :
1173 : : //Delete any selection
1174 [ # # ]: 0 : Delete();
1175 : :
1176 : : //Insert it
1177 [ # # ]: 0 : InsertNodes(pLineList);
1178 : :
1179 [ # # ]: 0 : EndEdit();
1180 : 0 : }
1181 : :
1182 : 0 : void SmCursor::Copy(){
1183 [ # # ]: 0 : if(!HasSelection())
1184 : 0 : return;
1185 : :
1186 : : //Find selected node
1187 : 0 : SmNode* pSNode = FindSelectedNode(pTree);
1188 : : //Find visual line
1189 : 0 : SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
1190 : :
1191 : : //Clone selected nodes
1192 : : SmNodeList* pList;
1193 [ # # ]: 0 : if(IsLineCompositionNode(pLine))
1194 [ # # ]: 0 : pList = CloneLineToList((SmStructureNode*)pLine, true);
1195 : : else{
1196 [ # # ]: 0 : pList = new SmNodeList();
1197 : : //Special care to only clone selected text
1198 [ # # ]: 0 : if(pLine->GetType() == NTEXT) {
1199 : 0 : SmTextNode *pText = (SmTextNode*)pLine;
1200 [ # # ]: 0 : SmTextNode *pClone = new SmTextNode( pText->GetToken(), pText->GetFontDesc() );
1201 : 0 : int start = pText->GetSelectionStart(),
1202 : 0 : length = pText->GetSelectionEnd() - pText->GetSelectionStart();
1203 [ # # ]: 0 : pClone->ChangeText(pText->GetText().Copy(start, length));
1204 : 0 : pClone->SetScaleMode(pText->GetScaleMode());
1205 [ # # ]: 0 : pList->push_front(pClone);
1206 : : } else {
1207 : 0 : SmCloningVisitor aCloneFactory;
1208 [ # # ][ # # ]: 0 : pList->push_front(aCloneFactory.Clone(pLine));
1209 : : }
1210 : : }
1211 : :
1212 : : //Set clipboard
1213 [ # # ]: 0 : if(pList->size() > 0)
1214 : 0 : SetClipboard(pList);
1215 : : }
1216 : :
1217 : 0 : void SmCursor::Paste() {
1218 : 0 : BeginEdit();
1219 : 0 : Delete();
1220 : :
1221 [ # # ][ # # ]: 0 : if(pClipboard && pClipboard->size() > 0)
[ # # ]
1222 : 0 : InsertNodes(CloneList(pClipboard));
1223 : :
1224 : 0 : EndEdit();
1225 : 0 : }
1226 : :
1227 : 0 : SmNodeList* SmCursor::CloneList(SmNodeList* pList){
1228 : 0 : SmCloningVisitor aCloneFactory;
1229 [ # # ][ # # ]: 0 : SmNodeList* pClones = new SmNodeList();
1230 : :
1231 : 0 : SmNodeList::iterator it;
1232 [ # # ]: 0 : for(it = pList->begin(); it != pList->end(); ++it){
1233 [ # # ]: 0 : SmNode *pClone = aCloneFactory.Clone(*it);
1234 [ # # ]: 0 : pClones->push_back(pClone);
1235 : : }
1236 : :
1237 : 0 : return pClones;
1238 : : }
1239 : :
1240 : 0 : void SmCursor::SetClipboard(SmNodeList* pList){
1241 [ # # ]: 0 : if(pClipboard){
1242 : : //Delete all nodes on the clipboard
1243 : 0 : SmNodeList::iterator it;
1244 [ # # ]: 0 : for(it = pClipboard->begin(); it != pClipboard->end(); ++it)
1245 [ # # ][ # # ]: 0 : delete (*it);
1246 [ # # ]: 0 : delete pClipboard;
1247 : : }
1248 : 0 : pClipboard = pList;
1249 : 0 : }
1250 : :
1251 : 0 : SmNode* SmCursor::FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected){
1252 : : //If we haven't got a subnode
1253 [ # # ]: 0 : if(!pSNode)
1254 : 0 : return NULL;
1255 : :
1256 : : //Move up parent untill we find a node who's
1257 : : //parent is NULL or isn't selected and not a type of:
1258 : : // SmExpressionNode
1259 : : // SmLineNode
1260 : : // SmBinHorNode
1261 : : // SmUnHorNode
1262 : : // SmAlignNode
1263 : : // SmFontNode
1264 [ # # ][ # # : 0 : while(pSNode->GetParent() &&
# # # # ]
[ # # ]
1265 : : ((MoveUpIfSelected &&
1266 : 0 : pSNode->GetParent()->IsSelected()) ||
1267 : 0 : IsLineCompositionNode(pSNode->GetParent())))
1268 : 0 : pSNode = pSNode->GetParent();
1269 : : //Now we have the selection line node
1270 : 0 : return pSNode;
1271 : : }
1272 : :
1273 : 0 : SmNode* SmCursor::FindSelectedNode(SmNode* pNode){
1274 [ # # ]: 0 : SmNodeIterator it(pNode);
1275 [ # # ][ # # ]: 0 : while(it.Next()){
1276 [ # # ]: 0 : if(it->IsSelected())
1277 : 0 : return it.Current();
1278 [ # # ]: 0 : SmNode* pRetVal = FindSelectedNode(it.Current());
1279 [ # # ]: 0 : if(pRetVal)
1280 : 0 : return pRetVal;
1281 : : }
1282 : 0 : return NULL;
1283 : : }
1284 : :
1285 : 0 : SmNodeList* SmCursor::LineToList(SmStructureNode* pLine, SmNodeList* list){
1286 [ # # ]: 0 : SmNodeIterator it(pLine);
1287 [ # # ][ # # ]: 0 : while(it.Next()){
1288 [ # # # ]: 0 : switch(it->GetType()){
1289 : : case NLINE:
1290 : : case NUNHOR:
1291 : : case NEXPRESSION:
1292 : : case NBINHOR:
1293 : : case NALIGN:
1294 : : case NFONT:
1295 [ # # ]: 0 : LineToList((SmStructureNode*)it.Current(), list);
1296 : 0 : break;
1297 : : case NERROR:
1298 [ # # ][ # # ]: 0 : delete it.Current();
1299 : 0 : break;
1300 : : default:
1301 [ # # ]: 0 : list->push_back(it.Current());
1302 : : }
1303 : : }
1304 [ # # ]: 0 : SmNodeArray emptyArray(0);
1305 [ # # ]: 0 : pLine->SetSubNodes(emptyArray);
1306 [ # # ][ # # ]: 0 : delete pLine;
1307 : 0 : return list;
1308 : : }
1309 : :
1310 : 0 : SmNodeList* SmCursor::CloneLineToList(SmStructureNode* pLine, bool bOnlyIfSelected, SmNodeList* pList){
1311 : 0 : SmCloningVisitor aCloneFactory;
1312 [ # # ]: 0 : SmNodeIterator it(pLine);
1313 [ # # ][ # # ]: 0 : while(it.Next()){
1314 [ # # ]: 0 : if( IsLineCompositionNode( it.Current() ) )
1315 [ # # ]: 0 : CloneLineToList( (SmStructureNode*)it.Current(), bOnlyIfSelected, pList );
1316 [ # # ][ # # ]: 0 : else if( (!bOnlyIfSelected || it->IsSelected()) && it->GetType() != NERROR ) {
[ # # ][ # # ]
1317 : : //Only clone selected text from SmTextNode
1318 [ # # ]: 0 : if(it->GetType() == NTEXT) {
1319 : 0 : SmTextNode *pText = (SmTextNode*)it.Current();
1320 [ # # ][ # # ]: 0 : SmTextNode *pClone = new SmTextNode( it->GetToken(), pText->GetFontDesc() );
1321 : 0 : int start = pText->GetSelectionStart(),
1322 : 0 : length = pText->GetSelectionEnd() - pText->GetSelectionStart();
1323 [ # # ][ # # ]: 0 : pClone->ChangeText(pText->GetText().Copy(start, length));
[ # # ]
1324 : 0 : pClone->SetScaleMode(pText->GetScaleMode());
1325 [ # # ]: 0 : pList->push_back(pClone);
1326 : : } else
1327 [ # # ][ # # ]: 0 : pList->push_back(aCloneFactory.Clone(it.Current()));
1328 : : }
1329 : : }
1330 : 0 : return pList;
1331 : : }
1332 : :
1333 : 0 : bool SmCursor::IsLineCompositionNode(SmNode* pNode){
1334 [ # # ]: 0 : switch(pNode->GetType()){
1335 : : case NLINE:
1336 : : case NUNHOR:
1337 : : case NEXPRESSION:
1338 : : case NBINHOR:
1339 : : case NALIGN:
1340 : : case NFONT:
1341 : 0 : return true;
1342 : : default:
1343 : 0 : return false;
1344 : : }
1345 : : }
1346 : :
1347 : 0 : int SmCursor::CountSelectedNodes(SmNode* pNode){
1348 : 0 : int nCount = 0;
1349 [ # # ]: 0 : SmNodeIterator it(pNode);
1350 [ # # ][ # # ]: 0 : while(it.Next()){
1351 [ # # ][ # # ]: 0 : if(it->IsSelected() && !IsLineCompositionNode(it.Current()))
[ # # ]
1352 : 0 : nCount++;
1353 [ # # ]: 0 : nCount += CountSelectedNodes(it.Current());
1354 : : }
1355 : 0 : return nCount;
1356 : : }
1357 : :
1358 : 0 : bool SmCursor::HasComplexSelection(){
1359 [ # # ]: 0 : if(!HasSelection())
1360 : 0 : return false;
1361 : 0 : AnnotateSelection();
1362 : :
1363 : 0 : return CountSelectedNodes(pTree) > 1;
1364 : : }
1365 : :
1366 : 0 : void SmCursor::FinishEdit(SmNodeList* pLineList,
1367 : : SmStructureNode* pParent,
1368 : : int nParentIndex,
1369 : : SmCaretPos PosAfterEdit,
1370 : : SmNode* pStartLine) {
1371 : : //Store number of nodes in line for later
1372 : 0 : int entries = pLineList->size();
1373 : :
1374 : : //Parse list of nodes to a tree
1375 : 0 : SmNodeListParser parser;
1376 [ # # ]: 0 : SmNode* pLine = parser.Parse(pLineList);
1377 [ # # ]: 0 : delete pLineList;
1378 : :
1379 : : //Check if we're making the body of a subsup node bigger than one
1380 [ # # ][ # # ]: 0 : if(pParent->GetType() == NSUBSUP &&
[ # # ][ # # ]
1381 : : nParentIndex == 0 &&
1382 : : entries > 1) {
1383 : : //Wrap pLine in scalable round brackets
1384 [ # # ]: 0 : SmToken aTok(TLEFT, '\0', "left", 0, 5);
1385 [ # # ][ # # ]: 0 : SmBraceNode *pBrace = new SmBraceNode(aTok);
1386 : 0 : pBrace->SetScaleMode(SCALE_HEIGHT);
1387 [ # # ]: 0 : SmNode *pLeft = CreateBracket(RoundBrackets, true),
1388 [ # # ]: 0 : *pRight = CreateBracket(RoundBrackets, false);
1389 [ # # ][ # # ]: 0 : SmBracebodyNode *pBody = new SmBracebodyNode(SmToken());
[ # # ][ # # ]
1390 [ # # ]: 0 : pBody->SetSubNodes(pLine, NULL);
1391 [ # # ]: 0 : pBrace->SetSubNodes(pLeft, pBody, pRight);
1392 [ # # ]: 0 : pBrace->Prepare(pDocShell->GetFormat(), *pDocShell);
1393 [ # # ]: 0 : pLine = pBrace;
1394 : : //TODO: Consider the following alternative behavior:
1395 : : //Consider the line: A + {B + C}^D lsub E
1396 : : //Here pLineList is B, + and C and pParent is a subsup node with
1397 : : //both RSUP and LSUB set. Imagine the user just inserted "B +" in
1398 : : //the body of the subsup node...
1399 : : //The most natural thing to do would be to make the line like this:
1400 : : //A + B lsub E + C ^ D
1401 : : //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
1402 : : //and RSUB to the last eleent in pLineList. But how should this act
1403 : : //for CSUP and CSUB ???
1404 : : //For this reason and because brackets was faster to implement, this solution
1405 : : //have been choosen. It might be worth working on the other solution later...
1406 : : }
1407 : :
1408 : : //Set pStartLine if NULL
1409 [ # # ]: 0 : if(!pStartLine)
1410 : 0 : pStartLine = pLine;
1411 : :
1412 : : //Insert it back into the parent
1413 [ # # ]: 0 : pParent->SetSubNode(nParentIndex, pLine);
1414 : :
1415 : : //Rebuild graph of caret position
1416 : 0 : anchor = NULL;
1417 : 0 : position = NULL;
1418 [ # # ]: 0 : BuildGraph();
1419 [ # # ]: 0 : AnnotateSelection(); //Update selection annotation!
1420 : :
1421 : : //Set caret position
1422 [ # # ][ # # ]: 0 : if(!SetCaretPosition(PosAfterEdit, true))
1423 [ # # ]: 0 : SetCaretPosition(SmCaretPos(pStartLine, 0), true);
1424 : :
1425 : : //End edit section
1426 [ # # ]: 0 : EndEdit();
1427 : 0 : }
1428 : :
1429 : 0 : void SmCursor::BeginEdit(){
1430 [ # # ]: 0 : if(nEditSections++ > 0) return;
1431 : :
1432 : 0 : bIsEnabledSetModifiedSmDocShell = pDocShell->IsEnableSetModified();
1433 [ # # ]: 0 : if( bIsEnabledSetModifiedSmDocShell )
1434 : 0 : pDocShell->EnableSetModified( sal_False );
1435 : : }
1436 : :
1437 : 0 : void SmCursor::EndEdit(){
1438 [ # # ]: 0 : if(--nEditSections > 0) return;
1439 : :
1440 : 0 : pDocShell->SetFormulaArranged(sal_False);
1441 : : //Okay, I don't know what this does... :)
1442 : : //It's used in SmDocShell::SetText and with places where everything is modified.
1443 : : //I think it does some magic, with sfx, but everything is totally undocumented so
1444 : : //it's kinda hard to tell...
1445 [ # # ]: 0 : if ( bIsEnabledSetModifiedSmDocShell )
1446 [ # # ]: 0 : pDocShell->EnableSetModified( bIsEnabledSetModifiedSmDocShell );
1447 : : //I think this notifies people around us that we've modified this document...
1448 [ # # ]: 0 : pDocShell->SetModified(sal_True);
1449 : : //I think SmDocShell uses this value when it sends an update graphics event
1450 : : //Anyway comments elsewhere suggests it need to be updated...
1451 : 0 : pDocShell->nModifyCount++;
1452 : :
1453 : : //TODO: Consider copying the update accessability code from SmDocShell::SetText in here...
1454 : : //This somehow updates the size of SmGraphicView if it is running in embedded mode
1455 [ # # ]: 0 : if( pDocShell->GetCreateMode() == SFX_CREATE_MODE_EMBEDDED )
1456 [ # # ]: 0 : pDocShell->OnDocumentPrinterChanged(0);
1457 : :
1458 : : //Request a replaint...
1459 [ # # ]: 0 : RequestRepaint();
1460 : :
1461 : : //Update the edit engine and text of the document
1462 [ # # ]: 0 : String formula;
1463 [ # # ]: 0 : SmNodeToTextVisitor(pTree, formula);
1464 : : //pTree->CreateTextFromNode(formula);
1465 [ # # ]: 0 : pDocShell->aText = formula;
1466 [ # # ][ # # ]: 0 : pDocShell->GetEditEngine().QuickInsertText( formula, ESelection( 0, 0, EE_PARA_ALL, EE_PARA_ALL ) );
1467 [ # # ][ # # ]: 0 : pDocShell->GetEditEngine().QuickFormatDoc();
[ # # ]
1468 : : }
1469 : :
1470 : 0 : void SmCursor::RequestRepaint(){
1471 : 0 : SmViewShell *pViewSh = SmGetActiveView();
1472 [ # # ]: 0 : if( pViewSh ) {
1473 [ # # ]: 0 : if ( SFX_CREATE_MODE_EMBEDDED == pDocShell->GetCreateMode() )
1474 : 0 : pDocShell->Repaint();
1475 : : else
1476 : 0 : pViewSh->GetGraphicWindow().Invalidate();
1477 : : }
1478 : 0 : }
1479 : :
1480 : 0 : bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType, SmBraceNode** ppBraceNode) const {
1481 : 0 : const SmCaretPos pos = GetPosition();
1482 [ # # ]: 0 : if (!pos.IsValid()) {
1483 : 0 : return false;
1484 : : }
1485 : :
1486 : 0 : SmNode* pNode = pos.pSelectedNode;
1487 : :
1488 [ # # ]: 0 : if (pNode->GetType() == NTEXT) {
1489 : 0 : SmTextNode* pTextNode = static_cast<SmTextNode*>(pNode);
1490 [ # # ]: 0 : if (pos.Index < pTextNode->GetText().Len()) {
1491 : : // The cursor is on a text node and at the middle of it.
1492 : 0 : return false;
1493 : : }
1494 : : } else {
1495 [ # # ]: 0 : if (pos.Index < 1) {
1496 : 0 : return false;
1497 : : }
1498 : : }
1499 : :
1500 : 0 : while (true) {
1501 : 0 : SmStructureNode* pParentNode = pNode->GetParent();
1502 [ # # ]: 0 : if (!pParentNode) {
1503 : : // There's no brace body node in the ancestors.
1504 : 0 : return false;
1505 : : }
1506 : :
1507 [ # # ]: 0 : sal_uInt16 index = pNode->FindIndex();
1508 [ # # ][ # # ]: 0 : if (index + 1 != pParentNode->GetNumSubNodes()) {
1509 : : // The cursor is not at the tail at one of ancestor nodes.
1510 : 0 : return false;
1511 : : }
1512 : :
1513 : 0 : pNode = pParentNode;
1514 [ # # ]: 0 : if (pNode->GetType() == NBRACEBODY) {
1515 : : // Found the brace body node.
1516 : 0 : break;
1517 : : }
1518 : : }
1519 : :
1520 : 0 : SmStructureNode* pBraceNodeTmp = pNode->GetParent();
1521 [ # # ][ # # ]: 0 : if (!pBraceNodeTmp || pBraceNodeTmp->GetType() != NBRACE) {
[ # # ]
1522 : : // Brace node is invalid.
1523 : 0 : return false;
1524 : : }
1525 : :
1526 : 0 : SmBraceNode* pBraceNode = static_cast<SmBraceNode*>(pBraceNodeTmp);
1527 [ # # ]: 0 : SmMathSymbolNode* pClosingNode = pBraceNode->ClosingBrace();
1528 [ # # ]: 0 : if (!pClosingNode) {
1529 : : // Couldn't get closing symbol node.
1530 : 0 : return false;
1531 : : }
1532 : :
1533 : : // Check if the closing brace matches eBracketType.
1534 : 0 : SmTokenType eClosingTokenType = pClosingNode->GetToken().eType;
1535 [ # # # # : 0 : switch (eBracketType) {
# # # # #
# # ]
1536 [ # # ]: 0 : case NoneBrackets: if (eClosingTokenType != TNONE) { return false; } break;
1537 [ # # ]: 0 : case RoundBrackets: if (eClosingTokenType != TRPARENT) { return false; } break;
1538 [ # # ]: 0 : case SquareBrackets: if (eClosingTokenType != TRBRACKET) { return false; } break;
1539 [ # # ]: 0 : case DoubleSquareBrackets: if (eClosingTokenType != TRDBRACKET) { return false; } break;
1540 [ # # ]: 0 : case LineBrackets: if (eClosingTokenType != TRLINE) { return false; } break;
1541 [ # # ]: 0 : case DoubleLineBrackets: if (eClosingTokenType != TRDLINE) { return false; } break;
1542 [ # # ]: 0 : case CurlyBrackets: if (eClosingTokenType != TRBRACE) { return false; } break;
1543 [ # # ]: 0 : case AngleBrackets: if (eClosingTokenType != TRANGLE) { return false; } break;
1544 [ # # ]: 0 : case CeilBrackets: if (eClosingTokenType != TRCEIL) { return false; } break;
1545 [ # # ]: 0 : case FloorBrackets: if (eClosingTokenType != TRFLOOR) { return false; } break;
1546 : : default:
1547 : 0 : return false;
1548 : : }
1549 : :
1550 [ # # ]: 0 : if (ppBraceNode) {
1551 : 0 : *ppBraceNode = static_cast<SmBraceNode*>(pBraceNode);
1552 : : }
1553 : :
1554 : 0 : return true;
1555 : : }
1556 : :
1557 : 0 : void SmCursor::MoveAfterBracket(SmBraceNode* pBraceNode, bool bMoveAnchor)
1558 : : {
1559 : 0 : position->CaretPos.pSelectedNode = pBraceNode;
1560 : 0 : position->CaretPos.Index = 1;
1561 [ # # ]: 0 : if (bMoveAnchor) {
1562 : 0 : anchor->CaretPos.pSelectedNode = pBraceNode;
1563 : 0 : anchor->CaretPos.Index = 1;
1564 : : }
1565 : 0 : RequestRepaint();
1566 : 0 : }
1567 : :
1568 : :
1569 : : /////////////////////////////////////// SmNodeListParser ///////////////////////////////////////
1570 : :
1571 : 0 : SmNode* SmNodeListParser::Parse(SmNodeList* list, bool bDeleteErrorNodes){
1572 : 0 : pList = list;
1573 [ # # ]: 0 : if(bDeleteErrorNodes){
1574 : : //Delete error nodes
1575 : 0 : SmNodeList::iterator it = pList->begin();
1576 [ # # ]: 0 : while(it != pList->end()) {
1577 [ # # ]: 0 : if((*it)->GetType() == NERROR){
1578 : : //Delete and erase
1579 [ # # ][ # # ]: 0 : delete *it;
1580 [ # # ]: 0 : it = pList->erase(it);
1581 : : }else
1582 : 0 : ++it;
1583 : : }
1584 : : }
1585 : 0 : SmNode* retval = Expression();
1586 : 0 : pList = NULL;
1587 : 0 : return retval;
1588 : : }
1589 : :
1590 : 0 : SmNode* SmNodeListParser::Expression(){
1591 [ # # ]: 0 : SmNodeArray NodeArray;
1592 : : //Accept as many relations as there is
1593 [ # # ][ # # ]: 0 : while(Terminal())
1594 [ # # ][ # # ]: 0 : NodeArray.push_back(Relation());
1595 : :
1596 : : //Create SmExpressionNode, I hope SmToken() will do :)
1597 [ # # ][ # # ]: 0 : SmStructureNode* pExpr = new SmExpressionNode(SmToken());
[ # # ][ # # ]
1598 [ # # ]: 0 : pExpr->SetSubNodes(NodeArray);
1599 : 0 : return pExpr;
1600 : : }
1601 : :
1602 : 0 : SmNode* SmNodeListParser::Relation(){
1603 : : //Read a sum
1604 : 0 : SmNode* pLeft = Sum();
1605 : : //While we have tokens and the next is a relation
1606 [ # # ][ # # ]: 0 : while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
[ # # ]
1607 : : //Take the operator
1608 : 0 : SmNode* pOper = Take();
1609 : : //Find the right side of the relation
1610 : 0 : SmNode* pRight = Sum();
1611 : : //Create new SmBinHorNode
1612 [ # # ][ # # ]: 0 : SmStructureNode* pNewNode = new SmBinHorNode(SmToken());
1613 : 0 : pNewNode->SetSubNodes(pLeft, pOper, pRight);
1614 : 0 : pLeft = pNewNode;
1615 : : }
1616 : 0 : return pLeft;
1617 : : }
1618 : :
1619 : 0 : SmNode* SmNodeListParser::Sum(){
1620 : : //Read a product
1621 : 0 : SmNode* pLeft = Product();
1622 : : //While we have tokens and the next is a sum
1623 [ # # ][ # # ]: 0 : while(Terminal() && IsSumOperator(Terminal()->GetToken())){
[ # # ]
1624 : : //Take the operator
1625 : 0 : SmNode* pOper = Take();
1626 : : //Find the right side of the sum
1627 : 0 : SmNode* pRight = Product();
1628 : : //Create new SmBinHorNode
1629 [ # # ][ # # ]: 0 : SmStructureNode* pNewNode = new SmBinHorNode(SmToken());
1630 : 0 : pNewNode->SetSubNodes(pLeft, pOper, pRight);
1631 : 0 : pLeft = pNewNode;
1632 : : }
1633 : 0 : return pLeft;
1634 : : }
1635 : :
1636 : 0 : SmNode* SmNodeListParser::Product(){
1637 : : //Read a Factor
1638 : 0 : SmNode* pLeft = Factor();
1639 : : //While we have tokens and the next is a product
1640 [ # # ][ # # ]: 0 : while(Terminal() && IsProductOperator(Terminal()->GetToken())){
[ # # ]
1641 : : //Take the operator
1642 : 0 : SmNode* pOper = Take();
1643 : : //Find the right side of the operation
1644 : 0 : SmNode* pRight = Factor();
1645 : : //Create new SmBinHorNode
1646 [ # # ][ # # ]: 0 : SmStructureNode* pNewNode = new SmBinHorNode(SmToken());
1647 : 0 : pNewNode->SetSubNodes(pLeft, pOper, pRight);
1648 : 0 : pLeft = pNewNode;
1649 : : }
1650 : 0 : return pLeft;
1651 : : }
1652 : :
1653 : 0 : SmNode* SmNodeListParser::Factor(){
1654 : : //Read unary operations
1655 [ # # ]: 0 : if(!Terminal())
1656 : 0 : return Error();
1657 : : //Take care of unary operators
1658 [ # # ]: 0 : else if(IsUnaryOperator(Terminal()->GetToken()))
1659 : : {
1660 [ # # ][ # # ]: 0 : SmStructureNode *pUnary = new SmUnHorNode(SmToken());
1661 : 0 : SmNode *pOper = Terminal(),
1662 : : *pArg;
1663 : :
1664 [ # # ]: 0 : if(Next())
1665 : 0 : pArg = Factor();
1666 : : else
1667 : 0 : pArg = Error();
1668 : :
1669 : 0 : pUnary->SetSubNodes(pOper, pArg);
1670 : 0 : return pUnary;
1671 : : }
1672 : 0 : return Postfix();
1673 : : }
1674 : :
1675 : 0 : SmNode* SmNodeListParser::Postfix(){
1676 [ # # ]: 0 : if(!Terminal())
1677 : 0 : return Error();
1678 : 0 : SmNode *pArg = NULL;
1679 [ # # ]: 0 : if(IsPostfixOperator(Terminal()->GetToken()))
1680 : 0 : pArg = Error();
1681 [ # # ]: 0 : else if(IsOperator(Terminal()->GetToken()))
1682 : 0 : return Error();
1683 : : else
1684 : 0 : pArg = Take();
1685 [ # # ][ # # ]: 0 : while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
[ # # ]
1686 [ # # ][ # # ]: 0 : SmStructureNode *pUnary = new SmUnHorNode(SmToken());
1687 : 0 : SmNode *pOper = Take();
1688 : 0 : pUnary->SetSubNodes(pArg, pOper);
1689 : 0 : pArg = pUnary;
1690 : : }
1691 : 0 : return pArg;
1692 : : }
1693 : :
1694 : 0 : SmNode* SmNodeListParser::Error(){
1695 [ # # ][ # # ]: 0 : return new SmErrorNode(PE_UNEXPECTED_TOKEN, SmToken());
1696 : : }
1697 : :
1698 : 0 : bool SmNodeListParser::IsOperator(const SmToken &token) {
1699 : 0 : return IsRelationOperator(token) ||
1700 : 0 : IsSumOperator(token) ||
1701 : 0 : IsProductOperator(token) ||
1702 : 0 : IsUnaryOperator(token) ||
1703 [ # # ][ # # : 0 : IsPostfixOperator(token);
# # # # #
# ]
1704 : : }
1705 : :
1706 : 0 : bool SmNodeListParser::IsRelationOperator(const SmToken &token) {
1707 : 0 : return token.nGroup & TGRELATION;
1708 : : }
1709 : :
1710 : 0 : bool SmNodeListParser::IsSumOperator(const SmToken &token) {
1711 : 0 : return token.nGroup & TGSUM;
1712 : : }
1713 : :
1714 : 0 : bool SmNodeListParser::IsProductOperator(const SmToken &token) {
1715 : : return token.nGroup & TGPRODUCT &&
1716 : : token.eType != TWIDESLASH &&
1717 : : token.eType != TWIDEBACKSLASH &&
1718 : : token.eType != TUNDERBRACE &&
1719 : : token.eType != TOVERBRACE &&
1720 [ # # ][ # # ]: 0 : token.eType != TOVER;
[ # # ][ # # ]
[ # # ][ # # ]
1721 : : }
1722 : :
1723 : 0 : bool SmNodeListParser::IsUnaryOperator(const SmToken &token) {
1724 : : return token.nGroup & TGUNOPER &&
1725 : : (token.eType == TPLUS ||
1726 : : token.eType == TMINUS ||
1727 : : token.eType == TPLUSMINUS ||
1728 : : token.eType == TMINUSPLUS ||
1729 : : token.eType == TNEG ||
1730 [ # # ][ # # ]: 0 : token.eType == TUOPER);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1731 : : }
1732 : :
1733 : 0 : bool SmNodeListParser::IsPostfixOperator(const SmToken &token) {
1734 : 0 : return token.eType == TFACT;
1735 [ + - ][ + - ]: 30 : }
1736 : :
1737 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|