summaryrefslogtreecommitdiffstats
path: root/lib/kformula/formulacursor.h
blob: 72cfb178191a191bcb4b6677b475d8d0025e74d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
/* This file is part of the KDE project
   Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
	              Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#ifndef FORMULACURSOR_H
#define FORMULACURSOR_H

#include <qstring.h>

#include "basicelement.h"
#include "kformuladefs.h"

KFORMULA_NAMESPACE_BEGIN

class FormulaElement;
class IndexElement;
class MatrixElement;
class NameSequence;
class RootElement;
class SymbolElement;
class TextElement;


/**
 * The selection. This might be a position selection or
 * an area. Each view will need one FormulaCursor.
 *
 * The @ref Container always uses the cursor to operate on
 * the element tree.
 *
 * Note that it is up to the elements to actually move the cursor.
 * (The cursor has no chance to know how.)
 */
class FormulaCursor {

    // Yes, we do have a friend.
    friend class SequenceElement;

public:

    /**
     * Creates a cursor and puts is at the beginning
     * of the formula.
     *
     * @param element the formula the cursor point to. This must not be 0.
     */
    FormulaCursor(FormulaElement* element);

    FormulaCursor& operator= (const FormulaCursor&);

    // where the cursor and the mark are
    uint getPos() const { return cursorPos; }
    int getMark() const { return markPos; }

    /**
     * Tells whether the cursor has changed since last cleaning.
     */
    bool hasChanged() const { return hasChangedFlag; }

    /**
     * Resets the cursor's change flag. The widget calls this
     * if it has drawn the cursor.
     */
    void clearChangedFlag() { hasChangedFlag = false; }

    /**
     * Returns wether we are in selection mode.
     */
    bool isSelectionMode() const { return selectionFlag; }

    /**
     * Returns wether there actually is a selection.
     */
    bool isSelection() const { return selectionFlag && (getPos() != getMark()); }

    /**
     * Sets the selection mode.
     */
    void setSelection(bool selection) { selectionFlag = selection; hasChangedFlag = true; }

    /**
     * Calculates the size of the cursor. Needs to be called before
     * the cursor can be drawn.
     */
    void calcCursorSize( const ContextStyle& context, bool smallCursor );

    /**
     * Draws the cursor at its current position.
     * The cursor will always be drawn in xor mode.
     */
    void draw( QPainter&, const ContextStyle& context, StyleAttributes& style,
			   bool smallCursor, bool activeCursor );


    // simple cursor movement.

    void moveLeft(int flag = NormalMovement);
    void moveRight(int flag = NormalMovement);
    void moveUp(int flag = NormalMovement);
    void moveDown(int flag = NormalMovement);

    void moveHome(int flag = NormalMovement);
    void moveEnd(int flag = NormalMovement);

    /** @returns whether the cursor is at the first position. */
    bool isHome() const;

    /** @returns whether the cursor is at the last position. */
    bool isEnd() const;

    // how to travel

    bool getLinearMovement() const { return linearMovement; }

    /**
     * Sets the cursor in linear mode. This means you can visit every
     * element just by moving left and right.
     */
    void setLinearMovement(bool linear) { linearMovement = linear; }

    /**
     * Moves the cursor inside the element. Selection is turned off.
     */
    void goInsideElement(BasicElement* element);

    // mouse selection

    void mousePress( const LuPixelPoint&, int flags );
    void mouseMove( const LuPixelPoint&, int flags );
    void mouseRelease( const LuPixelPoint&, int flags );

    /**
     * Inserts the child at the current position.
     * Ignores the selection.
     */
    void insert(BasicElement*, Direction = beforeCursor);

    /**
     * Inserts the listed children at the current position.
     * Ignores the selection.
     * The list will be emptied.
     */
    void insert(QPtrList<BasicElement>&,
                Direction = beforeCursor);

    /**
     * Removes the current selected children and returns them.
     * The cursor needs to be normal (that is be inside a SequenceElement)
     * for this to have any effect.
     */
    void remove(QPtrList<BasicElement>&,
                Direction = beforeCursor);


    /**
     * Replaces the current selection with the supplied element.
     * The replaced elements become the new element's main child's content.
     */
    void replaceSelectionWith(BasicElement*,
                              Direction = beforeCursor);

    /**
     * Replaces the element the cursor points to with its main child's
     * content.
     */
    BasicElement* replaceByMainChildContent(Direction = beforeCursor);

    /**
     * Trys to find the element we are the main child of and replace
     * it with our content.
     *
     * This is simply another form of replaceByMainChildContent. You
     * use this one if the cursor is normalized and inside the main child.
     */
    BasicElement* removeEnclosingElement(Direction = beforeCursor);

    /**
     * Returns wether the element the cursor points to should be replaced.
     * Elements are senseless as soon as they only contain a main child.
     */
    bool elementIsSenseless();


    // The range that is selected. Makes no sense if there is
    // no selection.

    int getSelectionStart() const { return QMIN(getPos(), getMark()); }
    int getSelectionEnd() const { return QMAX(getPos(), getMark()); }


    /**
     * Sets the cursor to a new position.
     * This gets called from the element that wants
     * to own the cursor. It is a mistake to call this if you aren't
     * an element.
     *
     * If you provide a mark >= 0 the selection gets turned on.
     * If there is a selection and you don't provide a mark the
     * current mark won't change.
     */
    void setTo(BasicElement* element, uint cursor, int mark=-1);

    void setPos(uint pos);
    void setMark(int mark);


    /**
     * The element we are in. In most cases this is a SequenceElement.
     * There is no way to place a cursor outside a SequenceElement by
     * normal movement.
     * But in special cases (e.g. if you remove an index from an
     * IndexElement) the cursor can be placed to odd places. This is
     * the reason why you have to normalize the cursor after each
     * removal.
     */
    BasicElement* getElement() { return current; }
    const BasicElement* getElement() const { return current; }


    /**
     * Moves the cursor to a normal position. That is somewhere
     * inside a SequenceElement.
     * You need to call this after each removal because the cursor
     * might point to some non existing place.
     */
    void normalize(Direction direction = beforeCursor);


    /**
     * Returns the sequence the cursor is in if we are normal. If not returns 0.
     */
    SequenceElement* normal();
    const SequenceElement* normal() const;

    /**
     * Returns the IndexElement the cursor is on or 0
     * if there is non.
     */
    IndexElement* getActiveIndexElement();

    /**
     * Returns the RootElement the cursor is on or 0
     * if there is non.
     */
    RootElement* getActiveRootElement();

    /**
     * Returns the SymbolElement the cursor is on or 0
     * if there is non.
     */
    SymbolElement* getActiveSymbolElement();

    /**
     * @returns the NameSequence the cursor is on or 0
     * if there is non.
     */
    NameSequence* getActiveNameSequence();

    /**
     * @returns the TextElement the cursor is on or 0.
     */
    TextElement* getActiveTextElement();

    /**
     * @returns the MatrixElement the cursor is on or 0.
     */
    MatrixElement* getActiveMatrixElement();

    /**
     * Selects the element the cursor points to (stands after)
     * if there is such an element and if there is no selection.
     */
    void selectActiveElement();

    /**
     * Stores the currently selected elements inside a dom.
     */
    void copy( QDomDocument& doc );

    /**
     * Inserts the elements that could be read from the dom into
     * the list. Returns true on success.
     */
    bool buildElementsFromDom( QDomElement root, QPtrList<BasicElement>& list );

    /**
     * Inserts the elements that could be read from the MathML dom into
     * the list. Returns true on success.
     */
    bool buildElementsFromMathMLDom( QDomElement root, QPtrList<BasicElement>& list );

    // undo/redo support

    /**
     * A black box that is supposed to contain everything
     * which is needed to describe a cursor. Only the cursor
     * itself is allowed to read it.
     */
    class CursorData {
        friend class FormulaCursor;
        BasicElement* current;
        uint cursorPos;
        int markPos;
        bool selectionFlag;
        bool linearMovement;
        bool readOnly;

        CursorData(BasicElement* c,
                   uint pos, int mark, bool selection, bool linear, bool ro)
            : current(c), cursorPos(pos), markPos(mark),
              selectionFlag(selection), linearMovement(linear),
              readOnly(ro) {}
    };

    /**
     * Creates a new CursorData object that describes the cursor.
     * It's up to the caller to delete this object.
     */
    CursorData* getCursorData();

    /**
     * Sets the cursor to where the CursorData points to. No checking is done
     * so you better make sure the point exists.
     */
    void setCursorData(CursorData* data);

    /**
     * The element is going to leave the formula with and all its children.
     */
    void elementWillVanish(BasicElement* element);

    /**
     * A new formula has been loaded. Our current element has to change.
     */
    void formulaLoaded(FormulaElement* rootElement);

    /**
     * @returns the point inside the formula widget where the cursor is.
     */
    const LuPixelPoint& getCursorPoint() const { return cursorPoint; }

    /**
     * @returns the area the cursor is currently on.
     */
    const LuPixelRect& getCursorSize() const { return cursorSize; }
    void addCursorSize( const LuPixelRect& rect ) { cursorSize |= rect; }

    /**
     * @returns whether we are allowed to alter the document.
     */
    bool isReadOnly() const;

    /**
     * Puts the widget in read only mode.
     */
    void setReadOnly(bool ro) { readOnly = ro; }

private:

    /**
     * Returns the child the cursor points to. Depending on the
     * direction this might be the child before or after the
     * cursor.
     *
     * Might be 0 is there is no such child.
     */
    BasicElement* getActiveChild(Direction direction);

    /**
     * Returns the child that is currently selected.
     *
     * Might be 0 is there is no such child. e.g. if there are more
     * than one element selected.
     */
    BasicElement* getSelectedChild();

    /**
     * Tells whether we currently point to the given elements
     * main child and to the place behind its last child.
     */
    bool pointsAfterMainChild(BasicElement*);

    /**
     * Sets the selection according to the shift key.
     */
    void handleSelectState(int flag);


    /**
     * The element the cursor is inside right now.
     */
    BasicElement* current;

    /**
     * The position the cursor in on inside the element.
     * Might be anything from 0 to current->children->size().
     *
     * This is where new elements are put in.
     */
    uint cursorPos;

    /**
     * The position of the mark. If we are in selection mode this
     * is the other side of the selected area.
     * Note that the mark always belongs to the same SequenceElement
     * as the cursor.
     */
    int markPos;

    /**
     * Tells whether there is a selection area.
     * (This is not equal to (markPos != -1).)
     */
    bool selectionFlag;

    /**
     * Tells whether we want to travel through all elements by
     * left and right movement.
     */
    bool linearMovement;

    /**
     * The point in the middle of the cursor. Gets updated
     * each time the cursor is drawn.
     */
    LuPixelPoint cursorPoint;

    /**
     * The area that is covered by the cursor. Gets updated
     * each time the cursor is drawn.
     */
    LuPixelRect cursorSize;

    /**
     * Tells whether the cursor has been changed. This is set
     * by any of the setSomething methods. It's used by the
     * widget the cursor belongs to.
     */
    bool hasChangedFlag;

    /**
     * Whether we are only allowed to read.
     */
    bool readOnly;
};

KFORMULA_NAMESPACE_END

#endif // FORMULACURSOR_H