File indexing completed on 2025-06-29 03:31:10

0001 // xlsxrichstring.cpp
0002 
0003 #include <QtGlobal>
0004 #include <QDebug>
0005 #include <QTextDocument>
0006 #include <QTextFragment>
0007 
0008 #include "xlsxrichstring.h"
0009 #include "xlsxrichstring_p.h"
0010 #include "xlsxformat_p.h"
0011 
0012 QT_BEGIN_NAMESPACE_XLSX
0013 
0014 RichStringPrivate::RichStringPrivate()
0015     :_dirty(true)
0016 {
0017 
0018 }
0019 
0020 RichStringPrivate::RichStringPrivate(const RichStringPrivate &other)
0021     :QSharedData(other), fragmentTexts(other.fragmentTexts)
0022     ,fragmentFormats(other.fragmentFormats)
0023     , _idKey(other.idKey()), _dirty(other._dirty)
0024 {
0025 
0026 }
0027 
0028 RichStringPrivate::~RichStringPrivate()
0029 {
0030 
0031 }
0032 
0033 /*!
0034     \class RichString
0035     \inmodule QtXlsx
0036     \brief This class add support for the rich text string of the cell.
0037 */
0038 
0039 /*!
0040     Constructs a null string.
0041  */
0042 RichString::RichString()
0043     :d(new RichStringPrivate)
0044 {
0045 }
0046 
0047 /*!
0048     Constructs a plain string with the given \a text.
0049 */
0050 RichString::RichString(const QString& text)
0051     :d(new RichStringPrivate)
0052 {
0053     addFragment(text, Format());
0054 }
0055 
0056 /*!
0057     Constructs a copy of \a other.
0058  */
0059 RichString::RichString(const RichString &other)
0060     :d(other.d)
0061 {
0062 
0063 }
0064 
0065 /*!
0066     Destructs the string.
0067  */
0068 RichString::~RichString()
0069 {
0070 
0071 }
0072 
0073 /*!
0074     Assigns \a other to this string and returns a reference to this string
0075  */
0076 RichString &RichString::operator =(const RichString &other)
0077 {
0078     this->d = other.d;
0079     return *this;
0080 }
0081 
0082 /*!
0083     Returns the rich string as a QVariant
0084 */
0085 RichString::operator QVariant() const
0086 {
0087     const auto& cref
0088 #if QT_VERSION >= 0x060000 // Qt 6.0 or over
0089         = QMetaType::fromType<RichString>();
0090 #else
0091         = qMetaTypeId<RichString>() ;
0092 #endif
0093     return QVariant(cref, this);
0094 }
0095 
0096 /*!
0097     Returns true if this is rich text string.
0098  */
0099 bool RichString::isRichString() const
0100 {
0101     if (fragmentCount() > 1) //Is this enough??
0102         return true;
0103     return false;
0104 }
0105 
0106 /*!
0107     Returns true is this is an Null string.
0108  */
0109 bool RichString::isNull() const
0110 {
0111     return d->fragmentTexts.size() == 0;
0112 }
0113 
0114 /*!
0115     Returns true is this is an empty string.
0116  */
0117 bool RichString::isEmtpy() const
0118 {
0119     for (const auto& str : d->fragmentTexts) {
0120         if (!str.isEmpty())
0121             return false;
0122     }
0123 
0124     return true;
0125 }
0126 
0127 /*!
0128     Converts to plain text string.
0129 */
0130 QString RichString::toPlainString() const
0131 {
0132     if (isEmtpy())
0133         return QString();
0134     if (d->fragmentTexts.size() == 1)
0135         return d->fragmentTexts[0];
0136 
0137     return d->fragmentTexts.join(QString());
0138 }
0139 
0140 /*!
0141   Converts to html string
0142 */
0143 QString RichString::toHtml() const
0144 {
0145     //: Todo
0146     return QString();
0147 }
0148 
0149 /*!
0150   Replaces the entire contents of the document
0151   with the given HTML-formatted text in the \a text string
0152 */
0153 void RichString::setHtml(const QString &text)
0154 {
0155     QTextDocument doc;
0156     doc.setHtml(text);
0157     QTextBlock block = doc.firstBlock();
0158     QTextBlock::iterator it;
0159     for (it = block.begin(); !(it.atEnd()); ++it) {
0160         QTextFragment textFragment = it.fragment();
0161         if (textFragment.isValid()) {
0162             Format fmt;
0163             fmt.setFont(textFragment.charFormat().font());
0164             fmt.setFontColor(textFragment.charFormat().foreground().color());
0165             addFragment(textFragment.text(), fmt);
0166         }
0167     }
0168 }
0169 
0170 /*!
0171     Returns fragment count.
0172  */
0173 int RichString::fragmentCount() const
0174 {
0175     return d->fragmentTexts.size();
0176 }
0177 
0178 /*!
0179     Appends a fragment with the given \a text and \a format.
0180  */
0181 void RichString::addFragment(const QString &text, const Format &format)
0182 {
0183     d->fragmentTexts.append(text);
0184     d->fragmentFormats.append(format);
0185     d->_dirty = true;
0186 }
0187 
0188 /*!
0189     Returns fragment text at the position \a index.
0190  */
0191 QString RichString::fragmentText(int index) const
0192 {
0193     if (index < 0 || index >= fragmentCount())
0194         return QString();
0195 
0196     return d->fragmentTexts[index];
0197 }
0198 
0199 /*!
0200     Returns fragment format at the position \a index.
0201  */
0202 Format RichString::fragmentFormat(int index) const
0203 {
0204     if (index < 0 || index >= fragmentCount())
0205         return Format();
0206 
0207     return d->fragmentFormats[index];
0208 }
0209 
0210 /*!
0211  * \internal
0212  */
0213 QByteArray RichStringPrivate::idKey() const
0214 {
0215     if (_dirty) {
0216         RichStringPrivate *rs = const_cast<RichStringPrivate *>(this);
0217         QByteArray bytes;
0218         if (fragmentTexts.size() == 1) {
0219             bytes = fragmentTexts[0].toUtf8();
0220         } else {
0221             //Generate a hash value base on QByteArray ?
0222             bytes.append("@@QtXlsxRichString=");
0223             for (int i=0; i<fragmentTexts.size(); ++i) {
0224                 bytes.append("@Text");
0225                 bytes.append(fragmentTexts[i].toUtf8());
0226                 bytes.append("@Format");
0227                 if (fragmentFormats[i].hasFontData())
0228                     bytes.append(fragmentFormats[i].fontKey());
0229             }
0230         }
0231         rs->_idKey = bytes;
0232         rs->_dirty = false;
0233     }
0234 
0235     return _idKey;
0236 }
0237 
0238 /*!
0239     Returns true if this string \a rs1 is equal to string \a rs2;
0240     otherwise returns false.
0241  */
0242 bool operator==(const RichString &rs1, const RichString &rs2)
0243 {
0244     if (rs1.fragmentCount() != rs2.fragmentCount())
0245         return false;
0246 
0247     return rs1.d->idKey() == rs2.d->idKey();
0248 }
0249 
0250 /*!
0251     Returns true if this string \a rs1 is not equal to string \a rs2;
0252     otherwise returns false.
0253  */
0254 bool operator!=(const RichString &rs1, const RichString &rs2)
0255 {
0256     if (rs1.fragmentCount() != rs2.fragmentCount())
0257         return true;
0258 
0259     return rs1.d->idKey() != rs2.d->idKey();
0260 }
0261 
0262 /*!
0263  * \internal
0264  */
0265 bool operator<(const RichString &rs1, const RichString &rs2)
0266 {
0267     return rs1.d->idKey() < rs2.d->idKey();
0268 }
0269 
0270 /*!
0271     \overload
0272     Returns true if this string \a rs1 is equal to string \a rs2;
0273     otherwise returns false.
0274  */
0275 bool operator ==(const RichString &rs1, const QString &rs2)
0276 {
0277     if (rs1.fragmentCount() == 1 && rs1.fragmentText(0) == rs2) //format == 0
0278         return true;
0279 
0280     return false;
0281 }
0282 
0283 /*!
0284     \overload
0285     Returns true if this string \a rs1 is not equal to string \a rs2;
0286     otherwise returns false.
0287  */
0288 bool operator !=(const RichString &rs1, const QString &rs2)
0289 {
0290     if (rs1.fragmentCount() == 1 && rs1.fragmentText(0) == rs2) //format == 0
0291         return false;
0292 
0293     return true;
0294 }
0295 
0296 /*!
0297     \overload
0298     Returns true if this string \a rs1 is equal to string \a rs2;
0299     otherwise returns false.
0300  */
0301 bool operator ==(const QString &rs1, const RichString &rs2)
0302 {
0303     return rs2 == rs1;
0304 }
0305 
0306 /*!
0307     \overload
0308     Returns true if this string \a rs1 is not equal to string \a rs2;
0309     otherwise returns false.
0310  */
0311 bool operator !=(const QString &rs1, const RichString &rs2)
0312 {
0313     return rs2 != rs1;
0314 }
0315 
0316 uint qHash(const RichString &rs, uint seed) Q_DECL_NOTHROW
0317 {
0318    return qHash(rs.d->idKey(), seed);
0319 }
0320 
0321 #ifndef QT_NO_DEBUG_STREAM
0322 QDebug operator<<(QDebug dbg, const RichString &rs)
0323 {
0324     dbg.nospace() << "QXlsx::RichString(" << rs.d->fragmentTexts << ")";
0325     return dbg.space();
0326 }
0327 #endif
0328 
0329 QT_END_NAMESPACE_XLSX