Salome HOME
Merge branch 'master' of ssh://git.salome-platform.org/modules/gui
[modules/gui.git] / src / Qtx / Qtx.cxx
1 // Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library 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 GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File:      Qtx.cxx
24 // Author:    Sergey TELKOV
25 //
26 #include "Qtx.h"
27
28 #include <QDir>
29 #include <QMenu>
30 #include <QRegExp>
31 #include <QBitmap>
32 #include <QWidget>
33 #include <QLayout>
34 #include <QPainter>
35 #include <QDirModel>
36 #include <QFileInfo>
37 #include <QCompleter>
38 #include <QApplication>
39 #include <QDesktopWidget>
40 #include <QtDebug>
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <clocale>
46
47 #define BICOLOR_CHANGE_HUE
48
49 /*!
50   \brief Auxiliary function converting string \a str to the integer value.
51   Parameter \a defVal specifies default value that is returned if conversion can't be done.
52   Parameters \a minVal and \a maxVal limit the resulting value.
53   \param str string being converted
54   \param defVal default value
55   \param minVal minimum allowed value
56   \param maxVal maximum allowed value
57   \return integer value obtained from the string
58   \internal
59 */
60 static int stringToInt( const QString& str, int defVal, int minVal, int maxVal )
61 {
62   bool ok;
63   int v = str.toInt( &ok );
64   if ( !ok ) v = defVal;
65   return qMin( qMax( v, minVal ), maxVal );
66 }
67
68 /*!
69   \class Qtx
70   \brief A set of helpful utility functions.
71
72   The class implements a set of the static functions which can be used
73   for the different purposes:
74   - specify tab order for the set of widgets: setTabOrder()
75   - align one widget to the coordinates of the another one: alignWidget()
76   - remove extra separators from the menu or toolbar: simplifySeparators()
77   - retrieve directory, file name and extension parts of the path:
78   dir(), file(), extension()
79   - get the path to the temporary directory: tmpDir()
80   - create or remove a directory (recursively): mkDir(), rmDir()
81   - convert text file from DOS to UNIX native format: dos2unix()
82   - convert a picture to the gray scale: grayscale()
83   - other
84 */
85
86 /*!
87   \brief Convert character array (ASCII string) to the QString.
88   \param str character array
89   \param len array length, if < 0, the array should be zero-terminated
90   \return QString object
91  */
92 QString Qtx::toQString( const char* str, const int len )
93 {
94   return toQString( (unsigned char*)str, len );
95 }
96
97 /*!
98   \brief Convert integer array (UNICODE string) to the QString.
99   \param str integer array
100   \param len array length, if < 0, the array should be zero-terminated
101   \return QString object
102  */
103 QString Qtx::toQString( const short* str, const int len )
104 {
105   return toQString( (unsigned short*)str, len );
106 }
107
108 /*!
109   \brief Convert character array (ASCII string) to the QString.
110   \param str character array
111   \param len array length, if < 0, the array should be zero-terminated
112   \return QString object
113  */
114 QString Qtx::toQString( const unsigned char* str, const int len )
115 {
116   QString res;
117   const unsigned char* s = str;
118   while ( len < 0 || res.length() < len )
119   {
120     if ( *s == '\0' )
121       break;
122
123     res.append( QChar( *s ) );
124     s++;
125   }
126   return res;
127 }
128
129 /*!
130   \brief Convert integer array (UNICODE string) to the QString.
131   \param str integer array
132   \param len array length, if < 0, the array should be zero-terminated
133   \return QString object
134  */
135 QString Qtx::toQString( const unsigned short* str, const int len )
136 {
137   QString res;
138   const unsigned short* s = str;
139   while ( len < 0 || res.length() < len )
140   {
141     if ( *s == '\0' )
142       break;
143
144     res.append( QChar( *s ) );
145     s++;
146   }
147   return res;
148 }
149
150 /*!
151   \brief Set tab order for specified list of widgets.
152
153   The function has arbitrary number of parameters, each should be
154   hovewer of QWidget* type. Last parameter should be null pointer.
155
156   \param first first widget in the sequence
157 */
158 void Qtx::setTabOrder( QWidget* first, ... )
159 {
160   va_list wids;
161   va_start( wids, first );
162
163   QWidgetList widList;
164
165   QWidget* cur = first;
166   while ( cur )
167   {
168     widList.append( cur );
169     cur = va_arg( wids, QWidget* );
170   }
171
172   setTabOrder( widList );
173 }
174
175 /*!
176   \brief Set tab order for specified list of widgets.
177   \param widgets list of widgets
178 */
179 void Qtx::setTabOrder( const QWidgetList& widgets )
180 {
181   if ( widgets.count() < 2 )
182     return;
183
184   QWidget* prev = 0;
185   for ( QWidgetList::const_iterator it = widgets.begin(); it!= widgets.end(); ++it )
186   {
187     QWidget* next = *it;
188     if ( prev && next )
189       QWidget::setTabOrder( prev, next );
190     prev = next;
191   }
192 }
193
194 /*!
195   \brief Align widget \a src relative to widget \a ref acording to the 
196          alignment flags \a alignFlags.
197   \param src source widget (being aligned)
198   \param ref reference widget (source widget being aligned to)
199   \param alignFlags alignment flags (Qtx::AlignmentFlags)
200 */
201 void Qtx::alignWidget( QWidget* src, const QWidget* ref, const int alignFlags )
202 {
203   if ( !src || !ref || !alignFlags )
204     return;
205
206   QPoint srcOri = src->pos();
207   QPoint refOri = ref->pos();
208   if ( src->parentWidget() && !src->isTopLevel() )
209     srcOri = src->parentWidget()->mapToGlobal( srcOri );
210   if ( ref->parentWidget() && !ref->isTopLevel() )
211     refOri = ref->parentWidget()->mapToGlobal( refOri );
212
213   int x = srcOri.x(), y = srcOri.y();
214   int refWidth = ref->frameGeometry().width(), refHei = ref->frameGeometry().height();
215   int srcWidth = src->frameGeometry().width(), srcHei = src->frameGeometry().height();
216
217   if ( srcWidth <= 0 )
218     srcWidth = src->sizeHint().width();
219   if ( srcHei <= 0 )
220     srcHei = src->sizeHint().height();
221
222   int border = 0;
223   if ( ref->isTopLevel() && ref->isMaximized() &&
224        src->isTopLevel() && !src->isMaximized() )
225     border = ( src->frameGeometry().width() - src->width() ) / 2;
226
227   if ( alignFlags & Qtx::AlignLeft )
228     x = refOri.x() + border;
229   if ( alignFlags & Qtx::AlignOutLeft )
230     x = refOri.x() - srcWidth - border;
231   if ( alignFlags & Qtx::AlignRight )
232     x = refOri.x() + refWidth - srcWidth - border;
233   if ( alignFlags & Qtx::AlignOutRight )
234     x = refOri.x() + refWidth + border;
235   if ( alignFlags & Qtx::AlignTop )
236     y = refOri.y() + border;
237   if ( alignFlags & Qtx::AlignOutTop )
238     y = refOri.y() - srcHei - border;
239   if ( alignFlags & Qtx::AlignBottom )
240     y = refOri.y() + refHei - srcHei - border;
241   if ( alignFlags & Qtx::AlignOutBottom )
242     y = refOri.y() + refHei + border;
243   if ( alignFlags & Qtx::AlignHCenter )
244     x = refOri.x() + ( refWidth - srcWidth ) / 2;
245   if ( alignFlags & Qtx::AlignVCenter )
246     y = refOri.y() + ( refHei - srcHei ) / 2;
247
248   if ( src->parentWidget() && !src->isTopLevel() )
249   {
250     QPoint pos = src->parentWidget()->mapFromGlobal( QPoint( x, y ) );
251     x = pos.x();
252     y = pos.y();
253   }
254
255   QWidget* desk = QApplication::desktop();
256   if ( desk && x + srcWidth + border > desk->width() )
257     x = desk->width() - srcWidth - border;
258   if ( desk && y + srcHei + border > desk->height() )
259     y = desk->height() - srcHei - border;
260
261   x = qMax( x, 0 );
262   y = qMax( y, 0 );
263
264   src->move( x, y );
265 }
266
267 /*!
268   \brief Remove (recursively) unnecessary separators from the menu or toolbar.
269   \param wid widget, should be of QMenu* or QToolBar* class
270 */
271 void Qtx::simplifySeparators( QWidget* wid, const bool recursive )
272 {
273   if ( !wid )
274     return;
275
276   QList<QAction*> items = wid->actions();
277   if ( items.isEmpty() )
278     return;
279
280   QList<QAction*> toRemove;
281   for ( int i = 1; i < items.count(); i++ )
282   {
283     if ( items[i]->isSeparator() && items[i - 1]->isSeparator() )
284       toRemove.append( items[i] );
285
286     if ( recursive && items[i]->menu() )
287       simplifySeparators( items[i]->menu(), recursive );
288   }
289
290   for ( QList<QAction*>::iterator it = toRemove.begin(); it != toRemove.end(); ++it )
291     wid->removeAction( *it );
292
293   items = wid->actions();
294   if ( !items.isEmpty() && items[0]->isSeparator() )
295     wid->removeAction( items[0] );
296
297   items = wid->actions();
298   if ( !items.isEmpty() && items[items.count() - 1]->isSeparator() )
299     wid->removeAction( items[items.count() - 1] );
300 }
301
302 /*!
303   \brief Return \c true if specified \a parent is a parent object
304          of given \a child (in terms of QObject).
305
306   This function works recursively. It means that \a true is also
307   returned if \a parent is a grand-father, grand-grand-father, etc
308   of \a child. If the same object is given as both \a parent and
309   \a child, \c true is also returned.
310
311   \param child child object
312   \param parent parent object
313   \return \c true if the \a parent is a parent of \a child
314 */
315 bool Qtx::isParent( QObject* child, QObject* parent )
316 {
317   if ( !child || !parent )
318     return false;
319
320   bool res = false;
321   QObject* obj = child;
322   while ( !res && obj )
323   {
324     res = obj == parent;
325     obj = obj->parent();
326   }
327   return res;
328 }
329
330 /*!
331   \brief Find the parent object of class specified by \a className (in terms of QObject).
332
333   \param obj current object
334   \param className class name of the parent
335   \return parent object or null pointer if the parent not found
336 */
337 QObject* Qtx::findParent( QObject* obj, const char* className )
338 {
339   if ( !obj )
340     return 0;
341
342   if ( !className || !strlen( className ) )
343     return obj->parent();
344
345   QObject* res = 0;
346   QObject* p = obj->parent();
347   while ( p && !res )
348   {
349     if ( p->inherits( className ) )
350       res = p;
351     p = p->parent();
352   }
353
354   return res;
355 }
356
357 /*!
358   \brief Return directory part of the file path.
359
360   If the file path does not include directory part (the file is in the
361   current directory), null string is returned.
362
363   \param path file path
364   \param abs if true (default) \a path parameter is treated as absolute file path
365   \return directory part of the file path
366 */
367 QString Qtx::dir( const QString& path, const bool abs )
368 {
369   QDir aDir = QFileInfo( path ).dir();
370   QString dirPath = abs ? aDir.absolutePath() : aDir.path();
371   if ( dirPath == QString( "." ) )
372     dirPath = QString();
373   return dirPath;
374 }
375
376 /*!
377   \brief Return file name part of the file path.
378
379   \param path file path
380   \param withExt if true (default) complete file name (with all
381          extension except the last) is returned, otherwise only base name
382          is returned
383   \return file name part of the file path
384 */
385 QString Qtx::file( const QString& path, bool withExt )
386 {
387   QString fPath = path;
388   while ( !fPath.isEmpty() && ( fPath[fPath.length() - 1] == '\\' || fPath[fPath.length() - 1] == '/' ) )
389     fPath.remove( fPath.length() - 1, 1 );
390
391   if ( withExt )
392     return QFileInfo( fPath ).fileName();
393   else
394     return QFileInfo( fPath ).completeBaseName();
395 }
396
397 /*!
398   \brief Return extension part of the file path.
399
400   \param path file path
401   \param full if true complete extension (all extensions, dot separated)
402          is returned, otherwise (default) only last extension is returned
403   \return extension part of the file path
404 */
405 QString Qtx::extension( const QString& path, const bool full )
406 {
407   return full ? QFileInfo( path ).completeSuffix() : QFileInfo( path ).suffix();
408 }
409
410 /*!
411   \brief Convert the given parameter to the platform-specific library name.
412
413   The function appends platform-specific prefix (lib) and suffix (.dll/.so)
414   to the library file name.
415   For example, if \a str = "mylib", "libmylib.so" is returned for Linux and
416   mylib.dll for Windows.
417
418   \param str short library name
419   \return full library name
420 */
421 QString Qtx::library( const QString& str )
422 {
423   QString path = dir( str, false );
424   QString name = file( str, false );
425   QString ext  = extension( str );
426
427 #ifndef WIN32
428   if ( !name.startsWith( "lib" ) )
429     name = QString( "lib" ) + name;
430 #endif
431
432 #ifdef WIN32
433   QString libExt( "dll" );
434 #else
435   QString libExt( "so" );
436 #endif
437
438   if ( ext.toLower() != QString( "so" ) && ext.toLower() != QString( "dll" ) )
439   {
440     if ( !name.isEmpty() && !ext.isEmpty() )
441       name += QString( "." );
442     name += ext;
443   }
444
445   ext = libExt;
446
447   QString fileName = addSlash( path ) + name + QString( "." ) + ext;
448
449   return fileName;
450 }
451
452 /*!
453   \brief Get the temporary directory name.
454   \return temporary directory (platform specific)
455 */
456 QString Qtx::tmpDir()
457 {
458   const char* tmpdir = ::getenv( "TEMP" );
459   if ( !tmpdir )
460     tmpdir = ::getenv ( "TMP" );
461   if ( !tmpdir )
462   {
463 #ifdef WIN32
464     tmpdir = "C:\\";
465 #else
466     tmpdir = "/tmp";
467 #endif
468   }
469   return QString( tmpdir );
470 }
471
472 /*!
473   \brief Create directory recursively including all intermediate sub directories.
474   \return \c true if the directory is successfully created and \c false otherwise
475 */
476 bool Qtx::mkDir( const QString& dirPath )
477 {
478   return QDir().mkpath( dirPath );
479 }
480
481 /*!
482   \brief Remove directory recursively including all subdirectories and files.
483   \return \c true if the directory is successfully removed and \c false otherwise
484 */
485 bool Qtx::rmDir( const QString& thePath )
486 {
487   QFileInfo fi( thePath );
488   if ( !fi.exists() )
489     return true;
490
491   bool stat = true;
492   if ( fi.isFile() )
493     stat = QFile::remove( thePath );
494   else if ( fi.isDir() )
495   {
496     QDir aDir( thePath );
497     QFileInfoList anEntries = aDir.entryInfoList();
498     for ( QFileInfoList::iterator it = anEntries.begin(); it != anEntries.end(); ++it )
499     {
500       QFileInfo inf = *it;
501       if ( inf.fileName() == "." || inf.fileName() == ".." )
502         continue;
503       stat = stat && rmDir( inf.absoluteFilePath() );
504     }
505     stat = stat && aDir.rmdir( thePath );
506   }
507   return stat;
508 }
509
510 /*!
511   \brief Add a slash (platform-specific) to the end of \a path
512          if it is not already there.
513   \param path directory path
514   \return modified path (with slash added to the end)
515 */
516 QString Qtx::addSlash( const QString& path )
517 {
518   QString res = path;
519   if ( !res.isEmpty() && res.at( res.length() - 1 ) != QChar( '/' ) &&
520        res.at( res.length() - 1 ) != QChar( '\\' ) )
521   res += QDir::separator();
522   return res;
523 }
524
525 /*!
526   \brief Convert text file from DOS format to UNIX.
527
528   The function replaces "LF/CR" symbols sequence by "LF" symbol.
529
530   \param absName file name
531   \return \c true if the file is converted successfully and \c false in
532          case of any error
533 */
534 bool Qtx::dos2unix( const QString& absName )
535 {
536   FILE* src = ::fopen( absName.toLatin1(), "rb" );
537   if ( !src )
538     return false;
539
540   /* we'll use temporary file */
541   char temp[512] = { '\0' };
542   QString dir = Qtx::dir( absName );
543   FILE* tgt = ::fopen( strcpy( temp, ::tempnam( dir.toLatin1(), "__x" ) ), "wb" );
544   if ( !tgt )
545     return false;
546
547   /* temp -> result of conversion */
548   const char CR = 0x0d;
549   const char LF = 0x0a;
550   bool waitingLF = false;
551
552   while( true )
553   {
554     int  outcnt = 0;
555     char inbuf[512], outbuf[512];
556
557     /* convert buffer */
558     int nbread = ::fread( inbuf, 1, sizeof( inbuf ), src );
559     for ( int incnt = 0; incnt < nbread; incnt++  )
560     {
561       if ( waitingLF )
562       {
563         waitingLF = false;
564         if ( inbuf[incnt] == LF )
565           outbuf[outcnt++] = LF;
566         else
567           outbuf[outcnt++] = CR;
568       }
569       else if ( inbuf[incnt] == CR )
570         waitingLF = true;
571       else
572         outbuf[outcnt++] = inbuf[incnt];
573     }
574
575     /* check last sym in buffer */
576     waitingLF = ( inbuf[nbread - 1] == CR );
577
578     /* write converted buffer to temp file */
579     int nbwri = ::fwrite( outbuf, 1, outcnt, tgt );
580     if ( nbwri != outcnt )
581     {
582       ::fclose( src );
583                         ::fclose( tgt );
584       QFile::remove( QString( temp ) );
585       return false;
586     }
587     if ( nbread != sizeof( inbuf ) )
588       break;              /* converted ok */
589   }
590   ::fclose( src );
591   ::fclose( tgt );
592
593   /* rename temp -> src */
594   if ( !QFile::remove( absName ) )
595     return false;
596
597   return QDir().rename( QString( temp ), absName );
598 }
599
600 /*!
601   \brief Create path completer which can be used in the widgets
602   to provide auto completions.
603
604   Create an instance of QCompleter class and returns the pointer on it.
605   The calling function is responsible to the desstroying of the created 
606   completer object.
607
608   The QCompleter class provides completions based on a item model and can be
609   used in such as QLineEdit and QComboBox. 
610   When the user starts typing a word, QCompleter suggests possible ways of 
611   completing the word, based on a word list. 
612
613   \param type path type (Qtx::PathType)
614   \param filter file/directory filters (list of wildcards, separated by ";;")
615   \return a pointer to the created completer
616 */
617 QCompleter* Qtx::pathCompleter( const PathType type, const QString& filter )
618 {
619   QStringList extList;
620   QStringList filterList = filter.split( ";;" );
621   for ( QStringList::const_iterator it = filterList.begin(); it != filterList.end(); ++it )
622   {
623     QRegExp rx( "[\\s\\w,;]*\\(?\\*\\.([\\w]+)\\)?[\\d\\s\\w]*" );
624     int index = 0;
625     while ( ( index = rx.indexIn( *it, index ) ) != -1 )
626     {
627       extList.append( QString( "*.%1" ).arg( rx.cap( 1 ) ) );
628       index += rx.matchedLength();
629     }
630   }
631
632   QDir::Filters filters = 0;
633   switch ( type )
634   {
635   case PT_OpenFile:
636   case PT_SaveFile:
637     filters = QDir::AllEntries | QDir::AllDirs | QDir::NoDotAndDotDot;
638     break;
639   case PT_Directory:
640     filters = QDir::Drives | QDir::Dirs | QDir::NoDotAndDotDot;
641     break;
642   }
643
644   QDirModel* dm = new QDirModel( extList, filters, QDir::Unsorted );
645   QCompleter* cmp = new QCompleter( dm, 0 );
646   dm->setParent( cmp );
647
648   return cmp;
649 }
650
651 /*!
652   \brief Parse given string to retrieve environment variable.
653
654   Looks through the string for the patterns: ${name} or $(name) or %name%.
655   If string contains variable satisfying any pattern, the variable name
656   is returned, start index of the variable is returned in the \a start parameter,
657   and length of the variable is returned in the \a len parameter.
658
659   \param str string being processed
660   \param start if variable is found, this parameter contains its starting 
661          position in the \a str
662   \param len if variable is found, this parameter contains its length 
663   \return first found variable or null QString if there is no ones
664 */
665 QString Qtx::findEnvVar( const QString& str, int& start, int& len )
666 {
667   QString varName;
668   len = 0;
669
670   QRegExp rx( "(^\\$\\{|[^\\$]\\$\\{)([a-zA-Z]+[a-zA-Z0-9_]*)(\\})|(^\\$\\(|[^\\$]\\$\\()([a-zA-Z]+[a-zA-Z0-9_]*)(\\))|(^\\$|[^\\$]\\$)([a-zA-Z]+[a-zA-Z0-9_]*)|(^%|[^%]%)([a-zA-Z]+[a-zA-Z0-9_]*)(%[^%]|%$)" );
671
672   int pos = rx.indexIn( str, start );
673   if ( pos != -1 )
674   {
675     int i = 1;
676     while ( i <= rx.numCaptures() && varName.isEmpty() )
677     {
678       QString capStr = rx.cap( i );
679       if ( !capStr.contains( "%" ) && !capStr.contains( "$" ) )
680         varName = capStr;
681       i++;
682     }
683
684     if ( !varName.isEmpty() )
685     {
686       int capIdx = i - 1;
687       start = rx.pos( capIdx );
688       int end = start + varName.length();
689       if ( capIdx > 1 && rx.cap( capIdx - 1 ).contains( QRegExp( "\\$|%" ) ) )
690         start = rx.pos( capIdx - 1 ) + rx.cap( capIdx - 1 ).indexOf( QRegExp( "\\$|%" ) );
691       if ( capIdx < rx.numCaptures() && !rx.cap( capIdx - 1 ).isEmpty() )
692         end++;
693       len = end - start;
694     }
695   }
696   return varName;
697 }
698
699 /*!
700   \brief Substitute environment variables by their values.
701
702   Environment variable is substituted by its value.
703
704   \param str string to be processed
705   \return processed string (with all substitutions made)
706 */
707 QString Qtx::makeEnvVarSubst( const QString& str, const SubstMode mode )
708 {
709   QString res = str;
710   if ( mode != Never )
711   {
712     QMap<QString, int> ignoreMap;
713
714     int start( 0 ), len( 0 );
715     while ( true )
716     {
717       QString envName = findEnvVar( res, start, len );
718       if ( envName.isNull() )
719         break;
720
721       QString newStr;
722       if ( ::getenv( envName.toLatin1() ) || mode == Always )
723         newStr = QString( ::getenv( envName.toLatin1() ) );
724
725       if ( newStr.isNull() )
726       {
727         if ( ignoreMap.contains( envName ) )
728         {
729           start += len;
730           continue;
731         }
732         ignoreMap.insert( envName, 0 );
733       }
734       res.replace( start, len, newStr );
735     }
736
737     res.replace( "$$", "$" );
738     res.replace( "%%", "%" );
739   }
740
741   return res;
742 }
743
744 /*!
745   \brief Pack the specified color into integer RGB set.
746   \param c unpacked color
747   \return packed color
748 */
749 int Qtx::rgbSet( const QColor& c )
750 {
751   return rgbSet( c.red(), c.green(), c.blue() );
752 }
753
754 /*!
755   \brief Pack the specified RGB color components into integer RGB set.
756   \param r red component
757   \param g green component
758   \param b blue component
759   \return packed color
760 */
761 int Qtx::rgbSet( const int r, const int g, const int b )
762 {
763   return ( ( ( 0xff & r ) << 16 ) + ( ( 0xff & g ) << 8 ) + ( 0xff & b ) );
764 }
765
766 /*!
767   \brief Unpack the specified integer RGB set to the color.
768   \param rgb packed color
769   \return unpacked color (QColor)
770 */
771 QColor Qtx::rgbSet( const int rgb )
772 {
773   int r, g, b;
774   rgbSet( rgb, r, g, b );
775   return QColor( r, g, b );
776 }
777
778 /*!
779   \brief Unpack the specified integer RGB set to the three RGB components.
780   \param rgb packed color
781   \param r returned unpacked red component
782   \param g returned unpacked green component
783   \param b returned unpacked blue component
784 */
785 void Qtx::rgbSet( const int rgb, int& r, int& g, int& b )
786 {
787   r = ( rgb >> 16 ) & 0xff;
788   g = ( rgb >> 8 ) & 0xff;
789   b = rgb & 0xff;
790 }
791
792 /*!
793   \brief Return the color specified by the index between min (blue) and max (red).
794   \param index color index
795   \param min required minimum hue value
796   \param max required maximum hue value
797   \return resulting color
798 */
799 QColor Qtx::scaleColor( const int index, const int min, const int max )
800 {
801   static const int HUE[10] = {230, 210, 195, 180, 160, 80, 60, 50, 30, 0};
802
803   int hue = HUE[0];
804
805   if ( min != max )
806   {
807     double aPosition = 9.0 * ( index - min ) / ( max - min );
808     if ( aPosition > 0.0 )
809     {
810       if ( aPosition >= 9.0 )
811         hue = HUE[9];
812       else
813       {
814         int idx = (int)aPosition;
815         hue = HUE[idx] + int( ( aPosition - idx ) * ( HUE[idx + 1] - HUE[idx] ) );
816       }
817     }
818   }
819
820   return QColor::fromHsv( hue, 255, 255 );
821 }
822
823 /*!
824   \brief Generate required number of colors aligned from blue to red.
825   \param num required number of colors
826   \param lst returned set of colors
827 */
828 void Qtx::scaleColors( const int num, QColorList& lst )
829 {
830   lst.clear();
831   for ( int i = 0; i < num; i++ )
832     lst.append( scaleColor( i, 0, num - 1 ) );
833 }
834
835 /*!
836   \brief Scale the pixmap to the required size.
837
838   If \a h is 0 (default) the value of \a w is used instead (to create
839   square pixmap).
840
841   \param icon pixmap to be resized
842   \param w required pixmap width
843   \param h required pixmap height
844   \return scaled pixmap
845 */
846 QPixmap Qtx::scaleIcon( const QPixmap& icon, const unsigned w, const unsigned h )
847 {
848   QPixmap p;
849   int aw = w, ah = h <= 0 ? w : h;
850   if ( icon.isNull() || aw <= 0 || ah <= 0 || ( aw == icon.width() && ah == icon.height() ) )
851     p = icon;
852   else
853     p = icon.fromImage( icon.toImage().scaled( aw, ah, Qt::KeepAspectRatio, Qt::SmoothTransformation ) );
854   return p;
855 }
856
857 /*!
858   \brief Convert given image to the grayscale format.
859   \param img initial image
860   \return converted to the grayscale image
861 */
862 QImage Qtx::grayscale( const QImage& img )
863 {
864   QImage res = img;
865
866   int colNum = res.numColors();
867   if ( colNum )
868   {
869     for ( int i = 0; i < colNum; i++ )
870       res.setColor( i, qGray( res.color( i ) ) );
871   }
872   else
873   {
874     for ( int y = 0; y < res.height(); y++ )
875     {
876       for ( int x = 0; x < res.width(); x++ )
877       {
878         QRgb pix = res.pixel( x, y );
879         res.setPixel( x, y, qRgba( qGray( pix ), qGray( pix ), qGray( pix ), qAlpha( pix ) ) );
880       }
881     }
882   }
883
884   return res;
885 }
886
887 /*!
888   \brief Convert given pixmap to the grayscale format.
889   \param pix initial pixmap
890   \return converted to the grayscale pixmap
891 */
892 QPixmap Qtx::grayscale( const QPixmap& pix )
893 {
894   QPixmap res;
895   res.fromImage( grayscale( pix.toImage() ) );
896   return res;
897 }
898
899 /*!
900   \brief Create transparent image.
901   \param w required image width
902   \param h required image height
903   \param d required image depth
904   \return generated image
905 */
906 QImage Qtx::transparentImage( const int w, const int h, const int d )
907 {
908   QImage::Format fmt;
909   switch ( d )
910   {
911   case 1:
912     fmt = QImage::Format_Mono;
913     break;
914   case 8:
915     fmt = QImage::Format_Indexed8;
916     break;
917   case 16:
918   case 24:
919   case 32:
920   default:
921     fmt = QImage::Format_ARGB32;
922     break;
923   }
924
925   QImage img( w, h, fmt );
926   if ( !img.isNull() )
927   {
928 //    img.setAlphaBuffer( true );
929     for ( int i = 0; i < img.height(); i++ )
930       for ( int j = 0; j < img.width(); j++ )
931         img.setPixel( j, i, qRgba( 0, 0, 0, 0 ) );
932   }
933   return img;
934 }
935
936 /*!
937   \brief Create transparent pixmap.
938   \param w required image width
939   \param h required pixmap height
940   \param d required pixmap depth
941   \return generated pixmap
942 */
943 QPixmap Qtx::transparentPixmap( const int w, const int h, const int d )
944 {
945   QPixmap pix;
946   QImage img = transparentImage( w, h, d );
947   if ( !img.isNull() )
948     pix.fromImage( img );
949   return pix;
950 }
951
952 /*!
953   \brief Create composite pixmap. 
954
955   Pixmap \a pix is drawn over pixmap \a dest with coordinates
956   specified relatively to the upper left corner of \a dest.
957   If \a dest is not given, the new empty pixmap with appropriate size created instead.
958
959   \param pix source pixmap
960   \param x horizontal shift
961   \param y vertical shift
962   \param dest background pixmap
963   \return resulting pixmap
964 */
965 QPixmap Qtx::composite( const QPixmap& pix, const int x, const int y, const QPixmap& dest )
966 {
967   if ( pix.isNull() )
968     return dest;
969
970   int width = qMax( pix.width() + x, dest.width() );
971   int height = qMax( pix.height() + y, dest.height() );
972
973   QPixmap res( width, height );
974   QImage img = transparentImage( width, height, 32 );
975
976   QPainter p;
977   p.begin( &res );
978   p.fillRect( 0, 0, width, height, QBrush( Qt::white ) );
979
980   if ( !dest.isNull() )
981   {
982     p.drawPixmap( 0, 0, dest );
983     QImage temp = dest.toImage();
984     for ( int i = 0; i < temp.width() && i < img.width(); i++ )
985     {
986       for ( int j = 0; j < temp.height() && j < img.height(); j++ )
987       {
988         if ( temp.hasAlphaChannel() )
989           img.setPixel( i, j, temp.pixel( i, j ) );
990         else
991         {
992           QRgb p = temp.pixel( i, j );
993           img.setPixel( i, j, qRgba( qRed( p ), qGreen( p ), qBlue( p ), 255 ) );
994         }
995       }
996     }
997   }
998
999   p.drawPixmap( x, y, pix );
1000   QImage temp = pix.toImage();
1001   for ( int c = x; c < temp.width() + x && c < img.width(); c++ )
1002   {
1003     for ( int r = y; r < temp.height() + y && r < img.height(); r++ )
1004     {
1005       if ( qAlpha( temp.pixel( c - x, r - y ) ) > 0 )
1006         img.setPixel( c, r, temp.pixel( c - x, r - y ) );
1007     }
1008   }
1009
1010   p.end();
1011
1012   for ( int ai = 0; ai < img.width(); ai++ )
1013   {
1014     for ( int aj = 0; aj < img.height(); aj++ )
1015     {
1016       if ( qAlpha( img.pixel( ai, aj ) ) < 1 )
1017         img.setPixel( ai, aj, qRgba( 255, 255, 255, 255 ) );
1018       else
1019         img.setPixel( ai, aj, qRgba( 0, 0, 0, 0 ) );
1020     }
1021   }
1022
1023   QBitmap bmp( width, height );
1024   bmp.fromImage( img, Qt::ColorMode_Mask | Qt::ThresholdDither );
1025   res.setMask( bmp );
1026
1027   return res;
1028 }
1029
1030 /*!
1031   \brief Convert color to the string representation.
1032   
1033   The resulting string is in the one of two possible formats
1034   (\c RR, \c GG, \c BB and \c AA value represent red, green, blue
1035   and alpha components of the color):
1036   - if color has alpha channel : "#RR,#GG,#BB,#AA"
1037   - if color does not have alpha channel : "#RRGGBB" 
1038
1039   If color is invalid, null string is returned.
1040
1041   Backward conversion can be done with stringToColor() method.
1042
1043   \param color color to be converted
1044   \return string representation of the color
1045
1046   \sa stringToColor()
1047 */
1048 QString Qtx::colorToString( const QColor& color )
1049 {
1050   QString str;
1051   if ( color.isValid() )
1052   {
1053     if ( color.alpha() != 255 )
1054     {
1055       QStringList vals;
1056       vals << QString( "#%1" ).arg( color.red(),   0, 16 );
1057       vals << QString( "#%1" ).arg( color.green(), 0, 16 );
1058       vals << QString( "#%1" ).arg( color.blue(),  0, 16 );
1059       vals << QString( "#%1" ).arg( color.alpha(), 0, 16 );
1060       str = vals.join( "," );
1061     }
1062     else
1063     {
1064       str = color.name();
1065     }
1066   }
1067   return str;
1068 }
1069
1070 /*!
1071   \brief Create color from the string representation.
1072   
1073   The parameter \a str must be in the one of following formats
1074   (\c RR, \c GG, \c BB and \c AA value represent red, green, blue
1075   and alpha components of the color):
1076   - "#RR,#GG,#BB[,#AA]" or "#RR #GG #BB[ #AA]" (\c RR, \c GG, \c BB
1077   and optional \c AA values represent red, green, blue and alpha
1078   components of the color in hexadecimal form)
1079   - "RR,GG,BB[,AA]" or "RR GG BB[ AA]" (\c RR, \c GG, \c BB
1080   and optional \c AA values represent red, green, blue and alpha
1081   components of the color in decimal form)
1082   - "#RRGGBB" - (\c RR, \c GG and \c BB values represent red, green and blue
1083   components of the color in hexadecimal form)
1084   - an integer value representing packed color components (see rgbSet())
1085   - a name from the list of colors defined in the list of SVG color keyword names
1086   provided by the World Wide Web Consortium; for example, "steelblue" or "gainsboro".
1087
1088   Backward conversion can be done with colorToString() method.
1089
1090   \param str string representation of the color
1091   \param color resulting color value
1092   \return \c true if the conversion is successful and \c false otherwise
1093
1094   \sa colorToString(), rgbSet()
1095 */
1096 bool Qtx::stringToColor( const QString& str, QColor& color )
1097 {
1098   bool res = true;
1099   QStringList vals = str.split( QRegExp( "[\\s|,]" ), QString::SkipEmptyParts );
1100
1101   QIntList nums;
1102   for ( QStringList::const_iterator it = vals.begin(); it != vals.end() && res; ++it )
1103   {
1104     int num = 0;
1105     if ( (*it).startsWith( "#" ) )
1106       num = (*it).mid( 1 ).toInt( &res, 16 );
1107     else
1108       num = (*it).toInt( &res, 10 );
1109     if ( res )
1110       nums.append( num );
1111   }
1112
1113   res = res && nums.count() >= 3;
1114   if ( res )
1115     color.setRgb( nums[0], nums[1], nums[2] );
1116
1117   if ( !res )
1118   {
1119     int pack = str.toInt( &res );
1120     if ( res )
1121       color = Qtx::rgbSet( pack );
1122   }
1123
1124   if ( !res )
1125   {
1126     color = QColor( str );
1127     res = color.isValid();
1128   }
1129
1130   return res;
1131 }
1132
1133 /*!
1134   \brief Convert bi-color value to the string representation.
1135   
1136   Bi-color value is specified as main color and integer delta
1137   value that is used to calculate secondary color by changing
1138   paremeters of the main color ("saturation" and "value"
1139   components in HSV notation).
1140
1141   The resulting string consists of two sub-strings separated by
1142   '|' symbol. The first part represents main color
1143   (see colorToString() for more details), the second part is a
1144   delta value.
1145
1146   Backward conversion can be done with stringToBiColor() method.
1147
1148   \param color color to be converted
1149   \param delta delta value
1150   \return string representation of the bi-color value
1151
1152   \sa stringToBiColor(), stringToColor()
1153 */
1154 QString Qtx::biColorToString( const QColor& color, const int delta )
1155 {
1156   return QString("%1|%2").arg( Qtx::colorToString( color ) ).arg( delta );
1157 }
1158
1159 /*!
1160   \brief Restore bi-color value from the string representation.
1161
1162   Bi-color value is specified as main color and integer delta
1163   value that is used to calculate secondary color by changing
1164   paremeters of the main color ("saturation" and "value"
1165   components in HSV notation).
1166
1167   The parameter \a str should consist of two sub-strings separated
1168   by '|' symbol. The first part represents main color
1169   (see stringToColor() for more details), the second part is a
1170   delta value.
1171
1172   Backward conversion can be done with biColorToString() method.
1173
1174   \param str string representation of the bi-color value
1175   \param color resulting color value
1176   \param delta resulting delta value
1177   \return \c true if the conversion is successful and \c false otherwise
1178
1179   \sa biColorToString(), stringToColor(), rgbSet()
1180 */
1181 bool Qtx::stringToBiColor( const QString& str, QColor& color, int& delta )
1182 {
1183   QStringList data = str.split( "|", QString::KeepEmptyParts );
1184   QColor c;
1185   int d = 0;
1186   bool ok = data.count() > 0 && Qtx::stringToColor( data[0], c );
1187   bool dok = false;
1188   if ( data.count() > 1 ) d = data[1].toInt( &dok );
1189   ok = ok && dok;
1190   color = ok ? c : QColor();
1191   delta = ok ? d : 0;
1192   return ok;
1193 }
1194
1195 /*!
1196   \brief Compute secondary color value from specified main color
1197   and delta.
1198
1199   Secondary color is calculated by changing paremeters of the main
1200   color ("saturation" and "value" components in HSV notation) using
1201   specified delta.
1202
1203   If main color is invalid, result of the function is also invalid color.
1204
1205   \param color source main color
1206   \param delta delta value
1207   \return resulting secondary color
1208   
1209   \sa biColorToString(), stringToBiColor()
1210 */
1211 QColor Qtx::mainColorToSecondary( const QColor& color, int delta )
1212 {
1213   QColor cs = color;
1214   if ( cs.isValid() ) {
1215     int val = qMin( 255, qMax( cs.value() + delta, 0 ) );
1216     int sat = qMin( 255, qMax( cs.saturation() + delta-(val-cs.value()), 0 ) );
1217 #ifdef BICOLOR_CHANGE_HUE
1218     const int BICOLOR_HUE_MAXDELTA = 40;
1219     int dh = delta-(val-cs.value())-(sat-cs.saturation());
1220     dh = qMin( BICOLOR_HUE_MAXDELTA, qAbs( dh ) ) * ( dh > 0 ? 1 : -1 );
1221     //int hue = qMin( 359, qMax( cs.hue() + delta-(val-cs.value())-(sat-cs.saturation()), 0 ) );
1222     //int hue = qMin( 359, qMax( cs.hue() + delta-(val-cs.value())-ds, 0 ) );
1223     int hue = cs.hue() + dh;
1224     if ( hue < 0 ) hue = 360 - hue;
1225 #else
1226     int hue = cs.hue();
1227 #endif
1228     cs.setHsv( hue, sat, val );
1229   }
1230   return cs;
1231 }
1232
1233 /*!
1234   \brief Dump linear gradient to the string description.
1235   \param gradient linear gradient to be converted
1236   \return string representation of the linear gradient
1237   \sa stringToLinearGradient()
1238 */
1239 QString Qtx::gradientToString( const QLinearGradient& gradient )
1240 {
1241   QStringList data;
1242   data << "linear";
1243   data << QString::number( gradient.start().x() );
1244   data << QString::number( gradient.start().y() );
1245   data << QString::number( gradient.finalStop().x() );
1246   data << QString::number( gradient.finalStop().y() );
1247   switch( gradient.spread() ) 
1248   {
1249   case QGradient::PadSpread:
1250     data << "pad";
1251     break;
1252   case QGradient::RepeatSpread:
1253     data << "repeat";
1254     break;
1255   case QGradient::ReflectSpread:
1256     data << "reflect";
1257     break;
1258   default:
1259     break;
1260   }
1261   QGradientStops stops = gradient.stops();
1262   QGradientStop stop;
1263   foreach ( stop, stops ) 
1264   {
1265     data << QString::number( stop.first );
1266     data << colorToString( stop.second );
1267   }
1268   return data.join( "|" );
1269 }
1270
1271 /*!
1272   \brief Dump radial gradient to the string description.
1273   \param gradient radial gradient to be converted
1274   \return string representation of the radial gradient
1275   \sa stringToRadialGradient()
1276 */
1277 QString Qtx::gradientToString( const QRadialGradient& gradient )
1278 {
1279   QStringList data;
1280   data << "radial";
1281   data << QString::number( gradient.center().x() );
1282   data << QString::number( gradient.center().y() );
1283   data << QString::number( gradient.focalPoint().x() );
1284   data << QString::number( gradient.focalPoint().y() );
1285   data << QString::number( gradient.radius() );
1286   switch( gradient.spread() ) 
1287   {
1288   case QGradient::PadSpread:
1289     data << "pad";
1290     break;
1291   case QGradient::RepeatSpread:
1292     data << "repeat";
1293     break;
1294   case QGradient::ReflectSpread:
1295     data << "reflect";
1296     break;
1297   default:
1298     break;
1299   }
1300   QGradientStops stops = gradient.stops();
1301   QGradientStop stop;
1302   foreach ( stop, stops ) 
1303   {
1304     data << QString::number( stop.first );
1305     data << colorToString( stop.second );
1306   }
1307   return data.join( "|" );
1308 }
1309
1310 /*!
1311   \brief Dump conical gradient to the string description.
1312   \param gradient conical gradient to be converted
1313   \return string representation of the conical gradient
1314   \sa stringToConicalGradient()
1315 */
1316 QString Qtx::gradientToString( const QConicalGradient& gradient )
1317 {
1318   QStringList data;
1319   data << "conical";
1320   data << QString::number( gradient.center().x() );
1321   data << QString::number( gradient.center().y() );
1322   data << QString::number( gradient.angle() );
1323   switch( gradient.spread() ) 
1324   {
1325   case QGradient::PadSpread:
1326     data << "pad";
1327     break;
1328   case QGradient::RepeatSpread:
1329     data << "repeat";
1330     break;
1331   case QGradient::ReflectSpread:
1332     data << "reflect";
1333     break;
1334   default:
1335     break;
1336   }
1337   QGradientStops stops = gradient.stops();
1338   QGradientStop stop;
1339   foreach ( stop, stops ) 
1340   {
1341     data << QString::number( stop.first );
1342     data << colorToString( stop.second );
1343   }
1344   return data.join( "|" );
1345 }
1346
1347 /*!
1348   \brief Create linear gradient from its string representation.
1349   \param str string representation of the linear gradient
1350   \param gradient resulting linear gradient object
1351   \return \c true if the conversion is successful and \c false otherwise
1352   \sa gradientToString()
1353 */
1354 bool Qtx::stringToLinearGradient( const QString& str, QLinearGradient& gradient )
1355 {
1356   bool success = false;
1357   QStringList vals = str.split( "|", QString::SkipEmptyParts );
1358   if ( vals.count() > 4 && ( vals[0] == "linear" || vals[0] == "lg" ) )
1359   {
1360     // start and end points 
1361     double x1, y1, x2, y2;
1362     bool bOk1, bOk2, bOk3, bOk4;
1363     x1 = vals[1].toDouble( &bOk1 );
1364     y1 = vals[2].toDouble( &bOk2 );
1365     x2 = vals[3].toDouble( &bOk3 );
1366     y2 = vals[4].toDouble( &bOk4 );
1367     if ( bOk1 && bOk2 && bOk3 && bOk4 )
1368     {
1369       gradient = QLinearGradient( x1, y1, x2, y2 );
1370       // spread type
1371       if ( vals.count() > 5 )
1372       {
1373         QString spread = vals[ 5 ].trimmed().toLower();
1374         if ( spread == "pad" || spread == "0" )
1375           gradient.setSpread( QGradient::PadSpread );
1376         else if ( spread == "repeat" || spread == "2" )
1377           gradient.setSpread( QGradient::RepeatSpread );
1378         else if ( spread == "reflect" || spread == "1" )
1379           gradient.setSpread( QGradient::ReflectSpread );
1380       }
1381       // stop points
1382       QGradientStops stops;
1383       for ( int i = 6; i < vals.count(); i+=2 )
1384       {
1385         bool bOk5, bOk6 = false;
1386         QColor c;
1387         double stop = vals[i].toDouble( &bOk5 );
1388         if ( i+1 < vals.count() )
1389           bOk6 = stringToColor( vals[ i+1 ], c );
1390         if ( bOk5 && stop >= 0.0 && stop <= 1.0 && bOk6 && c.isValid() )
1391           stops.append( QGradientStop( stop, c ) );
1392       }
1393       gradient.setStops( stops );
1394       success = true;
1395     }
1396   }
1397   return success;
1398 }
1399
1400 /*!
1401   \brief Create radial gradient from its string representation.
1402   \param str string representation of the radial gradient
1403   \param gradient resulting radial gradient object
1404   \return \c true if the conversion is successful and \c false otherwise
1405   \sa gradientToString()
1406 */
1407 bool Qtx::stringToRadialGradient( const QString& str, QRadialGradient& gradient )
1408 {
1409   bool success = false;
1410   QStringList vals = str.split( "|", QString::SkipEmptyParts );
1411   if ( vals.count() > 5 && ( vals[0] == "radial" || vals[0] == "rg" ) )
1412   {
1413     // center, radius and focal point
1414     double cx, cy, r, fx, fy;
1415     bool bOk1, bOk2, bOk3, bOk4, bOk5;
1416     cx = vals[1].toDouble( &bOk1 );
1417     cy = vals[2].toDouble( &bOk2 );
1418     fx = vals[3].toDouble( &bOk4 );
1419     fy = vals[4].toDouble( &bOk5 );
1420     r  = vals[5].toDouble( &bOk3 );
1421     if ( bOk1 && bOk2 && bOk3 && bOk4 && bOk5 )
1422     {
1423       gradient = QRadialGradient( cx, cy, r, fx, fy );
1424       // spread type
1425       if ( vals.count() > 6 )
1426       {
1427         QString spread = vals[ 6 ].trimmed().toLower();
1428         if ( spread == "pad" || spread == "0" )
1429           gradient.setSpread( QGradient::PadSpread );
1430         else if ( spread == "repeat" || spread == "2" )
1431           gradient.setSpread( QGradient::RepeatSpread );
1432         else if ( spread == "reflect" || spread == "1" )
1433           gradient.setSpread( QGradient::ReflectSpread );
1434       }
1435       // stop points
1436       QGradientStops stops;
1437       for ( int i = 7; i < vals.count(); i+=2 )
1438       {
1439         bool bOk7, bOk8 = false;
1440         QColor c;
1441         double stop = vals[i].toDouble( &bOk7 );
1442         if ( i+1 < vals.count() )
1443           bOk8 = stringToColor( vals[ i+1 ], c );
1444         if ( bOk7 && stop >= 0.0 && stop <= 1.0 && bOk8 && c.isValid() )
1445           stops.append( QGradientStop( stop, c ) );
1446       }
1447       gradient.setStops( stops );
1448       success = true;
1449     }
1450   }
1451   return success;
1452 }
1453
1454 /*!
1455   \brief Create conical gradient from its string representation.
1456   \param str string representation of the conical gradient
1457   \param gradient resulting conical gradient object
1458   \return \c true if the conversion is successful and \c false otherwise
1459   \sa gradientToString()
1460 */
1461 bool Qtx::stringToConicalGradient( const QString& str, QConicalGradient& gradient )
1462 {
1463   bool success = false;
1464   QStringList vals = str.split( "|", QString::SkipEmptyParts );
1465   if ( vals.count() > 3 && ( vals[0] == "conical" || vals[0] == "cg" ) )
1466   {
1467     // center and angle
1468     double cx, cy, a;
1469     bool bOk1, bOk2, bOk3;
1470     cx = vals[1].toDouble( &bOk1 );
1471     cy = vals[2].toDouble( &bOk2 );
1472     a = vals[3].toDouble( &bOk3 );
1473     if ( bOk1 && bOk2 && bOk3 )
1474     {
1475       gradient = QConicalGradient( cx, cy, a );
1476       // spread type
1477       if ( vals.count() > 4 )
1478       {
1479         QString spread = vals[ 4 ].trimmed().toLower();
1480         if ( spread == "pad" || spread == "0" )
1481           gradient.setSpread( QGradient::PadSpread );
1482         else if ( spread == "repeat" || spread == "2" )
1483           gradient.setSpread( QGradient::RepeatSpread );
1484         else if ( spread == "reflect" || spread == "1" )
1485           gradient.setSpread( QGradient::ReflectSpread );
1486       }
1487       // stop points
1488       QGradientStops stops;
1489       for ( int i = 5; i < vals.count(); i+=2 )
1490       {
1491         bool bOk4, bOk5 = false;
1492         QColor c;
1493         double stop = vals[i].toDouble( &bOk4 );
1494         if ( i+1 < vals.count() )
1495           bOk5 = stringToColor( vals[ i+1 ], c );
1496         if ( bOk4 && stop >= 0.0 && stop <= 1.0 && bOk5 && c.isValid() )
1497           stops.append( QGradientStop( stop, c ) );
1498       }
1499       gradient.setStops( stops );
1500       success = true;
1501     }
1502   }
1503   return success;
1504 }
1505
1506 /*!
1507   \brief Convert background data to the string representation.
1508   The resulting string consists of several sub-strings separated by ';' symbol. 
1509   These sub-strings represent:
1510   1. background type (enumerator, see Qtx::BackgroundMode)
1511   2. texture image file name (string)
1512   3. texture mode (enumerator, see Qtx::TextureMode)
1513   4. "show texture" flag (boolean)
1514   5. first color (for simple gradient data) or solid color (for single-colored mode)
1515   6. second color (for simple gradient data)
1516   7. type of simple gradient (some integer identifier)
1517   8. complex gradient data (for custom gradient mode)
1518   Each sub-string consists of keyword/value couple, in form of "<keyword>=<value>".
1519
1520   Backward conversion can be done with stringToBackground() method.
1521
1522   \param bgData background data
1523   \return string representation of the background data
1524
1525   \sa stringToBackground()
1526 */
1527 QString Qtx::backgroundToString( const Qtx::BackgroundData& bgData )
1528 {
1529   const QString dtSep         = ";";
1530   const QString kwSep         = "=";
1531   const QString kwBgType      = "bt";
1532   const QString kwFileName    = "fn";
1533   const QString kwTextureMode = "tm";
1534   const QString kwShowTexture = "ts";
1535   const QString kwFirstColor  = "c1";
1536   const QString kwSecondColor = "c2";
1537   const QString kwGrType      = "gt";
1538   const QString kwGrData      = "gr";
1539
1540   Qtx::BackgroundMode bgMode       = bgData.mode();
1541   QString             fileName;
1542   Qtx::TextureMode    textureMode  = bgData.texture( fileName );
1543   bool                showTexture  = bgData.isTextureShown();
1544   QColor              c1, c2;
1545   int                 gradientType = bgData.gradient( c1, c2 );
1546   const QGradient*    gradient     = bgData.gradient();
1547   QString             grString;
1548   if ( gradient ) {
1549     switch ( gradient->type() ) {
1550     case QGradient::LinearGradient:
1551       grString = gradientToString( *(static_cast<const QLinearGradient*>( gradient )) );
1552       break;
1553     case QGradient::RadialGradient:
1554       grString = gradientToString( *(static_cast<const QRadialGradient*>( gradient )) );
1555       break;
1556     case QGradient::ConicalGradient:
1557       grString = gradientToString( *(static_cast<const QConicalGradient*>( gradient )) );
1558       break;
1559     default:
1560       break;
1561     }
1562   }
1563   QStringList data;
1564   data << QString( "%1%2%3" ).arg( kwBgType ).arg( kwSep ).arg( (int)bgMode );
1565   data << QString( "%1%2%3" ).arg( kwFileName ).arg( kwSep ).arg( fileName );
1566   data << QString( "%1%2%3" ).arg( kwTextureMode ).arg( kwSep ).arg( (int)textureMode );
1567   data << QString( "%1%2%3" ).arg( kwShowTexture ).arg( kwSep ).arg( showTexture ? "true" : "false" );
1568   data << QString( "%1%2%3" ).arg( kwFirstColor ).arg( kwSep ).arg( Qtx::colorToString( c1 ) );
1569   data << QString( "%1%2%3" ).arg( kwSecondColor ).arg( kwSep ).arg( Qtx::colorToString( c2 ) );
1570   data << QString( "%1%2%3" ).arg( kwGrType ).arg( kwSep ).arg( gradientType );
1571   data << QString( "%1%2%3" ).arg( kwGrData ).arg( kwSep ).arg( grString );
1572
1573   return data.join( dtSep );
1574 }
1575
1576 /*!
1577   \brief Restore background data from the string representation.
1578
1579   The string should consist of several sub-strings separated by ';' symbol. 
1580   Each sub-string consists of keyword/value couple, in form of "<keyword>=<value>".
1581   The sub-strings can follow in arbitrary order, some keywords can be missing.
1582   The background data is described by the following values:
1583   - background type (enumerator, see Qtx::BackgroundMode), keyword "bt"
1584   - texture image file name (string), keyword "fn"
1585   - texture mode (enumerator, see Qtx::TextureMode), keyword "tm"
1586   - "show texture" flag (boolean), keyword "ts"
1587   - first color (for simple gradient data) or solid color (for single-colored mode), keyword "c1"
1588   - second color (for simple gradient data), keyword "c2"
1589   - name of gradient type (string), keyword "gt"
1590   - complex gradient data (for custom gradient mode), keyword "gr"
1591
1592   Also, for backward compatibility, background data can be represented by
1593   single color value, see stringToColor().
1594
1595   Backward conversion can be done with backgroundToString() method.
1596   Returns invalid background if conversion could not be done.
1597
1598   \code
1599   Qtx::BackgroundData bgData = Qtx::stringToBackground( str );
1600   if ( bgData.isValid() ) ) doSomething( bgData );
1601   \endcode
1602
1603   \param theString string representation of the background data
1604   \return resulting background data (invalid if conversion has failed)
1605
1606   \sa backgroundToString()
1607 */
1608
1609 Qtx::BackgroundData Qtx::stringToBackground( const QString& str )
1610 {
1611   const QString dtSep         = ";";
1612   const QString kwSep         = "=";
1613   const QString kwBgType      = "bt";
1614   const QString kwFileName    = "fn";
1615   const QString kwTextureMode = "tm";
1616   const QString kwShowTexture = "ts";
1617   const QString kwFirstColor  = "c1";
1618   const QString kwSecondColor = "c2";
1619   const QString kwGrType      = "gt";
1620   const QString kwGrData      = "gr";
1621
1622   Qtx::BackgroundData bgData = BackgroundData();
1623
1624   QStringList data = str.split( dtSep, QString::KeepEmptyParts );
1625   
1626   QColor c;
1627   if ( data.count() == 1 && !data.contains( kwSep ) && stringToColor( data[0], c ) ) {
1628     // solid color mode, for backward compatibility
1629     bgData.setColor( c );
1630   }
1631   else {
1632     QMap<QString, QString> dmap;
1633     // background data
1634     foreach( QString d, data ) {
1635       QStringList items = d.split( kwSep, QString::KeepEmptyParts );
1636       if ( items.count() > 0 ) {
1637         QString kw  = items.takeFirst().trimmed().toLower(); // keyword
1638         QString val = items.join( kwSep ).trimmed(); // if value contains "=" symbol, we have to restore it
1639         dmap[ kw ] = val;
1640       }
1641     }
1642     QString bgMode       = dmap.value( kwBgType,      QString() );
1643     QString fileName     = dmap.value( kwFileName,    QString() );
1644     QString textureMode  = dmap.value( kwTextureMode, QString() );
1645     QString showTexture  = dmap.value( kwShowTexture, QString() );
1646     QString color1       = dmap.value( kwFirstColor,  QString() );
1647     QString color2       = dmap.value( kwSecondColor, QString() );
1648     QString gradientType = dmap.value( kwGrType,      QString() );
1649     QString gradient     = dmap.value( kwGrData,      QString() );
1650     
1651     // texture data
1652     if ( !fileName.isEmpty() || !textureMode.isEmpty() || !showTexture.isEmpty() ) {
1653       Qtx::TextureMode m = (Qtx::TextureMode)( stringToInt( textureMode,        Qtx::CenterTexture, 
1654                                                             Qtx::CenterTexture, Qtx::StretchTexture ) );
1655       bgData.setTexture( fileName, m );
1656       QStringList boolvars; boolvars << "true" << "yes" << "ok" << "1";
1657       if ( boolvars.contains( showTexture.trimmed().toLower() ) )
1658         bgData.setTextureShown( true );
1659     }
1660     QColor c1, c2;
1661     // try color mode
1662     bool ok = Qtx::stringToColor( color1, c1 );
1663     if ( ok ) {
1664       bgData.setColor( c1 );
1665     }
1666     // try simple gradient mode
1667     ok = Qtx::stringToColor( color2, c2 );
1668     if ( ok || !gradientType.isEmpty() ) {
1669       int gt = gradientType.toInt( &ok );
1670       bgData.setGradient( ok ? gt : -1, c1, c2 );
1671     }
1672     // try custom gradient mode
1673     QLinearGradient  lg;
1674     QConicalGradient cg;
1675     QRadialGradient  rg;
1676     ok = Qtx::stringToLinearGradient( gradient, lg );
1677     if ( ok ) {
1678       bgData.setGradient( lg );
1679     }
1680     ok = Qtx::stringToRadialGradient( gradient, rg );
1681     if ( ok ) {
1682       bgData.setGradient( rg );
1683     }
1684     ok = Qtx::stringToConicalGradient( gradient, cg );
1685     if ( ok ) {
1686       bgData.setGradient( cg );
1687     }
1688     
1689     // finally set background mode
1690     Qtx::BackgroundMode m = (Qtx::BackgroundMode)( stringToInt( bgMode,            Qtx::ColorBackground, 
1691                                                                 Qtx::NoBackground, Qtx::CustomGradientBackground ) );
1692     bgData.setMode( m );
1693   }
1694
1695   return bgData;
1696 }
1697
1698 /*!
1699   \class Qtx::Localizer
1700   \brief Localization helper
1701
1702   This helper class can be used to solve the localization problems,
1703   usually related to the textual files reading/writing, namely when
1704   floating point values are read / written with API functions.
1705   The problem relates to such locale specific settings as decimal point
1706   separator, thousands separator, etc.
1707   
1708   To use the Localizer class, just create a local variable in the beginning
1709   of the code where you need to read / write data from textual file(s).
1710   The constructor of the class forces setting "C" locale temporariy.
1711   The destructor switches back to the initial locale.
1712
1713   \code
1714   Qtx::Localizer loc;
1715   readSomething();
1716   writeSomething();
1717   \endcode
1718 */
1719
1720 /*!
1721   \brief Constructor. Forces "C" locale to be set.
1722 */
1723 Qtx::Localizer::Localizer()
1724 {
1725   myCurLocale = setlocale( LC_NUMERIC, 0 );
1726   setlocale( LC_NUMERIC, "C" );
1727 }
1728
1729 /*!
1730   \brief Destructor. Reverts back to the initial locale.
1731 */
1732 Qtx::Localizer::~Localizer()
1733 {
1734   setlocale( LC_NUMERIC, myCurLocale.toLatin1().constData() );
1735 }
1736
1737 /*!
1738   \class Qtx::BackgroundData
1739   \brief Stores background data
1740
1741   This class is used to store background data. Depending on the mode,
1742   the background can be specified by:
1743   - image (by assigning the file name to be used as background texture), see setTexture(), setTextureShown()
1744   - single color (by assigning any color), see setColor()
1745   - simple two-color gradient (with the gradient type id and two colors), see setGradient( int, const QColor&, const QColor& )
1746   - complex gradient (by assigning arbitrary gradient data), see setGradient( const QGradient& )
1747
1748   The class stores all the data passed to it, so switching between different modes can be done
1749   just by calling setMode() function.
1750
1751   \note Texture is used with combination of the background mode.
1752
1753   \note Two-color gradient is specified by two colors and integer identifier. The interpretation of 
1754   this identifier should be done in the calling code.
1755
1756   \code
1757   Qtx::BackgroundData bg;
1758   bg.setColor( QColor(100, 100, 100) );                  // bg is switched to Qtx::ColorBackground mode
1759   bg.setGradient( Qt::Horizontal, Qt::gray, Qt::white ); // bg is switched to Qtx::ColorBackground mode
1760   QLinearGradient grad( 0,0,1,1 ); 
1761   grad.setColorAt( 0.0, Qt::gray ); 
1762   grad.setColorAt( 0.5, Qt::white );
1763   grad.setColorAt( 1.0, Qt::green );
1764   grad.setSpread( QGradient::PadSpread );
1765   bg.setGradient( grad );                                // bg is switched to Qtx::CustomGradientBackground mode
1766   bg.setMode( Qtx::ColorBackground );                    // bg is switched back to Qtx::ColorBackground mode
1767   bg.setTexture( "/data/images/background.png" );        // specify texture (in the centered mode by default)
1768   bg.setTextureShown( true );                            // draw texture on the solid color background
1769   \endcode
1770 */
1771
1772 /*!
1773   \brief Default constructor.
1774   Creates invalid background data.
1775 */
1776 Qtx::BackgroundData::BackgroundData()
1777   : myTextureMode( Qtx::CenterTexture ), myGradientType( -1 ), myTextureShown( false )
1778 {
1779   setMode( Qtx::NoBackground );
1780 }
1781
1782 /*!
1783   \brief Constructor.
1784   Creates background data initialized with the specified color
1785   \param c color
1786 */
1787 Qtx::BackgroundData::BackgroundData( const QColor& c )
1788   : myTextureMode( Qtx::CenterTexture ), myGradientType( -1 ), myTextureShown( false )
1789 {
1790   setColor( c );
1791 }
1792
1793 /*!
1794   \brief Constructor.
1795   Creates background data initialized with the specified two-color gradient
1796   \param type gradient type identifier
1797   \param c1 first gradient color
1798   \param c2 second gradient color
1799   \note the interpretation of the gradient identifier should be done in the calling code
1800 */
1801 Qtx::BackgroundData::BackgroundData( int type, const QColor& c1, const QColor& c2 )
1802   : myTextureMode( Qtx::CenterTexture ), myGradientType( -1 ), myTextureShown( false )
1803 {
1804   setGradient( type, c1, c2 );
1805 }
1806
1807 /*!
1808   \brief Constructor.
1809   Creates background data initialized with the arbirtary gradient data
1810   \param grad gradient data
1811 */
1812 Qtx::BackgroundData::BackgroundData( const QGradient& grad )
1813   : myTextureMode( Qtx::CenterTexture ), myGradientType( -1 ), myTextureShown( false )
1814 {
1815   setGradient( grad );
1816 }
1817
1818 /*!
1819   \brief Destructor.
1820 */
1821 Qtx::BackgroundData::~BackgroundData()
1822 {
1823 }
1824
1825 /*!
1826   \brief Compares two background data objects
1827 */
1828 bool Qtx::BackgroundData::operator==( const Qtx::BackgroundData& other ) const
1829 {
1830   return 
1831     ( myMode         == other.myMode )         && 
1832     ( myTextureMode  == other.myTextureMode )  &&
1833     ( myFileName     == other.myFileName )     &&
1834     ( myColors       == other.myColors )       &&
1835     ( myGradientType == other.myGradientType ) &&
1836     ( myGradient     == other.myGradient )     &&
1837     ( myTextureShown == other.myTextureShown );
1838 }
1839
1840 /*!
1841   \brief Returns \c false if background data is not set (invalid)
1842   \return \c true if background data is valid or \c false otherwise
1843   \sa mode()
1844 */
1845 bool Qtx::BackgroundData::isValid() const
1846 {
1847   return myMode != Qtx::NoBackground;
1848 }
1849
1850 /*!
1851   \brief Get background mode
1852   \return current background mode
1853   \sa setMode()
1854 */
1855 Qtx::BackgroundMode Qtx::BackgroundData::mode() const
1856 {
1857   return myMode;
1858 }
1859
1860 /*!
1861   \brief Set background mode
1862   \param m background mode being set
1863   \sa mode()
1864 */
1865 void Qtx::BackgroundData::setMode( const Qtx::BackgroundMode m )
1866 {
1867   myMode = m;
1868 }
1869
1870 /*!
1871   \brief Get file name used as a texture image
1872   \return path to the texture image file
1873   \sa setTexture(), setTextureShown()
1874 */
1875 Qtx::TextureMode Qtx::BackgroundData::texture( QString& fileName ) const
1876 {
1877   fileName = myFileName;
1878   return myTextureMode;
1879 }
1880
1881 /*!
1882   \brief Set file name to be used as a texture image.
1883
1884   \note To show texture image on the background it is necessary to call additionally
1885   setTextureShown() method.
1886
1887   \param fileName path to the texture image file name
1888   \param m texture mode (Qtx::CenterTexture by default)
1889   \sa texture(), setTextureShown()
1890 */
1891 void Qtx::BackgroundData::setTexture( const QString& fileName, const Qtx::TextureMode m )
1892 {
1893   myFileName = fileName;
1894   myTextureMode = m;
1895 }
1896
1897 /*!
1898   \brief Check if "show texture" flag is switched on
1899   \return \c true if "show texture" flag is set or \c false otherwise
1900   \sa setTextureShown(), texture()
1901 */
1902 bool Qtx::BackgroundData::isTextureShown() const
1903 {
1904   return myTextureShown;
1905 }
1906
1907 /*!
1908   \brief Specify if texture should be shown on the background or no.
1909   \param on \c true if texture should be shown or \c false otherwise
1910   \sa isTextureShown(), texture()
1911 */
1912 void Qtx::BackgroundData::setTextureShown( bool on )
1913 {
1914   myTextureShown = on;
1915 }
1916
1917 /*!
1918   \brief Get background color. Returns null QColor if color is not set
1919   \return solid background color
1920   \sa setColor(), mode()
1921 */
1922 QColor Qtx::BackgroundData::color() const
1923 {
1924   return myColors.count() > 0 ? myColors[0] : QColor();
1925 }
1926
1927 /*!
1928   \brief Set background color and switch to the Qtx::ColorBackground mode
1929   \param c color
1930   \sa color(), mode()
1931 */
1932 void Qtx::BackgroundData::setColor( const QColor& c )
1933 {
1934   myColors.clear();
1935   myColors << c;
1936   setMode( Qtx::ColorBackground );
1937 }
1938
1939 /*!
1940   \brief Get simple gradient data.
1941   Returns -1 and null QColor for \a c1 and \a c2 if gradient data is not set
1942   \param c1 first gradient color is returned via this parameter
1943   \param c2 second gradient color is returned via this parameter
1944   \return current two-colored gradient mode type identifier
1945   \note the interpretation of the gradient identifier should be done in the calling code
1946   \sa setGradient(int, const QColor&, const QColor&), mode()
1947 */
1948 int Qtx::BackgroundData::gradient( QColor& c1, QColor& c2 ) const
1949 {
1950   c1 = myColors.count() > 0 ? myColors[0] : QColor();
1951   c2 = myColors.count() > 1 ? myColors[1] : ( myColors.count() > 0 ? myColors[0] : QColor() );
1952   return myGradientType;
1953 }
1954
1955 /*!
1956   \brief Set simple background gradient data and switch to the Qtx::SimpleGradientBackground mode
1957   \param type two-colored gradient mode type identifier
1958   \param c1 first gradient color is returned via this parameter
1959   \param c2 second gradient color is returned via this parameter
1960   \note the interpretation of the gradient identifier should be done in the calling code
1961   \sa gradient(QColor&, QColor&), mode()
1962 */
1963 void Qtx::BackgroundData::setGradient( int type, const QColor& c1, const QColor& c2 )
1964 {
1965   myColors.clear();
1966   myColors << c1 << c2;
1967   myGradientType = type;
1968   setMode( Qtx::SimpleGradientBackground );
1969 }
1970
1971 /*!
1972   \brief Get complex gradient data.
1973   Returns QGradient of QGradient::NoGradient if gradient data is not set
1974   \note This function does not transform simple gradient data set with 
1975   setGradient( const QString&, const QColor&, const QColor& ) to QGradient class
1976   \return gradient data
1977   \sa setGradient(const QGradient&), mode()
1978 */
1979 const QGradient* Qtx::BackgroundData::gradient() const
1980 {
1981   return &myGradient;
1982 }
1983
1984 /*!
1985   \brief Set complex background gradient data and switch to the Qtx::CustomGradientBackground mode
1986   \param grad gradient data (QLinearGradient, QRadialGradient or QConicalGradient)
1987   \sa gradient(), mode()
1988 */
1989 void Qtx::BackgroundData::setGradient( const QGradient& grad )
1990 {
1991   myGradient = grad;
1992   setMode( Qtx::CustomGradientBackground );
1993 }
1994
1995 /*!
1996   \brief Convert string representation of version identifier to the numerical value.
1997   Resulting value can be used for comparison of different versions (lower, higher, equal).
1998
1999   String representation of the version consists of zero or more components:
2000
2001   [major[.minor[.release[patchid]]]]
2002  
2003   where
2004   - major is version major number
2005   - minor is version minor number
2006   - release is version release number
2007   - patchid is a version dev identifier which is one of the following
2008     * 1 letter optionally followed by 1 or 2 digits, e.g. "a" for "alpha", "b1" for "beta 1"
2009     * "rc" optionally followed by 1 or 2 digits, e.g. "rc1" for "release candidate 1"
2010     * "dev" for development version (note: 7.4.0dev > 7.4.0, 7.4.0dev < 7.4.1, 7.4.0dev < 7.4.0a1)
2011
2012   If version string does not include any component or has invalid format, the function returns 0.
2013
2014   Examples:
2015     1.0      - version 1.0
2016     1.2.3a   - version 1.2.3 alpha
2017     3.3.3b1  - version 3.3.3 beta 1
2018     7.4.0rc1 - version 7.4.0 release candidate 1
2019     7.4.0dev - dev version, i.e. future version 7.4.1 (or 7.5.0)
2020
2021   \param version string representation of version
2022   \return numerical identifier of the version
2023 */
2024 long Qtx::versionToId( const QString& version )
2025 {
2026   long id = 0;
2027
2028   QRegExp vers_exp( "^([0-9]+)([A-Z]|RC|DEV)?([0-9]{0,2})$", Qt::CaseInsensitive );
2029   
2030   QStringList vers = version.split( ".", QString::SkipEmptyParts );
2031   int major=0, minor=0;
2032   int release = 0, dev1 = 0, dev2 = 0;
2033   if ( vers.count() > 0 ) major = vers[0].toInt();
2034   if ( vers.count() > 1 ) minor = vers[1].toInt();
2035   if ( vers.count() > 2 ) {
2036     if ( vers_exp.indexIn( vers[2] ) != -1 ) {
2037       release = vers_exp.cap( 1 ).toInt();
2038       QString tag = vers_exp.cap( 2 ).toLower();
2039       if ( !tag.isEmpty() ) {
2040         // patchid is subtracted from version number
2041         // a   = 55 --> -(55 * 100) + patch number --> 4500..4599, e.g. 7.4.1a1  -> 704004501
2042         // b   = 54 --> -(54 * 100) + patch number --> 4600..4699, e.g. 7.4.1b1  -> 704004601
2043         // c   = 53 --> -(53 * 100) + patch number --> 4700..4799, e.g. 7.4.1c1  -> 704004701
2044         // ...
2045         // z   = 30 --> -( 1 * 100) + patch number --> 7000..7099, e.g. 7.4.1z1  -> 704007001
2046         // rc  =  1 --> -( 1 * 100) + patch number --> 9900..9999, e.g. 7.4.1rc1 -> 704009901
2047         // dev = -1 --> +( 1 * 100) + patch number --> 0100..0199, e.g. 7.4.1dev -> 704010100
2048         // ---
2049         // i.e. "a" < "b" < ... < "z" < "rc" < [stable] < "dev"
2050         if ( tag == "rc" )
2051           dev1 = 1;
2052         else if ( tag == "dev" )
2053           dev1 = -1;
2054         else
2055           dev1 = (int)( QChar('z').toLatin1() ) - (int)( tag[ 0 ].toLatin1() ) + 30;
2056       }
2057       if ( !vers_exp.cap( 3 ).isEmpty() )
2058         dev2 = vers_exp.cap( 3 ).toInt();
2059     }
2060   }
2061   
2062   int dev = dev1*100-dev2;
2063   id = major;
2064   id*=100; id+=minor;
2065   id*=100; id+=release;
2066   id*=10000;
2067   id-=dev;
2068
2069   return id;
2070 }
2071
2072 #ifndef WIN32
2073
2074 #include <X11/Xlib.h>
2075 #include <GL/glx.h>
2076
2077 /*!
2078   \brief Open the default X display and returns pointer to it.
2079          This method is available on Linux only.
2080   \return Pointer to X display.
2081   \sa getVisual()
2082 */
2083 void* Qtx::getDisplay()
2084 {
2085   static Display* pDisplay = NULL;
2086   if ( !pDisplay )
2087     pDisplay = XOpenDisplay( NULL );
2088   return pDisplay;
2089 }
2090
2091 /*!
2092   \brief Returns pointer to X visual suitable for 3D rendering.
2093          This method is available on Linux only.
2094   \return Pointer to X visual.
2095   \sa getDisplay()
2096 */
2097 Qt::HANDLE Qtx::getVisual()
2098 {
2099   Qt::HANDLE res = (Qt::HANDLE)NULL;
2100
2101   Display* pDisplay = (Display*)getDisplay();
2102   if ( !pDisplay )
2103     return res;
2104
2105   int errorBase;
2106   int eventBase;
2107
2108   // Make sure OpenGL's GLX extension supported
2109   if( !glXQueryExtension( pDisplay, &errorBase, &eventBase ) ){
2110     qCritical( "Could not find glx extension" );
2111     return res;
2112   }
2113
2114   // Find an appropriate visual
2115
2116   int doubleBufferVisual[]  = {
2117     GLX_RGBA,           // Needs to support OpenGL
2118     GLX_DEPTH_SIZE, 16, // Needs to support a 16 bit depth buffer
2119     GLX_DOUBLEBUFFER,   // Needs to support double-buffering
2120     GLX_STENCIL_SIZE, 1,
2121     None                // end of list
2122   };
2123
2124   // Try for the double-bufferd visual first
2125   XVisualInfo *visualInfo = NULL;
2126   visualInfo = glXChooseVisual( pDisplay, DefaultScreen(pDisplay), doubleBufferVisual );
2127
2128   if( visualInfo == NULL ){
2129     qCritical( "Could not find matching glx visual" );
2130     return res;
2131   }
2132
2133   qDebug() << "Picked visual 0x" << hex << XVisualIDFromVisual( visualInfo->visual );
2134   res = (Qt::HANDLE)( visualInfo->visual );
2135  
2136   return res;
2137 }
2138 #endif // WIN32