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