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