~bzoltan/kubuntu-packaging/decouple_cmake_plugin

« back to all changes in this revision

Viewing changes to src/libs/utils/ansiescapecodehandler.cpp

  • Committer: Timo Jyrinki
  • Date: 2013-11-15 12:25:23 UTC
  • mfrom: (1.1.28)
  • Revision ID: timo.jyrinki@canonical.com-20131115122523-i2kyamsu4gs2mu1m
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2013 Petar Perisin <petar.perisin@gmail.com>
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of Qt Creator.
 
7
**
 
8
** Commercial License Usage
 
9
** Licensees holding valid commercial Qt licenses may use this file in
 
10
** accordance with the commercial license agreement provided with the
 
11
** Software or, alternatively, in accordance with the terms contained in
 
12
** a written agreement between you and Digia.  For licensing terms and
 
13
** conditions see http://qt.digia.com/licensing.  For further information
 
14
** use the contact form at http://qt.digia.com/contact-us.
 
15
**
 
16
** GNU Lesser General Public License Usage
 
17
** Alternatively, this file may be used under the terms of the GNU Lesser
 
18
** General Public License version 2.1 as published by the Free Software
 
19
** Foundation and appearing in the file LICENSE.LGPL included in the
 
20
** packaging of this file.  Please review the following information to
 
21
** ensure the GNU Lesser General Public License version 2.1 requirements
 
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
23
**
 
24
** In addition, as a special exception, Digia gives you certain additional
 
25
** rights.  These rights are described in the Digia Qt LGPL Exception
 
26
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
27
**
 
28
****************************************************************************/
 
29
 
 
30
#include "ansiescapecodehandler.h"
 
31
#include <utils/qtcassert.h>
 
32
 
 
33
namespace Utils {
 
34
 
 
35
/*!
 
36
    \class Utils::AnsiEscapeCodeHandler
 
37
 
 
38
    \brief The AnsiEscapeCodeHandler class parses text and extracts ANSI escape codes from it.
 
39
 
 
40
    In order to preserve color information across text segments, an instance of this class
 
41
    must be stored for the lifetime of a stream.
 
42
    Also, one instance of this class should not handle multiple streams (at least not
 
43
    at the same time).
 
44
 
 
45
    Its main function is parseText(), which accepts text and default QTextCharFormat.
 
46
    This function is designed to parse text and split colored text to smaller strings,
 
47
    with their appropriate formatting information set inside QTextCharFormat.
 
48
 
 
49
    Usage:
 
50
    \list
 
51
    \li Create new instance of AnsiEscapeCodeHandler for a stream.
 
52
    \li To add new text, call parseText() with the text and a default QTextCharFormat.
 
53
        The result of this function is a list of strings with formats set in appropriate
 
54
        QTextCharFormat.
 
55
    \endlist
 
56
*/
 
57
 
 
58
AnsiEscapeCodeHandler::AnsiEscapeCodeHandler() :
 
59
    m_previousFormatClosed(true)
 
60
{
 
61
}
 
62
 
 
63
static QColor ansiColor(uint code)
 
64
{
 
65
    QTC_ASSERT(code < 8, return QColor());
 
66
 
 
67
    const int red   = code & 1 ? 170 : 0;
 
68
    const int green = code & 2 ? 170 : 0;
 
69
    const int blue  = code & 4 ? 170 : 0;
 
70
    return QColor(red, green, blue);
 
71
}
 
72
 
 
73
QList<StringFormatPair> AnsiEscapeCodeHandler::parseText(const QString &text,
 
74
                                                         const QTextCharFormat &defaultFormat)
 
75
{
 
76
    QList<StringFormatPair> outputData;
 
77
 
 
78
    QTextCharFormat charFormat = m_previousFormatClosed ? defaultFormat : m_previousFormat;
 
79
 
 
80
    const QString escape = QLatin1String("\x1b[");
 
81
    if (!text.contains(escape)) {
 
82
        outputData << StringFormatPair(text, charFormat);
 
83
        return outputData;
 
84
    } else if (!text.startsWith(escape)) {
 
85
        outputData << StringFormatPair(text.left(text.indexOf(escape)), charFormat);
 
86
    }
 
87
 
 
88
    const QChar semicolon       = QLatin1Char(';');
 
89
    const QChar colorTerminator = QLatin1Char('m');
 
90
    // strippedText always starts with "\e["
 
91
    QString strippedText = text.mid(text.indexOf(escape));
 
92
    while (!strippedText.isEmpty()) {
 
93
        while (strippedText.startsWith(escape)) {
 
94
            strippedText.remove(0, 2);
 
95
 
 
96
            // get the number
 
97
            QString strNumber;
 
98
            QStringList numbers;
 
99
            while (strippedText.at(0).isDigit() || strippedText.at(0) == semicolon) {
 
100
                if (strippedText.at(0).isDigit()) {
 
101
                    strNumber += strippedText.at(0);
 
102
                } else {
 
103
                    numbers << strNumber;
 
104
                    strNumber.clear();
 
105
                }
 
106
                strippedText.remove(0, 1);
 
107
            }
 
108
            if (!strNumber.isEmpty())
 
109
                numbers << strNumber;
 
110
 
 
111
            // remove terminating char
 
112
            if (!strippedText.startsWith(colorTerminator)) {
 
113
                strippedText.remove(0, 1);
 
114
                continue;
 
115
            }
 
116
            strippedText.remove(0, 1);
 
117
 
 
118
            if (numbers.isEmpty()) {
 
119
                charFormat = defaultFormat;
 
120
                endFormatScope();
 
121
            }
 
122
 
 
123
            for (int i = 0; i < numbers.size(); ++i) {
 
124
                const int code = numbers.at(i).toInt();
 
125
 
 
126
                if (code >= TextColorStart && code <= TextColorEnd) {
 
127
                    charFormat.setForeground(ansiColor(code - TextColorStart));
 
128
                    setFormatScope(charFormat);
 
129
                } else if (code >= BackgroundColorStart && code <= BackgroundColorEnd) {
 
130
                    charFormat.setBackground(ansiColor(code - BackgroundColorStart));
 
131
                    setFormatScope(charFormat);
 
132
                } else {
 
133
                    switch (code) {
 
134
                    case ResetFormat:
 
135
                        charFormat = defaultFormat;
 
136
                        endFormatScope();
 
137
                        break;
 
138
                    case BoldText:
 
139
                        charFormat.setFontWeight(QFont::Bold);
 
140
                        setFormatScope(charFormat);
 
141
                        break;
 
142
                    case DefaultTextColor:
 
143
                        charFormat.setForeground(defaultFormat.foreground());
 
144
                        setFormatScope(charFormat);
 
145
                        break;
 
146
                    case DefaultBackgroundColor:
 
147
                        charFormat.setBackground(defaultFormat.background());
 
148
                        setFormatScope(charFormat);
 
149
                        break;
 
150
                    case RgbTextColor:
 
151
                    case RgbBackgroundColor:
 
152
                        if (++i >= numbers.size())
 
153
                            break;
 
154
                        if (numbers.at(i).toInt() == 2) {
 
155
                            // RGB set with format: 38;2;<r>;<g>;<b>
 
156
                            if ((i + 3) < numbers.size()) {
 
157
                                (code == RgbTextColor) ?
 
158
                                      charFormat.setForeground(QColor(numbers.at(i + 1).toInt(),
 
159
                                                                      numbers.at(i + 2).toInt(),
 
160
                                                                      numbers.at(i + 3).toInt())) :
 
161
                                      charFormat.setBackground(QColor(numbers.at(i + 1).toInt(),
 
162
                                                                      numbers.at(i + 2).toInt(),
 
163
                                                                      numbers.at(i + 3).toInt()));
 
164
                                setFormatScope(charFormat);
 
165
                            }
 
166
                            i += 3;
 
167
                        } else if (numbers.at(i).toInt() == 5) {
 
168
                            // rgb set with format: 38;5;<i>
 
169
                            // unsupported because of unclear documentation, so we just skip <i>
 
170
                            ++i;
 
171
                        }
 
172
                        break;
 
173
                    default:
 
174
                        break;
 
175
                    }
 
176
                }
 
177
            }
 
178
        }
 
179
 
 
180
        if (strippedText.isEmpty())
 
181
            break;
 
182
        int index = strippedText.indexOf(escape);
 
183
        if (index > 0) {
 
184
            outputData << StringFormatPair(strippedText.left(index), charFormat);
 
185
            strippedText.remove(0, index);
 
186
        } else if (index == -1) {
 
187
            outputData << StringFormatPair(strippedText, charFormat);
 
188
            break;
 
189
        }
 
190
    }
 
191
    return outputData;
 
192
}
 
193
 
 
194
void AnsiEscapeCodeHandler::endFormatScope()
 
195
{
 
196
    m_previousFormatClosed = true;
 
197
}
 
198
 
 
199
void AnsiEscapeCodeHandler::setFormatScope(const QTextCharFormat &charFormat)
 
200
{
 
201
    m_previousFormat = charFormat;
 
202
    m_previousFormatClosed = false;
 
203
}
 
204
 
 
205
} // namespace Utils