File indexing completed on 2024-05-26 04:24:49

0001 
0002 // REFACTOR: Move into kpPainter
0003 
0004 /*
0005    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
0006    Copyright (c) 2010 Tasuku Suzuki <stasuku@gmail.com>
0007    All rights reserved.
0008 
0009    Redistribution and use in source and binary forms, with or without
0010    modification, are permitted provided that the following conditions
0011    are met:
0012 
0013    1. Redistributions of source code must retain the above copyright
0014       notice, this list of conditions and the following disclaimer.
0015    2. Redistributions in binary form must reproduce the above copyright
0016       notice, this list of conditions and the following disclaimer in the
0017       documentation and/or other materials provided with the distribution.
0018 
0019    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0020    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0021    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0022    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0023    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0024    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0025    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0026    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0027    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0028    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029 */
0030 
0031 
0032 #define DEBUG_KP_SELECTION 0
0033 
0034 
0035 #include "kpTextSelection.h"
0036 #include "kpTextSelectionPrivate.h"
0037 
0038 #include "kpTextStyle.h"
0039 #include "kpPreeditText.h"
0040 #include "pixmapfx/kpPixmapFX.h"
0041 
0042 #include "kpLogCategories.h"
0043 
0044 #include <QFont>
0045 #include <QList>
0046 #include <QPainter>
0047 #include <QTextCharFormat>
0048 
0049 //---------------------------------------------------------------------
0050 
0051 void kpTextSelection::drawPreeditString(QPainter &painter, int &x, int y, const kpPreeditText &preeditText) const
0052 {
0053     int i = 0;
0054     const QString& preeditString = preeditText.preeditString ();
0055     QString str;
0056     for (const auto &attr : preeditText.textFormatList ())
0057     {
0058         int start = attr.start;
0059         int length = attr.length;
0060         QTextCharFormat format = qvariant_cast<QTextFormat> (attr.value).toCharFormat ();
0061 
0062         if (i > start)
0063         {
0064             length = length - i + start;
0065             start = i;
0066         }
0067         if (length <= 0) {
0068             continue;
0069         }
0070 
0071         if (i < start)
0072         {
0073             str = preeditString.mid (i, start - i);
0074             painter.drawText (x, y, str);
0075             x += painter.fontMetrics().horizontalAdvance(str);
0076         }
0077 
0078         painter.save();
0079         str = preeditString.mid (start, length);
0080         int width = painter.fontMetrics().horizontalAdvance(str);
0081         if (format.background ().color () != Qt::black)
0082         {
0083             painter.save ();
0084             painter.setPen (format.background ().color ());
0085             painter.setBrush (format.background());
0086             painter.drawRect (x, y - painter.fontMetrics ().ascent (), width, painter.fontMetrics ().height ());
0087             painter.restore ();
0088         }
0089         if (format.foreground ().color () != Qt::black)
0090         {
0091             painter.setBrush (format.foreground ());
0092             painter.setPen (format.foreground ().color ());
0093         }
0094         if (format.underlineStyle ())
0095         {
0096             painter.drawLine (x, y + painter.fontMetrics ().descent (), x + width, y + painter.fontMetrics ().descent ());
0097         }
0098         painter.drawText (x, y, str);
0099 
0100         x += width;
0101         painter.restore ();
0102 
0103         i = start + length;
0104     }
0105     if (i < preeditString.length ())
0106     {
0107         str = preeditString.mid (i);
0108         painter.drawText (x, y, str);
0109         x += painter.fontMetrics().horizontalAdvance(str);
0110     }
0111 }
0112 
0113 //---------------------------------------------------------------------
0114 
0115 // public virtual [kpAbstractSelection]
0116 void kpTextSelection::paint(QImage *destPixmap, const QRect &docRect) const
0117 {
0118 #if DEBUG_KP_SELECTION
0119     qCDebug(kpLogLayers) << "kpTextSelection::paint() textStyle: fcol="
0120             << (int *) d->textStyle.foregroundColor ().toQRgb ()
0121             << " bcol="
0122             << (int *) d->textStyle.backgroundColor ().toQRgb ();
0123 #endif
0124 
0125     // Drawing text is slow so if the text box will be rendered completely
0126     // outside of <destRect>, don't bother rendering it at all.
0127     const QRect modifyingRect = docRect.intersected (boundingRect ());
0128     if (modifyingRect.isEmpty ()) {
0129         return;
0130     }
0131 
0132 
0133     // Is the text box completely invisible?
0134     if (textStyle ().foregroundColor ().isTransparent () &&
0135         textStyle ().backgroundColor ().isTransparent ())
0136     {
0137         return;
0138     }
0139 
0140     kpImage floatImage(modifyingRect.size(), QImage::Format_ARGB32_Premultiplied);
0141     floatImage.fill(0);
0142 
0143     QRect theWholeAreaRect, theTextAreaRect;
0144     theWholeAreaRect = boundingRect ().translated (-modifyingRect.topLeft ());
0145     theTextAreaRect = textAreaRect ().translated (-modifyingRect.topLeft ());
0146 
0147     QList <QString> theTextLines = textLines();
0148     kpTextStyle theTextStyle = textStyle();
0149 
0150     const QFontMetrics fontMetrics (theTextStyle.font ());
0151 
0152 #if DEBUG_KP_SELECTION
0153     qCDebug(kpLogLayers) << "kpTextSelection_Paint.cpp:DrawTextHelper";
0154     qCDebug(kpLogLayers) << "\theight=" << fontMetrics.height ()
0155                << " leading=" << fontMetrics.leading ()
0156                << " ascent=" << fontMetrics.ascent ()
0157                << " descent=" << fontMetrics.descent ()
0158                << " lineSpacing=" << fontMetrics.lineSpacing ();
0159 #endif
0160 
0161     QPainter painter(&floatImage);
0162 
0163     // Fill in the background using the transparent/opaque tool setting
0164     if ( theTextStyle.isBackgroundTransparent() ) {
0165       painter.fillRect(theWholeAreaRect, Qt::transparent);
0166     }
0167     else {
0168       painter.fillRect(theWholeAreaRect, theTextStyle.backgroundColor().toQColor());
0169     }
0170 
0171     painter.setClipRect(theWholeAreaRect);
0172     painter.setPen(theTextStyle.foregroundColor().toQColor());
0173     painter.setFont(theTextStyle.font());
0174 
0175     if ( theTextStyle.foregroundColor().toQColor().alpha() < 255 )
0176     {
0177       // if the foreground color has an alpha channel, we want to
0178       // see through the background, so we first need to punch holes
0179       // into the background where the text is
0180       painter.setCompositionMode(QPainter::CompositionMode_Clear);
0181 
0182       int baseLine = theTextAreaRect.y () + fontMetrics.ascent ();
0183       for (const auto &str : theTextLines)
0184       {
0185           painter.drawText (theTextAreaRect.x (), baseLine, str);
0186           baseLine += fontMetrics.lineSpacing ();
0187 
0188           // if the next textline would already be below the visible text area, stop drawing
0189           if ( (baseLine - fontMetrics.ascent()) > (theTextAreaRect.y() + theTextAreaRect.height()) ) {
0190             break;
0191           }
0192       }
0193       // the next text drawing will now blend the text foreground color with
0194       // what is really below the text background
0195       painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
0196     }
0197 
0198     // Draw a line at a time instead of using QPainter::drawText(QRect,...).
0199     // Else, the line heights become >QFontMetrics::height() if you type Chinese
0200     // characters (!) and then the cursor gets out of sync.
0201     int baseLine = theTextAreaRect.y () + fontMetrics.ascent ();
0202 
0203     kpPreeditText thePreeditText = preeditText();
0204 
0205     if ( theTextLines.isEmpty() )
0206     {
0207         if ( ! thePreeditText.isEmpty() )
0208         {
0209             int x = theTextAreaRect.x();
0210             drawPreeditString(painter, x, baseLine, thePreeditText);
0211         }
0212     }
0213     else
0214     {
0215         int i = 0;
0216         int row = thePreeditText.position().y();
0217         int col = thePreeditText.position().x();
0218         for (const auto &str : theTextLines)
0219         {
0220             if (row == i && !thePreeditText.isEmpty())
0221             {
0222                 QString left = str.left(col);
0223                 QString right = str.mid(col);
0224                 int x = theTextAreaRect.x();
0225                 painter.drawText(x, baseLine, left);
0226                 x += fontMetrics.horizontalAdvance(left);
0227                 drawPreeditString(painter, x, baseLine, thePreeditText);
0228 
0229                 painter.drawText(x, baseLine, right);
0230             }
0231             else
0232             {
0233                 painter.drawText(theTextAreaRect.x (), baseLine, str);
0234             }
0235             baseLine += fontMetrics.lineSpacing();
0236             i++;
0237 
0238             // if the next textline would already be below the visible text area, stop drawing
0239             if ( (baseLine - fontMetrics.ascent()) > (theTextAreaRect.y() + theTextAreaRect.height()) ) {
0240               break;
0241             }
0242         }
0243     }
0244 
0245     // ... convert that into "painting" transparent pixels on top of
0246     // the document.
0247     kpPixmapFX::paintPixmapAt (destPixmap,
0248         modifyingRect.topLeft () - docRect.topLeft (),
0249         floatImage);
0250 }
0251 
0252 //---------------------------------------------------------------------
0253 
0254 
0255 // public virtual [kpAbstractSelection]
0256 void kpTextSelection::paintBorder (QImage *destPixmap, const QRect &docRect,
0257         bool selectionFinished) const
0258 {
0259     paintRectangularBorder (destPixmap, docRect, selectionFinished);
0260 }
0261 
0262 //---------------------------------------------------------------------
0263 
0264 // public
0265 kpImage kpTextSelection::approximateImage () const
0266 {
0267     kpImage retImage (width (), height (), QImage::Format_ARGB32_Premultiplied);
0268     retImage.fill(0);
0269     paint (&retImage, boundingRect ());
0270     return retImage;
0271 }
0272 
0273 //---------------------------------------------------------------------