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 : : #ifndef CARET_H
29 : : #define CARET_H
30 : :
31 : : #include "node.hxx"
32 : :
33 : : /** Representation of caret position with an equantion */
34 : : struct SmCaretPos{
35 : 0 : SmCaretPos(SmNode* selectedNode = NULL, int iIndex = 0) {
36 : 0 : pSelectedNode = selectedNode;
37 : 0 : Index = iIndex;
38 : 0 : }
39 : : /** Selected node */
40 : : SmNode* pSelectedNode;
41 : : /** Index within the selected node
42 : : *
43 : : * 0: Position in front of a node
44 : : * 1: Position after a node or after first char in SmTextNode
45 : : * n: Position after n char in SmTextNode
46 : : *
47 : : * Notice how there's special cases for SmTextNode.
48 : : */
49 : : //TODO: Special cases for SmBlankNode is needed
50 : : //TODO: Consider forgetting about the todo above... As it's really unpleasent.
51 : : int Index;
52 : : /** True, if this is a valid caret position */
53 : 0 : bool IsValid() const { return pSelectedNode != NULL; }
54 : : bool operator!=(SmCaretPos pos) const {
55 : : return pos.pSelectedNode != pSelectedNode || Index != pos.Index;
56 : : }
57 : 0 : bool operator==(SmCaretPos pos) const {
58 [ # # ][ # # ]: 0 : return pos.pSelectedNode == pSelectedNode && Index == pos.Index;
59 : : }
60 : : /** Get the caret position after pNode, regardless of pNode
61 : : *
62 : : * Gets the caret position following pNode, this is SmCaretPos(pNode, 1).
63 : : * Unless pNode is an instance of SmTextNode, then the index is the text length.
64 : : */
65 : 0 : static SmCaretPos GetPosAfter(SmNode* pNode) {
66 [ # # ][ # # ]: 0 : if(pNode && pNode->GetType() == NTEXT)
[ # # ]
67 : 0 : return SmCaretPos(pNode, ((SmTextNode*)pNode)->GetText().Len());
68 : 0 : return SmCaretPos(pNode, 1);
69 : : }
70 : : };
71 : :
72 : : /** A line that represents a caret */
73 : : class SmCaretLine{
74 : : public:
75 : 0 : SmCaretLine(long left = 0, long top = 0, long height = 0) {
76 : 0 : _top = top;
77 : 0 : _left = left;
78 : 0 : _height = height;
79 : 0 : }
80 : 0 : long GetTop() const {return _top;}
81 : 0 : long GetLeft() const {return _left;}
82 : 0 : long GetHeight() const {return _height;}
83 : 0 : long SquaredDistanceX(SmCaretLine line) const{
84 : 0 : return (GetLeft() - line.GetLeft()) * (GetLeft() - line.GetLeft());
85 : : }
86 : 0 : long SquaredDistanceX(Point pos) const{
87 : 0 : return (GetLeft() - pos.X()) * (GetLeft() - pos.X());
88 : : }
89 : 0 : long SquaredDistanceY(SmCaretLine line) const{
90 : 0 : long d = GetTop() - line.GetTop();
91 [ # # ]: 0 : if(d < 0)
92 : 0 : d = (d * -1) - GetHeight();
93 : : else
94 : 0 : d = d - line.GetHeight();
95 [ # # ]: 0 : if(d < 0)
96 : 0 : return 0;
97 : 0 : return d * d;
98 : : }
99 : 0 : long SquaredDistanceY(Point pos) const{
100 : 0 : long d = GetTop() - pos.Y();
101 [ # # ]: 0 : if(d < 0)
102 : 0 : d = (d * -1) - GetHeight();
103 [ # # ]: 0 : if(d < 0)
104 : 0 : return 0;
105 : 0 : return d * d;
106 : : }
107 : : private:
108 : : long _top;
109 : : long _left;
110 : : long _height;
111 : : };
112 : :
113 : : /////////////////////////////// SmCaretPosGraph////////////////////////////////
114 : :
115 : : /** An entry in SmCaretPosGraph */
116 : : struct SmCaretPosGraphEntry{
117 : 0 : SmCaretPosGraphEntry(SmCaretPos pos = SmCaretPos(),
118 : : SmCaretPosGraphEntry* left = NULL,
119 : 0 : SmCaretPosGraphEntry* right = NULL){
120 : 0 : CaretPos = pos;
121 : 0 : Left = left;
122 : 0 : Right = right;
123 : 0 : }
124 : : /** Caret position */
125 : : SmCaretPos CaretPos;
126 : : /** Entry to the left visually */
127 : : SmCaretPosGraphEntry* Left;
128 : : /** Entry to the right visually */
129 : : SmCaretPosGraphEntry* Right;
130 : 0 : void SetRight(SmCaretPosGraphEntry* right){
131 : 0 : Right = right;
132 : 0 : }
133 : 0 : void SetLeft(SmCaretPosGraphEntry* left){
134 : 0 : Left = left;
135 : 0 : }
136 : : };
137 : :
138 : : /** Define SmCaretPosGraph to be less than one page 4096 */
139 : : #define SmCaretPosGraphSize 255
140 : :
141 : : class SmCaretPosGraph;
142 : :
143 : : /** Iterator for SmCaretPosGraph */
144 : : class SmCaretPosGraphIterator{
145 : : public:
146 : 0 : SmCaretPosGraphIterator(SmCaretPosGraph* graph){
147 : 0 : pGraph = graph;
148 : 0 : nOffset = 0;
149 : 0 : pEntry = NULL;
150 : 0 : }
151 : : /** Get the next entry, NULL if none */
152 : : SmCaretPosGraphEntry* Next();
153 : : /** Get the current entry, NULL if none */
154 : 0 : SmCaretPosGraphEntry* Current(){
155 : 0 : return pEntry;
156 : : }
157 : : /** Get the current entry, NULL if none */
158 : 0 : SmCaretPosGraphEntry* operator->(){
159 : 0 : return pEntry;
160 : : }
161 : : private:
162 : : /** Next entry to return */
163 : : int nOffset;
164 : : /** Current graph */
165 : : SmCaretPosGraph* pGraph;
166 : : /** Current entry */
167 : : SmCaretPosGraphEntry* pEntry;
168 : : };
169 : :
170 : :
171 : : /** A graph over all caret positions
172 : : * @remarks Graphs can only grow, entries cannot be removed!
173 : : */
174 : : class SmCaretPosGraph{
175 : : public:
176 [ # # ]: 0 : SmCaretPosGraph(){
177 : 0 : pNext = NULL;
178 : 0 : nOffset = 0;
179 : 0 : }
180 : : ~SmCaretPosGraph();
181 : : /** Add a caret position
182 : : * @remarks If Left and/or Right are set NULL, they will point back to the entry.
183 : : */
184 : : SmCaretPosGraphEntry* Add(SmCaretPosGraphEntry entry);
185 : : /** Add a caret position
186 : : * @remarks If left and/or right are set NULL, they will point back to the entry.
187 : : */
188 : 0 : SmCaretPosGraphEntry* Add(SmCaretPos pos,
189 : : SmCaretPosGraphEntry* left = NULL,
190 : : SmCaretPosGraphEntry* right = NULL){
191 : : OSL_ENSURE(pos.Index >= 0, "Index shouldn't be -1!");
192 [ # # ]: 0 : return Add(SmCaretPosGraphEntry(pos, left, right));
193 : : }
194 : : /** Get an iterator for this graph */
195 : 0 : SmCaretPosGraphIterator GetIterator(){
196 : 0 : return SmCaretPosGraphIterator(this);
197 : : }
198 : : friend class SmCaretPosGraphIterator;
199 : : private:
200 : : /** Next graph, to be used when this graph is full */
201 : : SmCaretPosGraph* pNext;
202 : : /** Next free entry in graph */
203 : : int nOffset;
204 : : /** Entries in this graph segment */
205 : : SmCaretPosGraphEntry Graph[SmCaretPosGraphSize];
206 : : };
207 : :
208 : : /** \page visual_formula_editing Visual Formula Editing
209 : : * A visual formula editor allows users to easily edit formulas without having to learn and
210 : : * use complicated commands. A visual formula editor is a WYSIWYG editor. For OpenOffice Math
211 : : * this essentially means that you can click on the formula image, to get a caret, which you
212 : : * can move with arrow keys, and use to modify the formula by entering text, clicking buttons
213 : : * or using shortcuts.
214 : : *
215 : : * \subsection formula_trees Formula Trees
216 : : * A formula in OpenOffice Math is a tree of nodes, take for instance the formula
217 : : * "A + {B cdot C} over D", it looks like this
218 : : * \f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$. The tree for this formula
219 : : * looks like this:
220 : : *
221 : : * \dot
222 : : * digraph {
223 : : * labelloc = "t";
224 : : * label= "Equation: \"A + {B cdot C} over D\"";
225 : : * size = "9,9";
226 : : * n0 [label="SmTableNode (1)"];
227 : : * n0 -> n1 [label="0"];
228 : : * n1 [label="SmLineNode (2)"];
229 : : * n1 -> n2 [label="0"];
230 : : * n2 [label="SmExpressionNode (3)"];
231 : : * n2 -> n3 [label="0"];
232 : : * n3 [label="SmBinHorNode (4)"];
233 : : * n3 -> n4 [label="0"];
234 : : * n4 [label="SmTextNode: A (5)"];
235 : : * n3 -> n5 [label="1"];
236 : : * n5 [label="SmMathSymbolNode: (6)"];
237 : : * n3 -> n6 [label="2"];
238 : : * n6 [label="SmBinVerNode (7)"];
239 : : * n6 -> n7 [label="0"];
240 : : * n7 [label="SmExpressionNode (8)"];
241 : : * n7 -> n8 [label="0"];
242 : : * n8 [label="SmBinHorNode (9)"];
243 : : * n8 -> n9 [label="0"];
244 : : * n9 [label="SmTextNode: B (10)"];
245 : : * n8 -> n10 [label="1"];
246 : : * n10 [label="SmMathSymbolNode: ⋅ (11)"];
247 : : * n8 -> n11 [label="2"];
248 : : * n11 [label="SmTextNode: C (12)"];
249 : : * n6 -> n12 [label="1"];
250 : : * n12 [label="SmRectangleNode (13)"];
251 : : * n6 -> n13 [label="2"];
252 : : * n13 [label="SmTextNode: D (14)"];
253 : : * }
254 : : * \enddot
255 : : *
256 : : * The vertices are nodes, their label says what kind of node and the number in parentheses is
257 : : * the identifier of the node (In practices a pointer is used instead of the id). The direction
258 : : * of the edges tells which node is parent and which is child. The label of the edges are the
259 : : * child node index number, given to SmNode::GetSubNode() of the parent to get the child node.
260 : : *
261 : : *
262 : : * \subsection visual_lines Visual Lines
263 : : *
264 : : * Inorder to do caret movement in visual lines, we need a definition of caret position and
265 : : * visual line. In a tree such as the above there are three visual lines. There's the outer most
266 : : * line, with entries such as
267 : : * \f$\mbox{A}\f$, \f$ + \f$ and \f$ \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$. Then there's
268 : : * the numerator line of the fraction it has entries \f$ \mbox{B} \f$, \f$ \cdot \f$ and \f$ \mbox{C} \f$.
269 : : * And last by not least there's the denominator line of the fraction it's only entry is \f$ \mbox{D} \f$.
270 : : *
271 : : * For visual editing it should be possible to place a caret on both sides of any line entry,
272 : : * consider a line entry a character or construction that in a line is treated as a character.
273 : : * Imagine the caret is placed to the right of the plus sign (id: 6), now if user presses
274 : : * backspace this should delete the plus sign (id: 6), and if the user presses delete this
275 : : * should delete the entire fraction (id: 7). This is because the caret is in the outer most
276 : : * line where the fraction is considered a line entry.
277 : : *
278 : : * However, inorder to prevent users from accidentally deleting large subtrees, just because
279 : : * they logically placed there caret a in the wrong line, require that complex constructions
280 : : * such as a fraction is selected before it is deleted. Thus in this case it wouldn't be
281 : : * deleted, but only selected and then deleted if the user hit delete again. Anyway, this is
282 : : * slightly off topic for now.
283 : : *
284 : : * Important about visual lines is that they don't always have an SmExpressionNode as root
285 : : * and the entries in a visual line is all the nodes of a subtree ordered left to right that
286 : : * isn't either an SmExpressionNode, SmBinHorNode or SmUnHorNode.
287 : : *
288 : : *
289 : : * \subsection caret_positions Caret Positions
290 : : *
291 : : * A caret position in OpenOffice Math is representated by an instance of SmCaretPos.
292 : : * That is a caret position is a node and an index related to this node. For most nodes the
293 : : * index 0, means caret is in front of this node, the index 1 means caret is after this node.
294 : : * For SmTextNode the index is the caret position after the specified number of characters,
295 : : * imagine an SmTextNode with the number 1337. The index 3 in such SmTextNode would mean a
296 : : * caret placed right before 7, e.g. "133|7".
297 : : *
298 : : * For SmExpressionNode, SmBinHorNode and SmUnHorNode the only legal index is 0, which means
299 : : * in front of the node. Actually the index 0 may only because for the first caret position
300 : : * in a visual line. From the example above, consider the following subtree that constitutes
301 : : * a visual line:
302 : : *
303 : : * \dot
304 : : * digraph {
305 : : * labelloc = "t";
306 : : * label= "Subtree that constitutes a visual line";
307 : : * size = "7,5";
308 : : * n7 [label="SmExpressionNode (8)"];
309 : : * n7 -> n8 [label="0"];
310 : : * n8 [label="SmBinHorNode (9)"];
311 : : * n8 -> n9 [label="0"];
312 : : * n9 [label="SmTextNode: B (10)"];
313 : : * n8 -> n10 [label="1"];
314 : : * n10 [label="SmMathSymbolNode: ⋅ (11)"];
315 : : * n8 -> n11 [label="2"];
316 : : * n11 [label="SmTextNode: C (12)"];
317 : : * }
318 : : * \enddot
319 : : * Here the caret positions are:
320 : : *
321 : : * <TABLE>
322 : : * <TR><TD><B>Caret position:</B></TD><TD><B>Example:</B></TD>
323 : : * </TR><TR>
324 : : * <TD>{id: 8, index: 0}</TD>
325 : : * <TD>\f$ \mid \mbox{C} \cdot \mbox{C} \f$</TD>
326 : : * </TR><TR>
327 : : * <TD>{id: 10, index: 1}</TD>
328 : : * <TD>\f$ \mbox{C} \mid \cdot \mbox{C} \f$</TD>
329 : : * </TR><TR>
330 : : * <TD>{id: 11, index: 1}</TD>
331 : : * <TD>\f$ \mbox{C} \cdot \mid \mbox{C} \f$</TD>
332 : : * </TR><TR>
333 : : * <TD>{id: 12, index: 1}</TD>
334 : : * <TD>\f$ \mbox{C} \cdot \mbox{C} \mid \f$</TD>
335 : : * </TR><TR>
336 : : * </TABLE>
337 : : *
338 : : * Where \f$ \mid \f$ is used to denote caret position.
339 : : *
340 : : * With these exceptions included in the definition the id and index: {id: 11, index: 0} does
341 : : * \b not constitute a caret position in the given context. Note the method
342 : : * SmCaretPos::IsValid() does not check if this invariant holds true, but code in SmCaret,
343 : : * SmSetSelectionVisitor and other places depends on this invariant to hold.
344 : : *
345 : : *
346 : : * \subsection caret_movement Caret Movement
347 : : *
348 : : * As the placement of caret positions depends very much on the context within which a node
349 : : * appears it is not trivial to find all caret positions and determine which follows which.
350 : : * In OpenOffice Math this is done by the SmCaretPosGraphBuildingVisitor. This visitor builds
351 : : * graph (an instnce of SmCaretPosGraph) over the caret positions. For details on how this
352 : : * graph is build, and how new methods should be implemented see SmCaretPosGraphBuildingVisitor.
353 : : *
354 : : * The result of the SmCaretPosGraphBuildingVisitor is a graph over the caret positions in a
355 : : * formula, representated by an instance of SmCaretPosGraph. Each entry (instances of SmCaretPosGraphEntry)
356 : : * has a pointer to the entry to the left and right of itself. This way we can easily find
357 : : * the caret position to a right or left of a given caret position. Note each caret position
358 : : * only appears once in this graph.
359 : : *
360 : : * When searching for a caret position after a left click on the formula this map is also used.
361 : : * We simply iterate over all entries, uses the SmCaretPos2LineVisitor to find a line for each
362 : : * caret position. Then the distance from the click to the line is computed and we choose the
363 : : * caret position closest to the click.
364 : : *
365 : : * For up and down movement, we also iterator over all caret positions and use SmCaretPos2LineVisitor
366 : : * to find a line for each caret position. Then we compute the distance from the current
367 : : * caret position to every other caret position and chooses the one closest that is either
368 : : * above or below the current caret position, depending on whether we're doing up or down movement.
369 : : *
370 : : * This result of this approach to caret movement is that we have logically predictable
371 : : * movement for left and right, whilst leftclick, up and down movement depends on the sizes
372 : : * and placement of all node and may be less logically predictable. This solution also means
373 : : * that we only have one complex visitor generating the graph, imagine the nightmare if we
374 : : * had a visitor for movement in each direction.
375 : : *
376 : : * Making up and down movement independent of node sizes and placement wouldn't necessarily
377 : : * be a good thing either. Consider the formula \f$ \frac{1+2+3+4+5}{6} \f$, if the caret is
378 : : * placed as displayed here: \f$ \frac{1+2+3+4+5}{6 \mid} \f$, up movement should move to right
379 : : * after "3": \f$ \frac{1+2+3|+4+5}{6} \f$. However, such a move depends on the sizes and placement
380 : : * of all nodes in the fraction.
381 : : *
382 : : *
383 : : * \subsubsection caretpos_graph_example Example of Caret Position Graph
384 : : *
385 : : * If we consider the formula
386 : : * \f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$ from \ref formula_trees.
387 : : * It has the following caret positions:
388 : : *
389 : : * <TABLE>
390 : : * <TR>
391 : : * <TD><B>Caret position:</B></TD>
392 : : * <TD><B>Example:</B></TD>
393 : : * </TR><TR>
394 : : * <TD>{id: 3, index: 0}</TD>
395 : : * <TD>\f$ \mid\mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD>
396 : : * </TR><TR>
397 : : * <TD>{id: 5, index: 1}</TD>
398 : : * <TD>\f$ \mbox{A}\mid + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD>
399 : : * </TR><TR>
400 : : * <TD>{id: 6, index: 1}</TD>
401 : : * <TD>\f$ \mbox{A} + \mid \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD>
402 : : * </TR><TR>
403 : : * <TD>{id: 8, index: 0}</TD>
404 : : * <TD>\f$ \mbox{A} + \frac{ \mid \mbox{B} \cdot \mbox{C}}{\mbox{D}} \f$</TD>
405 : : * </TR><TR>
406 : : * <TD>{id: 10, index: 1}</TD>
407 : : * <TD>\f$ \mbox{A} + \frac{\mbox{B} \mid \cdot \mbox{C}}{\mbox{D}} \f$</TD>
408 : : * </TR><TR>
409 : : * <TD>{id: 11, index: 1}</TD>
410 : : * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mid \mbox{C}}{\mbox{D}} \f$</TD>
411 : : * </TR><TR>
412 : : * <TD>{id: 12, index: 1}</TD>
413 : : * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C} \mid}{\mbox{D}} \f$</TD>
414 : : * </TR><TR>
415 : : * <TD>{id: 14, index: 0}</TD>
416 : : * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mid \mbox{D}} \f$</TD>
417 : : * </TR><TR>
418 : : * <TD>{id: 14, index: 1}</TD>
419 : : * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D} \mid} \f$</TD>
420 : : * </TR><TR>
421 : : * <TD>{id: 7, index: 1}</TD>
422 : : * <TD>\f$ \mbox{A} + \frac{\mbox{B} \cdot \mbox{C}}{\mbox{D}} \mid \f$</TD>
423 : : * </TR>
424 : : * </TABLE>
425 : : *
426 : : * Below is a directed graph over the caret postions and how you can move between them.
427 : : * \dot
428 : : * digraph {
429 : : * labelloc = "t";
430 : : * label= "Caret Position Graph";
431 : : * size = "4,6";
432 : : * p0 [label = "{id: 3, index: 0}"];
433 : : * p0 -> p1 [fontsize = 10.0, label = "right"];
434 : : * p1 [label = "{id: 5, index: 1}"];
435 : : * p1 -> p0 [fontsize = 10.0, label = "left"];
436 : : * p1 -> p2 [fontsize = 10.0, label = "right"];
437 : : * p2 [label = "{id: 6, index: 1}"];
438 : : * p2 -> p1 [fontsize = 10.0, label = "left"];
439 : : * p2 -> p3 [fontsize = 10.0, label = "right"];
440 : : * p3 [label = "{id: 8, index: 0}"];
441 : : * p3 -> p2 [fontsize = 10.0, label = "left"];
442 : : * p3 -> p4 [fontsize = 10.0, label = "right"];
443 : : * p4 [label = "{id: 10, index: 1}"];
444 : : * p4 -> p3 [fontsize = 10.0, label = "left"];
445 : : * p4 -> p5 [fontsize = 10.0, label = "right"];
446 : : * p5 [label = "{id: 11, index: 1}"];
447 : : * p5 -> p4 [fontsize = 10.0, label = "left"];
448 : : * p5 -> p6 [fontsize = 10.0, label = "right"];
449 : : * p6 [label = "{id: 12, index: 1}"];
450 : : * p6 -> p5 [fontsize = 10.0, label = "left"];
451 : : * p6 -> p9 [fontsize = 10.0, label = "right"];
452 : : * p7 [label = "{id: 14, index: 0}"];
453 : : * p7 -> p2 [fontsize = 10.0, label = "left"];
454 : : * p7 -> p8 [fontsize = 10.0, label = "right"];
455 : : * p8 [label = "{id: 14, index: 1}"];
456 : : * p8 -> p7 [fontsize = 10.0, label = "left"];
457 : : * p8 -> p9 [fontsize = 10.0, label = "right"];
458 : : * p9 [label = "{id: 7, index: 1}"];
459 : : * p9 -> p6 [fontsize = 10.0, label = "left"];
460 : : * }
461 : : * \enddot
462 : : */
463 : :
464 : : /* TODO: Write documentation about the following keywords:
465 : : *
466 : : * Visual Selections:
467 : : * - Show images
468 : : * - Talk about how the visitor does this
469 : : *
470 : : * Modifying a Visual Line:
471 : : * - Find top most non-compo of the line (e.g. The subtree that constitutes a line)
472 : : * - Make the line into a list
473 : : * - Edit the list, add/remove/modify nodes
474 : : * - Parse the list back into a subtree
475 : : * - Insert the new subtree where the old was taken
476 : : */
477 : :
478 : : #endif /* CARET_H */
479 : :
480 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|