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