File indexing completed on 2024-05-12 16:33:20

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2011 Jan Hambrecht <jaham@gmx.net>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Library General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 
0020 #include "ArtisticTextToolSelection.h"
0021 #include "ArtisticTextShape.h"
0022 
0023 #include <KoCanvasBase.h>
0024 #include <KoViewConverter.h>
0025 
0026 #include <QDebug>
0027 
0028 #include <QPainter>
0029 #include <QFontMetrics>
0030 
0031 ArtisticTextToolSelection::ArtisticTextToolSelection(KoCanvasBase *canvas, QObject *parent)
0032     : KoToolSelection(parent), m_canvas(canvas), m_currentShape(0)
0033     , m_selectionStart(-1), m_selectionCount(0)
0034 {
0035     Q_ASSERT(m_canvas);
0036 }
0037 
0038 ArtisticTextToolSelection::~ArtisticTextToolSelection()
0039 {
0040 
0041 }
0042 
0043 bool ArtisticTextToolSelection::hasSelection()
0044 {
0045     return m_currentShape && m_selectionCount > 0;
0046 }
0047 
0048 void ArtisticTextToolSelection::setSelectedShape(ArtisticTextShape *textShape)
0049 {
0050     if (textShape == m_currentShape)
0051         return;
0052     clear();
0053     m_currentShape = textShape;
0054 }
0055 
0056 ArtisticTextShape *ArtisticTextToolSelection::selectedShape() const
0057 {
0058     return m_currentShape;
0059 }
0060 
0061 void ArtisticTextToolSelection::selectText(int from, int to)
0062 {
0063     if (!m_currentShape)
0064         return;
0065 
0066     repaintDecoration();
0067 
0068     const int textCount = m_currentShape->plainText().count();
0069     m_selectionStart = qBound(0, from, textCount-1);
0070     m_selectionCount = qBound(from, to, textCount) - m_selectionStart;
0071 
0072     repaintDecoration();
0073 }
0074 
0075 int ArtisticTextToolSelection::selectionStart() const
0076 {
0077     return m_selectionStart;
0078 }
0079 
0080 int ArtisticTextToolSelection::selectionCount() const
0081 {
0082     return m_selectionCount;
0083 }
0084 
0085 void ArtisticTextToolSelection::clear()
0086 {
0087     repaintDecoration();
0088     m_selectionStart = -1;
0089     m_selectionCount = 0;
0090 }
0091 
0092 void ArtisticTextToolSelection::paint(QPainter &painter, const KoViewConverter &converter)
0093 {
0094     if (!hasSelection())
0095         return;
0096 
0097     m_currentShape->applyConversion( painter, converter );
0098     painter.setPen(Qt::NoPen);
0099     painter.setBrush(QColor(0, 0, 255, 127));
0100     painter.drawPath(outline());
0101 }
0102 
0103 QPainterPath ArtisticTextToolSelection::outline()
0104 {
0105     if (!hasSelection())
0106         return QPainterPath();
0107 
0108     CharIndex charPos = m_currentShape->indexOfChar(m_selectionStart);
0109     if (charPos.first < 0)
0110         return QPainterPath();
0111 
0112     QPainterPath outline;
0113 
0114     QPolygonF polygon;
0115 
0116     QList<ArtisticTextRange> ranges = m_currentShape->text();
0117     if (ranges.size() == 0) return outline;
0118 
0119     int globalCharIndex = m_selectionStart;
0120     int remainingChars = m_selectionCount;
0121     while (remainingChars && ranges.size() > charPos.first) {
0122         const ArtisticTextRange &currentRange = ranges[charPos.first];
0123 
0124         int currentTextLength = currentRange.text().length();
0125         while (charPos.second < currentTextLength && remainingChars > 0) {
0126             const QPointF pos = m_currentShape->charPositionAt(globalCharIndex);
0127             const qreal angle = m_currentShape->charAngleAt(globalCharIndex);
0128 
0129             QTransform charTransform;
0130             charTransform.translate( pos.x() - 1, pos.y() );
0131             charTransform.rotate( 360. - angle );
0132 
0133             QFontMetricsF metrics(currentRange.font());
0134 
0135             polygon.prepend(charTransform.map(QPointF(0.0, -metrics.ascent())));
0136             polygon.append(charTransform.map(QPointF(0.0, metrics.descent())));
0137 
0138             // advance to next character
0139             charPos.second++;
0140             globalCharIndex++;
0141             remainingChars--;
0142 
0143             // next character has y-offset or we are at the end of this text range
0144             const bool hasYOffset = currentRange.hasYOffset(charPos.second);
0145             const bool atRangeEnd = charPos.second == currentTextLength;
0146             const bool atSelectionEnd = remainingChars == 0;
0147             if (hasYOffset || atRangeEnd || atSelectionEnd) {
0148                 if (hasYOffset || atRangeEnd) {
0149                     const QChar c = currentRange.text().at(charPos.second-1);
0150                     const qreal w = metrics.width(c);
0151                     polygon.prepend(charTransform.map(QPointF(w, -metrics.ascent())));
0152                     polygon.append(charTransform.map(QPointF(w, metrics.descent())));
0153                 } else {
0154                     const QPointF pos = m_currentShape->charPositionAt(globalCharIndex);
0155                     const qreal angle = m_currentShape->charAngleAt(globalCharIndex);
0156                     charTransform.reset();
0157                     charTransform.translate( pos.x() - 1, pos.y() );
0158                     charTransform.rotate( 360. - angle );
0159                     polygon.prepend(charTransform.map(QPointF(0.0, -metrics.ascent())));
0160                     polygon.append(charTransform.map(QPointF(0.0, metrics.descent())));
0161                 }
0162                 QPainterPath p;
0163                 p.addPolygon(polygon);
0164                 outline = outline.united(p);
0165                 polygon.clear();
0166             }
0167         }
0168 
0169         // go to first character of next text range
0170         charPos.first++;
0171         charPos.second = 0;
0172     }
0173 
0174     // transform to document coordinates
0175     return m_currentShape->absoluteTransformation(0).map(outline);
0176 }
0177 
0178 void ArtisticTextToolSelection::repaintDecoration()
0179 {
0180     if (hasSelection())
0181         m_canvas->updateCanvas(outline().boundingRect());
0182 }