diff --git a/plugins/flake/textshape/dialogs/BibliographyTemplate.cpp b/plugins/flake/textshape/dialogs/BibliographyTemplate.cpp index c75b5f1c37..638b18bca0 100644 --- a/plugins/flake/textshape/dialogs/BibliographyTemplate.cpp +++ b/plugins/flake/textshape/dialogs/BibliographyTemplate.cpp @@ -1,82 +1,56 @@ /* This file is part of the KDE project * Copyright (C) 2011 Smit Patel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "BibliographyTemplate.h" #include #include #include #include #include #include BibliographyTemplate::BibliographyTemplate(KoStyleManager *manager) : m_manager(manager) { Q_ASSERT(manager); } QList BibliographyTemplate::templates() { //if you are adding your own custom styles specifically for bibliography, add it as an unused style in KoStyleManager // when the bibliography is used the style will be automatically move to the usedStyle section QList predefinedTemplates; - KoBibliographyInfo *firstTemplate = new KoBibliographyInfo(); - firstTemplate->m_indexTitleTemplate.text = i18n("Bibliography"); - - firstTemplate->m_indexTitleTemplate.styleId = m_manager->defaultBibliographyTitleStyle()->styleId(); - firstTemplate->m_indexTitleTemplate.styleName = m_manager->defaultBibliographyTitleStyle()->name(); - - Q_FOREACH (const QString &bibType, KoOdfBibliographyConfiguration::bibTypes) { - firstTemplate->m_entryTemplate[bibType].styleId = m_manager->defaultBibliographyEntryStyle(bibType)->styleId(); - firstTemplate->m_entryTemplate[bibType].styleName = m_manager->defaultBibliographyEntryStyle(bibType)->name(); - } - firstTemplate->m_entryTemplate = BibliographyGenerator::defaultBibliographyEntryTemplates(); - - KoBibliographyInfo *secondTemplate = new KoBibliographyInfo(); - secondTemplate->m_indexTitleTemplate.text = i18n("References"); - - secondTemplate->m_indexTitleTemplate.styleId = m_manager->defaultBibliographyTitleStyle()->styleId(); - secondTemplate->m_indexTitleTemplate.styleName = m_manager->defaultBibliographyTitleStyle()->name(); - - Q_FOREACH (const QString &bibType, KoOdfBibliographyConfiguration::bibTypes) { - secondTemplate->m_entryTemplate[bibType].styleId = m_manager->defaultBibliographyEntryStyle(bibType)->styleId(); - secondTemplate->m_entryTemplate[bibType].styleName = m_manager->defaultBibliographyEntryStyle(bibType)->name(); - } - secondTemplate->m_entryTemplate = BibliographyGenerator::defaultBibliographyEntryTemplates(); - - predefinedTemplates.append(firstTemplate); - predefinedTemplates.append(secondTemplate); return predefinedTemplates; } void BibliographyTemplate::moveTemplateToUsed(KoBibliographyInfo *info) { if (m_manager->unusedStyle(info->m_indexTitleTemplate.styleId)) { m_manager->moveToUsedStyles(info->m_indexTitleTemplate.styleId); } Q_FOREACH (const QString &bibType, KoOdfBibliographyConfiguration::bibTypes) { if (m_manager->unusedStyle(info->m_entryTemplate[bibType].styleId)) { m_manager->moveToUsedStyles(info->m_entryTemplate[bibType].styleId); } } } diff --git a/plugins/flake/textshape/kotext/BibliographyGenerator.cpp b/plugins/flake/textshape/kotext/BibliographyGenerator.cpp deleted file mode 100644 index 4fd10541b1..0000000000 --- a/plugins/flake/textshape/kotext/BibliographyGenerator.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 Smit Patel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "BibliographyGenerator.h" -#include "KoInlineCite.h" - -#include -#include -#include -#include -#include -#include - -#include - -static QList sortKeys; - -BibliographyGenerator::BibliographyGenerator(QTextDocument *bibDocument, const QTextBlock &block, KoBibliographyInfo *bibInfo) - : QObject(bibDocument) - , m_bibDocument(bibDocument) - , m_bibInfo(bibInfo) - , m_block(block) -{ - Q_ASSERT(bibDocument); - Q_ASSERT(bibInfo); - - m_bibInfo->setGenerator(this); - - bibDocument->setUndoRedoEnabled(false); - generate(); -} - -BibliographyGenerator::~BibliographyGenerator() -{ - delete m_bibInfo; -} - -static bool compare_on(int keyIndex, KoInlineCite *c1, KoInlineCite *c2) -{ - if ( keyIndex == sortKeys.size() ) return false; - else if (sortKeys[keyIndex].second == Qt::AscendingOrder) { - if (c1->dataField( sortKeys[keyIndex].first ) < c2->dataField( sortKeys[keyIndex].first )) return true; - else if (c1->dataField( sortKeys[keyIndex].first ) > c2->dataField( sortKeys[keyIndex].first )) return false; - } else if (sortKeys[keyIndex].second == Qt::DescendingOrder) { - if (c1->dataField( sortKeys[keyIndex].first ) < c2->dataField( sortKeys[keyIndex].first )) return false; - else if (c1->dataField( sortKeys[keyIndex].first ) > c2->dataField( sortKeys[keyIndex].first )) return true; - } else return compare_on( keyIndex + 1, c1, c2 ); - - return false; -} - -static bool lessThan(KoInlineCite *c1, KoInlineCite *c2) -{ - return compare_on(0, c1, c2); -} - -static QList sort(QList cites, QList keys) -{ - sortKeys = keys; - sortKeys << QPair("identifier", Qt::AscendingOrder); - std::sort(cites.begin(), cites.end(), lessThan); - return cites; -} - -void BibliographyGenerator::generate() -{ - if (!m_bibInfo) - return; - - QTextCursor cursor = m_bibDocument->rootFrame()->lastCursorPosition(); - cursor.setPosition(m_bibDocument->rootFrame()->firstPosition(), QTextCursor::KeepAnchor); - cursor.beginEditBlock(); - - KoTextDocument koDocument(m_block.document()); - KoStyleManager *styleManager = koDocument.styleManager(); - - if (!m_bibInfo->m_indexTitleTemplate.text.isNull()) { - KoParagraphStyle *titleStyle = styleManager->paragraphStyle(m_bibInfo->m_indexTitleTemplate.styleId); - if (!titleStyle) { - titleStyle = styleManager->defaultBibliographyTitleStyle(); - m_bibInfo->m_indexTitleTemplate.styleName = titleStyle->name(); - } - - QTextBlock titleTextBlock = cursor.block(); - titleStyle->applyStyle(titleTextBlock); - - cursor.insertText(m_bibInfo->m_indexTitleTemplate.text); - cursor.insertBlock(); - } - - QTextCharFormat savedCharFormat = cursor.charFormat(); - - QList citeList; - if (styleManager->bibliographyConfiguration()->sortByPosition()) { - citeList = koDocument.inlineTextObjectManager()-> - citationsSortedByPosition(false, m_block.document()->firstBlock()); - } else { - citeList = sort(koDocument.inlineTextObjectManager()-> - citationsSortedByPosition(false, m_block.document()->firstBlock()), - koDocument.styleManager()->bibliographyConfiguration()->sortKeys()); - } - - foreach (KoInlineCite *cite, citeList) - { - KoParagraphStyle *bibTemplateStyle = 0; - BibliographyEntryTemplate bibEntryTemplate; - if (m_bibInfo->m_entryTemplate.contains(cite->bibliographyType())) { - - bibEntryTemplate = m_bibInfo->m_entryTemplate[cite->bibliographyType()]; - - bibTemplateStyle = styleManager->paragraphStyle(bibEntryTemplate.styleId); - if (bibTemplateStyle == 0) { - bibTemplateStyle = styleManager->defaultBibliographyEntryStyle(bibEntryTemplate.bibliographyType); - bibEntryTemplate.styleName = bibTemplateStyle->name(); - } - } else { - continue; - } - - cursor.insertBlock(QTextBlockFormat(),QTextCharFormat()); - - QTextBlock bibEntryTextBlock = cursor.block(); - bibTemplateStyle->applyStyle(bibEntryTextBlock); - bool spanEnabled = false; //true if data field is not empty - - foreach (IndexEntry * entry, bibEntryTemplate.indexEntries) { - switch(entry->name) { - case IndexEntry::BIBLIOGRAPHY: { - IndexEntryBibliography *indexEntry = static_cast(entry); - cursor.insertText(QString(((spanEnabled)?" ":"")).append(cite->dataField(indexEntry->dataField))); - spanEnabled = !cite->dataField(indexEntry->dataField).isEmpty(); - break; - } - case IndexEntry::SPAN: { - if(spanEnabled) { - IndexEntrySpan *span = static_cast(entry); - cursor.insertText(span->text); - } - break; - } - case IndexEntry::TAB_STOP: { - IndexEntryTabStop *tabEntry = static_cast(entry); - - cursor.insertText("\t"); - - QTextBlockFormat blockFormat = cursor.blockFormat(); - QList tabList; - if (tabEntry->m_position == "MAX") { - tabEntry->tab.position = m_maxTabPosition; - } else { - tabEntry->tab.position = tabEntry->m_position.toDouble(); - } - tabList.append(QVariant::fromValue(tabEntry->tab)); - blockFormat.setProperty(KoParagraphStyle::TabPositions, QVariant::fromValue >(tabList)); - cursor.setBlockFormat(blockFormat); - break; - } - default:{ - break; - } - } - }// foreach - } - cursor.setCharFormat(savedCharFormat); // restore the cursor char format -} - -QMap BibliographyGenerator::defaultBibliographyEntryTemplates() -{ - QMap entryTemplates; - foreach (const QString &bibType, KoOdfBibliographyConfiguration::bibTypes) { - BibliographyEntryTemplate bibEntryTemplate; - - //Now creating default IndexEntries for all BibliographyEntryTemplates - IndexEntryBibliography *identifier = new IndexEntryBibliography(QString()); - IndexEntryBibliography *author = new IndexEntryBibliography(QString()); - IndexEntryBibliography *title = new IndexEntryBibliography(QString()); - IndexEntryBibliography *year = new IndexEntryBibliography(QString()); - IndexEntrySpan *firstSpan = new IndexEntrySpan(QString()); - IndexEntrySpan *otherSpan = new IndexEntrySpan(QString()); - - identifier->dataField = "identifier"; - author->dataField = "author"; - title->dataField = "title"; - year->dataField = "year"; - firstSpan->text = ":"; - otherSpan->text = ","; - - bibEntryTemplate.bibliographyType = bibType; - bibEntryTemplate.indexEntries.append(static_cast(identifier)); - bibEntryTemplate.indexEntries.append(static_cast(firstSpan)); - bibEntryTemplate.indexEntries.append(static_cast(author)); - bibEntryTemplate.indexEntries.append(static_cast(otherSpan)); - bibEntryTemplate.indexEntries.append(static_cast(title)); - bibEntryTemplate.indexEntries.append(static_cast(otherSpan)); - bibEntryTemplate.indexEntries.append(static_cast(year)); - - entryTemplates[bibType] = bibEntryTemplate; - } - return entryTemplates; -} diff --git a/plugins/flake/textshape/kotext/BibliographyGenerator.h b/plugins/flake/textshape/kotext/BibliographyGenerator.h deleted file mode 100644 index bcd78a1ef5..0000000000 --- a/plugins/flake/textshape/kotext/BibliographyGenerator.h +++ /dev/null @@ -1,47 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2011 Smit Patel - - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -#ifndef BIBLIOGRAPHYGENERATOR_H -#define BIBLIOGRAPHYGENERATOR_H - -#include -#include "kritatext_export.h" - -#include - -class KRITATEXT_EXPORT BibliographyGenerator : public QObject, public BibliographyGeneratorInterface -{ - Q_OBJECT -public: - explicit BibliographyGenerator(QTextDocument *bibDocument, const QTextBlock &block, KoBibliographyInfo *bibInfo); - ~BibliographyGenerator() override; - - static QMap defaultBibliographyEntryTemplates(); - -public Q_SLOTS: - void generate(); - -private: - QTextDocument *m_document; - QTextDocument *m_bibDocument; - KoBibliographyInfo *m_bibInfo; - QTextBlock m_block; - qreal m_maxTabPosition; -}; - -#endif diff --git a/plugins/flake/textshape/kotext/CMakeLists.txt b/plugins/flake/textshape/kotext/CMakeLists.txt index c8743a5852..4c93537505 100644 --- a/plugins/flake/textshape/kotext/CMakeLists.txt +++ b/plugins/flake/textshape/kotext/CMakeLists.txt @@ -1,135 +1,134 @@ include_directories(${FONTCONFIG_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS}) set(kritatext_LIB_SRCS KoDocumentRdfBase.cpp KoText.cpp KoTextBlockData.cpp KoTextBlockBorderData.cpp KoTextBlockPaintStrategyBase.cpp KoTextOdfSaveHelper.cpp KoTextDocument.cpp KoTextEditor.cpp KoTextEditor_undo.cpp KoTextEditor_format.cpp KoList.cpp KoTextEditingRegistry.cpp KoTextEditingFactory.cpp KoTextEditingPlugin.cpp KoTextRangeManager.cpp KoInlineTextObjectManager.cpp KoInlineObjectFactoryBase.cpp KoInlineObjectRegistry.cpp InsertInlineObjectActionBase_p.cpp InsertVariableAction.cpp InsertNamedVariableAction.cpp InsertTextReferenceAction.cpp InsertTextLocator.cpp KoInlineObject.cpp KoTextRange.cpp KoVariable.cpp KoVariableManager.cpp KoNamedVariable.cpp KoSection.cpp KoSectionEnd.cpp KoSectionUtils.cpp KoSectionModel.cpp KoTextLocator.cpp KoTextReference.cpp KoAnchorInlineObject.cpp KoAnchorTextRange.cpp KoTextShapeSavingContext.cpp KoAnnotation.cpp KoAnnotationManager.cpp KoBookmark.cpp KoBookmarkManager.cpp KoInlineNote.cpp KoInlineCite.cpp KoTextSoftPageBreak.cpp KoTextDebug.cpp KoTextPage.cpp KoPageProvider.cpp KoTableColumnAndRowStyleManager.cpp KoTextInlineRdf.cpp KoTextMeta.cpp KoTextTableTemplate.cpp OdfTextTrackStyles.cpp ToCBibGeneratorInfo.cpp KoTableOfContentsGeneratorInfo.cpp KoBibliographyInfo.cpp - BibliographyGenerator.cpp styles/Styles_p.cpp styles/KoCharacterStyle.cpp styles/KoParagraphStyle.cpp styles/KoStyleManager.cpp styles/KoListStyle.cpp styles/KoListLevelProperties.cpp styles/KoTableStyle.cpp styles/KoTableColumnStyle.cpp styles/KoTableRowStyle.cpp styles/KoTableCellStyle.cpp styles/KoSectionStyle.cpp opendocument/KoTextSharedLoadingData.cpp opendocument/KoTextSharedSavingData.cpp opendocument/KoTextLoader.cpp opendocument/KoTextWriter_p.cpp opendocument/KoTextWriter.cpp changetracker/KoChangeTracker.cpp changetracker/KoChangeTrackerElement.cpp changetracker/KoFormatChangeInformation.cpp changetracker/KoDeletedRowColumnDataStore.cpp changetracker/KoDeletedRowData.cpp changetracker/KoDeletedColumnData.cpp changetracker/KoDeletedCellData.cpp commands/ChangeAnchorPropertiesCommand.cpp commands/ChangeListCommand.cpp commands/ChangeStylesCommand.cpp commands/ChangeStylesMacroCommand.cpp commands/DeleteAnchorsCommand.cpp commands/DeleteAnnotationsCommand.cpp commands/DeleteCommand.cpp commands/DeleteTableColumnCommand.cpp commands/DeleteTableRowCommand.cpp commands/InsertNoteCommand.cpp commands/InsertTableColumnCommand.cpp commands/InsertTableRowCommand.cpp commands/ResizeTableCommand.cpp commands/InsertInlineObjectCommand.cpp commands/ListItemNumberingCommand.cpp commands/TextPasteCommand.cpp commands/AddTextRangeCommand.cpp commands/AddAnnotationCommand.cpp commands/ParagraphFormattingCommand.cpp commands/RenameSectionCommand.cpp commands/NewSectionCommand.cpp commands/SplitSectionsCommand.cpp KoTextDrag.cpp KoTextCommandBase.cpp TextDebug.cpp ) add_library(kritatext SHARED ${kritatext_LIB_SRCS}) generate_export_header(kritatext BASE_NAME kritatext) target_link_libraries(kritatext kritaflake Qt5::Gui kritawidgetutils ) target_link_libraries(kritatext LINK_INTERFACE_LIBRARIES kritaflake Qt5::Gui ) target_include_directories(kritatext PUBLIC $ $ $ ) set_target_properties(kritatext PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritatext ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/plugins/flake/textshape/kotext/KoTextEditor.cpp b/plugins/flake/textshape/kotext/KoTextEditor.cpp index 30eb8ec02f..cf2ac88238 100644 --- a/plugins/flake/textshape/kotext/KoTextEditor.cpp +++ b/plugins/flake/textshape/kotext/KoTextEditor.cpp @@ -1,1588 +1,1585 @@ /* This file is part of the KDE project * Copyright (C) 2009-2012 Pierre Stirnweiss * Copyright (C) 2006-2010 Thomas Zander * Copyright (c) 2011 Boudewijn Rempt * Copyright (C) 2011-2015 C. Boemann * Copyright (C) 2014-2015 Denis Kuplyakov * Copyright (C) 2015 Soma Schliszka * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoTextEditor.h" #include "KoTextEditor_p.h" #include "KoList.h" #include "KoBookmark.h" #include "KoAnnotation.h" #include "KoTextRangeManager.h" #include "KoInlineTextObjectManager.h" #include "KoInlineNote.h" #include "KoInlineCite.h" -#include "BibliographyGenerator.h" #include #include #include #include #include #include "KoShapeAnchor.h" #include "KoTextDocument.h" #include "KoTextLocator.h" #include "KoTableOfContentsGeneratorInfo.h" #include "KoBibliographyInfo.h" #include "changetracker/KoChangeTracker.h" #include "changetracker/KoChangeTrackerElement.h" #include "styles/KoCharacterStyle.h" #include "styles/KoParagraphStyle.h" #include "styles/KoStyleManager.h" #include "styles/KoTableCellStyle.h" #include "styles/KoTableStyle.h" #include "KoTableColumnAndRowStyleManager.h" #include "commands/DeleteTableRowCommand.h" #include "commands/DeleteTableColumnCommand.h" #include "commands/InsertTableRowCommand.h" #include "commands/InsertTableColumnCommand.h" #include "commands/ResizeTableCommand.h" #include "commands/TextPasteCommand.h" #include "commands/ListItemNumberingCommand.h" #include "commands/ChangeListCommand.h" #include "commands/InsertInlineObjectCommand.h" #include "commands/DeleteCommand.h" #include "commands/DeleteAnchorsCommand.h" #include "commands/DeleteAnnotationsCommand.h" #include "commands/InsertNoteCommand.h" #include "commands/AddTextRangeCommand.h" #include "commands/AddAnnotationCommand.h" #include "commands/RenameSectionCommand.h" #include "commands/NewSectionCommand.h" #include "commands/SplitSectionsCommand.h" #include #include #include #include #include #include #include #include #include #include #include #include "TextDebug.h" #include "KoTextDebug.h" Q_DECLARE_METATYPE(QTextFrame*) /*Private*/ KoTextEditor::Private::Private(KoTextEditor *qq, QTextDocument *document) : q(qq) , document (document) , addNewCommand(true) , dummyMacroAdded(false) , customCommandCount(0) , editProtectionCached(false) { caret = QTextCursor(document); editorState = NoOp; } void KoTextEditor::Private::emitTextFormatChanged() { emit q->textFormatChanged(); } void KoTextEditor::Private::newLine(KUndo2Command *parent) { // Handle if this is the special block before a table bool hiddenTableHandling = caret.blockFormat().hasProperty(KoParagraphStyle::HiddenByTable); if (hiddenTableHandling) { // Easy solution is to go back to the end of previous block and do the insertion from there. // However if there is no block before we have a problem. This may be the case if there is // a table before or we are at the beginning of a cell or a document. // So here is a better approach // 1) create block // 2) select the previous block so it gets deleted and replaced // 3) remove HiddenByTable from both new and previous block // 4) actually make new line replacing the block we just inserted // 5) set HiddenByTable on the block just before the table again caret.insertText("oops you should never see this"); caret.insertBlock(); caret.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); caret.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); QTextBlockFormat bf = caret.blockFormat(); bf.clearProperty(KoParagraphStyle::HiddenByTable); caret.setBlockFormat(bf); } if (caret.hasSelection()) { q->deleteChar(false, parent); } KoTextDocument textDocument(document); KoStyleManager *styleManager = textDocument.styleManager(); KoParagraphStyle *nextStyle = 0; KoParagraphStyle *currentStyle = 0; if (styleManager) { int id = caret.blockFormat().intProperty(KoParagraphStyle::StyleId); currentStyle = styleManager->paragraphStyle(id); if (currentStyle == 0) // not a style based parag. Lets make the next one correct. nextStyle = styleManager->defaultParagraphStyle(); else nextStyle = styleManager->paragraphStyle(currentStyle->nextStyle()); Q_ASSERT(nextStyle); if (currentStyle == nextStyle) nextStyle = 0; } QTextCharFormat format = caret.charFormat(); if (format.hasProperty(KoCharacterStyle::ChangeTrackerId)) { format.clearProperty(KoCharacterStyle::ChangeTrackerId); } // Build the block format and subtract the properties that are not inherited QTextBlockFormat bf = caret.blockFormat(); bf.clearProperty(KoParagraphStyle::BreakBefore); bf.clearProperty(KoParagraphStyle::ListStartValue); bf.clearProperty(KoParagraphStyle::UnnumberedListItem); bf.clearProperty(KoParagraphStyle::IsListHeader); bf.clearProperty(KoParagraphStyle::MasterPageName); bf.clearProperty(KoParagraphStyle::OutlineLevel); bf.clearProperty(KoParagraphStyle::HiddenByTable); // We should stay in the same section so we can't start new one. bf.clearProperty(KoParagraphStyle::SectionStartings); // But we move all the current endings to the next paragraph. QTextBlockFormat origin = caret.blockFormat(); origin.clearProperty(KoParagraphStyle::SectionEndings); caret.setBlockFormat(origin); // Build the block char format which is just a copy QTextCharFormat bcf = caret.blockCharFormat(); // Actually insert the new paragraph char int startPosition = caret.position(); caret.insertBlock(bf, bcf); int endPosition = caret.position(); // Mark the CR as a tracked change QTextCursor changeCursor(document); changeCursor.beginEditBlock(); changeCursor.setPosition(startPosition); changeCursor.setPosition(endPosition, QTextCursor::KeepAnchor); changeCursor.endEditBlock(); q->registerTrackedChange(changeCursor, KoGenChange::InsertChange, kundo2_i18n("New Paragraph"), format, format, false); // possibly change the style if requested if (nextStyle) { QTextBlock block = caret.block(); if (currentStyle) currentStyle->unapplyStyle(block); nextStyle->applyStyle(block); format = block.charFormat(); } caret.setCharFormat(format); if (hiddenTableHandling) { // see code and comment above QTextBlockFormat bf = caret.blockFormat(); bf.setProperty(KoParagraphStyle::HiddenByTable, true); caret.setBlockFormat(bf); caret.movePosition(QTextCursor::PreviousCharacter); } } /*KoTextEditor*/ //TODO factor out the changeTracking charFormat setting from all individual slots to a public slot, which will be available for external commands (TextShape) //The BlockFormatVisitor and CharFormatVisitor are used when a property needs to be modified relative to its current value (which could be different over the selection). For example: increase indentation by 10pt. //The BlockFormatVisitor is also used for the change tracking of a blockFormat. The changeTracker stores the information about the changeId in the charFormat. The BlockFormatVisitor ensures that thd changeId is set on the whole block (even if only a part of the block is actually selected). //Should such mechanisms be later provided directly by Qt, we could dispose of these classes. KoTextEditor::KoTextEditor(QTextDocument *document) : QObject(document), d (new Private(this, document)) { connect (d->document, SIGNAL (undoCommandAdded()), this, SLOT (documentCommandAdded())); } KoTextEditor::~KoTextEditor() { delete d; } KoTextEditor *KoTextEditor::getTextEditorFromCanvas(KoCanvasBase *canvas) { KoSelection *selection = canvas->shapeManager()->selection(); if (selection) { Q_FOREACH (KoShape *shape, selection->selectedShapes()) { if (KoTextShapeDataBase *textData = qobject_cast(shape->userData())) { KoTextDocument doc(textData->document()); return doc.textEditor(); } } } return 0; } QTextCursor* KoTextEditor::cursor() { return &(d->caret); } const QTextCursor KoTextEditor::constCursor() const { return QTextCursor(d->caret); } void KoTextEditor::registerTrackedChange(QTextCursor &/*selection*/, KoGenChange::Type /*changeType*/, const KUndo2MagicString &/*title*/, QTextFormat& /*format*/, QTextFormat& /*prevFormat*/, bool /*applyToWholeBlock*/) { } // To figure out if a the blocks of the selection are write protected we need to // traverse the entire document as sections build up the protectiveness recursively. void KoTextEditor::recursivelyVisitSelection(QTextFrame::iterator it, KoTextVisitor &visitor) const { do { if (visitor.abortVisiting()) return; QTextBlock block = it.currentBlock(); QTextTable *table = qobject_cast(it.currentFrame()); QTextFrame *subFrame = it.currentFrame(); if (table) { // There are 4 ways this table can be selected: // - "before to mid" // - "mid to after" // - "complex mid to mid" // - "simple mid to mid" // The 3 first are entire cells, the fourth is within a cell if (d->caret.selectionStart() <= table->lastPosition() && d->caret.selectionEnd() >= table->firstPosition()) { // We have a selection somewhere QTextTableCell cell1 = table->cellAt(d->caret.selectionStart()); QTextTableCell cell2 = table->cellAt(d->caret.selectionEnd()); if (cell1 != cell2 || !cell1.isValid() || !cell2.isValid()) { // And the selection is complex or entire table int selectionRow; int selectionColumn; int selectionRowSpan; int selectionColumnSpan; if (!cell1.isValid() || !cell2.isValid()) { // entire table visitor.visitTable(table, KoTextVisitor::Entirely); selectionRow = selectionColumn = 0; selectionRowSpan = table->rows(); selectionColumnSpan = table->columns(); } else { visitor.visitTable(table, KoTextVisitor::Partly); d->caret.selectedTableCells(&selectionRow, &selectionRowSpan, &selectionColumn, &selectionColumnSpan); } for (int r = selectionRow; r < selectionRow + selectionRowSpan; r++) { for (int c = selectionColumn; c < selectionColumn + selectionColumnSpan; c++) { QTextTableCell cell = table->cellAt(r,c); if (!cell.format().boolProperty(KoTableCellStyle::CellIsProtected)) { visitor.visitTableCell(&cell, KoTextVisitor::Partly); recursivelyVisitSelection(cell.begin(), visitor); } else { visitor.nonVisit(); } if (visitor.abortVisiting()) return; } } } else { visitor.visitTable(table, KoTextVisitor::Partly); // And the selection is simple if (!cell1.format().boolProperty(KoTableCellStyle::CellIsProtected)) { visitor.visitTableCell(&cell1, KoTextVisitor::Entirely); recursivelyVisitSelection(cell1.begin(), visitor); } else { visitor.nonVisit(); } return; } } if (d->caret.selectionEnd() <= table->lastPosition()) { return; } } else if (subFrame) { recursivelyVisitSelection(subFrame->begin(), visitor); } else { // TODO build up the section stack if (d->caret.selectionStart() < block.position() + block.length() && d->caret.selectionEnd() >= block.position()) { // We have a selection somewhere if (true) { // TODO don't change if block is protected by section visitor.visitBlock(block, d->caret); } else { visitor.nonVisit(); } } // TODO tear down the section stack if (d->caret.selectionEnd() < block.position() + block.length()) { return; } } if (!it.atEnd()) { ++it; } } while (!it.atEnd()); } KoBookmark *KoTextEditor::addBookmark(const QString &name) {//TODO changeTracking KUndo2Command *topCommand = beginEditBlock(kundo2_i18n("Add Bookmark")); KoBookmark *bookmark = new KoBookmark(d->caret); bookmark->setName(name); bookmark->setManager(KoTextDocument(d->document).textRangeManager()); addCommand(new AddTextRangeCommand(bookmark, topCommand)); endEditBlock(); return bookmark; } KoTextRangeManager *KoTextEditor::textRangeManager() const { return KoTextDocument(d->document).textRangeManager(); } KoAnnotation *KoTextEditor::addAnnotation(KoShape *annotationShape) { KUndo2Command *topCommand = beginEditBlock(kundo2_i18n("Add Annotation")); KoAnnotation *annotation = new KoAnnotation(d->caret); KoTextRangeManager *textRangeManager = KoTextDocument(d->document).textRangeManager(); annotation->setManager(textRangeManager); //FIXME: I need the name, a unique name, we can set selected text as annotation name or use createUniqueAnnotationName function // to do it for us. QString name = annotation->createUniqueAnnotationName(textRangeManager->annotationManager(), "", false); annotation->setName(name); annotation->setAnnotationShape(annotationShape); addCommand(new AddAnnotationCommand(annotation, topCommand)); endEditBlock(); return annotation; } KoInlineObject *KoTextEditor::insertIndexMarker() {//TODO changeTracking if (isEditProtected()) { return 0; } d->updateState(KoTextEditor::Private::Custom, kundo2_i18n("Insert Index")); if (d->caret.blockFormat().hasProperty(KoParagraphStyle::HiddenByTable)) { d->newLine(0); } QTextBlock block = d->caret.block(); if (d->caret.position() >= block.position() + block.length() - 1) return 0; // can't insert one at end of text if (block.text()[ d->caret.position() - block.position()].isSpace()) return 0; // can't insert one on a whitespace as that does not indicate a word. KoTextLocator *tl = new KoTextLocator(); KoTextDocument(d->document).inlineTextObjectManager()->insertInlineObject(d->caret, tl); d->updateState(KoTextEditor::Private::NoOp); return tl; } void KoTextEditor::insertInlineObject(KoInlineObject *inliner, KUndo2Command *cmd) { if (isEditProtected()) { return; } KUndo2Command *topCommand = cmd; if (!cmd) { topCommand = beginEditBlock(kundo2_i18n("Insert Variable")); } if (d->caret.hasSelection()) { deleteChar(false, topCommand); } d->caret.beginEditBlock(); if (d->caret.blockFormat().hasProperty(KoParagraphStyle::HiddenByTable)) { d->newLine(0); } QTextCharFormat format = d->caret.charFormat(); if (format.hasProperty(KoCharacterStyle::ChangeTrackerId)) { format.clearProperty(KoCharacterStyle::ChangeTrackerId); } InsertInlineObjectCommand *insertInlineObjectCommand = new InsertInlineObjectCommand(inliner, d->document, topCommand); Q_UNUSED(insertInlineObjectCommand); d->caret.endEditBlock(); if (!cmd) { addCommand(topCommand); endEditBlock(); } emit cursorPositionChanged(); } void KoTextEditor::updateInlineObjectPosition(int start, int end) { KoInlineTextObjectManager *inlineObjectManager = KoTextDocument(d->document).inlineTextObjectManager(); // and, of course, every inline object after the current position has the wrong position QTextCursor cursor = d->document->find(QString(QChar::ObjectReplacementCharacter), start); while (!cursor.isNull() && (end > -1 && cursor.position() < end )) { QTextCharFormat fmt = cursor.charFormat(); KoInlineObject *obj = inlineObjectManager->inlineTextObject(fmt); obj->updatePosition(d->document, cursor.position(), fmt); cursor = d->document->find(QString(QChar::ObjectReplacementCharacter), cursor.position()); } } void KoTextEditor::removeAnchors(const QList &anchors, KUndo2Command *parent) { Q_ASSERT(parent); addCommand(new DeleteAnchorsCommand(anchors, d->document, parent)); } void KoTextEditor::removeAnnotations(const QList &annotations, KUndo2Command *parent) { Q_ASSERT(parent); addCommand(new DeleteAnnotationsCommand(annotations, d->document, parent)); } void KoTextEditor::insertFrameBreak() { if (isEditProtected()) { return; } QTextCursor curr(d->caret.block()); if (dynamic_cast (curr.currentFrame())) { return; } d->updateState(KoTextEditor::Private::KeyPress, kundo2_i18n("Insert Break")); QTextBlock block = d->caret.block(); if (d->caret.position() == block.position() && block.length() > 0) { // start of parag QTextBlockFormat bf = d->caret.blockFormat(); bf.setProperty(KoParagraphStyle::BreakBefore, KoText::PageBreak); d->caret.insertBlock(bf); if (block.textList()) block.textList()->remove(block); } else { QTextBlockFormat bf = d->caret.blockFormat(); if (!d->caret.blockFormat().hasProperty(KoParagraphStyle::HiddenByTable)) { newLine(); } bf = d->caret.blockFormat(); bf.setProperty(KoParagraphStyle::BreakBefore, KoText::PageBreak); d->caret.setBlockFormat(bf); } d->updateState(KoTextEditor::Private::NoOp); emit cursorPositionChanged(); } void KoTextEditor::paste(KoCanvasBase *canvas, const QMimeData *mimeData, bool pasteAsText) { if (isEditProtected()) { return; } KoShapeController *shapeController = KoTextDocument(d->document).shapeController(); addCommand(new TextPasteCommand(mimeData, d->document, shapeController, canvas, 0, pasteAsText)); } void KoTextEditor::deleteChar(bool previous, KUndo2Command *parent) { if (isEditProtected()) { return; } KoShapeController *shapeController = KoTextDocument(d->document).shapeController(); // Find out if we should track changes or not // KoChangeTracker *changeTracker = KoTextDocument(d->document).changeTracker(); // bool trackChanges = false; // if (changeTracker && changeTracker->recordChanges()) { // trackChanges = true; // } if (previous) { if (!d->caret.hasSelection() && d->caret.block().blockFormat().hasProperty(KoParagraphStyle::HiddenByTable)) { movePosition(QTextCursor::PreviousCharacter); if (d->caret.block().length() <= 1) { movePosition(QTextCursor::NextCharacter); } else return; // it becomes just a cursor movement; } } else { if (!d->caret.hasSelection() && d->caret.block().length() > 1) { QTextCursor tmpCursor = d->caret; tmpCursor.movePosition(QTextCursor::NextCharacter); if (tmpCursor.block().blockFormat().hasProperty(KoParagraphStyle::HiddenByTable)) { movePosition(QTextCursor::NextCharacter); return; // it becomes just a cursor movement; } } } if (previous) { addCommand(new DeleteCommand(DeleteCommand::PreviousChar, d->document, shapeController, parent)); } else { addCommand(new DeleteCommand(DeleteCommand::NextChar, d->document, shapeController, parent)); } } void KoTextEditor::toggleListNumbering(bool numberingEnabled) { if (isEditProtected()) { return; } addCommand(new ListItemNumberingCommand(block(), numberingEnabled)); emit textFormatChanged(); } void KoTextEditor::setListProperties(const KoListLevelProperties &llp, ChangeListFlags flags, KUndo2Command *parent) { if (isEditProtected()) { return; } if (flags & AutoListStyle && d->caret.block().textList() == 0) { flags = MergeWithAdjacentList; } if (KoList *list = KoTextDocument(d->document).list(d->caret.block().textList())) { KoListStyle *listStyle = list->style(); if (KoStyleManager *styleManager = KoTextDocument(d->document).styleManager()) { QList paragraphStyles = styleManager->paragraphStyles(); foreach (KoParagraphStyle *paragraphStyle, paragraphStyles) { if (paragraphStyle->listStyle() == listStyle || (paragraphStyle->list() && paragraphStyle->list()->style() == listStyle)) { flags = NoFlags; break; } } } } addCommand(new ChangeListCommand(d->caret, llp, flags, parent)); emit textFormatChanged(); } int KoTextEditor::anchor() const { return d->caret.anchor(); } bool KoTextEditor::atBlockEnd() const { return d->caret.atBlockEnd(); } bool KoTextEditor::atBlockStart() const { return d->caret.atBlockStart(); } bool KoTextEditor::atEnd() const { QTextCursor cursor(d->caret.document()->rootFrame()->lastCursorPosition()); cursor.movePosition(QTextCursor::PreviousCharacter); QTextFrame *auxFrame = cursor.currentFrame(); if (auxFrame->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) { //auxFrame really is the auxiliary frame if (d->caret.position() == auxFrame->firstPosition() - 1) { return true; } return false; } return d->caret.atEnd(); } bool KoTextEditor::atStart() const { return d->caret.atStart(); } QTextBlock KoTextEditor::block() const { return d->caret.block(); } int KoTextEditor::blockNumber() const { return d->caret.blockNumber(); } void KoTextEditor::clearSelection() { d->caret.clearSelection(); } int KoTextEditor::columnNumber() const { return d->caret.columnNumber(); } void KoTextEditor::deleteChar() { if (isEditProtected()) { return; } if (!d->caret.hasSelection()) { if (d->caret.atEnd()) return; // We alson need to refuse delete if we are at final pos in table cell if (QTextTable *table = d->caret.currentTable()) { QTextTableCell cell = table->cellAt(d->caret.position()); if (d->caret.position() == cell.lastCursorPosition().position()) { return; } } // We also need to refuse delete if it will delete a note frame QTextCursor after(d->caret); after.movePosition(QTextCursor::NextCharacter); QTextFrame *beforeFrame = d->caret.currentFrame(); while (qobject_cast(beforeFrame)) { beforeFrame = beforeFrame->parentFrame(); } QTextFrame *afterFrame = after.currentFrame(); while (qobject_cast(afterFrame)) { afterFrame = afterFrame->parentFrame(); } if (beforeFrame != afterFrame) { return; } } deleteChar(false); emit cursorPositionChanged(); } void KoTextEditor::deletePreviousChar() { if (isEditProtected()) { return; } if (!d->caret.hasSelection()) { if (d->caret.atStart()) return; // We also need to refuse delete if we are at first pos in table cell if (QTextTable *table = d->caret.currentTable()) { QTextTableCell cell = table->cellAt(d->caret.position()); if (d->caret.position() == cell.firstCursorPosition().position()) { return; } } // We also need to refuse delete if it will delete a note frame QTextCursor after(d->caret); after.movePosition(QTextCursor::PreviousCharacter); QTextFrame *beforeFrame = d->caret.currentFrame(); while (qobject_cast(beforeFrame)) { beforeFrame = beforeFrame->parentFrame(); } QTextFrame *afterFrame = after.currentFrame(); while (qobject_cast(afterFrame)) { afterFrame = afterFrame->parentFrame(); } if (beforeFrame != afterFrame) { return; } } deleteChar(true); emit cursorPositionChanged(); } QTextDocument *KoTextEditor::document() const { return d->caret.document(); } bool KoTextEditor::hasComplexSelection() const { return d->caret.hasComplexSelection(); } bool KoTextEditor::hasSelection() const { return d->caret.hasSelection(); } class ProtectionCheckVisitor : public KoTextVisitor { public: ProtectionCheckVisitor(const KoTextEditor *editor) : KoTextVisitor(const_cast(editor)) { } // override super's implementation to not waste cpu cycles void visitBlock(QTextBlock&, const QTextCursor &) override { } void nonVisit() override { setAbortVisiting(true); } }; bool KoTextEditor::isEditProtected(bool useCached) const { ProtectionCheckVisitor visitor(this); if (useCached) { if (! d->editProtectionCached) { recursivelyVisitSelection(d->document->rootFrame()->begin(), visitor); d->editProtected = visitor.abortVisiting(); d->editProtectionCached = true; } return d->editProtected; } d->editProtectionCached = false; recursivelyVisitSelection(d->document->rootFrame()->begin(), visitor); return visitor.abortVisiting(); } void KoTextEditor::insertTable(int rows, int columns) { if (isEditProtected() || rows <= 0 || columns <= 0) { return; } bool hasSelection = d->caret.hasSelection(); if (!hasSelection) { d->updateState(KoTextEditor::Private::Custom, kundo2_i18n("Insert Table")); } else { KUndo2Command *topCommand = beginEditBlock(kundo2_i18n("Insert Table")); deleteChar(false, topCommand); d->caret.beginEditBlock(); } QTextTableFormat tableFormat; tableFormat.setWidth(QTextLength(QTextLength::PercentageLength, 100)); tableFormat.setProperty(KoTableStyle::CollapsingBorders, true); tableFormat.setMargin(5); KoChangeTracker *changeTracker = KoTextDocument(d->document).changeTracker(); if (changeTracker && changeTracker->recordChanges()) { QTextCharFormat charFormat = d->caret.charFormat(); QTextBlockFormat blockFormat = d->caret.blockFormat(); KUndo2MagicString title = kundo2_i18n("Insert Table"); int changeId; if (!d->caret.atBlockStart()) { changeId = changeTracker->mergeableId(KoGenChange::InsertChange, title, charFormat.intProperty(KoCharacterStyle::ChangeTrackerId)); } else { changeId = changeTracker->mergeableId(KoGenChange::InsertChange, title, blockFormat.intProperty(KoCharacterStyle::ChangeTrackerId)); } if (!changeId) { changeId = changeTracker->getInsertChangeId(title, 0); } tableFormat.setProperty(KoCharacterStyle::ChangeTrackerId, changeId); } QTextBlock currentBlock = d->caret.block(); if (d->caret.position() != currentBlock.position()) { d->caret.insertBlock(); currentBlock = d->caret.block(); } QTextTable *table = d->caret.insertTable(rows, columns, tableFormat); // Get (and thus create) columnandrowstyle manager so it becomes part of undo // and not something that happens uncontrollably during layout KoTableColumnAndRowStyleManager::getManager(table); // 'Hide' the block before the table QTextBlockFormat blockFormat = currentBlock.blockFormat(); QTextCursor cursor(currentBlock); blockFormat.setProperty(KoParagraphStyle::HiddenByTable, true); cursor.setBlockFormat(blockFormat); // Define the initial cell format QTextTableCellFormat format; KoTableCellStyle cellStyle; cellStyle.setEdge(KoBorder::TopBorder, KoBorder::BorderSolid, 2, QColor(Qt::black)); cellStyle.setEdge(KoBorder::LeftBorder, KoBorder::BorderSolid, 2, QColor(Qt::black)); cellStyle.setEdge(KoBorder::BottomBorder, KoBorder::BorderSolid, 2, QColor(Qt::black)); cellStyle.setEdge(KoBorder::RightBorder, KoBorder::BorderSolid, 2, QColor(Qt::black)); cellStyle.setPadding(5); cellStyle.applyStyle(format); // Apply formatting to all cells for (int row = 0; row < table->rows(); ++row) { for (int col = 0; col < table->columns(); ++col) { QTextTableCell cell = table->cellAt(row, col); cell.setFormat(format); } } if (hasSelection) { d->caret.endEditBlock(); endEditBlock(); } else { d->updateState(KoTextEditor::Private::NoOp); } emit cursorPositionChanged(); } void KoTextEditor::insertTableRowAbove() { if (isEditProtected()) { return; } QTextTable *table = d->caret.currentTable(); if (table) { addCommand(new InsertTableRowCommand(this, table, false)); } } void KoTextEditor::insertTableRowBelow() { if (isEditProtected()) { return; } QTextTable *table = d->caret.currentTable(); if (table) { addCommand(new InsertTableRowCommand(this, table, true)); } } void KoTextEditor::insertTableColumnLeft() { if (isEditProtected()) { return; } QTextTable *table = d->caret.currentTable(); if (table) { addCommand(new InsertTableColumnCommand(this, table, false)); } } void KoTextEditor::insertTableColumnRight() { if (isEditProtected()) { return; } QTextTable *table = d->caret.currentTable(); if (table) { addCommand(new InsertTableColumnCommand(this, table, true)); } } void KoTextEditor::deleteTableColumn() { if (isEditProtected()) { return; } QTextTable *table = d->caret.currentTable(); if (table) { addCommand(new DeleteTableColumnCommand(this, table)); } } void KoTextEditor::deleteTableRow() { if (isEditProtected()) { return; } QTextTable *table = d->caret.currentTable(); if (table) { addCommand(new DeleteTableRowCommand(this, table)); } } void KoTextEditor::mergeTableCells() { if (isEditProtected()) { return; } d->updateState(KoTextEditor::Private::Custom, kundo2_i18n("Merge Cells")); QTextTable *table = d->caret.currentTable(); if (table) { table->mergeCells(d->caret); } d->updateState(KoTextEditor::Private::NoOp); } void KoTextEditor::splitTableCells() { if (isEditProtected()) { return; } d->updateState(KoTextEditor::Private::Custom, kundo2_i18n("Split Cells")); QTextTable *table = d->caret.currentTable(); if (table) { QTextTableCell cell = table->cellAt(d->caret); table->splitCell(cell.row(), cell.column(), 1, 1); } d->updateState(KoTextEditor::Private::NoOp); } void KoTextEditor::adjustTableColumnWidth(QTextTable *table, int column, qreal width, KUndo2Command *parentCommand) { ResizeTableCommand *cmd = new ResizeTableCommand(table, true, column, width, parentCommand); addCommand(cmd); } void KoTextEditor::adjustTableRowHeight(QTextTable *table, int column, qreal height, KUndo2Command *parentCommand) { ResizeTableCommand *cmd = new ResizeTableCommand(table, false, column, height, parentCommand); addCommand(cmd); } void KoTextEditor::adjustTableWidth(QTextTable *table, qreal dLeft, qreal dRight) { d->updateState(KoTextEditor::Private::Custom, kundo2_i18n("Adjust Table Width")); d->caret.beginEditBlock(); QTextTableFormat fmt = table->format(); if (dLeft) { fmt.setLeftMargin(fmt.leftMargin() + dLeft); } if (dRight) { fmt.setRightMargin(fmt.rightMargin() + dRight); } table->setFormat(fmt); d->caret.endEditBlock(); d->updateState(KoTextEditor::Private::NoOp); } void KoTextEditor::setTableBorderData(QTextTable *table, int row, int column, KoBorder::BorderSide cellSide, const KoBorder::BorderData &data) { d->updateState(KoTextEditor::Private::Custom, kundo2_i18n("Change Border Formatting")); d->caret.beginEditBlock(); QTextTableCell cell = table->cellAt(row, column); QTextCharFormat fmt = cell.format(); KoBorder border = fmt.property(KoTableCellStyle::Borders).value(); border.setBorderData(cellSide, data); fmt.setProperty(KoTableCellStyle::Borders, QVariant::fromValue(border)); cell.setFormat(fmt); d->caret.endEditBlock(); d->updateState(KoTextEditor::Private::NoOp); } KoInlineNote *KoTextEditor::insertFootNote() { if (isEditProtected()) { return 0; } InsertNoteCommand *cmd = new InsertNoteCommand(KoInlineNote::Footnote, d->document); addCommand(cmd); emit cursorPositionChanged(); return cmd->m_inlineNote; } KoInlineNote *KoTextEditor::insertEndNote() { if (isEditProtected()) { return 0; } InsertNoteCommand *cmd = new InsertNoteCommand(KoInlineNote::Endnote, d->document); addCommand(cmd); emit cursorPositionChanged(); return cmd->m_inlineNote; } void KoTextEditor::insertTableOfContents(KoTableOfContentsGeneratorInfo *info) { if (isEditProtected()) { return; } bool hasSelection = d->caret.hasSelection(); if (!hasSelection) { d->updateState(KoTextEditor::Private::Custom, kundo2_i18n("Insert Table Of Contents")); } else { KUndo2Command *topCommand = beginEditBlock(kundo2_i18n("Insert Table Of Contents")); deleteChar(false, topCommand); d->caret.beginEditBlock(); } QTextBlockFormat tocFormat; KoTableOfContentsGeneratorInfo *newToCInfo = info->clone(); QTextDocument *tocDocument = new QTextDocument(); tocFormat.setProperty(KoParagraphStyle::TableOfContentsData, QVariant::fromValue(newToCInfo) ); tocFormat.setProperty(KoParagraphStyle::GeneratedDocument, QVariant::fromValue(tocDocument)); //make sure we set up the textrangemanager on the subdocument as well KoTextDocument(tocDocument).setTextRangeManager(new KoTextRangeManager); KoChangeTracker *changeTracker = KoTextDocument(d->document).changeTracker(); if (changeTracker && changeTracker->recordChanges()) { QTextCharFormat charFormat = d->caret.charFormat(); QTextBlockFormat blockFormat = d->caret.blockFormat(); KUndo2MagicString title = kundo2_i18n("Insert Table Of Contents"); int changeId; if (!d->caret.atBlockStart()) { changeId = changeTracker->mergeableId(KoGenChange::InsertChange, title, charFormat.intProperty(KoCharacterStyle::ChangeTrackerId)); } else { changeId = changeTracker->mergeableId(KoGenChange::InsertChange, title, blockFormat.intProperty(KoCharacterStyle::ChangeTrackerId)); } if (!changeId) { changeId = changeTracker->getInsertChangeId(title, 0); } tocFormat.setProperty(KoCharacterStyle::ChangeTrackerId, changeId); } d->caret.insertBlock(); d->caret.movePosition(QTextCursor::Left); d->caret.insertBlock(tocFormat); d->caret.movePosition(QTextCursor::Right); if (hasSelection) { d->caret.endEditBlock(); endEditBlock(); } else { d->updateState(KoTextEditor::Private::NoOp); } emit cursorPositionChanged(); } void KoTextEditor::setTableOfContentsConfig(KoTableOfContentsGeneratorInfo *info, const QTextBlock &block) { if (isEditProtected()) { return; } KoTableOfContentsGeneratorInfo *newToCInfo=info->clone(); d->updateState(KoTextEditor::Private::Custom, kundo2_i18n("Modify Table Of Contents")); QTextCursor cursor(block); QTextBlockFormat tocBlockFormat=block.blockFormat(); tocBlockFormat.setProperty(KoParagraphStyle::TableOfContentsData, QVariant::fromValue(newToCInfo) ); cursor.setBlockFormat(tocBlockFormat); d->updateState(KoTextEditor::Private::NoOp); emit cursorPositionChanged(); const_cast(document())->markContentsDirty(document()->firstBlock().position(), 0); } void KoTextEditor::insertBibliography(KoBibliographyInfo *info) { bool hasSelection = d->caret.hasSelection(); if (!hasSelection) { d->updateState(KoTextEditor::Private::Custom, kundo2_i18n("Insert Bibliography")); } else { KUndo2Command *topCommand = beginEditBlock(kundo2_i18n("Insert Bibliography")); deleteChar(false, topCommand); d->caret.beginEditBlock(); } QTextBlockFormat bibFormat; KoBibliographyInfo *newBibInfo = info->clone(); QTextDocument *bibDocument = new QTextDocument(); bibFormat.setProperty( KoParagraphStyle::BibliographyData, QVariant::fromValue(newBibInfo)); bibFormat.setProperty( KoParagraphStyle::GeneratedDocument, QVariant::fromValue(bibDocument)); //make sure we set up the textrangemanager on the subdocument as well KoTextDocument(bibDocument).setTextRangeManager(new KoTextRangeManager); KoChangeTracker *changeTracker = KoTextDocument(d->document).changeTracker(); if (changeTracker && changeTracker->recordChanges()) { QTextCharFormat charFormat = d->caret.charFormat(); QTextBlockFormat blockFormat = d->caret.blockFormat(); KUndo2MagicString title = kundo2_i18n("Insert Bibliography"); int changeId; if (!d->caret.atBlockStart()) { changeId = changeTracker->mergeableId(KoGenChange::InsertChange, title, charFormat.intProperty(KoCharacterStyle::ChangeTrackerId)); } else { changeId = changeTracker->mergeableId(KoGenChange::InsertChange, title, blockFormat.intProperty(KoCharacterStyle::ChangeTrackerId)); } if (!changeId) { changeId = changeTracker->getInsertChangeId(title, 0); } bibFormat.setProperty(KoCharacterStyle::ChangeTrackerId, changeId); } d->caret.insertBlock(); d->caret.movePosition(QTextCursor::Left); d->caret.insertBlock(bibFormat); d->caret.movePosition(QTextCursor::Right); - new BibliographyGenerator(bibDocument, block(), newBibInfo); - if (hasSelection) { d->caret.endEditBlock(); endEditBlock(); } else { d->updateState(KoTextEditor::Private::NoOp); } emit cursorPositionChanged(); } KoInlineCite *KoTextEditor::insertCitation() { bool hasSelection = d->caret.hasSelection(); if (!hasSelection) { d->updateState(KoTextEditor::Private::KeyPress, kundo2_i18n("Add Citation")); } else { KUndo2Command *topCommand = beginEditBlock(kundo2_i18n("Add Citation")); deleteChar(false, topCommand); d->caret.beginEditBlock(); } KoInlineCite *cite = new KoInlineCite(KoInlineCite::Citation); KoInlineTextObjectManager *manager = KoTextDocument(d->document).inlineTextObjectManager(); manager->insertInlineObject(d->caret,cite); if (hasSelection) { d->caret.endEditBlock(); endEditBlock(); } else { d->updateState(KoTextEditor::Private::NoOp); } return cite; } void KoTextEditor::insertText(const QString &text, const QString &hRef) { if (isEditProtected()) { return; } bool hasSelection = d->caret.hasSelection(); if (!hasSelection) { d->updateState(KoTextEditor::Private::KeyPress, kundo2_i18n("Typing")); } else { KUndo2Command *topCommand = beginEditBlock(kundo2_i18n("Typing")); deleteChar(false, topCommand); d->caret.beginEditBlock(); } //first we make sure that we clear the inlineObject charProperty, if we have no selection if (!hasSelection && d->caret.charFormat().hasProperty(KoCharacterStyle::InlineInstanceId)) d->clearCharFormatProperty(KoCharacterStyle::InlineInstanceId); int startPosition = d->caret.position(); if (d->caret.blockFormat().hasProperty(KoParagraphStyle::HiddenByTable)) { d->newLine(0); startPosition = d->caret.position(); } QTextCharFormat format = d->caret.charFormat(); if (format.hasProperty(KoCharacterStyle::ChangeTrackerId)) { format.clearProperty(KoCharacterStyle::ChangeTrackerId); } static QRegExp urlScanner("\\S+://\\S+"); if (!hRef.isEmpty()) { format.setAnchor(true); format.setProperty(KoCharacterStyle::AnchorType, KoCharacterStyle::Anchor); if ((urlScanner.indexIn(hRef)) == 0) {//web url format.setAnchorHref(hRef); } else { format.setAnchorHref("#"+hRef); } } d->caret.insertText(text, format); int endPosition = d->caret.position(); //Mark the inserted text d->caret.setPosition(startPosition); d->caret.setPosition(endPosition, QTextCursor::KeepAnchor); registerTrackedChange(d->caret, KoGenChange::InsertChange, kundo2_i18n("Typing"), format, format, false); d->caret.clearSelection(); if (hasSelection) { d->caret.endEditBlock(); endEditBlock(); } if (!hRef.isEmpty()) { format.setAnchor(false); format.clearProperty(KoCharacterStyle::Anchor); format.clearProperty(KoCharacterStyle::AnchorType); d->caret.setCharFormat(format); } emit cursorPositionChanged(); } void KoTextEditor::insertHtml(const QString &html) { if (isEditProtected()) { return; } // XXX: do the changetracking and everything! QTextBlock currentBlock = d->caret.block(); d->caret.insertHtml(html); QList pastedLists; KoList *currentPastedList = 0; while (currentBlock != d->caret.block()) { currentBlock = currentBlock.next(); QTextList *currentTextList = currentBlock.textList(); if(currentTextList && !pastedLists.contains(currentBlock.textList())) { KoListStyle *listStyle = KoTextDocument(d->document).styleManager()->defaultListStyle()->clone(); listStyle->setName(QString()); listStyle->setStyleId(0); currentPastedList = new KoList(d->document, listStyle); QTextListFormat currentTextListFormat = currentTextList->format(); KoListLevelProperties levelProperty = listStyle->levelProperties(currentTextListFormat.indent()); levelProperty.setStyle(static_cast(currentTextListFormat.style())); levelProperty.setLevel(currentTextListFormat.indent()); levelProperty.setListItemPrefix(QString()); levelProperty.setListItemSuffix(QString()); levelProperty.setListId((KoListStyle::ListIdType)currentTextList); listStyle->setLevelProperties(levelProperty); currentTextListFormat.setProperty(KoListStyle::Level, currentTextListFormat.indent()); currentBlock.textList()->setFormat(currentTextListFormat); currentPastedList->updateStoredList(currentBlock); currentPastedList->setStyle(listStyle); pastedLists.append(currentBlock.textList()); } } } bool KoTextEditor::movePosition(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode, int n) { d->editProtectionCached = false; // We need protection against moving in and out of note areas QTextCursor after(d->caret); bool b = after.movePosition (operation, mode, n); QTextFrame *beforeFrame = d->caret.currentFrame(); while (qobject_cast(beforeFrame)) { beforeFrame = beforeFrame->parentFrame(); } QTextFrame *afterFrame = after.currentFrame(); while (qobject_cast(afterFrame)) { afterFrame = afterFrame->parentFrame(); } if (beforeFrame == afterFrame) { if (after.selectionEnd() == after.document()->characterCount() -1) { QTextCursor cursor(d->caret.document()->rootFrame()->lastCursorPosition()); cursor.movePosition(QTextCursor::PreviousCharacter); QTextFrame *auxFrame = cursor.currentFrame(); if (auxFrame->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) { if (operation == QTextCursor::End) { d->caret.setPosition(auxFrame->firstPosition() - 1, mode); emit cursorPositionChanged(); return true; } return false; } } d->caret = after; emit cursorPositionChanged(); return b; } return false; } void KoTextEditor::newSection() { if (isEditProtected()) { return; } NewSectionCommand *cmd = new NewSectionCommand(d->document); addCommand(cmd); emit cursorPositionChanged(); } void KoTextEditor::splitSectionsStartings(int sectionIdToInsertBefore) { if (isEditProtected()) { return; } addCommand(new SplitSectionsCommand( d->document, SplitSectionsCommand::Startings, sectionIdToInsertBefore)); emit cursorPositionChanged(); } void KoTextEditor::splitSectionsEndings(int sectionIdToInsertAfter) { if (isEditProtected()) { return; } addCommand(new SplitSectionsCommand( d->document, SplitSectionsCommand::Endings, sectionIdToInsertAfter)); emit cursorPositionChanged(); } void KoTextEditor::renameSection(KoSection* section, const QString &newName) { if (isEditProtected()) { return; } addCommand(new RenameSectionCommand(section, newName, document())); } void KoTextEditor::newLine() { if (isEditProtected()) { return; } bool hasSelection = d->caret.hasSelection(); if (!hasSelection) { d->updateState(KoTextEditor::Private::Custom, kundo2_i18n("New Paragraph")); } else { KUndo2Command *topCommand = beginEditBlock(kundo2_i18n("New Paragraph")); deleteChar(false, topCommand); } d->caret.beginEditBlock(); d->newLine(0); d->caret.endEditBlock(); if (hasSelection) { endEditBlock(); } else { d->updateState(KoTextEditor::Private::NoOp); } emit cursorPositionChanged(); } class WithinSelectionVisitor : public KoTextVisitor { public: WithinSelectionVisitor(KoTextEditor *editor, int position) : KoTextVisitor(editor) , m_position(position) , m_returnValue(false) { } void visitBlock(QTextBlock &block, const QTextCursor &caret) override { if (m_position >= qMax(block.position(), caret.selectionStart()) && m_position <= qMin(block.position() + block.length(), caret.selectionEnd())) { m_returnValue = true; setAbortVisiting(true); } } int m_position; //the position we are searching for bool m_returnValue; //if position is within the selection }; bool KoTextEditor::isWithinSelection(int position) const { // we know the visitor doesn't do anything with the texteditor so let's const cast // to have a more beautiful outer api WithinSelectionVisitor visitor(const_cast(this), position); recursivelyVisitSelection(d->document->rootFrame()->begin(), visitor); return visitor.m_returnValue; } int KoTextEditor::position() const { return d->caret.position(); } void KoTextEditor::select(QTextCursor::SelectionType selection) { //TODO add selection of previous/next char, and option about hasSelection d->caret.select(selection); } QString KoTextEditor::selectedText() const { return d->caret.selectedText(); } QTextDocumentFragment KoTextEditor::selection() const { return d->caret.selection(); } int KoTextEditor::selectionEnd() const { return d->caret.selectionEnd(); } int KoTextEditor::selectionStart() const { return d->caret.selectionStart(); } void KoTextEditor::setPosition(int pos, QTextCursor::MoveMode mode) { d->editProtectionCached = false; if (pos == d->caret.document()->characterCount() -1) { QTextCursor cursor(d->caret.document()->rootFrame()->lastCursorPosition()); cursor.movePosition(QTextCursor::PreviousCharacter); QTextFrame *auxFrame = cursor.currentFrame(); if (auxFrame->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) { return; } } if (mode == QTextCursor::MoveAnchor) { d->caret.setPosition (pos, mode); emit cursorPositionChanged(); } // We need protection against moving in and out of note areas QTextCursor after(d->caret); after.setPosition (pos, mode); QTextFrame *beforeFrame = d->caret.currentFrame(); while (qobject_cast(beforeFrame)) { beforeFrame = beforeFrame->parentFrame(); } QTextFrame *afterFrame = after.currentFrame(); while (qobject_cast(afterFrame)) { afterFrame = afterFrame->parentFrame(); } if (beforeFrame == afterFrame) { d->caret = after; emit cursorPositionChanged(); } } void KoTextEditor::setVisualNavigation(bool b) { d->caret.setVisualNavigation (b); } bool KoTextEditor::visualNavigation() const { return d->caret.visualNavigation(); } const QTextFrame *KoTextEditor::currentFrame () const { return d->caret.currentFrame(); } const QTextList *KoTextEditor::currentList () const { return d->caret.currentList(); } const QTextTable *KoTextEditor::currentTable () const { return d->caret.currentTable(); } //have to include this because of Q_PRIVATE_SLOT #include "moc_KoTextEditor.cpp"