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 }