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