File indexing completed on 2024-05-12 16:35:02

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
0003  * Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org>
0004  * Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
0005  * Copyright (C) 2008, 2012 Pierre Stirnweiss <pstirnweiss@googlemail.org>
0006  * Copyright (C) 2009 KO GmbH <cbo@kogmbh.com>
0007  * Copyright (C) 2011 Mojtaba Shahi Senobari <mojtaba.shahi3000@gmail.com>
0008  * Copyright (C) 2014 Denis Kuplyakov <dener.kup@gmail.com>
0009  *
0010  * This library is free software; you can redistribute it and/or
0011  * modify it under the terms of the GNU Library General Public
0012  * License as published by the Free Software Foundation; either
0013  * version 2 of the License, or (at your option) any later version.
0014  *
0015  * This library is distributed in the hope that it will be useful,
0016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0018  * Library General Public License for more details.
0019  *
0020  * You should have received a copy of the GNU Library General Public License
0021  * along with this library; see the file COPYING.LIB.  If not, write to
0022  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0023  * Boston, MA 02110-1301, USA.
0024  */
0025 
0026 #include "TextTool.h"
0027 
0028 #include "TextEditingPluginContainer.h"
0029 #include "dialogs/SimpleCharacterWidget.h"
0030 #include "dialogs/SimpleParagraphWidget.h"
0031 #include "dialogs/SimpleTableWidget.h"
0032 #include "dialogs/SimpleInsertWidget.h"
0033 #include "dialogs/ParagraphSettingsDialog.h"
0034 #include "dialogs/StyleManagerDialog.h"
0035 #include "dialogs/InsertCharacter.h"
0036 #include "dialogs/FontDia.h"
0037 #include "dialogs/TableDialog.h"
0038 #include "dialogs/SectionFormatDialog.h"
0039 #include "dialogs/SectionsSplitDialog.h"
0040 #include "commands/AutoResizeCommand.h"
0041 #include "commands/ChangeListLevelCommand.h"
0042 #include "FontSizeAction.h"
0043 #include "FontFamilyAction.h"
0044 #include "TextShapeDebug.h"
0045 
0046 #include <KoOdf.h>
0047 #include <KoCanvasBase.h>
0048 #include <KoShapeController.h>
0049 #include <KoCanvasController.h>
0050 #include <KoCanvasResourceManager.h>
0051 #include <KoSelection.h>
0052 #include <KoShapeManager.h>
0053 #include <KoPointerEvent.h>
0054 #include <KoColor.h>
0055 #include <KoColorBackground.h>
0056 #include <KoColorPopupAction.h>
0057 #include <KoTextDocumentLayout.h>
0058 #include <KoParagraphStyle.h>
0059 #include <KoToolSelection.h>
0060 #include <KoTextEditingPlugin.h>
0061 #include <KoTextEditingRegistry.h>
0062 #include <KoInlineTextObjectManager.h>
0063 #include <KoTextRangeManager.h>
0064 #include <KoStyleManager.h>
0065 #include <KoTextOdfSaveHelper.h>
0066 #include <KoTextDrag.h>
0067 #include <KoTextDocument.h>
0068 #include <KoTextEditor.h>
0069 #include <KoChangeTracker.h>
0070 #include <KoChangeTrackerElement.h>
0071 #include <KoInlineNote.h>
0072 #include <KoBookmark.h>
0073 #include <KoBookmarkManager.h>
0074 #include <KoListLevelProperties.h>
0075 #include <KoTextLayoutRootArea.h>
0076 //#include <ResizeTableCommand.h>
0077 #include <KoIcon.h>
0078 
0079 #include <krun.h>
0080 #include <kstandardshortcut.h>
0081 #include <kactionmenu.h>
0082 #include <kstandardaction.h>
0083 #include <ksharedconfig.h>
0084 #include <kmessagebox.h>
0085 
0086 #include <QMenu>
0087 #include <QMenuBar>
0088 #include <QAction>
0089 #include <QTextTable>
0090 #include <QTextList>
0091 #include <QTabWidget>
0092 #include <QTextDocumentFragment>
0093 #include <QToolTip>
0094 #include <QGraphicsObject>
0095 #include <QLinearGradient>
0096 #include <QBitmap>
0097 #include <QDrag>
0098 #include <QDragLeaveEvent>
0099 #include <QDragMoveEvent>
0100 #include <QDropEvent>
0101 
0102 #include "AnnotationTextShape.h"
0103 #define AnnotationShape_SHAPEID "AnnotationTextShapeID"
0104 #include "KoShapeBasedDocumentBase.h"
0105 #include <KoAnnotation.h>
0106 #include <KoShapeRegistry.h>
0107 #include <kuser.h>
0108 
0109 #include <KoDocumentRdfBase.h>
0110 
0111 #include <algorithm>
0112 
0113 class TextToolSelection : public KoToolSelection
0114 {
0115 public:
0116 
0117     TextToolSelection(QPointer<KoTextEditor> editor)
0118         : KoToolSelection(0)
0119         , m_editor(editor)
0120     {
0121     }
0122 
0123     bool hasSelection() override
0124     {
0125         if (!m_editor.isNull()) {
0126             return m_editor.data()->hasSelection();
0127         }
0128         return false;
0129     }
0130 
0131     QPointer<KoTextEditor> m_editor;
0132 };
0133 
0134 static bool hit(const QKeySequence &input, KStandardShortcut::StandardShortcut shortcut)
0135 {
0136     foreach (const QKeySequence & ks, KStandardShortcut::shortcut(shortcut)) {
0137         if (input == ks)
0138             return true;
0139     }
0140     return false;
0141 }
0142 
0143 TextTool::TextTool(KoCanvasBase *canvas)
0144         : KoToolBase(canvas)
0145         , m_textShape(0)
0146         , m_textShapeData(0)
0147         , m_changeTracker(0)
0148         , m_allowActions(true)
0149         , m_allowAddUndoCommand(true)
0150         , m_allowResourceManagerUpdates(true)
0151         , m_prevCursorPosition(-1)
0152         , m_caretTimer(this)
0153         , m_caretTimerState(true)
0154         , m_currentCommand(0)
0155         , m_currentCommandHasChildren(false)
0156         , m_specialCharacterDocker(0)
0157         , m_textTyping(false)
0158         , m_textDeleting(false)
0159         , m_editTipTimer(this)
0160         , m_delayedEnsureVisible(false)
0161         , m_toolSelection(0)
0162         , m_tableDraggedOnce(false)
0163         , m_tablePenMode(false)
0164         , m_lastImMicroFocus(QRectF(0,0,0,0))
0165         , m_drag(0)
0166 {
0167     setTextMode(true);
0168 
0169     createActions();
0170 
0171     m_unit = canvas->resourceManager()->unitResource(KoCanvasResourceManager::Unit);
0172 
0173     foreach (KoTextEditingPlugin* plugin, textEditingPluginContainer()->values()) {
0174         connect(plugin, SIGNAL(startMacro(QString)),
0175                 this, SLOT(startMacro(QString)));
0176         connect(plugin, SIGNAL(stopMacro()), this, SLOT(stopMacro()));
0177         const QHash<QString, QAction*> actions = plugin->actions();
0178         QHash<QString, QAction*>::ConstIterator i = actions.begin();
0179         while (i != actions.end()) {
0180             addAction(i.key(), i.value());
0181             ++i;
0182         }
0183     }
0184 
0185     // setup the context list.
0186     QList<QAction*> list;
0187     list.append(this->action("format_font"));
0188     foreach (const QString &key, KoTextEditingRegistry::instance()->keys()) {
0189         KoTextEditingFactory *factory =  KoTextEditingRegistry::instance()->value(key);
0190         if (factory && factory->showInMenu()) {
0191             QAction *a = new QAction(factory->title(), this);
0192             connect(a, &QAction::triggered, [this, factory] { startTextEditingPlugin(factory->id()); });
0193             list.append(a);
0194             addAction(QString("apply_%1").arg(factory->id()), a);
0195         }
0196     }
0197     setPopupActionList(list);
0198 
0199     connect(canvas->shapeManager()->selection(), SIGNAL(selectionChanged()), this, SLOT(shapeAddedToCanvas()));
0200 
0201     m_caretTimer.setInterval(500);
0202     connect(&m_caretTimer, SIGNAL(timeout()), this, SLOT(blinkCaret()));
0203 
0204     m_editTipTimer.setInterval(500);
0205     m_editTipTimer.setSingleShot(true);
0206     connect(&m_editTipTimer, SIGNAL(timeout()), this, SLOT(showEditTip()));
0207 }
0208 
0209 void TextTool::createActions()
0210 {
0211     bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality)
0212                              & KoCanvasResourceManager::NoAdvancedText);
0213 
0214     m_actionConfigureSection = new QAction(koIconNeededWithSubs("", "configure-text-section", "configure"), i18n("Configure current section"), this);
0215     addAction("configure_section", m_actionConfigureSection);
0216     connect(m_actionConfigureSection, SIGNAL(triggered(bool)), this, SLOT(configureSection()));
0217 
0218     m_actionInsertSection = new QAction(koIconNeededWithSubs("", "insert-text-section", "insert-text"), i18n("Insert new section"), this);
0219     addAction("insert_section", m_actionInsertSection);
0220     connect(m_actionInsertSection, SIGNAL(triggered(bool)), this, SLOT(insertNewSection()));
0221 
0222     m_actionSplitSections = new QAction(koIconNeededWithSubs("", "text-section-split", "split"), i18n("Insert paragraph between sections"), this);
0223     addAction("split_sections", m_actionSplitSections);
0224     connect(m_actionSplitSections, SIGNAL(triggered(bool)), this, SLOT(splitSections()));
0225 
0226     m_actionPasteAsText  = new QAction(koIcon("edit-paste"), i18n("Paste As Text"), this);
0227     addAction("edit_paste_text", m_actionPasteAsText);
0228     m_actionPasteAsText->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V);
0229     connect(m_actionPasteAsText, SIGNAL(triggered(bool)), this, SLOT(pasteAsText()));
0230 
0231     m_actionFormatBold  = new QAction(koIcon("format-text-bold"), i18n("Bold"), this);
0232     addAction("format_bold", m_actionFormatBold);
0233     m_actionFormatBold->setShortcut(Qt::CTRL + Qt::Key_B);
0234     m_actionFormatBold->setCheckable(true);
0235     connect(m_actionFormatBold, SIGNAL(triggered(bool)), this, SLOT(bold(bool)));
0236 
0237     m_actionFormatItalic  = new QAction(koIcon("format-text-italic"), i18n("Italic"), this);
0238     addAction("format_italic", m_actionFormatItalic);
0239     m_actionFormatItalic->setShortcut(Qt::CTRL + Qt::Key_I);
0240     m_actionFormatItalic->setCheckable(true);
0241     connect(m_actionFormatItalic, SIGNAL(triggered(bool)), this, SLOT(italic(bool)));
0242 
0243     m_actionFormatUnderline  = new QAction(koIcon("format-text-underline"), i18nc("Text formatting", "Underline"), this);
0244     addAction("format_underline", m_actionFormatUnderline);
0245     m_actionFormatUnderline->setShortcut(Qt::CTRL + Qt::Key_U);
0246     m_actionFormatUnderline->setCheckable(true);
0247     connect(m_actionFormatUnderline, SIGNAL(triggered(bool)), this, SLOT(underline(bool)));
0248 
0249     m_actionFormatStrikeOut  = new QAction(koIcon("format-text-strikethrough"), i18n("Strikethrough"), this);
0250     addAction("format_strike", m_actionFormatStrikeOut);
0251     m_actionFormatStrikeOut->setCheckable(true);
0252     connect(m_actionFormatStrikeOut, SIGNAL(triggered(bool)), this, SLOT(strikeOut(bool)));
0253 
0254     QActionGroup *alignmentGroup = new QActionGroup(this);
0255     m_actionAlignLeft  = new QAction(koIcon("format-justify-left"), i18n("Align Left"), this);
0256     addAction("format_alignleft", m_actionAlignLeft);
0257     m_actionAlignLeft->setShortcut(Qt::CTRL + Qt::Key_L);
0258     m_actionAlignLeft->setCheckable(true);
0259     alignmentGroup->addAction(m_actionAlignLeft);
0260     connect(m_actionAlignLeft, SIGNAL(triggered(bool)), this, SLOT(alignLeft()));
0261 
0262     m_actionAlignRight  = new QAction(koIcon("format-justify-right"), i18n("Align Right"), this);
0263     addAction("format_alignright", m_actionAlignRight);
0264     m_actionAlignRight->setShortcut(Qt::CTRL + Qt::Key_R);
0265     m_actionAlignRight->setCheckable(true);
0266     alignmentGroup->addAction(m_actionAlignRight);
0267     connect(m_actionAlignRight, SIGNAL(triggered(bool)), this, SLOT(alignRight()));
0268 
0269     m_actionAlignCenter  = new QAction(koIcon("format-justify-center"), i18n("Align Center"), this);
0270     addAction("format_aligncenter", m_actionAlignCenter);
0271     m_actionAlignCenter->setShortcut(Qt::CTRL + Qt::Key_E);
0272     m_actionAlignCenter->setCheckable(true);
0273 
0274     alignmentGroup->addAction(m_actionAlignCenter);
0275     connect(m_actionAlignCenter, SIGNAL(triggered(bool)), this, SLOT(alignCenter()));
0276 
0277     m_actionAlignBlock  = new QAction(koIcon("format-justify-fill"), i18n("Align Block"), this);
0278     addAction("format_alignblock", m_actionAlignBlock);
0279     m_actionAlignBlock->setShortcut(Qt::CTRL + Qt::Key_J);
0280     m_actionAlignBlock->setCheckable(true);
0281     alignmentGroup->addAction(m_actionAlignBlock);
0282     connect(m_actionAlignBlock, SIGNAL(triggered(bool)), this, SLOT(alignBlock()));
0283 
0284     m_actionChangeDirection = new QAction(koIcon("format-text-direction-rtl"), i18n("Change text direction"), this);
0285     addAction("change_text_direction", m_actionChangeDirection);
0286     m_actionChangeDirection->setToolTip(i18n("Change writing direction"));
0287     m_actionChangeDirection->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D);
0288     m_actionChangeDirection->setCheckable(true);
0289     connect(m_actionChangeDirection, SIGNAL(triggered()), this, SLOT(textDirectionChanged()));
0290 
0291 
0292     m_actionFormatSuper = new QAction(koIcon("format-text-superscript"), i18n("Superscript"), this);
0293     m_actionFormatSuper->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_P);
0294     addAction("format_super", m_actionFormatSuper);
0295     m_actionFormatSuper->setCheckable(true);
0296     connect(m_actionFormatSuper, SIGNAL(triggered(bool)), this, SLOT(superScript(bool)));
0297 
0298     m_actionFormatSub = new QAction(koIcon("format-text-subscript"), i18n("Subscript"), this);
0299     m_actionFormatSub->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_B);
0300     addAction("format_sub", m_actionFormatSub);
0301     m_actionFormatSub->setCheckable(true);
0302     connect(m_actionFormatSub, SIGNAL(triggered(bool)), this, SLOT(subScript(bool)));
0303 
0304     const char* const increaseIndentActionIconName =
0305         QApplication::isRightToLeft() ? koIconNameCStr("format-indent-less") : koIconNameCStr("format-indent-more");
0306     m_actionFormatIncreaseIndent = new QAction(
0307         QIcon::fromTheme(QLatin1String(increaseIndentActionIconName)),
0308         i18n("Increase Indent"), this);
0309     addAction("format_increaseindent", m_actionFormatIncreaseIndent);
0310     connect(m_actionFormatIncreaseIndent, SIGNAL(triggered()), this, SLOT(increaseIndent()));
0311 
0312     const char* const decreaseIndentActionIconName =
0313         QApplication::isRightToLeft() ? koIconNameCStr("format-indent-more") : koIconNameCStr("format-indent-less");
0314     m_actionFormatDecreaseIndent = new QAction(QIcon::fromTheme(QLatin1String(decreaseIndentActionIconName)),
0315                                                i18n("Decrease Indent"), this);
0316     addAction("format_decreaseindent", m_actionFormatDecreaseIndent);
0317     connect(m_actionFormatDecreaseIndent, SIGNAL(triggered()), this, SLOT(decreaseIndent()));
0318 
0319     QAction *action = new QAction(koIcon("format-list-unordered"),  i18n("Toggle List or List Level Formatting"), this);
0320     action->setToolTip(i18n("Toggle list on/off, or change format of current level"));
0321     addAction("format_list", action);
0322 
0323     action = new QAction(i18n("Increase Font Size"), this);
0324     action->setShortcut(Qt::CTRL + Qt::Key_Greater);
0325     addAction("fontsizeup", action);
0326     connect(action, SIGNAL(triggered()), this, SLOT(increaseFontSize()));
0327 
0328     action = new QAction(i18n("Decrease Font Size"), this);
0329     action->setShortcut(Qt::CTRL + Qt::Key_Less);
0330     addAction("fontsizedown", action);
0331     connect(action, SIGNAL(triggered()), this, SLOT(decreaseFontSize()));
0332 
0333     m_actionFormatFontFamily = new KoFontFamilyAction(this);
0334     m_actionFormatFontFamily->setText(i18n("Font Family"));
0335     addAction("format_fontfamily", m_actionFormatFontFamily);
0336     connect(m_actionFormatFontFamily, SIGNAL(triggered(QString)),
0337             this, SLOT(setFontFamily(QString)));
0338 
0339     m_variableMenu = new KActionMenu(i18n("Variable"), this);
0340     addAction("insert_variable", m_variableMenu);
0341 
0342     // ------------------- Actions with a key binding and no GUI item
0343     action  = new QAction(i18n("Insert Non-Breaking Space"), this);
0344     addAction("nonbreaking_space", action);
0345     action->setShortcut(Qt::CTRL + Qt::Key_Space);
0346     connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingSpace()));
0347 
0348     action  = new QAction(i18n("Insert Non-Breaking Hyphen"), this);
0349     addAction("nonbreaking_hyphen", action);
0350     action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Minus);
0351     connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingHyphen()));
0352 
0353     action  = new QAction(i18n("Insert Index"), this);
0354     action->setShortcut(Qt::CTRL + Qt::Key_T);
0355     addAction("insert_index", action);
0356     connect(action, SIGNAL(triggered()), this, SLOT(insertIndexMarker()));
0357 
0358     action  = new QAction(i18n("Insert Soft Hyphen"), this);
0359     addAction("soft_hyphen", action);
0360     //action->setShortcut(Qt::CTRL + Qt::Key_Minus); // TODO this one is also used for the kde-global zoom-out :(
0361     connect(action, SIGNAL(triggered()), this, SLOT(softHyphen()));
0362 
0363     if (useAdvancedText) {
0364         action  = new QAction(i18n("Line Break"), this);
0365         addAction("line_break", action);
0366         action->setShortcut(Qt::SHIFT + Qt::Key_Return);
0367         connect(action, SIGNAL(triggered()), this, SLOT(lineBreak()));
0368 
0369         action  = new QAction(koIcon("insert-page-break"), i18n("Page Break"), this);
0370         addAction("insert_framebreak", action);
0371         action->setShortcut(Qt::CTRL + Qt::Key_Return);
0372         connect(action, SIGNAL(triggered()), this, SLOT(insertFrameBreak()));
0373         action->setToolTip(i18n("Insert a page break"));
0374         action->setWhatsThis(i18n("All text after this point will be moved into the next page."));
0375     }
0376 
0377     action  = new QAction(i18n("Font..."), this);
0378     addAction("format_font", action);
0379     action->setShortcut(Qt::ALT + Qt::CTRL + Qt::Key_F);
0380     action->setToolTip(i18n("Change character size, font, boldface, italics etc."));
0381     action->setWhatsThis(i18n("Change the attributes of the currently selected characters."));
0382     connect(action, SIGNAL(triggered()), this, SLOT(selectFont()));
0383 
0384     m_actionFormatFontSize = new FontSizeAction(i18n("Font Size"), this);
0385     addAction("format_fontsize", m_actionFormatFontSize);
0386     connect(m_actionFormatFontSize, SIGNAL(fontSizeChanged(qreal)), this, SLOT(setFontSize(qreal)));
0387 
0388     m_actionFormatTextColor = new KoColorPopupAction(this);
0389     m_actionFormatTextColor->setIcon(koIcon("format-text-color"));
0390     m_actionFormatTextColor->setToolTip(i18n("Text Color..."));
0391     m_actionFormatTextColor->setText(i18n("Text Color"));
0392     addAction("format_textcolor", m_actionFormatTextColor);
0393     connect(m_actionFormatTextColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setTextColor(KoColor)));
0394 
0395     m_actionFormatBackgroundColor = new KoColorPopupAction(this);
0396     m_actionFormatBackgroundColor->setIcon(koIcon("format-fill-color"));
0397     m_actionFormatBackgroundColor->setToolTip(i18n("Background Color..."));
0398     m_actionFormatBackgroundColor->setText(i18n("Background"));
0399     addAction("format_backgroundcolor", m_actionFormatBackgroundColor);
0400     connect(m_actionFormatBackgroundColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setBackgroundColor(KoColor)));
0401 
0402     m_autoResizeAction = new QAction(koIcon("zoom-fit-best"), i18n("Auto Resize To Content"), this);
0403     addAction("auto_resize", m_autoResizeAction);
0404     m_autoResizeAction->setCheckable(true);
0405     connect(m_autoResizeAction, SIGNAL(triggered(bool)), this, SLOT(setAutoResize(bool)));
0406 
0407     m_growWidthAction = new QAction(koIcon("zoom-fit-best"), i18n("Grow To Fit Width"), this);
0408     addAction("grow_to_fit_width", m_growWidthAction);
0409     m_growWidthAction->setCheckable(true);
0410     connect(m_growWidthAction, SIGNAL(triggered(bool)), this, SLOT(setGrowWidthToFit(bool)));
0411 
0412     m_growHeightAction = new QAction(koIcon("zoom-fit-best"), i18n("Grow To Fit Height"), this);
0413     addAction("grow_to_fit_height", m_growHeightAction);
0414     m_growHeightAction->setCheckable(true);
0415     connect(m_growHeightAction, SIGNAL(triggered(bool)), this, SLOT(setGrowHeightToFit(bool)));
0416 
0417     m_shrinkToFitAction = new QAction(koIcon("zoom-fit-best"), i18n("Shrink To Fit"), this);
0418     addAction("shrink_to_fit", m_shrinkToFitAction);
0419     m_shrinkToFitAction->setCheckable(true);
0420     connect(m_shrinkToFitAction, SIGNAL(triggered(bool)), this, SLOT(setShrinkToFit(bool)));
0421 
0422     if (useAdvancedText) {
0423         action = new QAction(koIcon("insert-table"), i18n("Insert Custom..."), this);
0424         addAction("insert_table", action);
0425         action->setToolTip(i18n("Insert a table into the document."));
0426         connect(action, SIGNAL(triggered()), this, SLOT(insertTable()));
0427 
0428         action  = new QAction(koIcon("edit-table-insert-row-above"), i18n("Row Above"), this);
0429         action->setToolTip(i18n("Insert Row Above"));
0430         addAction("insert_tablerow_above", action);
0431         connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowAbove()));
0432 
0433         action  = new QAction(koIcon("edit-table-insert-row-below"), i18n("Row Below"), this);
0434         action->setToolTip(i18n("Insert Row Below"));
0435         addAction("insert_tablerow_below", action);
0436         connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowBelow()));
0437 
0438         action  = new QAction(koIcon("edit-table-insert-column-left"), i18n("Column Left"), this);
0439         action->setToolTip(i18n("Insert Column Left"));
0440         addAction("insert_tablecolumn_left", action);
0441         connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnLeft()));
0442 
0443         action  = new QAction(koIcon("edit-table-insert-column-right"), i18n("Column Right"), this);
0444         action->setToolTip(i18n("Insert Column Right"));
0445         addAction("insert_tablecolumn_right", action);
0446         connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnRight()));
0447 
0448         action  = new QAction(koIcon("edit-table-delete-column"), i18n("Column"), this);
0449         action->setToolTip(i18n("Delete Column"));
0450         addAction("delete_tablecolumn", action);
0451         connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableColumn()));
0452 
0453         action  = new QAction(koIcon("edit-table-delete-row"), i18n("Row"), this);
0454         action->setToolTip(i18n("Delete Row"));
0455         addAction("delete_tablerow", action);
0456         connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableRow()));
0457 
0458         action  = new QAction(koIcon("edit-table-cell-merge"), i18n("Merge Cells"), this);
0459         addAction("merge_tablecells", action);
0460         connect(action, SIGNAL(triggered(bool)), this, SLOT(mergeTableCells()));
0461 
0462         action  = new QAction(koIcon("edit-table-cell-split"), i18n("Split Cells"), this);
0463         addAction("split_tablecells", action);
0464         connect(action, SIGNAL(triggered(bool)), this, SLOT(splitTableCells()));
0465 
0466         action = new QAction(koIcon("borderpainter"), "", this);
0467         action->setToolTip(i18n("Select a border style and paint that style onto a table"));
0468         addAction("activate_borderpainter", action);
0469     }
0470 
0471     action = new QAction(i18n("Paragraph..."), this);
0472     addAction("format_paragraph", action);
0473     action->setShortcut(Qt::ALT + Qt::CTRL + Qt::Key_P);
0474     action->setToolTip(i18n("Change paragraph margins, text flow, borders, bullets, numbering etc."));
0475     action->setWhatsThis(i18n("<p>Change paragraph margins, text flow, borders, bullets, numbering etc.</p><p>Select text in multiple paragraphs to change the formatting of all selected paragraphs.</p><p>If no text is selected, the paragraph where the cursor is located will be changed.</p>"));
0476     connect(action, SIGNAL(triggered()), this, SLOT(formatParagraph()));
0477 
0478     action = new QAction(i18n("Style Manager..."), this);
0479     action->setShortcut(Qt::ALT + Qt::CTRL + Qt::Key_S);
0480     action->setToolTip(i18n("Change attributes of styles"));
0481     action->setWhatsThis(i18n("<p>Change font and paragraph attributes of styles.</p><p>Multiple styles can be changed using the dialog box.</p>"));
0482     addAction("format_stylist", action);
0483     connect(action, SIGNAL(triggered()), this, SLOT(showStyleManager()));
0484 
0485     action = KStandardAction::selectAll(this, SLOT(selectAll()), this);
0486     addAction("edit_select_all", action);
0487 
0488     action = new QAction(i18n("Special Character..."), this);
0489     action->setIcon(koIcon("character-set"));
0490     action->setShortcut(Qt::ALT + Qt::SHIFT + Qt::Key_C);
0491     addAction("insert_specialchar", action);
0492     action->setToolTip(i18n("Insert one or more symbols or characters not found on the keyboard"));
0493     action->setWhatsThis(i18n("Insert one or more symbols or characters not found on the keyboard."));
0494     connect(action, SIGNAL(triggered()), this, SLOT(insertSpecialCharacter()));
0495 
0496     action = new QAction(i18n("Repaint"), this);
0497     action->setIcon(koIcon("view-refresh"));
0498     addAction("repaint", action);
0499     connect(action, SIGNAL(triggered()), this, SLOT(relayoutContent()));
0500 
0501     action = new QAction(i18n("Insert Comment"), this);
0502     addAction("insert_annotation", action);
0503     action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C);
0504     connect(action, SIGNAL(triggered()), this, SLOT(insertAnnotation()));
0505 
0506 #ifndef NDEBUG
0507     action = new QAction("Paragraph Debug", this); // do NOT add i18n!
0508     action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_P);
0509     addAction("detailed_debug_paragraphs", action);
0510     connect(action, SIGNAL(triggered()), this, SLOT(debugTextDocument()));
0511     action = new QAction("Styles Debug", this); // do NOT add i18n!
0512     action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_S);
0513     addAction("detailed_debug_styles", action);
0514     connect(action, SIGNAL(triggered()), this, SLOT(debugTextStyles()));
0515 #endif
0516 }
0517 
0518 
0519 #ifndef NDEBUG
0520 #include "tests/MockShapes.h"
0521 #include <kundo2stack.h>
0522 #include <QMimeDatabase>
0523 #include <QMimeType>
0524 
0525 TextTool::TextTool(MockCanvas *canvas)  // constructor for our unit tests;
0526     : KoToolBase(canvas),
0527     m_textShape(0),
0528     m_textShapeData(0),
0529     m_changeTracker(0),
0530     m_allowActions(true),
0531     m_allowAddUndoCommand(true),
0532     m_allowResourceManagerUpdates(true),
0533     m_prevCursorPosition(-1),
0534     m_caretTimer(this),
0535     m_caretTimerState(true),
0536     m_currentCommand(0),
0537     m_currentCommandHasChildren(false),
0538     m_specialCharacterDocker(0),
0539     m_textEditingPlugins(0)
0540     , m_editTipTimer(this)
0541     , m_delayedEnsureVisible(false)
0542     , m_tableDraggedOnce(false)
0543     , m_tablePenMode(false)
0544 {
0545     // we could init some vars here, but we probably don't have to
0546     QLocale::setDefault(QLocale("en"));
0547     QTextDocument *document = new QTextDocument(); // this document is leaked
0548 
0549     KoInlineTextObjectManager *inlineManager = new KoInlineTextObjectManager();
0550     KoTextDocument(document).setInlineTextObjectManager(inlineManager);
0551 
0552     KoTextRangeManager *locationManager = new KoTextRangeManager();
0553     KoTextDocument(document).setTextRangeManager(locationManager);
0554 
0555     m_textEditor = new KoTextEditor(document);
0556     KoTextDocument(document).setTextEditor(m_textEditor.data());
0557     m_toolSelection = new TextToolSelection(m_textEditor);
0558 
0559     m_changeTracker = new KoChangeTracker();
0560     KoTextDocument(document).setChangeTracker(m_changeTracker);
0561 
0562     KoTextDocument(document).setUndoStack(new KUndo2Stack());
0563 }
0564 #endif
0565 
0566 TextTool::~TextTool()
0567 {
0568     delete m_toolSelection;
0569 }
0570 
0571 void TextTool::showEditTip()
0572 {
0573     if (!m_textShapeData || m_editTipPointedAt.position == -1)
0574         return;
0575 
0576     QTextCursor c(m_textShapeData->document());
0577     c.setPosition(m_editTipPointedAt.position);
0578     QString text = "<p align=center style=\'white-space:pre\' >";
0579     int toolTipWidth = 0;
0580 
0581     if (m_changeTracker && m_changeTracker->containsInlineChanges(c.charFormat())
0582         && m_changeTracker->displayChanges()) {
0583         KoChangeTrackerElement *element = m_changeTracker->elementById(c.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt());
0584         if (element->isEnabled()) {
0585             QString changeType;
0586             if (element->getChangeType() == KoGenChange::InsertChange)
0587                 changeType = i18n("Insertion");
0588             else if (element->getChangeType() == KoGenChange::DeleteChange)
0589                 changeType = i18n("Deletion");
0590             else
0591                 changeType = i18n("Formatting");
0592 
0593             text += "<b>" + changeType + "</b><br/>";
0594 
0595             QString date = element->getDate();
0596             //Remove the T which separates the Data and Time.
0597             date[10] = QLatin1Char(' ');
0598             date = element->getCreator() + QLatin1Char(' ') + date;
0599             text += date + "</p>";
0600 
0601             toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(date).width();
0602         }
0603     }
0604 
0605     if (m_editTipPointedAt.bookmark || !m_editTipPointedAt.externalHRef.isEmpty()) {
0606             QString help = i18n("Ctrl+click to go to link ");
0607             help += m_editTipPointedAt.externalHRef;
0608             text += help + "</p>";
0609             toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width();
0610     }
0611 
0612     if (m_editTipPointedAt.note) {
0613             QString help = i18n("Ctrl+click to go to the note ");
0614             text += help + "</p>";
0615             toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width();
0616     }
0617 
0618     if (m_editTipPointedAt.noteReference>0) {
0619             QString help = i18n("Ctrl+click to go to the note reference");
0620             text += help + "</p>";
0621             toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width();
0622     }
0623 
0624     QToolTip::hideText();
0625 
0626     if (toolTipWidth) {
0627         QRect keepRect(m_editTipPos - QPoint(3,3), QSize(6,6));
0628         QToolTip::showText(m_editTipPos - QPoint(toolTipWidth/2, 0), text, canvas()->canvasWidget(), keepRect);
0629     }
0630 
0631 }
0632 
0633 void TextTool::blinkCaret()
0634 {
0635     if (!(canvas()->canvasWidget() ? canvas()->canvasWidget()->hasFocus() : canvas()->canvasItem()->hasFocus())) {
0636         m_caretTimer.stop();
0637         m_caretTimerState = false; // not visible.
0638     }
0639     else {
0640         m_caretTimerState = !m_caretTimerState;
0641     }
0642     repaintCaret();
0643 }
0644 
0645 void TextTool::relayoutContent()
0646 {
0647     KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout*>(m_textShapeData->document()->documentLayout());
0648     Q_ASSERT(lay);
0649     foreach (KoTextLayoutRootArea *rootArea, lay->rootAreas()) {
0650         rootArea->setDirty();
0651     }
0652     lay->emitLayoutIsDirty();
0653 }
0654 
0655 void TextTool::paint(QPainter &painter, const KoViewConverter &converter)
0656 {
0657     if (m_textEditor.isNull())
0658         return;
0659     if (canvas()
0660             && (( canvas()->canvasWidget() && canvas()->canvasWidget()->hasFocus())
0661                   || (canvas()->canvasItem() && canvas()->canvasItem()->hasFocus())
0662                )
0663             && !m_caretTimer.isActive()) { // make sure we blink
0664         m_caretTimer.start();
0665         m_caretTimerState = true;
0666     }
0667     if (!m_caretTimerState)
0668         m_caretTimer.setInterval(500); // we set it lower during typing, so set it back to normal
0669 
0670     if (!m_textShapeData)
0671         return;
0672     if (m_textShapeData->isDirty())
0673         return;
0674 
0675     qreal zoomX, zoomY;
0676     converter.zoom(&zoomX, &zoomY);
0677 
0678     painter.save();
0679     QTransform shapeMatrix = m_textShape->absoluteTransformation(&converter);
0680     shapeMatrix.scale(zoomX, zoomY);
0681     shapeMatrix.translate(0, -m_textShapeData->documentOffset());
0682 
0683     // Possibly draw table dragging visual cues
0684     const qreal boxHeight = 20;
0685     if (m_tableDragInfo.tableHit == KoPointedAt::ColumnDivider) {
0686         QPointF anchorPos = m_tableDragInfo.tableDividerPos - QPointF(m_dx, 0.0);
0687         if (m_tableDragInfo.tableColumnDivider > 0) {
0688             //let's draw left
0689             qreal w = m_tableDragInfo.tableLeadSize - m_dx;
0690             QRectF rect(anchorPos - QPointF(w, 0.0), QSizeF(w, 0.0));
0691             QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight()));
0692             drawRect.setHeight(boxHeight);
0693             drawRect.moveTop(drawRect.top() - 1.5 * boxHeight);
0694             QString label = m_unit.toUserStringValue(w);
0695             int labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
0696             painter.fillRect(drawRect, QColor(64, 255, 64, 196));
0697             painter.setPen(QPen(QColor(0, 0, 0, 196), 0));
0698             if (labelWidth + 10 < drawRect.width()) {
0699                 QPointF centerLeft(drawRect.left(), drawRect.center().y());
0700                 QPointF centerRight(drawRect.right(), drawRect.center().y());
0701                 painter.drawLine(centerLeft, drawRect.center() - QPointF(labelWidth/2+5, 0.0));
0702                 painter.drawLine(centerLeft, centerLeft + QPointF(7, -5));
0703                 painter.drawLine(centerLeft, centerLeft + QPointF(7, 5));
0704                 painter.drawLine(drawRect.center() + QPointF(labelWidth/2+5, 0.0), centerRight);
0705                 painter.drawLine(centerRight, centerRight + QPointF(-7, -5));
0706                 painter.drawLine(centerRight, centerRight + QPointF(-7, 5));
0707                 painter.drawText(drawRect, Qt::AlignCenter, label);
0708             }
0709         }
0710         if (m_tableDragInfo.tableColumnDivider <  m_tableDragInfo.table->columns()) {
0711             //let's draw right
0712             qreal w = m_tableDragInfo.tableTrailSize + m_dx;
0713             QRectF rect(anchorPos, QSizeF(w, 0.0));
0714             QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight()));
0715             drawRect.setHeight(boxHeight);
0716             drawRect.moveTop(drawRect.top() - 1.5 * boxHeight);
0717             QString label;
0718             int labelWidth;
0719             if (m_tableDragWithShift) {
0720                 label = i18n("follows along");
0721                 labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
0722                 drawRect.setWidth(2 * labelWidth);
0723                 QLinearGradient g(drawRect.topLeft(), drawRect.topRight());
0724                 g.setColorAt(0.6, QColor(255, 64, 64, 196));
0725                 g.setColorAt(1.0, QColor(255, 64, 64, 0));
0726                 QBrush brush(g);
0727                 painter.fillRect(drawRect, brush);
0728             } else {
0729                 label = m_unit.toUserStringValue(w);
0730                 labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
0731                 drawRect.setHeight(boxHeight);
0732                 painter.fillRect(drawRect, QColor(64, 255, 64, 196));
0733             }
0734             painter.setPen(QPen(QColor(0, 0, 0, 196), 0));
0735             if (labelWidth + 10 < drawRect.width()) {
0736                 QPointF centerLeft(drawRect.left(), drawRect.center().y());
0737                 QPointF centerRight(drawRect.right(), drawRect.center().y());
0738                 painter.drawLine(centerLeft, drawRect.center() - QPointF(labelWidth/2+5, 0.0));
0739                 painter.drawLine(centerLeft, centerLeft + QPointF(7, -5));
0740                 painter.drawLine(centerLeft, centerLeft + QPointF(7, 5));
0741                 if (!m_tableDragWithShift) {
0742                     painter.drawLine(drawRect.center() + QPointF(labelWidth/2+5, 0.0), centerRight);
0743                     painter.drawLine(centerRight, centerRight + QPointF(-7, -5));
0744                     painter.drawLine(centerRight, centerRight + QPointF(-7, 5));
0745                 }
0746                 painter.drawText(drawRect, Qt::AlignCenter, label);
0747             }
0748             if (!m_tableDragWithShift) {
0749                 // let's draw a helper text too
0750                 label = i18n("Press shift to not resize this");
0751                 labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width();
0752                 labelWidth += 10;
0753                 //if (labelWidth < drawRect.width())
0754                 {
0755                     drawRect.moveTop(drawRect.top() + boxHeight);
0756                     drawRect.moveLeft(drawRect.left() + (drawRect.width() - labelWidth)/2);
0757                     drawRect.setWidth(labelWidth);
0758                     painter.fillRect(drawRect, QColor(64, 255, 64, 196));
0759                     painter.drawText(drawRect, Qt::AlignCenter, label);
0760                 }
0761             }
0762         }
0763     }
0764     // Possibly draw table dragging visual cues
0765     if (m_tableDragInfo.tableHit == KoPointedAt::RowDivider) {
0766         QPointF anchorPos = m_tableDragInfo.tableDividerPos - QPointF(0.0, m_dy);
0767         if (m_tableDragInfo.tableRowDivider > 0) {
0768             qreal h = m_tableDragInfo.tableLeadSize - m_dy;
0769             QRectF rect(anchorPos - QPointF(0.0, h), QSizeF(0.0, h));
0770             QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight()));
0771             drawRect.setWidth(boxHeight);
0772             drawRect.moveLeft(drawRect.left() - 1.5 * boxHeight);
0773             QString label = m_unit.toUserStringValue(h);
0774             QRectF labelRect = QFontMetrics(QToolTip::font()).boundingRect(label);
0775             labelRect.setHeight(boxHeight);
0776             labelRect.setWidth(labelRect.width() + 10);
0777             labelRect.moveTopLeft(drawRect.center() - QPointF(labelRect.width(), labelRect.height())/2);
0778             painter.fillRect(drawRect, QColor(64, 255, 64, 196));
0779             painter.fillRect(labelRect, QColor(64, 255, 64, 196));
0780             painter.setPen(QPen(QColor(0, 0, 0, 196), 0));
0781             if (labelRect.height() + 10 < drawRect.height()) {
0782                 QPointF centerTop(drawRect.center().x(), drawRect.top());
0783                 QPointF centerBottom(drawRect.center().x(), drawRect.bottom());
0784                 painter.drawLine(centerTop, drawRect.center() - QPointF(0.0, labelRect.height()/2+5));
0785                 painter.drawLine(centerTop, centerTop + QPointF(-5, 7));
0786                 painter.drawLine(centerTop, centerTop + QPointF(5, 7));
0787                 painter.drawLine(drawRect.center() + QPointF(0.0, labelRect.height()/2+5), centerBottom);
0788                 painter.drawLine(centerBottom, centerBottom + QPointF(-5, -7));
0789                 painter.drawLine(centerBottom, centerBottom + QPointF(5, -7));
0790             }
0791             painter.drawText(labelRect, Qt::AlignCenter, label);
0792         }
0793     }
0794     if (m_caretTimerState) {
0795         // Lets draw the caret ourselves, as the Qt method doesn't take cursor
0796         // charFormat into consideration.
0797         QTextBlock block = m_textEditor.data()->block();
0798         if (block.isValid()) {
0799             int posInParag = m_textEditor.data()->position() - block.position();
0800             if (posInParag <= block.layout()->preeditAreaPosition())
0801                 posInParag += block.layout()->preeditAreaText().length();
0802 
0803             QTextLine tl = block.layout()->lineForTextPosition(m_textEditor.data()->position() - block.position());
0804             if (tl.isValid()) {
0805                 painter.setRenderHint(QPainter::Antialiasing, false);
0806                 QRectF rect = caretRect(m_textEditor.data()->cursor());
0807                 QPointF baselinePoint;
0808                 if (tl.ascent() > 0) {
0809                     QFontMetricsF fm(m_textEditor.data()->charFormat().font(), painter.device());
0810                     rect.setY(rect.y() + tl.ascent() - qMin(tl.ascent(), fm.ascent()));
0811                     rect.setHeight(qMin(tl.ascent(), fm.ascent()) + qMin(tl.descent(), fm.descent()));
0812                     baselinePoint = QPoint(rect.x(), rect.y() + tl.ascent());
0813                 } else {
0814                     //line only filled with characters-without-size (eg anchors)
0815                     // layout will make sure line has height of block font
0816                     QFontMetricsF fm(block.charFormat().font(), painter.device());
0817                     rect.setHeight(fm.ascent() + fm.descent());
0818                     baselinePoint = QPoint(rect.x(), rect.y() + fm.ascent());
0819                 }
0820                 QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomLeft()));
0821                 drawRect.setWidth(2);
0822                 painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
0823                 if (m_textEditor.data()->isEditProtected(true)) {
0824                     QRectF circleRect(shapeMatrix.map(baselinePoint),QSizeF(14, 14));
0825                     circleRect.translate(-6.5, -6.5);
0826                     QPen pen(QColor(16, 255, 255));
0827                     pen.setWidthF(2.0);
0828                     painter.setPen(pen);
0829                     painter.setRenderHint(QPainter::Antialiasing, true);
0830                     painter.drawEllipse(circleRect);
0831                     painter.drawLine(circleRect.topLeft() + QPointF(4.5,4.5),
0832                                     circleRect.bottomRight() - QPointF(4.5,4.5));
0833                 } else {
0834                     painter.fillRect(drawRect, QColor(128, 255, 128));
0835                 }
0836             }
0837         }
0838     }
0839 
0840     painter.restore();
0841 }
0842 
0843 void TextTool::updateSelectedShape(const QPointF &point, bool noDocumentChange)
0844 {
0845     QRectF area(point, QSizeF(1, 1));
0846     if (m_textEditor.data()->hasSelection())
0847         repaintSelection();
0848     else
0849         repaintCaret();
0850     QList<KoShape*> sortedShapes = canvas()->shapeManager()->shapesAt(area, true);
0851     std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
0852     for (int count = sortedShapes.count() - 1; count >= 0; count--) {
0853         KoShape *shape = sortedShapes.at(count);
0854 
0855         if (shape->isContentProtected())
0856             continue;
0857         TextShape *textShape = dynamic_cast<TextShape*>(shape);
0858         if (textShape) {
0859             if (textShape != m_textShape) {
0860                 if (static_cast<KoTextShapeData*>(textShape->userData())->document() != m_textShapeData->document()) {
0861                     //we should only change to another document if allowed
0862                     if (noDocumentChange) {
0863                         return;
0864                     }
0865 
0866                     // if we change to another textdocument we need to remove selection in old document
0867                     // or it would continue to be painted etc
0868 
0869                     m_textEditor.data()->setPosition(m_textEditor.data()->position());
0870                 }
0871                 m_textShape = textShape;
0872 
0873                 setShapeData(static_cast<KoTextShapeData*>(m_textShape->userData()));
0874 
0875                 // This is how we inform the rulers of the active range
0876                 // For now we will not consider table cells, but just give the shape dimensions
0877                 QVariant v;
0878                 QRectF rect(QPoint(), m_textShape->size());
0879                 rect = m_textShape->absoluteTransformation(0).mapRect(rect);
0880                 v.setValue(rect);
0881                 canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, v);
0882             }
0883             return;
0884         }
0885     }
0886 }
0887 
0888 void TextTool::mousePressEvent(KoPointerEvent *event)
0889 {
0890     if (m_textEditor.isNull())
0891         return;
0892 
0893     // request the software keyboard, if any
0894     if (event->button() == Qt::LeftButton && qApp->autoSipEnabled()) {
0895         QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(qApp->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
0896         // the two following bools just make it all a lot easier to read in the following if()
0897         // basically, we require a widget for this to work (passing nullptr to QApplication::sendEvent
0898         // crashes) and there are three tests any one of which can be true to trigger the event
0899         const bool hasWidget = canvas()->canvasWidget();
0900         const bool hasItem = canvas()->canvasItem();
0901         if ((behavior == QStyle::RSIP_OnMouseClick && (hasWidget || hasItem)) ||
0902             (hasWidget && canvas()->canvasWidget()->hasFocus()) ||
0903             (hasItem && canvas()->canvasItem()->hasFocus())) {
0904             QEvent event(QEvent::RequestSoftwareInputPanel);
0905             if (hasWidget) {
0906                 QApplication::sendEvent(canvas()->canvasWidget(), &event);
0907             } else {
0908                 QApplication::sendEvent(canvas()->canvasItem(), &event);
0909             }
0910         }
0911     }
0912 
0913     bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
0914 
0915     updateSelectedShape(event->point, shiftPressed);
0916 
0917     KoSelection *selection = canvas()->shapeManager()->selection();
0918     if (m_textShape && !selection->isSelected(m_textShape) && m_textShape->isSelectable()) {
0919         selection->deselectAll();
0920         selection->select(m_textShape);
0921     }
0922 
0923     KoPointedAt pointedAt = hitTest(event->point);
0924     m_tableDraggedOnce = false;
0925     m_clickWithinSelection = false;
0926     if (pointedAt.position != -1) {
0927         m_tablePenMode = false;
0928 
0929         if ((event->button() == Qt::LeftButton) && !shiftPressed && m_textEditor.data()->hasSelection() && m_textEditor.data()->isWithinSelection(pointedAt.position)) {
0930             m_clickWithinSelection = true;
0931             m_draggingOrigin = event->pos(); //we store the pixel pos
0932         } else if (! (event->button() == Qt::RightButton && m_textEditor.data()->hasSelection() && m_textEditor.data()->isWithinSelection(pointedAt.position))) {
0933             m_textEditor.data()->setPosition(pointedAt.position, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
0934             useCursor(Qt::IBeamCursor);
0935         }
0936         m_tableDragInfo.tableHit = KoPointedAt::None;
0937         if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw)
0938             m_caretTimer.stop();
0939             m_caretTimer.setInterval(50);
0940             m_caretTimer.start();
0941             m_caretTimerState = true; // turn caret instantly on on click
0942         }
0943     } else {
0944         if (event->button() == Qt::RightButton) {
0945             m_tablePenMode = false;
0946             KoTextEditingPlugin *plugin = textEditingPluginContainer()->spellcheck();
0947             if (plugin)
0948                 plugin->setCurrentCursorPosition(m_textShapeData->document(), -1);
0949 
0950             event->ignore();
0951         } else if (m_tablePenMode) {
0952             m_textEditor.data()->beginEditBlock(kundo2_i18n("Change Border Formatting"));
0953             if (pointedAt.tableHit == KoPointedAt::ColumnDivider) {
0954                 if (pointedAt.tableColumnDivider < pointedAt.table->columns()) {
0955                     m_textEditor.data()->setTableBorderData(pointedAt.table,
0956                         pointedAt.tableRowDivider, pointedAt.tableColumnDivider,
0957                         KoBorder::LeftBorder, m_tablePenBorderData);
0958                 }
0959                 if (pointedAt.tableColumnDivider > 0) {
0960                     m_textEditor.data()->setTableBorderData(pointedAt.table,
0961                         pointedAt.tableRowDivider, pointedAt.tableColumnDivider - 1,
0962                         KoBorder::RightBorder, m_tablePenBorderData);
0963                 }
0964             } else if (pointedAt.tableHit == KoPointedAt::RowDivider) {
0965                 if (pointedAt.tableRowDivider < pointedAt.table->rows()) {
0966                     m_textEditor.data()->setTableBorderData(pointedAt.table,
0967                         pointedAt.tableRowDivider, pointedAt.tableColumnDivider,
0968                         KoBorder::TopBorder, m_tablePenBorderData);
0969                 }
0970                 if (pointedAt.tableRowDivider > 0) {
0971                     m_textEditor.data()->setTableBorderData(pointedAt.table,
0972                         pointedAt.tableRowDivider-1, pointedAt.tableColumnDivider,
0973                         KoBorder::BottomBorder, m_tablePenBorderData);
0974                 }
0975             }
0976             m_textEditor.data()->endEditBlock();
0977         } else {
0978             m_tableDragInfo = pointedAt;
0979             m_tablePenMode = false;
0980         }
0981         return;
0982     }
0983     if (shiftPressed) // altered selection.
0984         repaintSelection();
0985     else
0986         repaintCaret();
0987 
0988     updateSelectionHandler();
0989     updateStyleManager();
0990 
0991     updateActions();
0992 
0993     //activate context-menu for spelling-suggestions
0994     if (event->button() == Qt::RightButton) {
0995         KoTextEditingPlugin *plugin = textEditingPluginContainer()->spellcheck();
0996         if (plugin)
0997             plugin->setCurrentCursorPosition(m_textShapeData->document(), m_textEditor.data()->position());
0998 
0999         event->ignore();
1000     }
1001 
1002     if (event->button() ==  Qt::MidButton) { // Paste
1003         const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Selection);
1004 
1005         // on windows we do not have data if we try to paste this selection
1006         if (data) {
1007             m_prevCursorPosition = m_textEditor.data()->position();
1008             m_textEditor.data()->paste(canvas(), data, canvas()->resourceManager());
1009             editingPluginEvents();
1010         }
1011     }
1012 }
1013 
1014 void TextTool::setShapeData(KoTextShapeData *data)
1015 {
1016     bool docChanged = !data || !m_textShapeData || m_textShapeData->document() != data->document();
1017     if (m_textShapeData) {
1018         disconnect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
1019     }
1020     m_textShapeData = data;
1021     if (!m_textShapeData)
1022         return;
1023     connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
1024     if (docChanged) {
1025         if (!m_textEditor.isNull()) {
1026             disconnect(m_textEditor.data(), SIGNAL(textFormatChanged()), this, SLOT(updateActions()));
1027         }
1028         m_textEditor = KoTextDocument(m_textShapeData->document()).textEditor();
1029         Q_ASSERT(m_textEditor.data());
1030         if (!m_toolSelection) {
1031             m_toolSelection = new TextToolSelection(m_textEditor.data());
1032         }
1033         else {
1034             m_toolSelection->m_editor = m_textEditor.data();
1035         }
1036 
1037         m_variableMenu->menu()->clear();
1038         KoTextDocument document(m_textShapeData->document());
1039         foreach (QAction *action, document.inlineTextObjectManager()->createInsertVariableActions(canvas())) {
1040             m_variableMenu->addAction(action);
1041             connect(action, SIGNAL(triggered()), this, SLOT(returnFocusToCanvas()));
1042         }
1043 
1044         connect(m_textEditor.data(), SIGNAL(textFormatChanged()), this, SLOT(updateActions()));
1045         updateActions();
1046     }
1047 }
1048 
1049 void TextTool::updateSelectionHandler()
1050 {
1051     if (m_textEditor) {
1052         emit selectionChanged(m_textEditor.data()->hasSelection());
1053         if (m_textEditor.data()->hasSelection()) {
1054             QClipboard *clipboard = QApplication::clipboard();
1055             if (clipboard->supportsSelection())
1056                 clipboard->setText(m_textEditor.data()->selectedText(), QClipboard::Selection);
1057         }
1058     }
1059 
1060     KoCanvasResourceManager *p = canvas()->resourceManager();
1061     m_allowResourceManagerUpdates = false;
1062     if (m_textEditor && m_textShapeData) {
1063         p->setResource(KoText::CurrentTextPosition, m_textEditor.data()->position());
1064         p->setResource(KoText::CurrentTextAnchor, m_textEditor.data()->anchor());
1065         QVariant variant;
1066         variant.setValue<void*>(m_textShapeData->document());
1067         p->setResource(KoText::CurrentTextDocument, variant);
1068     } else {
1069         p->clearResource(KoText::CurrentTextPosition);
1070         p->clearResource(KoText::CurrentTextAnchor);
1071         p->clearResource(KoText::CurrentTextDocument);
1072     }
1073     m_allowResourceManagerUpdates = true;
1074 }
1075 
1076 QMimeData *TextTool::generateMimeData() const
1077 {
1078     if (!m_textShapeData || m_textEditor.isNull() || !m_textEditor.data()->hasSelection())
1079         return 0;
1080     int from = m_textEditor.data()->position();
1081     int to = m_textEditor.data()->anchor();
1082     KoTextOdfSaveHelper saveHelper(m_textShapeData->document(), from, to);
1083     KoTextDrag drag;
1084 
1085 #ifdef SHOULD_BUILD_RDF
1086     KoDocumentResourceManager *rm = 0;
1087     if (canvas()->shapeController()) {
1088         rm = canvas()->shapeController()->resourceManager();
1089     }
1090 
1091     if (rm && rm->hasResource(KoText::DocumentRdf)) {
1092         KoDocumentRdfBase *rdf = qobject_cast<KoDocumentRdfBase*>(rm->resource(KoText::DocumentRdf).value<QObject*>());
1093         if (rdf) {
1094             saveHelper.setRdfModel(rdf->model());
1095         }
1096     }
1097 #endif
1098     drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper);
1099     QTextDocumentFragment fragment = m_textEditor.data()->selection();
1100     drag.setData("text/plain", fragment.toPlainText().toUtf8());
1101 
1102     return drag.takeMimeData();
1103 }
1104 
1105 TextEditingPluginContainer *TextTool::textEditingPluginContainer()
1106 {
1107     m_textEditingPlugins = canvas()->resourceManager()->
1108         resource(TextEditingPluginContainer::ResourceId).value<TextEditingPluginContainer*>();
1109 
1110     if (m_textEditingPlugins == 0) {
1111         m_textEditingPlugins = new TextEditingPluginContainer(canvas()->resourceManager());
1112         QVariant variant;
1113         variant.setValue(m_textEditingPlugins.data());
1114         canvas()->resourceManager()->setResource(TextEditingPluginContainer::ResourceId, variant);
1115 
1116         foreach (KoTextEditingPlugin* plugin, m_textEditingPlugins->values()) {
1117             connect(plugin, SIGNAL(startMacro(QString)),
1118                     this, SLOT(startMacro(QString)));
1119             connect(plugin, SIGNAL(stopMacro()), this, SLOT(stopMacro()));
1120             const QHash<QString, QAction*> actions = plugin->actions();
1121             QHash<QString, QAction*>::ConstIterator i = actions.begin();
1122             while (i != actions.end()) {
1123                 addAction(i.key(), i.value());
1124                 ++i;
1125             }
1126         }
1127 
1128     }
1129     return m_textEditingPlugins;
1130 }
1131 
1132 void TextTool::copy() const
1133 {
1134     QMimeData *mimeData = generateMimeData();
1135     if (mimeData) {
1136         QApplication::clipboard()->setMimeData(mimeData);
1137     }
1138 }
1139 
1140 void TextTool::deleteSelection()
1141 {
1142     m_textEditor.data()->deleteChar();
1143     editingPluginEvents();
1144 }
1145 
1146 bool TextTool::paste()
1147 {
1148     const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
1149 
1150     // on windows we do not have data if we try to paste the selection
1151     if (!data) return false;
1152 
1153     // since this is not paste-as-text we will not paste in urls, but instead let KoToolProxy solve it
1154     if (data->hasUrls()) return false;
1155 
1156     if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))
1157         ||  data->hasText()) {
1158         m_prevCursorPosition = m_textEditor.data()->position();
1159         m_textEditor.data()->paste(canvas(), data);
1160         editingPluginEvents();
1161         return true;
1162     }
1163 
1164     return false;
1165 }
1166 
1167 void TextTool::cut()
1168 {
1169     if (m_textEditor.data()->hasSelection()) {
1170         copy();
1171         KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Cut"));
1172         m_textEditor.data()->deleteChar(false, topCmd);
1173         m_textEditor.data()->endEditBlock();
1174     }
1175 }
1176 
1177 QStringList TextTool::supportedPasteMimeTypes() const
1178 {
1179     QStringList list;
1180     list << "text/plain" << "application/vnd.oasis.opendocument.text";
1181     return list;
1182 }
1183 
1184 void TextTool::dragMoveEvent(QDragMoveEvent *event, const QPointF &point)
1185 {
1186     if (event->mimeData()->hasFormat(KoOdf::mimeType(KoOdf::Text))
1187                     || event->mimeData()->hasFormat(KoOdf::mimeType(KoOdf::OpenOfficeClipboard))
1188                     || event->mimeData()->hasText()) {
1189         if (m_drag) {
1190             event->setDropAction(Qt::MoveAction);
1191             event->accept();
1192         } else if (event->proposedAction() == Qt::CopyAction) {
1193             event->acceptProposedAction();
1194         } else {
1195             event->ignore();
1196             return;
1197         }
1198         KoPointedAt pointedAt = hitTest(point);
1199 
1200         if (pointedAt.position == -1) {
1201             event->ignore();
1202         }
1203         if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw)
1204             m_caretTimer.stop();
1205             m_caretTimer.setInterval(50);
1206             m_caretTimer.start();
1207             m_caretTimerState = true; // turn caret instantly on on click
1208         }
1209 
1210         if (m_preDragSelection.cursor.isNull()) {
1211             repaintSelection();
1212 
1213             m_preDragSelection.cursor = QTextCursor(*m_textEditor.data()->cursor());
1214 
1215             if (m_drag) {
1216                 // Make a selection that looks like the current cursor selection
1217                 // so we can move the real caret around freely
1218                 QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections();
1219 
1220                 m_preDragSelection.format = QTextCharFormat();
1221                 m_preDragSelection.format.setBackground(qApp->palette().brush(QPalette::Highlight));
1222                 m_preDragSelection.format.setForeground(qApp->palette().brush(QPalette::HighlightedText));
1223                 sels.append(m_preDragSelection);
1224                 KoTextDocument(m_textShapeData->document()).setSelections(sels);
1225             } // else we want the selection to disappear
1226         }
1227         repaintCaret(); // will erase caret
1228         m_textEditor.data()->setPosition(pointedAt.position);
1229         repaintCaret(); // will paint caret in new spot
1230 
1231         // Selection has visually not appeared at a new spot so no need to repaint it
1232     }
1233 }
1234 
1235 void TextTool::dragLeaveEvent(QDragLeaveEvent *event)
1236 {
1237     if (m_drag) {
1238         // restore the old selections
1239         QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections();
1240         sels.pop_back();
1241         KoTextDocument(m_textShapeData->document()).setSelections(sels);
1242     }
1243 
1244     repaintCaret(); // will erase caret in old spot
1245     m_textEditor.data()->setPosition(m_preDragSelection.cursor.anchor());
1246     m_textEditor.data()->setPosition(m_preDragSelection.cursor.position(), QTextCursor::KeepAnchor);
1247     repaintCaret(); // will paint caret in new spot
1248 
1249     if (!m_drag) {
1250         repaintSelection(); // will paint selection again
1251     }
1252 
1253     // mark that we now are back to normal selection
1254     m_preDragSelection.cursor = QTextCursor();
1255     event->accept();
1256 }
1257 
1258 void TextTool::dropEvent(QDropEvent *event, const QPointF &)
1259 {
1260     if (m_drag) {
1261         // restore the old selections
1262         QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections();
1263         sels.pop_back();
1264         KoTextDocument(m_textShapeData->document()).setSelections(sels);
1265     }
1266 
1267     QTextCursor insertCursor(*m_textEditor.data()->cursor());
1268 
1269     m_textEditor.data()->setPosition(m_preDragSelection.cursor.anchor());
1270     m_textEditor.data()->setPosition(m_preDragSelection.cursor.position(), QTextCursor::KeepAnchor);
1271     repaintSelection(); // will erase the selection in new spot
1272     if (m_drag) {
1273         m_textEditor.data()->deleteChar();
1274     }
1275     m_prevCursorPosition = insertCursor.position();
1276     m_textEditor.data()->setPosition(m_prevCursorPosition);
1277     m_textEditor.data()->paste(canvas(), event->mimeData());
1278     m_textEditor.data()->setPosition(m_prevCursorPosition);
1279     //since the paste made insertCursor we can now use that for the end position
1280     m_textEditor.data()->setPosition(insertCursor.position(), QTextCursor::KeepAnchor);
1281 
1282     // mark that we no are back to normal selection
1283     m_preDragSelection.cursor = QTextCursor();
1284     event->accept();
1285 }
1286 
1287 KoPointedAt TextTool::hitTest(const QPointF & point) const
1288 {
1289     if (!m_textShape || !m_textShapeData) {
1290         return KoPointedAt();
1291     }
1292     QPointF p = m_textShape->convertScreenPos(point);
1293     KoTextLayoutRootArea *rootArea = m_textShapeData->rootArea();
1294     return rootArea ? rootArea->hitTest(p, Qt::FuzzyHit) : KoPointedAt();
1295 }
1296 
1297 void TextTool::mouseDoubleClickEvent(KoPointerEvent *event)
1298 {
1299     if (canvas()->shapeManager()->shapeAt(event->point) != m_textShape) {
1300         event->ignore(); // allow the event to be used by another
1301         return;
1302     }
1303 
1304     if (event->modifiers() & Qt::ShiftModifier) {
1305         // When whift is pressed we behave as a single press
1306         return mousePressEvent(event);
1307     }
1308 
1309     m_textEditor.data()->select(QTextCursor::WordUnderCursor);
1310 
1311     m_clickWithinSelection = false;
1312 
1313     repaintSelection();
1314     updateSelectionHandler();
1315 }
1316 
1317 void TextTool::mouseTripleClickEvent(KoPointerEvent *event)
1318 {
1319     if (canvas()->shapeManager()->shapeAt(event->point) != m_textShape) {
1320         event->ignore(); // allow the event to be used by another
1321         return;
1322     }
1323 
1324     if (event->modifiers() & Qt::ShiftModifier) {
1325         // When whift is pressed we behave as a single press
1326         return mousePressEvent(event);
1327     }
1328 
1329     m_textEditor.data()->clearSelection();
1330     m_textEditor.data()->movePosition(QTextCursor::StartOfBlock);
1331     m_textEditor.data()->movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
1332 
1333     m_clickWithinSelection = false;
1334 
1335     repaintSelection();
1336     updateSelectionHandler();
1337 }
1338 
1339 void TextTool::mouseMoveEvent(KoPointerEvent *event)
1340 {
1341     m_editTipPos = event->globalPos();
1342 
1343     if (event->buttons()) {
1344         updateSelectedShape(event->point, true);
1345     }
1346 
1347     m_editTipTimer.stop();
1348 
1349     if (QToolTip::isVisible())
1350         QToolTip::hideText();
1351 
1352     KoPointedAt pointedAt = hitTest(event->point);
1353 
1354     if (event->buttons() == Qt::NoButton) {
1355         if (m_tablePenMode) {
1356             if (pointedAt.tableHit == KoPointedAt::ColumnDivider || pointedAt.tableHit == KoPointedAt::RowDivider) {
1357                 useTableBorderCursor();
1358             } else {
1359                 useCursor(Qt::IBeamCursor);
1360             }
1361             // do nothing else
1362             return;
1363         }
1364 
1365         if (!m_textShapeData || pointedAt.position < 0) {
1366             if (pointedAt.tableHit == KoPointedAt::ColumnDivider) {
1367                 useCursor(Qt::SplitHCursor);
1368                 m_draggingOrigin = event->point;
1369             } else if (pointedAt.tableHit == KoPointedAt::RowDivider) {
1370                 if (pointedAt.tableRowDivider > 0) {
1371                     useCursor(Qt::SplitVCursor);
1372                     m_draggingOrigin = event->point;
1373                 } else
1374                     useCursor(Qt::IBeamCursor);
1375             } else {
1376                 useCursor(Qt::IBeamCursor);
1377             }
1378             return;
1379         }
1380 
1381         QTextCursor mouseOver(m_textShapeData->document());
1382         mouseOver.setPosition(pointedAt.position);
1383 
1384         if (m_changeTracker && m_changeTracker->containsInlineChanges(mouseOver.charFormat())) {
1385             m_editTipPointedAt = pointedAt;
1386             if (QToolTip::isVisible()) {
1387                 QTimer::singleShot(0, this, SLOT(showEditTip()));
1388             }else {
1389                 m_editTipTimer.start();
1390             }
1391         }
1392 
1393         if ((pointedAt.bookmark || !pointedAt.externalHRef.isEmpty()) || pointedAt.note || (pointedAt.noteReference>0)) {
1394             if (event->modifiers() & Qt::ControlModifier) {
1395                 useCursor(Qt::PointingHandCursor);
1396             }
1397             m_editTipPointedAt = pointedAt;
1398             if (QToolTip::isVisible()) {
1399                 QTimer::singleShot(0, this, SLOT(showEditTip()));
1400             }else {
1401                 m_editTipTimer.start();
1402             }
1403             return;
1404         }
1405 
1406         // check if mouse pointer is over shape with hyperlink
1407         KoShape *selectedShape = canvas()->shapeManager()->shapeAt(event->point);
1408         if (selectedShape != 0 && selectedShape != m_textShape && selectedShape->hyperLink().size() != 0) {
1409             useCursor(Qt::PointingHandCursor);
1410             return;
1411         }
1412 
1413         useCursor(Qt::IBeamCursor);
1414 
1415         // Set Arrow Cursor when mouse is on top of annotation shape.
1416         if (selectedShape) {
1417             if (selectedShape->shapeId() == "AnnotationTextShapeID") {
1418                 QPointF point(event->point);
1419                 if (point.y() <= (selectedShape->position().y() + 25))
1420                     useCursor(Qt::ArrowCursor);
1421             }
1422         }
1423 
1424         return;
1425     } else {
1426         if (m_tableDragInfo.tableHit == KoPointedAt::ColumnDivider) {
1427             m_tableDragWithShift = event->modifiers() & Qt::ShiftModifier;
1428             if(m_tableDraggedOnce) {
1429                 canvas()->shapeController()->resourceManager()->undoStack()->undo();
1430             }
1431             KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Adjust Column Width"));
1432             m_dx = m_draggingOrigin.x() - event->point.x();
1433             if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()
1434                     && m_tableDragInfo.tableTrailSize + m_dx < 0) {
1435                 m_dx = -m_tableDragInfo.tableTrailSize;
1436             }
1437             if (m_tableDragInfo.tableColumnDivider > 0) {
1438                 if (m_tableDragInfo.tableLeadSize - m_dx < 0) {
1439                     m_dx = m_tableDragInfo.tableLeadSize;
1440                 }
1441                 m_textEditor.data()->adjustTableColumnWidth(m_tableDragInfo.table,
1442                     m_tableDragInfo.tableColumnDivider - 1,
1443                     m_tableDragInfo.tableLeadSize - m_dx, topCmd);
1444             } else {
1445                 m_textEditor.data()->adjustTableWidth(m_tableDragInfo.table, -m_dx, 0.0);
1446             }
1447             if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()) {
1448                 if (!m_tableDragWithShift) {
1449                     m_textEditor.data()->adjustTableColumnWidth(m_tableDragInfo.table,
1450                         m_tableDragInfo.tableColumnDivider,
1451                         m_tableDragInfo.tableTrailSize + m_dx, topCmd);
1452                 }
1453             } else {
1454                 m_tableDragWithShift = true; // act like shift pressed
1455             }
1456             if (m_tableDragWithShift) {
1457                 m_textEditor.data()->adjustTableWidth(m_tableDragInfo.table, 0.0, m_dx);
1458             }
1459             m_textEditor.data()->endEditBlock();
1460             m_tableDragInfo.tableDividerPos.setY(m_textShape->convertScreenPos(event->point).y());
1461             if (m_tableDraggedOnce) {
1462                 //we need to redraw like this so we update outside the textshape too
1463                 if (canvas()->canvasWidget())
1464                     canvas()->canvasWidget()->update();
1465                 if (canvas()->canvasItem())
1466                     canvas()->canvasItem()->update();
1467             }
1468             m_tableDraggedOnce = true;
1469         } else if (m_tableDragInfo.tableHit == KoPointedAt::RowDivider) {
1470             if(m_tableDraggedOnce) {
1471                 canvas()->shapeController()->resourceManager()->undoStack()->undo();
1472             }
1473             if (m_tableDragInfo.tableRowDivider > 0) {
1474                 KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Adjust Row Height"));
1475                 m_dy = m_draggingOrigin.y() - event->point.y();
1476 
1477                 if (m_tableDragInfo.tableLeadSize - m_dy < 0) {
1478                     m_dy = m_tableDragInfo.tableLeadSize;
1479                 }
1480 
1481                 m_textEditor.data()->adjustTableRowHeight(m_tableDragInfo.table,
1482                     m_tableDragInfo.tableRowDivider - 1,
1483                     m_tableDragInfo.tableLeadSize - m_dy, topCmd);
1484 
1485                 m_textEditor.data()->endEditBlock();
1486 
1487                 m_tableDragInfo.tableDividerPos.setX(m_textShape->convertScreenPos(event->point).x());
1488                 if (m_tableDraggedOnce) {
1489                     //we need to redraw like this so we update outside the textshape too
1490                     if (canvas()->canvasWidget())
1491                         canvas()->canvasWidget()->update();
1492                     if (canvas()->canvasItem())
1493                         canvas()->canvasItem()->update();
1494                 }
1495                 m_tableDraggedOnce = true;
1496             }
1497 
1498         } else if (m_tablePenMode) {
1499             // do nothing
1500         } else if (m_clickWithinSelection) {
1501             if (!m_drag && (event->pos() - m_draggingOrigin).manhattanLength()
1502           >= QApplication::startDragDistance()) {
1503                 QMimeData *mimeData = generateMimeData();
1504                 if (mimeData) {
1505                     m_drag = new QDrag(canvas()->canvasWidget());
1506                     m_drag->setMimeData(mimeData);
1507 
1508                     m_drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction);
1509 
1510                     m_drag = 0;
1511                 }
1512             }
1513         } else {
1514             useCursor(Qt::IBeamCursor);
1515             if (pointedAt.position == m_textEditor.data()->position()) return;
1516             if (pointedAt.position >= 0) {
1517                 if (m_textEditor.data()->hasSelection())
1518                     repaintSelection(); // will erase selection
1519                 else
1520                     repaintCaret();
1521 
1522                 m_textEditor.data()->setPosition(pointedAt.position, QTextCursor::KeepAnchor);
1523 
1524                 if (m_textEditor.data()->hasSelection())
1525                     repaintSelection();
1526                 else
1527                     repaintCaret();
1528             }
1529         }
1530 
1531         updateSelectionHandler();
1532     }
1533 }
1534 
1535 void TextTool::mouseReleaseEvent(KoPointerEvent *event)
1536 {
1537     event->ignore();
1538     editingPluginEvents();
1539 
1540     m_tableDragInfo.tableHit = KoPointedAt::None;
1541     if (m_tableDraggedOnce) {
1542         m_tableDraggedOnce = false;
1543         //we need to redraw like this so we update outside the textshape too
1544         if (canvas()->canvasWidget())
1545             canvas()->canvasWidget()->update();
1546         if (canvas()->canvasItem())
1547             canvas()->canvasItem()->update();
1548     }
1549 
1550     if (!m_textShapeData)
1551         return;
1552 
1553     // check if mouse pointer is not over some shape with hyperlink
1554     KoShape *selectedShape = canvas()->shapeManager()->shapeAt(event->point);
1555     if (selectedShape != 0 && selectedShape != m_textShape && selectedShape->hyperLink().size() != 0) {
1556         QString url = selectedShape->hyperLink();
1557         runUrl(event, url);
1558         return;
1559     }
1560 
1561     KoPointedAt pointedAt = hitTest(event->point);
1562 
1563     if (m_clickWithinSelection && !m_drag) {
1564         if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw)
1565             m_caretTimer.stop();
1566             m_caretTimer.setInterval(50);
1567             m_caretTimer.start();
1568             m_caretTimerState = true; // turn caret instantly on on click
1569         }
1570         repaintCaret(); // will erase caret
1571         repaintSelection(); // will erase selection
1572         m_textEditor.data()->setPosition(pointedAt.position);
1573         repaintCaret(); // will paint caret in new spot
1574     }
1575 
1576     // Is there an anchor here ?
1577     if ((event->modifiers() & Qt::ControlModifier) && !m_textEditor.data()->hasSelection()) {
1578         if (pointedAt.bookmark) {
1579             m_textEditor.data()->setPosition(pointedAt.bookmark->rangeStart());
1580             ensureCursorVisible();
1581             event->accept();
1582             return;
1583         }
1584         if (pointedAt.note) {
1585             m_textEditor.data()->setPosition(pointedAt.note->textFrame()->firstPosition());
1586             ensureCursorVisible();
1587             event->accept();
1588             return;
1589         }
1590         if (pointedAt.noteReference>0) {
1591             m_textEditor.data()->setPosition(pointedAt.noteReference);
1592             ensureCursorVisible();
1593             event->accept();
1594             return;
1595         }
1596         if (!pointedAt.externalHRef.isEmpty()) {
1597             runUrl(event, pointedAt.externalHRef);
1598         }
1599     }
1600 }
1601 
1602 void TextTool::shortcutOverrideEvent(QKeyEvent *event)
1603 {
1604     QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers()));
1605     if (hit(item, KStandardShortcut::Begin) ||
1606         hit(item, KStandardShortcut::End)) {
1607         event->accept();
1608     }
1609 }
1610 
1611 void TextTool::keyPressEvent(QKeyEvent *event)
1612 {
1613     int destinationPosition = -1; // for those cases where the moveOperation is not relevant;
1614     QTextCursor::MoveOperation moveOperation = QTextCursor::NoMove;
1615     KoTextEditor *textEditor = m_textEditor.data();
1616     m_tablePenMode = false; // keypress always stops the table (border) pen mode
1617     Q_ASSERT(textEditor);
1618     if (event->key() == Qt::Key_Backspace) {
1619         if (!textEditor->hasSelection() && textEditor->block().textList()
1620             && (textEditor->position() == textEditor->block().position())
1621             && !(m_changeTracker && m_changeTracker->recordChanges())) {
1622             if (!textEditor->blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) {
1623                 // backspace at beginning of numbered list item, makes it unnumbered
1624                 textEditor->toggleListNumbering(false);
1625             } else {
1626                 KoListLevelProperties llp;
1627                 llp.setLabelType(KoListStyle::None);
1628                 llp.setLevel(0);
1629                 // backspace on numbered, empty parag, removes numbering.
1630                 textEditor->setListProperties(llp);
1631             }
1632         } else if (textEditor->position() > 0 || textEditor->hasSelection()) {
1633             if (!textEditor->hasSelection() && event->modifiers() & Qt::ControlModifier) { // delete prev word.
1634                 textEditor->movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
1635             }
1636             textEditor->deletePreviousChar();
1637 
1638             editingPluginEvents();
1639         }
1640     } else if ((event->key() == Qt::Key_Tab)
1641         && ((!textEditor->hasSelection() && (textEditor->position() == textEditor->block().position())) || (textEditor->block().document()->findBlock(textEditor->anchor()) != textEditor->block().document()->findBlock(textEditor->position()))) && textEditor->block().textList()) {
1642         ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::IncreaseLevel;
1643         ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, 1);
1644         textEditor->addCommand(cll);
1645         editingPluginEvents();
1646     } else if ((event->key() == Qt::Key_Backtab)
1647         && ((!textEditor->hasSelection() && (textEditor->position() == textEditor->block().position())) || (textEditor->block().document()->findBlock(textEditor->anchor()) != textEditor->block().document()->findBlock(textEditor->position()))) && textEditor->block().textList() && !(m_changeTracker && m_changeTracker->recordChanges())) {
1648         ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::DecreaseLevel;
1649         ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, 1);
1650         textEditor->addCommand(cll);
1651         editingPluginEvents();
1652     } else if (event->key() == Qt::Key_Delete) {
1653         if (!textEditor->hasSelection() && event->modifiers() & Qt::ControlModifier) {// delete next word.
1654             textEditor->movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
1655         }
1656         // the event only gets through when the Del is not used in the app
1657         // if the app forwards Del then deleteSelection is used
1658         textEditor->deleteChar();
1659         editingPluginEvents();
1660     } else if ((event->key() == Qt::Key_Left) && (event->modifiers() & Qt::ControlModifier) == 0) {
1661         moveOperation = QTextCursor::Left;
1662     } else if ((event->key() == Qt::Key_Right) && (event->modifiers() & Qt::ControlModifier) == 0) {
1663         moveOperation = QTextCursor::Right;
1664     } else if ((event->key() == Qt::Key_Up) && (event->modifiers() & Qt::ControlModifier) == 0) {
1665         moveOperation = QTextCursor::Up;
1666     } else if ((event->key() == Qt::Key_Down) && (event->modifiers() & Qt::ControlModifier) == 0) {
1667         moveOperation = QTextCursor::Down;
1668     } else {
1669         // check for shortcuts.
1670         QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers()));
1671         if (hit(item, KStandardShortcut::Begin)) {
1672             // Goto beginning of the document. Default: Ctrl-Home
1673             destinationPosition = 0;
1674         } else if (hit(item, KStandardShortcut::End)) {
1675             // Goto end of the document. Default: Ctrl-End
1676             if (m_textShapeData) {
1677                 QTextBlock last = m_textShapeData->document()->lastBlock();
1678                 destinationPosition = last.position() + last.length() - 1;
1679             }
1680         } else if (hit(item, KStandardShortcut::Prior)) { // page up
1681             // Scroll up one page. Default: Prior
1682             event->ignore(); // let app level actions handle it
1683             return;
1684         }
1685         else if (hit(item, KStandardShortcut::Next)) {
1686             // Scroll down one page. Default: Next
1687             event->ignore(); // let app level actions handle it
1688             return;
1689         }
1690         else if (hit(item, KStandardShortcut::BeginningOfLine))
1691             // Goto beginning of current line. Default: Home
1692             moveOperation = QTextCursor::StartOfLine;
1693         else if (hit(item, KStandardShortcut::EndOfLine))
1694             // Goto end of current line. Default: End
1695             moveOperation = QTextCursor::EndOfLine;
1696         else if (hit(item, KStandardShortcut::BackwardWord))
1697             moveOperation = QTextCursor::WordLeft;
1698         else if (hit(item, KStandardShortcut::ForwardWord))
1699             moveOperation = QTextCursor::WordRight;
1700 #ifdef Q_WS_MAC
1701         // Don't reject "alt" key, it may be used for typing text on Mac OS
1702         else if ((event->modifiers() & Qt::ControlModifier)
1703 #else
1704         else if ((event->modifiers() & (Qt::ControlModifier | Qt::AltModifier))
1705 #endif
1706             || event->text().length() == 0 || event->key() == Qt::Key_Escape) {
1707             event->ignore();
1708             return;
1709         } else if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) {
1710             m_prevCursorPosition = textEditor->position();
1711             textEditor->newLine();
1712             updateActions();
1713             editingPluginEvents();
1714         } else if ((event->key() == Qt::Key_Tab || !(event->text().length() == 1 && !event->text().at(0).isPrint()))) { // insert the text
1715             m_prevCursorPosition = textEditor->position();
1716             startingSimpleEdit(); //signal editing plugins that this is a simple edit
1717             textEditor->insertText(event->text());
1718             editingPluginEvents();
1719         }
1720     }
1721     if (moveOperation != QTextCursor::NoMove || destinationPosition != -1) {
1722         useCursor(Qt::BlankCursor);
1723         bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
1724         if (textEditor->hasSelection())
1725             repaintSelection(); // will erase selection
1726         else
1727             repaintCaret();
1728         QTextBlockFormat format = textEditor->blockFormat();
1729 
1730         KoText::Direction dir = static_cast<KoText::Direction>(format.intProperty(KoParagraphStyle::TextProgressionDirection));
1731         bool isRtl;
1732         if (dir == KoText::AutoDirection)
1733             isRtl = textEditor->block().text().isRightToLeft();
1734         else
1735             isRtl =  dir == KoText::RightLeftTopBottom;
1736 
1737         if (isRtl) { // if RTL toggle direction of cursor movement.
1738             switch (moveOperation) {
1739             case QTextCursor::Left: moveOperation = QTextCursor::Right; break;
1740             case QTextCursor::Right: moveOperation = QTextCursor::Left; break;
1741             case QTextCursor::WordRight: moveOperation = QTextCursor::WordLeft; break;
1742             case QTextCursor::WordLeft: moveOperation = QTextCursor::WordRight; break;
1743             default: break;
1744             }
1745         }
1746         int prevPosition = textEditor->position();
1747         if (moveOperation != QTextCursor::NoMove)
1748             textEditor->movePosition(moveOperation,
1749                 shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
1750         else
1751             textEditor->setPosition(destinationPosition,
1752                 shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
1753         if (moveOperation == QTextCursor::Down && prevPosition == textEditor->position()) {
1754             // change behavior a little big from Qt; at the bottom of the doc we go to the end of the doc
1755             textEditor->movePosition(QTextCursor::End,
1756                 shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
1757         }
1758         if (shiftPressed) // altered selection.
1759             repaintSelection();
1760         else
1761             repaintCaret();
1762         updateActions();
1763         editingPluginEvents();
1764     }
1765     if (m_caretTimer.isActive()) { // make the caret not blink but decide on the action if its visible or not.
1766         m_caretTimer.stop();
1767         m_caretTimer.setInterval(50);
1768         m_caretTimer.start();
1769         m_caretTimerState = true; // turn caret on while typing
1770     }
1771     if (moveOperation != QTextCursor::NoMove)
1772         // this difference in handling is need to prevent leaving a trail of old cursors onscreen
1773         ensureCursorVisible();
1774     else
1775         m_delayedEnsureVisible = true;
1776     updateActions();
1777     updateSelectionHandler();
1778 }
1779 
1780 QVariant TextTool::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const
1781 {
1782     KoTextEditor *textEditor = m_textEditor.data();
1783     if (!textEditor || !m_textShapeData)
1784         return QVariant();
1785 
1786     switch (query) {
1787     case Qt::ImMicroFocus: {
1788         // The rectangle covering the area of the input cursor in widget coordinates.
1789         QRectF rect = caretRect(textEditor->cursor());
1790         rect.moveTop(rect.top() - m_textShapeData->documentOffset());
1791         QTransform shapeMatrix = m_textShape->absoluteTransformation(&converter);
1792         qreal zoomX, zoomY;
1793         converter.zoom(&zoomX, &zoomY);
1794         shapeMatrix.scale(zoomX, zoomY);
1795         rect = shapeMatrix.mapRect(rect);
1796         return rect.toRect();
1797     }
1798     case Qt::ImFont:
1799         // The currently used font for text input.
1800         return textEditor->charFormat().font();
1801     case Qt::ImCursorPosition:
1802         // The logical position of the cursor within the text surrounding the input area (see ImSurroundingText).
1803         return textEditor->position() - textEditor->block().position();
1804     case Qt::ImSurroundingText:
1805         // The plain text around the input area, for example the current paragraph.
1806         return textEditor->block().text();
1807     case Qt::ImCurrentSelection:
1808         // The currently selected text.
1809         return textEditor->selectedText();
1810     default:
1811         ; // Qt 4.6 adds ImMaximumTextLength and ImAnchorPosition
1812     }
1813     return QVariant();
1814 }
1815 
1816 void TextTool::inputMethodEvent(QInputMethodEvent *event)
1817 {
1818     KoTextEditor *textEditor = m_textEditor.data();
1819     if (textEditor == 0)
1820         return;
1821     if (event->replacementLength() > 0) {
1822         textEditor->setPosition(textEditor->position() + event->replacementStart());
1823         for (int i = event->replacementLength(); i > 0; --i) {
1824             textEditor->deleteChar();
1825         }
1826     }
1827     if (!event->commitString().isEmpty()) {
1828         QKeyEvent ke(QEvent::KeyPress, -1, 0, event->commitString());
1829         keyPressEvent(&ke);
1830         // The cursor may reside in a different block before vs. after keyPressEvent.
1831         QTextBlock block = textEditor->block();
1832         QTextLayout *layout = block.layout();
1833         Q_ASSERT(layout);
1834         layout->setPreeditArea(-1, QString());
1835     } else {
1836         QTextBlock block = textEditor->block();
1837         QTextLayout *layout = block.layout();
1838         Q_ASSERT(layout);
1839         layout->setPreeditArea(textEditor->position() - block.position(), event->preeditString());
1840         const_cast<QTextDocument*>(textEditor->document())->markContentsDirty(textEditor->position(), event->preeditString().length());
1841     }
1842     event->accept();
1843 }
1844 
1845 void TextTool::ensureCursorVisible(bool moveView)
1846 {
1847     KoTextEditor *textEditor = m_textEditor.data();
1848     if (!textEditor || !m_textShapeData)
1849         return;
1850 
1851     bool upToDate;
1852     QRectF cRect = caretRect(textEditor->cursor(), &upToDate);
1853 
1854     KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout*>(m_textShapeData->document()->documentLayout());
1855     Q_ASSERT(lay);
1856     KoTextLayoutRootArea *rootArea = lay->rootAreaForPoint(cRect.center());
1857     if (rootArea && rootArea->associatedShape() && m_textShapeData->rootArea() != rootArea) {
1858         // If we have changed root area we need to update m_textShape and m_textShapeData
1859         m_textShape = static_cast<TextShape*>(rootArea->associatedShape());
1860         Q_ASSERT(m_textShape);
1861         disconnect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
1862         m_textShapeData = static_cast<KoTextShapeData*>(m_textShape->userData());
1863         Q_ASSERT(m_textShapeData);
1864         connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
1865     }
1866 
1867     if (!moveView) {
1868         return;
1869     }
1870 
1871     if (! upToDate) { // paragraph is not yet layouted.
1872         // The number one usecase for this is when the user pressed enter.
1873         // try to do it on next caret blink
1874         m_delayedEnsureVisible = true;
1875         return; // we shouldn't move to an obsolete position
1876     }
1877     cRect.moveTop(cRect.top() - m_textShapeData->documentOffset());
1878     canvas()->ensureVisible(m_textShape->absoluteTransformation(0).mapRect(cRect));
1879 }
1880 
1881 void TextTool::keyReleaseEvent(QKeyEvent *event)
1882 {
1883     event->accept();
1884 }
1885 
1886 void TextTool::updateActions()
1887 {
1888     bool notInAnnotation = !dynamic_cast<AnnotationTextShape *>(m_textShape);
1889     KoTextEditor *textEditor = m_textEditor.data();
1890     if (textEditor == 0) {
1891         return;
1892     }
1893     m_allowActions = false;
1894 
1895     //Update the characterStyle related GUI elements
1896     QTextCharFormat cf = textEditor->charFormat();
1897     m_actionFormatBold->setChecked(cf.fontWeight() > QFont::Normal);
1898     m_actionFormatItalic->setChecked(cf.fontItalic());
1899     m_actionFormatUnderline->setChecked(cf.intProperty(KoCharacterStyle::UnderlineType) != KoCharacterStyle::NoLineType);
1900     m_actionFormatStrikeOut->setChecked(cf.intProperty(KoCharacterStyle::StrikeOutType) != KoCharacterStyle::NoLineType);
1901     bool super = false, sub = false;
1902     switch (cf.verticalAlignment()) {
1903     case QTextCharFormat::AlignSuperScript:
1904         super = true;
1905         break;
1906     case QTextCharFormat::AlignSubScript:
1907         sub = true;
1908         break;
1909     default:;
1910     }
1911     m_actionFormatSuper->setChecked(super);
1912     m_actionFormatSub->setChecked(sub);
1913     m_actionFormatFontSize->setFontSize(cf.font().pointSizeF());
1914     m_actionFormatFontFamily->setFont(cf.font().family());
1915 
1916     KoTextShapeData::ResizeMethod resizemethod = KoTextShapeData::AutoResize;
1917     if(m_textShapeData) {
1918         resizemethod = m_textShapeData->resizeMethod();
1919     }
1920     m_shrinkToFitAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation);
1921     m_shrinkToFitAction->setChecked(resizemethod == KoTextShapeData::ShrinkToFitResize);
1922 
1923     m_growWidthAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation);
1924     m_growWidthAction->setChecked(resizemethod == KoTextShapeData::AutoGrowWidth || resizemethod == KoTextShapeData::AutoGrowWidthAndHeight);
1925 
1926     m_growHeightAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation);
1927     m_growHeightAction->setChecked(resizemethod == KoTextShapeData::AutoGrowHeight || resizemethod == KoTextShapeData::AutoGrowWidthAndHeight);
1928 
1929     //update paragraphStyle GUI element
1930     QTextBlockFormat bf = textEditor->blockFormat();
1931 
1932     if (bf.hasProperty(KoParagraphStyle::TextProgressionDirection)) {
1933         switch(bf.intProperty(KoParagraphStyle::TextProgressionDirection))
1934         {
1935         case KoText::RightLeftTopBottom:
1936             m_actionChangeDirection->setChecked(true);
1937             break;
1938         case KoText::LeftRightTopBottom:
1939         default:
1940             m_actionChangeDirection->setChecked(false);
1941             break;
1942         }
1943     } else {
1944         m_actionChangeDirection->setChecked(textEditor->block().text().isRightToLeft());
1945     }
1946     if (bf.alignment() == Qt::AlignLeading || bf.alignment() == Qt::AlignTrailing) {
1947         bool revert = (textEditor->block().layout()->textOption().textDirection() == Qt::RightToLeft);
1948         if ((bf.alignment() == Qt::AlignLeading) ^ revert)
1949             m_actionAlignLeft->setChecked(true);
1950         else
1951             m_actionAlignRight->setChecked(true);
1952     } else if (bf.alignment() == Qt::AlignHCenter)
1953         m_actionAlignCenter->setChecked(true);
1954     if (bf.alignment() == Qt::AlignJustify)
1955         m_actionAlignBlock->setChecked(true);
1956     else if (bf.alignment() == (Qt::AlignLeft | Qt::AlignAbsolute))
1957         m_actionAlignLeft->setChecked(true);
1958     else if (bf.alignment() == (Qt::AlignRight | Qt::AlignAbsolute))
1959         m_actionAlignRight->setChecked(true);
1960 
1961     if (textEditor->block().textList()) {
1962         QTextListFormat listFormat = textEditor->block().textList()->format();
1963         if(listFormat.intProperty(KoListStyle::Level) > 1) {
1964             m_actionFormatDecreaseIndent->setEnabled(true);
1965         } else {
1966             m_actionFormatDecreaseIndent->setEnabled(false);
1967         }
1968 
1969         if (listFormat.intProperty(KoListStyle::Level) < 10) {
1970             m_actionFormatIncreaseIndent->setEnabled(true);
1971         } else {
1972             m_actionFormatIncreaseIndent->setEnabled(false);
1973         }
1974     } else {
1975         m_actionFormatDecreaseIndent->setEnabled(textEditor->blockFormat().leftMargin() > 0.);
1976     }
1977 
1978     m_allowActions = true;
1979 
1980     bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality)
1981                             & KoCanvasResourceManager::NoAdvancedText);
1982     if (useAdvancedText) {
1983         action("insert_table")->setEnabled(notInAnnotation);
1984 
1985         bool hasTable = textEditor->currentTable();
1986         action("insert_tablerow_above")->setEnabled(hasTable && notInAnnotation);
1987         action("insert_tablerow_below")->setEnabled(hasTable && notInAnnotation);
1988         action("insert_tablecolumn_left")->setEnabled(hasTable && notInAnnotation);
1989         action("insert_tablecolumn_right")->setEnabled(hasTable && notInAnnotation);
1990         action("delete_tablerow")->setEnabled(hasTable && notInAnnotation);
1991         action("delete_tablecolumn")->setEnabled(hasTable && notInAnnotation);
1992         action("merge_tablecells")->setEnabled(hasTable && notInAnnotation);
1993         action("split_tablecells")->setEnabled(hasTable && notInAnnotation);
1994         action("activate_borderpainter")->setEnabled(hasTable && notInAnnotation);
1995     }
1996     action("insert_annotation")->setEnabled(notInAnnotation);
1997 
1998     ///TODO if selection contains several different format
1999     emit blockChanged(textEditor->block());
2000     emit charFormatChanged(cf, textEditor->blockCharFormat());
2001     emit blockFormatChanged(bf);
2002 }
2003 
2004 void TextTool::updateStyleManager()
2005 {
2006     if (!m_textShapeData)
2007         return;
2008     KoStyleManager *styleManager = KoTextDocument(m_textShapeData->document()).styleManager();
2009     emit styleManagerChanged(styleManager);
2010 
2011     //TODO move this to its own method
2012     m_changeTracker = KoTextDocument(m_textShapeData->document()).changeTracker();
2013 }
2014 
2015 void TextTool::activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes)
2016 {
2017     Q_UNUSED(toolActivation);
2018     m_caretTimer.start();
2019     m_caretTimerState = true;
2020     foreach (KoShape *shape, shapes) {
2021         TextShape *textShape = dynamic_cast<TextShape*>(shape);
2022         if (textShape) {
2023             if (!m_textEditor) {
2024                 m_textShape = textShape;
2025                 break;
2026             }
2027             // Since there is a text editor, we must select the shape that is edited,
2028             // or else we get out of sync
2029             KoTextShapeData *data = static_cast<KoTextShapeData*>(textShape->userData());
2030             if (data && data->document() == m_textEditor->constCursor().document()) {
2031                 m_textShape = textShape;
2032                 break;
2033             }
2034             if (!m_textShape) {
2035                 m_textShape = textShape;
2036             }
2037         }
2038     }
2039     if (!m_textShape) { // none found
2040         emit done();
2041         // This is how we inform the rulers of the active range
2042         // No shape means no active range
2043         canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, QVariant(QRectF()));
2044         return;
2045     }
2046 
2047     // This is how we inform the rulers of the active range
2048     // For now we will not consider table cells, but just give the shape dimensions
2049     QVariant v;
2050     QRectF rect(QPoint(), m_textShape->size());
2051     rect = m_textShape->absoluteTransformation(0).mapRect(rect);
2052     v.setValue(rect);
2053     canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, v);
2054     if ((!m_oldTextEditor.isNull()) && m_oldTextEditor.data()->document() != static_cast<KoTextShapeData*>(m_textShape->userData())->document()) {
2055         m_oldTextEditor.data()->setPosition(m_oldTextEditor.data()->position());
2056         //we need to redraw like this so we update the old textshape whereever it may be
2057         if (canvas()->canvasWidget())
2058             canvas()->canvasWidget()->update();
2059     }
2060     setShapeData(static_cast<KoTextShapeData*>(m_textShape->userData()));
2061     useCursor(Qt::IBeamCursor);
2062 
2063     updateStyleManager();
2064     repaintSelection();
2065     updateSelectionHandler();
2066     updateActions();
2067     if (m_specialCharacterDocker)
2068         m_specialCharacterDocker->setEnabled(true);
2069 }
2070 
2071 void TextTool::deactivate()
2072 {
2073     m_caretTimer.stop();
2074     m_caretTimerState = false;
2075     repaintCaret();
2076     m_textShape = 0;
2077 
2078     // This is how we inform the rulers of the active range
2079     // No shape means no active range
2080     canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, QVariant(QRectF()));
2081 
2082     m_oldTextEditor = m_textEditor;
2083     setShapeData(0);
2084 
2085     updateSelectionHandler();
2086     if (m_specialCharacterDocker) {
2087         m_specialCharacterDocker->setEnabled(false);
2088         m_specialCharacterDocker->setVisible(false);
2089     }
2090 }
2091 
2092 void TextTool::repaintDecorations()
2093 {
2094     if (m_textShapeData)
2095         repaintSelection();
2096 }
2097 
2098 void TextTool::repaintCaret()
2099 {
2100     KoTextEditor *textEditor = m_textEditor.data();
2101     if (!textEditor || !m_textShapeData)
2102         return;
2103 
2104     KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout*>(m_textShapeData->document()->documentLayout());
2105     Q_ASSERT(lay); Q_UNUSED(lay);
2106 
2107     // If we have changed root area we need to update m_textShape and m_textShapeData
2108     if (m_delayedEnsureVisible) {
2109         m_delayedEnsureVisible = false;
2110         ensureCursorVisible();
2111         return;
2112     }
2113 
2114     ensureCursorVisible(false); // ensures the various vars are updated
2115 
2116     bool upToDate;
2117     QRectF repaintRect = caretRect(textEditor->cursor(), &upToDate);
2118     repaintRect.moveTop(repaintRect.top() - m_textShapeData->documentOffset());
2119     if (repaintRect.isValid()) {
2120         repaintRect = m_textShape->absoluteTransformation(0).mapRect(repaintRect);
2121 
2122         // Make sure there is enough space to show an icon
2123         QRectF iconSize = canvas()->viewConverter()->viewToDocument(QRect(0, 0, 18, 18));
2124         repaintRect.setX(repaintRect.x() - iconSize.width() / 2);
2125         repaintRect.setRight(repaintRect.right() + iconSize.width() / 2);
2126         repaintRect.setTop(repaintRect.y() - iconSize.height() / 2);
2127         repaintRect.setBottom(repaintRect.bottom() + iconSize.height() / 2);
2128         canvas()->updateCanvas(repaintRect);
2129     }
2130 }
2131 
2132 void TextTool::repaintSelection()
2133 {
2134     KoTextEditor *textEditor = m_textEditor.data();
2135     if (textEditor == 0)
2136         return;
2137     QTextCursor cursor = *textEditor->cursor();
2138 
2139     QList<TextShape *> shapes;
2140     KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout*>(textEditor->document()->documentLayout());
2141     Q_ASSERT(lay);
2142     foreach (KoShape* shape, lay->shapes()) {
2143         TextShape *textShape = dynamic_cast<TextShape*>(shape);
2144         if (textShape == 0) // when the shape is being deleted its no longer a TextShape but a KoShape
2145             continue;
2146 
2147         //Q_ASSERT(!shapes.contains(textShape));
2148         if (!shapes.contains(textShape)) {
2149             shapes.append(textShape);
2150         }
2151     }
2152 
2153     // loop over all shapes that contain the text and update per shape.
2154     QRectF repaintRect = textRect(cursor);
2155     foreach (TextShape *ts, shapes) {
2156         QRectF rect = repaintRect;
2157         rect.moveTop(rect.y() - ts->textShapeData()->documentOffset());
2158         rect = ts->absoluteTransformation(0).mapRect(rect);
2159         QRectF r = ts->boundingRect().intersected(rect);
2160         canvas()->updateCanvas(r);
2161     }
2162 }
2163 
2164 QRectF TextTool::caretRect(QTextCursor *cursor, bool *upToDate) const
2165 {
2166     QTextCursor tmpCursor(*cursor);
2167     tmpCursor.setPosition(cursor->position()); // looses the anchor
2168 
2169     QRectF rect = textRect(tmpCursor);
2170     if (rect.size() == QSizeF(0,0)) {
2171         if (upToDate) {
2172             *upToDate = false;
2173         }
2174         rect = m_lastImMicroFocus; // prevent block changed but layout not done
2175     } else {
2176         if (upToDate) {
2177             *upToDate = true;
2178         }
2179         m_lastImMicroFocus = rect;
2180     }
2181     return rect;
2182 }
2183 
2184 QRectF TextTool::textRect(QTextCursor &cursor) const
2185 {
2186     if (!m_textShapeData)
2187         return QRectF();
2188     KoTextEditor *textEditor = m_textEditor.data();
2189     KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout*>(textEditor->document()->documentLayout());
2190     return lay->selectionBoundingBox(cursor);
2191 }
2192 
2193 KoToolSelection* TextTool::selection()
2194 {
2195     return m_toolSelection;
2196 }
2197 
2198 QList<QPointer<QWidget> > TextTool::createOptionWidgets()
2199 {
2200     QList<QPointer<QWidget> > widgets;
2201     SimpleCharacterWidget *scw = new SimpleCharacterWidget(this, 0);
2202     SimpleParagraphWidget *spw = new SimpleParagraphWidget(this, 0);
2203     if (m_textEditor.data()) {
2204 //        connect(m_textEditor.data(), SIGNAL(paragraphStyleApplied(KoParagraphStyle*)), spw, SLOT(slotParagraphStyleApplied(KoParagraphStyle*)));
2205 //        connect(m_textEditor.data(), SIGNAL(characterStyleApplied(KoCharacterStyle*)), scw, SLOT(slotCharacterStyleApplied(KoCharacterStyle*)));
2206         //initialise the char- and par- widgets with the current block and formats.
2207         scw->setCurrentBlockFormat(m_textEditor.data()->blockFormat());
2208         scw->setCurrentFormat(m_textEditor.data()->charFormat(), m_textEditor.data()-> blockCharFormat());
2209         spw->setCurrentBlock(m_textEditor.data()->block());
2210         spw->setCurrentFormat(m_textEditor.data()->blockFormat());
2211     }
2212     SimpleTableWidget *stw = new SimpleTableWidget(this, 0);
2213     SimpleInsertWidget *siw = new SimpleInsertWidget(this, 0);
2214 
2215 /* We do not use these for now. Let's see if they become useful at a certain point in time. If not, we can remove the whole chain (SimpleCharWidget, SimpleParWidget, DockerStyleComboModel)
2216     if (m_textShapeData && KoTextDocument(m_textShapeData->document()).styleManager()) {
2217         scw->setInitialUsedStyles(KoTextDocument(m_textShapeData->document()).styleManager()->usedCharacterStyles());
2218         spw->setInitialUsedStyles(KoTextDocument(m_textShapeData->document()).styleManager()->usedParagraphStyles());
2219     }
2220 */
2221     // Connect to/with simple character widget (docker)
2222     connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), scw, SLOT(setStyleManager(KoStyleManager*)));
2223     connect(this, SIGNAL(charFormatChanged(QTextCharFormat,QTextCharFormat)), scw, SLOT(setCurrentFormat(QTextCharFormat,QTextCharFormat)));
2224     connect(this, SIGNAL(blockFormatChanged(QTextBlockFormat)), scw, SLOT(setCurrentBlockFormat(QTextBlockFormat)));
2225     connect(scw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
2226     connect(scw, SIGNAL(characterStyleSelected(KoCharacterStyle*)), this, SLOT(setStyle(KoCharacterStyle*)));
2227     connect(scw, SIGNAL(newStyleRequested(QString)), this, SLOT(createStyleFromCurrentCharFormat(QString)));
2228     connect(scw, SIGNAL(showStyleManager(int)), this, SLOT(showStyleManager(int)));
2229 
2230 
2231     // Connect to/with simple paragraph widget (docker)
2232     connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), spw, SLOT(setStyleManager(KoStyleManager*)));
2233     connect(this, SIGNAL(blockChanged(QTextBlock)), spw, SLOT(setCurrentBlock(QTextBlock)));
2234     connect(this, SIGNAL(blockFormatChanged(QTextBlockFormat)), spw, SLOT(setCurrentFormat(QTextBlockFormat)));
2235     connect(spw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
2236     connect(spw, SIGNAL(paragraphStyleSelected(KoParagraphStyle*)), this, SLOT(setStyle(KoParagraphStyle*)));
2237     connect(spw, SIGNAL(newStyleRequested(QString)), this, SLOT(createStyleFromCurrentBlockFormat(QString)));
2238     connect(spw, SIGNAL(showStyleManager(int)), this, SLOT(showStyleManager(int)));
2239 
2240     // Connect to/with simple table widget (docker)
2241     connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), stw, SLOT(setStyleManager(KoStyleManager*)));
2242     connect(stw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
2243     connect(stw, SIGNAL(tableBorderDataUpdated(KoBorder::BorderData)), this, SLOT(setTableBorderData(KoBorder::BorderData)));
2244 
2245     // Connect to/with simple insert widget (docker)
2246     connect(siw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas()));
2247     connect(siw, SIGNAL(insertTableQuick(int,int)), this, SLOT(insertTableQuick(int,int)));
2248 
2249     updateStyleManager();
2250     if (m_textShape) {
2251         updateActions();
2252     }
2253     scw->setWindowTitle(i18n("Character"));
2254     widgets.append(scw);
2255     spw->setWindowTitle(i18n("Paragraph"));
2256     widgets.append(spw);
2257 
2258     bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality)
2259                              & KoCanvasResourceManager::NoAdvancedText);
2260     if (useAdvancedText) {
2261         stw->setWindowTitle(i18n("Table"));
2262         widgets.append(stw);
2263         siw->setWindowTitle(i18n("Insert"));
2264         widgets.append(siw);
2265     }
2266     return widgets;
2267 }
2268 
2269 void TextTool::returnFocusToCanvas()
2270 {
2271     canvas()->canvasWidget()->setFocus();
2272 }
2273 
2274 void TextTool::startEditing(KUndo2Command* command)
2275 {
2276     m_currentCommand = command;
2277     m_currentCommandHasChildren = true;
2278 }
2279 
2280 void TextTool::stopEditing()
2281 {
2282     m_currentCommand = 0;
2283     m_currentCommandHasChildren = false;
2284 }
2285 
2286 void TextTool::insertNewSection()
2287 {
2288     KoTextEditor *textEditor = m_textEditor.data();
2289     if (!textEditor) return;
2290 
2291     textEditor->newSection();
2292 }
2293 
2294 void TextTool::configureSection()
2295 {
2296     KoTextEditor *textEditor = m_textEditor.data();
2297     if (!textEditor) return;
2298 
2299     SectionFormatDialog *dia = new SectionFormatDialog(0, m_textEditor.data());
2300     dia->exec();
2301     delete dia;
2302 
2303     returnFocusToCanvas();
2304     updateActions();
2305 }
2306 
2307 void TextTool::splitSections()
2308 {
2309     KoTextEditor *textEditor = m_textEditor.data();
2310     if (!textEditor) return;
2311 
2312     SectionsSplitDialog *dia = new SectionsSplitDialog(0, m_textEditor.data());
2313     dia->exec();
2314     delete dia;
2315 
2316     returnFocusToCanvas();
2317     updateActions();
2318 }
2319 
2320 void TextTool::pasteAsText()
2321 {
2322     KoTextEditor *textEditor = m_textEditor.data();
2323     if (!textEditor) return;
2324 
2325     const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Clipboard);
2326     // on windows we do not have data if we try to paste this selection
2327     if (!data) return;
2328 
2329     if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))
2330         ||  data->hasText()) {
2331         m_prevCursorPosition = m_textEditor.data()->position();
2332         m_textEditor.data()->paste(canvas(), data, true);
2333         editingPluginEvents();
2334     }
2335 }
2336 
2337 void TextTool::bold(bool bold)
2338 {
2339     m_textEditor.data()->bold(bold);
2340 }
2341 
2342 void TextTool::italic(bool italic)
2343 {
2344     m_textEditor.data()->italic(italic);
2345 }
2346 
2347 void TextTool::underline(bool underline)
2348 {
2349     m_textEditor.data()->underline(underline);
2350 }
2351 
2352 void TextTool::strikeOut(bool strikeOut)
2353 {
2354     m_textEditor.data()->strikeOut(strikeOut);
2355 }
2356 
2357 void TextTool::nonbreakingSpace()
2358 {
2359     if (!m_allowActions || !m_textEditor.data()) return;
2360     m_textEditor.data()->insertText(QString(QChar(Qt::Key_nobreakspace)));
2361 }
2362 
2363 void TextTool::nonbreakingHyphen()
2364 {
2365     if (!m_allowActions || !m_textEditor.data()) return;
2366     m_textEditor.data()->insertText(QString(QChar(0x2013)));
2367 }
2368 
2369 void TextTool::softHyphen()
2370 {
2371     if (!m_allowActions || !m_textEditor.data()) return;
2372     m_textEditor.data()->insertText(QString(QChar(Qt::Key_hyphen)));
2373 }
2374 
2375 void TextTool::lineBreak()
2376 {
2377     if (!m_allowActions || !m_textEditor.data()) return;
2378     m_textEditor.data()->insertText(QString(QChar(0x2028)));
2379 }
2380 
2381 void TextTool::alignLeft()
2382 {
2383     if (!m_allowActions || !m_textEditor.data()) return;
2384     m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignLeft | Qt::AlignAbsolute);
2385 }
2386 
2387 void TextTool::alignRight()
2388 {
2389     if (!m_allowActions || !m_textEditor.data()) return;
2390     m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignRight | Qt::AlignAbsolute);
2391 }
2392 
2393 void TextTool::alignCenter()
2394 {
2395     if (!m_allowActions || !m_textEditor.data()) return;
2396     m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignHCenter);
2397 }
2398 
2399 void TextTool::alignBlock()
2400 {
2401     if (!m_allowActions || !m_textEditor.data()) return;
2402     m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignJustify);
2403 }
2404 
2405 void TextTool::superScript(bool on)
2406 {
2407     if (!m_allowActions || !m_textEditor.data()) return;
2408     if (on)
2409         m_actionFormatSub->setChecked(false);
2410     m_textEditor.data()->setVerticalTextAlignment(on ? Qt::AlignTop : Qt::AlignVCenter);
2411 }
2412 
2413 void TextTool::subScript(bool on)
2414 {
2415     if (!m_allowActions || !m_textEditor.data()) return;
2416     if (on)
2417         m_actionFormatSuper->setChecked(false);
2418     m_textEditor.data()->setVerticalTextAlignment(on ? Qt::AlignBottom : Qt::AlignVCenter);
2419 }
2420 
2421 void TextTool::increaseIndent()
2422 {
2423     if (!m_allowActions || !m_textEditor.data()) return;
2424     if (m_textEditor.data()->block().textList()) {
2425         ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::IncreaseLevel;
2426         ChangeListLevelCommand *cll = new ChangeListLevelCommand(*(m_textEditor.data()->cursor()), type, 1);
2427         m_textEditor.data()->addCommand(cll);
2428         editingPluginEvents();
2429     } else {
2430         m_textEditor.data()->increaseIndent();
2431     }
2432     updateActions();
2433 }
2434 
2435 void TextTool::decreaseIndent()
2436 {
2437     if (!m_allowActions || !m_textEditor.data()) return;
2438     if (m_textEditor.data()->block().textList()) {
2439         ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::DecreaseLevel;
2440         ChangeListLevelCommand *cll = new ChangeListLevelCommand(*(m_textEditor.data()->cursor()), type, 1);
2441         m_textEditor.data()->addCommand(cll);
2442         editingPluginEvents();
2443     } else {
2444         m_textEditor.data()->decreaseIndent();
2445     }
2446     updateActions();
2447 }
2448 
2449 void TextTool::decreaseFontSize()
2450 {
2451     if (!m_allowActions || !m_textEditor.data()) return;
2452     m_textEditor.data()->decreaseFontSize();
2453 }
2454 
2455 void TextTool::increaseFontSize()
2456 {
2457     if (!m_allowActions || !m_textEditor.data()) return;
2458     m_textEditor.data()->increaseFontSize();
2459 }
2460 
2461 void TextTool::setFontFamily(const QString &font)
2462 {
2463     if (!m_allowActions || !m_textEditor.data()) return;
2464     m_textEditor.data()->setFontFamily(font);
2465 }
2466 
2467 void TextTool::setFontSize (qreal size)
2468 {
2469     if (!m_allowActions || !m_textEditor.data()) return;
2470     m_textEditor.data()->setFontSize(size);
2471 }
2472 
2473 void TextTool::insertIndexMarker()
2474 {
2475     // TODO handle result when we figure out how to report errors from a tool.
2476     m_textEditor.data()->insertIndexMarker();
2477 }
2478 
2479 void TextTool::insertFrameBreak()
2480 {
2481     m_textEditor.data()->insertFrameBreak();
2482 
2483     ensureCursorVisible();
2484     m_delayedEnsureVisible = true;
2485 }
2486 
2487 void TextTool::setStyle(KoCharacterStyle *style)
2488 {
2489     KoCharacterStyle *charStyle = style;
2490     //if the given KoCharacterStyle is null, set the KoParagraphStyle character properties
2491     if (!charStyle){
2492         charStyle = static_cast<KoCharacterStyle*>(KoTextDocument(m_textShapeData->document()).styleManager()->paragraphStyle(m_textEditor.data()->blockFormat().intProperty(KoParagraphStyle::StyleId)));
2493     }
2494     if (charStyle) {
2495         m_textEditor.data()->setStyle(charStyle);
2496         updateActions();
2497     }
2498 }
2499 
2500 void TextTool::setStyle(KoParagraphStyle *style)
2501 {
2502     m_textEditor.data()->setStyle(style);
2503     updateActions();
2504 }
2505 
2506 void TextTool::insertTable()
2507 {
2508     TableDialog *dia = new TableDialog(0);
2509     if (dia->exec() == TableDialog::Accepted)
2510         m_textEditor.data()->insertTable(dia->rows(), dia->columns());
2511     delete dia;
2512 
2513     updateActions();
2514 }
2515 
2516 void TextTool::insertTableQuick(int rows, int columns)
2517 {
2518     m_textEditor.data()->insertTable(rows, columns);
2519     updateActions();
2520 }
2521 
2522 void TextTool::insertTableRowAbove()
2523 {
2524     m_textEditor.data()->insertTableRowAbove();
2525 }
2526 
2527 void TextTool::insertTableRowBelow()
2528 {
2529     m_textEditor.data()->insertTableRowBelow();
2530 }
2531 
2532 void TextTool::insertTableColumnLeft()
2533 {
2534     m_textEditor.data()->insertTableColumnLeft();
2535 }
2536 
2537 void TextTool::insertTableColumnRight()
2538 {
2539     m_textEditor.data()->insertTableColumnRight();
2540 }
2541 
2542 void TextTool::deleteTableColumn()
2543 {
2544     m_textEditor.data()->deleteTableColumn();
2545 }
2546 
2547 void TextTool::deleteTableRow()
2548 {
2549     m_textEditor.data()->deleteTableRow();
2550 }
2551 
2552 void TextTool::mergeTableCells()
2553 {
2554     m_textEditor.data()->mergeTableCells();
2555 }
2556 
2557 void TextTool::splitTableCells()
2558 {
2559     m_textEditor.data()->splitTableCells();
2560 }
2561 
2562 void TextTool::useTableBorderCursor()
2563 {
2564     static const unsigned char data[] = {
2565         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x68, 0x00,
2566         0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0xfd, 0x00,
2567         0x00, 0x80, 0x7e, 0x00, 0x00, 0x40, 0x3f, 0x00, 0x00, 0xa0, 0x1f, 0x00,
2568         0x00, 0xd0, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xf4, 0x03, 0x00,
2569         0x00, 0xe4, 0x01, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00,
2570         0x40, 0x32, 0x00, 0x00, 0xa0, 0x0f, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00,
2571         0xd0, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00,
2572         0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2573     };
2574 
2575     QBitmap result(32, 32);
2576     result.fill(Qt::color0);
2577     QPainter painter(&result);
2578     painter.drawPixmap(0, 0, QBitmap::fromData(QSize(25, 23), data));
2579     QBitmap brushMask = result.createHeuristicMask(false);
2580 
2581     useCursor(QCursor(result, brushMask, 1, 21));
2582 }
2583 
2584 void TextTool::setTableBorderData(const KoBorder::BorderData &data)
2585 {
2586     m_tablePenMode = true;
2587     m_tablePenBorderData = data;
2588 }
2589 
2590 void TextTool::formatParagraph()
2591 {
2592     ParagraphSettingsDialog *dia = new ParagraphSettingsDialog(this, m_textEditor.data());
2593     dia->setUnit(canvas()->unit());
2594     dia->setImageCollection(m_textShape->imageCollection());
2595     dia->exec();
2596     delete dia;
2597     returnFocusToCanvas();
2598 }
2599 
2600 void TextTool::testSlot(bool on)
2601 {
2602     debugTextShape << "signal received. bool:" << on;
2603 }
2604 
2605 void TextTool::selectAll()
2606 {
2607     KoTextEditor *textEditor = m_textEditor.data();
2608     if (!textEditor || !m_textShapeData)
2609         return;
2610     const int selectionLength = qAbs(textEditor->position() - textEditor->anchor());
2611     textEditor->movePosition(QTextCursor::End);
2612     textEditor->setPosition(0, QTextCursor::KeepAnchor);
2613     repaintSelection();
2614     if (selectionLength != qAbs(textEditor->position() - textEditor->anchor())) // it actually changed
2615         emit selectionChanged(true);
2616 }
2617 
2618 void TextTool::startMacro(const QString &title)
2619 {
2620     if (title != i18n("Key Press") && title !=i18n("Autocorrection")) //dirty hack while waiting for refactor of text editing
2621         m_textTyping = false;
2622     else
2623         m_textTyping = true;
2624 
2625     if (title != i18n("Delete") && title != i18n("Autocorrection")) //same dirty hack as above
2626         m_textDeleting = false;
2627     else
2628         m_textDeleting = true;
2629 
2630     if (m_currentCommand) return;
2631 
2632     class MacroCommand : public KUndo2Command
2633     {
2634     public:
2635         MacroCommand(const KUndo2MagicString &title) : KUndo2Command(title), m_first(true) {}
2636         void redo() override {
2637             if (! m_first)
2638                 KUndo2Command::redo();
2639             m_first = false;
2640         }
2641         bool mergeWith(const KUndo2Command *) override {
2642             return false;
2643         }
2644         bool m_first;
2645     };
2646 
2647     /**
2648      * FIXME: The messages genearted by the Text Tool might not be
2649      *        properly translated, since we don't control it in
2650      *        type-safe way.
2651      *
2652      *        The title is already translated string, we just don't
2653      *        have any type control over it.
2654      */
2655     KUndo2MagicString title_workaround = kundo2_noi18n(title);
2656     m_currentCommand = new MacroCommand(title_workaround);
2657     m_currentCommandHasChildren = false;
2658 }
2659 
2660 void TextTool::stopMacro()
2661 {
2662     if (!m_currentCommand)
2663         return;
2664     if (! m_currentCommandHasChildren)
2665         delete m_currentCommand;
2666     m_currentCommand = 0;
2667 }
2668 
2669 void TextTool::showStyleManager(int styleId)
2670 {
2671     if (!m_textShapeData)
2672         return;
2673     KoStyleManager *styleManager = KoTextDocument(m_textShapeData->document()).styleManager();
2674     Q_ASSERT(styleManager);
2675     if (!styleManager)
2676         return;  //don't crash
2677     StyleManagerDialog *dia = new StyleManagerDialog(canvas()->canvasWidget());
2678     dia->setStyleManager(styleManager);
2679     dia->setUnit(canvas()->unit());
2680 
2681     KoParagraphStyle *paragraphStyle = styleManager->paragraphStyle(styleId);
2682     if (paragraphStyle) {
2683         dia->setParagraphStyle(paragraphStyle);
2684     }
2685     KoCharacterStyle *characterStyle = styleManager->characterStyle(styleId);
2686     if (characterStyle) {
2687         dia->setCharacterStyle(characterStyle);
2688     }
2689     dia->show();
2690 }
2691 
2692 void TextTool::startTextEditingPlugin(const QString &pluginId)
2693 {
2694     KoTextEditingPlugin *plugin = textEditingPluginContainer()->plugin(pluginId);
2695     if (plugin) {
2696         if (m_textEditor.data()->hasSelection()) {
2697             plugin->checkSection(m_textShapeData->document(), m_textEditor.data()->selectionStart(), m_textEditor.data()->selectionEnd());
2698         } else
2699             plugin->finishedWord(m_textShapeData->document(), m_textEditor.data()->position());
2700     }
2701 }
2702 
2703 void TextTool::canvasResourceChanged(int key, const QVariant &var)
2704 {
2705     if (m_textEditor.isNull())
2706         return;
2707     if (!m_textShapeData)
2708         return;
2709     if (m_allowResourceManagerUpdates == false)
2710         return;
2711     if (key == KoText::CurrentTextPosition) {
2712         repaintSelection();
2713         m_textEditor.data()->setPosition(var.toInt());
2714         ensureCursorVisible();
2715     } else if (key == KoText::CurrentTextAnchor) {
2716         repaintSelection();
2717         int pos = m_textEditor.data()->position();
2718         m_textEditor.data()->setPosition(var.toInt());
2719         m_textEditor.data()->setPosition(pos, QTextCursor::KeepAnchor);
2720     } else if (key == KoCanvasResourceManager::Unit) {
2721         m_unit = var.value<KoUnit>();
2722     } else return;
2723 
2724     repaintSelection();
2725 }
2726 
2727 void TextTool::insertSpecialCharacter()
2728 {
2729     if (m_specialCharacterDocker == 0) {
2730         m_specialCharacterDocker = new InsertCharacter(canvas()->canvasWidget());
2731         connect(m_specialCharacterDocker, SIGNAL(insertCharacter(QString)),
2732                 this, SLOT(insertString(QString)));
2733     }
2734 
2735     m_specialCharacterDocker->show();
2736 }
2737 
2738 void TextTool::insertString(const QString& string)
2739 {
2740     m_textEditor.data()->insertText(string);
2741     returnFocusToCanvas();
2742 }
2743 
2744 void TextTool::selectFont()
2745 {
2746     FontDia *fontDlg = new FontDia(m_textEditor.data());
2747     fontDlg->exec();
2748     delete fontDlg;
2749     returnFocusToCanvas();
2750 }
2751 
2752 void TextTool::shapeAddedToCanvas()
2753 {
2754     debugTextShape;
2755     if (m_textShape) {
2756         KoSelection *selection = canvas()->shapeManager()->selection();
2757         KoShape *shape = selection->firstSelectedShape();
2758         if (shape != m_textShape && canvas()->shapeManager()->shapes().contains(m_textShape)) {
2759             // this situation applies when someone, not us, changed the selection by selecting another
2760             // text shape. Possibly by adding one.
2761             // Deselect the new shape again, so we can keep editing what we were already editing
2762             selection->select(m_textShape);
2763             selection->deselect(shape);
2764         }
2765     }
2766 }
2767 
2768 void TextTool::shapeDataRemoved()
2769 {
2770     m_textShapeData = 0;
2771     m_textShape = 0;
2772     if (!m_textEditor.isNull() && !m_textEditor.data()->cursor()->isNull()) {
2773         const QTextDocument *doc = m_textEditor.data()->document();
2774         Q_ASSERT(doc);
2775         KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout*>(doc->documentLayout());
2776         if (!lay || lay->shapes().isEmpty()) {
2777             emit done();
2778             return;
2779         }
2780         m_textShape = static_cast<TextShape*>(lay->shapes().first());
2781         m_textShapeData = static_cast<KoTextShapeData*>(m_textShape->userData());
2782         connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved()));
2783     }
2784 }
2785 
2786 void TextTool::createStyleFromCurrentBlockFormat(const QString &name)
2787 {
2788     KoTextDocument document(m_textShapeData->document());
2789     KoStyleManager *styleManager = document.styleManager();
2790     KoParagraphStyle *paragraphStyle = new KoParagraphStyle(m_textEditor.data()->blockFormat(), m_textEditor.data()->charFormat());
2791     paragraphStyle->setName(name);
2792     styleManager->add(paragraphStyle);
2793     m_textEditor.data()->setStyle(paragraphStyle);
2794     emit charFormatChanged(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
2795     emit blockFormatChanged(m_textEditor.data()->blockFormat());
2796 }
2797 
2798 void TextTool::createStyleFromCurrentCharFormat(const QString &name)
2799 {
2800     KoTextDocument document(m_textShapeData->document());
2801     KoStyleManager *styleManager = document.styleManager();
2802     KoCharacterStyle *originalCharStyle = styleManager->characterStyle(m_textEditor.data()->charFormat().intProperty(KoCharacterStyle::StyleId));
2803     KoCharacterStyle *autoStyle;
2804     if (!originalCharStyle) {
2805         KoCharacterStyle blankStyle;
2806         originalCharStyle = &blankStyle;
2807         autoStyle = originalCharStyle->autoStyle(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
2808         autoStyle->setParentStyle(0);
2809     } else {
2810         autoStyle = originalCharStyle->autoStyle(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
2811     }
2812     autoStyle->setName(name);
2813     styleManager->add(autoStyle);
2814     m_textEditor.data()->setStyle(autoStyle);
2815     emit charFormatChanged(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat());
2816 }
2817 
2818 // ---------- editing plugins methods.
2819 void TextTool::editingPluginEvents()
2820 {
2821     if (m_prevCursorPosition == -1 || m_prevCursorPosition == m_textEditor.data()->position()) {
2822         debugTextShape<<"m_prevCursorPosition="<<m_prevCursorPosition<<"m_textEditor.data()->position()="<<m_textEditor.data()->position();
2823         return;
2824     }
2825 
2826     QTextBlock block = m_textEditor.data()->block();
2827     if (! block.contains(m_prevCursorPosition)) {
2828         debugTextShape<<"m_prevCursorPosition="<<m_prevCursorPosition;
2829         finishedWord();
2830         finishedParagraph();
2831         m_prevCursorPosition = -1;
2832     } else {
2833         int from = m_prevCursorPosition;
2834         int to = m_textEditor.data()->position();
2835         if (from > to)
2836             qSwap(from, to);
2837         QString section = block.text().mid(from - block.position(), to - from);
2838         debugTextShape<<"from="<<from<<"to="<<to;
2839         if (section.contains(' ')) {
2840             finishedWord();
2841             m_prevCursorPosition = -1;
2842         }
2843     }
2844 }
2845 
2846 void TextTool::finishedWord()
2847 {
2848     if (m_textShapeData && textEditingPluginContainer()) {
2849         foreach (KoTextEditingPlugin* plugin, textEditingPluginContainer()->values()) {
2850             plugin->finishedWord(m_textShapeData->document(), m_prevCursorPosition);
2851         }
2852     }
2853 }
2854 
2855 void TextTool::finishedParagraph()
2856 {
2857     if (m_textShapeData && textEditingPluginContainer()) {
2858         foreach (KoTextEditingPlugin* plugin, textEditingPluginContainer()->values()) {
2859             plugin->finishedParagraph(m_textShapeData->document(), m_prevCursorPosition);
2860         }
2861     }
2862 }
2863 
2864 void TextTool::startingSimpleEdit()
2865 {
2866     if (m_textShapeData && textEditingPluginContainer()) {
2867         foreach (KoTextEditingPlugin* plugin, textEditingPluginContainer()->values()) {
2868             plugin->startingSimpleEdit(m_textShapeData->document(), m_prevCursorPosition);
2869         }
2870     }
2871 
2872 }
2873 
2874 void TextTool::setTextColor(const KoColor &color)
2875 {
2876     m_textEditor.data()->setTextColor(color.toQColor());
2877 }
2878 
2879 void TextTool::setBackgroundColor(const KoColor &color)
2880 {
2881     m_textEditor.data()->setTextBackgroundColor(color.toQColor());
2882 }
2883 
2884 void TextTool::setAutoResize(bool enabled)
2885 {
2886     m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoResize, enabled));
2887     updateActions();
2888 }
2889 
2890 void TextTool::setGrowWidthToFit(bool enabled)
2891 {
2892     m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoGrowWidth, enabled));
2893     updateActions();
2894 }
2895 
2896 void TextTool::setGrowHeightToFit(bool enabled)
2897 {
2898     m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoGrowHeight, enabled));
2899     updateActions();
2900 }
2901 
2902 void TextTool::setShrinkToFit(bool enabled)
2903 {
2904     m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::ShrinkToFitResize, enabled));
2905     updateActions();
2906 }
2907 
2908 void TextTool::runUrl(KoPointerEvent *event, QString &url)
2909 {
2910     QUrl _url = QUrl::fromLocalFile(url);
2911     if (_url.isLocalFile()) {
2912         QMimeDatabase db;
2913         QString type = db.mimeTypeForUrl(_url).name();
2914 
2915         if (KRun::isExecutableFile(_url, type)) {
2916             QString question = i18n("This link points to the program or script '%1'.\n"
2917                                     "Malicious programs can harm your computer. "
2918                                     "Are you sure that you want to run this program?", url);
2919             // this will also start local programs, so adding a "don't warn again"
2920             // checkbox will probably be too dangerous
2921             int choice = KMessageBox::warningYesNo(0, question, i18n("Open Link?"));
2922             if (choice != KMessageBox::Yes)
2923                 return;
2924         }
2925     }
2926 
2927     event->accept();
2928     new KRun(_url, 0);
2929 }
2930 
2931 void TextTool::debugTextDocument()
2932 {
2933 #ifndef NDEBUG
2934     if (!m_textShapeData)
2935         return;
2936     const int CHARSPERLINE = 80; // TODO Make configurable using ENV var?
2937     const int CHARPOSITION = 278301935;
2938     KoTextDocument document(m_textShapeData->document());
2939     KoStyleManager *styleManager = document.styleManager();
2940     KoInlineTextObjectManager *inlineManager = document.inlineTextObjectManager();
2941 
2942     QTextBlock block = m_textShapeData->document()->begin();
2943     for (;block.isValid(); block = block.next()) {
2944         QVariant var = block.blockFormat().property(KoParagraphStyle::StyleId);
2945         if (!var.isNull()) {
2946             KoParagraphStyle *ps = styleManager->paragraphStyle(var.toInt());
2947             debugTextShape << "--- Paragraph Style:" << (ps ? ps->name() : QString()) << var.toInt();
2948         }
2949         var = block.charFormat().property(KoCharacterStyle::StyleId);
2950         if (!var.isNull()) {
2951             KoCharacterStyle *cs = styleManager->characterStyle(var.toInt());
2952             debugTextShape << "--- Character Style:" << (cs ? cs->name() : QString()) << var.toInt();
2953         }
2954         int lastPrintedChar = -1;
2955         QTextBlock::iterator it;
2956         QString fragmentText;
2957         QList<QTextCharFormat> inlineCharacters;
2958         for (it = block.begin(); !it.atEnd(); ++it) {
2959             QTextFragment fragment = it.fragment();
2960             if (!fragment.isValid())
2961                 continue;
2962             QTextCharFormat fmt = fragment.charFormat();
2963             debugTextShape << "changeId: " << fmt.property(KoCharacterStyle::ChangeTrackerId);
2964             const int fragmentStart = fragment.position() - block.position();
2965             for (int i = fragmentStart; i < fragmentStart + fragment.length(); i += CHARSPERLINE) {
2966                 if (lastPrintedChar == fragmentStart-1)
2967                     fragmentText += '|';
2968                 if (lastPrintedChar < fragmentStart || i > fragmentStart) {
2969                     QString debug = block.text().mid(lastPrintedChar, CHARSPERLINE);
2970                     lastPrintedChar += CHARSPERLINE;
2971                     if (lastPrintedChar > block.length())
2972                         debug += "\\n";
2973                     debugTextShape << debug;
2974                 }
2975                 var = fmt.property(KoCharacterStyle::StyleId);
2976                 QString charStyleLong, charStyleShort;
2977                 if (! var.isNull()) { // named style
2978                     charStyleShort = QString::number(var.toInt());
2979                     KoCharacterStyle *cs = styleManager->characterStyle(var.toInt());
2980                     if (cs)
2981                         charStyleLong = cs->name();
2982                 }
2983                 if (inlineManager && fmt.hasProperty(KoCharacterStyle::InlineInstanceId)) {
2984                     QTextCharFormat inlineFmt = fmt;
2985                     inlineFmt.setProperty(CHARPOSITION, fragmentStart);
2986                     inlineCharacters << inlineFmt;
2987                 }
2988 
2989                 if (fragment.length() > charStyleLong.length())
2990                     fragmentText += charStyleLong;
2991                 else if (fragment.length() > charStyleShort.length())
2992                     fragmentText += charStyleShort;
2993                 else if (fragment.length() >= 2)
2994                     fragmentText += QChar(8230); // ellipsis
2995 
2996 
2997 
2998                 int rest =  fragmentStart - (lastPrintedChar-CHARSPERLINE) + fragment.length() - fragmentText.length();
2999                 rest = qMin(rest, CHARSPERLINE - fragmentText.length());
3000                 if (rest >= 2)
3001                     fragmentText = QString("%1%2").arg(fragmentText).arg(' ', rest);
3002                 if (rest >= 0)
3003                     fragmentText += '|';
3004                 if (fragmentText.length() >= CHARSPERLINE) {
3005                     debugTextShape << fragmentText;
3006                     fragmentText.clear();
3007                 }
3008             }
3009         }
3010         if (!fragmentText.isEmpty()) {
3011             debugTextShape << fragmentText;
3012         }
3013         else if (block.length() == 1) { // no actual tet
3014             debugTextShape << "\\n";
3015         }
3016         foreach (const QTextCharFormat &cf, inlineCharacters) {
3017             KoInlineObject *object= inlineManager->inlineTextObject(cf);
3018             debugTextShape << "At pos:" << cf.intProperty(CHARPOSITION) << object;
3019             // debugTextShape << "-> id:" << cf.intProperty(577297549);
3020         }
3021         QTextList *list = block.textList();
3022         if (list) {
3023             if (list->format().hasProperty(KoListStyle::StyleId)) {
3024                 KoListStyle *ls = styleManager->listStyle(list->format().intProperty(KoListStyle::StyleId));
3025                 debugTextShape << "   List style applied:" << ls->styleId() << ls->name();
3026             }
3027             else
3028                 debugTextShape << " +- is a list..." << list;
3029         }
3030     }
3031 #endif
3032 }
3033 
3034 void TextTool::debugTextStyles()
3035 {
3036 #ifndef NDEBUG
3037     if (!m_textShapeData)
3038         return;
3039     KoTextDocument document(m_textShapeData->document());
3040     KoStyleManager *styleManager = document.styleManager();
3041 
3042     QSet<int> seenStyles;
3043 
3044     foreach (KoParagraphStyle *style, styleManager->paragraphStyles()) {
3045         debugTextShape << style->styleId() << style->name() << (styleManager->defaultParagraphStyle() == style ? "[Default]" : "");
3046         KoListStyle *ls = style->listStyle();
3047         if (ls) { // optional ;)
3048             debugTextShape << "  +- ListStyle: " << ls->styleId() << ls->name()
3049                 << (ls == styleManager->defaultListStyle() ? "[Default]":"");
3050             foreach (int level, ls->listLevels()) {
3051                 KoListLevelProperties llp = ls->levelProperties(level);
3052                 debugTextShape << "  |  level" << llp.level() << " style (enum):" << llp.labelType();
3053                 if (llp.bulletCharacter().unicode() != 0) {
3054                     debugTextShape << "  |  bullet" << llp.bulletCharacter();
3055                 }
3056             }
3057             seenStyles << ls->styleId();
3058         }
3059     }
3060 
3061     bool first = true;
3062     foreach (KoCharacterStyle *style, styleManager->characterStyles()) {
3063         if (seenStyles.contains(style->styleId()))
3064             continue;
3065         if (first) {
3066             debugTextShape << "--- Character styles ---";
3067             first = false;
3068         }
3069         debugTextShape << style->styleId() << style->name();
3070         debugTextShape << style->font();
3071     }
3072 
3073     first = true;
3074     foreach (KoListStyle *style, styleManager->listStyles()) {
3075         if (seenStyles.contains(style->styleId()))
3076             continue;
3077         if (first) {
3078             debugTextShape << "--- List styles ---";
3079             first = false;
3080         }
3081         debugTextShape << style->styleId() << style->name()
3082                 << (style == styleManager->defaultListStyle() ? "[Default]":"");
3083     }
3084 #endif
3085 }
3086 
3087 void TextTool::textDirectionChanged()
3088 {
3089     if (!m_allowActions || !m_textEditor.data()) return;
3090 
3091     QTextBlockFormat blockFormat;
3092     if (m_actionChangeDirection->isChecked()) {
3093         blockFormat.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::RightLeftTopBottom);
3094     }
3095     else {
3096         blockFormat.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::LeftRightTopBottom);
3097      }
3098     m_textEditor.data()->mergeBlockFormat(blockFormat);
3099 }
3100 
3101 void TextTool::setListLevel(int level)
3102 {
3103     if (level < 1 || level > 10) {
3104         return;
3105     }
3106 
3107     KoTextEditor *textEditor = m_textEditor.data();
3108     if (textEditor->block().textList()) {
3109         ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::SetLevel;
3110         ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, level);
3111         textEditor->addCommand(cll);
3112         editingPluginEvents();
3113     }
3114 }
3115 
3116 void TextTool::insertAnnotation()
3117 {
3118     // HACK to avoid crash when we try to add a comment to an annotation shape.
3119     // We should not get here when the shape is an annotation shape,
3120     // but just disabling the insert_annotation action somehow does not work.
3121     if (m_textShape->shapeId() == AnnotationShape_SHAPEID) {
3122         return;
3123     }
3124     AnnotationTextShape *shape = (AnnotationTextShape*)KoShapeRegistry::instance()->value(AnnotationShape_SHAPEID)->createDefaultShape(canvas()->shapeController()->resourceManager());
3125     textEditor()->addAnnotation(shape);
3126 
3127     // Set annotation creator.
3128     KConfig cfg("calligrarc");
3129     cfg.reparseConfiguration();
3130     KConfigGroup authorGroup(&cfg, "Author");
3131     QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
3132      KSharedConfig::openConfig()->reparseConfiguration();
3133     KConfigGroup appAuthorGroup( KSharedConfig::openConfig(), "Author");
3134     QString profile = appAuthorGroup.readEntry("active-profile", "");
3135     KConfigGroup cgs(&authorGroup, "Author-" + profile);
3136 
3137     if (profiles.contains(profile)) {
3138         KConfigGroup cgs(&authorGroup, "Author-" + profile);
3139         shape->setCreator(cgs.readEntry("creator"));
3140     } else {
3141         if (profile == "anonymous") {
3142             shape->setCreator("Anonymous");
3143         } else {
3144             KUser user(KUser::UseRealUserID);
3145             shape->setCreator(user.property(KUser::FullName).toString());
3146         }
3147     }
3148     // Set Annotation creation date.
3149     shape->setDate(QDate::currentDate().toString(Qt::ISODate));
3150 }