1 | /* |
2 | This file is part of Konsole, an X terminal. |
3 | |
4 | Copyright 2006-2008 by Robert Knight <robertknight@gmail.com> |
5 | |
6 | This program is free software; you can redistribute it and/or modify |
7 | it under the terms of the GNU Lesser General Public License as published by |
8 | the Free Software Foundation; either version 2 of the License, or |
9 | (at your option) any later version. |
10 | |
11 | This program is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | GNU General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Lesser General Public License |
17 | along with this program; if not, write to the Free Software |
18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
19 | 02110-1301 USA. |
20 | */ |
21 | |
22 | // Own |
23 | #include "TerminalCharacterDecoder.h" |
24 | |
25 | // Qt |
26 | #include <QTextStream> |
27 | |
28 | // KDE |
29 | //#include <kdebug.h> |
30 | |
31 | // Konsole |
32 | #include "konsole_wcwidth.h" |
33 | |
34 | using namespace Konsole; |
35 | PlainTextDecoder::PlainTextDecoder() |
36 | : _output(0) |
37 | , _includeTrailingWhitespace(true) |
38 | , _recordLinePositions(false) |
39 | { |
40 | |
41 | } |
42 | void PlainTextDecoder::setTrailingWhitespace(bool enable) |
43 | { |
44 | _includeTrailingWhitespace = enable; |
45 | } |
46 | bool PlainTextDecoder::trailingWhitespace() const |
47 | { |
48 | return _includeTrailingWhitespace; |
49 | } |
50 | void PlainTextDecoder::begin(QTextStream* output) |
51 | { |
52 | _output = output; |
53 | if (!_linePositions.isEmpty()) |
54 | _linePositions.clear(); |
55 | } |
56 | void PlainTextDecoder::end() |
57 | { |
58 | _output = 0; |
59 | } |
60 | |
61 | void PlainTextDecoder::setRecordLinePositions(bool record) |
62 | { |
63 | _recordLinePositions = record; |
64 | } |
65 | QList<int> PlainTextDecoder::linePositions() const |
66 | { |
67 | return _linePositions; |
68 | } |
69 | void PlainTextDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/ |
70 | ) |
71 | { |
72 | Q_ASSERT( _output ); |
73 | |
74 | if (_recordLinePositions && _output->string()) |
75 | { |
76 | int pos = _output->string()->count(); |
77 | _linePositions << pos; |
78 | } |
79 | |
80 | //TODO should we ignore or respect the LINE_WRAPPED line property? |
81 | |
82 | //note: we build up a QString and send it to the text stream rather writing into the text |
83 | //stream a character at a time because it is more efficient. |
84 | //(since QTextStream always deals with QStrings internally anyway) |
85 | std::wstring plainText; |
86 | plainText.reserve(count); |
87 | |
88 | int outputCount = count; |
89 | |
90 | // if inclusion of trailing whitespace is disabled then find the end of the |
91 | // line |
92 | if ( !_includeTrailingWhitespace ) |
93 | { |
94 | for (int i = count-1 ; i >= 0 ; i--) |
95 | { |
96 | if ( characters[i].character != L' ' ) |
97 | break; |
98 | else |
99 | outputCount--; |
100 | } |
101 | } |
102 | |
103 | for (int i=0;i<outputCount;) |
104 | { |
105 | plainText.push_back( characters[i].character ); |
106 | i += qMax(1,konsole_wcwidth(characters[i].character)); |
107 | } |
108 | *_output << QString::fromStdWString(plainText); |
109 | } |
110 | |
111 | HTMLDecoder::HTMLDecoder() : |
112 | _output(0) |
113 | ,_colorTable(base_color_table) |
114 | ,_innerSpanOpen(false) |
115 | ,_lastRendition(DEFAULT_RENDITION) |
116 | { |
117 | |
118 | } |
119 | |
120 | void HTMLDecoder::begin(QTextStream* output) |
121 | { |
122 | _output = output; |
123 | |
124 | std::wstring text; |
125 | |
126 | //open monospace span |
127 | openSpan(text,QLatin1String("font-family:monospace" )); |
128 | |
129 | *output << QString::fromStdWString(text); |
130 | } |
131 | |
132 | void HTMLDecoder::end() |
133 | { |
134 | Q_ASSERT( _output ); |
135 | |
136 | std::wstring text; |
137 | |
138 | closeSpan(text); |
139 | |
140 | *_output << QString::fromStdWString(text); |
141 | |
142 | _output = 0; |
143 | |
144 | } |
145 | |
146 | //TODO: Support for LineProperty (mainly double width , double height) |
147 | void HTMLDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/ |
148 | ) |
149 | { |
150 | Q_ASSERT( _output ); |
151 | |
152 | std::wstring text; |
153 | |
154 | int spaceCount = 0; |
155 | |
156 | for (int i=0;i<count;i++) |
157 | { |
158 | wchar_t ch(characters[i].character); |
159 | |
160 | //check if appearance of character is different from previous char |
161 | if ( characters[i].rendition != _lastRendition || |
162 | characters[i].foregroundColor != _lastForeColor || |
163 | characters[i].backgroundColor != _lastBackColor ) |
164 | { |
165 | if ( _innerSpanOpen ) |
166 | closeSpan(text); |
167 | |
168 | _lastRendition = characters[i].rendition; |
169 | _lastForeColor = characters[i].foregroundColor; |
170 | _lastBackColor = characters[i].backgroundColor; |
171 | |
172 | //build up style string |
173 | QString style; |
174 | |
175 | bool useBold; |
176 | ColorEntry::FontWeight weight = characters[i].fontWeight(_colorTable); |
177 | if (weight == ColorEntry::UseCurrentFormat) |
178 | useBold = _lastRendition & RE_BOLD; |
179 | else |
180 | useBold = weight == ColorEntry::Bold; |
181 | |
182 | if (useBold) |
183 | style.append(QLatin1String("font-weight:bold;" )); |
184 | |
185 | if ( _lastRendition & RE_UNDERLINE ) |
186 | style.append(QLatin1String("font-decoration:underline;" )); |
187 | |
188 | //colours - a colour table must have been defined first |
189 | if ( _colorTable ) |
190 | { |
191 | style.append( QString::fromLatin1("color:%1;" ).arg(_lastForeColor.color(_colorTable).name() ) ); |
192 | |
193 | if (!characters[i].isTransparent(_colorTable)) |
194 | { |
195 | style.append( QString::fromLatin1("background-color:%1;" ).arg(_lastBackColor.color(_colorTable).name() ) ); |
196 | } |
197 | } |
198 | |
199 | //open the span with the current style |
200 | openSpan(text,style); |
201 | _innerSpanOpen = true; |
202 | } |
203 | |
204 | //handle whitespace |
205 | if (std::isspace(ch)) |
206 | spaceCount++; |
207 | else |
208 | spaceCount = 0; |
209 | |
210 | |
211 | //output current character |
212 | if (spaceCount < 2) |
213 | { |
214 | //escape HTML tag characters and just display others as they are |
215 | if ( ch == '<' ) |
216 | text.append(L"<" ); |
217 | else if (ch == '>') |
218 | text.append(L">" ); |
219 | else |
220 | text.push_back(ch); |
221 | } |
222 | else |
223 | { |
224 | text.append(L" " ); //HTML truncates multiple spaces, so use a space marker instead |
225 | } |
226 | |
227 | } |
228 | |
229 | //close any remaining open inner spans |
230 | if ( _innerSpanOpen ) |
231 | closeSpan(text); |
232 | |
233 | //start new line |
234 | text.append(L"<br>" ); |
235 | |
236 | *_output << QString::fromStdWString(text); |
237 | } |
238 | void HTMLDecoder::openSpan(std::wstring& text , const QString& style) |
239 | { |
240 | text.append( QString(QLatin1String("<span style=\"%1\">" )).arg(style).toStdWString() ); |
241 | } |
242 | |
243 | void HTMLDecoder::closeSpan(std::wstring& text) |
244 | { |
245 | text.append(L"</span>" ); |
246 | } |
247 | |
248 | void HTMLDecoder::setColorTable(const ColorEntry* table) |
249 | { |
250 | _colorTable = table; |
251 | } |
252 | |