e5b2118b8e0a739afeaaa3ce94df5a34fb452b56
[profile/ivi/qtxmlpatterns.git] / src / xmlpatterns / api / qcoloroutput.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QFile>
43 #include <QHash>
44 #include <QTextCodec>
45
46 #include "qcoloroutput_p.h"
47
48 // TODO: rename insertMapping() to insertColorMapping()
49 // TODO: Use a smart pointer for managing ColorOutputPrivate *d;
50 // TODO: break out the C++ example into a snippet file
51
52 /* This include must appear here, because if it appears at the beginning of the file for
53  * instance, it breaks build -- "qglobal.h:628: error: template with
54  * C linkage" -- on Mac OS X 10.4. */
55 #ifndef Q_OS_WIN
56 #include <unistd.h>
57 #endif
58
59 QT_BEGIN_NAMESPACE
60
61 using namespace QPatternist;
62
63 namespace QPatternist
64 {
65     class ColorOutputPrivate
66     {
67     public:
68         ColorOutputPrivate() : currentColorID(-1)
69
70         {
71             /* - QIODevice::Unbuffered because we want it to appear when the user actually calls, performance
72              *   is considered of lower priority.
73              */
74             m_out.open(stderr, QIODevice::WriteOnly | QIODevice::Unbuffered);
75
76             coloringEnabled = isColoringPossible();
77         }
78
79         ColorOutput::ColorMapping   colorMapping;
80         int                         currentColorID;
81         bool                        coloringEnabled;
82
83         static const char *const foregrounds[];
84         static const char *const backgrounds[];
85
86         inline void write(const QString &msg)
87         {
88             m_out.write(msg.toLocal8Bit());
89         }
90
91         static QString escapeCode(const QString &in)
92         {
93             QString result;
94             result.append(QChar(0x1B));
95             result.append(QLatin1Char('['));
96             result.append(in);
97             result.append(QLatin1Char('m'));
98             return result;
99         }
100
101     private:
102         QFile                       m_out;
103
104         /*!
105          Returns true if it's suitable to send colored output to \c stderr.
106          */
107         inline bool isColoringPossible() const
108         {
109 #           if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
110                 /* Windows doesn't at all support ANSI escape codes, unless
111                  * the user install a "device driver". See the Wikipedia links in the
112                  * class documentation for details. */
113                 return false;
114 #           else
115                 /* We use QFile::handle() to get the file descriptor. It's a bit unsure
116                  * whether it's 2 on all platforms and in all cases, so hopefully this layer
117                  * of abstraction helps handle such cases. */
118                 return isatty(m_out.handle());
119 #           endif
120         }
121     };
122 }
123
124 const char *const ColorOutputPrivate::foregrounds[] =
125 {
126     "0;30",
127     "0;34",
128     "0;32",
129     "0;36",
130     "0;31",
131     "0;35",
132     "0;33",
133     "0;37",
134     "1;30",
135     "1;34",
136     "1;32",
137     "1;36",
138     "1;31",
139     "1;35",
140     "1;33",
141     "1;37"
142 };
143
144 const char *const ColorOutputPrivate::backgrounds[] =
145 {
146     "0;40",
147     "0;44",
148     "0;42",
149     "0;46",
150     "0;41",
151     "0;45",
152     "0;43"
153 };
154
155 /*!
156   \class ColorOutput
157   \since 4.4
158   \nonreentrant
159   \brief Outputs colored messages to \c stderr.
160   \internal
161
162   ColorOutput is a convenience class for outputting messages to \c
163   stderr using color escape codes, as mandated in ECMA-48. ColorOutput
164   will only color output when it is detected to be suitable. For
165   instance, if \c stderr is detected to be attached to a file instead
166   of a TTY, no coloring will be done.
167
168   ColorOutput does its best attempt. but it is generally undefined
169   what coloring or effect the various coloring flags has. It depends
170   strongly on what terminal software that is being used.
171
172   When using `echo -e 'my escape sequence'`, \c{\033} works as an
173   initiator but not when printing from a C++ program, despite having
174   escaped the backslash.  That's why we below use characters with
175   value 0x1B.
176
177   It can be convenient to subclass ColorOutput with a private scope,
178   such that the functions are directly available in the class using
179   it.
180
181   \section1 Usage
182
183   To output messages, call write() or writeUncolored(). write() takes
184   as second argument an integer, which ColorOutput uses as a lookup
185   key to find the color it should color the text in. The mapping from
186   keys to colors is done using insertMapping(). Typically this is used
187   by having enums for the various kinds of messages, which
188   subsequently are registered.
189
190   \code
191   enum MyMessage
192   {
193     Error,
194     Important
195   };
196
197   ColorOutput output;
198   output.insertMapping(Error, ColorOutput::RedForeground);
199   output.insertMapping(Import, ColorOutput::BlueForeground);
200
201   output.write("This is important", Important);
202   output.write("Jack, I'm only the selected official!", Error);
203   \endcode
204
205   \sa {http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html}{Bash Prompt HOWTO, 6.1. Colours},
206       {http://linuxgazette.net/issue51/livingston-blade.html}{Linux Gazette, Tweaking Eterm, Edward Livingston-Blade},
207       {http://www.ecma-international.org/publications/standards/Ecma-048.htm}{Standard ECMA-48, Control Functions for Coded Character Sets, ECMA International},
208       {http://en.wikipedia.org/wiki/ANSI_escape_code}{Wikipedia, ANSI escape code},
209       {http://linuxgazette.net/issue65/padala.html}{Linux Gazette, So You Like Color!, Pradeep Padala}
210  */
211
212 /*!
213   \enum ColorOutput::ColorCodeComponent
214   \value BlackForeground
215   \value BlueForeground
216   \value GreenForeground
217   \value CyanForeground
218   \value RedForeground
219   \value PurpleForeground
220   \value BrownForeground
221   \value LightGrayForeground
222   \value DarkGrayForeground
223   \value LightBlueForeground
224   \value LightGreenForeground
225   \value LightCyanForeground
226   \value LightRedForeground
227   \value LightPurpleForeground
228   \value YellowForeground
229   \value WhiteForeground
230   \value BlackBackground
231   \value BlueBackground
232   \value GreenBackground
233   \value CyanBackground
234   \value RedBackground
235   \value PurpleBackground
236   \value BrownBackground
237
238   \value DefaultColor ColorOutput performs no coloring. This typically
239                      means black on white or white on black, depending
240                      on the settings of the user's terminal.
241  */
242
243 /*!
244  Sets the color mapping to be \a cMapping.
245
246  Negative values are disallowed.
247
248  \sa colorMapping(), insertMapping()
249  */
250 void ColorOutput::setColorMapping(const ColorMapping &cMapping)
251 {
252     d->colorMapping = cMapping;
253 }
254
255 /*!
256  Returns the color mappings in use.
257
258  \sa setColorMapping(), insertMapping()
259  */
260 ColorOutput::ColorMapping ColorOutput::colorMapping() const
261 {
262     return d->colorMapping;
263 }
264
265 /*!
266   Constructs a ColorOutput instance, ready for use.
267  */
268 ColorOutput::ColorOutput() : d(new ColorOutputPrivate())
269 {
270 }
271
272 /*!
273  Destructs this ColorOutput instance.
274  */
275 ColorOutput::~ColorOutput()
276 {
277     delete d;
278 }
279
280 /*!
281  Sends \a message to \c stderr, using the color looked up in colorMapping() using \a colorID.
282
283  If \a color isn't available in colorMapping(), result and behavior is undefined.
284
285  If \a colorID is 0, which is the default value, the previously used coloring is used. ColorOutput
286  is initialized to not color at all.
287
288  If \a message is empty, effects are undefined.
289
290  \a message will be printed as is. For instance, no line endings will be inserted.
291  */
292 void ColorOutput::write(const QString &message, int colorID)
293 {
294     d->write(colorify(message, colorID));
295 }
296
297 /*!
298  Writes \a message to \c stderr as if for instance
299  QTextStream would have been used, and adds a line ending at the end.
300
301  This function can be practical to use such that one can use ColorOutput for all forms of writing.
302  */
303 void ColorOutput::writeUncolored(const QString &message)
304 {
305     d->write(message + QLatin1Char('\n'));
306 }
307
308 /*!
309  Treats \a message and \a colorID identically to write(), but instead of writing
310  \a message to \c stderr, it is prepared for being written to \c stderr, but is then
311  returned.
312
313  This is useful when the colored string is inserted into a translated string(dividing
314  the string into several small strings prevents proper translation).
315  */
316 QString ColorOutput::colorify(const QString &message, int colorID) const
317 {
318     Q_ASSERT_X(colorID == -1 || d->colorMapping.contains(colorID), Q_FUNC_INFO,
319                qPrintable(QString::fromLatin1("There is no color registered by id %1").arg(colorID)));
320     Q_ASSERT_X(!message.isEmpty(), Q_FUNC_INFO, "It makes no sense to attempt to print an empty string.");
321
322     if(colorID != -1)
323         d->currentColorID = colorID;
324
325     if(d->coloringEnabled && colorID != -1)
326     {
327         const int color(d->colorMapping.value(colorID));
328
329         /* If DefaultColor is set, we don't want to color it. */
330         if(color & DefaultColor)
331             return message;
332
333         const int foregroundCode = (int(color) & ForegroundMask) >> ForegroundShift;
334         const int backgroundCode = (int(color) & BackgroundMask) >> BackgroundShift;
335         QString finalMessage;
336         bool closureNeeded = false;
337
338         if(foregroundCode)
339         {
340             finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::foregrounds[foregroundCode - 1])));
341             closureNeeded = true;
342         }
343
344         if(backgroundCode)
345         {
346             finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::backgrounds[backgroundCode - 1])));
347             closureNeeded = true;
348         }
349
350         finalMessage.append(message);
351
352         if(closureNeeded)
353         {
354             finalMessage.append(QChar(0x1B));
355             finalMessage.append(QLatin1String("[0m"));
356         }
357
358         return finalMessage;
359     }
360     else
361         return message;
362 }
363
364 /*!
365   Adds a color mapping from \a colorID to \a colorCode, for this ColorOutput instance.
366
367   This is a convenience function for creating a ColorOutput::ColorMapping instance and
368   calling setColorMapping().
369
370   \sa colorMapping(), setColorMapping()
371  */
372 void ColorOutput::insertMapping(int colorID, const ColorCode colorCode)
373 {
374     d->colorMapping.insert(colorID, colorCode);
375 }
376
377 QT_END_NAMESPACE