File indexing completed on 2024-04-28 05:50:39

0001 /*
0002     SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // Own
0008 #include "PlainTextDecoder.h"
0009 
0010 // Konsole characters
0011 #include <ExtendedCharTable.h>
0012 
0013 // Qt
0014 #include <QList>
0015 #include <QTextStream>
0016 
0017 using namespace Konsole;
0018 
0019 PlainTextDecoder::PlainTextDecoder()
0020     : _output(nullptr)
0021     , _includeLeadingWhitespace(true)
0022     , _includeTrailingWhitespace(true)
0023     , _recordLinePositions(false)
0024     , _linePositions(QList<int>())
0025 {
0026 }
0027 
0028 void PlainTextDecoder::setLeadingWhitespace(bool enable)
0029 {
0030     _includeLeadingWhitespace = enable;
0031 }
0032 
0033 void PlainTextDecoder::setTrailingWhitespace(bool enable)
0034 {
0035     _includeTrailingWhitespace = enable;
0036 }
0037 
0038 void PlainTextDecoder::begin(QTextStream *output)
0039 {
0040     _output = output;
0041     if (!_linePositions.isEmpty()) {
0042         _linePositions.clear();
0043     }
0044 }
0045 
0046 void PlainTextDecoder::end()
0047 {
0048     _output = nullptr;
0049 }
0050 
0051 void PlainTextDecoder::setRecordLinePositions(bool record)
0052 {
0053     _recordLinePositions = record;
0054 }
0055 
0056 QList<int> PlainTextDecoder::linePositions() const
0057 {
0058     return _linePositions;
0059 }
0060 
0061 void PlainTextDecoder::decodeLine(const Character *const characters, int count, LineProperty /*properties*/)
0062 {
0063     Q_ASSERT(_output);
0064 
0065     if (_recordLinePositions && (_output->string() != nullptr)) {
0066         int pos = _output->string()->length();
0067         _linePositions << pos;
0068     }
0069 
0070     // TODO should we ignore or respect the LINE_WRAPPED line property?
0071 
0072     // If we should remove leading whitespace find the first non-space character
0073     int start = 0;
0074     if (!_includeLeadingWhitespace) {
0075         while (start < count && characters[start].isSpace()) {
0076             start++;
0077         }
0078     }
0079 
0080     int outputCount = count - start;
0081 
0082     if (outputCount <= 0) {
0083         return;
0084     }
0085 
0086     // if inclusion of trailing whitespace is disabled then find the end of the
0087     // line
0088     if (!_includeTrailingWhitespace) {
0089         while (outputCount > 0 && characters[start + outputCount - 1].isSpace()) {
0090             outputCount--;
0091         }
0092     }
0093 
0094     // find out the last technically real character in the line
0095     int realCharacterGuard = -1;
0096     for (int i = count - 1; i >= start; i--) {
0097         // FIXME: the special case of '\n' here is really ugly
0098         // Maybe the '\n' should be added after calling this method in
0099         // Screen::copyLineToStream()
0100         if ((characters[i].flags & EF_REAL) != 0 && characters[i].character != '\n') {
0101             realCharacterGuard = i;
0102             break;
0103         }
0104     }
0105 
0106     // note:  we build up a QVector<char32_t> and send it to the text stream transformed into a //
0107     // QString rather than writing into the text stream a character at a time because it is more
0108     // efficient (since QTextStream always deals with QStrings internally anyway)
0109     QVector<char32_t> characterBuffer;
0110     characterBuffer.reserve(count);
0111 
0112     for (int i = start; i < outputCount;) {
0113         if (characters[i].rendition.f.extended != 0) {
0114             ushort extendedCharLength = 0;
0115             const char32_t *chars = ExtendedCharTable::instance.lookupExtendedChar(characters[i].character, extendedCharLength);
0116             if (chars != nullptr) {
0117                 for (uint nchar = 0; nchar < extendedCharLength; nchar++) {
0118                     characterBuffer.append(chars[nchar]);
0119                 }
0120                 i += qMax(1, Character::stringWidth(chars, extendedCharLength));
0121             } else {
0122                 ++i;
0123             }
0124         } else {
0125             // All characters which appear before the last real character are
0126             // seen as real characters, even when they are technically marked as
0127             // non-real.
0128             //
0129             // This feels tricky, but otherwise leading "whitespaces" may be
0130             // lost in some situation. One typical example is copying the result
0131             // of `dialog --infobox "qwe" 10 10` .
0132             if ((characters[i].flags & EF_REAL) != 0 || i <= realCharacterGuard) {
0133                 if (characters[i].isRightHalfOfDoubleWide()) {
0134                     i += 1;
0135                 } else {
0136                     characterBuffer.append(characters[i].character);
0137                     i += qMax(1, Character::stringWidth(&characters[i].character, 1));
0138                 }
0139             } else {
0140                 ++i; // should we 'break' directly here?
0141             }
0142         }
0143     }
0144     *_output << QString::fromUcs4(characterBuffer.data(), characterBuffer.size());
0145 }