Warning, file /office/calligra/libs/text/KoTextEditor_p.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002 * Copyright (C) 2009 Pierre Stirnweiss <pstirnweiss@googlemail.com>
0003 * Copyright (C) 2009 Thomas Zander <zander@kde.org>
0004 * Copyright (C) 2015 Soma Schliszka <soma.schliszka@gmail.com>
0005 *
0006 * This library is free software; you can redistribute it and/or
0007 * modify it under the terms of the GNU Library General Public
0008 * License as published by the Free Software Foundation; either
0009 * version 2 of the License, or (at your option) any later version.
0010 *
0011 * This library is distributed in the hope that it will be useful,
0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014 * Library General Public License for more details.
0015 *
0016 * You should have received a copy of the GNU Library General Public License
0017 * along with this library; see the file COPYING.LIB.  If not, write to
0018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019 * Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #ifndef KOTEXTEDITOR_P_H
0023 #define KOTEXTEDITOR_P_H
0024 
0025 #include "KoTextEditor.h"
0026 
0027 #include "KoTextDocument.h"
0028 #include "styles/KoParagraphStyle.h"
0029 #include "styles/KoStyleManager.h"
0030 #include "changetracker/KoChangeTracker.h"
0031 
0032 #include <klocalizedstring.h>
0033 #include <kundo2magicstring.h>
0034 
0035 #include <QStack>
0036 #include <QTextBlock>
0037 #include <QTextDocument>
0038 #include <QTextTableCell>
0039 #include <QTimer>
0040 
0041 class KUndo2Command;
0042 
0043 class Q_DECL_HIDDEN KoTextEditor::Private
0044 {
0045 public:
0046     enum State {
0047         NoOp,
0048         KeyPress,
0049         Delete,
0050         Format,
0051         Custom
0052     };
0053 
0054     explicit Private(KoTextEditor *qq, QTextDocument *document);
0055 
0056     ~Private() {}
0057 
0058     void documentCommandAdded();
0059     void updateState(State newState, const KUndo2MagicString &title = KUndo2MagicString());
0060 
0061     void newLine(KUndo2Command *parent);
0062     void clearCharFormatProperty(int propertyId);
0063 
0064     void emitTextFormatChanged();
0065 
0066     KoTextEditor *q;
0067     QTextCursor caret;
0068     QTextDocument *document;
0069     QStack<KUndo2Command*> commandStack;
0070     bool addNewCommand;
0071     bool dummyMacroAdded;
0072     int customCommandCount;
0073     KUndo2MagicString commandTitle;
0074 
0075     State editorState;
0076 
0077     bool editProtected;
0078     bool editProtectionCached;
0079 };
0080 
0081 class KoTextVisitor
0082 {
0083 public:
0084     /// The ObjectVisitingMode enum marks how was the visited object selected.
0085     enum ObjectVisitingMode {
0086         Partly,     /// The visited object (table, cell, ...) is just @b partly selected. (Eg. just one cell is selected in the visited table)
0087         Entirely,   /// The visited object (table, cell, ...) is @b entirely selected.
0088     };
0089 
0090     explicit KoTextVisitor(KoTextEditor *editor)
0091         : m_abortVisiting(false)
0092         , m_editor(editor)
0093     {
0094     }
0095 
0096     virtual ~KoTextVisitor() {}
0097     // called whenever a visit was prevented by editprotection
0098     virtual void nonVisit() {}
0099 
0100     virtual void visitFragmentSelection(QTextCursor &)
0101     {
0102     }
0103 
0104     /**
0105      * This method allows to perform custom operation when the visitor reaches a QTextTable
0106      * @param visitedTable pointer to the currently visited table object
0107      * @param visitingMode flag, marks if the table is just partly visited or entirely
0108      */
0109     virtual void visitTable(QTextTable *visitedTable, ObjectVisitingMode visitingMode)
0110     {
0111         Q_UNUSED(visitedTable);
0112         Q_UNUSED(visitingMode);
0113     }
0114 
0115     /**
0116      * This method allows to perform custom operation when the visitor reaches a QTextTableCell
0117      * @param visitedCell pointer to the currently visited cell object
0118      * @param visitingMode flag, marks if the cell is just partly visited or entirely
0119      */
0120     virtual void visitTableCell(QTextTableCell *visitedCell, ObjectVisitingMode visitingMode)
0121     {
0122         Q_UNUSED(visitedCell);
0123         Q_UNUSED(visitingMode);
0124     }
0125 
0126     // The default implementation calls visitFragmentSelection on each fragment.intersect.selection
0127     virtual void visitBlock(QTextBlock &block, const QTextCursor &caret)
0128     {
0129         for (QTextBlock::iterator it = block.begin(); it != block.end(); ++it) {
0130             QTextCursor fragmentSelection(caret);
0131             fragmentSelection.setPosition(qMax(caret.selectionStart(), it.fragment().position()));
0132             fragmentSelection.setPosition(qMin(caret.selectionEnd(), it.fragment().position() + it.fragment().length()), QTextCursor::KeepAnchor);
0133 
0134             if (fragmentSelection.anchor() >= fragmentSelection.position()) {
0135                 continue;
0136             }
0137 
0138             visitFragmentSelection(fragmentSelection);
0139         }
0140     }
0141 
0142     bool abortVisiting() { return m_abortVisiting;}
0143     void setAbortVisiting(bool abort) {m_abortVisiting = abort;}
0144     KoTextEditor * editor() const {return m_editor;}
0145 private:
0146     bool m_abortVisiting;
0147     KoTextEditor *m_editor;
0148 };
0149 
0150 class BlockFormatVisitor
0151 {
0152 public:
0153     BlockFormatVisitor() {}
0154     virtual ~BlockFormatVisitor() {}
0155 
0156     virtual void visit(QTextBlock &block) const = 0;
0157 
0158     static void visitSelection(KoTextEditor *editor, const BlockFormatVisitor &visitor, const KUndo2MagicString &title = kundo2_i18n("Format"), bool resetProperties = false, bool registerChange = true) {
0159         int start = qMin(editor->position(), editor->anchor());
0160         int end = qMax(editor->position(), editor->anchor());
0161 
0162         QTextBlock block = editor->block();
0163         if (block.position() > start)
0164             block = block.document()->findBlock(start);
0165 
0166         // now loop over all blocks that the selection contains and alter the text fragments where applicable.
0167         while (block.isValid() && block.position() <= end) {
0168             QTextBlockFormat prevFormat = block.blockFormat();
0169             if (resetProperties) {
0170                 if (KoTextDocument(editor->document()).styleManager()) {
0171                     KoParagraphStyle *old = KoTextDocument(editor->document()).styleManager()->paragraphStyle(block.blockFormat().intProperty(KoParagraphStyle::StyleId));
0172                     if (old)
0173                         old->unapplyStyle(block);
0174                 }
0175             }
0176             visitor.visit(block);
0177             QTextCursor cursor(block);
0178             QTextBlockFormat format = cursor.blockFormat();
0179             if (registerChange)
0180                 editor->registerTrackedChange(cursor, KoGenChange::FormatChange, title, format, prevFormat, true);
0181             block = block.next();
0182         }
0183     }
0184 };
0185 
0186 class CharFormatVisitor
0187 {
0188 public:
0189     CharFormatVisitor() {}
0190     virtual ~CharFormatVisitor() {}
0191 
0192     virtual void visit(QTextCharFormat &format) const = 0;
0193 
0194     static void visitSelection(KoTextEditor *editor, const CharFormatVisitor &visitor, const KUndo2MagicString &title = kundo2_i18n("Format"), bool registerChange = true) {
0195         int start = qMin(editor->position(), editor->anchor());
0196         int end = qMax(editor->position(), editor->anchor());
0197         if (start == end) { // just set a new one.
0198             QTextCharFormat format = editor->charFormat();
0199             visitor.visit(format);
0200 
0201             if (registerChange && KoTextDocument(editor->document()).changeTracker() && KoTextDocument(editor->document()).changeTracker()->recordChanges()) {
0202                 QTextCharFormat prevFormat(editor->charFormat());
0203 
0204                 int changeId = KoTextDocument(editor->document()).changeTracker()->getFormatChangeId(title, format, prevFormat, editor->charFormat().property( KoCharacterStyle::ChangeTrackerId ).toInt());
0205                 format.setProperty(KoCharacterStyle::ChangeTrackerId, changeId);
0206             }
0207 
0208             editor->cursor()->setCharFormat(format);
0209             return;
0210         }
0211 
0212         QTextBlock block = editor->block();
0213         if (block.position() > start)
0214             block = block.document()->findBlock(start);
0215 
0216         QList<QTextCursor> cursors;
0217         QVector<QTextCharFormat> formats;
0218         // now loop over all blocks that the selection contains and alter the text fragments where applicable.
0219         while (block.isValid() && block.position() < end) {
0220             QTextBlock::iterator iter = block.begin();
0221             while (! iter.atEnd()) {
0222                 QTextFragment fragment = iter.fragment();
0223                 if (fragment.position() > end)
0224                     break;
0225                 if (fragment.position() + fragment.length() <= start) {
0226                     ++iter;
0227                     continue;
0228                 }
0229 
0230                 QTextCursor cursor(block);
0231                 cursor.setPosition(fragment.position() + 1);
0232                 QTextCharFormat format = cursor.charFormat(); // this gets the format one char after the position.
0233                 visitor.visit(format);
0234 
0235                 if (registerChange && KoTextDocument(editor->document()).changeTracker() && KoTextDocument(editor->document()).changeTracker()->recordChanges()) {
0236                     QTextCharFormat prevFormat(cursor.charFormat());
0237 
0238                     int changeId = KoTextDocument(editor->document()).changeTracker()->getFormatChangeId(title, format, prevFormat, cursor.charFormat().property( KoCharacterStyle::ChangeTrackerId ).toInt());
0239                     format.setProperty(KoCharacterStyle::ChangeTrackerId, changeId);
0240                 }
0241 
0242                 cursor.setPosition(qMax(start, fragment.position()));
0243                 int to = qMin(end, fragment.position() + fragment.length());
0244                 cursor.setPosition(to, QTextCursor::KeepAnchor);
0245                 cursors.append(cursor);
0246                 formats.append(format);
0247 
0248                 QTextCharFormat prevFormat(cursor.charFormat());
0249                 if (registerChange)
0250                     editor->registerTrackedChange(cursor,KoGenChange::FormatChange,title, format, prevFormat, false); //this will lead to every fragment having a different change untill the change merging in registerTrackedChange checks also for formatChange or not?
0251 
0252                 ++iter;
0253             }
0254             block = block.next();
0255         }
0256         QVector<QTextCharFormat>::Iterator iter = formats.begin();
0257         foreach(QTextCursor cursor, cursors) {
0258             cursor.setCharFormat(*iter);
0259             ++iter;
0260         }
0261     }
0262 };
0263 
0264 #endif //KOTEXTEDITOR_P_H