[prev in list] [next in list] [prev in thread] [next in thread] 

List:       koffice-devel
Subject:    a tool for editing paragraph spacing
From:       Florian Merz <FlorianMerz () gmx ! de>
Date:       2008-02-27 10:48:28
Message-ID: 200802271148.29175.FlorianMerz () gmx ! de
[Download RAW message or body]

Hi KOffice developers,

I've been working on a KoTool for the TextShape which tries to allow editing 
of paragraph spacing and indentation in a more visual and intuitive way. 
Please keep in mind, that right now this is not more than a proof of 
concept. There are lots of bugs and corner cases which need to be taken 
care of before this is ready for some real use. It also needs a few more 
features to be truly useful, such as line spacing, QTextListFormat support 
and QTextTableFormat support.

How to test it:
Apply the attached patch and recompile and install plugins/textshape/. Open 
KWord and write a few paragraphs of text (preferably multi-line paragraphs, 
but a single line works, too). Select the tool from the toolbox (sorry, it 
doesn't have its own icon yet). Click on any text paragraph in your 
document. The tool will now display a frame around the paragraph which 
consists of a number of rulers. You can drag any of these rulers to change 
the corresponding indentation or margin. The text should be layed out 
accordingly as soon as you release the mouse button.

What do you think about it? Any suggestions or ideas? If this is considered 
useful for koffice I'd be thankful for a code review. I'm not too sure I 
used the koffice libraries properly...

 Florian

["paragraph_spacing_tool.patch" (text/x-diff)]

Index: plugins/textshape/ParagraphSpacingToolFactory.cpp
===================================================================
--- plugins/textshape/ParagraphSpacingToolFactory.cpp	(Revision 0)
+++ plugins/textshape/ParagraphSpacingToolFactory.cpp	(Revision 0)
@@ -0,0 +1,43 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2006 Florian Merz <florianmerz@web.de>
+ *
+ * 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 "ParagraphSpacingToolFactory.h"
+#include "ParagraphSpacingTool.h"
+#include "TextShape.h"
+
+#include <klocale.h>
+
+ParagraphSpacingToolFactory::ParagraphSpacingToolFactory( QObject *parent )
+: KoToolFactory(parent, "ParagraphSpacingToolFactory_ID", i18n("Paragraph formatting \
tool")) +{
+    setToolTip(i18n("Paragraph formatting tool"));
+    setToolType(dynamicToolType());
+    setIcon("draw-text");
+    setPriority(2);
+    setActivationShapeId(TextShape_SHAPEID);
+}
+
+ParagraphSpacingToolFactory::~ParagraphSpacingToolFactory() {
+}
+
+KoTool * ParagraphSpacingToolFactory::createTool( KoCanvasBase *canvas ) {
+    return new ParagraphSpacingTool(canvas);
+}
+
+#include "ParagraphSpacingToolFactory.moc"
Index: plugins/textshape/HRuler.cpp
===================================================================
--- plugins/textshape/HRuler.cpp	(Revision 0)
+++ plugins/textshape/HRuler.cpp	(Revision 0)
@@ -0,0 +1,89 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Florian Merz <florianmerz@web.de>
+ *
+ * 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 "HRuler.h"
+
+#include <stdio.h>
+
+
+QPointF HRuler::arrowOrigin() const
+{
+    return QPointF(m_position.x(), m_position.y() + m_lineLength / 2.0);
+}
+
+QLineF HRuler::baseline() const
+{
+    return QLineF(m_position.x(), m_position.y(), m_position.x(), m_position.y() + \
m_lineLength); +}
+
+QLineF HRuler::ruler() const
+{
+    return QLineF(m_position.x() + m_value, m_position.y(),
+                  m_position.x() + m_value, m_position.y() + m_lineLength);
+}
+
+void HRuler::mouseMoveEvent(QPointF point)
+{
+    if (mousePressed()) {
+        moveRuler(point.x() - m_position.x());
+    }
+}
+
+void HRuler::paint(QPainter &painter) const
+{
+    QPointF origin(arrowOrigin());
+    QPointF dest(origin.x() + m_value, origin.y());
+    QLineF value(origin, dest);
+
+    if (mouseHovers())
+        painter.setPen(QPen(Qt::red));
+    else
+        painter.setPen(QPen(Qt::darkGray));
+
+    painter.drawLine(ruler());
+
+    painter.setPen(QPen(Qt::darkGray));
+
+    if (options() & drawBaseline && Ruler::value() != 0.0)
+        painter.drawLine(baseline());
+
+    if (options() & (drawBaseArrow | drawRulerArrow))
+        painter.drawLine(value);
+
+    if (m_value > 0.0) {
+        if (options() & drawBaseArrow)
+            paintArrow(painter, origin, 180.0, m_value);
+        if (options() & drawRulerArrow)
+            paintArrow(painter, dest, 0.0, m_value);
+    }
+    else {
+        if (options() & drawBaseArrow)
+            paintArrow(painter, origin, 0.0, -m_value);
+        if (options() & drawRulerArrow)
+            paintArrow(painter, dest, 180.0, -m_value);
+    }
+}
+
+bool HRuler::hitTest(QPointF point) const
+{
+    QRectF boundingRect(m_position.x() + m_value - 5.0, m_position.y(), 10.0, \
m_lineLength); +
+    return boundingRect.contains(point);
+}
+
Index: plugins/textshape/TextPlugin.cpp
===================================================================
--- plugins/textshape/TextPlugin.cpp	(Revision 779602)
+++ plugins/textshape/TextPlugin.cpp	(Arbeitskopie)
@@ -18,6 +18,7 @@
  */
 #include "TextPlugin.h"
 #include "TextToolFactory.h"
+#include "ParagraphSpacingToolFactory.h"
 #include "TextShapeFactory.h"
 
 #include <KoShapeRegistry.h>
@@ -31,6 +32,7 @@
     : QObject(parent)
 {
     KoToolRegistry::instance()->add(new TextToolFactory(parent));
+    KoToolRegistry::instance()->add(new ParagraphSpacingToolFactory(parent));
     KoShapeRegistry::instance()->add(new TextShapeFactory(parent));
 }
 
Index: plugins/textshape/ParagraphSpacingTool.h
===================================================================
--- plugins/textshape/ParagraphSpacingTool.h	(Revision 0)
+++ plugins/textshape/ParagraphSpacingTool.h	(Revision 0)
@@ -0,0 +1,134 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Florian Merz <florianmerz@web.de>
+ *
+ * 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 KOPARAGRAPHFORMATTOOL_H
+#define KOPARAGRAPHFORMATTOOL_H
+
+#include "TextShape.h"
+#include "Ruler.h"
+
+#include <KoTool.h>
+#include <KoParagraphStyle.h>
+#include <KoUnit.h>
+#include <KoCanvasBase.h>
+
+#include <QTextCursor>
+#include <QTextBlock>
+
+class QTextLayout;
+class QAbstractTextDocumentLayout;
+class TextShape;
+
+/**
+ * This is the tool for editing paragraph formatting
+ * It displays all paragraph formatting parameters directly on the canvas
+ * and allows to modify them, too.
+ */
+class ParagraphSpacingTool : public KoTool {
+    Q_OBJECT
+public:
+    explicit ParagraphSpacingTool(KoCanvasBase *canvas);
+    ~ParagraphSpacingTool();
+
+    void initializeHRuler(Ruler **ruler, int options);
+    void initializeVRuler(Ruler **ruler, int options);
+
+    // reimplemented from superclass
+    virtual void paint( QPainter &painter, const KoViewConverter &converter );
+
+    // select the text block and the given textShape
+    void selectTextBlock(TextShape *textShape, QTextBlock textBlock);
+
+    // deselect the current text block (for example use clicked somwhere else)
+    void deselectTextBlock();
+
+    // reimplemented from superclass
+    virtual void mousePressEvent( KoPointerEvent *event );
+
+    // reimplemented from superclass
+    virtual void mouseMoveEvent( KoPointerEvent *event );
+
+    // reimplemented from superclass
+    virtual void mouseReleaseEvent( KoPointerEvent *event );
+
+    // reimplemented from superclass
+    virtual void activate (bool temporary=false);
+
+    // reimplemented from superclass
+    virtual void deactivate();
+
+    virtual void repaintDecorations();
+
+public slots:
+    // should be called when any of the rulers needs a repaint
+    void scheduleRepaint();
+    
+    // should be called when the value of any of the rulers changed
+    void updateLayout();
+
+protected:
+    // get all necessary properties from the layout system and pass them to the \
controls +    void loadRulers();
+    void saveRulers();
+
+    // paint a label at the specified position
+    void paintLabel(QPainter &painter, const QPointF position, const QString text) \
const; +    // y-offset of the current text block in it's shape
+    // essentially a shortcut to access KoShapeData.documentOffset()
+    qreal textBlockOffset() const;
+
+    // maps document coordinates to coordinates of the current text block
+    QPointF mapDocumentToTextBlock(QPointF point) const;
+
+    // some internal api, if one of these is too slow we can turn it into a class \
member +    const QTextDocument *document() const { return textBlock().document(); }
+    QTextBlock textBlock() const { Q_ASSERT(m_textBlockValid); return m_block; }
+    QTextBlockFormat blockFormat() const { return textBlock().blockFormat(); }
+    QTextCharFormat charFormat() const { return textBlock().charFormat(); }
+    QTextLayout *textLayout() const { return textBlock().layout(); }
+    TextShape *textShape() const { Q_ASSERT(m_textShape != NULL); return \
m_textShape; } +
+private:
+    TextShape *m_textShape;
+    QTextBlock m_block;
+    KoParagraphStyle *m_paragraphStyle;
+    QRectF m_textRect;
+    QRectF m_borderRect;
+
+    QPointF m_mousePosition;
+
+    qreal m_firstLineHeight,
+          m_secondLineY;
+
+    bool m_singleLine,
+         m_textBlockValid,
+         m_needsRepaint;
+
+    Ruler *m_firstIndentRuler,
+          *m_followingIndentRuler,
+          *m_rightMarginRuler,
+          *m_topMarginRuler,
+          *m_bottomMarginRuler;
+
+    Ruler *m_activeRuler;
+    Ruler *m_hoverRuler;
+};
+
+#endif
+
Index: plugins/textshape/VRuler.cpp
===================================================================
--- plugins/textshape/VRuler.cpp	(Revision 0)
+++ plugins/textshape/VRuler.cpp	(Revision 0)
@@ -0,0 +1,103 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Florian Merz <florianmerz@web.de>
+ *
+ * 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 "VRuler.h"
+
+#include <stdio.h>
+
+QPointF VRuler::arrowOrigin() const
+{
+    return QPointF(m_position.x() + m_lineLength / 2.0, m_position.y());
+}
+
+QLineF VRuler::baseline() const
+{
+    return QLineF(m_position.x(), m_position.y(), m_position.x() + m_lineLength, \
m_position.y()); +}
+
+QLineF VRuler::ruler() const
+{
+    return QLineF(m_position.x(), m_position.y() + value(),
+                  m_position.x() + m_lineLength, m_position.y() + value());
+}
+
+QLineF VRuler::oldRuler() const
+{
+    return QLineF(m_position.x(), m_position.y() + oldValue(),
+                  m_position.x() + m_lineLength, m_position.y() + oldValue());
+}
+
+void VRuler::mouseMoveEvent(QPointF point)
+{
+    if (mousePressed())
+    {
+        moveRuler(point.y() - m_position.y());
+    }
+}
+
+void VRuler::paint(QPainter &painter) const
+{
+    QPointF origin(arrowOrigin());
+    QPointF dest(origin.x(), origin.y() + value());
+    QLineF distance(origin, dest);
+
+    if (mouseHovers() || mousePressed())
+        painter.setPen(QPen(Qt::red));
+    else
+        painter.setPen(QPen(Qt::darkGray));
+
+    painter.drawLine(ruler());
+
+    painter.setPen(QPen(Qt::darkGray));
+
+    if (options() & drawSides) {
+        painter.drawLine(baseline().p1(), oldRuler().p1());
+        painter.drawLine(baseline().p2(), oldRuler().p2());
+    }
+
+    if (options() & drawBaseline && value() != 0.0)
+        painter.drawLine(baseline());
+
+    if (value() != oldValue() && oldValue() != 0.0)
+        painter.drawLine(oldRuler());
+
+    if (options() & (drawBaseArrow | drawRulerArrow))
+        painter.drawLine(distance);
+
+    if (value() > 0.0) {
+        if (options() & drawBaseArrow)
+            paintArrow(painter, origin, 270.0, value());
+        if (options() & drawRulerArrow)
+            paintArrow(painter, dest, 90.0, value());
+    }
+    else {
+        if (options() & drawBaseArrow)
+            paintArrow(painter, origin, 90.0, -value());
+        if (options() & drawRulerArrow)
+            paintArrow(painter, dest, 270.0, -value());
+    }
+}
+
+bool VRuler::hitTest(QPointF point) const
+{
+    QRectF boundingRect(m_position.x(), m_position.y() + value() - 5.0, \
m_lineLength, 10.0); +
+    return boundingRect.contains(point);
+}
+
Index: plugins/textshape/CMakeLists.txt
===================================================================
--- plugins/textshape/CMakeLists.txt	(Revision 779602)
+++ plugins/textshape/CMakeLists.txt	(Arbeitskopie)
@@ -15,6 +15,11 @@
     Layout.cpp
     ListItemsHelper.cpp
     PluginHelperAction.cpp
+    ParagraphSpacingTool.cpp
+    ParagraphSpacingToolFactory.cpp
+    Ruler.cpp
+    VRuler.cpp
+    HRuler.cpp
 
     ChangeTracker.cpp
     TextChanges.cpp
Index: plugins/textshape/Ruler.h
===================================================================
--- plugins/textshape/Ruler.h	(Revision 0)
+++ plugins/textshape/Ruler.h	(Revision 0)
@@ -0,0 +1,133 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Florian Merz <florianmerz@web.de>
+ *
+ * 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 RULER_H
+#define RULER_H
+
+#include <KoUnit.h>
+
+#include <QObject>
+#include <QPainter>
+#include <QPointF>
+
+/** A Ruler is a user interface element which can be used to change the Spacing and \
Dimensions in a KOffice document, + * for example the spacing in a text paragraph, by \
simply dragging a ruler line across the screen */ +class Ruler : public QObject
+{
+    Q_OBJECT
+public:
+    Ruler(QObject *parent);
+
+    virtual ~Ruler() {}
+
+    virtual QPointF arrowOrigin() const = 0;
+    void setBaseline(QPointF position, qreal lineLength);
+
+    KoUnit unit() { return m_unit; }
+    void setUnit(KoUnit unit);
+
+    qreal value() const;
+    QString valueString() const;
+    void setValue(qreal value);
+
+    qreal oldValue() const { return m_oldValue; }
+
+    // distance in points between steps when moving the ruler, set this to 0.0 to \
disable stepping +    qreal stepValue() const { return m_stepValue; }
+    void setStepValue(qreal stepValue) { m_stepValue = stepValue; }
+
+    qreal minimumValue() const;
+    void setMinimumValue(qreal value);
+
+    qreal maximumValue() const;
+    void setMaximumValue(qreal value);
+
+    bool isVisible() { return m_visible; }
+    void setVisible(bool visible) { m_visible = visible; }
+    void hide() { setVisible(false); }
+    void show() { setVisible(true); }
+
+    // these options specify how the ruler will be drawn
+    enum Options{
+        noOptions = 0,
+        drawBaseArrow  = 1<<0,
+        drawRulerArrow = 1<<1,
+        drawBaseline = 1<<2,
+        drawSides = 1<<3
+    };
+
+    int options() const { return m_options; }
+    void setOptions(int options) { m_options = m_options | options; }
+
+    virtual void mousePressEvent(QPointF point);
+    virtual void mouseReleaseEvent(QPointF point);
+    virtual void mouseMoveEvent(QPointF point);
+    virtual void mouseEnterEvent(QPointF point);
+    virtual void mouseLeaveEvent(QPointF point);
+
+    // paint this Ruler using painter
+    virtual void paint(QPainter &painter) const = 0;
+
+    // returns the boundingRectangle of this arrowcontrol
+    // this is used to determine if this arrowhead should respond to mouse events
+    virtual bool hitTest(const QPointF point) const = 0;
+
+signals:
+    // emitted when value has been changed via the user interface
+    // (in contrast to via the setValue() method)
+    void valueChanged(qreal value);
+
+    // emitted when the ruler needs to be repainted
+    void needsRepaint();
+
+protected:
+    void moveRuler(qreal value);
+    void setOldValue(qreal value) { m_oldValue = value; }
+
+    void paintArrow(QPainter &painter, const QPointF tip, const qreal angle, qreal \
value) const; +
+    bool mousePressed() const { return m_mousePressed; }
+    bool mouseHovers() const { return m_mouseHover; }
+
+    // baseline properties
+    QPointF m_position;
+    qreal m_lineLength;
+
+    // all values in points
+    qreal m_value; // value is distance between baseline and ruler line
+    qreal m_oldValue;
+    qreal m_stepValue;
+    qreal m_minValue;
+    qreal m_maxValue;
+    KoUnit m_unit;
+
+private:
+    // some convenience methods which describe how an arrow should be rendered
+    static qreal arrowSize() { return 10.0; }
+    static qreal arrowDiagonal() { return arrowSize() / sqrt(2.0) / 2.0; }
+    static qreal arrowMinimumValue() { return arrowDiagonal() *2.0 + 2.0; }
+
+    bool m_visible;
+    bool m_mousePressed;
+    bool m_mouseHover;
+    int m_options;
+};
+
+#endif
+
Index: plugins/textshape/ParagraphSpacingToolFactory.h
===================================================================
--- plugins/textshape/ParagraphSpacingToolFactory.h	(Revision 0)
+++ plugins/textshape/ParagraphSpacingToolFactory.h	(Revision 0)
@@ -0,0 +1,35 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2006 Florian Merz <florianmerz@web.de>
+ *
+ * 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 KOPARAGRAPHFORMATTOOLFACTORY_H
+#define KOPARAGRAPHFORMATTOOLFACTORY_H
+
+#include <KoToolFactory.h>
+
+
+class ParagraphSpacingToolFactory : public KoToolFactory {
+    Q_OBJECT
+public:
+    explicit ParagraphSpacingToolFactory(QObject *parent);
+    ~ParagraphSpacingToolFactory();
+
+    KoTool * createTool(KoCanvasBase *canvas);
+};
+
+#endif
Index: plugins/textshape/HRuler.h
===================================================================
--- plugins/textshape/HRuler.h	(Revision 0)
+++ plugins/textshape/HRuler.h	(Revision 0)
@@ -0,0 +1,51 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Florian Merz <florianmerz@web.de>
+ *
+ * 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 HARROWCONTROL_H
+#define HARROWCONTROL_H
+
+#include "Ruler.h"
+
+#include <QPainter>
+#include <QPointF>
+
+/* class representing an arrow control element
+ * the arrowhead can be dragged to modify the arrows length
+ */
+class HRuler : public Ruler
+{
+public:
+    HRuler(QObject *parent)
+        : Ruler(parent) {}
+    ~HRuler() {}
+
+    virtual QPointF arrowOrigin() const;
+
+    virtual void mouseMoveEvent(QPointF point);
+
+    virtual void paint(QPainter &painter) const;
+    virtual bool hitTest(const QPointF point) const;
+
+protected:
+    QLineF baseline() const;
+    QLineF ruler() const;
+};
+
+#endif
+
Index: plugins/textshape/ParagraphSpacingTool.cpp
===================================================================
--- plugins/textshape/ParagraphSpacingTool.cpp	(Revision 0)
+++ plugins/textshape/ParagraphSpacingTool.cpp	(Revision 0)
@@ -0,0 +1,382 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Florian Merz <florianmerz@web.de>
+ *
+ * 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 "ParagraphSpacingTool.h"
+
+#include "HRuler.h"
+#include "VRuler.h"
+
+#include <KoPointerEvent.h>
+#include <KoCanvasBase.h>
+#include <KoShapeManager.h>
+#include <KoStyleManager.h>
+#include <KoTextDocumentLayout.h>
+
+#include <QTextLine>
+#include <QTextBlock>
+#include <QTextDocument>
+#include <QAbstractTextDocumentLayout>
+#include <QString>
+#include <QPen>
+
+#include <iostream>
+
+ParagraphSpacingTool::ParagraphSpacingTool(KoCanvasBase *canvas)
+    : KoTool(canvas),
+    m_textShape(NULL),
+    m_paragraphStyle(NULL),
+    m_textBlockValid(false),
+    m_needsRepaint(false),
+    m_activeRuler(NULL),
+    m_hoverRuler(NULL)
+{
+    int defaultOptions = Ruler::drawRulerArrow | Ruler::drawBaseline;
+    int sidesOptions = defaultOptions | Ruler::drawSides;
+
+    initializeHRuler(&m_firstIndentRuler, defaultOptions);
+    m_firstIndentRuler->setMinimumValue(0.0);
+
+    initializeHRuler(&m_followingIndentRuler, defaultOptions);
+    m_followingIndentRuler->setMinimumValue(0.0);
+
+    initializeHRuler(&m_rightMarginRuler, defaultOptions);
+    m_rightMarginRuler->setMaximumValue(0.0);
+
+    initializeVRuler(&m_topMarginRuler, sidesOptions);
+    m_topMarginRuler->setMinimumValue(0.0);
+
+    initializeVRuler(&m_bottomMarginRuler, sidesOptions);
+    m_bottomMarginRuler->setMinimumValue(0.0);
+}
+
+ParagraphSpacingTool::~ParagraphSpacingTool()
+{}
+
+// helper function to initalize rulers
+void ParagraphSpacingTool::initializeHRuler(Ruler **ruler, int options)
+{
+    *ruler = new HRuler(this);
+    (*ruler)->setOptions(options);
+    (*ruler)->setUnit(canvas()->unit());
+    connect(*ruler, SIGNAL(needsRepaint()), this, SLOT(scheduleRepaint()));
+    connect(*ruler, SIGNAL(valueChanged(qreal)), this, SLOT(updateLayout()));
+}
+
+void ParagraphSpacingTool::initializeVRuler(Ruler **ruler, int options)
+{
+    *ruler = new VRuler(this);
+    (*ruler)->setOptions(options);
+    (*ruler)->setUnit(canvas()->unit());
+    connect(*ruler, SIGNAL(needsRepaint()), this, SLOT(scheduleRepaint()));
+    connect(*ruler, SIGNAL(valueChanged(qreal)), this, SLOT(updateLayout()));
+}
+
+void ParagraphSpacingTool::loadRulers()
+{
+    // create rectangles
+    m_singleLine = (textLayout()->lineCount() == 1);
+
+    m_textRect = textLayout()->boundingRect();
+    if (!m_singleLine && m_paragraphStyle->textIndent() < 0.0)
+        m_textRect.setLeft(m_textRect.left() - m_paragraphStyle->textIndent());
+    else if (m_singleLine)
+        m_textRect.setLeft(m_textRect.left() - m_paragraphStyle->textIndent() + \
m_paragraphStyle->leftMargin()); +
+    m_borderRect = QRectF(m_textRect.left() - m_paragraphStyle->leftMargin(),
+            m_textRect.y() - m_paragraphStyle->topMargin(),
+            m_paragraphStyle->leftMargin() +  m_textRect.width() + \
m_paragraphStyle->rightMargin(), +            m_paragraphStyle->topMargin() + \
m_textRect.height() + m_paragraphStyle->bottomMargin()); +
+    // load rulers
+    m_rightMarginRuler->setBaseline(QPointF(m_borderRect.right(), m_textRect.top()), \
m_textRect.height()); +    \
m_rightMarginRuler->setValue(-m_paragraphStyle->rightMargin()); +
+    m_topMarginRuler->setBaseline(QPointF(m_borderRect.left(), m_borderRect.top()), \
m_borderRect.width()); +    \
m_topMarginRuler->setValue(m_paragraphStyle->topMargin()); +
+    m_bottomMarginRuler->setBaseline(QPointF(m_borderRect.left(), m_textRect.top() + \
m_textRect.height()), +            m_borderRect.width());
+    m_bottomMarginRuler->setValue(m_paragraphStyle->bottomMargin());
+
+    if (m_singleLine) {
+        m_firstLineHeight = 0.0;
+        m_secondLineY = 0.0;
+
+        m_firstIndentRuler->setBaseline(QPointF(m_borderRect.left(), \
m_textRect.top()), m_textRect.height()); +        \
m_firstIndentRuler->setValue(m_paragraphStyle->leftMargin() + \
m_paragraphStyle->textIndent()); +
+        m_followingIndentRuler->hide();
+    }
+    else {
+        m_firstLineHeight = textLayout()->lineAt(0).height();
+        m_secondLineY = textLayout()->lineAt(1).y() - textLayout()->lineAt(0).y();
+        qreal lineBorder = (m_firstLineHeight + m_secondLineY) / 2.0;
+
+        m_firstIndentRuler->setBaseline(QPointF(m_borderRect.left(), \
m_textRect.top()), lineBorder ); +        \
m_firstIndentRuler->setValue(m_paragraphStyle->leftMargin() + \
m_paragraphStyle->textIndent()); +
+        m_followingIndentRuler->setBaseline(QPointF(m_borderRect.x(), \
m_textRect.top() + lineBorder), +                m_textRect.height() - lineBorder);
+        m_followingIndentRuler->setValue(m_paragraphStyle->leftMargin());
+        m_followingIndentRuler->show();
+    }
+}
+
+
+void ParagraphSpacingTool::saveRulers()
+{
+    // what happens if the style change forces the paragraph to another shape
+    // we have to somehow find out where this new KoShape is...
+    // is there a list of alle TextShapes belonging to a QTextDocument somewhere?
+    Q_ASSERT(m_paragraphStyle != NULL);
+
+    m_paragraphStyle->setLeftMargin(m_followingIndentRuler->value());
+    m_paragraphStyle->setRightMargin(-m_rightMarginRuler->value());
+    m_paragraphStyle->setTopMargin(m_topMarginRuler->value());
+    m_paragraphStyle->setBottomMargin(m_bottomMarginRuler->value());
+    m_paragraphStyle->setTextIndent(m_firstIndentRuler->value() - \
m_followingIndentRuler->value()); +
+    QTextBlockFormat format;
+    m_paragraphStyle->applyStyle(format);
+
+    QTextCursor cursor(textBlock());
+    cursor.mergeBlockFormat(format);
+
+    m_block = cursor.block();
+}
+
+void ParagraphSpacingTool::updateLayout()
+{
+    saveRulers();
+    static_cast<KoTextDocumentLayout*>(document()->documentLayout())->layout();
+    loadRulers();
+
+    repaintDecorations();
+}
+
+void ParagraphSpacingTool::paintLabel(QPainter &painter, const QPointF position, \
const QString text) const +{
+    QSizeF size(60.0, 10.0);
+    QRectF label(position.x() - size.width() / 2.0, position.y() - size.height() / \
2.0, size.width(), size.height()); +
+    painter.save();
+    painter.fillRect(label, QColor(Qt::red));
+    painter.setPen(QPen(Qt::white));
+    painter.drawText(label, Qt::AlignHCenter | Qt::AlignVCenter, text);
+    painter.restore();
+}
+
+void ParagraphSpacingTool::paint(QPainter &painter, const KoViewConverter \
&converter) +{
+    if (!m_textBlockValid)
+        return;
+
+    painter.save();
+
+    // transform painter from view coordinate system to text block coordinate system
+    painter.setMatrix(textShape()->absoluteTransformation(&converter) * \
painter.matrix()); +    KoShape::applyConversion(painter, converter);
+    painter.translate(0.0, -textBlockOffset());
+
+    painter.setPen(Qt::darkGray);
+
+    // paint line between first line and following lines
+    qreal lineMiddle((m_firstLineHeight + m_secondLineY) / 2.0);
+    if (!m_singleLine) {
+        painter.drawLine(m_borderRect.x(), m_textRect.y() + lineMiddle, \
m_textRect.right(), m_textRect.y() + lineMiddle); +    }
+
+    foreach (Ruler *arrowRuler, findChildren<Ruler *>()) {
+        if (arrowRuler->isVisible())
+            arrowRuler->paint(painter);
+    }
+
+    painter.restore();
+
+    if (m_activeRuler != NULL) {
+        QPointF labelPosition(converter.documentToView(m_mousePosition));
+        labelPosition.rx() += 40.0;
+        labelPosition.ry() += 10.0;
+        paintLabel(painter, labelPosition, m_activeRuler->valueString());
+    }
+
+}
+
+void ParagraphSpacingTool::repaintDecorations()
+{
+    if (!m_needsRepaint || m_textShape == NULL)
+        return;
+
+    // how do we determine the proper area for repainting?
+    // FIXME: we currently set repaint area to the shape size + 10 pt in every \
direction. this is wrong in so many ways +    QRectF \
boundingRect(m_textShape->boundingRect()); +    \
boundingRect.setLeft(boundingRect.left() - 10.0); +    \
boundingRect.setRight(boundingRect.right() + 10.0); +    \
boundingRect.setTop(boundingRect.top() - 10.0); +    \
boundingRect.setBottom(boundingRect.bottom() + 10.0); +
+    canvas()->updateCanvas(boundingRect);
+    m_needsRepaint = false;
+}
+
+void ParagraphSpacingTool::scheduleRepaint()
+{
+    m_needsRepaint = true;
+}
+
+qreal ParagraphSpacingTool::textBlockOffset() const
+{
+    QRectF blockBoundingRect = textLayout()->boundingRect();
+    KoTextShapeData *textShapeData = static_cast<KoTextShapeData*> \
(textShape()->userData()); +
+    return textShapeData->documentOffset();
+}
+
+QPointF ParagraphSpacingTool::mapDocumentToTextBlock(QPointF point) const
+{
+    QMatrix matrix = textShape()->absoluteTransformation(NULL);
+    matrix.translate(0.0, -textBlockOffset());
+    return matrix.inverted().map(point);
+}
+
+void ParagraphSpacingTool::selectTextBlock(TextShape *textShape, QTextBlock block)
+{
+    // the text block is already selected, no need for a repaint and all that
+    if (m_textBlockValid && block == m_block && textShape == m_textShape)
+        return;
+
+    m_textBlockValid = true;
+    scheduleRepaint();
+
+    m_textShape = textShape;
+    m_block = block;
+    m_paragraphStyle = KoParagraphStyle::fromBlock(textBlock());
+
+    loadRulers();
+}
+
+void ParagraphSpacingTool::deselectTextBlock()
+{
+    if (!m_textBlockValid)
+        return;
+
+    m_textBlockValid = false;
+    scheduleRepaint();
+}
+
+void ParagraphSpacingTool::mousePressEvent(KoPointerEvent *event)
+{
+    // we check if the mouse pointer pressed on one of the current textblock's \
rulers +    if (m_textBlockValid) {
+        QPointF point(mapDocumentToTextBlock(event->point));
+
+        foreach (Ruler *arrowRuler, findChildren<Ruler *>()) {
+            if (arrowRuler->hitTest(point)) {
+                // release one control, activate another
+                arrowRuler->mousePressEvent(point);
+
+                m_activeRuler = arrowRuler;
+                repaintDecorations();
+                return;
+            }
+        }
+
+        m_activeRuler = NULL;
+    }
+
+    // if there is a new text block (possibly in another shape) under the mouse, \
then we select that block +    TextShape *textShape = dynamic_cast<TextShape*> \
(canvas()->shapeManager()->shapeAt(event->point)); +    if (textShape) {
+        KoTextShapeData *textShapeData = static_cast<KoTextShapeData*> \
(textShape->userData()); +        QTextDocument *document = \
textShapeData->document(); +
+        int position = \
document->documentLayout()->hitTest(textShape->convertScreenPos(event->point), \
Qt::ExactHit); +        if (position != -1) {
+            selectTextBlock(textShape, document->findBlock(position));
+            repaintDecorations();
+            return;
+        }
+    }
+
+    // if there is no ruler and no text block und the mouse, then we simply deselect \
the current text block +    deselectTextBlock();
+    repaintDecorations();
+    return;
+}
+
+void ParagraphSpacingTool::mouseMoveEvent(KoPointerEvent *event)
+{
+    scheduleRepaint();
+    // need to map to the same coordinate system as the paint process
+    m_mousePosition = event->point;
+
+    if (m_textBlockValid) {
+        QPointF point(mapDocumentToTextBlock(event->point));
+
+        // send a mouseMoveEvent to the activeRuler
+        // do this first so the active control can resize if necessary
+        if (m_activeRuler != NULL) {
+            m_activeRuler->mouseMoveEvent(point);
+        }
+
+        // check if we left the element over which we were hovering
+        if (m_hoverRuler != NULL && !m_hoverRuler->hitTest(point)) {
+            m_hoverRuler->mouseLeaveEvent(point);
+            m_hoverRuler = NULL;
+        }
+
+        // check if we are hovering over a new control
+        if (m_hoverRuler == NULL) {
+            foreach (Ruler *arrowRuler, findChildren<Ruler *>()) {
+                if (arrowRuler->hitTest(point))
+                {
+                    m_hoverRuler = arrowRuler;
+                    m_hoverRuler->mouseEnterEvent(point);
+                    break;
+                }
+            }
+        }
+    }
+
+    repaintDecorations();
+}
+
+void ParagraphSpacingTool::mouseReleaseEvent(KoPointerEvent *event)
+{
+    if (m_textBlockValid && m_activeRuler != NULL) {
+        QPointF point(mapDocumentToTextBlock(event->point));
+        m_activeRuler->mouseReleaseEvent(point);
+        m_activeRuler = NULL;
+    }
+
+    repaintDecorations();
+}
+
+void  ParagraphSpacingTool::activate( bool )
+{
+    // don't know why I have to use the force=true and what it does, but almost \
everyone else uses it... +    useCursor(Qt::ArrowCursor, true);
+}
+
+void ParagraphSpacingTool::deactivate()
+{
+    // the document might have changed, so we deselect the text block
+    deselectTextBlock();
+}
+
Index: plugins/textshape/VRuler.h
===================================================================
--- plugins/textshape/VRuler.h	(Revision 0)
+++ plugins/textshape/VRuler.h	(Revision 0)
@@ -0,0 +1,47 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Florian Merz <florianmerz@web.de>
+ *
+ * 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 VARROWCONTROL_H
+#define VARROWCONTROL_H
+
+#include "Ruler.h"
+#include <QPainter>
+#include <QPointF>
+
+class VRuler : public Ruler
+{
+public:
+    VRuler(QObject *parent) : Ruler(parent) {}
+    ~VRuler() {}
+
+    virtual QPointF arrowOrigin() const;
+
+    virtual void mouseMoveEvent(QPointF point);
+
+    virtual void paint(QPainter &painter) const;
+    virtual bool hitTest(QPointF point) const;
+
+protected:
+    QLineF baseline() const;
+    QLineF ruler() const;
+    QLineF oldRuler() const;
+};
+
+#endif
+
Index: plugins/textshape/Ruler.cpp
===================================================================
--- plugins/textshape/Ruler.cpp	(Revision 0)
+++ plugins/textshape/Ruler.cpp	(Revision 0)
@@ -0,0 +1,211 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2008 Florian Merz <florianmerz@web.de>
+ *
+ * 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 "Ruler.h"
+
+#include "ParagraphSpacingTool.h"
+
+#include <QLineF>
+
+#include <limits>
+#include <cmath>
+#include <stdio.h>
+
+Ruler::Ruler(QObject *parent)
+    : QObject(parent),
+    m_value(0.0),
+    m_oldValue(0.0),
+    m_stepValue(10.0),
+    m_minValue(-std::numeric_limits<qreal>::infinity()),
+    m_maxValue(std::numeric_limits<qreal>::infinity()),
+    m_visible(true),
+    m_mousePressed(false),
+    m_mouseHover(false),
+    m_options(noOptions)
+{}
+
+void Ruler::setBaseline(QPointF position, qreal lineLength)
+{
+    if (m_position != position || m_lineLength != lineLength)
+        emit needsRepaint();
+
+    m_position = position;
+    m_lineLength = lineLength;
+}
+
+void Ruler::setUnit(KoUnit unit)
+{
+    m_unit = unit;
+
+    // approximately 15 points seems to be a good value for the step size
+    // but it looks better if we get rounded canvas units
+    switch (m_unit.indexInList(false)) {
+        case KoUnit::Millimeter:
+            setStepValue(14.17325288515625502486);
+            break;
+        case KoUnit::Point:
+            setStepValue(15.0);
+            break;
+        case KoUnit::Inch:
+            setStepValue(14.399999999998848);
+            break;
+        case KoUnit::Centimeter:
+            setStepValue(14.17325288515625502486);
+            break;
+        case KoUnit::Decimeter:
+            setStepValue(14.17325288515625502486);
+            break;
+        case KoUnit::Pica:
+            setStepValue(15.00000006000000024);
+            break;
+        case KoUnit::Cicero:
+            setStepValue(12.84010270181826254741);
+            break;
+        case KoUnit::Pixel:
+        default:
+            setStepValue(15.0);
+            break;
+    }
+}
+
+qreal Ruler::value() const
+{
+    return m_value;
+}
+
+QString Ruler::valueString() const
+{
+    QString ret;
+    ret.append(m_unit.toUserStringValue(m_value));
+    ret.append(KoUnit::unitName(m_unit));
+    return ret;
+}
+
+void Ruler::setValue(qreal value)
+{
+    setOldValue(value);
+
+    if (value != m_value) {
+        // we only emit value changed if the value has been changed by the user \
interface +        m_value = value;
+        emit needsRepaint();
+    }
+}
+
+void Ruler::moveRuler(qreal value)
+{
+    qreal newValue;
+
+    if (value < minimumValue())
+        newValue = minimumValue();
+    else if (value > maximumValue())
+        newValue = maximumValue();
+    else {
+        if (m_stepValue == 0.0) {
+            newValue = value;
+        }
+        else {
+            newValue = value - fmod(value + m_stepValue * 0.5, m_stepValue) + \
m_stepValue * 0.5; +        }
+    }
+
+    if (newValue != m_value) {
+        m_value = newValue;
+        emit needsRepaint();
+    }
+}
+
+qreal Ruler::minimumValue() const
+{
+    return m_minValue;
+}
+void Ruler::setMinimumValue(qreal value)
+{
+    m_minValue = value;
+}
+
+qreal Ruler::maximumValue() const
+{
+    return m_maxValue;
+}
+
+void Ruler::setMaximumValue(qreal value)
+{
+    m_maxValue = value;
+}
+
+void Ruler::mousePressEvent(QPointF)
+{
+    m_mousePressed = true;
+    emit needsRepaint();
+}
+
+void Ruler::mouseReleaseEvent(QPointF)
+{
+    m_mousePressed = false;
+    if (m_oldValue == m_value) {
+        emit needsRepaint();
+    }
+    else {
+        m_oldValue = m_value;
+        emit valueChanged(m_value);
+    }
+}
+
+void Ruler::mouseMoveEvent(QPointF)
+{
+}
+
+void Ruler::mouseEnterEvent(QPointF)
+{
+    m_mouseHover = true;
+    emit needsRepaint();
+}
+
+void Ruler::mouseLeaveEvent(QPointF)
+{
+    m_mouseHover = false;
+    emit needsRepaint();
+}
+
+void Ruler::paintArrow(QPainter &painter, const QPointF tip, const qreal angle, \
qreal value) const +{
+    QLineF arrowLeft(-arrowDiagonal(), arrowDiagonal(), 0.0, 0.0);
+    QLineF arrowRight(-arrowDiagonal(), -arrowDiagonal(), 0.0, 0.0);
+
+    painter.save();
+
+    painter.translate(tip);
+   
+    if (value < arrowMinimumValue())
+        painter.rotate(angle + 180.0);
+    else
+        painter.rotate(angle);
+
+    painter.drawLine(arrowLeft);
+    painter.drawLine(arrowRight);
+
+    if (value < arrowMinimumValue()) {
+        QLineF arrowMiddle(-arrowSize(), 0.0, 0.0, 0.0);
+        painter.drawLine(arrowMiddle);
+    }
+
+    painter.restore();
+}
+



_______________________________________________
koffice-devel mailing list
koffice-devel@kde.org
https://mail.kde.org/mailman/listinfo/koffice-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic