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 //---------------------------------------------------------------------