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