diff --git a/libs/text/styles/KoParagraphStyle.h b/libs/text/styles/KoParagraphStyle.h index 28752735930..e6a2bddfa73 100644 --- a/libs/text/styles/KoParagraphStyle.h +++ b/libs/text/styles/KoParagraphStyle.h @@ -1,745 +1,744 @@ /* This file is part of the KDE project * Copyright (C) 2006-2009 Thomas Zander * Copyright (C) 2007,2008 Sebastian Sauer * Copyright (C) 2007-2011 Pierre Ducroquet * Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2008 Girish Ramakrishnan * * 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 KOPARAGRAPHSTYLE_H #define KOPARAGRAPHSTYLE_H #include "KoCharacterStyle.h" #include "KoText.h" #include "kotext_export.h" #include #include #include #include extern QVariant val; class KoShadowStyle; class KoListStyle; class QTextBlock; class KoGenStyle; class KoShapeLoadingContext; class KoShapeSavingContext; class KoList; /** * A container for all properties for the paragraph wide style. * Each paragraph in the main text either is based on a parag style, or its not. Where * it is based on a paragraph style this is indicated that it has a property 'StyleId' * with an integer as value. The integer value corresponds to the styleId() output of * a specific KoParagraphStyle. * @see KoStyleManager */ class KOTEXT_EXPORT KoParagraphStyle : public KoCharacterStyle { Q_OBJECT public: enum Property { // Every 10 properties, the decimal number shown indicates the decimal offset over the QTextFormat::UserProperty enum value StyleId = QTextFormat::UserProperty + 1, // Linespacing properties PercentLineHeight, ///< this property is used for a percentage of the highest character on that line FixedLineHeight, ///< this property is used to use a non-default line height MinimumLineHeight, ///< this property is used to have a minimum line spacing LineSpacing, ///< Hard leader height. LineSpacingFromFont, ///< if false, use fontsize (in pt) solely, otherwise respect font settings AlignLastLine, ///< When the paragraph is justified, what to do with the last word line WidowThreshold, ///< If 'keep together'=false, amount of lines to keep it anyway. OrphanThreshold, ///< If 'keep together'=false, amount of lines to keep it anyway. DropCaps, /*10*/ ///< defines if a paragraph renders its first char(s) with drop-caps DropCapsLength, ///< Number of glyphs to show as drop-caps DropCapsLines, ///< Number of lines that the drop-caps span DropCapsDistance, ///< Distance between drop caps and text DropCapsTextStyle, ///< Text style of dropped chars. FollowDocBaseline, ///< If true the baselines will be aligned with the doc-wide grid // border stuff HasLeftBorder, ///< If true, paint a border on the left HasTopBorder, ///< If true, paint a border on the top HasRightBorder, ///< If true, paint a border on the right HasBottomBorder,///< If true, paint a border on the bottom BorderLineWidth, /*20*/ ///< Thickness of inner-border SecondBorderLineWidth, ///< Thickness of outer-border DistanceToSecondBorder, ///< Distance between inner and outer border LeftPadding, ///< distance between text and border TopPadding, ///< distance between text and border RightPadding, ///< distance between text and border BottomPadding, ///< distance between text and border LeftBorderWidth, ///< The thickness of the border, or 0 if there is no border LeftInnerBorderWidth, ///< In case of style being 'double' the thickness of the inner border line LeftBorderSpacing, ///< In case of style being 'double' the space between the inner and outer border lines LeftBorderStyle, /*30*/ ///< The border style. (see BorderStyle) LeftBorderColor, ///< The border Color TopBorderWidth, ///< The thickness of the border, or 0 if there is no border TopInnerBorderWidth, ///< In case of style being 'double' the thickness of the inner border line TopBorderSpacing, ///< In case of style being 'double' the space between the inner and outer border lines TopBorderStyle, ///< The border style. (see BorderStyle) TopBorderColor, ///< The border Color RightBorderWidth, ///< The thickness of the border, or 0 if there is no border RightInnerBorderWidth, ///< In case of style being 'double' the thickness of the inner border line RightBorderSpacing, ///< In case of style being 'double' the space between the inner and outer border lines RightBorderStyle, /*40*/ ///< The border style. (see BorderStyle) RightBorderColor, ///< The border Color BottomBorderWidth, ///< The thickness of the border, or 0 if there is no border BottomInnerBorderWidth, ///< In case of style being 'double' the thickness of the inner border line BottomBorderSpacing, ///< In case of style being 'double' the space between the inner and outer border lines BottomBorderStyle, ///< The border style. (see BorderStyle) BottomBorderColor, ///< The border Color // lists ListStyleId, ///< Style Id of associated list style ListStartValue, ///< Int with the list-value that that parag will have. Ignored if this is not a list. RestartListNumbering, ///< boolean to indicate that this paragraph will have numbering restart at the list-start. Ignored if this is not a list. ListLevel, /*50*/ ///< int with the list-level that the paragraph will get when this is a list (numbered paragraphs) IsListHeader, ///< bool, if true the paragraph shows up as a list item, but w/o a list label. UnnumberedListItem, ///< bool. if true this paragraph is part of a list but is not numbered AutoTextIndent, ///< bool, says whether the paragraph is auto-indented or not TabStopDistance, ///< Double, Length. specifies that there's a tab stop every n inches ///< (after the last of the TabPositions, if any) TabPositions, ///< A list of tab positions TextProgressionDirection, MasterPageName, ///< Optional name of the master-page OutlineLevel, ///< Outline level for headings DefaultOutlineLevel, // numbering LineNumbering, /*60*/ ///< bool, specifies whether lines should be numbered in this paragraph LineNumberStartValue, ///< integer value that specifies the number for the first line in the paragraph SectionStartings, ///< list of section definitions SectionEndings, ///< list // do 15.5.24 // continue at 15.5.28 ForceDisablingList, ///< bool, for compatibility with the weird text:enable-numbering attribute not used anymore by OpenOffice.org // other properties BackgroundTransparency, ///< qreal between 0 and 1, background transparency SnapToLayoutGrid, ///< bool, snap the paragraph to the layout grid of the page JoinBorder, ///< bool, whether a border for one paragraph is to be extended around the following paragraph RegisterTrue, ///< bool, align lines on both sides of a printed text StrictLineBreak, ///< bool, if true, line breaks are forbidden between some characters JustifySingleWord, ///< bool, if true, a single word will be justified BreakBefore, ///< KoText::TextBreakProperty, whether there is a page/column break before the paragraphs BreakAfter, ///< KoText::TextBreakProperty, whether there is a page/column break after the paragraphs AutomaticWritingMode, ///< bool PageNumber, ///< int, 0 means auto (ie. previous page number + 1), N sets up a new page number TextAutoSpace, ///< AutoSpace, indicating whether to add space between portions of Asian, Western and complex texts KeepWithNext, ///< Try to keep this block with its following block on the same page KeepHyphenation, ///< bool, whether both parts of a hyphenated word shall lie within a single page HyphenationLadderCount, ///< int, 0 means no limit, else limit the number of successive hyphenated line areas in a block PunctuationWrap, ///< bool, whether a punctuation mark can be at the end of a full line (false) or not (true) VerticalAlignment, ///< KoParagraphStyle::VerticalAlign, the alignment of this paragraph text HiddenByTable, ///< don't let this paragraph have any height NormalLineHeight, ///< bool, internal property for reserved usage BibliographyData, TableOfContentsData, // set when block is instead a TableOfContents GeneratedDocument, // set when block is instead a generated document Shadow, //< KoShadowStyle, the shadow of this paragraph NextStyle, ///< holds the styleId of the style to be used on a new paragraph ParagraphListStyleId, ///< this holds the listStyleId of the list got from style:list-style-name property from ODF 1.2 EndCharStyle // QSharedPointer used when final line is empty }; enum AutoSpace { NoAutoSpace, ///< space should not be added between portions of Asian, Western and complex texts IdeographAlpha ///< space should be added between portions of Asian, Western and complex texts }; enum VerticalAlign { VAlignAuto, VAlignBaseline, VAlignBottom, VAlignMiddle, VAlignTop }; /// Constructor KoParagraphStyle(QObject *parent = 0); /// Creates a KoParagrahStyle with the given block format, the block character format and \a parent KoParagraphStyle(const QTextBlockFormat &blockFormat, const QTextCharFormat &blockCharFormat, QObject *parent = 0); /// Destructor ~KoParagraphStyle() override; KoCharacterStyle::Type styleType() const override; /// Creates a KoParagraphStyle that represents the formatting of \a block. static KoParagraphStyle *fromBlock(const QTextBlock &block, QObject *parent = 0); /// creates a clone of this style with the specified parent KoParagraphStyle *clone(QObject *parent = 0) const; // ***** Linespacing /** * Sets the line height as a percentage of the highest character on that line. * A good typographically correct value would be 120% * Note that lineSpacing() is added to this. * You should consider doing a remove(KoParagraphStyle::LineSpacing); because if set, it will * be used instead of this value. * @see setLineSpacingFromFont */ void setLineHeightPercent(qreal lineHeight); /// @see setLineHeightPercent qreal lineHeightPercent() const; /** * Sets the line height to a specific pt-based height, ignoring the font size. * Setting this will ignore the lineHeightPercent() and lineSpacing() values. */ void setLineHeightAbsolute(qreal height); /// @see setLineHeightAbsolute qreal lineHeightAbsolute() const; /** * Sets the line height to have a minimum height in pt. * You should consider doing a remove(KoParagraphStyle::FixedLineHeight); because if set, it will * be used instead of this value. */ void setMinimumLineHeight(const QTextLength &height); /// @see setMinimumLineHeight qreal minimumLineHeight() const; /** * Sets the space between two lines to be a specific height. The total linespacing will become * the line height + this height. Where the line height is dependent on the font. * You should consider doing a remove(KoParagraphStyle::FixedLineHeight) and a * remove(KoParagraphStyle::PercentLineHeight); because if set, they will be used instead of this value. */ void setLineSpacing(qreal spacing); /// @see setLineSpacing qreal lineSpacing() const; /** * Set the line-height to "normal". This overwrites a line-height set before either * with \a setLineHeightAbsolute or \a setMinimumLineHeight . If set then a value * set with \a setLineSpacing will be ignored. */ void setNormalLineHeight(); /// @see setNormalLineHeight bool hasNormalLineHeight() const; /** * If set to true the font-encoded height will be used instead of the font-size property * This property influences setLineHeightPercent() behavior. * When off (default) a font of 12pt will always have a linespacing of 12pt times the * current linespacing percentage. When on the linespacing embedded in the font * is used which can differ for various fonts, even if they are the same pt-size. */ void setLineSpacingFromFont(bool on); /** * @see setLineSpacingFromFont */ bool lineSpacingFromFont() const; /** * For paragraphs that are justified the last line alignment is specified here. * There are only 3 valid options, Left, Center and Justified. (where Left will * be right aligned for RTL text). */ void setAlignLastLine(Qt::Alignment alignment); /** * @see setAlignLastLine */ Qt::Alignment alignLastLine() const; /** * Paragraphs that are broken across two frames are normally broken at the bottom * of the frame. Using this property we can set the minimum number of lines that should * appear in the second frame to avoid really short paragraphs standing alone (also called * widows). So, if a 10 line parag is broken in a way that only one line is in the second * frame, setting a widowThreshold of 4 will break at 6 lines instead to leave the * requested 4 lines. */ void setWidowThreshold(int lines); /** * @see setWidowThreshold */ int widowThreshold() const; /** * Paragraphs that are broken across two frames are normally broken at the bottom * of the frame. Using this property we can set the minimum number of lines that should * appear in the first frame to avoid really short paragraphs standing alone (also called * orphans). So, if a paragraph is broken so only 2 line is left in the first frame * setting the orphanThreshold to something greater than 2 will move the whole paragraph * to the second frame. */ void setOrphanThreshold(int lines); /** * @see setOrphanThreshold */ int orphanThreshold() const; /** * If true, make the first character span multiple lines. * @see setDropCapsLength * @see setDropCapsLines * @see dropCapsDistance */ void setDropCaps(bool on); /** * @see setDropCaps */ bool dropCaps() const; /** * Set the number of glyphs to show as drop-caps * @see setDropCaps * @see setDropCapsLines * @see dropCapsDistance */ void setDropCapsLength(int characters); /** * set dropCaps Length in characters * @see setDropCapsLength */ int dropCapsLength() const; /** * Set the number of lines that the drop-caps span * @see setDropCapsLength * @see setDropCaps * @see dropCapsDistance */ void setDropCapsLines(int lines); /** * The dropCapsLines * @see setDropCapsLines */ int dropCapsLines() const; /** * set the distance between drop caps and text in pt * @see setDropCapsLength * @see setDropCaps * @see setDropCapsLines */ void setDropCapsDistance(qreal distance); /** * The dropCaps distance * @see setDropCapsDistance */ qreal dropCapsDistance() const; /** * Set the style id of the text style used for dropcaps * @see setDropCapsDistance */ void setDropCapsTextStyleId(int id); /** * The style id of the text style used for dropcaps * @see setDropCapsTextStyleId */ int dropCapsTextStyleId() const; /** * If true the baselines will be aligned with the doc-wide grid */ void setFollowDocBaseline(bool on); /** * return if baseline alignment is used * @see setFollowDocBaseline */ bool followDocBaseline() const; /// See similar named method on QTextBlockFormat void setBackground(const QBrush &brush); /// See similar named method on QTextBlockFormat QBrush background() const; /// See similar named method on QTextBlockFormat void clearBackground(); qreal backgroundTransparency() const; void setBackgroundTransparency(qreal transparency); bool snapToLayoutGrid() const; void setSnapToLayoutGrid(bool value); bool registerTrue() const; void setRegisterTrue(bool value); bool strictLineBreak() const; void setStrictLineBreak(bool value); bool justifySingleWord() const; void setJustifySingleWord(bool value); bool automaticWritingMode() const; void setAutomaticWritingMode(bool value); void setPageNumber(int pageNumber); int pageNumber() const; void setKeepWithNext(bool value); bool keepWithNext() const; void setPunctuationWrap(bool value); bool punctuationWrap() const; void setTextAutoSpace(AutoSpace value); AutoSpace textAutoSpace() const; void setKeepHyphenation(bool value); bool keepHyphenation() const; void setHyphenationLadderCount(int value); int hyphenationLadderCount() const; VerticalAlign verticalAlignment() const; void setVerticalAlignment(VerticalAlign value); void setBreakBefore(KoText::KoTextBreakProperty value); KoText::KoTextBreakProperty breakBefore() const; void setBreakAfter(KoText::KoTextBreakProperty value); KoText::KoTextBreakProperty breakAfter() const; void setLeftPadding(qreal padding); qreal leftPadding() const; void setTopPadding(qreal padding); qreal topPadding() const; void setRightPadding(qreal padding); qreal rightPadding() const; void setBottomPadding(qreal padding); qreal bottomPadding() const; void setPadding(qreal padding); void setLeftBorderWidth(qreal width); qreal leftBorderWidth() const; void setLeftInnerBorderWidth(qreal width); qreal leftInnerBorderWidth() const; void setLeftBorderSpacing(qreal width); qreal leftBorderSpacing() const; void setLeftBorderStyle(KoBorder::BorderStyle style); KoBorder::BorderStyle leftBorderStyle() const; void setLeftBorderColor(const QColor &color); QColor leftBorderColor() const; void setTopBorderWidth(qreal width); qreal topBorderWidth() const; void setTopInnerBorderWidth(qreal width); qreal topInnerBorderWidth() const; void setTopBorderSpacing(qreal width); qreal topBorderSpacing() const; void setTopBorderStyle(KoBorder::BorderStyle style); KoBorder::BorderStyle topBorderStyle() const; void setTopBorderColor(const QColor &color); QColor topBorderColor() const; void setRightBorderWidth(qreal width); qreal rightBorderWidth() const; void setRightInnerBorderWidth(qreal width); qreal rightInnerBorderWidth() const; void setRightBorderSpacing(qreal width); qreal rightBorderSpacing() const; void setRightBorderStyle(KoBorder::BorderStyle style); KoBorder::BorderStyle rightBorderStyle() const; void setRightBorderColor(const QColor &color); QColor rightBorderColor() const; void setBottomBorderWidth(qreal width); qreal bottomBorderWidth() const; void setBottomInnerBorderWidth(qreal width); qreal bottomInnerBorderWidth() const; void setBottomBorderSpacing(qreal width); qreal bottomBorderSpacing() const; void setBottomBorderStyle(KoBorder::BorderStyle style); KoBorder::BorderStyle bottomBorderStyle() const; void setBottomBorderColor(const QColor &color); QColor bottomBorderColor() const; bool joinBorder() const; void setJoinBorder(bool value); KoText::Direction textProgressionDirection() const; void setTextProgressionDirection(KoText::Direction dir); // ************ properties from QTextBlockFormat /// duplicated property from QTextBlockFormat void setTopMargin(QTextLength topMargin); /// duplicated property from QTextBlockFormat qreal topMargin() const; /// duplicated property from QTextBlockFormat void setBottomMargin(QTextLength margin); /// duplicated property from QTextBlockFormat qreal bottomMargin() const; /// duplicated property from QTextBlockFormat void setLeftMargin(QTextLength margin); /// duplicated property from QTextBlockFormat qreal leftMargin() const; /// duplicated property from QTextBlockFormat void setRightMargin(QTextLength margin); /// duplicated property from QTextBlockFormat qreal rightMargin() const; /// set the margin around the paragraph, making the margin on all sides equal. void setMargin(QTextLength margin); void setIsListHeader(bool on); bool isListHeader() const; /// duplicated property from QTextBlockFormat void setAlignment(Qt::Alignment alignment); /// duplicated property from QTextBlockFormat Qt::Alignment alignment() const; /// duplicated property from QTextBlockFormat void setTextIndent(QTextLength margin); /// duplicated property from QTextBlockFormat qreal textIndent() const; /// Custom KoParagraphStyle property for auto-text-indent void setAutoTextIndent(bool on); bool autoTextIndent() const; /// duplicated property from QTextBlockFormat void setNonBreakableLines(bool on); /// duplicated property from QTextBlockFormat bool nonBreakableLines() const; /// set the default style this one inherits its unset properties from if no parent style. void setDefaultStyle(KoParagraphStyle *parent); /// set the parent style this one inherits its unset properties from. void setParentStyle(KoParagraphStyle *parent); /// return the parent style KoParagraphStyle *parentStyle() const; /// the 'next' style is the one used when the user creates a new paragraph after this one. void setNextStyle(int next); /// the 'next' style is the one used when the user creates a new paragraph after this one. int nextStyle() const; /// return the name of the style. QString name() const; /// set a user-visible name on the style. void setName(const QString &name); /// each style has a unique ID (non persistent) given out by the styleManager int styleId() const; /// each style has a unique ID (non persistent) given out by the styleManager void setStyleId(int id); /// return the optional name of the master-page or a QString() if this paragraph isn't attached to a master-page. QString masterPageName() const; /// Set the name of the master-page. void setMasterPageName(const QString &name); /// Set the list start value void setListStartValue(int value); /// Returns the list start value int listStartValue() const; /// Set to true if this paragraph is marked to start the list numbering from the first entry. void setRestartListNumbering(bool on); /// return if this paragraph is marked to start the list numbering from the first entry. bool restartListNumbering(); /// Set the tab stop distance for this paragraph style. void setTabStopDistance(qreal value); /// return the tab stop distance for this paragraph style qreal tabStopDistance() const; /// Set the tab data for this paragraph style. void setTabPositions(const QVector &tabs); /// return the tabs data for this paragraph style QVector tabPositions() const; /// If this style is a list, then this sets the nested-ness (aka level) of this paragraph. A H2 has level 2. void setListLevel(int value); /// return the list level. int listLevel() const; /** * Return the outline level of this block, or 0 if it's not a heading. * This information is here and not in the styles because the OpenDocument specification says so. * See ODF Spec 1.1, §14.1, Outline Numbering Level, but also other parts of the specification. */ int outlineLevel() const; /** * Change this block outline level */ void setOutlineLevel(int outline); /** * Return the default outline level of this style, or 0 if there is none. */ int defaultOutlineLevel() const; /** * Change the default outline level for this style. */ void setDefaultOutlineLevel(int outline); /** * 15.5.30: The text:number-lines attribute controls whether or not lines are numbered */ bool lineNumbering() const; void setLineNumbering(bool lineNumbering); /** * 15.5.31: * The text:line-number property specifies a new start value for line numbering. The attribute is * only recognized if there is also a text:number-lines attribute with a value of true in the * same properties element. */ int lineNumberStartValue() const; void setLineNumberStartValue(int lineNumberStartValue); /** * 20.349 style:shadow * The style:shadow attribute specifies a shadow effect. * The defined values for this attribute are those defined in §7.16.5 of [XSL], except the value * inherit. * The shadow effect is not applied to the text content of an element, but depending on the element * where the attribute appears, to a paragraph, a text box, a page body, a header, a footer, a table * or a table cell. */ KoShadowStyle shadow() const; void setShadow (const KoShadowStyle &shadow); /// copy all the properties from the other style to this style, effectively duplicating it. void copyProperties(const KoParagraphStyle *style); void unapplyStyle(QTextBlock &block) const; /** * Apply this style to a blockFormat by copying all properties from this, and parent * styles to the target block format. Note that the character format will not be applied * using this method, use the other applyStyle() method for that. */ void applyStyle(QTextBlockFormat &format) const; /** * Apply this style to the textBlock by copying all properties from this, parent and * the character style (where relevant) to the target block formats. */ void applyStyle(QTextBlock &block, bool applyListStyle = true) const; /* /// return the character "properties" for this paragraph style, Note it does not inherit KoCharacterStyle *characterStyle(); /// return the character "properties" for this paragraph style, Note it does not inherit const KoCharacterStyle *characterStyle() const; /// set the character "properties" for this paragraph style void setCharacterStyle(KoCharacterStyle *style); */ /** * Returns the list style for this paragraph style. * @see KoListStyle::isValid() * @see setListStyle() * @see removeListStyle() */ KoListStyle *listStyle() const; /** * Set a new liststyle on this paragraph style, making all paragraphs that use this style * automatically be part of the list. * @see setListStyle() * @see removeListStyle() */ void setListStyle(KoListStyle *style); void remove(int key); /// Compare the paragraph, character and list properties of this style with the other bool operator==(const KoParagraphStyle &other) const; /// Compare the paragraph properties of this style with other bool compareParagraphProperties(const KoParagraphStyle &other) const; void removeDuplicates(const KoParagraphStyle &other); /** * Load the style form the element * * @param context the odf loading context * @param element the element containing the style * @param loadParents true = use the stylestack, false = use just the element */ void loadOdf(const KoXmlElement *element, KoShapeLoadingContext &context, bool loadParents = false); void saveOdf(KoGenStyle &style, KoShapeSavingContext &context) const; /** * Returns true if this paragraph style has the property set. * Note that this method does not delegate to the parent style. * @param key the key as found in the Property enum */ bool hasProperty(int key) const; /** * Set a property with key to a certain value, overriding the value from the parent style. * If the value set is equal to the value of the parent style, the key will be removed instead. * @param key the Property to set. * @param value the new value to set on this style. * @see hasProperty(), value() */ void setProperty(int key, const QVariant &value); /** * Return the value of key as represented on this style, taking into account parent styles. * You should consider using the direct accessors for individual properties instead. * @param key the Property to request. * @returns a QVariant which holds the property value. */ QVariant value(int key) const; /** * Returns true if this pragraph style has default properties * Note that the value of StyleId property is not considered */ bool hasDefaults() const; KoList *list() const; void applyParagraphListStyle(QTextBlock &block, const QTextBlockFormat &blockFormat) const; /** Returns true if the style is in use. */ bool isApplied() const; Q_SIGNALS: - void nameChanged(const QString &newName); void styleApplied(const KoParagraphStyle*) const; private: /** * Load the style from the \a KoStyleStack style stack using the * OpenDocument format. */ void loadOdfProperties(KoShapeLoadingContext &scontext); qreal propertyDouble(int key) const; QTextLength propertyLength(int key) const; int propertyInt(int key) const; bool propertyBoolean(int key) const; QColor propertyColor(int key) const; class Private; Private * const d; }; Q_DECLARE_METATYPE(KoParagraphStyle *) Q_DECLARE_METATYPE(const KoParagraphStyle *) Q_DECLARE_METATYPE(QSharedPointer) #endif diff --git a/libs/widgets/KoDialog.cpp b/libs/widgets/KoDialog.cpp index 2beba5bf8f5..4893198cb80 100644 --- a/libs/widgets/KoDialog.cpp +++ b/libs/widgets/KoDialog.cpp @@ -1,1054 +1,1050 @@ /* This file is part of the KDE Libraries * Copyright (C) 1998 Thomas Tanghus (tanghus@earthling.net) * Additions 1999-2000 by Espen Sand (espen@kde.org) * by Holger Freyther * 2005-2009 by Olivier Goffart (ogoffart at kde.org) * * 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 "KoDialog.h" #include "KoDialog_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void KoDialogPrivate::setupLayout() { Q_Q(KoDialog); if (!dirty) { QMetaObject::invokeMethod(q, "queuedLayoutUpdate", Qt::QueuedConnection); dirty = true; } } void KoDialogPrivate::queuedLayoutUpdate() { if (!dirty) { return; } dirty = false; Q_Q(KoDialog); // Don't lose the focus widget when re-creating the layout. // Testcase: KOrganizer's "Select Categories" dialog QPointer focusWidget = mMainWidget ? mMainWidget->focusWidget() : 0; if (q->layout() && q->layout() != mTopLayout) { qWarning() << q->metaObject()->className() << "created with a layout; don't do that, KoDialog takes care of it, use mainWidget or setMainWidget instead"; delete q->layout(); } delete mTopLayout; if (mButtonOrientation == Qt::Horizontal) { mTopLayout = new QVBoxLayout(q); } else { mTopLayout = new QHBoxLayout(q); } if (mUrlHelp) { mTopLayout->addWidget(mUrlHelp, 0, Qt::AlignRight); } if (mMainWidget) { mTopLayout->addWidget(mMainWidget, 10); } if (mDetailsWidget) { mTopLayout->addWidget(mDetailsWidget); } if (mActionSeparator) { mTopLayout->addWidget(mActionSeparator); } if (mButtonBox) { mButtonBox->setOrientation(mButtonOrientation); mTopLayout->addWidget(mButtonBox); } if (focusWidget) { focusWidget->setFocus(); } } void KoDialogPrivate::appendButton(KoDialog::ButtonCode key, const KGuiItem &item) { Q_Q(KoDialog); QDialogButtonBox::ButtonRole role = QDialogButtonBox::InvalidRole; switch (key) { case KoDialog::Help: case KoDialog::Details: role = QDialogButtonBox::HelpRole; break; case KoDialog::Default: case KoDialog::Reset: role = QDialogButtonBox::ResetRole; break; case KoDialog::Ok: role = QDialogButtonBox::AcceptRole; break; case KoDialog::Apply: role = QDialogButtonBox::ApplyRole; break; case KoDialog::Try: case KoDialog::Yes: role = QDialogButtonBox::YesRole; break; case KoDialog::Close: case KoDialog::Cancel: role = QDialogButtonBox::RejectRole; break; case KoDialog::No: role = QDialogButtonBox::NoRole; break; case KoDialog::User1: case KoDialog::User2: case KoDialog::User3: role = QDialogButtonBox::ActionRole; break; default: role = QDialogButtonBox::InvalidRole; break; } if (role == QDialogButtonBox::InvalidRole) { return; } QPushButton *button = new QPushButton; KGuiItem::assign(button, item); mButtonBox->addButton(button, role); mButtonList.insert(key, button); - mButtonSignalMapper.setMapping(button, key); - QObject::connect(button, SIGNAL(clicked()), - &mButtonSignalMapper, SLOT(map())); + QObject::connect(button, &QPushButton::clicked, [this, key] { q_ptr->slotButtonClicked(key); }); if (key == mDefaultButton) { // Now that it exists, set it as default q->setDefaultButton(mDefaultButton); } } void KoDialogPrivate::init(KoDialog *q) { q_ptr = q; dirty = false; q->setButtons(KoDialog::Ok | KoDialog::Cancel); q->setDefaultButton(KoDialog::Ok); - q->connect(&mButtonSignalMapper, SIGNAL(mapped(int)), q, SLOT(slotButtonClicked(int))); - q->setPlainCaption(qApp->applicationDisplayName()); // set appropriate initial window title for case it gets not set later } void KoDialogPrivate::helpLinkClicked() { q_ptr->slotButtonClicked(KoDialog::Help); } KoDialog::KoDialog(QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags) , d_ptr(new KoDialogPrivate) { d_ptr->init(this); } KoDialog::KoDialog(KoDialogPrivate &dd, QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags) , d_ptr(&dd) { d_ptr->init(this); } KoDialog::~KoDialog() { delete d_ptr; } void KoDialog::setButtons(ButtonCodes buttonMask) { Q_D(KoDialog); if (d->mButtonBox) { d->mButtonList.clear(); delete d->mButtonBox; d->mButtonBox = 0; } if (buttonMask & Cancel) { buttonMask &= ~Close; } if (buttonMask & Apply) { buttonMask &= ~Try; } if (buttonMask & Details) { buttonMask &= ~Default; } if (buttonMask == None) { d->setupLayout(); return; // When we want no button box } d->mEscapeButton = (buttonMask & Cancel) ? Cancel : Close; d->mButtonBox = new QDialogButtonBox(this); if (buttonMask & Help) { d->appendButton(Help, KStandardGuiItem::help()); } if (buttonMask & Default) { d->appendButton(Default, KStandardGuiItem::defaults()); } if (buttonMask & Reset) { d->appendButton(Reset, KStandardGuiItem::reset()); } if (buttonMask & User3) { d->appendButton(User3, KGuiItem()); } if (buttonMask & User2) { d->appendButton(User2, KGuiItem()); } if (buttonMask & User1) { d->appendButton(User1, KGuiItem()); } if (buttonMask & Ok) { d->appendButton(Ok, KStandardGuiItem::ok()); } if (buttonMask & Apply) { d->appendButton(Apply, KStandardGuiItem::apply()); } if (buttonMask & Try) { d->appendButton(Try, KGuiItem(i18n("&Try"))); } if (buttonMask & Cancel) { d->appendButton(Cancel, KStandardGuiItem::cancel()); } if (buttonMask & Close) { d->appendButton(Close, KStandardGuiItem::close()); } if (buttonMask & Yes) { d->appendButton(Yes, KStandardGuiItem::yes()); } if (buttonMask & No) { d->appendButton(No, KStandardGuiItem::no()); } if (buttonMask & Details) { d->appendButton(Details, KGuiItem(QString(), "help-about")); setDetailsWidgetVisible(false); } d->setupLayout(); } void KoDialog::setButtonsOrientation(Qt::Orientation orientation) { Q_D(KoDialog); if (d->mButtonOrientation != orientation) { d->mButtonOrientation = orientation; if (d->mActionSeparator) { d->mActionSeparator->setOrientation(d->mButtonOrientation); } if (d->mButtonOrientation == Qt::Vertical) { enableLinkedHelp(false); // 2000-06-18 Espen: No support for this yet. } } } void KoDialog::setEscapeButton(ButtonCode id) { d_func()->mEscapeButton = id; } void KoDialog::setDefaultButton(ButtonCode newDefaultButton) { Q_D(KoDialog); if (newDefaultButton == None) { newDefaultButton = NoDefault; // #148969 } const KoDialog::ButtonCode oldDefault = defaultButton(); bool oldDefaultHadFocus = false; if (oldDefault != NoDefault) { QPushButton *old = button(oldDefault); if (old) { oldDefaultHadFocus = (focusWidget() == old); old->setDefault(false); } } if (newDefaultButton != NoDefault) { QPushButton *b = button(newDefaultButton); if (b) { b->setDefault(true); if (focusWidget() == 0 || oldDefaultHadFocus) { // No widget had focus yet, or the old default button had // -> ok to give focus to the new default button, so that it's // really default (Enter triggers it). // But we don't do this if the kdialog user gave focus to a // specific widget in the dialog. b->setFocus(); } } } d->mDefaultButton = newDefaultButton; Q_ASSERT(defaultButton() == newDefaultButton); } KoDialog::ButtonCode KoDialog::defaultButton() const { Q_D(const KoDialog); QHashIterator it(d->mButtonList); while (it.hasNext()) { it.next(); if (it.value()->isDefault()) { return (ButtonCode)it.key(); } } return d->mDefaultButton; } void KoDialog::setMainWidget(QWidget *widget) { Q_D(KoDialog); if (d->mMainWidget == widget) { return; } d->mMainWidget = widget; if (d->mMainWidget && d->mMainWidget->layout()) { // Avoid double-margin problem d->mMainWidget->layout()->setMargin(0); } d->setupLayout(); } QWidget *KoDialog::mainWidget() { Q_D(KoDialog); if (!d->mMainWidget) { setMainWidget(new QWidget(this)); } return d->mMainWidget; } QSize KoDialog::sizeHint() const { Q_D(const KoDialog); if (!d->mMinSize.isEmpty()) { return d->mMinSize.expandedTo(minimumSizeHint()) + d->mIncSize; } else { if (d->dirty) { const_cast(d)->queuedLayoutUpdate(); } return QDialog::sizeHint() + d->mIncSize; } } QSize KoDialog::minimumSizeHint() const { Q_D(const KoDialog); if (d->dirty) { const_cast(d)->queuedLayoutUpdate(); } return QDialog::minimumSizeHint() + d->mIncSize; } // // Grab QDialogs keypresses if non-modal. // void KoDialog::keyPressEvent(QKeyEvent *event) { Q_D(KoDialog); if (event->modifiers() == 0) { if (event->key() == Qt::Key_F1) { QPushButton *button = this->button(Help); if (button) { button->animateClick(); event->accept(); return; } } if (event->key() == Qt::Key_Escape) { QPushButton *button = this->button(d->mEscapeButton); if (button) { button->animateClick(); event->accept(); return; } } } else if (event->key() == Qt::Key_F1 && event->modifiers() == Qt::ShiftModifier) { QWhatsThis::enterWhatsThisMode(); event->accept(); return; } else if (event->modifiers() == Qt::ControlModifier && (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)) { // accept the dialog when Ctrl-Return is pressed QPushButton *button = this->button(Ok); if (button) { button->animateClick(); event->accept(); return; } } QDialog::keyPressEvent(event); } int KoDialog::marginHint() { return QApplication::style()->pixelMetric(QStyle::PM_DefaultChildMargin); } int KoDialog::spacingHint() { return QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); } int KoDialog::groupSpacingHint() { return QApplication::fontMetrics().lineSpacing(); } QString KoDialog::makeStandardCaption(const QString &userCaption, QWidget *window, CaptionFlags flags) { Q_UNUSED(window); QString caption = qApp->applicationDisplayName(); QString captionString = userCaption.isEmpty() ? caption : userCaption; // If the document is modified, add '[modified]'. if (flags & ModifiedCaption) { captionString += QString::fromUtf8(" [") + i18n("modified") + QString::fromUtf8("]"); } if (!userCaption.isEmpty()) { // Add the application name if: // User asked for it, it's not a duplication and the app name (caption()) is not empty if (flags & AppNameCaption && !caption.isEmpty() && !userCaption.endsWith(caption)) { // TODO: check to see if this is a transient/secondary window before trying to add the app name // on platforms that need this captionString += i18nc("Document/application separator in titlebar", " – ") + caption; } } return captionString; } void KoDialog::setCaption(const QString &_caption) { const QString caption = makeStandardCaption(_caption, this); setPlainCaption(caption); } void KoDialog::setCaption(const QString &caption, bool modified) { CaptionFlags flags = HIGCompliantCaption; // ### Qt5 TODO: port to [*], see QWidget::setWindowFilePath if (modified) { flags |= ModifiedCaption; } setPlainCaption(makeStandardCaption(caption, this, flags)); } void KoDialog::setPlainCaption(const QString &caption) { if (QWidget *win = window()) { win->setWindowTitle(caption); } } void KoDialog::resizeLayout(QWidget *widget, int margin, int spacing) //static { if (widget->layout()) { resizeLayout(widget->layout(), margin, spacing); } if (widget->children().count() > 0) { const QList list = widget->children(); foreach (QObject *object, list) { if (object->isWidgetType()) { resizeLayout((QWidget *)object, margin, spacing); } } } } void KoDialog::resizeLayout(QLayout *layout, int margin, int spacing) //static { QLayoutItem *child; int pos = 0; while ((child = layout->itemAt(pos))) { if (child->layout()) { resizeLayout(child->layout(), margin, spacing); } ++pos; } if (layout->layout()) { layout->layout()->setMargin(margin); layout->layout()->setSpacing(spacing); } } static QRect screenRect(QWidget *widget, int screen) { QDesktopWidget *desktop = QApplication::desktop(); KConfig gc("kdeglobals", KConfig::NoGlobals); KConfigGroup cg(&gc, "Windows"); if (desktop->isVirtualDesktop() && cg.readEntry("XineramaEnabled", true) && cg.readEntry("XineramaPlacementEnabled", true)) { if (screen < 0 || screen >= desktop->numScreens()) { if (screen == -1) { screen = desktop->primaryScreen(); } else if (screen == -3) { screen = desktop->screenNumber(QCursor::pos()); } else { screen = desktop->screenNumber(widget); } } return desktop->availableGeometry(screen); } else { return desktop->geometry(); } } void KoDialog::centerOnScreen(QWidget *widget, int screen) { if (!widget) { return; } QRect rect = screenRect(widget, screen); widget->move(rect.center().x() - widget->width() / 2, rect.center().y() - widget->height() / 2); } bool KoDialog::avoidArea(QWidget *widget, const QRect &area, int screen) { if (!widget) { return false; } QRect fg = widget->frameGeometry(); if (!fg.intersects(area)) { return true; // nothing to do. } const QRect scr = screenRect(widget, screen); QRect avoid(area); // let's add some margin avoid.translate(-5, -5); avoid.setRight(avoid.right() + 10); avoid.setBottom(avoid.bottom() + 10); if (qMax(fg.top(), avoid.top()) <= qMin(fg.bottom(), avoid.bottom())) { // We need to move the widget up or down int spaceAbove = qMax(0, avoid.top() - scr.top()); int spaceBelow = qMax(0, scr.bottom() - avoid.bottom()); if (spaceAbove > spaceBelow) // where's the biggest side? if (fg.height() <= spaceAbove) { // big enough? fg.setY(avoid.top() - fg.height()); } else { return false; } else if (fg.height() <= spaceBelow) { // big enough? fg.setY(avoid.bottom()); } else { return false; } } if (qMax(fg.left(), avoid.left()) <= qMin(fg.right(), avoid.right())) { // We need to move the widget left or right const int spaceLeft = qMax(0, avoid.left() - scr.left()); const int spaceRight = qMax(0, scr.right() - avoid.right()); if (spaceLeft > spaceRight) // where's the biggest side? if (fg.width() <= spaceLeft) { // big enough? fg.setX(avoid.left() - fg.width()); } else { return false; } else if (fg.width() <= spaceRight) { // big enough? fg.setX(avoid.right()); } else { return false; } } widget->move(fg.x(), fg.y()); return true; } void KoDialog::showButtonSeparator(bool state) { Q_D(KoDialog); if ((d->mActionSeparator != 0) == state) { return; } if (state) { if (d->mActionSeparator) { return; } d->mActionSeparator = new KSeparator(this); d->mActionSeparator->setOrientation(d->mButtonOrientation); } else { delete d->mActionSeparator; d->mActionSeparator = 0; } d->setupLayout(); } void KoDialog::setInitialSize(const QSize &size) { d_func()->mMinSize = size; adjustSize(); } void KoDialog::incrementInitialSize(const QSize &size) { d_func()->mIncSize = size; adjustSize(); } QPushButton *KoDialog::button(ButtonCode id) const { Q_D(const KoDialog); return d->mButtonList.value(id, 0); } void KoDialog::enableButton(ButtonCode id, bool state) { QPushButton *button = this->button(id); if (button) { button->setEnabled(state); } } bool KoDialog::isButtonEnabled(ButtonCode id) const { QPushButton *button = this->button(id); if (button) { return button->isEnabled(); } return false; } void KoDialog::enableButtonOk(bool state) { enableButton(Ok, state); } void KoDialog::enableButtonApply(bool state) { enableButton(Apply, state); } void KoDialog::enableButtonCancel(bool state) { enableButton(Cancel, state); } void KoDialog::showButton(ButtonCode id, bool state) { QPushButton *button = this->button(id); if (button) { state ? button->show() : button->hide(); } } void KoDialog::setButtonGuiItem(ButtonCode id, const KGuiItem &item) { QPushButton *button = this->button(id); if (!button) { return; } KGuiItem::assign(button, item); } void KoDialog::setButtonText(ButtonCode id, const QString &text) { Q_D(KoDialog); if (!d->mSettingDetails && (id == Details)) { d->mDetailsButtonText = text; setDetailsWidgetVisible(d->mDetailsVisible); return; } QPushButton *button = this->button(id); if (button) { button->setText(text); } } QString KoDialog::buttonText(ButtonCode id) const { QPushButton *button = this->button(id); if (button) { return button->text(); } else { return QString(); } } void KoDialog::setButtonIcon(ButtonCode id, const QIcon &icon) { QPushButton *button = this->button(id); if (button) { button->setIcon(icon); } } QIcon KoDialog::buttonIcon(ButtonCode id) const { QPushButton *button = this->button(id); if (button) { return button->icon(); } else { return QIcon(); } } void KoDialog::setButtonToolTip(ButtonCode id, const QString &text) { QPushButton *button = this->button(id); if (button) { if (text.isEmpty()) { button->setToolTip(QString()); } else { button->setToolTip(text); } } } QString KoDialog::buttonToolTip(ButtonCode id) const { QPushButton *button = this->button(id); if (button) { return button->toolTip(); } else { return QString(); } } void KoDialog::setButtonWhatsThis(ButtonCode id, const QString &text) { QPushButton *button = this->button(id); if (button) { if (text.isEmpty()) { button->setWhatsThis(QString()); } else { button->setWhatsThis(text); } } } QString KoDialog::buttonWhatsThis(ButtonCode id) const { QPushButton *button = this->button(id); if (button) { return button->whatsThis(); } else { return QString(); } } void KoDialog::setButtonFocus(ButtonCode id) { QPushButton *button = this->button(id); if (button) { button->setFocus(); } } void KoDialog::setDetailsWidget(QWidget *detailsWidget) { Q_D(KoDialog); if (d->mDetailsWidget == detailsWidget) { return; } delete d->mDetailsWidget; d->mDetailsWidget = detailsWidget; if (d->mDetailsWidget->parentWidget() != this) { d->mDetailsWidget->setParent(this); } d->mDetailsWidget->hide(); d->setupLayout(); if (!d->mSettingDetails) { setDetailsWidgetVisible(d->mDetailsVisible); } } bool KoDialog::isDetailsWidgetVisible() const { return d_func()->mDetailsVisible; } void KoDialog::setDetailsWidgetVisible(bool visible) { Q_D(KoDialog); if (d->mDetailsButtonText.isEmpty()) { d->mDetailsButtonText = i18n("&Details"); } d->mSettingDetails = true; d->mDetailsVisible = visible; if (d->mDetailsVisible) { emit aboutToShowDetails(); setButtonText(Details, d->mDetailsButtonText + " <<"); if (d->mDetailsWidget) { if (layout()) { layout()->setEnabled(false); } d->mDetailsWidget->show(); adjustSize(); if (layout()) { layout()->activate(); layout()->setEnabled(true); } } } else { setButtonText(Details, d->mDetailsButtonText + " >>"); if (d->mDetailsWidget) { d->mDetailsWidget->hide(); } if (layout()) { layout()->activate(); adjustSize(); } } d->mSettingDetails = false; } void KoDialog::delayedDestruct() { if (isVisible()) { hide(); } deleteLater(); } void KoDialog::slotButtonClicked(int button) { Q_D(KoDialog); emit buttonClicked(static_cast(button)); switch (button) { case Ok: emit okClicked(); accept(); break; case Apply: emit applyClicked(); break; case Try: emit tryClicked(); break; case User3: emit user3Clicked(); break; case User2: emit user2Clicked(); break; case User1: emit user1Clicked(); break; case Yes: emit yesClicked(); done(Yes); break; case No: emit noClicked(); done(No); break; case Cancel: emit cancelClicked(); reject(); break; case Close: emit closeClicked(); done(Close); // KDE5: call reject() instead; more QDialog-like. break; case Help: emit helpClicked(); if (!d->mAnchor.isEmpty() || !d->mHelpApp.isEmpty()) { KHelpClient::invokeHelp(d->mAnchor, d->mHelpApp); } break; case Default: emit defaultClicked(); break; case Reset: emit resetClicked(); break; case Details: setDetailsWidgetVisible(!d->mDetailsVisible); break; } // If we're here from the closeEvent, and auto-delete is on, well, auto-delete now. if (d->mDeferredDelete) { d->mDeferredDelete = false; delayedDestruct(); } } void KoDialog::enableLinkedHelp(bool state) { Q_D(KoDialog); if ((d->mUrlHelp != 0) == state) { return; } if (state) { if (d->mUrlHelp) { return; } d->mUrlHelp = new KUrlLabel(this); d->mUrlHelp->setText(helpLinkText()); d->mUrlHelp->setFloatEnabled(true); d->mUrlHelp->setUnderline(true); d->mUrlHelp->setMinimumHeight(fontMetrics().height() + marginHint()); connect(d->mUrlHelp, SIGNAL(leftClickedUrl()), SLOT(helpLinkClicked())); d->mUrlHelp->show(); } else { delete d->mUrlHelp; d->mUrlHelp = 0; } d->setupLayout(); } void KoDialog::setHelp(const QString &anchor, const QString &appname) { Q_D(KoDialog); d->mAnchor = anchor; d->mHelpApp = appname; } void KoDialog::setHelpLinkText(const QString &text) { Q_D(KoDialog); d->mHelpLinkText = text; if (d->mUrlHelp) { d->mUrlHelp->setText(helpLinkText()); } } QString KoDialog::helpLinkText() const { Q_D(const KoDialog); return (d->mHelpLinkText.isEmpty() ? i18n("Get help...") : d->mHelpLinkText); } void KoDialog::updateGeometry() { } void KoDialog::hideEvent(QHideEvent *event) { emit hidden(); if (!event->spontaneous()) { emit finished(); } } void KoDialog::closeEvent(QCloseEvent *event) { Q_D(KoDialog); QPushButton *button = this->button(d->mEscapeButton); if (button && !isHidden()) { button->animateClick(); if (testAttribute(Qt::WA_DeleteOnClose)) { // Don't let QWidget::close do a deferred delete just yet, wait for the click first d->mDeferredDelete = true; setAttribute(Qt::WA_DeleteOnClose, false); } } else { QDialog::closeEvent(event); } } #include "moc_KoDialog.cpp" diff --git a/libs/widgets/KoDialog_p.h b/libs/widgets/KoDialog_p.h index 6cc29f489ac..277ecf72fc8 100644 --- a/libs/widgets/KoDialog_p.h +++ b/libs/widgets/KoDialog_p.h @@ -1,90 +1,88 @@ /* This file is part of the KDE project Copyright (C) 2007 Matthias Kretz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 KODIALOG_P_H #define KODIALOG_P_H #include "KoDialog.h" #include -#include #include #include class QBoxLayout; class QPushButton; class KUrlLabel; class KSeparator; class QDialogButtonBox; class KoDialogPrivate { Q_DECLARE_PUBLIC(KoDialog) protected: KoDialogPrivate() : mDetailsVisible(false), mSettingDetails(false), mDeferredDelete(false), mDetailsWidget(0), mTopLayout(0), mMainWidget(0), mUrlHelp(0), mActionSeparator(0), mButtonOrientation(Qt::Horizontal), mDefaultButton(KoDialog::NoDefault), mButtonBox(0) { } virtual ~KoDialogPrivate() = default; KoDialog *q_ptr; void setupLayout(); void appendButton(KoDialog::ButtonCode code, const KGuiItem &item); bool mDetailsVisible; bool mSettingDetails; bool mDeferredDelete; QWidget *mDetailsWidget; QSize mIncSize; QSize mMinSize; QString mDetailsButtonText; QBoxLayout *mTopLayout; QPointer mMainWidget; KUrlLabel *mUrlHelp; KSeparator *mActionSeparator; QString mAnchor; QString mHelpApp; QString mHelpLinkText; Qt::Orientation mButtonOrientation; KoDialog::ButtonCode mDefaultButton; KoDialog::ButtonCode mEscapeButton; QDialogButtonBox *mButtonBox; QHash mButtonList; - QSignalMapper mButtonSignalMapper; protected Q_SLOTS: void queuedLayoutUpdate(); void helpLinkClicked(); private: void init(KoDialog *); bool dirty: 1; }; #endif // KDEUI_KDIALOG_P_H diff --git a/plugins/formulashape/KoFormulaTool.cpp b/plugins/formulashape/KoFormulaTool.cpp index decba10e312..6db24cad001 100644 --- a/plugins/formulashape/KoFormulaTool.cpp +++ b/plugins/formulashape/KoFormulaTool.cpp @@ -1,524 +1,527 @@ /* This file is part of the KDE project Copyright (C) 2006 Martin Pfeiffer 2009 Jeremias Epperlein 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 "KoFormulaTool.h" #include "KoFormulaShape.h" #include "FormulaToolWidget.h" #include "BasicElement.h" #include "FormulaEditor.h" #include "FormulaDebug.h" #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include "FormulaCommand.h" #include "FormulaCommandUpdate.h" #include #include #include #include #include "FormulaRenderer.h" #include +// this adds const to non-const objects (like std::as_const) +template Q_DECL_CONSTEXPR typename std::add_const::type &koAsConst(T &t) noexcept { return t; } +// prevent rvalue arguments: +template void koAsConst(const T &&) = delete; KoFormulaTool::KoFormulaTool( KoCanvasBase* canvas ) : KoToolBase( canvas ), m_formulaShape( 0 ), m_formulaEditor( 0 ) { - m_signalMapper = new QSignalMapper(this); setupActions(); setTextMode(true); } KoFormulaTool::~KoFormulaTool() { if( m_formulaEditor ) { m_cursorList.removeAll(m_formulaEditor); delete m_formulaEditor; } foreach (FormulaEditor* tmp, m_cursorList) { delete tmp; } } void KoFormulaTool::activate(ToolActivation toolActivation, const QSet &shapes) { Q_UNUSED(toolActivation); foreach (KoShape *shape, shapes) { m_formulaShape = dynamic_cast( shape ); if( m_formulaShape ) break; } if( m_formulaShape == 0 ) // none found { emit done(); return; } useCursor(Qt::IBeamCursor); m_formulaEditor=0; for (int i = 0; i < m_cursorList.count(); i++) { FormulaEditor* editor = m_cursorList[i]; FormulaData* formulaData=m_formulaShape->formulaData(); if (editor->formulaData() == formulaData) { //we have to check if the cursors current element is actually a //child of the m_formulaShape->formulaData() m_cursorList.removeAll(editor); if (formulaData->formulaElement()->hasDescendant(editor->cursor().currentElement())) { if (editor->cursor().isAccepted()) { debugFormula << "Found old cursor"; m_formulaEditor=editor; break; } } delete editor; } } if (m_formulaEditor==0) { //TODO: there should be a extra constructor for this m_formulaEditor = new FormulaEditor( m_formulaShape->formulaData()); } connect(m_formulaShape->formulaData(), SIGNAL(dataChanged(FormulaCommand*,bool)), this, SLOT(updateCursor(FormulaCommand*,bool))); - connect(m_signalMapper, SIGNAL(mapped(QString)), this, SLOT(insert(QString))); + for (const TemplateAction &templateAction : koAsConst(m_templateActions)) { + connect(templateAction.action, &QAction::triggered, this, [this, templateAction] { insert(templateAction.data); }); + } //Only for debugging: connect(action("write_elementTree"),SIGNAL(triggered(bool)), m_formulaShape->formulaData(), SLOT(writeElementTree())); } void KoFormulaTool::deactivate() { + for (const TemplateAction &templateAction : koAsConst(m_templateActions)) { + disconnect(templateAction.action, &QAction::triggered, this, nullptr); + } disconnect(m_formulaShape->formulaData(),0,this,0); - disconnect(m_signalMapper,0,this,0); if (canvas()) { m_cursorList.append(m_formulaEditor); debugFormula << "Appending cursor"; } if (m_cursorList.count() > 20) { // don't let it grow indefinitely - //TODO: is this save? - delete m_cursorList[0]; - m_cursorList.removeAt(0); + delete m_cursorList.takeAt(0); } m_formulaShape = 0; } void KoFormulaTool::updateCursor(FormulaCommand* command, bool undo) { if (command!=0) { debugFormula << "Going to change cursor"; command->changeCursor(m_formulaEditor->cursor(),undo); } else { debugFormula << "Going to reset cursor"; resetFormulaEditor(); } repaintCursor(); } void KoFormulaTool::paint( QPainter &painter, const KoViewConverter &converter ) { painter.save(); // transform painter from view coordinate system to document coordinate system // remember that matrix multiplication is not commutative so painter.matrix // has to come last painter.setTransform(m_formulaShape->absoluteTransformation(&converter) * painter.transform()); KoShape::applyConversion(painter,converter); m_formulaShape->formulaRenderer()->paintElement(painter,m_formulaShape->formulaData()->formulaElement(),true); m_formulaEditor->paint( painter ); painter.restore(); } void KoFormulaTool::repaintCursor() { canvas()->updateCanvas( m_formulaShape->boundingRect() ); } void KoFormulaTool::mousePressEvent( KoPointerEvent *event ) { // Check if the event is valid means inside the shape if(!m_formulaShape->boundingRect().contains( event->point )) { return; } // transform the global coordinates into shape coordinates QPointF p = m_formulaShape->absoluteTransformation(0).inverted().map( event->point ); if (event->modifiers() & Qt::ShiftModifier) { m_formulaEditor->cursor().setSelecting(true); } else { m_formulaEditor->cursor().setSelecting(false); } // set the cursor to the element the user clicked on m_formulaEditor->cursor().setCursorTo( p ); repaintCursor(); event->accept(); } void KoFormulaTool::mouseDoubleClickEvent( KoPointerEvent *event ) { if( !m_formulaShape->boundingRect().contains( event->point ) ) { return; } // transform the global coordinates into shape coordinates QPointF p = m_formulaShape->absoluteTransformation(0).inverted().map( event->point ); //clear the current selection m_formulaEditor->cursor().setSelecting(false); //place the cursor m_formulaEditor->cursor().setCursorTo(p); m_formulaEditor->cursor().selectElement(m_formulaEditor->cursor().currentElement()); repaintCursor(); event->accept(); } void KoFormulaTool::mouseMoveEvent( KoPointerEvent *event ) { // Q_UNUSED( event ) if (!(event->buttons() & Qt::LeftButton)) { return; } // Check if the event is valid means inside the shape if( !m_formulaShape->boundingRect().contains( event->point ) ) debugFormula << "Getting most probably invalid mouseMoveEvent"; // transform the global coordinates into shape coordinates QPointF p = m_formulaShape->absoluteTransformation(0).inverted().map( event->point ); //TODO Implement drag and drop of elements m_formulaEditor->cursor().setSelecting(true); m_formulaEditor->cursor().setCursorTo( p ); repaintCursor(); event->accept(); } void KoFormulaTool::mouseReleaseEvent( KoPointerEvent *event ) { Q_UNUSED( event ) // TODO Implement drag and drop } void KoFormulaTool::keyPressEvent( QKeyEvent *event ) { FormulaCommand *command=0; if( !m_formulaEditor ) return; if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right || event->key() == Qt::Key_Up || event->key() == Qt::Key_Down || event->key() == Qt::Key_Home || event->key() == Qt::Key_End ) { if (event->modifiers() & Qt::ShiftModifier) { m_formulaEditor->cursor().setSelecting(true); } else { m_formulaEditor->cursor().setSelecting(false); } } switch( event->key() ) // map key to movement or action { case Qt::Key_Backspace: m_formulaShape->update(); command=m_formulaEditor->remove( true ); m_formulaShape->updateLayout(); m_formulaShape->update(); break; case Qt::Key_Delete: m_formulaShape->update(); command=m_formulaEditor->remove( false ); m_formulaShape->updateLayout(); m_formulaShape->update(); break; case Qt::Key_Left: m_formulaEditor->cursor().move( MoveLeft ); break; case Qt::Key_Up: m_formulaEditor->cursor().move( MoveUp ); break; case Qt::Key_Right: m_formulaEditor->cursor().move( MoveRight ); break; case Qt::Key_Down: m_formulaEditor->cursor().move( MoveDown ); break; case Qt::Key_End: m_formulaEditor->cursor().moveEnd(); break; case Qt::Key_Home: m_formulaEditor->cursor().moveHome(); break; default: if( event->text().length() != 0 ) { command=m_formulaEditor->insertText( event->text() ); } } if (command!=0) { canvas()->addCommand(new FormulaCommandUpdate(m_formulaShape,command)); } repaintCursor(); event->accept(); } void KoFormulaTool::keyReleaseEvent( QKeyEvent *event ) { event->accept(); } void KoFormulaTool::remove( bool backSpace ) { m_formulaShape->update(); m_formulaEditor->remove( backSpace ); m_formulaShape->updateLayout(); m_formulaShape->update(); } void KoFormulaTool::insert( const QString& action ) { FormulaCommand *command; m_formulaShape->update(); command=m_formulaEditor->insertMathML( action ); if (command!=0) { canvas()->addCommand(new FormulaCommandUpdate(m_formulaShape, command)); } } void KoFormulaTool::changeTable ( QAction* action ) { FormulaCommand *command; m_formulaShape->update(); bool row=action->data().toList()[0].toBool(); bool insert=action->data().toList()[1].toBool(); command=m_formulaEditor->changeTable(insert,row); if (command!=0) { canvas()->addCommand(new FormulaCommandUpdate(m_formulaShape, command)); } } void KoFormulaTool::insertSymbol ( const QString& symbol ) { FormulaCommand *command; m_formulaShape->update(); command=m_formulaEditor->insertText( symbol ); if (command!=0) { canvas()->addCommand(new FormulaCommandUpdate(m_formulaShape, command)); } } QWidget* KoFormulaTool::createOptionWidget() { FormulaToolWidget* options = new FormulaToolWidget( this ); options->setFormulaTool( this ); return options; } KoFormulaShape* KoFormulaTool::shape() { return m_formulaShape; } FormulaEditor* KoFormulaTool::formulaEditor() { return m_formulaEditor; } void KoFormulaTool::resetFormulaEditor() { m_formulaEditor->setData(m_formulaShape->formulaData()); FormulaCursor cursor(FormulaCursor(m_formulaShape->formulaData()->formulaElement(),false,0,0)); m_formulaEditor->setCursor(cursor); //if the cursor is not allowed at the beginning of the formula, move it right //TODO: check, if this can ever happen if ( !m_formulaEditor->cursor().isAccepted() ) { m_formulaEditor->cursor().move(MoveRight); } } void KoFormulaTool::loadFormula() { // get an filepath const QString fileName = QFileDialog::getOpenFileName(); if( fileName.isEmpty() || !shape() ) return; // open the file the filepath points to QFile file( fileName ); if( !file.open( QIODevice::ReadOnly | QIODevice::Text ) ) return; KoOdfStylesReader stylesReader; KoOdfLoadingContext odfContext( stylesReader, 0 ); KoShapeLoadingContext shapeContext(odfContext, canvas()->shapeController()->resourceManager()); // setup a DOM structure and start the actual loading process KoXmlDocument tmpDocument; tmpDocument.setContent( &file, false, 0, 0, 0 ); FormulaElement* formulaElement = new FormulaElement(); // create a new root element formulaElement->readMathML( tmpDocument.documentElement() ); // and load the new formula FormulaCommand* command=new FormulaCommandLoad(m_formulaShape->formulaData(),formulaElement); canvas()->addCommand(new FormulaCommandUpdate(m_formulaShape, command)); } void KoFormulaTool::saveFormula() { const QString filePath = QFileDialog::getSaveFileName(); if( filePath.isEmpty() || !shape() ) return; QFile file( filePath ); KoXmlWriter writer( &file ); KoGenStyles styles; KoEmbeddedDocumentSaver embeddedSaver; KoShapeSavingContext shapeSavingContext( writer, styles, embeddedSaver ); m_formulaShape->formulaData()->saveMathML( shapeSavingContext ); } void KoFormulaTool::setupActions() { //notice that only empty mrows hows parent is a inferred mrow are treated as placeholders //this causes the constructs addTemplateAction(i18n("Insert fenced element"), "insert_fence","", koIconNameCStr("brackets")); addTemplateAction(i18n("Insert enclosed element"), "insert_enclosed", "", koIconNameCStr("enclosed")); addTemplateAction(i18n("Insert root"), "insert_root","", koIconNameCStr("root")); addTemplateAction(i18n("Insert square root"), "insert_sqrt","", koIconNameCStr("sqrt")); addTemplateAction(i18n("Insert fraction"), "insert_fraction", "", koIconNameCStr("frac")); addTemplateAction(i18n("Insert bevelled fraction"), "insert_bevelled_fraction", "", koIconNameCStr("bevelled")); addTemplateAction(i18n("Insert 3x3 table"), "insert_33table", "" \ "" \ "", koIconNameCStr("matrix")); addTemplateAction(i18n("Insert 2 dimensional vector"), "insert_21table", "", koIconNameCStr("vector")); addTemplateAction(i18n("Insert subscript"), "insert_subscript", "", koIconNameCStr("rsub")); addTemplateAction(i18n("Insert superscript"), "insert_supscript", "", koIconNameCStr("rsup")); addTemplateAction(i18n("Insert sub- and superscript"), "insert_subsupscript", "", koIconNameCStr("rsubup")); addTemplateAction(i18n("Insert overscript"), "insert_overscript", "", koIconNameCStr("gsup")); addTemplateAction(i18n("Insert underscript"), "insert_underscript", "", koIconNameCStr("gsub")); addTemplateAction(i18n("Insert under- and overscript"), "insert_underoverscript", "", koIconNameCStr("gsubup")); //only for debugging QAction * action; action = new QAction( "Debug - writeElementTree" , this ); addAction( "write_elementTree", action ); QList list; action = new QAction( i18n( "Insert row" ), this ); list<setData( list); list.clear(); addAction( "insert_row", action ); action->setIcon(koIcon("insrow")); action = new QAction( i18n( "Insert column" ), this ); list<setData( list); list.clear(); addAction( "insert_column", action ); action->setIcon(koIcon("inscol")); action = new QAction( i18n( "Remove row" ), this ); list<setData( list); list.clear(); addAction( "remove_row", action ); action->setIcon(koIcon("remrow")); action = new QAction( i18n( "Remove column" ), this ); list<setData( list); list.clear(); addAction( "remove_column", action ); action->setIcon(koIcon("remcol")); } void KoFormulaTool::addTemplateAction(const QString &caption, const QString &name, const QString &data, const char *iconName) { - QAction * action; - action = new QAction( caption, this ); - m_signalMapper->setMapping(action, data); - addAction( name , action ); + QAction *action = new QAction( caption, this ); + addAction(name , action); action->setIcon(QIcon::fromTheme(QLatin1String(iconName))); - connect( action, SIGNAL(triggered()), m_signalMapper, SLOT(map())); + m_templateActions.push_back(TemplateAction { action, data }); + // the connection takes place when this KoToolBase is activated } void KoFormulaTool::copy() const { QApplication::clipboard()->setText("test"); } void KoFormulaTool::deleteSelection() { KoToolBase::deleteSelection(); } bool KoFormulaTool::paste() { const QMimeData* data=QApplication::clipboard()->mimeData(); if (data->hasFormat("text/plain")) { debugFormula << data->text(); FormulaCommand* command=m_formulaEditor->insertText(data->text()); if (command!=0) { canvas()->addCommand(new FormulaCommandUpdate(m_formulaShape,command)); } repaintCursor(); return true; } return false; } QStringList KoFormulaTool::supportedPasteMimeTypes() const { QStringList tmp; tmp << "text/plain"; tmp << "application/xml"; return tmp; } diff --git a/plugins/formulashape/KoFormulaTool.h b/plugins/formulashape/KoFormulaTool.h index 911167aeb65..c3b0c1ff63f 100644 --- a/plugins/formulashape/KoFormulaTool.h +++ b/plugins/formulashape/KoFormulaTool.h @@ -1,128 +1,132 @@ /* This file is part of the KDE project Copyright (C) 2006 Martin Pfeiffer 2009 Jeremias Epperlein 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 KOFORMULATOOL_H #define KOFORMULATOOL_H #include class KoFormulaShape; class FormulaEditor; class FormulaCommand; -class QSignalMapper; + +struct TemplateAction { + QAction *action; + QString data; +}; /** * @short The flake tool for a formula * @author Martin Pfeiffer */ class KoFormulaTool : public KoToolBase { Q_OBJECT public: /// The standard constructor explicit KoFormulaTool( KoCanvasBase *canvas ); /// The standard destructor ~KoFormulaTool() override; /// reimplemented void paint( QPainter &painter, const KoViewConverter &converter ) override; /// reimplemented void mousePressEvent( KoPointerEvent *event ) override ; /// reimplemented void mouseDoubleClickEvent( KoPointerEvent *event ) override; /// reimplemented void mouseMoveEvent( KoPointerEvent *event ) override; /// reimplemented void mouseReleaseEvent( KoPointerEvent *event ) override; void keyPressEvent( QKeyEvent *event ) override; void keyReleaseEvent( QKeyEvent *event ) override; void remove( bool backSpace ); /// @return The currently manipulated KoFormulaShape KoFormulaShape* shape(); /// @return The currently active cursor FormulaEditor* formulaEditor(); /// Reset the cursor void resetFormulaEditor(); public Q_SLOTS: /// Called when this tool instance is activated and fills m_formulaShape void activate(ToolActivation toolActivation, const QSet &shapes) override; /// Called when this tool instance is deactivated void deactivate() override; /// Insert the element tied to the given @p action void insert( const QString& action ); void changeTable( QAction* action); void insertSymbol( const QString& symbol); /// Reposition the cursor according to the data change void updateCursor(FormulaCommand* command, bool undo); void saveFormula(); void loadFormula(); protected: /// Create default option widget QWidget* createOptionWidget() override; void copy() const override; void deleteSelection() override; bool paste() override; QStringList supportedPasteMimeTypes() const override; private: /// Repaint the cursor and selection void repaintCursor(); /// Creates all the actions provided by the tool void setupActions(); void addTemplateAction(const QString &caption, const QString &name, const QString &data, const char *iconName); /// The FormulaShape the tool is manipulating KoFormulaShape* m_formulaShape; /// The FormulaEditor the tool uses to move around in the formula FormulaEditor* m_formulaEditor; QList m_cursorList; - QSignalMapper* m_signalMapper; + std::vector m_templateActions; }; #endif diff --git a/plugins/textediting/spellcheck/SpellCheckMenu.cpp b/plugins/textediting/spellcheck/SpellCheckMenu.cpp index 8e6008e071d..488be3d51ba 100644 --- a/plugins/textediting/spellcheck/SpellCheckMenu.cpp +++ b/plugins/textediting/spellcheck/SpellCheckMenu.cpp @@ -1,165 +1,159 @@ /* This file is part of the KDE project * Copyright (C) 2010 Christoph Goerlich * Copyright (C) 2012 Shreya Pandit * * 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 "SpellCheckMenu.h" #include "SpellCheck.h" #include "SpellCheckDebug.h" #include #include #include #include -#include SpellCheckMenu::SpellCheckMenu(const Sonnet::Speller &speller, SpellCheck *spellCheck) : QObject(spellCheck), m_spellCheck(spellCheck), m_speller(speller), m_suggestionsMenuAction(0), m_ignoreWordAction(0), m_addToDictionaryAction(0), m_suggestionsMenu(0), - m_suggestionsSignalMapper(new QSignalMapper(this)), m_currentMisspelledPosition(-1) { m_suggestionsMenuAction = new KActionMenu(i18n("Spelling"), this); m_suggestionsMenu = m_suggestionsMenuAction->menu(); connect(m_suggestionsMenu, SIGNAL(aboutToShow()), this, SLOT(createSuggestionsMenu())); m_addToDictionaryAction = new QAction(i18n("Add to Dictionary"), this); connect(m_addToDictionaryAction, SIGNAL(triggered()), this, SLOT(addWordToDictionary())); // disabling this as if it calls the speller it's only changed in a local copy // see addWordToDictionary for how it should be done, except background checker // doesn't have suche a method for ignoreWord // Only option left is to personally ignore words // m_ignoreWordAction = new QAction(i18n("Ignore Word"), this); // connect(m_ignoreWordAction, SIGNAL(triggered()), this, SLOT(ignoreWord())); - connect(m_suggestionsSignalMapper, SIGNAL(mapped(QString)), - this, SLOT(replaceWord(QString))); - setEnabled(false); setVisible(false); } SpellCheckMenu::~SpellCheckMenu() { } QPair SpellCheckMenu::menuAction() { return QPair("spelling_suggestions", m_suggestionsMenuAction); } void SpellCheckMenu::createSuggestionsMenu() { m_suggestions.clear(); m_suggestionsMenu->clear(); m_suggestionsMenu->addAction(m_ignoreWordAction); m_suggestionsMenu->addAction(m_addToDictionaryAction); m_suggestionsMenu->addSeparator(); if (!m_currentMisspelled.isEmpty()) { m_suggestions = m_speller.suggest(m_currentMisspelled); for (int i = 0; i < m_suggestions.count(); ++i) { const QString &suggestion = m_suggestions.at(i); QAction *action = new QAction(suggestion, m_suggestionsMenu); - connect(action, SIGNAL(triggered()), m_suggestionsSignalMapper, SLOT(map())); - m_suggestionsSignalMapper->setMapping(action, suggestion); + connect(action, &QAction::triggered, [this, suggestion] { replaceWord(suggestion); }); m_suggestionsMenu->addAction(action); } } } void SpellCheckMenu::ignoreWord() { if (m_currentMisspelled.isEmpty() || m_currentMisspelledPosition < 0) return; // see comment in ctor why this will never work m_speller.addToSession(m_currentMisspelled); emit clearHighlightingForWord(m_currentMisspelledPosition); m_currentMisspelled.clear(); m_currentMisspelledPosition = -1; } void SpellCheckMenu::addWordToDictionary() { if (m_currentMisspelled.isEmpty() || m_currentMisspelledPosition < 0) return; m_spellCheck->addWordToPersonal(m_currentMisspelled, m_currentMisspelledPosition); m_currentMisspelled.clear(); m_currentMisspelledPosition = -1; } void SpellCheckMenu::setMisspelled(const QString &word, int position,int length) { m_currentMisspelled = word; m_lengthMisspelled=length; m_currentMisspelledPosition = position; } void SpellCheckMenu::setEnabled(bool b) { if (m_suggestionsMenuAction) m_suggestionsMenuAction->setEnabled(b); if (m_addToDictionaryAction) m_addToDictionaryAction->setEnabled(b); if (m_ignoreWordAction) m_ignoreWordAction->setEnabled(b); } void SpellCheckMenu::setVisible(bool b) { if (m_suggestionsMenuAction) m_suggestionsMenuAction->setVisible(b); if (m_addToDictionaryAction) m_addToDictionaryAction->setVisible(b); if (m_ignoreWordAction) m_ignoreWordAction->setVisible(b); } void SpellCheckMenu::replaceWord(const QString &suggestion) { if (suggestion.isEmpty() || m_currentMisspelledPosition < 0) return; m_spellCheck->replaceWordBySuggestion(suggestion, m_currentMisspelledPosition,m_lengthMisspelled); m_currentMisspelled.clear(); m_currentMisspelledPosition = -1; } void SpellCheckMenu::setCurrentLanguage(const QString &language) { m_speller.setLanguage(language); } diff --git a/plugins/textediting/spellcheck/SpellCheckMenu.h b/plugins/textediting/spellcheck/SpellCheckMenu.h index 633f391f78c..120c5680f84 100644 --- a/plugins/textediting/spellcheck/SpellCheckMenu.h +++ b/plugins/textediting/spellcheck/SpellCheckMenu.h @@ -1,72 +1,70 @@ /* This file is part of the KDE project * Copyright (C) 2010 Christoph Goerlich * Copyright (C) 2012 Shreya Pandit * * 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 SPELLCHECKMENU_H #define SPELLCHECKMENU_H #include #include #include class KActionMenu; class QAction; class QAction; class QMenu; -class QSignalMapper; class SpellCheck; class SpellCheckMenu : public QObject { Q_OBJECT public: explicit SpellCheckMenu(const Sonnet::Speller &speller, SpellCheck *spellCheck); ~SpellCheckMenu() override; QPair menuAction(); void setMisspelled(const QString &word, int position,int length); void setEnabled(bool b); void setVisible(bool b); void setCurrentLanguage(const QString &language); Q_SIGNALS: void clearHighlightingForWord(int startPosition); private Q_SLOTS: void createSuggestionsMenu(); void replaceWord(const QString &suggestion); void ignoreWord(); void addWordToDictionary(); private: SpellCheck *m_spellCheck; Sonnet::Speller m_speller; KActionMenu *m_suggestionsMenuAction; QAction *m_ignoreWordAction; QAction *m_addToDictionaryAction; QMenu *m_suggestionsMenu; int m_lengthMisspelled; - QSignalMapper *m_suggestionsSignalMapper; int m_currentMisspelledPosition; QString m_currentMisspelled; QStringList m_suggestions; }; #endif // SPELLCHECKMENU_H diff --git a/plugins/textshape/TextTool.cpp b/plugins/textshape/TextTool.cpp index 8a58425d886..798e1a76943 100644 --- a/plugins/textshape/TextTool.cpp +++ b/plugins/textshape/TextTool.cpp @@ -1,3154 +1,3150 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander * Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2008 Girish Ramakrishnan * Copyright (C) 2008, 2012 Pierre Stirnweiss * Copyright (C) 2009 KO GmbH * Copyright (C) 2011 Mojtaba Shahi Senobari * Copyright (C) 2014 Denis Kuplyakov * * 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 "TextTool.h" #include "TextEditingPluginContainer.h" #include "dialogs/SimpleCharacterWidget.h" #include "dialogs/SimpleParagraphWidget.h" #include "dialogs/SimpleTableWidget.h" #include "dialogs/SimpleInsertWidget.h" #include "dialogs/ParagraphSettingsDialog.h" #include "dialogs/StyleManagerDialog.h" #include "dialogs/InsertCharacter.h" #include "dialogs/FontDia.h" #include "dialogs/TableDialog.h" #include "dialogs/SectionFormatDialog.h" #include "dialogs/SectionsSplitDialog.h" #include "commands/AutoResizeCommand.h" #include "commands/ChangeListLevelCommand.h" #include "FontSizeAction.h" #include "FontFamilyAction.h" #include "TextShapeDebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include "AnnotationTextShape.h" #define AnnotationShape_SHAPEID "AnnotationTextShapeID" #include "KoShapeBasedDocumentBase.h" #include #include #include #include #include class TextToolSelection : public KoToolSelection { public: TextToolSelection(QPointer editor) : KoToolSelection(0) , m_editor(editor) { } bool hasSelection() override { if (!m_editor.isNull()) { return m_editor.data()->hasSelection(); } return false; } QPointer m_editor; }; static bool hit(const QKeySequence &input, KStandardShortcut::StandardShortcut shortcut) { foreach (const QKeySequence & ks, KStandardShortcut::shortcut(shortcut)) { if (input == ks) return true; } return false; } TextTool::TextTool(KoCanvasBase *canvas) : KoToolBase(canvas) , m_textShape(0) , m_textShapeData(0) , m_changeTracker(0) , m_allowActions(true) , m_allowAddUndoCommand(true) , m_allowResourceManagerUpdates(true) , m_prevCursorPosition(-1) , m_caretTimer(this) , m_caretTimerState(true) , m_currentCommand(0) , m_currentCommandHasChildren(false) , m_specialCharacterDocker(0) , m_textTyping(false) , m_textDeleting(false) , m_editTipTimer(this) , m_delayedEnsureVisible(false) , m_toolSelection(0) , m_tableDraggedOnce(false) , m_tablePenMode(false) , m_lastImMicroFocus(QRectF(0,0,0,0)) , m_drag(0) { setTextMode(true); createActions(); m_unit = canvas->resourceManager()->unitResource(KoCanvasResourceManager::Unit); foreach (KoTextEditingPlugin* plugin, textEditingPluginContainer()->values()) { connect(plugin, SIGNAL(startMacro(QString)), this, SLOT(startMacro(QString))); connect(plugin, SIGNAL(stopMacro()), this, SLOT(stopMacro())); const QHash actions = plugin->actions(); QHash::ConstIterator i = actions.begin(); while (i != actions.end()) { addAction(i.key(), i.value()); ++i; } } // setup the context list. - QSignalMapper *signalMapper = new QSignalMapper(this); - connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(startTextEditingPlugin(QString))); QList list; list.append(this->action("format_font")); foreach (const QString &key, KoTextEditingRegistry::instance()->keys()) { KoTextEditingFactory *factory = KoTextEditingRegistry::instance()->value(key); if (factory && factory->showInMenu()) { QAction *a = new QAction(factory->title(), this); - connect(a, SIGNAL(triggered()), signalMapper, SLOT(map())); - signalMapper->setMapping(a, factory->id()); + connect(a, &QAction::triggered, [this, factory] { startTextEditingPlugin(factory->id()); }); list.append(a); addAction(QString("apply_%1").arg(factory->id()), a); } } setPopupActionList(list); connect(canvas->shapeManager()->selection(), SIGNAL(selectionChanged()), this, SLOT(shapeAddedToCanvas())); m_caretTimer.setInterval(500); connect(&m_caretTimer, SIGNAL(timeout()), this, SLOT(blinkCaret())); m_editTipTimer.setInterval(500); m_editTipTimer.setSingleShot(true); connect(&m_editTipTimer, SIGNAL(timeout()), this, SLOT(showEditTip())); } void TextTool::createActions() { bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality) & KoCanvasResourceManager::NoAdvancedText); m_actionConfigureSection = new QAction(koIconNeededWithSubs("", "configure-text-section", "configure"), i18n("Configure current section"), this); addAction("configure_section", m_actionConfigureSection); connect(m_actionConfigureSection, SIGNAL(triggered(bool)), this, SLOT(configureSection())); m_actionInsertSection = new QAction(koIconNeededWithSubs("", "insert-text-section", "insert-text"), i18n("Insert new section"), this); addAction("insert_section", m_actionInsertSection); connect(m_actionInsertSection, SIGNAL(triggered(bool)), this, SLOT(insertNewSection())); m_actionSplitSections = new QAction(koIconNeededWithSubs("", "text-section-split", "split"), i18n("Insert paragraph between sections"), this); addAction("split_sections", m_actionSplitSections); connect(m_actionSplitSections, SIGNAL(triggered(bool)), this, SLOT(splitSections())); m_actionPasteAsText = new QAction(koIcon("edit-paste"), i18n("Paste As Text"), this); addAction("edit_paste_text", m_actionPasteAsText); m_actionPasteAsText->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V); connect(m_actionPasteAsText, SIGNAL(triggered(bool)), this, SLOT(pasteAsText())); m_actionFormatBold = new QAction(koIcon("format-text-bold"), i18n("Bold"), this); addAction("format_bold", m_actionFormatBold); m_actionFormatBold->setShortcut(Qt::CTRL + Qt::Key_B); m_actionFormatBold->setCheckable(true); connect(m_actionFormatBold, SIGNAL(triggered(bool)), this, SLOT(bold(bool))); m_actionFormatItalic = new QAction(koIcon("format-text-italic"), i18n("Italic"), this); addAction("format_italic", m_actionFormatItalic); m_actionFormatItalic->setShortcut(Qt::CTRL + Qt::Key_I); m_actionFormatItalic->setCheckable(true); connect(m_actionFormatItalic, SIGNAL(triggered(bool)), this, SLOT(italic(bool))); m_actionFormatUnderline = new QAction(koIcon("format-text-underline"), i18nc("Text formatting", "Underline"), this); addAction("format_underline", m_actionFormatUnderline); m_actionFormatUnderline->setShortcut(Qt::CTRL + Qt::Key_U); m_actionFormatUnderline->setCheckable(true); connect(m_actionFormatUnderline, SIGNAL(triggered(bool)), this, SLOT(underline(bool))); m_actionFormatStrikeOut = new QAction(koIcon("format-text-strikethrough"), i18n("Strikethrough"), this); addAction("format_strike", m_actionFormatStrikeOut); m_actionFormatStrikeOut->setCheckable(true); connect(m_actionFormatStrikeOut, SIGNAL(triggered(bool)), this, SLOT(strikeOut(bool))); QActionGroup *alignmentGroup = new QActionGroup(this); m_actionAlignLeft = new QAction(koIcon("format-justify-left"), i18n("Align Left"), this); addAction("format_alignleft", m_actionAlignLeft); m_actionAlignLeft->setShortcut(Qt::CTRL + Qt::Key_L); m_actionAlignLeft->setCheckable(true); alignmentGroup->addAction(m_actionAlignLeft); connect(m_actionAlignLeft, SIGNAL(triggered(bool)), this, SLOT(alignLeft())); m_actionAlignRight = new QAction(koIcon("format-justify-right"), i18n("Align Right"), this); addAction("format_alignright", m_actionAlignRight); m_actionAlignRight->setShortcut(Qt::CTRL + Qt::Key_R); m_actionAlignRight->setCheckable(true); alignmentGroup->addAction(m_actionAlignRight); connect(m_actionAlignRight, SIGNAL(triggered(bool)), this, SLOT(alignRight())); m_actionAlignCenter = new QAction(koIcon("format-justify-center"), i18n("Align Center"), this); addAction("format_aligncenter", m_actionAlignCenter); m_actionAlignCenter->setShortcut(Qt::CTRL + Qt::Key_E); m_actionAlignCenter->setCheckable(true); alignmentGroup->addAction(m_actionAlignCenter); connect(m_actionAlignCenter, SIGNAL(triggered(bool)), this, SLOT(alignCenter())); m_actionAlignBlock = new QAction(koIcon("format-justify-fill"), i18n("Align Block"), this); addAction("format_alignblock", m_actionAlignBlock); m_actionAlignBlock->setShortcut(Qt::CTRL + Qt::Key_J); m_actionAlignBlock->setCheckable(true); alignmentGroup->addAction(m_actionAlignBlock); connect(m_actionAlignBlock, SIGNAL(triggered(bool)), this, SLOT(alignBlock())); m_actionChangeDirection = new QAction(koIcon("format-text-direction-rtl"), i18n("Change text direction"), this); addAction("change_text_direction", m_actionChangeDirection); m_actionChangeDirection->setToolTip(i18n("Change writing direction")); m_actionChangeDirection->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D); m_actionChangeDirection->setCheckable(true); connect(m_actionChangeDirection, SIGNAL(triggered()), this, SLOT(textDirectionChanged())); m_actionFormatSuper = new QAction(koIcon("format-text-superscript"), i18n("Superscript"), this); m_actionFormatSuper->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_P); addAction("format_super", m_actionFormatSuper); m_actionFormatSuper->setCheckable(true); connect(m_actionFormatSuper, SIGNAL(triggered(bool)), this, SLOT(superScript(bool))); m_actionFormatSub = new QAction(koIcon("format-text-subscript"), i18n("Subscript"), this); m_actionFormatSub->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_B); addAction("format_sub", m_actionFormatSub); m_actionFormatSub->setCheckable(true); connect(m_actionFormatSub, SIGNAL(triggered(bool)), this, SLOT(subScript(bool))); const char* const increaseIndentActionIconName = QApplication::isRightToLeft() ? koIconNameCStr("format-indent-less") : koIconNameCStr("format-indent-more"); m_actionFormatIncreaseIndent = new QAction( QIcon::fromTheme(QLatin1String(increaseIndentActionIconName)), i18n("Increase Indent"), this); addAction("format_increaseindent", m_actionFormatIncreaseIndent); connect(m_actionFormatIncreaseIndent, SIGNAL(triggered()), this, SLOT(increaseIndent())); const char* const decreaseIndentActionIconName = QApplication::isRightToLeft() ? koIconNameCStr("format-indent-more") : koIconNameCStr("format-indent-less"); m_actionFormatDecreaseIndent = new QAction(QIcon::fromTheme(QLatin1String(decreaseIndentActionIconName)), i18n("Decrease Indent"), this); addAction("format_decreaseindent", m_actionFormatDecreaseIndent); connect(m_actionFormatDecreaseIndent, SIGNAL(triggered()), this, SLOT(decreaseIndent())); QAction *action = new QAction(koIcon("format-list-unordered"), i18n("Toggle List or List Level Formatting"), this); action->setToolTip(i18n("Toggle list on/off, or change format of current level")); addAction("format_list", action); action = new QAction(i18n("Increase Font Size"), this); action->setShortcut(Qt::CTRL + Qt::Key_Greater); addAction("fontsizeup", action); connect(action, SIGNAL(triggered()), this, SLOT(increaseFontSize())); action = new QAction(i18n("Decrease Font Size"), this); action->setShortcut(Qt::CTRL + Qt::Key_Less); addAction("fontsizedown", action); connect(action, SIGNAL(triggered()), this, SLOT(decreaseFontSize())); m_actionFormatFontFamily = new KoFontFamilyAction(this); m_actionFormatFontFamily->setText(i18n("Font Family")); addAction("format_fontfamily", m_actionFormatFontFamily); connect(m_actionFormatFontFamily, SIGNAL(triggered(QString)), this, SLOT(setFontFamily(QString))); m_variableMenu = new KActionMenu(i18n("Variable"), this); addAction("insert_variable", m_variableMenu); // ------------------- Actions with a key binding and no GUI item action = new QAction(i18n("Insert Non-Breaking Space"), this); addAction("nonbreaking_space", action); action->setShortcut(Qt::CTRL + Qt::Key_Space); connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingSpace())); action = new QAction(i18n("Insert Non-Breaking Hyphen"), this); addAction("nonbreaking_hyphen", action); action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Minus); connect(action, SIGNAL(triggered()), this, SLOT(nonbreakingHyphen())); action = new QAction(i18n("Insert Index"), this); action->setShortcut(Qt::CTRL + Qt::Key_T); addAction("insert_index", action); connect(action, SIGNAL(triggered()), this, SLOT(insertIndexMarker())); action = new QAction(i18n("Insert Soft Hyphen"), this); addAction("soft_hyphen", action); //action->setShortcut(Qt::CTRL + Qt::Key_Minus); // TODO this one is also used for the kde-global zoom-out :( connect(action, SIGNAL(triggered()), this, SLOT(softHyphen())); if (useAdvancedText) { action = new QAction(i18n("Line Break"), this); addAction("line_break", action); action->setShortcut(Qt::SHIFT + Qt::Key_Return); connect(action, SIGNAL(triggered()), this, SLOT(lineBreak())); action = new QAction(koIcon("insert-page-break"), i18n("Page Break"), this); addAction("insert_framebreak", action); action->setShortcut(Qt::CTRL + Qt::Key_Return); connect(action, SIGNAL(triggered()), this, SLOT(insertFrameBreak())); action->setToolTip(i18n("Insert a page break")); action->setWhatsThis(i18n("All text after this point will be moved into the next page.")); } action = new QAction(i18n("Font..."), this); addAction("format_font", action); action->setShortcut(Qt::ALT + Qt::CTRL + Qt::Key_F); action->setToolTip(i18n("Change character size, font, boldface, italics etc.")); action->setWhatsThis(i18n("Change the attributes of the currently selected characters.")); connect(action, SIGNAL(triggered()), this, SLOT(selectFont())); m_actionFormatFontSize = new FontSizeAction(i18n("Font Size"), this); addAction("format_fontsize", m_actionFormatFontSize); connect(m_actionFormatFontSize, SIGNAL(fontSizeChanged(qreal)), this, SLOT(setFontSize(qreal))); m_actionFormatTextColor = new KoColorPopupAction(this); m_actionFormatTextColor->setIcon(koIcon("format-text-color")); m_actionFormatTextColor->setToolTip(i18n("Text Color...")); m_actionFormatTextColor->setText(i18n("Text Color")); addAction("format_textcolor", m_actionFormatTextColor); connect(m_actionFormatTextColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setTextColor(KoColor))); m_actionFormatBackgroundColor = new KoColorPopupAction(this); m_actionFormatBackgroundColor->setIcon(koIcon("format-fill-color")); m_actionFormatBackgroundColor->setToolTip(i18n("Background Color...")); m_actionFormatBackgroundColor->setText(i18n("Background")); addAction("format_backgroundcolor", m_actionFormatBackgroundColor); connect(m_actionFormatBackgroundColor, SIGNAL(colorChanged(KoColor)), this, SLOT(setBackgroundColor(KoColor))); m_autoResizeAction = new QAction(koIcon("zoom-fit-best"), i18n("Auto Resize To Content"), this); addAction("auto_resize", m_autoResizeAction); m_autoResizeAction->setCheckable(true); connect(m_autoResizeAction, SIGNAL(triggered(bool)), this, SLOT(setAutoResize(bool))); m_growWidthAction = new QAction(koIcon("zoom-fit-best"), i18n("Grow To Fit Width"), this); addAction("grow_to_fit_width", m_growWidthAction); m_growWidthAction->setCheckable(true); connect(m_growWidthAction, SIGNAL(triggered(bool)), this, SLOT(setGrowWidthToFit(bool))); m_growHeightAction = new QAction(koIcon("zoom-fit-best"), i18n("Grow To Fit Height"), this); addAction("grow_to_fit_height", m_growHeightAction); m_growHeightAction->setCheckable(true); connect(m_growHeightAction, SIGNAL(triggered(bool)), this, SLOT(setGrowHeightToFit(bool))); m_shrinkToFitAction = new QAction(koIcon("zoom-fit-best"), i18n("Shrink To Fit"), this); addAction("shrink_to_fit", m_shrinkToFitAction); m_shrinkToFitAction->setCheckable(true); connect(m_shrinkToFitAction, SIGNAL(triggered(bool)), this, SLOT(setShrinkToFit(bool))); if (useAdvancedText) { action = new QAction(koIcon("insert-table"), i18n("Insert Custom..."), this); addAction("insert_table", action); action->setToolTip(i18n("Insert a table into the document.")); connect(action, SIGNAL(triggered()), this, SLOT(insertTable())); action = new QAction(koIcon("edit-table-insert-row-above"), i18n("Row Above"), this); action->setToolTip(i18n("Insert Row Above")); addAction("insert_tablerow_above", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowAbove())); action = new QAction(koIcon("edit-table-insert-row-below"), i18n("Row Below"), this); action->setToolTip(i18n("Insert Row Below")); addAction("insert_tablerow_below", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableRowBelow())); action = new QAction(koIcon("edit-table-insert-column-left"), i18n("Column Left"), this); action->setToolTip(i18n("Insert Column Left")); addAction("insert_tablecolumn_left", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnLeft())); action = new QAction(koIcon("edit-table-insert-column-right"), i18n("Column Right"), this); action->setToolTip(i18n("Insert Column Right")); addAction("insert_tablecolumn_right", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(insertTableColumnRight())); action = new QAction(koIcon("edit-table-delete-column"), i18n("Column"), this); action->setToolTip(i18n("Delete Column")); addAction("delete_tablecolumn", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableColumn())); action = new QAction(koIcon("edit-table-delete-row"), i18n("Row"), this); action->setToolTip(i18n("Delete Row")); addAction("delete_tablerow", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteTableRow())); action = new QAction(koIcon("edit-table-cell-merge"), i18n("Merge Cells"), this); addAction("merge_tablecells", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(mergeTableCells())); action = new QAction(koIcon("edit-table-cell-split"), i18n("Split Cells"), this); addAction("split_tablecells", action); connect(action, SIGNAL(triggered(bool)), this, SLOT(splitTableCells())); action = new QAction(koIcon("borderpainter"), "", this); action->setToolTip(i18n("Select a border style and paint that style onto a table")); addAction("activate_borderpainter", action); } action = new QAction(i18n("Paragraph..."), this); addAction("format_paragraph", action); action->setShortcut(Qt::ALT + Qt::CTRL + Qt::Key_P); action->setToolTip(i18n("Change paragraph margins, text flow, borders, bullets, numbering etc.")); action->setWhatsThis(i18n("

Change paragraph margins, text flow, borders, bullets, numbering etc.

Select text in multiple paragraphs to change the formatting of all selected paragraphs.

If no text is selected, the paragraph where the cursor is located will be changed.

")); connect(action, SIGNAL(triggered()), this, SLOT(formatParagraph())); action = new QAction(i18n("Style Manager..."), this); action->setShortcut(Qt::ALT + Qt::CTRL + Qt::Key_S); action->setToolTip(i18n("Change attributes of styles")); action->setWhatsThis(i18n("

Change font and paragraph attributes of styles.

Multiple styles can be changed using the dialog box.

")); addAction("format_stylist", action); connect(action, SIGNAL(triggered()), this, SLOT(showStyleManager())); action = KStandardAction::selectAll(this, SLOT(selectAll()), this); addAction("edit_select_all", action); action = new QAction(i18n("Special Character..."), this); action->setIcon(koIcon("character-set")); action->setShortcut(Qt::ALT + Qt::SHIFT + Qt::Key_C); addAction("insert_specialchar", action); action->setToolTip(i18n("Insert one or more symbols or characters not found on the keyboard")); action->setWhatsThis(i18n("Insert one or more symbols or characters not found on the keyboard.")); connect(action, SIGNAL(triggered()), this, SLOT(insertSpecialCharacter())); action = new QAction(i18n("Repaint"), this); action->setIcon(koIcon("view-refresh")); addAction("repaint", action); connect(action, SIGNAL(triggered()), this, SLOT(relayoutContent())); action = new QAction(i18n("Insert Comment"), this); addAction("insert_annotation", action); action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C); connect(action, SIGNAL(triggered()), this, SLOT(insertAnnotation())); #ifndef NDEBUG action = new QAction("Paragraph Debug", this); // do NOT add i18n! action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_P); addAction("detailed_debug_paragraphs", action); connect(action, SIGNAL(triggered()), this, SLOT(debugTextDocument())); action = new QAction("Styles Debug", this); // do NOT add i18n! action->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_S); addAction("detailed_debug_styles", action); connect(action, SIGNAL(triggered()), this, SLOT(debugTextStyles())); #endif } #ifndef NDEBUG #include "tests/MockShapes.h" #include #include #include TextTool::TextTool(MockCanvas *canvas) // constructor for our unit tests; : KoToolBase(canvas), m_textShape(0), m_textShapeData(0), m_changeTracker(0), m_allowActions(true), m_allowAddUndoCommand(true), m_allowResourceManagerUpdates(true), m_prevCursorPosition(-1), m_caretTimer(this), m_caretTimerState(true), m_currentCommand(0), m_currentCommandHasChildren(false), m_specialCharacterDocker(0), m_textEditingPlugins(0) , m_editTipTimer(this) , m_delayedEnsureVisible(false) , m_tableDraggedOnce(false) , m_tablePenMode(false) { // we could init some vars here, but we probably don't have to QLocale::setDefault(QLocale("en")); QTextDocument *document = new QTextDocument(); // this document is leaked KoInlineTextObjectManager *inlineManager = new KoInlineTextObjectManager(); KoTextDocument(document).setInlineTextObjectManager(inlineManager); KoTextRangeManager *locationManager = new KoTextRangeManager(); KoTextDocument(document).setTextRangeManager(locationManager); m_textEditor = new KoTextEditor(document); KoTextDocument(document).setTextEditor(m_textEditor.data()); m_toolSelection = new TextToolSelection(m_textEditor); m_changeTracker = new KoChangeTracker(); KoTextDocument(document).setChangeTracker(m_changeTracker); KoTextDocument(document).setUndoStack(new KUndo2Stack()); } #endif TextTool::~TextTool() { delete m_toolSelection; } void TextTool::showEditTip() { if (!m_textShapeData || m_editTipPointedAt.position == -1) return; QTextCursor c(m_textShapeData->document()); c.setPosition(m_editTipPointedAt.position); QString text = "

"; int toolTipWidth = 0; if (m_changeTracker && m_changeTracker->containsInlineChanges(c.charFormat()) && m_changeTracker->displayChanges()) { KoChangeTrackerElement *element = m_changeTracker->elementById(c.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt()); if (element->isEnabled()) { QString changeType; if (element->getChangeType() == KoGenChange::InsertChange) changeType = i18n("Insertion"); else if (element->getChangeType() == KoGenChange::DeleteChange) changeType = i18n("Deletion"); else changeType = i18n("Formatting"); text += "" + changeType + "
"; QString date = element->getDate(); //Remove the T which separates the Data and Time. date[10] = QLatin1Char(' '); date = element->getCreator() + QLatin1Char(' ') + date; text += date + "

"; toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(date).width(); } } if (m_editTipPointedAt.bookmark || !m_editTipPointedAt.externalHRef.isEmpty()) { QString help = i18n("Ctrl+click to go to link "); help += m_editTipPointedAt.externalHRef; text += help + "

"; toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width(); } if (m_editTipPointedAt.note) { QString help = i18n("Ctrl+click to go to the note "); text += help + "

"; toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width(); } if (m_editTipPointedAt.noteReference>0) { QString help = i18n("Ctrl+click to go to the note reference"); text += help + "

"; toolTipWidth = QFontMetrics(QToolTip::font()).boundingRect(help).width(); } QToolTip::hideText(); if (toolTipWidth) { QRect keepRect(m_editTipPos - QPoint(3,3), QSize(6,6)); QToolTip::showText(m_editTipPos - QPoint(toolTipWidth/2, 0), text, canvas()->canvasWidget(), keepRect); } } void TextTool::blinkCaret() { if (!(canvas()->canvasWidget() ? canvas()->canvasWidget()->hasFocus() : canvas()->canvasItem()->hasFocus())) { m_caretTimer.stop(); m_caretTimerState = false; // not visible. } else { m_caretTimerState = !m_caretTimerState; } repaintCaret(); } void TextTool::relayoutContent() { KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout()); Q_ASSERT(lay); foreach (KoTextLayoutRootArea *rootArea, lay->rootAreas()) { rootArea->setDirty(); } lay->emitLayoutIsDirty(); } void TextTool::paint(QPainter &painter, const KoViewConverter &converter) { if (m_textEditor.isNull()) return; if (canvas() && (( canvas()->canvasWidget() && canvas()->canvasWidget()->hasFocus()) || (canvas()->canvasItem() && canvas()->canvasItem()->hasFocus()) ) && !m_caretTimer.isActive()) { // make sure we blink m_caretTimer.start(); m_caretTimerState = true; } if (!m_caretTimerState) m_caretTimer.setInterval(500); // we set it lower during typing, so set it back to normal if (!m_textShapeData) return; if (m_textShapeData->isDirty()) return; qreal zoomX, zoomY; converter.zoom(&zoomX, &zoomY); painter.save(); QTransform shapeMatrix = m_textShape->absoluteTransformation(&converter); shapeMatrix.scale(zoomX, zoomY); shapeMatrix.translate(0, -m_textShapeData->documentOffset()); // Possibly draw table dragging visual cues const qreal boxHeight = 20; if (m_tableDragInfo.tableHit == KoPointedAt::ColumnDivider) { QPointF anchorPos = m_tableDragInfo.tableDividerPos - QPointF(m_dx, 0.0); if (m_tableDragInfo.tableColumnDivider > 0) { //let's draw left qreal w = m_tableDragInfo.tableLeadSize - m_dx; QRectF rect(anchorPos - QPointF(w, 0.0), QSizeF(w, 0.0)); QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight())); drawRect.setHeight(boxHeight); drawRect.moveTop(drawRect.top() - 1.5 * boxHeight); QString label = m_unit.toUserStringValue(w); int labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width(); painter.fillRect(drawRect, QColor(64, 255, 64, 196)); painter.setPen(QPen(QColor(0, 0, 0, 196), 0)); if (labelWidth + 10 < drawRect.width()) { QPointF centerLeft(drawRect.left(), drawRect.center().y()); QPointF centerRight(drawRect.right(), drawRect.center().y()); painter.drawLine(centerLeft, drawRect.center() - QPointF(labelWidth/2+5, 0.0)); painter.drawLine(centerLeft, centerLeft + QPointF(7, -5)); painter.drawLine(centerLeft, centerLeft + QPointF(7, 5)); painter.drawLine(drawRect.center() + QPointF(labelWidth/2+5, 0.0), centerRight); painter.drawLine(centerRight, centerRight + QPointF(-7, -5)); painter.drawLine(centerRight, centerRight + QPointF(-7, 5)); painter.drawText(drawRect, Qt::AlignCenter, label); } } if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()) { //let's draw right qreal w = m_tableDragInfo.tableTrailSize + m_dx; QRectF rect(anchorPos, QSizeF(w, 0.0)); QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight())); drawRect.setHeight(boxHeight); drawRect.moveTop(drawRect.top() - 1.5 * boxHeight); QString label; int labelWidth; if (m_tableDragWithShift) { label = i18n("follows along"); labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width(); drawRect.setWidth(2 * labelWidth); QLinearGradient g(drawRect.topLeft(), drawRect.topRight()); g.setColorAt(0.6, QColor(255, 64, 64, 196)); g.setColorAt(1.0, QColor(255, 64, 64, 0)); QBrush brush(g); painter.fillRect(drawRect, brush); } else { label = m_unit.toUserStringValue(w); labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width(); drawRect.setHeight(boxHeight); painter.fillRect(drawRect, QColor(64, 255, 64, 196)); } painter.setPen(QPen(QColor(0, 0, 0, 196), 0)); if (labelWidth + 10 < drawRect.width()) { QPointF centerLeft(drawRect.left(), drawRect.center().y()); QPointF centerRight(drawRect.right(), drawRect.center().y()); painter.drawLine(centerLeft, drawRect.center() - QPointF(labelWidth/2+5, 0.0)); painter.drawLine(centerLeft, centerLeft + QPointF(7, -5)); painter.drawLine(centerLeft, centerLeft + QPointF(7, 5)); if (!m_tableDragWithShift) { painter.drawLine(drawRect.center() + QPointF(labelWidth/2+5, 0.0), centerRight); painter.drawLine(centerRight, centerRight + QPointF(-7, -5)); painter.drawLine(centerRight, centerRight + QPointF(-7, 5)); } painter.drawText(drawRect, Qt::AlignCenter, label); } if (!m_tableDragWithShift) { // let's draw a helper text too label = i18n("Press shift to not resize this"); labelWidth = QFontMetrics(QToolTip::font()).boundingRect(label).width(); labelWidth += 10; //if (labelWidth < drawRect.width()) { drawRect.moveTop(drawRect.top() + boxHeight); drawRect.moveLeft(drawRect.left() + (drawRect.width() - labelWidth)/2); drawRect.setWidth(labelWidth); painter.fillRect(drawRect, QColor(64, 255, 64, 196)); painter.drawText(drawRect, Qt::AlignCenter, label); } } } } // Possibly draw table dragging visual cues if (m_tableDragInfo.tableHit == KoPointedAt::RowDivider) { QPointF anchorPos = m_tableDragInfo.tableDividerPos - QPointF(0.0, m_dy); if (m_tableDragInfo.tableRowDivider > 0) { qreal h = m_tableDragInfo.tableLeadSize - m_dy; QRectF rect(anchorPos - QPointF(0.0, h), QSizeF(0.0, h)); QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomRight())); drawRect.setWidth(boxHeight); drawRect.moveLeft(drawRect.left() - 1.5 * boxHeight); QString label = m_unit.toUserStringValue(h); QRectF labelRect = QFontMetrics(QToolTip::font()).boundingRect(label); labelRect.setHeight(boxHeight); labelRect.setWidth(labelRect.width() + 10); labelRect.moveTopLeft(drawRect.center() - QPointF(labelRect.width(), labelRect.height())/2); painter.fillRect(drawRect, QColor(64, 255, 64, 196)); painter.fillRect(labelRect, QColor(64, 255, 64, 196)); painter.setPen(QPen(QColor(0, 0, 0, 196), 0)); if (labelRect.height() + 10 < drawRect.height()) { QPointF centerTop(drawRect.center().x(), drawRect.top()); QPointF centerBottom(drawRect.center().x(), drawRect.bottom()); painter.drawLine(centerTop, drawRect.center() - QPointF(0.0, labelRect.height()/2+5)); painter.drawLine(centerTop, centerTop + QPointF(-5, 7)); painter.drawLine(centerTop, centerTop + QPointF(5, 7)); painter.drawLine(drawRect.center() + QPointF(0.0, labelRect.height()/2+5), centerBottom); painter.drawLine(centerBottom, centerBottom + QPointF(-5, -7)); painter.drawLine(centerBottom, centerBottom + QPointF(5, -7)); } painter.drawText(labelRect, Qt::AlignCenter, label); } } if (m_caretTimerState) { // Lets draw the caret ourselves, as the Qt method doesn't take cursor // charFormat into consideration. QTextBlock block = m_textEditor.data()->block(); if (block.isValid()) { int posInParag = m_textEditor.data()->position() - block.position(); if (posInParag <= block.layout()->preeditAreaPosition()) posInParag += block.layout()->preeditAreaText().length(); QTextLine tl = block.layout()->lineForTextPosition(m_textEditor.data()->position() - block.position()); if (tl.isValid()) { painter.setRenderHint(QPainter::Antialiasing, false); QRectF rect = caretRect(m_textEditor.data()->cursor()); QPointF baselinePoint; if (tl.ascent() > 0) { QFontMetricsF fm(m_textEditor.data()->charFormat().font(), painter.device()); rect.setY(rect.y() + tl.ascent() - qMin(tl.ascent(), fm.ascent())); rect.setHeight(qMin(tl.ascent(), fm.ascent()) + qMin(tl.descent(), fm.descent())); baselinePoint = QPoint(rect.x(), rect.y() + tl.ascent()); } else { //line only filled with characters-without-size (eg anchors) // layout will make sure line has height of block font QFontMetricsF fm(block.charFormat().font(), painter.device()); rect.setHeight(fm.ascent() + fm.descent()); baselinePoint = QPoint(rect.x(), rect.y() + fm.ascent()); } QRectF drawRect(shapeMatrix.map(rect.topLeft()), shapeMatrix.map(rect.bottomLeft())); drawRect.setWidth(2); painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination); if (m_textEditor.data()->isEditProtected(true)) { QRectF circleRect(shapeMatrix.map(baselinePoint),QSizeF(14, 14)); circleRect.translate(-6.5, -6.5); QPen pen(QColor(16, 255, 255)); pen.setWidthF(2.0); painter.setPen(pen); painter.setRenderHint(QPainter::Antialiasing, true); painter.drawEllipse(circleRect); painter.drawLine(circleRect.topLeft() + QPointF(4.5,4.5), circleRect.bottomRight() - QPointF(4.5,4.5)); } else { painter.fillRect(drawRect, QColor(128, 255, 128)); } } } } painter.restore(); } void TextTool::updateSelectedShape(const QPointF &point, bool noDocumentChange) { QRectF area(point, QSizeF(1, 1)); if (m_textEditor.data()->hasSelection()) repaintSelection(); else repaintCaret(); QList sortedShapes = canvas()->shapeManager()->shapesAt(area, true); std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex); for (int count = sortedShapes.count() - 1; count >= 0; count--) { KoShape *shape = sortedShapes.at(count); if (shape->isContentProtected()) continue; TextShape *textShape = dynamic_cast(shape); if (textShape) { if (textShape != m_textShape) { if (static_cast(textShape->userData())->document() != m_textShapeData->document()) { //we should only change to another document if allowed if (noDocumentChange) { return; } // if we change to another textdocument we need to remove selection in old document // or it would continue to be painted etc m_textEditor.data()->setPosition(m_textEditor.data()->position()); } m_textShape = textShape; setShapeData(static_cast(m_textShape->userData())); // This is how we inform the rulers of the active range // For now we will not consider table cells, but just give the shape dimensions QVariant v; QRectF rect(QPoint(), m_textShape->size()); rect = m_textShape->absoluteTransformation(0).mapRect(rect); v.setValue(rect); canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, v); } return; } } } void TextTool::mousePressEvent(KoPointerEvent *event) { if (m_textEditor.isNull()) return; // request the software keyboard, if any if (event->button() == Qt::LeftButton && qApp->autoSipEnabled()) { QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(qApp->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel)); // the two following bools just make it all a lot easier to read in the following if() // basically, we require a widget for this to work (passing nullptr to QApplication::sendEvent // crashes) and there are three tests any one of which can be true to trigger the event const bool hasWidget = canvas()->canvasWidget(); const bool hasItem = canvas()->canvasItem(); if ((behavior == QStyle::RSIP_OnMouseClick && (hasWidget || hasItem)) || (hasWidget && canvas()->canvasWidget()->hasFocus()) || (hasItem && canvas()->canvasItem()->hasFocus())) { QEvent event(QEvent::RequestSoftwareInputPanel); if (hasWidget) { QApplication::sendEvent(canvas()->canvasWidget(), &event); } else { QApplication::sendEvent(canvas()->canvasItem(), &event); } } } bool shiftPressed = event->modifiers() & Qt::ShiftModifier; updateSelectedShape(event->point, shiftPressed); KoSelection *selection = canvas()->shapeManager()->selection(); if (m_textShape && !selection->isSelected(m_textShape) && m_textShape->isSelectable()) { selection->deselectAll(); selection->select(m_textShape); } KoPointedAt pointedAt = hitTest(event->point); m_tableDraggedOnce = false; m_clickWithinSelection = false; if (pointedAt.position != -1) { m_tablePenMode = false; if ((event->button() == Qt::LeftButton) && !shiftPressed && m_textEditor.data()->hasSelection() && m_textEditor.data()->isWithinSelection(pointedAt.position)) { m_clickWithinSelection = true; m_draggingOrigin = event->pos(); //we store the pixel pos } else if (! (event->button() == Qt::RightButton && m_textEditor.data()->hasSelection() && m_textEditor.data()->isWithinSelection(pointedAt.position))) { m_textEditor.data()->setPosition(pointedAt.position, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); useCursor(Qt::IBeamCursor); } m_tableDragInfo.tableHit = KoPointedAt::None; if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw) m_caretTimer.stop(); m_caretTimer.setInterval(50); m_caretTimer.start(); m_caretTimerState = true; // turn caret instantly on on click } } else { if (event->button() == Qt::RightButton) { m_tablePenMode = false; KoTextEditingPlugin *plugin = textEditingPluginContainer()->spellcheck(); if (plugin) plugin->setCurrentCursorPosition(m_textShapeData->document(), -1); event->ignore(); } else if (m_tablePenMode) { m_textEditor.data()->beginEditBlock(kundo2_i18n("Change Border Formatting")); if (pointedAt.tableHit == KoPointedAt::ColumnDivider) { if (pointedAt.tableColumnDivider < pointedAt.table->columns()) { m_textEditor.data()->setTableBorderData(pointedAt.table, pointedAt.tableRowDivider, pointedAt.tableColumnDivider, KoBorder::LeftBorder, m_tablePenBorderData); } if (pointedAt.tableColumnDivider > 0) { m_textEditor.data()->setTableBorderData(pointedAt.table, pointedAt.tableRowDivider, pointedAt.tableColumnDivider - 1, KoBorder::RightBorder, m_tablePenBorderData); } } else if (pointedAt.tableHit == KoPointedAt::RowDivider) { if (pointedAt.tableRowDivider < pointedAt.table->rows()) { m_textEditor.data()->setTableBorderData(pointedAt.table, pointedAt.tableRowDivider, pointedAt.tableColumnDivider, KoBorder::TopBorder, m_tablePenBorderData); } if (pointedAt.tableRowDivider > 0) { m_textEditor.data()->setTableBorderData(pointedAt.table, pointedAt.tableRowDivider-1, pointedAt.tableColumnDivider, KoBorder::BottomBorder, m_tablePenBorderData); } } m_textEditor.data()->endEditBlock(); } else { m_tableDragInfo = pointedAt; m_tablePenMode = false; } return; } if (shiftPressed) // altered selection. repaintSelection(); else repaintCaret(); updateSelectionHandler(); updateStyleManager(); updateActions(); //activate context-menu for spelling-suggestions if (event->button() == Qt::RightButton) { KoTextEditingPlugin *plugin = textEditingPluginContainer()->spellcheck(); if (plugin) plugin->setCurrentCursorPosition(m_textShapeData->document(), m_textEditor.data()->position()); event->ignore(); } if (event->button() == Qt::MidButton) { // Paste const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Selection); // on windows we do not have data if we try to paste this selection if (data) { m_prevCursorPosition = m_textEditor.data()->position(); m_textEditor.data()->paste(canvas(), data, canvas()->resourceManager()); editingPluginEvents(); } } } void TextTool::setShapeData(KoTextShapeData *data) { bool docChanged = !data || !m_textShapeData || m_textShapeData->document() != data->document(); if (m_textShapeData) { disconnect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved())); } m_textShapeData = data; if (!m_textShapeData) return; connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved())); if (docChanged) { if (!m_textEditor.isNull()) { disconnect(m_textEditor.data(), SIGNAL(textFormatChanged()), this, SLOT(updateActions())); } m_textEditor = KoTextDocument(m_textShapeData->document()).textEditor(); Q_ASSERT(m_textEditor.data()); if (!m_toolSelection) { m_toolSelection = new TextToolSelection(m_textEditor.data()); } else { m_toolSelection->m_editor = m_textEditor.data(); } m_variableMenu->menu()->clear(); KoTextDocument document(m_textShapeData->document()); foreach (QAction *action, document.inlineTextObjectManager()->createInsertVariableActions(canvas())) { m_variableMenu->addAction(action); connect(action, SIGNAL(triggered()), this, SLOT(returnFocusToCanvas())); } connect(m_textEditor.data(), SIGNAL(textFormatChanged()), this, SLOT(updateActions())); updateActions(); } } void TextTool::updateSelectionHandler() { if (m_textEditor) { emit selectionChanged(m_textEditor.data()->hasSelection()); if (m_textEditor.data()->hasSelection()) { QClipboard *clipboard = QApplication::clipboard(); if (clipboard->supportsSelection()) clipboard->setText(m_textEditor.data()->selectedText(), QClipboard::Selection); } } KoCanvasResourceManager *p = canvas()->resourceManager(); m_allowResourceManagerUpdates = false; if (m_textEditor && m_textShapeData) { p->setResource(KoText::CurrentTextPosition, m_textEditor.data()->position()); p->setResource(KoText::CurrentTextAnchor, m_textEditor.data()->anchor()); QVariant variant; variant.setValue(m_textShapeData->document()); p->setResource(KoText::CurrentTextDocument, variant); } else { p->clearResource(KoText::CurrentTextPosition); p->clearResource(KoText::CurrentTextAnchor); p->clearResource(KoText::CurrentTextDocument); } m_allowResourceManagerUpdates = true; } QMimeData *TextTool::generateMimeData() const { if (!m_textShapeData || m_textEditor.isNull() || !m_textEditor.data()->hasSelection()) return 0; int from = m_textEditor.data()->position(); int to = m_textEditor.data()->anchor(); KoTextOdfSaveHelper saveHelper(m_textShapeData->document(), from, to); KoTextDrag drag; #ifdef SHOULD_BUILD_RDF KoDocumentResourceManager *rm = 0; if (canvas()->shapeController()) { rm = canvas()->shapeController()->resourceManager(); } if (rm && rm->hasResource(KoText::DocumentRdf)) { KoDocumentRdfBase *rdf = qobject_cast(rm->resource(KoText::DocumentRdf).value()); if (rdf) { saveHelper.setRdfModel(rdf->model()); } } #endif drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper); QTextDocumentFragment fragment = m_textEditor.data()->selection(); drag.setData("text/plain", fragment.toPlainText().toUtf8()); return drag.takeMimeData(); } TextEditingPluginContainer *TextTool::textEditingPluginContainer() { m_textEditingPlugins = canvas()->resourceManager()-> resource(TextEditingPluginContainer::ResourceId).value(); if (m_textEditingPlugins == 0) { m_textEditingPlugins = new TextEditingPluginContainer(canvas()->resourceManager()); QVariant variant; variant.setValue(m_textEditingPlugins.data()); canvas()->resourceManager()->setResource(TextEditingPluginContainer::ResourceId, variant); foreach (KoTextEditingPlugin* plugin, m_textEditingPlugins->values()) { connect(plugin, SIGNAL(startMacro(QString)), this, SLOT(startMacro(QString))); connect(plugin, SIGNAL(stopMacro()), this, SLOT(stopMacro())); const QHash actions = plugin->actions(); QHash::ConstIterator i = actions.begin(); while (i != actions.end()) { addAction(i.key(), i.value()); ++i; } } } return m_textEditingPlugins; } void TextTool::copy() const { QMimeData *mimeData = generateMimeData(); if (mimeData) { QApplication::clipboard()->setMimeData(mimeData); } } void TextTool::deleteSelection() { m_textEditor.data()->deleteChar(); editingPluginEvents(); } bool TextTool::paste() { const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Clipboard); // on windows we do not have data if we try to paste the selection if (!data) return false; // since this is not paste-as-text we will not paste in urls, but instead let KoToolProxy solve it if (data->hasUrls()) return false; if (data->hasFormat(KoOdf::mimeType(KoOdf::Text)) || data->hasText()) { m_prevCursorPosition = m_textEditor.data()->position(); m_textEditor.data()->paste(canvas(), data); editingPluginEvents(); return true; } return false; } void TextTool::cut() { if (m_textEditor.data()->hasSelection()) { copy(); KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Cut")); m_textEditor.data()->deleteChar(false, topCmd); m_textEditor.data()->endEditBlock(); } } QStringList TextTool::supportedPasteMimeTypes() const { QStringList list; list << "text/plain" << "application/vnd.oasis.opendocument.text"; return list; } void TextTool::dragMoveEvent(QDragMoveEvent *event, const QPointF &point) { if (event->mimeData()->hasFormat(KoOdf::mimeType(KoOdf::Text)) || event->mimeData()->hasFormat(KoOdf::mimeType(KoOdf::OpenOfficeClipboard)) || event->mimeData()->hasText()) { if (m_drag) { event->setDropAction(Qt::MoveAction); event->accept(); } else if (event->proposedAction() == Qt::CopyAction) { event->acceptProposedAction(); } else { event->ignore(); return; } KoPointedAt pointedAt = hitTest(point); if (pointedAt.position == -1) { event->ignore(); } if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw) m_caretTimer.stop(); m_caretTimer.setInterval(50); m_caretTimer.start(); m_caretTimerState = true; // turn caret instantly on on click } if (m_preDragSelection.cursor.isNull()) { repaintSelection(); m_preDragSelection.cursor = QTextCursor(*m_textEditor.data()->cursor()); if (m_drag) { // Make a selection that looks like the current cursor selection // so we can move the real caret around freely QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections(); m_preDragSelection.format = QTextCharFormat(); m_preDragSelection.format.setBackground(qApp->palette().brush(QPalette::Highlight)); m_preDragSelection.format.setForeground(qApp->palette().brush(QPalette::HighlightedText)); sels.append(m_preDragSelection); KoTextDocument(m_textShapeData->document()).setSelections(sels); } // else we want the selection to disappear } repaintCaret(); // will erase caret m_textEditor.data()->setPosition(pointedAt.position); repaintCaret(); // will paint caret in new spot // Selection has visually not appeared at a new spot so no need to repaint it } } void TextTool::dragLeaveEvent(QDragLeaveEvent *event) { if (m_drag) { // restore the old selections QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections(); sels.pop_back(); KoTextDocument(m_textShapeData->document()).setSelections(sels); } repaintCaret(); // will erase caret in old spot m_textEditor.data()->setPosition(m_preDragSelection.cursor.anchor()); m_textEditor.data()->setPosition(m_preDragSelection.cursor.position(), QTextCursor::KeepAnchor); repaintCaret(); // will paint caret in new spot if (!m_drag) { repaintSelection(); // will paint selection again } // mark that we now are back to normal selection m_preDragSelection.cursor = QTextCursor(); event->accept(); } void TextTool::dropEvent(QDropEvent *event, const QPointF &) { if (m_drag) { // restore the old selections QVector< QAbstractTextDocumentLayout::Selection > sels = KoTextDocument(m_textShapeData->document()).selections(); sels.pop_back(); KoTextDocument(m_textShapeData->document()).setSelections(sels); } QTextCursor insertCursor(*m_textEditor.data()->cursor()); m_textEditor.data()->setPosition(m_preDragSelection.cursor.anchor()); m_textEditor.data()->setPosition(m_preDragSelection.cursor.position(), QTextCursor::KeepAnchor); repaintSelection(); // will erase the selection in new spot if (m_drag) { m_textEditor.data()->deleteChar(); } m_prevCursorPosition = insertCursor.position(); m_textEditor.data()->setPosition(m_prevCursorPosition); m_textEditor.data()->paste(canvas(), event->mimeData()); m_textEditor.data()->setPosition(m_prevCursorPosition); //since the paste made insertCursor we can now use that for the end position m_textEditor.data()->setPosition(insertCursor.position(), QTextCursor::KeepAnchor); // mark that we no are back to normal selection m_preDragSelection.cursor = QTextCursor(); event->accept(); } KoPointedAt TextTool::hitTest(const QPointF & point) const { if (!m_textShape || !m_textShapeData) { return KoPointedAt(); } QPointF p = m_textShape->convertScreenPos(point); KoTextLayoutRootArea *rootArea = m_textShapeData->rootArea(); return rootArea ? rootArea->hitTest(p, Qt::FuzzyHit) : KoPointedAt(); } void TextTool::mouseDoubleClickEvent(KoPointerEvent *event) { if (canvas()->shapeManager()->shapeAt(event->point) != m_textShape) { event->ignore(); // allow the event to be used by another return; } if (event->modifiers() & Qt::ShiftModifier) { // When whift is pressed we behave as a single press return mousePressEvent(event); } m_textEditor.data()->select(QTextCursor::WordUnderCursor); m_clickWithinSelection = false; repaintSelection(); updateSelectionHandler(); } void TextTool::mouseTripleClickEvent(KoPointerEvent *event) { if (canvas()->shapeManager()->shapeAt(event->point) != m_textShape) { event->ignore(); // allow the event to be used by another return; } if (event->modifiers() & Qt::ShiftModifier) { // When whift is pressed we behave as a single press return mousePressEvent(event); } m_textEditor.data()->clearSelection(); m_textEditor.data()->movePosition(QTextCursor::StartOfBlock); m_textEditor.data()->movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); m_clickWithinSelection = false; repaintSelection(); updateSelectionHandler(); } void TextTool::mouseMoveEvent(KoPointerEvent *event) { m_editTipPos = event->globalPos(); if (event->buttons()) { updateSelectedShape(event->point, true); } m_editTipTimer.stop(); if (QToolTip::isVisible()) QToolTip::hideText(); KoPointedAt pointedAt = hitTest(event->point); if (event->buttons() == Qt::NoButton) { if (m_tablePenMode) { if (pointedAt.tableHit == KoPointedAt::ColumnDivider || pointedAt.tableHit == KoPointedAt::RowDivider) { useTableBorderCursor(); } else { useCursor(Qt::IBeamCursor); } // do nothing else return; } if (!m_textShapeData || pointedAt.position < 0) { if (pointedAt.tableHit == KoPointedAt::ColumnDivider) { useCursor(Qt::SplitHCursor); m_draggingOrigin = event->point; } else if (pointedAt.tableHit == KoPointedAt::RowDivider) { if (pointedAt.tableRowDivider > 0) { useCursor(Qt::SplitVCursor); m_draggingOrigin = event->point; } else useCursor(Qt::IBeamCursor); } else { useCursor(Qt::IBeamCursor); } return; } QTextCursor mouseOver(m_textShapeData->document()); mouseOver.setPosition(pointedAt.position); if (m_changeTracker && m_changeTracker->containsInlineChanges(mouseOver.charFormat())) { m_editTipPointedAt = pointedAt; if (QToolTip::isVisible()) { QTimer::singleShot(0, this, SLOT(showEditTip())); }else { m_editTipTimer.start(); } } if ((pointedAt.bookmark || !pointedAt.externalHRef.isEmpty()) || pointedAt.note || (pointedAt.noteReference>0)) { if (event->modifiers() & Qt::ControlModifier) { useCursor(Qt::PointingHandCursor); } m_editTipPointedAt = pointedAt; if (QToolTip::isVisible()) { QTimer::singleShot(0, this, SLOT(showEditTip())); }else { m_editTipTimer.start(); } return; } // check if mouse pointer is over shape with hyperlink KoShape *selectedShape = canvas()->shapeManager()->shapeAt(event->point); if (selectedShape != 0 && selectedShape != m_textShape && selectedShape->hyperLink().size() != 0) { useCursor(Qt::PointingHandCursor); return; } useCursor(Qt::IBeamCursor); // Set Arrow Cursor when mouse is on top of annotation shape. if (selectedShape) { if (selectedShape->shapeId() == "AnnotationTextShapeID") { QPointF point(event->point); if (point.y() <= (selectedShape->position().y() + 25)) useCursor(Qt::ArrowCursor); } } return; } else { if (m_tableDragInfo.tableHit == KoPointedAt::ColumnDivider) { m_tableDragWithShift = event->modifiers() & Qt::ShiftModifier; if(m_tableDraggedOnce) { canvas()->shapeController()->resourceManager()->undoStack()->undo(); } KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Adjust Column Width")); m_dx = m_draggingOrigin.x() - event->point.x(); if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns() && m_tableDragInfo.tableTrailSize + m_dx < 0) { m_dx = -m_tableDragInfo.tableTrailSize; } if (m_tableDragInfo.tableColumnDivider > 0) { if (m_tableDragInfo.tableLeadSize - m_dx < 0) { m_dx = m_tableDragInfo.tableLeadSize; } m_textEditor.data()->adjustTableColumnWidth(m_tableDragInfo.table, m_tableDragInfo.tableColumnDivider - 1, m_tableDragInfo.tableLeadSize - m_dx, topCmd); } else { m_textEditor.data()->adjustTableWidth(m_tableDragInfo.table, -m_dx, 0.0); } if (m_tableDragInfo.tableColumnDivider < m_tableDragInfo.table->columns()) { if (!m_tableDragWithShift) { m_textEditor.data()->adjustTableColumnWidth(m_tableDragInfo.table, m_tableDragInfo.tableColumnDivider, m_tableDragInfo.tableTrailSize + m_dx, topCmd); } } else { m_tableDragWithShift = true; // act like shift pressed } if (m_tableDragWithShift) { m_textEditor.data()->adjustTableWidth(m_tableDragInfo.table, 0.0, m_dx); } m_textEditor.data()->endEditBlock(); m_tableDragInfo.tableDividerPos.setY(m_textShape->convertScreenPos(event->point).y()); if (m_tableDraggedOnce) { //we need to redraw like this so we update outside the textshape too if (canvas()->canvasWidget()) canvas()->canvasWidget()->update(); if (canvas()->canvasItem()) canvas()->canvasItem()->update(); } m_tableDraggedOnce = true; } else if (m_tableDragInfo.tableHit == KoPointedAt::RowDivider) { if(m_tableDraggedOnce) { canvas()->shapeController()->resourceManager()->undoStack()->undo(); } if (m_tableDragInfo.tableRowDivider > 0) { KUndo2Command *topCmd = m_textEditor.data()->beginEditBlock(kundo2_i18n("Adjust Row Height")); m_dy = m_draggingOrigin.y() - event->point.y(); if (m_tableDragInfo.tableLeadSize - m_dy < 0) { m_dy = m_tableDragInfo.tableLeadSize; } m_textEditor.data()->adjustTableRowHeight(m_tableDragInfo.table, m_tableDragInfo.tableRowDivider - 1, m_tableDragInfo.tableLeadSize - m_dy, topCmd); m_textEditor.data()->endEditBlock(); m_tableDragInfo.tableDividerPos.setX(m_textShape->convertScreenPos(event->point).x()); if (m_tableDraggedOnce) { //we need to redraw like this so we update outside the textshape too if (canvas()->canvasWidget()) canvas()->canvasWidget()->update(); if (canvas()->canvasItem()) canvas()->canvasItem()->update(); } m_tableDraggedOnce = true; } } else if (m_tablePenMode) { // do nothing } else if (m_clickWithinSelection) { if (!m_drag && (event->pos() - m_draggingOrigin).manhattanLength() >= QApplication::startDragDistance()) { QMimeData *mimeData = generateMimeData(); if (mimeData) { m_drag = new QDrag(canvas()->canvasWidget()); m_drag->setMimeData(mimeData); m_drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction); m_drag = 0; } } } else { useCursor(Qt::IBeamCursor); if (pointedAt.position == m_textEditor.data()->position()) return; if (pointedAt.position >= 0) { if (m_textEditor.data()->hasSelection()) repaintSelection(); // will erase selection else repaintCaret(); m_textEditor.data()->setPosition(pointedAt.position, QTextCursor::KeepAnchor); if (m_textEditor.data()->hasSelection()) repaintSelection(); else repaintCaret(); } } updateSelectionHandler(); } } void TextTool::mouseReleaseEvent(KoPointerEvent *event) { event->ignore(); editingPluginEvents(); m_tableDragInfo.tableHit = KoPointedAt::None; if (m_tableDraggedOnce) { m_tableDraggedOnce = false; //we need to redraw like this so we update outside the textshape too if (canvas()->canvasWidget()) canvas()->canvasWidget()->update(); if (canvas()->canvasItem()) canvas()->canvasItem()->update(); } if (!m_textShapeData) return; // check if mouse pointer is not over some shape with hyperlink KoShape *selectedShape = canvas()->shapeManager()->shapeAt(event->point); if (selectedShape != 0 && selectedShape != m_textShape && selectedShape->hyperLink().size() != 0) { QString url = selectedShape->hyperLink(); runUrl(event, url); return; } KoPointedAt pointedAt = hitTest(event->point); if (m_clickWithinSelection && !m_drag) { if (m_caretTimer.isActive()) { // make the caret not blink, (blinks again after first draw) m_caretTimer.stop(); m_caretTimer.setInterval(50); m_caretTimer.start(); m_caretTimerState = true; // turn caret instantly on on click } repaintCaret(); // will erase caret repaintSelection(); // will erase selection m_textEditor.data()->setPosition(pointedAt.position); repaintCaret(); // will paint caret in new spot } // Is there an anchor here ? if ((event->modifiers() & Qt::ControlModifier) && !m_textEditor.data()->hasSelection()) { if (pointedAt.bookmark) { m_textEditor.data()->setPosition(pointedAt.bookmark->rangeStart()); ensureCursorVisible(); event->accept(); return; } if (pointedAt.note) { m_textEditor.data()->setPosition(pointedAt.note->textFrame()->firstPosition()); ensureCursorVisible(); event->accept(); return; } if (pointedAt.noteReference>0) { m_textEditor.data()->setPosition(pointedAt.noteReference); ensureCursorVisible(); event->accept(); return; } if (!pointedAt.externalHRef.isEmpty()) { runUrl(event, pointedAt.externalHRef); } } } void TextTool::shortcutOverrideEvent(QKeyEvent *event) { QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers())); if (hit(item, KStandardShortcut::Begin) || hit(item, KStandardShortcut::End)) { event->accept(); } } void TextTool::keyPressEvent(QKeyEvent *event) { int destinationPosition = -1; // for those cases where the moveOperation is not relevant; QTextCursor::MoveOperation moveOperation = QTextCursor::NoMove; KoTextEditor *textEditor = m_textEditor.data(); m_tablePenMode = false; // keypress always stops the table (border) pen mode Q_ASSERT(textEditor); if (event->key() == Qt::Key_Backspace) { if (!textEditor->hasSelection() && textEditor->block().textList() && (textEditor->position() == textEditor->block().position()) && !(m_changeTracker && m_changeTracker->recordChanges())) { if (!textEditor->blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) { // backspace at beginning of numbered list item, makes it unnumbered textEditor->toggleListNumbering(false); } else { KoListLevelProperties llp; llp.setLabelType(KoListStyle::None); llp.setLevel(0); // backspace on numbered, empty parag, removes numbering. textEditor->setListProperties(llp); } } else if (textEditor->position() > 0 || textEditor->hasSelection()) { if (!textEditor->hasSelection() && event->modifiers() & Qt::ControlModifier) { // delete prev word. textEditor->movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); } textEditor->deletePreviousChar(); editingPluginEvents(); } } else if ((event->key() == Qt::Key_Tab) && ((!textEditor->hasSelection() && (textEditor->position() == textEditor->block().position())) || (textEditor->block().document()->findBlock(textEditor->anchor()) != textEditor->block().document()->findBlock(textEditor->position()))) && textEditor->block().textList()) { ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::IncreaseLevel; ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, 1); textEditor->addCommand(cll); editingPluginEvents(); } else if ((event->key() == Qt::Key_Backtab) && ((!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())) { ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::DecreaseLevel; ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, 1); textEditor->addCommand(cll); editingPluginEvents(); } else if (event->key() == Qt::Key_Delete) { if (!textEditor->hasSelection() && event->modifiers() & Qt::ControlModifier) {// delete next word. textEditor->movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor); } // the event only gets through when the Del is not used in the app // if the app forwards Del then deleteSelection is used textEditor->deleteChar(); editingPluginEvents(); } else if ((event->key() == Qt::Key_Left) && (event->modifiers() & Qt::ControlModifier) == 0) { moveOperation = QTextCursor::Left; } else if ((event->key() == Qt::Key_Right) && (event->modifiers() & Qt::ControlModifier) == 0) { moveOperation = QTextCursor::Right; } else if ((event->key() == Qt::Key_Up) && (event->modifiers() & Qt::ControlModifier) == 0) { moveOperation = QTextCursor::Up; } else if ((event->key() == Qt::Key_Down) && (event->modifiers() & Qt::ControlModifier) == 0) { moveOperation = QTextCursor::Down; } else { // check for shortcuts. QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers())); if (hit(item, KStandardShortcut::Begin)) { // Goto beginning of the document. Default: Ctrl-Home destinationPosition = 0; } else if (hit(item, KStandardShortcut::End)) { // Goto end of the document. Default: Ctrl-End if (m_textShapeData) { QTextBlock last = m_textShapeData->document()->lastBlock(); destinationPosition = last.position() + last.length() - 1; } } else if (hit(item, KStandardShortcut::Prior)) { // page up // Scroll up one page. Default: Prior event->ignore(); // let app level actions handle it return; } else if (hit(item, KStandardShortcut::Next)) { // Scroll down one page. Default: Next event->ignore(); // let app level actions handle it return; } else if (hit(item, KStandardShortcut::BeginningOfLine)) // Goto beginning of current line. Default: Home moveOperation = QTextCursor::StartOfLine; else if (hit(item, KStandardShortcut::EndOfLine)) // Goto end of current line. Default: End moveOperation = QTextCursor::EndOfLine; else if (hit(item, KStandardShortcut::BackwardWord)) moveOperation = QTextCursor::WordLeft; else if (hit(item, KStandardShortcut::ForwardWord)) moveOperation = QTextCursor::WordRight; #ifdef Q_WS_MAC // Don't reject "alt" key, it may be used for typing text on Mac OS else if ((event->modifiers() & Qt::ControlModifier) #else else if ((event->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) #endif || event->text().length() == 0 || event->key() == Qt::Key_Escape) { event->ignore(); return; } else if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) { m_prevCursorPosition = textEditor->position(); textEditor->newLine(); updateActions(); editingPluginEvents(); } else if ((event->key() == Qt::Key_Tab || !(event->text().length() == 1 && !event->text().at(0).isPrint()))) { // insert the text m_prevCursorPosition = textEditor->position(); startingSimpleEdit(); //signal editing plugins that this is a simple edit textEditor->insertText(event->text()); editingPluginEvents(); } } if (moveOperation != QTextCursor::NoMove || destinationPosition != -1) { useCursor(Qt::BlankCursor); bool shiftPressed = event->modifiers() & Qt::ShiftModifier; if (textEditor->hasSelection()) repaintSelection(); // will erase selection else repaintCaret(); QTextBlockFormat format = textEditor->blockFormat(); KoText::Direction dir = static_cast(format.intProperty(KoParagraphStyle::TextProgressionDirection)); bool isRtl; if (dir == KoText::AutoDirection) isRtl = textEditor->block().text().isRightToLeft(); else isRtl = dir == KoText::RightLeftTopBottom; if (isRtl) { // if RTL toggle direction of cursor movement. switch (moveOperation) { case QTextCursor::Left: moveOperation = QTextCursor::Right; break; case QTextCursor::Right: moveOperation = QTextCursor::Left; break; case QTextCursor::WordRight: moveOperation = QTextCursor::WordLeft; break; case QTextCursor::WordLeft: moveOperation = QTextCursor::WordRight; break; default: break; } } int prevPosition = textEditor->position(); if (moveOperation != QTextCursor::NoMove) textEditor->movePosition(moveOperation, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); else textEditor->setPosition(destinationPosition, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); if (moveOperation == QTextCursor::Down && prevPosition == textEditor->position()) { // change behavior a little big from Qt; at the bottom of the doc we go to the end of the doc textEditor->movePosition(QTextCursor::End, shiftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor); } if (shiftPressed) // altered selection. repaintSelection(); else repaintCaret(); updateActions(); editingPluginEvents(); } if (m_caretTimer.isActive()) { // make the caret not blink but decide on the action if its visible or not. m_caretTimer.stop(); m_caretTimer.setInterval(50); m_caretTimer.start(); m_caretTimerState = true; // turn caret on while typing } if (moveOperation != QTextCursor::NoMove) // this difference in handling is need to prevent leaving a trail of old cursors onscreen ensureCursorVisible(); else m_delayedEnsureVisible = true; updateActions(); updateSelectionHandler(); } QVariant TextTool::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor || !m_textShapeData) return QVariant(); switch (query) { case Qt::ImMicroFocus: { // The rectangle covering the area of the input cursor in widget coordinates. QRectF rect = caretRect(textEditor->cursor()); rect.moveTop(rect.top() - m_textShapeData->documentOffset()); QTransform shapeMatrix = m_textShape->absoluteTransformation(&converter); qreal zoomX, zoomY; converter.zoom(&zoomX, &zoomY); shapeMatrix.scale(zoomX, zoomY); rect = shapeMatrix.mapRect(rect); return rect.toRect(); } case Qt::ImFont: // The currently used font for text input. return textEditor->charFormat().font(); case Qt::ImCursorPosition: // The logical position of the cursor within the text surrounding the input area (see ImSurroundingText). return textEditor->position() - textEditor->block().position(); case Qt::ImSurroundingText: // The plain text around the input area, for example the current paragraph. return textEditor->block().text(); case Qt::ImCurrentSelection: // The currently selected text. return textEditor->selectedText(); default: ; // Qt 4.6 adds ImMaximumTextLength and ImAnchorPosition } return QVariant(); } void TextTool::inputMethodEvent(QInputMethodEvent *event) { KoTextEditor *textEditor = m_textEditor.data(); if (textEditor == 0) return; if (event->replacementLength() > 0) { textEditor->setPosition(textEditor->position() + event->replacementStart()); for (int i = event->replacementLength(); i > 0; --i) { textEditor->deleteChar(); } } if (!event->commitString().isEmpty()) { QKeyEvent ke(QEvent::KeyPress, -1, 0, event->commitString()); keyPressEvent(&ke); // The cursor may reside in a different block before vs. after keyPressEvent. QTextBlock block = textEditor->block(); QTextLayout *layout = block.layout(); Q_ASSERT(layout); layout->setPreeditArea(-1, QString()); } else { QTextBlock block = textEditor->block(); QTextLayout *layout = block.layout(); Q_ASSERT(layout); layout->setPreeditArea(textEditor->position() - block.position(), event->preeditString()); const_cast(textEditor->document())->markContentsDirty(textEditor->position(), event->preeditString().length()); } event->accept(); } void TextTool::ensureCursorVisible(bool moveView) { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor || !m_textShapeData) return; bool upToDate; QRectF cRect = caretRect(textEditor->cursor(), &upToDate); KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout()); Q_ASSERT(lay); KoTextLayoutRootArea *rootArea = lay->rootAreaForPoint(cRect.center()); if (rootArea && rootArea->associatedShape() && m_textShapeData->rootArea() != rootArea) { // If we have changed root area we need to update m_textShape and m_textShapeData m_textShape = static_cast(rootArea->associatedShape()); Q_ASSERT(m_textShape); disconnect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved())); m_textShapeData = static_cast(m_textShape->userData()); Q_ASSERT(m_textShapeData); connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved())); } if (!moveView) { return; } if (! upToDate) { // paragraph is not yet layouted. // The number one usecase for this is when the user pressed enter. // try to do it on next caret blink m_delayedEnsureVisible = true; return; // we shouldn't move to an obsolete position } cRect.moveTop(cRect.top() - m_textShapeData->documentOffset()); canvas()->ensureVisible(m_textShape->absoluteTransformation(0).mapRect(cRect)); } void TextTool::keyReleaseEvent(QKeyEvent *event) { event->accept(); } void TextTool::updateActions() { bool notInAnnotation = !dynamic_cast(m_textShape); KoTextEditor *textEditor = m_textEditor.data(); if (textEditor == 0) { return; } m_allowActions = false; //Update the characterStyle related GUI elements QTextCharFormat cf = textEditor->charFormat(); m_actionFormatBold->setChecked(cf.fontWeight() > QFont::Normal); m_actionFormatItalic->setChecked(cf.fontItalic()); m_actionFormatUnderline->setChecked(cf.intProperty(KoCharacterStyle::UnderlineType) != KoCharacterStyle::NoLineType); m_actionFormatStrikeOut->setChecked(cf.intProperty(KoCharacterStyle::StrikeOutType) != KoCharacterStyle::NoLineType); bool super = false, sub = false; switch (cf.verticalAlignment()) { case QTextCharFormat::AlignSuperScript: super = true; break; case QTextCharFormat::AlignSubScript: sub = true; break; default:; } m_actionFormatSuper->setChecked(super); m_actionFormatSub->setChecked(sub); m_actionFormatFontSize->setFontSize(cf.font().pointSizeF()); m_actionFormatFontFamily->setFont(cf.font().family()); KoTextShapeData::ResizeMethod resizemethod = KoTextShapeData::AutoResize; if(m_textShapeData) { resizemethod = m_textShapeData->resizeMethod(); } m_shrinkToFitAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation); m_shrinkToFitAction->setChecked(resizemethod == KoTextShapeData::ShrinkToFitResize); m_growWidthAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation); m_growWidthAction->setChecked(resizemethod == KoTextShapeData::AutoGrowWidth || resizemethod == KoTextShapeData::AutoGrowWidthAndHeight); m_growHeightAction->setEnabled(resizemethod != KoTextShapeData::AutoResize && notInAnnotation); m_growHeightAction->setChecked(resizemethod == KoTextShapeData::AutoGrowHeight || resizemethod == KoTextShapeData::AutoGrowWidthAndHeight); //update paragraphStyle GUI element QTextBlockFormat bf = textEditor->blockFormat(); if (bf.hasProperty(KoParagraphStyle::TextProgressionDirection)) { switch(bf.intProperty(KoParagraphStyle::TextProgressionDirection)) { case KoText::RightLeftTopBottom: m_actionChangeDirection->setChecked(true); break; case KoText::LeftRightTopBottom: default: m_actionChangeDirection->setChecked(false); break; } } else { m_actionChangeDirection->setChecked(textEditor->block().text().isRightToLeft()); } if (bf.alignment() == Qt::AlignLeading || bf.alignment() == Qt::AlignTrailing) { bool revert = (textEditor->block().layout()->textOption().textDirection() == Qt::RightToLeft); if ((bf.alignment() == Qt::AlignLeading) ^ revert) m_actionAlignLeft->setChecked(true); else m_actionAlignRight->setChecked(true); } else if (bf.alignment() == Qt::AlignHCenter) m_actionAlignCenter->setChecked(true); if (bf.alignment() == Qt::AlignJustify) m_actionAlignBlock->setChecked(true); else if (bf.alignment() == (Qt::AlignLeft | Qt::AlignAbsolute)) m_actionAlignLeft->setChecked(true); else if (bf.alignment() == (Qt::AlignRight | Qt::AlignAbsolute)) m_actionAlignRight->setChecked(true); if (textEditor->block().textList()) { QTextListFormat listFormat = textEditor->block().textList()->format(); if(listFormat.intProperty(KoListStyle::Level) > 1) { m_actionFormatDecreaseIndent->setEnabled(true); } else { m_actionFormatDecreaseIndent->setEnabled(false); } if (listFormat.intProperty(KoListStyle::Level) < 10) { m_actionFormatIncreaseIndent->setEnabled(true); } else { m_actionFormatIncreaseIndent->setEnabled(false); } } else { m_actionFormatDecreaseIndent->setEnabled(textEditor->blockFormat().leftMargin() > 0.); } m_allowActions = true; bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality) & KoCanvasResourceManager::NoAdvancedText); if (useAdvancedText) { action("insert_table")->setEnabled(notInAnnotation); bool hasTable = textEditor->currentTable(); action("insert_tablerow_above")->setEnabled(hasTable && notInAnnotation); action("insert_tablerow_below")->setEnabled(hasTable && notInAnnotation); action("insert_tablecolumn_left")->setEnabled(hasTable && notInAnnotation); action("insert_tablecolumn_right")->setEnabled(hasTable && notInAnnotation); action("delete_tablerow")->setEnabled(hasTable && notInAnnotation); action("delete_tablecolumn")->setEnabled(hasTable && notInAnnotation); action("merge_tablecells")->setEnabled(hasTable && notInAnnotation); action("split_tablecells")->setEnabled(hasTable && notInAnnotation); action("activate_borderpainter")->setEnabled(hasTable && notInAnnotation); } action("insert_annotation")->setEnabled(notInAnnotation); ///TODO if selection contains several different format emit blockChanged(textEditor->block()); emit charFormatChanged(cf, textEditor->blockCharFormat()); emit blockFormatChanged(bf); } void TextTool::updateStyleManager() { if (!m_textShapeData) return; KoStyleManager *styleManager = KoTextDocument(m_textShapeData->document()).styleManager(); emit styleManagerChanged(styleManager); //TODO move this to its own method m_changeTracker = KoTextDocument(m_textShapeData->document()).changeTracker(); } void TextTool::activate(ToolActivation toolActivation, const QSet &shapes) { Q_UNUSED(toolActivation); m_caretTimer.start(); m_caretTimerState = true; foreach (KoShape *shape, shapes) { TextShape *textShape = dynamic_cast(shape); if (textShape) { if (!m_textEditor) { m_textShape = textShape; break; } // Since there is a text editor, we must select the shape that is edited, // or else we get out of sync KoTextShapeData *data = static_cast(textShape->userData()); if (data && data->document() == m_textEditor->constCursor().document()) { m_textShape = textShape; break; } if (!m_textShape) { m_textShape = textShape; } } } if (!m_textShape) { // none found emit done(); // This is how we inform the rulers of the active range // No shape means no active range canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, QVariant(QRectF())); return; } // This is how we inform the rulers of the active range // For now we will not consider table cells, but just give the shape dimensions QVariant v; QRectF rect(QPoint(), m_textShape->size()); rect = m_textShape->absoluteTransformation(0).mapRect(rect); v.setValue(rect); canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, v); if ((!m_oldTextEditor.isNull()) && m_oldTextEditor.data()->document() != static_cast(m_textShape->userData())->document()) { m_oldTextEditor.data()->setPosition(m_oldTextEditor.data()->position()); //we need to redraw like this so we update the old textshape whereever it may be if (canvas()->canvasWidget()) canvas()->canvasWidget()->update(); } setShapeData(static_cast(m_textShape->userData())); useCursor(Qt::IBeamCursor); updateStyleManager(); repaintSelection(); updateSelectionHandler(); updateActions(); if (m_specialCharacterDocker) m_specialCharacterDocker->setEnabled(true); } void TextTool::deactivate() { m_caretTimer.stop(); m_caretTimerState = false; repaintCaret(); m_textShape = 0; // This is how we inform the rulers of the active range // No shape means no active range canvas()->resourceManager()->setResource(KoCanvasResourceManager::ActiveRange, QVariant(QRectF())); m_oldTextEditor = m_textEditor; setShapeData(0); updateSelectionHandler(); if (m_specialCharacterDocker) { m_specialCharacterDocker->setEnabled(false); m_specialCharacterDocker->setVisible(false); } } void TextTool::repaintDecorations() { if (m_textShapeData) repaintSelection(); } void TextTool::repaintCaret() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor || !m_textShapeData) return; KoTextDocumentLayout *lay = qobject_cast(m_textShapeData->document()->documentLayout()); Q_ASSERT(lay); Q_UNUSED(lay); // If we have changed root area we need to update m_textShape and m_textShapeData if (m_delayedEnsureVisible) { m_delayedEnsureVisible = false; ensureCursorVisible(); return; } ensureCursorVisible(false); // ensures the various vars are updated bool upToDate; QRectF repaintRect = caretRect(textEditor->cursor(), &upToDate); repaintRect.moveTop(repaintRect.top() - m_textShapeData->documentOffset()); if (repaintRect.isValid()) { repaintRect = m_textShape->absoluteTransformation(0).mapRect(repaintRect); // Make sure there is enough space to show an icon QRectF iconSize = canvas()->viewConverter()->viewToDocument(QRect(0, 0, 18, 18)); repaintRect.setX(repaintRect.x() - iconSize.width() / 2); repaintRect.setRight(repaintRect.right() + iconSize.width() / 2); repaintRect.setTop(repaintRect.y() - iconSize.height() / 2); repaintRect.setBottom(repaintRect.bottom() + iconSize.height() / 2); canvas()->updateCanvas(repaintRect); } } void TextTool::repaintSelection() { KoTextEditor *textEditor = m_textEditor.data(); if (textEditor == 0) return; QTextCursor cursor = *textEditor->cursor(); QList shapes; KoTextDocumentLayout *lay = qobject_cast(textEditor->document()->documentLayout()); Q_ASSERT(lay); foreach (KoShape* shape, lay->shapes()) { TextShape *textShape = dynamic_cast(shape); if (textShape == 0) // when the shape is being deleted its no longer a TextShape but a KoShape continue; //Q_ASSERT(!shapes.contains(textShape)); if (!shapes.contains(textShape)) { shapes.append(textShape); } } // loop over all shapes that contain the text and update per shape. QRectF repaintRect = textRect(cursor); foreach (TextShape *ts, shapes) { QRectF rect = repaintRect; rect.moveTop(rect.y() - ts->textShapeData()->documentOffset()); rect = ts->absoluteTransformation(0).mapRect(rect); QRectF r = ts->boundingRect().intersected(rect); canvas()->updateCanvas(r); } } QRectF TextTool::caretRect(QTextCursor *cursor, bool *upToDate) const { QTextCursor tmpCursor(*cursor); tmpCursor.setPosition(cursor->position()); // looses the anchor QRectF rect = textRect(tmpCursor); if (rect.size() == QSizeF(0,0)) { if (upToDate) { *upToDate = false; } rect = m_lastImMicroFocus; // prevent block changed but layout not done } else { if (upToDate) { *upToDate = true; } m_lastImMicroFocus = rect; } return rect; } QRectF TextTool::textRect(QTextCursor &cursor) const { if (!m_textShapeData) return QRectF(); KoTextEditor *textEditor = m_textEditor.data(); KoTextDocumentLayout *lay = qobject_cast(textEditor->document()->documentLayout()); return lay->selectionBoundingBox(cursor); } KoToolSelection* TextTool::selection() { return m_toolSelection; } QList > TextTool::createOptionWidgets() { QList > widgets; SimpleCharacterWidget *scw = new SimpleCharacterWidget(this, 0); SimpleParagraphWidget *spw = new SimpleParagraphWidget(this, 0); if (m_textEditor.data()) { // connect(m_textEditor.data(), SIGNAL(paragraphStyleApplied(KoParagraphStyle*)), spw, SLOT(slotParagraphStyleApplied(KoParagraphStyle*))); // connect(m_textEditor.data(), SIGNAL(characterStyleApplied(KoCharacterStyle*)), scw, SLOT(slotCharacterStyleApplied(KoCharacterStyle*))); //initialise the char- and par- widgets with the current block and formats. scw->setCurrentBlockFormat(m_textEditor.data()->blockFormat()); scw->setCurrentFormat(m_textEditor.data()->charFormat(), m_textEditor.data()-> blockCharFormat()); spw->setCurrentBlock(m_textEditor.data()->block()); spw->setCurrentFormat(m_textEditor.data()->blockFormat()); } SimpleTableWidget *stw = new SimpleTableWidget(this, 0); SimpleInsertWidget *siw = new SimpleInsertWidget(this, 0); /* 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) if (m_textShapeData && KoTextDocument(m_textShapeData->document()).styleManager()) { scw->setInitialUsedStyles(KoTextDocument(m_textShapeData->document()).styleManager()->usedCharacterStyles()); spw->setInitialUsedStyles(KoTextDocument(m_textShapeData->document()).styleManager()->usedParagraphStyles()); } */ // Connect to/with simple character widget (docker) connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), scw, SLOT(setStyleManager(KoStyleManager*))); connect(this, SIGNAL(charFormatChanged(QTextCharFormat,QTextCharFormat)), scw, SLOT(setCurrentFormat(QTextCharFormat,QTextCharFormat))); connect(this, SIGNAL(blockFormatChanged(QTextBlockFormat)), scw, SLOT(setCurrentBlockFormat(QTextBlockFormat))); connect(scw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); connect(scw, SIGNAL(characterStyleSelected(KoCharacterStyle*)), this, SLOT(setStyle(KoCharacterStyle*))); connect(scw, SIGNAL(newStyleRequested(QString)), this, SLOT(createStyleFromCurrentCharFormat(QString))); connect(scw, SIGNAL(showStyleManager(int)), this, SLOT(showStyleManager(int))); // Connect to/with simple paragraph widget (docker) connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), spw, SLOT(setStyleManager(KoStyleManager*))); connect(this, SIGNAL(blockChanged(QTextBlock)), spw, SLOT(setCurrentBlock(QTextBlock))); connect(this, SIGNAL(blockFormatChanged(QTextBlockFormat)), spw, SLOT(setCurrentFormat(QTextBlockFormat))); connect(spw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); connect(spw, SIGNAL(paragraphStyleSelected(KoParagraphStyle*)), this, SLOT(setStyle(KoParagraphStyle*))); connect(spw, SIGNAL(newStyleRequested(QString)), this, SLOT(createStyleFromCurrentBlockFormat(QString))); connect(spw, SIGNAL(showStyleManager(int)), this, SLOT(showStyleManager(int))); // Connect to/with simple table widget (docker) connect(this, SIGNAL(styleManagerChanged(KoStyleManager*)), stw, SLOT(setStyleManager(KoStyleManager*))); connect(stw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); connect(stw, SIGNAL(tableBorderDataUpdated(KoBorder::BorderData)), this, SLOT(setTableBorderData(KoBorder::BorderData))); // Connect to/with simple insert widget (docker) connect(siw, SIGNAL(doneWithFocus()), this, SLOT(returnFocusToCanvas())); connect(siw, SIGNAL(insertTableQuick(int,int)), this, SLOT(insertTableQuick(int,int))); updateStyleManager(); if (m_textShape) { updateActions(); } scw->setWindowTitle(i18n("Character")); widgets.append(scw); spw->setWindowTitle(i18n("Paragraph")); widgets.append(spw); bool useAdvancedText = !(canvas()->resourceManager()->intResource(KoCanvasResourceManager::ApplicationSpeciality) & KoCanvasResourceManager::NoAdvancedText); if (useAdvancedText) { stw->setWindowTitle(i18n("Table")); widgets.append(stw); siw->setWindowTitle(i18n("Insert")); widgets.append(siw); } return widgets; } void TextTool::returnFocusToCanvas() { canvas()->canvasWidget()->setFocus(); } void TextTool::startEditing(KUndo2Command* command) { m_currentCommand = command; m_currentCommandHasChildren = true; } void TextTool::stopEditing() { m_currentCommand = 0; m_currentCommandHasChildren = false; } void TextTool::insertNewSection() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor) return; textEditor->newSection(); } void TextTool::configureSection() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor) return; SectionFormatDialog *dia = new SectionFormatDialog(0, m_textEditor.data()); dia->exec(); delete dia; returnFocusToCanvas(); updateActions(); } void TextTool::splitSections() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor) return; SectionsSplitDialog *dia = new SectionsSplitDialog(0, m_textEditor.data()); dia->exec(); delete dia; returnFocusToCanvas(); updateActions(); } void TextTool::pasteAsText() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor) return; const QMimeData *data = QApplication::clipboard()->mimeData(QClipboard::Clipboard); // on windows we do not have data if we try to paste this selection if (!data) return; if (data->hasFormat(KoOdf::mimeType(KoOdf::Text)) || data->hasText()) { m_prevCursorPosition = m_textEditor.data()->position(); m_textEditor.data()->paste(canvas(), data, true); editingPluginEvents(); } } void TextTool::bold(bool bold) { m_textEditor.data()->bold(bold); } void TextTool::italic(bool italic) { m_textEditor.data()->italic(italic); } void TextTool::underline(bool underline) { m_textEditor.data()->underline(underline); } void TextTool::strikeOut(bool strikeOut) { m_textEditor.data()->strikeOut(strikeOut); } void TextTool::nonbreakingSpace() { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->insertText(QString(QChar(Qt::Key_nobreakspace))); } void TextTool::nonbreakingHyphen() { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->insertText(QString(QChar(0x2013))); } void TextTool::softHyphen() { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->insertText(QString(QChar(Qt::Key_hyphen))); } void TextTool::lineBreak() { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->insertText(QString(QChar(0x2028))); } void TextTool::alignLeft() { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignLeft | Qt::AlignAbsolute); } void TextTool::alignRight() { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignRight | Qt::AlignAbsolute); } void TextTool::alignCenter() { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignHCenter); } void TextTool::alignBlock() { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->setHorizontalTextAlignment(Qt::AlignJustify); } void TextTool::superScript(bool on) { if (!m_allowActions || !m_textEditor.data()) return; if (on) m_actionFormatSub->setChecked(false); m_textEditor.data()->setVerticalTextAlignment(on ? Qt::AlignTop : Qt::AlignVCenter); } void TextTool::subScript(bool on) { if (!m_allowActions || !m_textEditor.data()) return; if (on) m_actionFormatSuper->setChecked(false); m_textEditor.data()->setVerticalTextAlignment(on ? Qt::AlignBottom : Qt::AlignVCenter); } void TextTool::increaseIndent() { if (!m_allowActions || !m_textEditor.data()) return; if (m_textEditor.data()->block().textList()) { ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::IncreaseLevel; ChangeListLevelCommand *cll = new ChangeListLevelCommand(*(m_textEditor.data()->cursor()), type, 1); m_textEditor.data()->addCommand(cll); editingPluginEvents(); } else { m_textEditor.data()->increaseIndent(); } updateActions(); } void TextTool::decreaseIndent() { if (!m_allowActions || !m_textEditor.data()) return; if (m_textEditor.data()->block().textList()) { ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::DecreaseLevel; ChangeListLevelCommand *cll = new ChangeListLevelCommand(*(m_textEditor.data()->cursor()), type, 1); m_textEditor.data()->addCommand(cll); editingPluginEvents(); } else { m_textEditor.data()->decreaseIndent(); } updateActions(); } void TextTool::decreaseFontSize() { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->decreaseFontSize(); } void TextTool::increaseFontSize() { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->increaseFontSize(); } void TextTool::setFontFamily(const QString &font) { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->setFontFamily(font); } void TextTool::setFontSize (qreal size) { if (!m_allowActions || !m_textEditor.data()) return; m_textEditor.data()->setFontSize(size); } void TextTool::insertIndexMarker() { // TODO handle result when we figure out how to report errors from a tool. m_textEditor.data()->insertIndexMarker(); } void TextTool::insertFrameBreak() { m_textEditor.data()->insertFrameBreak(); ensureCursorVisible(); m_delayedEnsureVisible = true; } void TextTool::setStyle(KoCharacterStyle *style) { KoCharacterStyle *charStyle = style; //if the given KoCharacterStyle is null, set the KoParagraphStyle character properties if (!charStyle){ charStyle = static_cast(KoTextDocument(m_textShapeData->document()).styleManager()->paragraphStyle(m_textEditor.data()->blockFormat().intProperty(KoParagraphStyle::StyleId))); } if (charStyle) { m_textEditor.data()->setStyle(charStyle); updateActions(); } } void TextTool::setStyle(KoParagraphStyle *style) { m_textEditor.data()->setStyle(style); updateActions(); } void TextTool::insertTable() { TableDialog *dia = new TableDialog(0); if (dia->exec() == TableDialog::Accepted) m_textEditor.data()->insertTable(dia->rows(), dia->columns()); delete dia; updateActions(); } void TextTool::insertTableQuick(int rows, int columns) { m_textEditor.data()->insertTable(rows, columns); updateActions(); } void TextTool::insertTableRowAbove() { m_textEditor.data()->insertTableRowAbove(); } void TextTool::insertTableRowBelow() { m_textEditor.data()->insertTableRowBelow(); } void TextTool::insertTableColumnLeft() { m_textEditor.data()->insertTableColumnLeft(); } void TextTool::insertTableColumnRight() { m_textEditor.data()->insertTableColumnRight(); } void TextTool::deleteTableColumn() { m_textEditor.data()->deleteTableColumn(); } void TextTool::deleteTableRow() { m_textEditor.data()->deleteTableRow(); } void TextTool::mergeTableCells() { m_textEditor.data()->mergeTableCells(); } void TextTool::splitTableCells() { m_textEditor.data()->splitTableCells(); } void TextTool::useTableBorderCursor() { static const unsigned char data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x80, 0x7e, 0x00, 0x00, 0x40, 0x3f, 0x00, 0x00, 0xa0, 0x1f, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xf4, 0x03, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x40, 0x32, 0x00, 0x00, 0xa0, 0x0f, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00, 0xd0, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; QBitmap result(32, 32); result.fill(Qt::color0); QPainter painter(&result); painter.drawPixmap(0, 0, QBitmap::fromData(QSize(25, 23), data)); QBitmap brushMask = result.createHeuristicMask(false); useCursor(QCursor(result, brushMask, 1, 21)); } void TextTool::setTableBorderData(const KoBorder::BorderData &data) { m_tablePenMode = true; m_tablePenBorderData = data; } void TextTool::formatParagraph() { ParagraphSettingsDialog *dia = new ParagraphSettingsDialog(this, m_textEditor.data()); dia->setUnit(canvas()->unit()); dia->setImageCollection(m_textShape->imageCollection()); dia->exec(); delete dia; returnFocusToCanvas(); } void TextTool::testSlot(bool on) { debugTextShape << "signal received. bool:" << on; } void TextTool::selectAll() { KoTextEditor *textEditor = m_textEditor.data(); if (!textEditor || !m_textShapeData) return; const int selectionLength = qAbs(textEditor->position() - textEditor->anchor()); textEditor->movePosition(QTextCursor::End); textEditor->setPosition(0, QTextCursor::KeepAnchor); repaintSelection(); if (selectionLength != qAbs(textEditor->position() - textEditor->anchor())) // it actually changed emit selectionChanged(true); } void TextTool::startMacro(const QString &title) { if (title != i18n("Key Press") && title !=i18n("Autocorrection")) //dirty hack while waiting for refactor of text editing m_textTyping = false; else m_textTyping = true; if (title != i18n("Delete") && title != i18n("Autocorrection")) //same dirty hack as above m_textDeleting = false; else m_textDeleting = true; if (m_currentCommand) return; class MacroCommand : public KUndo2Command { public: MacroCommand(const KUndo2MagicString &title) : KUndo2Command(title), m_first(true) {} void redo() override { if (! m_first) KUndo2Command::redo(); m_first = false; } bool mergeWith(const KUndo2Command *) override { return false; } bool m_first; }; /** * FIXME: The messages genearted by the Text Tool might not be * properly translated, since we don't control it in * type-safe way. * * The title is already translated string, we just don't * have any type control over it. */ KUndo2MagicString title_workaround = kundo2_noi18n(title); m_currentCommand = new MacroCommand(title_workaround); m_currentCommandHasChildren = false; } void TextTool::stopMacro() { if (!m_currentCommand) return; if (! m_currentCommandHasChildren) delete m_currentCommand; m_currentCommand = 0; } void TextTool::showStyleManager(int styleId) { if (!m_textShapeData) return; KoStyleManager *styleManager = KoTextDocument(m_textShapeData->document()).styleManager(); Q_ASSERT(styleManager); if (!styleManager) return; //don't crash StyleManagerDialog *dia = new StyleManagerDialog(canvas()->canvasWidget()); dia->setStyleManager(styleManager); dia->setUnit(canvas()->unit()); KoParagraphStyle *paragraphStyle = styleManager->paragraphStyle(styleId); if (paragraphStyle) { dia->setParagraphStyle(paragraphStyle); } KoCharacterStyle *characterStyle = styleManager->characterStyle(styleId); if (characterStyle) { dia->setCharacterStyle(characterStyle); } dia->show(); } void TextTool::startTextEditingPlugin(const QString &pluginId) { KoTextEditingPlugin *plugin = textEditingPluginContainer()->plugin(pluginId); if (plugin) { if (m_textEditor.data()->hasSelection()) { plugin->checkSection(m_textShapeData->document(), m_textEditor.data()->selectionStart(), m_textEditor.data()->selectionEnd()); } else plugin->finishedWord(m_textShapeData->document(), m_textEditor.data()->position()); } } void TextTool::canvasResourceChanged(int key, const QVariant &var) { if (m_textEditor.isNull()) return; if (!m_textShapeData) return; if (m_allowResourceManagerUpdates == false) return; if (key == KoText::CurrentTextPosition) { repaintSelection(); m_textEditor.data()->setPosition(var.toInt()); ensureCursorVisible(); } else if (key == KoText::CurrentTextAnchor) { repaintSelection(); int pos = m_textEditor.data()->position(); m_textEditor.data()->setPosition(var.toInt()); m_textEditor.data()->setPosition(pos, QTextCursor::KeepAnchor); } else if (key == KoCanvasResourceManager::Unit) { m_unit = var.value(); } else return; repaintSelection(); } void TextTool::insertSpecialCharacter() { if (m_specialCharacterDocker == 0) { m_specialCharacterDocker = new InsertCharacter(canvas()->canvasWidget()); connect(m_specialCharacterDocker, SIGNAL(insertCharacter(QString)), this, SLOT(insertString(QString))); } m_specialCharacterDocker->show(); } void TextTool::insertString(const QString& string) { m_textEditor.data()->insertText(string); returnFocusToCanvas(); } void TextTool::selectFont() { FontDia *fontDlg = new FontDia(m_textEditor.data()); fontDlg->exec(); delete fontDlg; returnFocusToCanvas(); } void TextTool::shapeAddedToCanvas() { debugTextShape; if (m_textShape) { KoSelection *selection = canvas()->shapeManager()->selection(); KoShape *shape = selection->firstSelectedShape(); if (shape != m_textShape && canvas()->shapeManager()->shapes().contains(m_textShape)) { // this situation applies when someone, not us, changed the selection by selecting another // text shape. Possibly by adding one. // Deselect the new shape again, so we can keep editing what we were already editing selection->select(m_textShape); selection->deselect(shape); } } } void TextTool::shapeDataRemoved() { m_textShapeData = 0; m_textShape = 0; if (!m_textEditor.isNull() && !m_textEditor.data()->cursor()->isNull()) { const QTextDocument *doc = m_textEditor.data()->document(); Q_ASSERT(doc); KoTextDocumentLayout *lay = qobject_cast(doc->documentLayout()); if (!lay || lay->shapes().isEmpty()) { emit done(); return; } m_textShape = static_cast(lay->shapes().first()); m_textShapeData = static_cast(m_textShape->userData()); connect(m_textShapeData, SIGNAL(destroyed(QObject*)), this, SLOT(shapeDataRemoved())); } } void TextTool::createStyleFromCurrentBlockFormat(const QString &name) { KoTextDocument document(m_textShapeData->document()); KoStyleManager *styleManager = document.styleManager(); KoParagraphStyle *paragraphStyle = new KoParagraphStyle(m_textEditor.data()->blockFormat(), m_textEditor.data()->charFormat()); paragraphStyle->setName(name); styleManager->add(paragraphStyle); m_textEditor.data()->setStyle(paragraphStyle); emit charFormatChanged(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat()); emit blockFormatChanged(m_textEditor.data()->blockFormat()); } void TextTool::createStyleFromCurrentCharFormat(const QString &name) { KoTextDocument document(m_textShapeData->document()); KoStyleManager *styleManager = document.styleManager(); KoCharacterStyle *originalCharStyle = styleManager->characterStyle(m_textEditor.data()->charFormat().intProperty(KoCharacterStyle::StyleId)); KoCharacterStyle *autoStyle; if (!originalCharStyle) { KoCharacterStyle blankStyle; originalCharStyle = &blankStyle; autoStyle = originalCharStyle->autoStyle(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat()); autoStyle->setParentStyle(0); } else { autoStyle = originalCharStyle->autoStyle(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat()); } autoStyle->setName(name); styleManager->add(autoStyle); m_textEditor.data()->setStyle(autoStyle); emit charFormatChanged(m_textEditor.data()->charFormat(), m_textEditor.data()->blockCharFormat()); } // ---------- editing plugins methods. void TextTool::editingPluginEvents() { if (m_prevCursorPosition == -1 || m_prevCursorPosition == m_textEditor.data()->position()) { debugTextShape<<"m_prevCursorPosition="<position()="<position(); return; } QTextBlock block = m_textEditor.data()->block(); if (! block.contains(m_prevCursorPosition)) { debugTextShape<<"m_prevCursorPosition="<position(); if (from > to) qSwap(from, to); QString section = block.text().mid(from - block.position(), to - from); debugTextShape<<"from="<values()) { plugin->finishedWord(m_textShapeData->document(), m_prevCursorPosition); } } } void TextTool::finishedParagraph() { if (m_textShapeData && textEditingPluginContainer()) { foreach (KoTextEditingPlugin* plugin, textEditingPluginContainer()->values()) { plugin->finishedParagraph(m_textShapeData->document(), m_prevCursorPosition); } } } void TextTool::startingSimpleEdit() { if (m_textShapeData && textEditingPluginContainer()) { foreach (KoTextEditingPlugin* plugin, textEditingPluginContainer()->values()) { plugin->startingSimpleEdit(m_textShapeData->document(), m_prevCursorPosition); } } } void TextTool::setTextColor(const KoColor &color) { m_textEditor.data()->setTextColor(color.toQColor()); } void TextTool::setBackgroundColor(const KoColor &color) { m_textEditor.data()->setTextBackgroundColor(color.toQColor()); } void TextTool::setAutoResize(bool enabled) { m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoResize, enabled)); updateActions(); } void TextTool::setGrowWidthToFit(bool enabled) { m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoGrowWidth, enabled)); updateActions(); } void TextTool::setGrowHeightToFit(bool enabled) { m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::AutoGrowHeight, enabled)); updateActions(); } void TextTool::setShrinkToFit(bool enabled) { m_textEditor.data()->addCommand(new AutoResizeCommand(m_textShapeData, KoTextShapeData::ShrinkToFitResize, enabled)); updateActions(); } void TextTool::runUrl(KoPointerEvent *event, QString &url) { QUrl _url = QUrl::fromLocalFile(url); if (_url.isLocalFile()) { QMimeDatabase db; QString type = db.mimeTypeForUrl(_url).name(); if (KRun::isExecutableFile(_url, type)) { QString question = i18n("This link points to the program or script '%1'.\n" "Malicious programs can harm your computer. " "Are you sure that you want to run this program?", url); // this will also start local programs, so adding a "don't warn again" // checkbox will probably be too dangerous int choice = KMessageBox::warningYesNo(0, question, i18n("Open Link?")); if (choice != KMessageBox::Yes) return; } } event->accept(); new KRun(_url, 0); } void TextTool::debugTextDocument() { #ifndef NDEBUG if (!m_textShapeData) return; const int CHARSPERLINE = 80; // TODO Make configurable using ENV var? const int CHARPOSITION = 278301935; KoTextDocument document(m_textShapeData->document()); KoStyleManager *styleManager = document.styleManager(); KoInlineTextObjectManager *inlineManager = document.inlineTextObjectManager(); QTextBlock block = m_textShapeData->document()->begin(); for (;block.isValid(); block = block.next()) { QVariant var = block.blockFormat().property(KoParagraphStyle::StyleId); if (!var.isNull()) { KoParagraphStyle *ps = styleManager->paragraphStyle(var.toInt()); debugTextShape << "--- Paragraph Style:" << (ps ? ps->name() : QString()) << var.toInt(); } var = block.charFormat().property(KoCharacterStyle::StyleId); if (!var.isNull()) { KoCharacterStyle *cs = styleManager->characterStyle(var.toInt()); debugTextShape << "--- Character Style:" << (cs ? cs->name() : QString()) << var.toInt(); } int lastPrintedChar = -1; QTextBlock::iterator it; QString fragmentText; QList inlineCharacters; for (it = block.begin(); !it.atEnd(); ++it) { QTextFragment fragment = it.fragment(); if (!fragment.isValid()) continue; QTextCharFormat fmt = fragment.charFormat(); debugTextShape << "changeId: " << fmt.property(KoCharacterStyle::ChangeTrackerId); const int fragmentStart = fragment.position() - block.position(); for (int i = fragmentStart; i < fragmentStart + fragment.length(); i += CHARSPERLINE) { if (lastPrintedChar == fragmentStart-1) fragmentText += '|'; if (lastPrintedChar < fragmentStart || i > fragmentStart) { QString debug = block.text().mid(lastPrintedChar, CHARSPERLINE); lastPrintedChar += CHARSPERLINE; if (lastPrintedChar > block.length()) debug += "\\n"; debugTextShape << debug; } var = fmt.property(KoCharacterStyle::StyleId); QString charStyleLong, charStyleShort; if (! var.isNull()) { // named style charStyleShort = QString::number(var.toInt()); KoCharacterStyle *cs = styleManager->characterStyle(var.toInt()); if (cs) charStyleLong = cs->name(); } if (inlineManager && fmt.hasProperty(KoCharacterStyle::InlineInstanceId)) { QTextCharFormat inlineFmt = fmt; inlineFmt.setProperty(CHARPOSITION, fragmentStart); inlineCharacters << inlineFmt; } if (fragment.length() > charStyleLong.length()) fragmentText += charStyleLong; else if (fragment.length() > charStyleShort.length()) fragmentText += charStyleShort; else if (fragment.length() >= 2) fragmentText += QChar(8230); // ellipsis int rest = fragmentStart - (lastPrintedChar-CHARSPERLINE) + fragment.length() - fragmentText.length(); rest = qMin(rest, CHARSPERLINE - fragmentText.length()); if (rest >= 2) fragmentText = QString("%1%2").arg(fragmentText).arg(' ', rest); if (rest >= 0) fragmentText += '|'; if (fragmentText.length() >= CHARSPERLINE) { debugTextShape << fragmentText; fragmentText.clear(); } } } if (!fragmentText.isEmpty()) { debugTextShape << fragmentText; } else if (block.length() == 1) { // no actual tet debugTextShape << "\\n"; } foreach (const QTextCharFormat &cf, inlineCharacters) { KoInlineObject *object= inlineManager->inlineTextObject(cf); debugTextShape << "At pos:" << cf.intProperty(CHARPOSITION) << object; // debugTextShape << "-> id:" << cf.intProperty(577297549); } QTextList *list = block.textList(); if (list) { if (list->format().hasProperty(KoListStyle::StyleId)) { KoListStyle *ls = styleManager->listStyle(list->format().intProperty(KoListStyle::StyleId)); debugTextShape << " List style applied:" << ls->styleId() << ls->name(); } else debugTextShape << " +- is a list..." << list; } } #endif } void TextTool::debugTextStyles() { #ifndef NDEBUG if (!m_textShapeData) return; KoTextDocument document(m_textShapeData->document()); KoStyleManager *styleManager = document.styleManager(); QSet seenStyles; foreach (KoParagraphStyle *style, styleManager->paragraphStyles()) { debugTextShape << style->styleId() << style->name() << (styleManager->defaultParagraphStyle() == style ? "[Default]" : ""); KoListStyle *ls = style->listStyle(); if (ls) { // optional ;) debugTextShape << " +- ListStyle: " << ls->styleId() << ls->name() << (ls == styleManager->defaultListStyle() ? "[Default]":""); foreach (int level, ls->listLevels()) { KoListLevelProperties llp = ls->levelProperties(level); debugTextShape << " | level" << llp.level() << " style (enum):" << llp.labelType(); if (llp.bulletCharacter().unicode() != 0) { debugTextShape << " | bullet" << llp.bulletCharacter(); } } seenStyles << ls->styleId(); } } bool first = true; foreach (KoCharacterStyle *style, styleManager->characterStyles()) { if (seenStyles.contains(style->styleId())) continue; if (first) { debugTextShape << "--- Character styles ---"; first = false; } debugTextShape << style->styleId() << style->name(); debugTextShape << style->font(); } first = true; foreach (KoListStyle *style, styleManager->listStyles()) { if (seenStyles.contains(style->styleId())) continue; if (first) { debugTextShape << "--- List styles ---"; first = false; } debugTextShape << style->styleId() << style->name() << (style == styleManager->defaultListStyle() ? "[Default]":""); } #endif } void TextTool::textDirectionChanged() { if (!m_allowActions || !m_textEditor.data()) return; QTextBlockFormat blockFormat; if (m_actionChangeDirection->isChecked()) { blockFormat.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::RightLeftTopBottom); } else { blockFormat.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::LeftRightTopBottom); } m_textEditor.data()->mergeBlockFormat(blockFormat); } void TextTool::setListLevel(int level) { if (level < 1 || level > 10) { return; } KoTextEditor *textEditor = m_textEditor.data(); if (textEditor->block().textList()) { ChangeListLevelCommand::CommandType type = ChangeListLevelCommand::SetLevel; ChangeListLevelCommand *cll = new ChangeListLevelCommand(*textEditor->cursor(), type, level); textEditor->addCommand(cll); editingPluginEvents(); } } void TextTool::insertAnnotation() { // HACK to avoid crash when we try to add a comment to an annotation shape. // We should not get here when the shape is an annotation shape, // but just disabling the insert_annotation action somehow does not work. if (m_textShape->shapeId() == AnnotationShape_SHAPEID) { return; } AnnotationTextShape *shape = (AnnotationTextShape*)KoShapeRegistry::instance()->value(AnnotationShape_SHAPEID)->createDefaultShape(canvas()->shapeController()->resourceManager()); textEditor()->addAnnotation(shape); // Set annotation creator. KConfig cfg("calligrarc"); cfg.reparseConfiguration(); KConfigGroup authorGroup(&cfg, "Author"); QStringList profiles = authorGroup.readEntry("profile-names", QStringList()); KSharedConfig::openConfig()->reparseConfiguration(); KConfigGroup appAuthorGroup( KSharedConfig::openConfig(), "Author"); QString profile = appAuthorGroup.readEntry("active-profile", ""); KConfigGroup cgs(&authorGroup, "Author-" + profile); if (profiles.contains(profile)) { KConfigGroup cgs(&authorGroup, "Author-" + profile); shape->setCreator(cgs.readEntry("creator")); } else { if (profile == "anonymous") { shape->setCreator("Anonymous"); } else { KUser user(KUser::UseRealUserID); shape->setCreator(user.property(KUser::FullName).toString()); } } // Set Annotation creation date. shape->setDate(QDate::currentDate().toString(Qt::ISODate)); } diff --git a/plugins/textshape/dialogs/SimpleCitationBibliographyWidget.cpp b/plugins/textshape/dialogs/SimpleCitationBibliographyWidget.cpp index 16d2e58a060..f7776ec4501 100644 --- a/plugins/textshape/dialogs/SimpleCitationBibliographyWidget.cpp +++ b/plugins/textshape/dialogs/SimpleCitationBibliographyWidget.cpp @@ -1,129 +1,118 @@ /* This file is part of the KDE project * Copyright (C) 2010 C. Boemann * * 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 "SimpleCitationBibliographyWidget.h" #include "ReferencesTool.h" #include "BibliographyPreview.h" #include "BibliographyTemplate.h" #include #include #include #include #include -#include SimpleCitationBibliographyWidget::SimpleCitationBibliographyWidget(ReferencesTool *tool, QWidget *parent) : QWidget(parent), m_blockSignals(false), - m_referenceTool(tool), - m_signalMapper(0) + m_referenceTool(tool) { widget.setupUi(this); Q_ASSERT(tool); m_templateGenerator = new BibliographyTemplate(KoTextDocument(m_referenceTool->editor()->document()).styleManager()); widget.addCitation->setDefaultAction(tool->action("insert_citation")); connect(widget.addCitation,SIGNAL(clicked(bool)),this,SIGNAL(doneWithFocus())); widget.addBibliography->setDefaultAction(tool->action("insert_bibliography")); connect(widget.addBibliography,SIGNAL(clicked(bool)),this,SIGNAL(doneWithFocus())); connect(widget.addBibliography, SIGNAL(aboutToShowMenu()), this, SLOT(prepareTemplateMenu())); connect(widget.addBibliography, SIGNAL(itemTriggered(int)), this, SLOT(applyTemplate(int))); widget.configureBibliography->setDefaultAction(tool->action("configure_bibliography")); connect(widget.configureBibliography,SIGNAL(clicked(bool)),this,SIGNAL(doneWithFocus())); } SimpleCitationBibliographyWidget::~SimpleCitationBibliographyWidget() { delete m_templateGenerator; } void SimpleCitationBibliographyWidget::setStyleManager(KoStyleManager *sm) { m_styleManager = sm; } void SimpleCitationBibliographyWidget::prepareTemplateMenu() { m_previewGenerator.clear(); - if (m_signalMapper) { - delete m_signalMapper; - m_signalMapper = 0; - } qDeleteAll(m_templateList.begin(), m_templateList.end()); m_templateList.clear(); - m_signalMapper = new QSignalMapper(); - m_templateList = m_templateGenerator->templates(); - connect(m_signalMapper, SIGNAL(mapped(int)), this, SLOT(pixmapReady(int))); - m_chooser = widget.addBibliography->addItemChooser(1); int index = 0; foreach (KoBibliographyInfo *info, m_templateList) { BibliographyPreview *preview = new BibliographyPreview(); preview->setStyleManager(KoTextDocument(m_referenceTool->editor()->document()).styleManager()); preview->setPreviewSize(QSize(200,120)); preview->updatePreview(info); - connect(preview, SIGNAL(pixmapGenerated()), m_signalMapper, SLOT(map())); - m_signalMapper->setMapping(preview, index); + connect(preview, &BibliographyPreview::pixmapGenerated, [this, index] { pixmapReady(index); }); m_previewGenerator.append(preview); ++index; //put dummy pixmaps until the actual pixmap previews are generated and added in pixmapReady() if (! widget.addBibliography->hasItemId(index)) { QPixmap pmm(QSize(200,120)); pmm.fill(Qt::white); widget.addBibliography->addItem(m_chooser, pmm, index); } } if (widget.addBibliography->isFirstTimeMenuShown()) { widget.addBibliography->addSeparator(); widget.addBibliography->addAction(m_referenceTool->action("insert_custom_bibliography")); connect(m_referenceTool->action("insert_custom_bibliography"), SIGNAL(triggered()), this, SLOT(insertCustomBibliography()), Qt::UniqueConnection); } } void SimpleCitationBibliographyWidget::insertCustomBibliography() { m_templateGenerator->moveTemplateToUsed(m_templateList.at(0)); m_referenceTool->insertCustomBibliography(m_templateList.at(0)); } void SimpleCitationBibliographyWidget::pixmapReady(int templateId) { // +1 to the templateId is because formattingButton does not allow id = 0 widget.addBibliography->addItem(m_chooser, m_previewGenerator.at(templateId)->previewPixmap(), templateId + 1); - disconnect(m_previewGenerator.at(templateId), SIGNAL(pixmapGenerated()), m_signalMapper, SLOT(map())); + disconnect(m_previewGenerator.at(templateId), &BibliographyPreview::pixmapGenerated, this, nullptr); m_previewGenerator.at(templateId)->deleteLater(); } void SimpleCitationBibliographyWidget::applyTemplate(int templateId) { m_templateGenerator->moveTemplateToUsed(m_templateList.at(templateId - 1)); m_referenceTool->editor()->insertBibliography(m_templateList.at(templateId - 1)); } diff --git a/plugins/textshape/dialogs/SimpleCitationBibliographyWidget.h b/plugins/textshape/dialogs/SimpleCitationBibliographyWidget.h index f758b567e55..4940860178e 100644 --- a/plugins/textshape/dialogs/SimpleCitationBibliographyWidget.h +++ b/plugins/textshape/dialogs/SimpleCitationBibliographyWidget.h @@ -1,69 +1,67 @@ /* This file is part of the KDE project * Copyright (C) 2010 C. Boemann * * 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 SIMPLECITATIONINDEXWIDGET_H #define SIMPLECITATIONINDEXWIDGET_H #include #include #include "FormattingButton.h" #include #include class ReferencesTool; class KoStyleManager; class KoBibliographyInfo; class BibliographyPreview; class BibliographyTemplate; -class QSignalMapper; class SimpleCitationBibliographyWidget : public QWidget { Q_OBJECT public: explicit SimpleCitationBibliographyWidget(ReferencesTool *tool,QWidget *parent = 0); ~SimpleCitationBibliographyWidget() override; public Q_SLOTS: void setStyleManager(KoStyleManager *sm); void prepareTemplateMenu(); void pixmapReady(int templateId); private Q_SLOTS: void applyTemplate(int templateId); void insertCustomBibliography(); Q_SIGNALS: void doneWithFocus(); private: Ui::SimpleCitationBibliographyWidget widget; KoStyleManager *m_styleManager; bool m_blockSignals; QTextBlock m_currentBlock; ReferencesTool *m_referenceTool; QList m_templateList; //each template in the template list will have have a previewGenerator that will be deleted after preview is generated QList m_previewGenerator; ItemChooserAction *m_chooser; - QSignalMapper *m_signalMapper; BibliographyTemplate *m_templateGenerator; }; #endif diff --git a/plugins/textshape/dialogs/SimpleParagraphWidget.cpp b/plugins/textshape/dialogs/SimpleParagraphWidget.cpp index c8eb9082e76..5f671c97a66 100644 --- a/plugins/textshape/dialogs/SimpleParagraphWidget.cpp +++ b/plugins/textshape/dialogs/SimpleParagraphWidget.cpp @@ -1,583 +1,582 @@ /* This file is part of the KDE project * Copyright (C) 2007, 2008, 2010 Thomas Zander * Copyright (C) 2009-2010 C. Boemann * Copyright (C) 2011 Mojtaba Shahi Senobari * Copyright (C) 2011-2012 Pierre Stirnweiss * * 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 "SimpleParagraphWidget.h" #include "TextTool.h" #include #include "FormattingButton.h" #include #include "StylesCombo.h" #include "StylesModel.h" #include "DockerStylesComboModel.h" #include "StylesDelegate.h" #include "ListLevelWidget.h" #include "commands/ChangeListLevelCommand.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include SimpleParagraphWidget::SimpleParagraphWidget(TextTool *tool, QWidget *parent) : QWidget(parent) , m_styleManager(0) , m_blockSignals(false) , m_tool(tool) , m_directionButtonState(Auto) , m_thumbnailer(new KoStyleThumbnailer()) , m_stylesModel(new StylesModel(0, StylesModel::ParagraphStyle)) , m_sortedStylesModel(new DockerStylesComboModel()) , m_stylesDelegate(0) { widget.setupUi(this); widget.alignCenter->setDefaultAction(tool->action("format_aligncenter")); widget.alignBlock->setDefaultAction(tool->action("format_alignblock")); // RTL layout will reverse the button order, but the align left/right then get mixed up. // this makes sure that whatever happens the 'align left' is to the left of the 'align right' if (QApplication::isRightToLeft()) { widget.alignLeft->setDefaultAction(tool->action("format_alignright")); widget.alignRight->setDefaultAction(tool->action("format_alignleft")); } else { widget.alignLeft->setDefaultAction(tool->action("format_alignleft")); widget.alignRight->setDefaultAction(tool->action("format_alignright")); } widget.decreaseIndent->setDefaultAction(tool->action("format_decreaseindent")); widget.increaseIndent->setDefaultAction(tool->action("format_increaseindent")); widget.changeTextDirection->setDefaultAction(tool->action("change_text_direction")); widget.moreOptions->setText("..."); widget.moreOptions->setToolTip(i18n("Change paragraph format")); connect(widget.moreOptions, SIGNAL(clicked(bool)), tool->action("format_paragraph"), SLOT(trigger())); connect(widget.changeTextDirection, SIGNAL(clicked()), this, SIGNAL(doneWithFocus())); connect(widget.alignCenter, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); connect(widget.alignBlock, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); connect(widget.alignLeft, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); connect(widget.alignRight, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); connect(widget.decreaseIndent, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); connect(widget.increaseIndent, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); widget.bulletListButton->setDefaultAction(tool->action("format_list")); fillListButtons(); widget.bulletListButton->addSeparator(); connect(widget.bulletListButton, SIGNAL(itemTriggered(int)), this, SLOT(listStyleChanged(int))); m_stylesModel->setStyleThumbnailer(m_thumbnailer); widget.paragraphStyleCombo->setStylesModel(m_sortedStylesModel); connect(widget.paragraphStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex))); connect(widget.paragraphStyleCombo, SIGNAL(newStyleRequested(QString)), this, SIGNAL(newStyleRequested(QString))); connect(widget.paragraphStyleCombo, SIGNAL(newStyleRequested(QString)), this, SIGNAL(doneWithFocus())); connect(widget.paragraphStyleCombo, SIGNAL(showStyleManager(int)), this, SLOT(slotShowStyleManager(int))); m_sortedStylesModel->setStylesModel(m_stylesModel); } SimpleParagraphWidget::~SimpleParagraphWidget() { QBuffer dev; KoXmlWriter writer(&dev); KoGenStyles genStyles; KoEmbeddedDocumentSaver mainStyles; KoShapeSavingContext savingContext(writer, genStyles, mainStyles); writer.startElement("templates:templates"); foreach (const KoListLevelProperties &llp, m_levelLibrary) { llp.saveOdf(&writer, savingContext); } writer.endElement(); // list-level-properties KSharedConfig::openConfig()->reparseConfiguration(); KConfigGroup appAuthorGroup( KSharedConfig::openConfig("calligrarc"), ""); appAuthorGroup.writeEntry("listLevelFormats", QString(dev.data())); //the style model is set on the comboBox who takes over ownership delete m_thumbnailer; } QPixmap SimpleParagraphWidget::generateListLevelPixmap(const KoListLevelProperties &llp) { KoZoomHandler zoomHandler; zoomHandler.setZoom(1.2); zoomHandler.setDpi(72, 72); KoInlineTextObjectManager itom; KoTextRangeManager tlm; TextShape textShape(&itom, &tlm); textShape.setSize(QSizeF(300, 100)); QTextCursor cursor (textShape.textShapeData()->document()); textShape.textShapeData()->document()->setUndoRedoEnabled(false); // let's noth bother QPixmap pm(48,48); pm.fill(Qt::transparent); QPainter p(&pm); p.translate(0, -1.5); p.setRenderHint(QPainter::Antialiasing); if(llp.labelType() == KoListStyle::None) { } else if (KoListStyle::isNumberingStyle(llp.labelType())) { KoListStyle listStyle; listStyle.setLevelProperties(llp); cursor.select(QTextCursor::Document); QTextCharFormat textCharFormat=cursor.blockCharFormat(); textCharFormat.setFontPointSize(11); textCharFormat.setFontWeight(QFont::Normal); cursor.setCharFormat(textCharFormat); QTextBlock cursorBlock = cursor.block(); KoTextBlockData data(cursorBlock); cursor.insertText("----"); listStyle.applyStyle(cursor.block(),1); cursorBlock = cursor.block(); KoTextBlockData data1(cursorBlock); cursor.insertText("\n----"); cursorBlock = cursor.block(); KoTextBlockData data2(cursorBlock); cursor.insertText("\n----"); cursorBlock = cursor.block(); KoTextBlockData data3(cursorBlock); } else { KoListStyle listStyle; listStyle.setLevelProperties(llp); cursor.select(QTextCursor::Document); QTextCharFormat textCharFormat=cursor.blockCharFormat(); textCharFormat.setFontPointSize(27); textCharFormat.setFontWeight(QFont::Normal); cursor.setBlockCharFormat(textCharFormat); QTextBlock cursorBlock = cursor.block(); KoTextBlockData data(cursorBlock); listStyle.applyStyle(cursor.block(),1); } KoTextDocumentLayout *lay = dynamic_cast(textShape.textShapeData()->document()->documentLayout()); if(lay) lay->layout(); KoShapePaintingContext paintContext; //FIXME textShape.paintComponent(p, zoomHandler, paintContext); return pm; } void SimpleParagraphWidget::fillListButtons() { KSharedConfig::openConfig()->reparseConfiguration(); KConfigGroup appAuthorGroup( KSharedConfig::openConfig("calligrarc"), ""); QString formats = appAuthorGroup.readEntry("listLevelFormats", QString()); formats.replace("\n", ""); if (false/*!formats.isEmpty()*/) { KoXmlDocument document; document.setContent(formats); KoXmlElement styleElem; forEachElement(styleElem, document.documentElement()) { KoListLevelProperties llp; // properties.loadOdf(scontext, styleElem); m_levelLibrary.append(llp); } // KoOdfLoadingContext odfContext; // KoShapeLoadingContext loadingContext(odfContext); } else { KoListStyle listStyle; KoListLevelProperties llp = listStyle.levelProperties(1); llp.setMargin(36.0); llp.setMarginIncrease(18.0); llp.setTextIndent(-18.0); llp.setTabStopPosition(36.0); llp.setLabelFollowedBy(KoListStyle::ListTab); llp.setDisplayLevel(4); llp.setLabelType(KoListStyle::BulletCharLabelType); llp.setBulletCharacter(QChar(0x2022)); // Bullet m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::BulletCharLabelType); llp.setBulletCharacter(QChar(0x25A0)); // Square m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::BulletCharLabelType); llp.setBulletCharacter(QChar(0x25C6)); // Rhombus m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::BulletCharLabelType); llp.setBulletCharacter(QChar(0x25CB)); // Circle m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::BulletCharLabelType); llp.setBulletCharacter(QChar(0x2714)); // HeavyCheckMark m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::BulletCharLabelType); llp.setBulletCharacter(QChar(0x2794)); // Right Arrow m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::NumberLabelType); llp.setNumberFormat(KoOdfNumberDefinition::Numeric); llp.setListItemSuffix("."); m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::NumberLabelType); llp.setNumberFormat(KoOdfNumberDefinition::Numeric); llp.setListItemSuffix(")"); m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::NumberLabelType); llp.setNumberFormat(KoOdfNumberDefinition::AlphabeticLowerCase); llp.setListItemSuffix("."); m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::NumberLabelType); llp.setNumberFormat(KoOdfNumberDefinition::AlphabeticLowerCase); llp.setListItemSuffix(")"); m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::NumberLabelType); llp.setNumberFormat(KoOdfNumberDefinition::AlphabeticUpperCase); llp.setListItemSuffix(""); m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::NumberLabelType); llp.setNumberFormat(KoOdfNumberDefinition::RomanLowerCase); llp.setListItemSuffix(""); m_levelLibrary.append(llp); llp.setLabelType(KoListStyle::NumberLabelType); llp.setNumberFormat(KoOdfNumberDefinition::RomanUpperCase); llp.setListItemSuffix(""); m_levelLibrary.append(llp); } m_recentChooserAction = widget.bulletListButton->addItemChooser(5, i18n("Recently Used Level Formats")); int id=1; m_recentListFormats.append(m_levelLibrary.at(0)); widget.bulletListButton->addItem(m_recentChooserAction, generateListLevelPixmap( m_recentListFormats.at(0)), id); m_libraryChooserAction = widget.bulletListButton->addItemChooser(5, i18n("Library of Level Formats")); id=1000; foreach(const KoListLevelProperties &llp, m_levelLibrary) { widget.bulletListButton->addItem(m_libraryChooserAction, generateListLevelPixmap(llp), id); QAction *a = widget.bulletListButton->addItemMenuItem(m_libraryChooserAction, id, i18n("Delete")); a->setData(id); connect(a, SIGNAL(triggered(bool)), this, SLOT(deleteLevelFormat())); a = widget.bulletListButton->addItemMenuItem(m_libraryChooserAction, id, i18n("Edit...")); a->setData(id); connect(a, SIGNAL(triggered(bool)), this, SLOT(editLevelFormat())); id++; } widget.bulletListButton->addSeparator(); QAction *action = new QAction(i18n("Define New Level Format..."),this); action->setToolTip(i18n("Define new bullet or numbering format")); widget.bulletListButton->addAction(action); connect(action, SIGNAL(triggered(bool)), this, SLOT(defineLevelFormat())); /* action = new QAction(i18n("Continue Previous List"),this); action->setToolTip(i18n("Continue the list from a previous list")); widget.bulletListButton->addAction(action); action = new QAction(i18n("Set Numbering Value..."),this); action->setToolTip(i18n("Set the numbering value")); widget.bulletListButton->addAction(action); */ } void SimpleParagraphWidget::defineLevelFormat() { ListLevelWidget *llw = new ListLevelWidget(); KoDialog dia(this); dia.setModal(true); dia.setButtons(KoDialog::Ok | KoDialog::Cancel); dia.setMainWidget(llw); dia.setWindowTitle(i18n("Define New List Level Format")); KoListLevelProperties llp; llp.setMargin(36.0); llp.setMarginIncrease(18.0); llp.setTextIndent(-18.0); llp.setTabStopPosition(36.0); llp.setLabelFollowedBy(KoListStyle::ListTab); llw->setDisplay(llp); if (dia.exec()) { for(int i = 0; i < m_levelLibrary.size(); ++i) { KoListLevelProperties llp = m_levelLibrary.at(i); llp.setLevel(1); widget.bulletListButton->addItem(m_libraryChooserAction, generateListLevelPixmap(m_levelLibrary.at(i)), i+1000); } int id = m_levelLibrary.size() + 1000; llw->save(llp); m_levelLibrary.append(llp); llp.setLevel(1); widget.bulletListButton->addItem(m_libraryChooserAction, generateListLevelPixmap(m_levelLibrary.at(id-1000)), id); QAction *a = widget.bulletListButton->addItemMenuItem(m_libraryChooserAction, id, i18n("Delete")); a->setData(id); connect(a, SIGNAL(triggered(bool)), this, SLOT(deleteLevelFormat())); a = widget.bulletListButton->addItemMenuItem(m_libraryChooserAction, id, i18n("Edit...")); a->setData(id); connect(a, SIGNAL(triggered(bool)), this, SLOT(editLevelFormat())); } } void SimpleParagraphWidget::setCurrentBlock(const QTextBlock &block) { if (block == m_currentBlock) { return; } m_currentBlock = block; m_blockSignals = true; struct Finally { Finally(SimpleParagraphWidget *p) { parent = p; } ~Finally() { parent->m_blockSignals = false; } SimpleParagraphWidget *parent; }; Finally finally(this); setCurrentFormat(m_currentBlock.blockFormat()); } void SimpleParagraphWidget::setCurrentFormat(const QTextBlockFormat &format) { if (!m_styleManager || format == m_currentBlockFormat) return; m_currentBlockFormat = format; int id = m_currentBlockFormat.intProperty(KoParagraphStyle::StyleId); KoParagraphStyle *style(m_styleManager->paragraphStyle(id)); if (style) { bool unchanged = true; foreach(int property, m_currentBlockFormat.properties().keys()) { switch (property) { case QTextFormat::ObjectIndex: case KoParagraphStyle::ListStyleId: case KoParagraphStyle::OutlineLevel: case KoParagraphStyle::ListStartValue: case KoParagraphStyle::IsListHeader: case KoParagraphStyle::UnnumberedListItem: continue; // These can be both content and style properties so let's ignore case KoParagraphStyle::BreakBefore: case KoParagraphStyle::MasterPageName: continue; default: break; } if (property == QTextBlockFormat::BlockAlignment) { //the default alignment can be retrieved in the defaultTextOption. However, calligra sets the Qt::AlignAbsolute flag, so we need to or this flag with the default alignment before comparing. if ((m_currentBlockFormat.property(property) != style->value(property)) && !(style->value(property).isNull() && ((m_currentBlockFormat.intProperty(property)) == int(m_currentBlock.document()->defaultTextOption().alignment()| Qt::AlignAbsolute)))) { unchanged = false; break; } else { continue; } } if (property == KoParagraphStyle::TextProgressionDirection) { if (style->value(property).isNull() && m_currentBlockFormat.intProperty(property) == KoText::LeftRightTopBottom) { //LTR seems to be Qt default when unset continue; } } if ((m_currentBlockFormat.property(property) != style->value(property)) && !(style->value(property).isNull() && !m_currentBlockFormat.property(property).toBool())) { //the last check seems to work. might be cause of a bug. The problem is when comparing an unset property in the style with a set to {0, false, ...) property in the format (eg. set then unset bold) unchanged = false; break; } } //we are updating the combo's selected item to what is the current format. we do not want this to apply the style as it would mess up the undo stack, the change tracking,... disconnect(widget.paragraphStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex))); m_sortedStylesModel->styleApplied(style); widget.paragraphStyleCombo->setCurrentIndex(m_sortedStylesModel->indexOf(style).row()); widget.paragraphStyleCombo->setStyleIsOriginal(unchanged); m_stylesModel->setCurrentParagraphStyle(id); widget.paragraphStyleCombo->slotUpdatePreview(); connect(widget.paragraphStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex))); } } void SimpleParagraphWidget::setStyleManager(KoStyleManager *sm) { Q_ASSERT(sm); if (!sm || m_styleManager == sm) { return; } if (m_styleManager) { disconnect(m_styleManager, SIGNAL(styleApplied(const KoParagraphStyle*)), this, SLOT(slotParagraphStyleApplied(const KoParagraphStyle*))); } m_styleManager = sm; //we want to disconnect this before setting the stylemanager. Populating the model apparently selects the first inserted item. We don't want this to actually set a new style. disconnect(widget.paragraphStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex))); m_stylesModel->setStyleManager(sm); m_sortedStylesModel->setStyleManager(sm); connect(widget.paragraphStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex))); connect(m_styleManager, SIGNAL(styleApplied(const KoParagraphStyle*)), this, SLOT(slotParagraphStyleApplied(const KoParagraphStyle*))); } void SimpleParagraphWidget::setInitialUsedStyles(QVector list) { m_sortedStylesModel->setInitialUsedStyles(list); } void SimpleParagraphWidget::listStyleChanged(int id) { emit doneWithFocus(); if (m_blockSignals) return; KoListLevelProperties llp; if (id >= 1000) { llp = m_levelLibrary.at(id-1000); } else { llp = m_recentListFormats.at(id-1); m_recentListFormats.removeAt(id-1); } llp.setLevel(1); m_recentListFormats.prepend(llp); if (m_recentListFormats.size() > 5) { m_recentListFormats.removeLast(); } for(int i = 0; i < m_recentListFormats.size(); ++i) { widget.bulletListButton->addItem(m_recentChooserAction, generateListLevelPixmap(m_recentListFormats.at(i)), i+1); // +1 as items are 1 based } KoTextEditor::ChangeListFlags flags(KoTextEditor::AutoListStyle | KoTextEditor::DontUnsetIfSame); m_tool->textEditor()->setListProperties(llp, flags); } void SimpleParagraphWidget::deleteLevelFormat() { int id = qobject_cast(sender())->data().toInt(); m_levelLibrary.takeAt(id-1000); widget.bulletListButton->removeLastItem(m_libraryChooserAction); for(int i = 0; i < m_levelLibrary.size(); ++i) { KoListLevelProperties llp = m_levelLibrary.at(i); llp.setLevel(1); if(llp.labelType() != KoListStyle::None) { widget.bulletListButton->addItem(m_libraryChooserAction, generateListLevelPixmap(m_levelLibrary.at(i)), i+1000); /* QAction *a = widget.bulletListButton->addItemMenuItem(m_libraryChooserAction, id, i18n("Delete")); a->setData(id); connect(a, SIGNAL(triggered(bool)), this, SLOT(deleteLevelFormat())); a = widget.bulletListButton->addItemMenuItem(m_libraryChooserAction, id, i18n("Edit...")); a->setData(id); connect(a, SIGNAL(triggered(bool)), this, SLOT(editLevelFormat())); */ } } } void SimpleParagraphWidget::editLevelFormat() { int id = qobject_cast(sender())->data().toInt(); ListLevelWidget *llw = new ListLevelWidget(); KoDialog dia(this); dia.setModal(true); dia.setButtons(KoDialog::Ok | KoDialog::Cancel); dia.setMainWidget(llw); dia.setWindowTitle(i18n("Edit List Level Format")); llw->setDisplay(m_levelLibrary.at(id-1000)); if (dia.exec()) { llw->save(m_levelLibrary[id-1000]); widget.bulletListButton->addItem(m_libraryChooserAction, generateListLevelPixmap(m_levelLibrary.at(id-1000)), id); } } void SimpleParagraphWidget::styleSelected(int index) { KoParagraphStyle *paragStyle = m_styleManager->paragraphStyle(m_sortedStylesModel->index(index, 0, QModelIndex()).internalId()); if (paragStyle) { emit paragraphStyleSelected(paragStyle); } emit doneWithFocus(); } void SimpleParagraphWidget::styleSelected(const QModelIndex &index) { if (!index.isValid()) { return; } KoParagraphStyle *paragStyle = m_styleManager->paragraphStyle(index.internalId()); if (paragStyle) { emit paragraphStyleSelected(paragStyle); } emit doneWithFocus(); } void SimpleParagraphWidget::slotShowStyleManager(int index) { int styleId = m_sortedStylesModel->index(index, 0, QModelIndex()).internalId(); emit showStyleManager(styleId); emit doneWithFocus(); } void SimpleParagraphWidget::slotParagraphStyleApplied(const KoParagraphStyle *style) { m_sortedStylesModel->styleApplied(style); } diff --git a/plugins/textshape/dialogs/SimpleParagraphWidget.h b/plugins/textshape/dialogs/SimpleParagraphWidget.h index 0987dcb2d79..7cb3499b556 100644 --- a/plugins/textshape/dialogs/SimpleParagraphWidget.h +++ b/plugins/textshape/dialogs/SimpleParagraphWidget.h @@ -1,105 +1,104 @@ /* This file is part of the KDE project * Copyright (C) 2007 Thomas Zander * Copyright (C) 2010 C. Boemann * Copyright (C) 2011 Mojtaba Shahi Senobari * Copyright (C) 2011-2012 Pierre Stirnweiss * * 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 SIMPLEPARAGRAPHWIDGET_H #define SIMPLEPARAGRAPHWIDGET_H #include #include #include #include #include class TextTool; class KoStyleManager; class KoParagraphStyle; class KoStyleThumbnailer; class StylesModel; class DockerStylesComboModel; class StylesDelegate; -class QSignalMapper; namespace Lists {class ListStyleItem;}; class SimpleParagraphWidget : public QWidget { Q_OBJECT public: explicit SimpleParagraphWidget(TextTool *tool, QWidget *parent = 0); ~SimpleParagraphWidget() override; void setInitialUsedStyles(QVector list); public Q_SLOTS: void setCurrentBlock(const QTextBlock &block); void setCurrentFormat(const QTextBlockFormat& format); void setStyleManager(KoStyleManager *sm); void slotShowStyleManager(int index); void slotParagraphStyleApplied(const KoParagraphStyle *style); Q_SIGNALS: void doneWithFocus(); void paragraphStyleSelected(KoParagraphStyle *); void newStyleRequested(const QString &name); void showStyleManager(int styleId); private Q_SLOTS: void defineLevelFormat(); void listStyleChanged(int id); void deleteLevelFormat(); void editLevelFormat(); void styleSelected(int index); void styleSelected(const QModelIndex &index); private: enum DirectionButtonState { LTR, RTL, Auto }; void updateDirection(DirectionButtonState state); QPixmap generateListLevelPixmap(const KoListLevelProperties &llp); void fillListButtons(); Ui::SimpleParagraphWidget widget; QList m_levelLibrary; KoStyleManager *m_styleManager; bool m_blockSignals; QTextBlock m_currentBlock; QTextBlockFormat m_currentBlockFormat; TextTool *m_tool; DirectionButtonState m_directionButtonState; KoStyleThumbnailer *m_thumbnailer; QList m_recentListFormats; ItemChooserAction *m_recentChooserAction; ItemChooserAction *m_libraryChooserAction; StylesModel *m_stylesModel; DockerStylesComboModel *m_sortedStylesModel; StylesDelegate *m_stylesDelegate; }; #endif diff --git a/plugins/textshape/dialogs/SimpleTableOfContentsWidget.cpp b/plugins/textshape/dialogs/SimpleTableOfContentsWidget.cpp index cc0312beec4..0cf305c4b8c 100644 --- a/plugins/textshape/dialogs/SimpleTableOfContentsWidget.cpp +++ b/plugins/textshape/dialogs/SimpleTableOfContentsWidget.cpp @@ -1,126 +1,115 @@ /* This file is part of the KDE project * Copyright (C) 2010 C. Boemann * Copyright (C) 2011 Gopalakrishna Bhat A * * 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 "SimpleTableOfContentsWidget.h" #include "ReferencesTool.h" #include "TableOfContentsConfigure.h" #include "TableOfContentsTemplate.h" #include "TableOfContentsPreview.h" #include #include #include #include #include #include #include -#include SimpleTableOfContentsWidget::SimpleTableOfContentsWidget(ReferencesTool *tool, QWidget *parent) : QWidget(parent), m_blockSignals(false), - m_referenceTool(tool), - m_signalMapper(0) + m_referenceTool(tool) { widget.setupUi(this); Q_ASSERT(tool); m_templateGenerator = new TableOfContentsTemplate(KoTextDocument(m_referenceTool->editor()->document()).styleManager()); widget.addToC->setIcon(koIcon("insert-table-of-contents")); connect(widget.addToC, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus())); connect(widget.addToC, SIGNAL(aboutToShowMenu()), this, SLOT(prepareTemplateMenu())); connect(widget.addToC, SIGNAL(itemTriggered(int)), this, SLOT(applyTemplate(int))); } SimpleTableOfContentsWidget::~SimpleTableOfContentsWidget() { delete m_templateGenerator; } void SimpleTableOfContentsWidget::setStyleManager(KoStyleManager *sm) { m_styleManager = sm; } void SimpleTableOfContentsWidget::prepareTemplateMenu() { m_previewGenerator.clear(); - if (m_signalMapper) { - delete m_signalMapper; - m_signalMapper = 0; - } qDeleteAll(m_templateList.begin(), m_templateList.end()); m_templateList.clear(); - m_signalMapper = new QSignalMapper(); - m_templateList = m_templateGenerator->templates(); - connect(m_signalMapper, SIGNAL(mapped(int)), this, SLOT(pixmapReady(int))); - m_chooser = widget.addToC->addItemChooser(1); int index = 0; foreach (KoTableOfContentsGeneratorInfo *info, m_templateList) { TableOfContentsPreview *preview = new TableOfContentsPreview(); preview->setStyleManager(KoTextDocument(m_referenceTool->editor()->document()).styleManager()); preview->setPreviewSize(QSize(200,120)); preview->updatePreview(info); - connect(preview, SIGNAL(pixmapGenerated()), m_signalMapper, SLOT(map())); - m_signalMapper->setMapping(preview, index); + connect(preview, &TableOfContentsPreview::pixmapGenerated, this, [this, index] { pixmapReady(index); }); m_previewGenerator.append(preview); ++index; //put dummy pixmaps until the actual pixmap previews are generated and added in pixmapReady() if (! widget.addToC->hasItemId(index)) { QPixmap pmm(QSize(200,120)); pmm.fill(Qt::white); widget.addToC->addItem(m_chooser, pmm, index); } } if (widget.addToC->isFirstTimeMenuShown()) { widget.addToC->addSeparator(); widget.addToC->addAction(m_referenceTool->action("insert_configure_tableofcontents")); connect(m_referenceTool->action("insert_configure_tableofcontents"), SIGNAL(triggered()), this, SLOT(insertCustomToC()), Qt::UniqueConnection); widget.addToC->addAction(m_referenceTool->action("format_tableofcontents")); } } void SimpleTableOfContentsWidget::pixmapReady(int templateId) { // +1 to the templateId is because formattingButton does not allow id = 0 widget.addToC->addItem(m_chooser, m_previewGenerator.at(templateId)->previewPixmap(), templateId + 1); - disconnect(m_previewGenerator.at(templateId), SIGNAL(pixmapGenerated()), m_signalMapper, SLOT(map())); + disconnect(m_previewGenerator.at(templateId), &TableOfContentsPreview::pixmapGenerated, this, nullptr); m_previewGenerator.at(templateId)->deleteLater(); } void SimpleTableOfContentsWidget::applyTemplate(int templateId) { m_templateGenerator->moveTemplateToUsed(m_templateList.at(templateId - 1)); m_referenceTool->editor()->insertTableOfContents(m_templateList.at(templateId - 1)); } void SimpleTableOfContentsWidget::insertCustomToC() { m_templateGenerator->moveTemplateToUsed(m_templateList.at(0)); m_referenceTool->insertCustomToC(m_templateList.at(0)); } diff --git a/plugins/textshape/dialogs/SimpleTableOfContentsWidget.h b/plugins/textshape/dialogs/SimpleTableOfContentsWidget.h index 9d414da7c67..74461ebb611 100644 --- a/plugins/textshape/dialogs/SimpleTableOfContentsWidget.h +++ b/plugins/textshape/dialogs/SimpleTableOfContentsWidget.h @@ -1,69 +1,67 @@ /* This file is part of the KDE project * Copyright (C) 2010 C. Boemann * Copyright (C) 2011 Gopalakrishna Bhat A * * 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 SIMPLETABLEOFCONTENTSWIDGET_H #define SIMPLETABLEOFCONTENTSWIDGET_H #include #include #include #include class ReferencesTool; class KoStyleManager; class KoTableOfContentsGeneratorInfo; class TableOfContentsPreview; -class QSignalMapper; class TableOfContentsTemplate; class SimpleTableOfContentsWidget : public QWidget { Q_OBJECT public: explicit SimpleTableOfContentsWidget(ReferencesTool *tool, QWidget *parent = 0); ~SimpleTableOfContentsWidget() override; public Q_SLOTS: void setStyleManager(KoStyleManager *sm); void prepareTemplateMenu(); void pixmapReady(int templateId); Q_SIGNALS: void doneWithFocus(); private Q_SLOTS: void applyTemplate(int templateId); void insertCustomToC(); private: Ui::SimpleTableOfContentsWidget widget; KoStyleManager *m_styleManager; bool m_blockSignals; QTextBlock m_currentBlock; QList m_templateList; //each template in the template list will have have a previewGenerator that will be deleted after preview is generated QList m_previewGenerator; ItemChooserAction *m_chooser; ReferencesTool *m_referenceTool; - QSignalMapper *m_signalMapper; TableOfContentsTemplate *m_templateGenerator; }; #endif diff --git a/plugins/textshape/dialogs/StylesModel.cpp b/plugins/textshape/dialogs/StylesModel.cpp index 4d166594355..1a73a491095 100644 --- a/plugins/textshape/dialogs/StylesModel.cpp +++ b/plugins/textshape/dialogs/StylesModel.cpp @@ -1,578 +1,572 @@ /* This file is part of the KDE project * Copyright (C) 2008 Thomas Zander * Copyright (C) 2011 C. Boemann * Copyright (C) 2011-2012 Pierre Stirnweiss * * 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 "StylesModel.h" #include #include #include #include #include #include #include -#include #include #include #include #include StylesModel::StylesModel(KoStyleManager *manager, AbstractStylesModel::Type modelType, QObject *parent) : AbstractStylesModel(parent), m_styleManager(0), m_currentParagraphStyle(0), m_defaultCharacterStyle(0), - m_styleMapper(new QSignalMapper(this)), m_provideStyleNone(false) { m_modelType = modelType; setStyleManager(manager); //Create a default characterStyle for the preview of "None" character style if (m_modelType == StylesModel::CharacterStyle) { m_defaultCharacterStyle = new KoCharacterStyle(); m_defaultCharacterStyle->setStyleId(NoneStyleId); m_defaultCharacterStyle->setName(i18n("None")); m_defaultCharacterStyle->setFontPointSize(12); m_provideStyleNone = true; } - - connect(m_styleMapper, SIGNAL(mapped(int)), this, SLOT(updateName(int))); } StylesModel::~StylesModel() { delete m_currentParagraphStyle; delete m_defaultCharacterStyle; } QModelIndex StylesModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column != 0) return QModelIndex(); if (! parent.isValid()) { if (row >= m_styleList.count()) return QModelIndex(); return createIndex(row, column, m_styleList[row]); } return QModelIndex(); } QModelIndex StylesModel::parent(const QModelIndex &child) const { Q_UNUSED(child); return QModelIndex(); } int StylesModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) return m_styleList.count(); return 0; } int StylesModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1; } QVariant StylesModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); int id = (int)index.internalId(); switch (role){ case Qt::DisplayRole: { return QVariant(); } case Qt::DecorationRole: { if (!m_styleThumbnailer) { return QPixmap(); } if (m_modelType == StylesModel::ParagraphStyle) { KoParagraphStyle *paragStyle = m_styleManager->paragraphStyle(id); if (paragStyle) { return m_styleThumbnailer->thumbnail(paragStyle); } if (!paragStyle && m_draftParStyleList.contains(id)) { return m_styleThumbnailer->thumbnail(m_draftParStyleList[id]); } } else { KoCharacterStyle *usedStyle = 0; if (id == NoneStyleId) { usedStyle = static_cast(m_currentParagraphStyle); if (!usedStyle) { usedStyle = m_defaultCharacterStyle; } usedStyle->setName(i18n("None")); if (usedStyle->styleId() >= 0) { //if the styleId is NoneStyleId, we are using the default character style usedStyle->setStyleId(-usedStyle->styleId()); //this style is not managed by the styleManager but its styleId will be used in the thumbnail cache as part of the key. } return m_styleThumbnailer->thumbnail(usedStyle); } else { usedStyle = m_styleManager->characterStyle(id); if (usedStyle) { return m_styleThumbnailer->thumbnail(usedStyle, m_currentParagraphStyle); } if (!usedStyle && m_draftCharStyleList.contains(id)) { return m_styleThumbnailer->thumbnail(m_draftCharStyleList[id]); } } } break; } case Qt::SizeHintRole: { return QVariant(QSize(250, 48)); } default: break; }; return QVariant(); } Qt::ItemFlags StylesModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; return (Qt::ItemIsSelectable | Qt::ItemIsEnabled); } void StylesModel::setCurrentParagraphStyle(int styleId) { if (!m_styleManager || m_currentParagraphStyle == m_styleManager->paragraphStyle(styleId) || !m_styleManager->paragraphStyle(styleId)) { return; //TODO do we create a default paragraphStyle? use the styleManager default? } if (m_currentParagraphStyle) { delete m_currentParagraphStyle; m_currentParagraphStyle = 0; } m_currentParagraphStyle = m_styleManager->paragraphStyle(styleId)->clone(); } void StylesModel::setProvideStyleNone(bool provide) { if (m_modelType == StylesModel::CharacterStyle) { m_provideStyleNone = provide; } } QModelIndex StylesModel::indexOf(const KoCharacterStyle *style) const { if (style) { return createIndex(m_styleList.indexOf(style->styleId()), 0, style->styleId()); } else { return QModelIndex(); } } QImage StylesModel::stylePreview(int row, const QSize &size) { if (!m_styleManager || !m_styleThumbnailer) { return QImage(); } if (m_modelType == StylesModel::ParagraphStyle) { KoParagraphStyle *usedStyle = 0; usedStyle = m_styleManager->paragraphStyle(index(row).internalId()); if (usedStyle) { return m_styleThumbnailer->thumbnail(usedStyle, size); } if (!usedStyle && m_draftParStyleList.contains(index(row).internalId())) { return m_styleThumbnailer->thumbnail(m_draftParStyleList[index(row).internalId()], size); } } else { KoCharacterStyle *usedStyle = 0; if (index(row).internalId() == (quintptr)NoneStyleId) { usedStyle = static_cast(m_currentParagraphStyle); if (!usedStyle) { usedStyle = m_defaultCharacterStyle; } usedStyle->setName(i18n("None")); if (usedStyle->styleId() >= 0) { usedStyle->setStyleId(-usedStyle->styleId()); //this style is not managed by the styleManager but its styleId will be used in the thumbnail cache as part of the key. } return m_styleThumbnailer->thumbnail(usedStyle, m_currentParagraphStyle, size); } else { usedStyle = m_styleManager->characterStyle(index(row).internalId()); if (usedStyle) { return m_styleThumbnailer->thumbnail(usedStyle, m_currentParagraphStyle, size); } if (!usedStyle && m_draftCharStyleList.contains(index(row).internalId())) { return m_styleThumbnailer->thumbnail(m_draftCharStyleList[index(row).internalId()],m_currentParagraphStyle, size); } } } return QImage(); } /* QImage StylesModel::stylePreview(QModelIndex &index, const QSize &size) { if (!m_styleManager || !m_styleThumbnailer) { return QImage(); } if (m_modelType == StylesModel::ParagraphStyle) { KoParagraphStyle *usedStyle = 0; usedStyle = m_styleManager->paragraphStyle(index.internalId()); if (usedStyle) { return m_styleThumbnailer->thumbnail(usedStyle, size); } if (!usedStyle && m_draftParStyleList.contains(index.internalId())) { return m_styleThumbnailer->thumbnail(m_draftParStyleList[index.internalId()], size); } } else { KoCharacterStyle *usedStyle = 0; if (index.internalId() == NoneStyleId) { usedStyle = static_cast(m_currentParagraphStyle); if (!usedStyle) { usedStyle = m_defaultCharacterStyle; } usedStyle->setName(i18n("None")); if (usedStyle->styleId() >= 0) { usedStyle->setStyleId(-usedStyle->styleId()); //this style is not managed by the styleManager but its styleId will be used in the thumbnail cache as part of the key. } return m_styleThumbnailer->thumbnail(usedStyle, m_currentParagraphStyle, size); } else { usedStyle = m_styleManager->characterStyle(index.internalId()); if (usedStyle) { return m_styleThumbnailer->thumbnail(usedStyle, m_currentParagraphStyle, size); } if (!usedStyle && m_draftCharStyleList.contains(index.internalId())) { return m_styleThumbnailer->thumbnail(m_draftCharStyleList[index.internalId()],m_currentParagraphStyle, size); } } } return QImage(); } */ void StylesModel::setStyleManager(KoStyleManager *sm) { if (sm == m_styleManager) return; if (m_styleManager) { disconnect(sm, SIGNAL(styleAdded(KoParagraphStyle*)), this, SLOT(addParagraphStyle(KoParagraphStyle*))); disconnect(sm, SIGNAL(styleAdded(KoCharacterStyle*)), this, SLOT(addCharacterStyle(KoCharacterStyle*))); disconnect(sm, SIGNAL(styleRemoved(KoParagraphStyle*)), this, SLOT(removeParagraphStyle(KoParagraphStyle*))); disconnect(sm, SIGNAL(styleRemoved(KoCharacterStyle*)), this, SLOT(removeCharacterStyle(KoCharacterStyle*))); } m_styleManager = sm; if (m_styleManager == 0) { return; } if (m_modelType == StylesModel::ParagraphStyle) { updateParagraphStyles(); connect(sm, SIGNAL(styleAdded(KoParagraphStyle*)), this, SLOT(addParagraphStyle(KoParagraphStyle*))); connect(sm, SIGNAL(styleRemoved(KoParagraphStyle*)), this, SLOT(removeParagraphStyle(KoParagraphStyle*))); } else { updateCharacterStyles(); connect(sm, SIGNAL(styleAdded(KoCharacterStyle*)), this, SLOT(addCharacterStyle(KoCharacterStyle*))); connect(sm, SIGNAL(styleRemoved(KoCharacterStyle*)), this, SLOT(removeCharacterStyle(KoCharacterStyle*))); } } void StylesModel::setStyleThumbnailer(KoStyleThumbnailer *thumbnailer) { m_styleThumbnailer = thumbnailer; } // called when the stylemanager adds a style void StylesModel::addParagraphStyle(KoParagraphStyle *style) { Q_ASSERT(style); QCollator collator; QList::iterator begin = m_styleList.begin(); int index = 0; for ( ; begin != m_styleList.end(); ++begin) { KoParagraphStyle *s = m_styleManager->paragraphStyle(*begin); if (!s && m_draftParStyleList.contains(*begin)) s = m_draftParStyleList[*begin]; // s should be found as the manager and the m_styleList should be in sync Q_ASSERT(s); if (collator.compare(style->name(),s->name()) < 0) { break; } ++index; } beginInsertRows(QModelIndex(), index, index); - m_styleList.insert(begin, style->styleId()); - m_styleMapper->setMapping(style, style->styleId()); - connect(style, SIGNAL(nameChanged(QString)), m_styleMapper, SLOT(map())); + int styleId = style->styleId(); + m_styleList.insert(begin, styleId); + connect(style, &KoParagraphStyle::nameChanged, this, [this, styleId] { updateName(styleId); }); endInsertRows(); } bool sortParagraphStyleByName(KoParagraphStyle *style1, KoParagraphStyle *style2) { Q_ASSERT(style1); Q_ASSERT(style2); return QCollator().compare(style1->name(), style2->name()) < 0; } void StylesModel::updateParagraphStyles() { Q_ASSERT(m_styleManager); beginResetModel(); m_styleList.clear(); QList styles = m_styleManager->paragraphStyles(); std::sort(styles.begin(), styles.end(), sortParagraphStyleByName); foreach(KoParagraphStyle *style, styles) { if (style != m_styleManager->defaultParagraphStyle()) { //The default character style is not user selectable. It only provides individual property defaults and is not a style per say. - m_styleList.append(style->styleId()); - m_styleMapper->setMapping(style, style->styleId()); - connect(style, SIGNAL(nameChanged(QString)), m_styleMapper, SLOT(map())); + int styleId = style->styleId(); + m_styleList.append(styleId); + connect(style, &KoParagraphStyle::nameChanged, this, [this, styleId] { updateName(styleId); }); } } endResetModel(); } // called when the stylemanager adds a style void StylesModel::addCharacterStyle(KoCharacterStyle *style) { Q_ASSERT(style); // find the place where we need to insert the style QCollator collator; QList::ConstIterator begin = m_styleList.constBegin(); int index = 0; // the None style should also be the first one so only start after it if (begin != m_styleList.constEnd() && *begin == NoneStyleId) { ++begin; ++index; } for ( ; begin != m_styleList.constEnd(); ++begin) { KoCharacterStyle *s = m_styleManager->characterStyle(*begin); if (!s && m_draftCharStyleList.contains(*begin)) s = m_draftCharStyleList[*begin]; // s should be found as the manager and the m_styleList should be in sync Q_ASSERT(s); if (collator.compare(style->name(),s->name()) < 0) { break; } ++index; } beginInsertRows(QModelIndex(), index, index); m_styleList.insert(index, style->styleId()); endInsertRows(); - m_styleMapper->setMapping(style, style->styleId()); - connect(style, SIGNAL(nameChanged(QString)), m_styleMapper, SLOT(map())); + int styleId = style->styleId(); + connect(style, &KoParagraphStyle::nameChanged, this, [this, styleId] (const QString &) { updateName(styleId); }); } bool sortCharacterStyleByName(KoCharacterStyle *style1, KoCharacterStyle *style2) { Q_ASSERT(style1); Q_ASSERT(style2); return QCollator().compare(style1->name(), style2->name()) < 0; } void StylesModel::updateCharacterStyles() { Q_ASSERT(m_styleManager); beginResetModel(); m_styleList.clear(); if (m_provideStyleNone && m_styleManager->paragraphStyles().count()) { m_styleList.append(NoneStyleId); } QList styles = m_styleManager->characterStyles(); std::sort(styles.begin(), styles.end(), sortCharacterStyleByName); foreach(KoCharacterStyle *style, styles) { if (style != m_styleManager->defaultCharacterStyle()) { //The default character style is not user selectable. It only provides individual property defaults and is not a style per say. - m_styleList.append(style->styleId()); - m_styleMapper->setMapping(style, style->styleId()); - connect(style, SIGNAL(nameChanged(QString)), m_styleMapper, SLOT(map())); + int styleId = style->styleId(); + m_styleList.append(styleId); + connect(style, &KoParagraphStyle::nameChanged, this, [this, styleId] { updateName(styleId); }); } } endResetModel(); } // called when the stylemanager removes a style void StylesModel::removeParagraphStyle(KoParagraphStyle *style) { int row = m_styleList.indexOf(style->styleId()); beginRemoveRows(QModelIndex(), row, row); - m_styleMapper->removeMappings(style); - disconnect(style, SIGNAL(nameChanged(QString)), m_styleMapper, SLOT(map())); + disconnect(style, &KoParagraphStyle::nameChanged, this, nullptr); m_styleList.removeAt(row); endRemoveRows(); } // called when the stylemanager removes a style void StylesModel::removeCharacterStyle(KoCharacterStyle *style) { int row = m_styleList.indexOf(style->styleId()); beginRemoveRows(QModelIndex(), row, row); - m_styleMapper->removeMappings(style); - disconnect(style, SIGNAL(nameChanged(QString)), m_styleMapper, SLOT(map())); + disconnect(style, &KoParagraphStyle::nameChanged, this, nullptr); m_styleList.removeAt(row); endRemoveRows(); } void StylesModel::updateName(int styleId) { // updating the name of a style can mean that the style needs to be moved inside the list to keep the sort order. QCollator collator; int oldIndex = m_styleList.indexOf(styleId); if (oldIndex >= 0) { int newIndex = 0; if (m_modelType == StylesModel::ParagraphStyle) { KoParagraphStyle *paragStyle = m_styleManager->paragraphStyle(styleId); if (!paragStyle && m_draftParStyleList.contains(styleId)) paragStyle = m_draftParStyleList.value(styleId); if (paragStyle) { m_styleThumbnailer->removeFromCache(paragStyle); QList::ConstIterator begin = m_styleList.constBegin(); for ( ; begin != m_styleList.constEnd(); ++begin) { // don't test again the same style if (*begin == styleId) { continue; } KoParagraphStyle *s = m_styleManager->paragraphStyle(*begin); if (!s && m_draftParStyleList.contains(*begin)) s = m_draftParStyleList[*begin]; // s should be found as the manager and the m_styleList should be in sync Q_ASSERT(s); if (collator.compare(paragStyle->name(), s->name()) < 0) { break; } ++newIndex; } if (oldIndex != newIndex) { // beginMoveRows needs the index where it would be placed when it is still in the old position // so add one when newIndex > oldIndex beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex > oldIndex ? newIndex + 1 : newIndex); m_styleList.removeAt(oldIndex); m_styleList.insert(newIndex, styleId); endMoveRows(); } } } else { KoCharacterStyle *characterStyle = m_styleManager->characterStyle(styleId); if (!characterStyle && m_draftCharStyleList.contains(styleId)) characterStyle = m_draftCharStyleList[styleId]; if (characterStyle) { m_styleThumbnailer->removeFromCache(characterStyle); QList::ConstIterator begin = m_styleList.constBegin(); if (begin != m_styleList.constEnd() && *begin == NoneStyleId) { ++begin; ++newIndex; } for ( ; begin != m_styleList.constEnd(); ++begin) { // don't test again the same style if (*begin == styleId) { continue; } KoCharacterStyle *s = m_styleManager->characterStyle(*begin); if (!s && m_draftCharStyleList.contains(*begin)) s = m_draftCharStyleList[*begin]; // s should be found as the manager and the m_styleList should be in sync Q_ASSERT(s); if (collator.compare(characterStyle->name(), s->name()) < 0) { break; } ++newIndex; } if (oldIndex != newIndex) { // beginMoveRows needs the index where it would be placed when it is still in the old position // so add one when newIndex > oldIndex beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex > oldIndex ? newIndex + 1 : newIndex); m_styleList.removeAt(oldIndex); m_styleList.insert(newIndex, styleId); endMoveRows(); } } } } } QModelIndex StylesModel::firstStyleIndex() { if (!m_styleList.count()) { return QModelIndex(); } return createIndex(m_styleList.indexOf(m_styleList.at(0)), 0, m_styleList.at(0)); } QList StylesModel::StyleList() { return m_styleList; } QHash StylesModel::draftParStyleList() { return m_draftParStyleList; } QHash StylesModel::draftCharStyleList() { return m_draftCharStyleList; } void StylesModel::addDraftParagraphStyle(KoParagraphStyle *style) { style->setStyleId(-(m_draftParStyleList.count()+1)); m_draftParStyleList.insert(style->styleId(), style); addParagraphStyle(style); } void StylesModel::addDraftCharacterStyle(KoCharacterStyle *style) { if (m_draftCharStyleList.count() == 0) // we have a character style "m_defaultCharacterStyle" with style id NoneStyleId in style model. style->setStyleId(-(m_draftCharStyleList.count()+2)); else style->setStyleId(-(m_draftCharStyleList.count()+1)); m_draftCharStyleList.insert(style->styleId(), style); addCharacterStyle(style); } void StylesModel::clearDraftStyles() { foreach(KoParagraphStyle *style, m_draftParStyleList.values()) { removeParagraphStyle(style); } m_draftParStyleList.clear(); foreach(KoCharacterStyle *style, m_draftCharStyleList.values()) { removeCharacterStyle(style); } m_draftCharStyleList.clear(); } StylesModel::Type StylesModel::stylesType() const { return m_modelType; } diff --git a/plugins/textshape/dialogs/StylesModel.h b/plugins/textshape/dialogs/StylesModel.h index 57adcd28583..c7e3cc5d7d6 100644 --- a/plugins/textshape/dialogs/StylesModel.h +++ b/plugins/textshape/dialogs/StylesModel.h @@ -1,173 +1,170 @@ /* This file is part of the KDE project * Copyright (C) 2008 Thomas Zander * Copyright (C) 2011 C. Boemann * Copyright (C) 2011-2012 Pierre Stirnweiss * * 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 MODEL_H #define MODEL_H #include "AbstractStylesModel.h" #include #include class KoStyleThumbnailer; class KoStyleManager; class KoParagraphStyle; class KoCharacterStyle; class QImage; -class QSignalMapper; /** This class is used to provide widgets (like the @class StylesCombo) the styles available to the document being worked on. The @class StylesModel can be of two types: character styles or paragraph styles type. This allows the widget to ignore the type of style it is handling. * Character styles in ODF can be specified in two ways. First, a named character style, specifying character formatting properties. It is meant to be used on a couple of individual characters. Secondly, a paragraph style also specifies character formatting properties, which are to be considered the default for that particular paragraph. * For this reason, the @class Stylesmodel, when of the type @value characterStyle, do not list the paragraph style names. Only the specific named character styles are listed. Additionally, as the first item, a virtual style "As paragraph" is provided. Selecting this "style" will set the character properties as specified by the paragraph style currently applied to the selection. * This class requires that a @class KoStyleManager and a @class KoStyleThumbnailer be set. See below methods. * * The StylesModel re-implement the AbstractStylesModel interface. Several components assume the following properties: * - the StylesModel is a flat list of items (this also means that "parent" QModelIndexes are always invalid) * - the StylesModel has only one column * - there is no header in the model * - only the following methods are used when updating the underlying model's data: resetModel, insertRows, moveRows, removeRows */ class StylesModel : public AbstractStylesModel { Q_OBJECT public: enum CategoriesInternalIds { NoneStyleId = -1 }; explicit StylesModel(KoStyleManager *styleManager, AbstractStylesModel::Type modelType, QObject *parent = 0); ~StylesModel() override; /** Re-implemented from QAbstractItemModel. */ QModelIndex index(int row, int column=0, const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QModelIndex parent(const QModelIndex &child) const override; int columnCount(const QModelIndex &parent) const override; /** *********************************** */ /** Specific methods of the StylesModel */ /** ************************* */ /** Initialising of the model */ /** Specify if the combo should provide the virtual style None. This style is a virtual style which equates to no style. It is only relevant for character styles. In case the "None" character style is selected, the character formatting properties of the paragraph style are used. A @class StylesModel of the @enum Type ParagraphStyle always has this property set to false. On the other hand, the default for a @class StylesModel of the @enum Type CharacterStyle is true. It is important to set this before setting the stylemanager on the model. The flag is used when populating the styles from the KoStyleManager. */ void setProvideStyleNone(bool provide); /** Sets the @class KoStyleManager of the model. Setting this will populate the styles. It is required that a @param manager is set before using the model. * CAUTION: Populating the style will select the first inserted item. If this model is already set on a view, this might cause the view to emit an item selection changed signal. */ void setStyleManager(KoStyleManager *manager); /** Sets the @class KoStyleThumbnailer of the model. It is required that a @param thumbnailer is set before using the model. */ void setStyleThumbnailer(KoStyleThumbnailer *thumbnailer) override; /** *************** */ /** Using the model */ /** Return a @class QModelIndex for the specified @param style. * @param style may be either a character or paragraph style. */ QModelIndex indexOf(const KoCharacterStyle *style) const override; /** Returns a QImage which is a preview of the style specified by @param row of the given @param size. * If size isn't specified, the default size of the given @class KoStyleThumbnailer is used. */ QImage stylePreview(int row, const QSize &size = QSize()) override; // QImage stylePreview(QModelIndex &index, const QSize &size = QSize()); /** Specifies which paragraph style is currently the active one (on the current paragraph). This is used in order to properly preview the "As paragraph" virtual character style. */ void setCurrentParagraphStyle(int styleId); /** Return the first index at list. */ QModelIndex firstStyleIndex(); /** Return style id list. */ QList StyleList(); /** Return new styles and their ids. */ QHash draftParStyleList(); QHash draftCharStyleList(); /** Add a paragraph style to paragraph style list but this style is not applied. */ void addDraftParagraphStyle(KoParagraphStyle *style); /** Add a character style to character style list but this style is not applied. */ void addDraftCharacterStyle(KoCharacterStyle *style); /** we call this when we apply our unapplied styles and we clear our list. */ void clearDraftStyles(); /** We call this when we want a clear style model. */ void clearStyleModel(); /** Returns the type of styles in the model */ AbstractStylesModel::Type stylesType() const override; private Q_SLOTS: void removeParagraphStyle(KoParagraphStyle*); void removeCharacterStyle(KoCharacterStyle*); void updateName(int styleId); public Q_SLOTS: void addParagraphStyle(KoParagraphStyle*); void addCharacterStyle(KoCharacterStyle*); private: void updateParagraphStyles(); void updateCharacterStyles(); protected: QList m_styleList; // list of style IDs QHash m_draftParStyleList; // list of new styles that are not applied QHash m_draftCharStyleList; private: KoStyleManager *m_styleManager; KoParagraphStyle *m_currentParagraphStyle; KoCharacterStyle *m_defaultCharacterStyle; - QSignalMapper *m_styleMapper; - bool m_provideStyleNone; }; #endif diff --git a/words/plugins/scripting/Tool.h b/words/plugins/scripting/Tool.h index 4dbf33297be..05842e89127 100644 --- a/words/plugins/scripting/Tool.h +++ b/words/plugins/scripting/Tool.h @@ -1,163 +1,159 @@ /* * This file is part of Words * * Copyright (c) 2007 Sebastian Sauer * Copyright (C) 2010 Boudewijn Rempt * * This program 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 program 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SCRIPTING_TOOL_H #define SCRIPTING_TOOL_H -#include #include #include #include #include #include #include #include #include "Module.h" #include "TextCursor.h" namespace Scripting { /** * The Tool class provides access to functionality like handling for * example current/active selections. * * Python sample code that walks through the actions the Tool provides * and executes each of them; * \code * import Words * tool = Words.tool() * def triggered(actionname): * print "Action %s executed" % actionname * tool.connect("actionTriggered(QString)",triggered) * for n in tool.actionNames(): * print "Trying to execute %s" % tool.actionText(n) * tool.triggerAction(n) * \endcode */ class Tool : public QObject { Q_OBJECT public: explicit Tool(Module* module) : QObject(module), m_module(module) { KWView* v = dynamic_cast< KWView* >(m_module->view()); KoCanvasBase* c = v ? v->canvasBase() : 0; m_toolproxy = c ? c->toolProxy() : 0; - m_signalMapper = new QSignalMapper(this); QHash actionhash = actions(); for (QHash::const_iterator it = actionhash.constBegin(); it != actionhash.constEnd(); ++it) { - connect(it.value(), SIGNAL(triggered()), m_signalMapper, SLOT(map())); - m_signalMapper->setMapping(it.value() , it.key()); + QString key = it.key(); + connect(it.value(), &QAction::triggered, this, [this, key] { actionTriggered(key); }); } - connect(m_signalMapper, SIGNAL(mapped(QString)), this, SIGNAL(actionTriggered(QString))); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), this, SIGNAL(changedTool())); } virtual ~Tool() {} KoToolSelection* toolSelection() const { return m_toolproxy ? m_toolproxy->selection() : 0; } KoTextEditor* textSelection() const { return dynamic_cast< KoTextEditor* >(toolSelection()); } QHash actions() const { return m_toolproxy ? m_toolproxy->actions() : QHash(); } public Q_SLOTS: /** Return true if there is actually a selection. */ bool hasSelection() const { return toolSelection() != 0; } /** Return true if the selected object is a text object. */ bool hasTextSelection() const { return textSelection() != 0; } /** Return the selected text. */ QString selectedText() const { KoTextEditor* h = textSelection(); return h ? h->selectedText() : QString(); } //TODO return the textEditor /** Return the active/current \a TextCursor object. */ /* QObject* cursor() { KoTextEditor* h = textSelection(); return h ? new TextCursor(this, h->caret()) : 0; } */ /** Set the active/current \a TextCursor object. */ /* bool setCursor(QObject* cursor) { kDebug(32010) << "Scripting::Selection::setCursor"; TextCursor* textcursor = dynamic_cast< TextCursor* >(cursor); if (! textcursor) return false; KWView* v = dynamic_cast< KWView* >(m_module->view()); KoCanvasBase* c = v ? v->KoCanvasBase() : 0; KoCanvasResourceManager* r = c ? c->resourceManager() : 0; if (! r) return false; QVariant variant; variant.setValue((QObject*) &textcursor->cursor()); //TODO store TextEditor? // the above can't work; storing a pointer to a value based object (QTextCursor). // I don't even think its possible to store a QTextCursor in any form in a QVarient. (TZ) //r->setResource(Words::CurrentTextCursor, variant); return true; } */ /** Return a list of the action names. */ QStringList actionNames() { return QStringList(actions().keys()); } /** Return the text the action with \p actionname has. */ QString actionText(const QString& actionname) { QAction* a = actions()[ actionname ]; return a ? a->text() : QString(); } /** Trigger the action with \p actionname . */ void triggerAction(const QString& actionname) { QAction* a = actions()[ actionname ]; if (a) a->trigger(); } Q_SIGNALS: /** This signal got emitted if an action was triggered. */ void actionTriggered(const QString& actionname); /** This signal got emitted if the tool changed. */ void changedTool(); private: Module* m_module; KoToolProxy* m_toolproxy; - QSignalMapper* m_signalMapper; }; } #endif