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

List:       koffice-devel
Subject:    paragraph tool
From:       Florian Merz <FlorianMerz () gmx ! de>
Date:       2008-07-02 11:02:53
Message-ID: 200807021302.54160.FlorianMerz () gmx ! de
[Download RAW message or body]

Hi list,

sorry about being quiet for a while. Attached is a new patch for the 
paragraph tool. For those who can't remember what I'm talking about: the 
tool is supposed to make it easier to change the spacing and formating of a 
paragraph in a TextShape by presenting visual controls for these 
properties.

Unfortunately I didn't have much spare time for this so progress has been 
very slow. I don't think this will change in the near future. You gotta be 
patient with me.

I did a major refactoring, fixed a few bugs, improved the design a lot and 
added some features. Hope you like it so far.

Greetings,
 Florian

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

Index: plugins/textshape/TextPlugin.cpp
===================================================================
--- plugins/textshape/TextPlugin.cpp	(Revision 823008)
+++ plugins/textshape/TextPlugin.cpp	(Arbeitskopie)
@@ -18,6 +18,7 @@
  */
 #include "TextPlugin.h"
 #include "TextToolFactory.h"
+#include "ParagraphToolFactory.h"
 #include "TextShapeFactory.h"
 
 #include <KoShapeRegistry.h>
@@ -31,6 +32,7 @@
     : QObject(parent)
 {
     KoToolRegistry::instance()->add(new TextToolFactory(parent));
+    KoToolRegistry::instance()->add(new ParagraphToolFactory(parent));
     KoShapeRegistry::instance()->add(new TextShapeFactory(parent));
 }
 
Index: plugins/textshape/ParagraphTool.cpp
===================================================================
--- plugins/textshape/ParagraphTool.cpp	(Revision 0)
+++ plugins/textshape/ParagraphTool.cpp	(Revision 0)
@@ -0,0 +1,631 @@
+/* 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 "ParagraphTool.h"
+#include "dialogs/ParagraphSettingsDialog.h"
+
+#include <KoCanvasBase.h>
+#include <KoParagraphStyle.h>
+#include <KoPointerEvent.h>
+#include <KoShapeBorderModel.h>
+#include <KoShapeManager.h>
+#include <KoStyleManager.h>
+#include <KoTextBlockData.h>
+#include <KoTextDocumentLayout.h>
+#include <KoUnit.h>
+
+#include <KDebug>
+#include <KLocalizedString>
+
+#include <QAbstractTextDocumentLayout>
+#include <QAction>
+#include <QCheckBox>
+#include <QLabel>
+#include <QPen>
+#include <QString>
+#include <QTextBlock>
+#include <QTextDocument>
+#include <QTextLine>
+#include <QTextList>
+#include <QGridLayout>
+
+ParagraphTool::ParagraphTool(KoCanvasBase *canvas)
+    : KoTool(canvas),
+    m_textShape(NULL),
+    m_paragraphStyle(NULL),
+    m_textBlockValid(false),
+    m_needsRepaint(false),
+    m_smoothMovement(false),
+    m_firstIndentRuler(this),
+    m_followingIndentRuler(this),
+    m_rightMarginRuler(this),
+    m_topMarginRuler(this),
+    m_bottomMarginRuler(this),
+    m_activeRuler(NULL),
+    m_hoverRuler(NULL)
+{
+    initializeRuler(&m_firstIndentRuler);
+    initializeRuler(&m_followingIndentRuler);
+    initializeRuler(&m_rightMarginRuler);
+    initializeRuler(&m_topMarginRuler, Ruler::drawSides);
+    initializeRuler(&m_bottomMarginRuler, Ruler::drawSides);
+
+    QAction *action = new QAction(i18n("Apply parent style to ruler"), this);
+    action->setShortcut(Qt::ALT + Qt::CTRL + Qt::Key_P);
+    addAction("apply_parent_style_to_active_ruler", action);
+    connect(action, SIGNAL(triggered()), this, \
SLOT(applyParentStyleToActiveRuler())); +}
+
+ParagraphTool::~ParagraphTool()
+{}
+
+// helper function to initalize rulers
+void ParagraphTool::initializeRuler(Ruler *ruler, int options)
+{
+    ruler->setOptions(options);
+    ruler->setUnit(canvas()->unit());
+    ruler->setMinimumValue(0.0);
+    connect(ruler, SIGNAL(needsRepaint()), this, SLOT(scheduleRepaint()));
+    connect(ruler, SIGNAL(valueChanged(qreal)), this, SLOT(updateLayout()));
+}
+
+QWidget *ParagraphTool::createOptionWidget()
+{
+    // TODO: move this to a ui file, can't do right now, because Qt's designer is \
broken on my system +    QWidget *widget = new QWidget();
+    QGridLayout *layout = new QGridLayout;
+
+    QLabel *styleNameLabel = new QLabel(i18n("Paragraph Style:"));
+    layout->addWidget(styleNameLabel, 0, 0);
+
+    QLabel *styleName = new QLabel(i18n("n/a"));
+    layout->addWidget(styleName, 0, 1);
+    connect(this, SIGNAL(styleNameChanged(const QString&)), styleName, \
SLOT(setText(const QString &))); +
+    QCheckBox *applyToParent = new QCheckBox(i18n("Apply to all of paragraphs of \
this style")); +    applyToParent->setDisabled(true);
+    layout->addWidget(applyToParent, 2, 0, 1, -1);
+
+    QCheckBox *smoothCheckBox = new QCheckBox(i18n("Enable Smooth Movement"));
+    layout->addWidget(smoothCheckBox, 1, 0, 1, -1);
+    connect(this, SIGNAL(smoothMovementChanged(bool)), smoothCheckBox, \
SLOT(setChecked(bool))); +    connect(smoothCheckBox, SIGNAL(clicked(bool)), this, \
SLOT(setSmoothMovement(bool))); +
+    layout->setRowStretch(3, 1);
+    widget->setLayout(layout);
+    return widget;
+}
+
+void ParagraphTool::loadDimensions()
+{
+    m_singleLine = (textLayout()->lineCount() == 1);
+
+    // border rectangle left and right
+    m_border.setLeft(0.0);
+    m_border.setRight(textShape()->size().width());
+
+    // first line rectangle
+    m_firstLine = textLayout()->lineAt(0).rect();
+
+    // counter rectangle 
+    KoTextBlockData *blockData = static_cast<KoTextBlockData*> \
(textBlock().userData()); +    if (blockData != NULL) {
+        m_counter = QRectF(blockData->counterPosition(), \
QSizeF(blockData->counterWidth() - blockData->counterSpacing(), +                    \
m_firstLine.height())); +        m_isList = true;
+    }
+    else {
+        m_isList = false;
+    }
+
+    // folowing lines rectangle
+    if (!m_singleLine) {
+        m_followingLines = QRectF(textLayout()->lineAt(1).rect().topLeft(),
+                textLayout()->lineAt(textLayout()->lineCount() - \
1).rect().bottomRight()); +    }
+    else {
+        m_followingLines = m_firstLine;
+    }
+
+    // border rectangle top and bottom
+    m_border.setTop(m_firstLine.top() - m_paragraphStyle->topMargin());
+    m_border.setBottom(m_singleLine ? m_firstLine.bottom() + \
m_paragraphStyle->bottomMargin() +            : m_followingLines.bottom() + \
m_paragraphStyle->bottomMargin()); +
+    // workaround: the lines overlap slightly so right now we simply calculate the \
mean of the two y-values +    if (!m_singleLine) {
+        qreal lineBreak((m_firstLine.bottom() + m_followingLines.top()) / 2.0);
+        m_firstLine.setBottom(lineBreak);
+        m_counter.setBottom(lineBreak);
+        m_followingLines.setTop(lineBreak);
+    }
+}
+
+void ParagraphTool::loadRulers()
+{
+    m_rightMarginRuler.setBaseline(QPointF(m_border.right(), \
m_followingLines.bottom()), +            QPointF(m_border.right(), \
m_firstLine.top())); +    \
m_rightMarginRuler.setValue(m_paragraphStyle->rightMargin()); +
+    m_topMarginRuler.setBaseline(QPointF(m_border.right(), m_border.top()),
+                QPointF(m_border.left(), m_border.top()));
+    m_topMarginRuler.setValue(m_paragraphStyle->topMargin());
+
+    m_bottomMarginRuler.setBaseline(QPointF(m_border.right(), \
m_followingLines.bottom()),  +                QPointF(m_border.left(), \
m_followingLines.bottom())); +    \
m_bottomMarginRuler.setValue(m_paragraphStyle->bottomMargin()); +
+    if (m_singleLine) {
+        m_firstIndentRuler.setBaseline(QPointF(m_border.left(), m_firstLine.top()), 
+                QPointF(m_border.left(), m_firstLine.bottom()));
+        m_firstIndentRuler.setValue(m_paragraphStyle->leftMargin() + \
m_paragraphStyle->textIndent()); +
+        m_followingIndentRuler.hide();
+    }
+    else {
+        m_firstIndentRuler.setBaseline(QPointF(m_border.left(), m_firstLine.top()),
+                   QPointF(m_border.left(), m_firstLine.bottom()));
+        m_firstIndentRuler.setValue(m_paragraphStyle->leftMargin() + \
m_paragraphStyle->textIndent()); +
+        m_followingIndentRuler.setBaseline(QPointF(m_border.left(), \
m_followingLines.top()), +                    QPointF(m_border.left(), \
m_followingLines.bottom())); +        \
m_followingIndentRuler.setValue(m_paragraphStyle->leftMargin()); +        \
m_followingIndentRuler.show(); +    }
+}
+
+
+void ParagraphTool::saveRulers()
+{
+    // TODO: split this into separate functions for each property, that way we can \
apply properties to parent styles +    // how do we handle following line indent if \
we want to apply it to the parent ruler? we always need to apply both +    // at the \
same time. maybe it's best to return followingIndent to the way the backend does it + \
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();
+}
+
+QString ParagraphTool::styleName()
+{
+    KoParagraphStyle *style = m_paragraphStyle;
+    while (style != NULL) {
+        QString name = style->name();
+        if (!name.isNull() && !name.isEmpty()) {
+            return name;
+        }
+        style = style->parent();
+    }
+
+    return QString(i18n("None"));
+}
+
+void ParagraphTool::updateLayout()
+{
+    saveRulers();
+
+    if (!shapeContainsBlock()) {
+        // for now, if the block moved to a different shape, we deselect the block
+        // in the future we should switch to the new shape that contains the text \
block +        kDebug() << "Block moved to different shape: deslecting block";
+        deselectTextBlock();
+        repaintDecorations();
+        return;
+    }
+
+    static_cast<KoTextDocumentLayout*>(document()->documentLayout())->layout();
+    loadDimensions();
+    loadRulers();
+
+    repaintDecorations();
+}
+
+void ParagraphTool::paintLabel(QPainter &painter, const QMatrix &matrix, const Ruler \
*ruler) const +{
+    QColor foregroundColor(ruler == m_activeRuler ? ruler->activeColor() : \
ruler->highlightColor()); +    QString text(ruler->valueString());
+    QLineF connector(matrix.map(ruler->labelConnector()));
+    connector.setLength(10.0);
+
+    painter.save();
+
+    painter.resetMatrix();
+
+    QColor foreground(foregroundColor);
+    painter.setBrush(Qt::white);
+    painter.setPen(foreground);
+
+    QRectF label(connector.p2().x(), connector.p2().y(), 0.0, 0.0);
+    label = painter.boundingRect(label, Qt::AlignCenter | Qt::AlignVCenter, text);
+    label.adjust(-4.0, 0.0, 4.0, 0.0);
+
+    // adjust label so that the connector ends on the edge of the label
+    if (abs(connector.dx()) > abs(connector.dy())) {
+        qreal halfWidth = connector.dy() < 0.0 ? label.width() / 2.0 : \
-label.width() / 2.0; +        label.adjust(halfWidth, 0.0, halfWidth, 0.0);
+    }
+    else {
+        qreal halfHeight = connector.dy() >= 0.0 ? label.height() / 2.0 : \
-label.height() / 2.0; +        label.adjust(0.0, halfHeight, 0.0, halfHeight);
+    }
+
+    painter.drawLine(connector);
+    painter.drawRoundRect(label, 720.0 / label.width(), 720.0 /  label.height());
+    painter.drawText(label, Qt::AlignHCenter | Qt::AlignVCenter, text);
+    painter.restore();
+}
+
+void ParagraphTool::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, -shapeStartOffset());
+
+    painter.setPen(Qt::darkGray);
+
+    foreach (Ruler *ruler, findChildren<Ruler *>()) {
+        if (ruler->isVisible())
+            ruler->paint(painter);
+    }
+
+    // paint line between first line and following lines
+    if (!m_singleLine) {
+        painter.drawLine(m_border.left(), m_firstLine.bottom(), m_firstLine.right(), \
m_firstLine.bottom()); +    }
+
+    QMatrix matrix(painter.combinedMatrix());
+
+    painter.restore();
+
+    Ruler *labeledRuler = m_activeRuler != NULL ? m_activeRuler : m_hoverRuler;
+    if (labeledRuler != NULL) {
+        paintLabel(painter, matrix, labeledRuler);
+    }
+}
+
+void ParagraphTool::repaintDecorations()
+{
+    if (!m_needsRepaint || m_textShape == NULL)
+        return;
+
+    QRectF boundingRect( QPointF(0, 0), textShape()->size() );
+    if(textShape()->border()) {
+        KoInsets insets;
+        textShape()->border()->borderInsets(textShape(), insets);
+        boundingRect.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
+    }
+
+    // adjust for arrow heads and label (although we can't be sure about the label)
+    boundingRect.adjust(-50.0, -50.0, 50.0, 50.0);
+
+    boundingRect = textShape()->absoluteTransformation(0).mapRect(boundingRect);
+
+    // TODO: should add previous and current label position to the repaint area, \
too. + 
+    canvas()->updateCanvas(boundingRect);
+
+    m_needsRepaint = false;
+}
+
+void ParagraphTool::scheduleRepaint()
+{
+    m_needsRepaint = true;
+}
+
+qreal ParagraphTool::shapeStartOffset() const
+{
+    KoTextShapeData *textShapeData = static_cast<KoTextShapeData*> \
(textShape()->userData()); +
+    return textShapeData->documentOffset();
+}
+
+qreal ParagraphTool::shapeEndOffset() const
+{
+    KoTextShapeData *textShapeData = static_cast<KoTextShapeData*> \
(textShape()->userData()); +
+    return textShapeData->documentOffset() + textShape()->size().height();
+}
+
+bool ParagraphTool::shapeContainsBlock()
+{
+    QTextLayout *layout = textLayout();
+    qreal blockStart = layout->lineAt(0).y();
+
+    QTextLine endLine = layout->lineAt(layout->lineCount()-1);
+    qreal blockEnd = endLine.y() + endLine.height();
+
+    qreal shapeStart = shapeStartOffset();
+    qreal shapeEnd = shapeEndOffset();
+
+    if (blockEnd < shapeStart || blockStart > shapeEnd) {
+        return false;
+    }
+    else {
+        return true;
+    }
+}
+
+QPointF ParagraphTool::mapDocumentToTextBlock(QPointF point) const
+{
+    QMatrix matrix = textShape()->absoluteTransformation(NULL);
+    matrix.translate(0.0, -shapeStartOffset());
+    return matrix.inverted().map(point);
+}
+
+void ParagraphTool::selectTextBlock(TextShape *newTextShape, QTextBlock block)
+{
+    // the text block is already selected, no need for a repaint and all that
+    if (m_textBlockValid && block == m_block && newTextShape == textShape())
+        return;
+
+    m_textBlockValid = true;
+
+    m_textShape = newTextShape;
+    m_block = block;
+    m_paragraphStyle = KoParagraphStyle::fromBlock(textBlock());
+
+    emit styleNameChanged(styleName());
+
+    scheduleRepaint();
+
+    loadDimensions();
+    loadRulers();
+}
+
+void ParagraphTool::deselectTextBlock()
+{
+    if (!m_textBlockValid)
+        return;
+
+    emit styleNameChanged(QString(i18n("n/a")));
+
+    m_textBlockValid = false;
+    scheduleRepaint();
+}
+
+void ParagraphTool::activateRuler(Ruler *ruler)
+{
+    m_activeRuler = ruler;
+    m_activeRuler->setActive(true);
+
+    // disable hovering if we have an active ruler
+    // hovering over the wrong ruler confuses the user
+    if (m_hoverRuler != NULL) {
+        m_hoverRuler->setHighlighted(false);
+        m_hoverRuler = NULL;
+    }
+}
+
+void ParagraphTool::deactivateActiveRuler()
+{
+    if (m_activeRuler == NULL) {
+        return;
+    }
+
+    m_activeRuler->setActive(false);
+    m_activeRuler = NULL;
+
+    // there's no active ruler anymore, so we have to if we hover somewhere
+    QPointF point(mapDocumentToTextBlock(m_mousePosition));
+    foreach (Ruler *ruler, findChildren<Ruler *>()) {
+        if (ruler->hitTest(point)) {
+            m_hoverRuler = ruler;
+            m_hoverRuler->setHighlighted(true);
+            break;
+        }
+    }
+}
+
+void ParagraphTool::resetActiveRuler()
+{
+    if (m_activeRuler != NULL) {
+        m_activeRuler->reset();
+        deactivateActiveRuler();
+    }
+}
+
+void ParagraphTool::applyParentStyleToActiveRuler()
+{
+    if (m_activeRuler == NULL) {
+        return;
+    }
+
+    if (m_activeRuler == &m_firstIndentRuler) {
+        m_paragraphStyle->remove(QTextFormat::TextIndent);
+        m_activeRuler->setValue(m_paragraphStyle->textIndent());
+    }
+    else if (m_activeRuler == &m_followingIndentRuler) {
+        m_paragraphStyle->remove(QTextFormat::BlockLeftMargin);
+        m_activeRuler->setValue(m_paragraphStyle->leftMargin());
+    }
+    else if (m_activeRuler == &m_rightMarginRuler) {
+        m_paragraphStyle->remove(QTextFormat::BlockRightMargin);
+        m_activeRuler->setValue(m_paragraphStyle->rightMargin());
+    }
+    else if (m_activeRuler == &m_topMarginRuler) {
+        m_paragraphStyle->remove(QTextFormat::BlockTopMargin);
+        m_activeRuler->setValue(m_paragraphStyle->topMargin());
+    }
+    else if (m_activeRuler == &m_bottomMarginRuler) {
+        m_paragraphStyle->remove(QTextFormat::BlockBottomMargin);
+        m_activeRuler->setValue(m_paragraphStyle->bottomMargin());
+    }
+
+    deactivateActiveRuler();
+
+    // we need to call the updateLayout() slot manually, it is not emitted if
+    // the ruler has been changed by calling setValue()
+    // TODO: maybe setValue() should emit valueChanged(), too
+    // but make sure that this doesn't trigger additional repaints or relayouts
+    updateLayout();
+}
+
+void ParagraphTool::mousePressEvent(KoPointerEvent *event)
+{
+    m_mousePosition = event->point;
+
+    if (event->button() == Qt::LeftButton) {
+
+        // 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 *ruler, findChildren<Ruler *>()) {
+                if (ruler->hitTest(point)) {
+                    activateRuler(ruler);
+
+                    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 under the mouse, then we simply \
deselect the current text block +        deselectTextBlock();
+    }
+    else if (event->button() == Qt::RightButton) {
+        resetActiveRuler();
+    }
+    else if(event->button() == Qt::MidButton) {
+        applyParentStyleToActiveRuler();
+    }
+
+    repaintDecorations();
+}
+
+void ParagraphTool::mouseReleaseEvent(KoPointerEvent *event)
+{
+    m_mousePosition = event->point;
+
+    if (m_textBlockValid && m_activeRuler != NULL) {
+        deactivateActiveRuler();
+    }
+
+    repaintDecorations();
+}
+
+void ParagraphTool::mouseMoveEvent(KoPointerEvent *event)
+{
+    // 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->moveRuler(point, smoothMovement());
+        }
+        // we don't want to hover if we have an active ruler
+        else {
+            // check if we left the element over which we were hovering
+            if (m_hoverRuler != NULL && !m_hoverRuler->hitTest(point)) {
+                m_hoverRuler->setHighlighted(false);
+                m_hoverRuler = NULL;
+            }
+
+            // check if we are hovering over a new control
+            if (m_hoverRuler == NULL) {
+                foreach (Ruler *ruler, findChildren<Ruler *>()) {
+                    if (ruler->hitTest(point)) {
+                        m_hoverRuler = ruler;
+                        m_hoverRuler->setHighlighted(true);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    repaintDecorations();
+}
+
+void ParagraphTool::keyPressEvent(QKeyEvent *event)
+{
+    switch (event->key()) {
+        case Qt::Key_Shift:
+            toggleSmoothMovement();
+            break;
+        case Qt::Key_Escape:
+            resetActiveRuler();
+            break;
+        case Qt::Key_Delete:
+        case Qt::Key_BackSpace:
+            applyParentStyleToActiveRuler();
+            break;
+        default:
+            break;
+    }
+
+    repaintDecorations();
+}
+
+void ParagraphTool::keyReleaseEvent( QKeyEvent *event)
+{
+    if (event->key() == Qt::Key_Shift)
+        toggleSmoothMovement();
+}
+
+void  ParagraphTool::activate( bool )
+{
+    // don't know why force=true is needed and what it does, but almost everyone \
else uses it... +    useCursor(Qt::ArrowCursor, true);
+}
+
+void ParagraphTool::deactivate()
+{
+    // the document might have changed, so we have to deselect the text block
+    deselectTextBlock();
+}
+
Index: plugins/textshape/CMakeLists.txt
===================================================================
--- plugins/textshape/CMakeLists.txt	(Revision 823008)
+++ plugins/textshape/CMakeLists.txt	(Arbeitskopie)
@@ -15,6 +15,9 @@
     Layout.cpp
     ListItemsHelper.cpp
     PluginHelperAction.cpp
+    ParagraphTool.cpp
+    ParagraphToolFactory.cpp
+    Ruler.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,137 @@
+/* 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 <QColor>
+#include <QLineF>
+#include <QMatrix>
+#include <QObject>
+#include <QPointF>
+
+class QPainter;
+
+/** 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 the ruler lacross the screen */ +class Ruler : public QObject
+{
+    Q_OBJECT
+public:
+    Ruler(QObject *parent);
+
+    ~Ruler() {}
+
+    qreal width() const { return m_width; }
+    void setBaseline(const QLineF &baseline);
+    void setBaseline(const QPointF &from, const QPointF &to);
+
+    KoUnit unit() const { return m_unit; }
+    void setUnit(KoUnit unit);
+
+    qreal value() const;
+    QString valueString() const;
+    void setValue(qreal value);
+
+    qreal oldValue() const { return m_oldValue; }
+    void reset();
+
+    // 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() const { 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,
+        drawSides = 1<<0
+    };
+
+    int options() const { return m_options; }
+    void setOptions(int options) { m_options = m_options | options; }
+
+    void moveRuler(const QPointF &point, bool smooth);
+    void moveRuler(qreal value, bool smooth);
+
+    bool isActive() const { return m_active; }
+    void setActive(bool active);
+
+    bool isHighlighted() const { return m_highlighted; }
+    void setHighlighted(bool highlighted);
+
+    QLineF labelConnector() const;
+
+    QColor activeColor() const { return QColor(100, 148, 255); }
+    QColor highlightColor() const { return QColor(78, 117, 201); }
+    QColor normalColor() const { return QColor(100, 100, 100); }
+
+    void paint(QPainter &painter) const;
+
+    bool hitTest(const QPointF &point) const;
+
+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 setOldValue(qreal value) { m_oldValue = value; }
+    void paintArrow(QPainter &painter, const QPointF &tip, const qreal angle, qreal \
value) const; +
+    // baseline properties
+    QMatrix m_matrix;
+    qreal m_width;
+
+    // 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_active;
+    bool m_highlighted;
+    int m_options;
+};
+
+#endif
+
Index: plugins/textshape/ParagraphToolFactory.h
===================================================================
--- plugins/textshape/ParagraphToolFactory.h	(Revision 0)
+++ plugins/textshape/ParagraphToolFactory.h	(Revision 0)
@@ -0,0 +1,35 @@
+/* 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 PARAGRAPHTOOLFACTORY_H
+#define PARAGRAPHTOOLFACTORY_H
+
+#include <KoToolFactory.h>
+
+
+class ParagraphToolFactory : public KoToolFactory {
+    Q_OBJECT
+public:
+    explicit ParagraphToolFactory(QObject *parent);
+    ~ParagraphToolFactory();
+
+    KoTool * createTool(KoCanvasBase *canvas);
+};
+
+#endif
Index: plugins/textshape/ParagraphTool.h
===================================================================
--- plugins/textshape/ParagraphTool.h	(Revision 0)
+++ plugins/textshape/ParagraphTool.h	(Revision 0)
@@ -0,0 +1,170 @@
+/* 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 PARAGRAPHTOOL_H
+#define PARAGRAPHTOOL_H
+
+#include "TextShape.h"
+#include "Ruler.h"
+
+#include <KoTool.h>
+
+#include <QTextBlock>
+
+class TextShape;
+
+class KoParagraphStyle;
+class KoCanvasBase;
+
+class QAbstractTextDocumentLayout;
+class QColor;
+class QTextLayout;
+
+/**
+ * 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 ParagraphTool : public KoTool {
+    Q_OBJECT
+public:
+    explicit ParagraphTool(KoCanvasBase *canvas);
+    ~ParagraphTool();
+
+    void initializeRuler(Ruler *ruler, int options = 0);
+
+    // 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 mouseReleaseEvent( KoPointerEvent *event );
+
+    // reimplemented from superclass
+    virtual void mouseMoveEvent( KoPointerEvent *event );
+
+    // reimplemented from superclass
+    virtual void keyPressEvent( QKeyEvent *event );
+
+    // reimplemented from superclass
+    virtual void keyReleaseEvent( QKeyEvent *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();
+
+    // Apply the parent's style to the active ruler (essentially deletes the \
setting) +    void applyParentStyleToActiveRuler();
+
+    // if the parargraph style is inherited from a named paragraph style return that \
name +    // otherwise return QString()
+    QString styleName();
+
+    void toggleSmoothMovement() {m_smoothMovement = !m_smoothMovement; emit \
smoothMovementChanged(m_smoothMovement); } +    void setSmoothMovement(bool \
smoothMovement) { m_smoothMovement = smoothMovement; } +
+signals:
+    void styleNameChanged(const QString&);
+    void smoothMovementChanged(bool smooth);
+
+protected:
+    QWidget *createOptionWidget();
+
+    // get all necessary properties from the layout system and pass them to the \
controls +    void loadDimensions();
+    void loadRulers();
+    void saveRulers();
+
+    void activateRuler(Ruler *ruler);
+    void deactivateActiveRuler();
+    void resetActiveRuler();
+
+    bool smoothMovement() { return m_smoothMovement; }
+
+    // paint a label at the specified position
+    void paintLabel(QPainter &painter, const QMatrix &matrix, const Ruler *ruler) \
const; +
+    // y-offset of the current text block in it's shape
+    // essentially a shortcut to access KoShapeData.documentOffset()
+    qreal shapeStartOffset() const;
+
+    qreal shapeEndOffset() const;
+
+    bool shapeContainsBlock();
+
+    // maps document coordinates to coordinates of the current text block
+    QPointF mapDocumentToTextBlock(QPointF point) const;
+
+    // internal convencience methods
+    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;
+
+    QPointF m_mousePosition;
+
+    QRectF m_counter,
+           m_firstLine,
+           m_followingLines,
+           m_border;
+
+    bool m_singleLine,
+         m_isList,
+         m_textBlockValid,
+         m_needsRepaint,
+         m_smoothMovement;
+
+    Ruler m_firstIndentRuler,
+          m_followingIndentRuler,
+          m_rightMarginRuler,
+          m_topMarginRuler,
+          m_bottomMarginRuler;
+
+    Ruler *m_activeRuler,
+          *m_hoverRuler;
+};
+
+#endif
+
Index: plugins/textshape/Ruler.cpp
===================================================================
--- plugins/textshape/Ruler.cpp	(Revision 0)
+++ plugins/textshape/Ruler.cpp	(Revision 0)
@@ -0,0 +1,274 @@
+/* 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 <KDebug>
+
+#include <QColor>
+#include <QPolygonF>
+#include <QPainter>
+#include <QRectF>
+
+#include <limits>
+#include <cmath>
+
+
+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_active(false),
+    m_highlighted(false),
+    m_options(noOptions)
+{}
+
+void Ruler::setBaseline(const QLineF &baseline)
+{
+    QMatrix matrix;
+    matrix.translate(baseline.p1().x(), baseline.p1().y());
+    matrix.rotate(baseline.angle(QLineF(0.0, 0.0, 0.0, 1.0)));
+
+    if (matrix == m_matrix && baseline.length() == width()) {
+        return;
+    }
+
+    m_matrix = matrix;
+    m_width = baseline.length();
+
+    emit needsRepaint();
+}
+
+void Ruler::setBaseline(const QPointF &from, const QPointF &to)
+{
+    setBaseline(QLineF(from, to));
+}
+
+void Ruler::setUnit(KoUnit unit)
+{
+    m_unit = unit;
+
+    // approximately 15 points seems to be a good value for the step size
+    switch (m_unit.indexInList(false)) {
+        case KoUnit::Millimeter:
+            setStepValue(14.17325288515625502486); // 5.0 mm
+            break;
+        case KoUnit::Point:
+            setStepValue(15.0); // 15 pt
+            break;
+        case KoUnit::Inch:
+            setStepValue(14.399999999998848); // 0.2 inch
+            break;
+        case KoUnit::Centimeter:
+            setStepValue(14.17325288515625502486); // 0.5 cm
+            break;
+        case KoUnit::Decimeter:
+            setStepValue(14.17325288515625502486); // 0.05 dm
+            break;
+        case KoUnit::Pica:
+            setStepValue(15.00000006000000024); // 1.25 pica
+            break;
+        case KoUnit::Cicero:
+            setStepValue(12.84010270181826254741); // 1 cicero
+            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) {
+        m_value = value;
+        emit needsRepaint();
+    }
+}
+
+void Ruler::reset()
+{
+    setValue(oldValue());
+}
+
+void Ruler::moveRuler(const QPointF &point, bool smooth)
+{
+    moveRuler(m_matrix.inverted().map(point).x(), smooth);
+}
+
+void Ruler::moveRuler(qreal value, bool smooth)
+{
+    qreal newValue;
+
+    if (value < minimumValue())
+        newValue = minimumValue();
+    else if (value > maximumValue())
+        newValue = maximumValue();
+    else {
+        if (smooth || m_stepValue == 0.0) {
+            newValue = value;
+        }
+        else if (value > 0.0) {
+            newValue = value - fmod(value + m_stepValue * 0.5, m_stepValue) + \
m_stepValue * 0.5; +        }
+        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::setActive(bool active)
+{
+    if (m_active == true && active == false && m_oldValue != m_value) {
+        m_active = false;
+        m_oldValue = m_value;
+        emit valueChanged(m_value);
+    }
+    else if (m_active != active) {
+        m_active = active;
+        emit needsRepaint();
+    }
+}
+
+void Ruler::setHighlighted(bool highlighted)
+{
+    m_highlighted = highlighted;
+    emit needsRepaint();
+}
+
+QLineF Ruler::labelConnector() const
+{
+    return m_matrix.map(QLineF(value()/2.0, width()/2.0, value()/2.0, width()/2.0 + \
1.0)); +}
+
+void Ruler::paint(QPainter &painter) const
+{
+    painter.save();
+
+    painter.setWorldMatrix(m_matrix, true);
+
+    // draw dark gray square for old value
+
+    painter.setPen(QPen(Qt::darkGray));
+
+    if (value() != 0.0)
+        painter.drawLine(QLineF(0.0, 0.0, 0.0, width()));
+
+    if (oldValue() != value() && oldValue() != 0.0)
+        painter.drawLine(QLineF(oldValue(), 0.0, oldValue(), width()));
+
+    if (options() & drawSides) {
+        painter.drawLine(QLineF(0.0, 0.0, oldValue(), 0.0));
+        painter.drawLine(QLineF(0.0, width(), oldValue(), width()));
+    }
+
+    // draw arrow and ruler line for current value
+
+    if (isActive())
+        painter.setPen(activeColor());
+    else if (isHighlighted())
+        painter.setPen(highlightColor());
+    else
+        painter.setPen(normalColor());
+
+    painter.drawLine(QLineF(value(), 0.0, value(), width()));
+
+    painter.drawLine(QLineF(0.0, width()/2.0, value(), width()/2.0));
+
+    if (value() >= 0.0) {
+        paintArrow(painter, QPointF(value(), width()/2.0), 0.0, value());
+    }
+    else {
+        paintArrow(painter, QPointF(value(), width()/2.0), 180.0, -value());
+    }
+
+    painter.restore();
+}
+
+void Ruler::paintArrow(QPainter &painter, const QPointF &tip, const qreal angle, \
qreal value) const +{
+
+    painter.save();
+
+    painter.translate(tip);
+
+    painter.rotate(angle);
+
+    QLineF arrowLeft(-arrowDiagonal(), arrowDiagonal(), 0.0, 0.0);
+    painter.drawLine(arrowLeft);
+
+    QLineF arrowRight(-arrowDiagonal(), -arrowDiagonal(), 0.0, 0.0);
+    painter.drawLine(arrowRight);
+
+    if (value < arrowMinimumValue()) {
+        QLineF arrowMiddle(-arrowSize(), 0.0, 0.0, 0.0);
+        painter.drawLine(arrowMiddle);
+    }
+
+    painter.restore();
+}
+
+bool Ruler::hitTest(const QPointF &point) const
+{
+    return QRectF(value() - 5.0, 0.0, 10.0, \
width()).contains(m_matrix.inverted().map(point)); +}
+
Index: plugins/textshape/ParagraphToolFactory.cpp
===================================================================
--- plugins/textshape/ParagraphToolFactory.cpp	(Revision 0)
+++ plugins/textshape/ParagraphToolFactory.cpp	(Revision 0)
@@ -0,0 +1,43 @@
+/* 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 "ParagraphToolFactory.h"
+#include "ParagraphTool.h"
+#include "TextShape.h"
+
+#include <klocale.h>
+
+ParagraphToolFactory::ParagraphToolFactory( QObject *parent )
+: KoToolFactory(parent, "ParagraphToolFactory_ID", i18n("Paragraph tool"))
+{
+    setToolTip(i18n("Paragraph tool"));
+    setToolType(dynamicToolType());
+    setIcon("draw-text");
+    setPriority(2);
+    setActivationShapeId(TextShape_SHAPEID);
+}
+
+ParagraphToolFactory::~ParagraphToolFactory() {
+}
+
+KoTool * ParagraphToolFactory::createTool( KoCanvasBase *canvas ) {
+    return new ParagraphTool(canvas);
+}
+
+#include "ParagraphToolFactory.moc"



_______________________________________________
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