[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