Salome HOME
Update from BR_V5_DEV 13Feb2009
[modules/gui.git] / src / Qtx / Qtx.cxx
1 //  Copyright (C) 2007-2008  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.
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 // File:      Qtx.cxx
23 // Author:    Sergey TELKOV
24 //
25 #include "Qtx.h"
26
27 #include <QDir>
28 #include <QMenu>
29 #include <QRegExp>
30 #include <QBitmap>
31 #include <QWidget>
32 #include <QLayout>
33 #include <QPainter>
34 #include <QDirModel>
35 #include <QFileInfo>
36 #include <QCompleter>
37 #include <QApplication>
38 #include <QDesktopWidget>
39 #include <QLinearGradient>
40 #include <QRadialGradient>
41 #include <QConicalGradient>
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46
47 /*!
48   \class Qtx
49   \brief A set of helpful utility functions.
50
51   The class implements a set of the static functions which can be used
52   for the different purposes:
53   - specify tab order for the set of widgets: setTabOrder()
54   - align one widget to the coordinates of the another one: alignWidget()
55   - remove extra separators from the menu or toolbar: simplifySeparators()
56   - retrieve directory, file name and extension parts of the path:
57   dir(), file(), extension()
58   - get the path to the temporary directory: tmpDir()
59   - create or remove a directory (recursively): mkDir(), rmDir()
60   - convert text file from DOS to UNIX native format: dos2unix()
61   - convert a picture to the gray scale: grayscale()
62   - other
63 */
64
65 /*!
66   \brief Convert character array (ASCII string) to the QString.
67   \param str character array
68   \param len array length, if < 0, the array should be zero-terminated
69   \return QString object
70  */
71 QString Qtx::toQString( const char* str, const int len )
72 {
73   return toQString( (unsigned char*)str, len );
74 }
75
76 /*!
77   \brief Convert integer array (UNICODE string) to the QString.
78   \param str integer array
79   \param len array length, if < 0, the array should be zero-terminated
80   \return QString object
81  */
82 QString Qtx::toQString( const short* str, const int len )
83 {
84   return toQString( (unsigned short*)str, len );
85 }
86
87 /*!
88   \brief Convert character array (ASCII string) to the QString.
89   \param str character array
90   \param len array length, if < 0, the array should be zero-terminated
91   \return QString object
92  */
93 QString Qtx::toQString( const unsigned char* str, const int len )
94 {
95   QString res;
96   const unsigned char* s = str;
97   while ( len < 0 || res.length() < len )
98   {
99     if ( *s == '\0' )
100       break;
101
102     res.append( QChar( *s ) );
103     s++;
104   }
105   return res;
106 }
107
108 /*!
109   \brief Convert integer array (UNICODE string) to the QString.
110   \param str integer array
111   \param len array length, if < 0, the array should be zero-terminated
112   \return QString object
113  */
114 QString Qtx::toQString( const unsigned short* str, const int len )
115 {
116   QString res;
117   const unsigned short* s = str;
118   while ( len < 0 || res.length() < len )
119   {
120     if ( *s == '\0' )
121       break;
122
123     res.append( QChar( *s ) );
124     s++;
125   }
126   return res;
127 }
128
129 /*!
130   \brief Set tab order for specified list of widgets.
131
132   The function has arbitrary number of parameters, each should be
133   hovewer of QWidget* type. Last parameter should be null pointer.
134
135   \param first first widget in the sequence
136 */
137 void Qtx::setTabOrder( QWidget* first, ... )
138 {
139   va_list wids;
140   va_start( wids, first );
141
142   QWidgetList widList;
143
144   QWidget* cur = first;
145   while ( cur )
146   {
147     widList.append( cur );
148     cur = va_arg( wids, QWidget* );
149   }
150
151   setTabOrder( widList );
152 }
153
154 /*!
155   \brief Set tab order for specified list of widgets.
156   \param widgets list of widgets
157 */
158 void Qtx::setTabOrder( const QWidgetList& widgets )
159 {
160   if ( widgets.count() < 2 )
161     return;
162
163   QWidget* prev = 0;
164   for ( QWidgetList::const_iterator it = widgets.begin(); it!= widgets.end(); ++it )
165   {
166     QWidget* next = *it;
167     if ( prev && next )
168       QWidget::setTabOrder( prev, next );
169     prev = next;
170   }
171 }
172
173 /*!
174   \brief Align widget \a src relative to widget \a ref acording to the 
175          alignment flags \a alignFlags.
176   \param src source widget (being aligned)
177   \param ref reference widget (source widget being aligned to)
178   \param alignFlags alignment flags (Qtx::AlignmentFlags)
179 */
180 void Qtx::alignWidget( QWidget* src, const QWidget* ref, const int alignFlags )
181 {
182   if ( !src || !ref || !alignFlags )
183     return;
184
185   QPoint srcOri = src->pos();
186   QPoint refOri = ref->pos();
187   if ( src->parentWidget() && !src->isTopLevel() )
188     srcOri = src->parentWidget()->mapToGlobal( srcOri );
189   if ( ref->parentWidget() && !ref->isTopLevel() )
190     refOri = ref->parentWidget()->mapToGlobal( refOri );
191
192   int x = srcOri.x(), y = srcOri.y();
193   int refWidth = ref->frameGeometry().width(), refHei = ref->frameGeometry().height();
194   int srcWidth = src->frameGeometry().width(), srcHei = src->frameGeometry().height();
195
196   if ( srcWidth <= 0 )
197     srcWidth = src->sizeHint().width();
198   if ( srcHei <= 0 )
199     srcHei = src->sizeHint().height();
200
201   int border = 0;
202   if ( ref->isTopLevel() && ref->isMaximized() &&
203        src->isTopLevel() && !src->isMaximized() )
204     border = ( src->frameGeometry().width() - src->width() ) / 2;
205
206   if ( alignFlags & Qtx::AlignLeft )
207     x = refOri.x() + border;
208   if ( alignFlags & Qtx::AlignOutLeft )
209     x = refOri.x() - srcWidth - border;
210   if ( alignFlags & Qtx::AlignRight )
211     x = refOri.x() + refWidth - srcWidth - border;
212   if ( alignFlags & Qtx::AlignOutRight )
213     x = refOri.x() + refWidth + border;
214   if ( alignFlags & Qtx::AlignTop )
215     y = refOri.y() + border;
216   if ( alignFlags & Qtx::AlignOutTop )
217     y = refOri.y() - srcHei - border;
218   if ( alignFlags & Qtx::AlignBottom )
219     y = refOri.y() + refHei - srcHei - border;
220   if ( alignFlags & Qtx::AlignOutBottom )
221     y = refOri.y() + refHei + border;
222   if ( alignFlags & Qtx::AlignHCenter )
223     x = refOri.x() + ( refWidth - srcWidth ) / 2;
224   if ( alignFlags & Qtx::AlignVCenter )
225     y = refOri.y() + ( refHei - srcHei ) / 2;
226
227   if ( src->parentWidget() && !src->isTopLevel() )
228   {
229     QPoint pos = src->parentWidget()->mapFromGlobal( QPoint( x, y ) );
230     x = pos.x();
231     y = pos.y();
232   }
233
234   QWidget* desk = QApplication::desktop();
235   if ( desk && x + srcWidth + border > desk->width() )
236     x = desk->width() - srcWidth - border;
237   if ( desk && y + srcHei + border > desk->height() )
238     y = desk->height() - srcHei - border;
239
240   x = qMax( x, 0 );
241   y = qMax( y, 0 );
242
243   src->move( x, y );
244 }
245
246 /*!
247   \brief Remove (recursively) unnecessary separators from the menu or toolbar.
248   \param wid widget, should be of QMenu* or QToolBar* class
249 */
250 void Qtx::simplifySeparators( QWidget* wid, const bool recursive )
251 {
252   if ( !wid )
253     return;
254
255   QList<QAction*> items = wid->actions();
256   if ( items.isEmpty() )
257     return;
258
259   QList<QAction*> toRemove;
260   for ( int i = 1; i < items.count(); i++ )
261   {
262     if ( items[i]->isSeparator() && items[i - 1]->isSeparator() )
263       toRemove.append( items[i] );
264
265     if ( recursive && items[i]->menu() )
266       simplifySeparators( items[i]->menu(), recursive );
267   }
268
269   for ( QList<QAction*>::iterator it = toRemove.begin(); it != toRemove.end(); ++it )
270     wid->removeAction( *it );
271
272   items = wid->actions();
273   if ( !items.isEmpty() && items[0]->isSeparator() )
274     wid->removeAction( items[0] );
275
276   items = wid->actions();
277   if ( !items.isEmpty() && items[items.count() - 1]->isSeparator() )
278     wid->removeAction( items[items.count() - 1] );
279 }
280
281 /*!
282   \brief Return \c true if specified \a parent is a parent object
283          of given \a child (in terms of QObject).
284
285   This function works recursively. It means that \a true is also
286   returned if \a parent is a grand-father, grand-grand-father, etc
287   of \a child. If the same object is given as both \a parent and
288   \a child, \c true is also returned.
289
290   \param child child object
291   \param parent parent object
292   \return \c true if the \a parent is a parent of \a child
293 */
294 bool Qtx::isParent( QObject* child, QObject* parent )
295 {
296   if ( !child || !parent )
297     return false;
298
299   bool res = false;
300   QObject* obj = child;
301   while ( !res && obj )
302   {
303     res = obj == parent;
304     obj = obj->parent();
305   }
306   return res;
307 }
308
309 /*!
310   \brief Find the parent object of class specified by \a className (in terms of QObject).
311
312   \param obj current object
313   \param className class name of the parent
314   \return parent object or null pointer if the parent not found
315 */
316 QObject* Qtx::findParent( QObject* obj, const char* className )
317 {
318   if ( !obj )
319     return 0;
320
321   if ( !className || !strlen( className ) )
322     return obj->parent();
323
324   QObject* res = 0;
325   QObject* p = obj->parent();
326   while ( p && !res )
327   {
328     if ( p->inherits( className ) )
329       res = p;
330     p = p->parent();
331   }
332
333   return res;
334 }
335
336 /*!
337   \brief Return directory part of the file path.
338
339   If the file path does not include directory part (the file is in the
340   current directory), null string is returned.
341
342   \param path file path
343   \param abs if true (default) \a path parameter is treated as absolute file path
344   \return directory part of the file path
345 */
346 QString Qtx::dir( const QString& path, const bool abs )
347 {
348   QDir aDir = QFileInfo( path ).dir();
349   QString dirPath = abs ? aDir.absolutePath() : aDir.path();
350   if ( dirPath == QString( "." ) )
351     dirPath = QString();
352   return dirPath;
353 }
354
355 /*!
356   \brief Return file name part of the file path.
357
358   \param path file path
359   \param withExt if true (default) complete file name (with all
360          extension except the last) is returned, otherwise only base name
361          is returned
362   \return file name part of the file path
363 */
364 QString Qtx::file( const QString& path, bool withExt )
365 {
366   QString fPath = path;
367   while ( !fPath.isEmpty() && ( fPath[fPath.length() - 1] == '\\' || fPath[fPath.length() - 1] == '/' ) )
368     fPath.remove( fPath.length() - 1, 1 );
369
370   if ( withExt )
371     return QFileInfo( fPath ).fileName();
372   else
373     return QFileInfo( fPath ).baseName();
374 }
375
376 /*!
377   \brief Return extension part of the file path.
378
379   \param path file path
380   \param full if true complete extension (all extensions, dot separated)
381          is returned, otherwise (default) only last extension is returned
382   \return extension part of the file path
383 */
384 QString Qtx::extension( const QString& path, const bool full )
385 {
386   return full ? QFileInfo( path ).completeSuffix() : QFileInfo( path ).suffix();
387 }
388
389 /*!
390   \brief Convert the given parameter to the platform-specific library name.
391
392   The function appends platform-specific prefix (lib) and suffix (.dll/.so)
393   to the library file name.
394   For example, if \a str = "mylib", "libmylib.so" is returned for Linux and
395   mylib.dll for Windows.
396
397   \param str short library name
398   \return full library name
399 */
400 QString Qtx::library( const QString& str )
401 {
402   QString path = dir( str, false );
403   QString name = file( str, false );
404   QString ext  = extension( str );
405
406 #ifndef WIN32
407   if ( !name.startsWith( "lib" ) )
408     name = QString( "lib" ) + name;
409 #endif
410
411 #ifdef WIN32
412   QString libExt( "dll" );
413 #else
414   QString libExt( "so" );
415 #endif
416
417   if ( ext.toLower() != QString( "so" ) && ext.toLower() != QString( "dll" ) )
418   {
419     if ( !name.isEmpty() && !ext.isEmpty() )
420       name += QString( "." );
421     name += ext;
422   }
423
424   ext = libExt;
425
426   QString fileName = addSlash( path ) + name + QString( "." ) + ext;
427
428   return fileName;
429 }
430
431 /*!
432   \brief Get the temporary directory name.
433   \return temporary directory (platform specific)
434 */
435 QString Qtx::tmpDir()
436 {
437   char* tmpdir = ::getenv( "TEMP" );
438   if ( !tmpdir )
439     tmpdir = ::getenv ( "TMP" );
440   if ( !tmpdir )
441   {
442 #ifdef WIN32
443     tmpdir = "C:\\";
444 #else
445     tmpdir = "/tmp";
446 #endif
447   }
448   return QString( tmpdir );
449 }
450
451 /*!
452   \brief Create directory recursively including all intermediate sub directories.
453   \return \c true if the directory is successfully created and \c false otherwise
454 */
455 bool Qtx::mkDir( const QString& dirPath )
456 {
457   return QDir().mkpath( dirPath );
458 }
459
460 /*!
461   \brief Remove directory recursively including all subdirectories and files.
462   \return \c true if the directory is successfully removed and \c false otherwise
463 */
464 bool Qtx::rmDir( const QString& thePath )
465 {
466   QFileInfo fi( thePath );
467   if ( !fi.exists() )
468     return true;
469
470   bool stat = true;
471   if ( fi.isFile() )
472     stat = QFile::remove( thePath );
473   else if ( fi.isDir() )
474   {
475     QDir aDir( thePath );
476     QFileInfoList anEntries = aDir.entryInfoList();
477     for ( QFileInfoList::iterator it = anEntries.begin(); it != anEntries.end(); ++it )
478     {
479       QFileInfo inf = *it;
480       if ( inf.fileName() == "." || inf.fileName() == ".." )
481         continue;
482       stat = stat && rmDir( inf.absoluteFilePath() );
483     }
484     stat = stat && aDir.rmdir( thePath );
485   }
486   return stat;
487 }
488
489 /*!
490   \brief Add a slash (platform-specific) to the end of \a path
491          if it is not already there.
492   \param path directory path
493   \return modified path (with slash added to the end)
494 */
495 QString Qtx::addSlash( const QString& path )
496 {
497   QString res = path;
498   if ( !res.isEmpty() && res.at( res.length() - 1 ) != QChar( '/' ) &&
499        res.at( res.length() - 1 ) != QChar( '\\' ) )
500   res += QDir::separator();
501   return res;
502 }
503
504 /*!
505   \brief Convert text file from DOS format to UNIX.
506
507   The function replaces "LF/CR" symbols sequence by "LF" symbol.
508
509   \param absName file name
510   \return \c true if the file is converted successfully and \c false in
511          case of any error
512 */
513 bool Qtx::dos2unix( const QString& absName )
514 {
515   FILE* src = ::fopen( absName.toLatin1(), "rb" );
516   if ( !src )
517     return false;
518
519   /* we'll use temporary file */
520   char temp[512] = { '\0' };
521   QString dir = Qtx::dir( absName );
522   FILE* tgt = ::fopen( strcpy( temp, ::tempnam( dir.toLatin1(), "__x" ) ), "wb" );
523   if ( !tgt )
524     return false;
525
526   /* temp -> result of conversion */
527   const char CR = 0x0d;
528   const char LF = 0x0a;
529   bool waitingLF = false;
530
531   while( true )
532   {
533     int  outcnt = 0;
534     char inbuf[512], outbuf[512];
535
536     /* convert buffer */
537     int nbread = ::fread( inbuf, 1, sizeof( inbuf ), src );
538     for ( int incnt = 0; incnt < nbread; incnt++  )
539     {
540       if ( waitingLF )
541       {
542         waitingLF = false;
543         if ( inbuf[incnt] == LF )
544           outbuf[outcnt++] = LF;
545         else
546           outbuf[outcnt++] = CR;
547       }
548       else if ( inbuf[incnt] == CR )
549         waitingLF = true;
550       else
551         outbuf[outcnt++] = inbuf[incnt];
552     }
553
554     /* check last sym in buffer */
555     waitingLF = ( inbuf[nbread - 1] == CR );
556
557     /* write converted buffer to temp file */
558     int nbwri = ::fwrite( outbuf, 1, outcnt, tgt );
559     if ( nbwri != outcnt )
560     {
561       ::fclose( src );
562                         ::fclose( tgt );
563       QFile::remove( QString( temp ) );
564       return false;
565     }
566     if ( nbread != sizeof( inbuf ) )
567       break;              /* converted ok */
568   }
569   ::fclose( src );
570   ::fclose( tgt );
571
572   /* rename temp -> src */
573   if ( !QFile::remove( absName ) )
574     return false;
575
576   return QDir().rename( QString( temp ), absName );
577 }
578
579 /*!
580   \brief Create path completer which can be used in the widgets
581   to provide auto completions.
582
583   Create an instance of QCompleter class and returns the pointer on it.
584   The calling function is responsible to the desstroying of the created 
585   completer object.
586
587   The QCompleter class provides completions based on a item model and can be
588   used in such as QLineEdit and QComboBox. 
589   When the user starts typing a word, QCompleter suggests possible ways of 
590   completing the word, based on a word list. 
591
592   \param type path type (Qtx::PathType)
593   \param filter file/directory filters (list of wildcards, separated by ";;")
594   \return a pointer to the created completer
595 */
596 QCompleter* Qtx::pathCompleter( const PathType type, const QString& filter )
597 {
598   QStringList extList;
599   QStringList filterList = filter.split( ";;" );
600   for ( QStringList::const_iterator it = filterList.begin(); it != filterList.end(); ++it )
601   {
602     QRegExp rx( "[\\s\\w,;]*\\(?\\*\\.([\\w]+)\\)?[\\d\\s\\w]*" );
603     int index = 0;
604     while ( ( index = rx.indexIn( *it, index ) ) != -1 )
605     {
606       extList.append( QString( "*.%1" ).arg( rx.cap( 1 ) ) );
607       index += rx.matchedLength();
608     }
609   }
610
611   QDir::Filters filters = 0;
612   switch ( type )
613   {
614   case PT_OpenFile:
615   case PT_SaveFile:
616     filters = QDir::AllEntries | QDir::AllDirs | QDir::NoDotAndDotDot;
617     break;
618   case PT_Directory:
619     filters = QDir::Drives | QDir::Dirs | QDir::NoDotAndDotDot;
620     break;
621   }
622
623   QDirModel* dm = new QDirModel( extList, filters, QDir::Unsorted );
624   QCompleter* cmp = new QCompleter( dm, 0 );
625   dm->setParent( cmp );
626
627   return cmp;
628 }
629
630 /*!
631   \brief Pack the specified color into integer RGB set.
632   \param c unpacked color
633   \return packed color
634 */
635 int Qtx::rgbSet( const QColor& c )
636 {
637   return rgbSet( c.red(), c.green(), c.blue() );
638 }
639
640 /*!
641   \brief Pack the specified RGB color components into integer RGB set.
642   \param r red component
643   \param g green component
644   \param b blue component
645   \return packed color
646 */
647 int Qtx::rgbSet( const int r, const int g, const int b )
648 {
649   return ( ( ( 0xff & r ) << 16 ) + ( ( 0xff & g ) << 8 ) + ( 0xff & b ) );
650 }
651
652 /*!
653   \brief Unpack the specified integer RGB set to the color.
654   \param rgb packed color
655   \return unpacked color (QColor)
656 */
657 QColor Qtx::rgbSet( const int rgb )
658 {
659   int r, g, b;
660   rgbSet( rgb, r, g, b );
661   return QColor( r, g, b );
662 }
663
664 /*!
665   \brief Unpack the specified integer RGB set to the three RGB components.
666   \param rgb packed color
667   \param r returned unpacked red component
668   \param g returned unpacked green component
669   \param b returned unpacked blue component
670 */
671 void Qtx::rgbSet( const int rgb, int& r, int& g, int& b )
672 {
673   r = ( rgb >> 16 ) & 0xff;
674   g = ( rgb >> 8 ) & 0xff;
675   b = rgb & 0xff;
676 }
677
678 /*!
679   \brief Return the color specified by the index between min (blue) and max (red).
680   \param index color index
681   \param min required minimum hue value
682   \param max required maximum hue value
683   \return resulting color
684 */
685 QColor Qtx::scaleColor( const int index, const int min, const int max )
686 {
687   static const int HUE[10] = {230, 210, 195, 180, 160, 80, 60, 50, 30, 0};
688
689   int hue = HUE[0];
690
691   if ( min != max )
692   {
693     double aPosition = 9.0 * ( index - min ) / ( max - min );
694     if ( aPosition > 0.0 )
695     {
696       if ( aPosition >= 9.0 )
697         hue = HUE[9];
698       else
699       {
700         int idx = (int)aPosition;
701         hue = HUE[idx] + int( ( aPosition - idx ) * ( HUE[idx + 1] - HUE[idx] ) );
702       }
703     }
704   }
705
706   return QColor::fromHsv( hue, 255, 255 );
707 }
708
709 /*!
710   \brief Generate required number of colors aligned from blue to red.
711   \param num required number of colors
712   \param lst returned set of colors
713 */
714 void Qtx::scaleColors( const int num, QColorList& lst )
715 {
716   lst.clear();
717   for ( int i = 0; i < num; i++ )
718     lst.append( scaleColor( i, 0, num - 1 ) );
719 }
720
721 /*!
722   \brief Scale the pixmap to the required size.
723
724   If \h is 0 (default) the value of \a w is used instead (to create
725   square pixmap).
726
727   \param icon pixmap to be resized
728   \param w required pixmap width
729   \param h required pixmap height
730   \return scaled pixmap
731 */
732 QPixmap Qtx::scaleIcon( const QPixmap& icon, const unsigned w, const unsigned h )
733 {
734   QPixmap p;
735   int aw = w, ah = h <= 0 ? w : h;
736   if ( icon.isNull() || aw <= 0 || ah <= 0 || aw == icon.width() && ah == icon.height() )
737     p = icon;
738   else
739     p = icon.fromImage( icon.toImage().scaled( aw, ah, Qt::KeepAspectRatio, Qt::SmoothTransformation ) );
740   return p;
741 }
742
743 /*!
744   \brief Convert given image to the grayscale format.
745   \param img initial image
746   \return converted to the grayscale image
747 */
748 QImage Qtx::grayscale( const QImage& img )
749 {
750   QImage res = img;
751
752   int colNum = res.numColors();
753   if ( colNum )
754   {
755     for ( int i = 0; i < colNum; i++ )
756       res.setColor( i, qGray( res.color( i ) ) );
757   }
758   else
759   {
760     for ( int y = 0; y < res.height(); y++ )
761     {
762       for ( int x = 0; x < res.width(); x++ )
763       {
764         QRgb pix = res.pixel( x, y );
765         res.setPixel( x, y, qRgba( qGray( pix ), qGray( pix ), qGray( pix ), qAlpha( pix ) ) );
766       }
767     }
768   }
769
770   return res;
771 }
772
773 /*!
774   \brief Convert given pixmap to the grayscale format.
775   \param pix initial pixmap
776   \return converted to the grayscale pixmap
777 */
778 QPixmap Qtx::grayscale( const QPixmap& pix )
779 {
780   QPixmap res;
781   res.fromImage( grayscale( pix.toImage() ) );
782   return res;
783 }
784
785 /*!
786   \brief Create transparent image.
787   \param w required image width
788   \param h required image height
789   \param d required image depth
790   \return generated image
791 */
792 QImage Qtx::transparentImage( const int w, const int h, const int d )
793 {
794   QImage::Format fmt;
795   switch ( d )
796   {
797   case 1:
798     fmt = QImage::Format_Mono;
799     break;
800   case 8:
801     fmt = QImage::Format_Indexed8;
802     break;
803   case 16:
804   case 24:
805   case 32:
806   default:
807     fmt = QImage::Format_ARGB32;
808     break;
809   }
810
811   QImage img( w, h, fmt );
812   if ( !img.isNull() )
813   {
814 //    img.setAlphaBuffer( true );
815     for ( int i = 0; i < img.height(); i++ )
816       for ( int j = 0; j < img.width(); j++ )
817         img.setPixel( j, i, qRgba( 0, 0, 0, 0 ) );
818   }
819   return img;
820 }
821
822 /*!
823   \brief Create transparent pixmap.
824   \param w required image width
825   \param h required pixmap height
826   \param d required pixmap depth
827   \return generated pixmap
828 */
829 QPixmap Qtx::transparentPixmap( const int w, const int h, const int d )
830 {
831   QPixmap pix;
832   QImage img = transparentImage( w, h, d );
833   if ( !img.isNull() )
834     pix.fromImage( img );
835   return pix;
836 }
837
838 /*!
839   \brief Create composite pixmap. 
840
841   Pixmap \a pix is drawn over pixmap \a dest with coordinates
842   specified relatively to the upper left corner of \a dest.
843   If \a dest is not given, the new empty pixmap with appropriate size created instead.
844
845   \param pix source pixmap
846   \param x horizontal shift
847   \param y vertical shift
848   \param dest background pixmap
849   \return resulting pixmap
850 */
851 QPixmap Qtx::composite( const QPixmap& pix, const int x, const int y, const QPixmap& dest )
852 {
853   if ( pix.isNull() )
854     return dest;
855
856   int width = qMax( pix.width() + x, dest.width() );
857   int height = qMax( pix.height() + y, dest.height() );
858
859   QPixmap res( width, height );
860   QImage img = transparentImage( width, height, 32 );
861
862   QPainter p;
863   p.begin( &res );
864   p.fillRect( 0, 0, width, height, QBrush( Qt::white ) );
865
866   if ( !dest.isNull() )
867   {
868     p.drawPixmap( 0, 0, dest );
869     QImage temp = dest.toImage();
870     for ( int i = 0; i < temp.width() && i < img.width(); i++ )
871     {
872       for ( int j = 0; j < temp.height() && j < img.height(); j++ )
873       {
874         if ( temp.hasAlphaChannel() )
875           img.setPixel( i, j, temp.pixel( i, j ) );
876         else
877         {
878           QRgb p = temp.pixel( i, j );
879           img.setPixel( i, j, qRgba( qRed( p ), qGreen( p ), qBlue( p ), 255 ) );
880         }
881       }
882     }
883   }
884
885   p.drawPixmap( x, y, pix );
886   QImage temp = pix.toImage();
887   for ( int c = x; c < temp.width() + x && c < img.width(); c++ )
888   {
889     for ( int r = y; r < temp.height() + y && r < img.height(); r++ )
890     {
891       if ( qAlpha( temp.pixel( c - x, r - y ) ) > 0 )
892         img.setPixel( c, r, temp.pixel( c - x, r - y ) );
893     }
894   }
895
896   p.end();
897
898   for ( int ai = 0; ai < img.width(); ai++ )
899   {
900     for ( int aj = 0; aj < img.height(); aj++ )
901     {
902       if ( qAlpha( img.pixel( ai, aj ) ) < 1 )
903         img.setPixel( ai, aj, qRgba( 255, 255, 255, 255 ) );
904       else
905         img.setPixel( ai, aj, qRgba( 0, 0, 0, 0 ) );
906     }
907   }
908
909   QBitmap bmp( width, height );
910   bmp.fromImage( img, Qt::ColorMode_Mask | Qt::ThresholdDither );
911   res.setMask( bmp );
912
913   return res;
914 }
915
916 /*!
917   \brief Convert color to the string representation.
918   
919   The resulting string is in the one of two possible formats
920   (\c RR, \c GG, \c BB and \c AA value represent red, green, blue
921   and alpha components of the color):
922   - if color has alpha channel : "#RR,#GG,#BB,#AA"
923   - if color does not have alpha channel : "#RRGGBB" 
924
925   If color is invalid, null string is returned.
926
927   Backward conversion can be done with stringToColor() method.
928
929   \param color color to be converted
930   \return string representation of the color
931
932   \sa stringToColor()
933 */
934 QString Qtx::colorToString( const QColor& color )
935 {
936   QString str;
937   if ( color.isValid() )
938   {
939     if ( color.alpha() != 255 )
940     {
941       QStringList vals;
942       vals << QString( "#%1" ).arg( color.red(),   0, 16 );
943       vals << QString( "#%1" ).arg( color.green(), 0, 16 );
944       vals << QString( "#%1" ).arg( color.blue(),  0, 16 );
945       vals << QString( "#%1" ).arg( color.alpha(), 0, 16 );
946       str = vals.join( "," );
947     }
948     else
949     {
950       str = color.name();
951     }
952   }
953   return str;
954 }
955
956 /*!
957   \brief Create color from the string representation.
958   
959   The parameter \a str must be in the one of following formats
960   (\c RR, \c GG, \c BB and \c AA value represent red, green, blue
961   and alpha components of the color):
962   - "#RR,#GG,#BB[,#AA]" or "#RR #GG #BB[ #AA]" (\c RR, \c GG, \c BB
963   and optional \c AA values represent red, green, blue and alpha
964   components of the color in hexadecimal form)
965   - "RR,GG,BB[,AA]" or "RR GG BB[ AA]" (\c RR, \c GG, \c BB
966   and optional \c AA values represent red, green, blue and alpha
967   components of the color in decimal form)
968   - #RRGGBB" - (\c RR, \c GG and \c BB values represent red, green and blue
969   components of the color in hexadecimal form)
970   - an integer value representing packed color components (see rgbSet())
971   - a name from the list of colors defined in the list of SVG color keyword names
972   provided by the World Wide Web Consortium; for example, "steelblue" or "gainsboro".
973
974   Backward conversion can be done with colorToString() method.
975
976   \param str string representation of the color
977   \param color resulting color value
978   \return \c true if the conversion is successful and \c false otherwise
979
980   \sa colorToString(), rgbSet()
981 */
982 bool Qtx::stringToColor( const QString& str, QColor& color )
983 {
984   bool res = true;
985   QStringList vals = str.split( QRegExp( "[\\s|,]" ), QString::SkipEmptyParts );
986
987   QIntList nums;
988   for ( QStringList::const_iterator it = vals.begin(); it != vals.end() && res; ++it )
989   {
990     int num = 0;
991     if ( (*it).startsWith( "#" ) )
992       num = (*it).mid( 1 ).toInt( &res, 16 );
993     else
994       num = (*it).toInt( &res, 10 );
995     if ( res )
996       nums.append( num );
997   }
998
999   res = res && nums.count() >= 3;
1000   if ( res )
1001     color.setRgb( nums[0], nums[1], nums[2] );
1002
1003   if ( !res )
1004   {
1005     int pack = str.toInt( &res );
1006     if ( res )
1007       color = Qtx::rgbSet( pack );
1008   }
1009
1010   if ( !res )
1011   {
1012     color = QColor( str );
1013     res = color.isValid();
1014   }
1015
1016   return res;
1017 }
1018
1019 /*!
1020   \brief Dump linear gradient to the string description.
1021   \param gradient linear gradient to be converted
1022   \return string representation of the linear gradient
1023   \sa stringToLinearGradient()
1024 */
1025 QString Qtx::gradientToString( const QLinearGradient& gradient )
1026 {
1027   QStringList data;
1028   data << "linear";
1029   data << QString::number( gradient.start().x() );
1030   data << QString::number( gradient.start().y() );
1031   data << QString::number( gradient.finalStop().x() );
1032   data << QString::number( gradient.finalStop().y() );
1033   switch( gradient.spread() ) 
1034   {
1035   case QGradient::PadSpread:
1036     data << "pad";
1037     break;
1038   case QGradient::RepeatSpread:
1039     data << "repeat";
1040     break;
1041   case QGradient::ReflectSpread:
1042     data << "reflect";
1043     break;
1044   default:
1045     break;
1046   }
1047   QGradientStops stops = gradient.stops();
1048   QGradientStop stop;
1049   foreach ( stop, stops ) 
1050   {
1051     data << QString::number( stop.first );
1052     data << colorToString( stop.second );
1053   }
1054   return data.join( "|" );
1055 }
1056
1057 /*!
1058   \brief Dump radial gradient to the string description.
1059   \param gradient radial gradient to be converted
1060   \return string representation of the radial gradient
1061   \sa stringToRadialGradient()
1062 */
1063 QString Qtx::gradientToString( const QRadialGradient& gradient )
1064 {
1065   QStringList data;
1066   data << "radial";
1067   data << QString::number( gradient.center().x() );
1068   data << QString::number( gradient.center().y() );
1069   data << QString::number( gradient.focalPoint().x() );
1070   data << QString::number( gradient.focalPoint().y() );
1071   data << QString::number( gradient.radius() );
1072   switch( gradient.spread() ) 
1073   {
1074   case QGradient::PadSpread:
1075     data << "pad";
1076     break;
1077   case QGradient::RepeatSpread:
1078     data << "repeat";
1079     break;
1080   case QGradient::ReflectSpread:
1081     data << "reflect";
1082     break;
1083   default:
1084     break;
1085   }
1086   QGradientStops stops = gradient.stops();
1087   QGradientStop stop;
1088   foreach ( stop, stops ) 
1089   {
1090     data << QString::number( stop.first );
1091     data << colorToString( stop.second );
1092   }
1093   return data.join( "|" );
1094 }
1095
1096 /*!
1097   \brief Dump conical gradient to the string description.
1098   \param gradient conical gradient to be converted
1099   \return string representation of the conical gradient
1100   \sa stringToConicalGradient()
1101 */
1102 QString Qtx::gradientToString( const QConicalGradient& gradient )
1103 {
1104   QStringList data;
1105   data << "conical";
1106   data << QString::number( gradient.center().x() );
1107   data << QString::number( gradient.center().y() );
1108   data << QString::number( gradient.angle() );
1109   switch( gradient.spread() ) 
1110   {
1111   case QGradient::PadSpread:
1112     data << "pad";
1113     break;
1114   case QGradient::RepeatSpread:
1115     data << "repeat";
1116     break;
1117   case QGradient::ReflectSpread:
1118     data << "reflect";
1119     break;
1120   default:
1121     break;
1122   }
1123   QGradientStops stops = gradient.stops();
1124   QGradientStop stop;
1125   foreach ( stop, stops ) 
1126   {
1127     data << QString::number( stop.first );
1128     data << colorToString( stop.second );
1129   }
1130   return data.join( "|" );
1131 }
1132
1133 /*!
1134   \brief Create linear gradient from its string representation.
1135   \param str string representation of the linear gradient
1136   \param gradient resulting linear gradient object
1137   \return \c true if the conversion is successful and \c false otherwise
1138   \sa gradientToString()
1139 */
1140 bool Qtx::stringToLinearGradient( const QString& str, QLinearGradient& gradient )
1141 {
1142   bool success = false;
1143   QStringList vals = str.split( "|", QString::SkipEmptyParts );
1144   if ( vals.count() > 4 && ( vals[0] == "linear" || vals[0] == "lg" ) )
1145   {
1146     // start and end points 
1147     double x1, y1, x2, y2;
1148     bool bOk1, bOk2, bOk3, bOk4;
1149     x1 = vals[1].toDouble( &bOk1 );
1150     y1 = vals[2].toDouble( &bOk2 );
1151     x2 = vals[3].toDouble( &bOk3 );
1152     y2 = vals[4].toDouble( &bOk4 );
1153     if ( bOk1 && bOk2 && bOk3 && bOk4 )
1154     {
1155       gradient = QLinearGradient( x1, y1, x2, y2 );
1156       // spread type
1157       if ( vals.count() > 5 )
1158       {
1159         QString spread = vals[ 5 ].trimmed().toLower();
1160         if ( spread == "pad" || spread == "0" )
1161           gradient.setSpread( QGradient::PadSpread );
1162         else if ( spread == "repeat" || spread == "2" )
1163           gradient.setSpread( QGradient::RepeatSpread );
1164         else if ( spread == "reflect" || spread == "1" )
1165           gradient.setSpread( QGradient::ReflectSpread );
1166       }
1167       // stop points
1168       QGradientStops stops;
1169       for ( int i = 6; i < vals.count(); i+=2 )
1170       {
1171         bool bOk5, bOk6 = false;
1172         QColor c;
1173         double stop = vals[i].toDouble( &bOk5 );
1174         if ( i+1 < vals.count() )
1175           bOk6 = stringToColor( vals[ i+1 ], c );
1176         if ( bOk5 && stop >= 0.0 && stop <= 1.0 && bOk6 && c.isValid() )
1177           stops.append( QGradientStop( stop, c ) );
1178       }
1179       gradient.setStops( stops );
1180       success = true;
1181     }
1182   }
1183   return success;
1184 }
1185
1186 /*!
1187   \brief Create radial gradient from its string representation.
1188   \param str string representation of the radial gradient
1189   \param gradient resulting radial gradient object
1190   \return \c true if the conversion is successful and \c false otherwise
1191   \sa gradientToString()
1192 */
1193 bool Qtx::stringToRadialGradient( const QString& str, QRadialGradient& gradient )
1194 {
1195   bool success = false;
1196   QStringList vals = str.split( "|", QString::SkipEmptyParts );
1197   if ( vals.count() > 5 && vals[0] == "radial" || vals[0] == "rg" ) 
1198   {
1199     // center, radius and focal point
1200     double cx, cy, r, fx, fy;
1201     bool bOk1, bOk2, bOk3, bOk4, bOk5;
1202     cx = vals[1].toDouble( &bOk1 );
1203     cy = vals[2].toDouble( &bOk2 );
1204     fx = vals[3].toDouble( &bOk4 );
1205     fy = vals[4].toDouble( &bOk5 );
1206     r  = vals[5].toDouble( &bOk3 );
1207     if ( bOk1 && bOk2 && bOk3 && bOk4 && bOk5 )
1208     {
1209       gradient = QRadialGradient( cx, cy, r, fx, fy );
1210       // spread type
1211       if ( vals.count() > 6 )
1212       {
1213         QString spread = vals[ 6 ].trimmed().toLower();
1214         if ( spread == "pad" || spread == "0" )
1215           gradient.setSpread( QGradient::PadSpread );
1216         else if ( spread == "repeat" || spread == "2" )
1217           gradient.setSpread( QGradient::RepeatSpread );
1218         else if ( spread == "reflect" || spread == "1" )
1219           gradient.setSpread( QGradient::ReflectSpread );
1220       }
1221       // stop points
1222       QGradientStops stops;
1223       for ( int i = 7; i < vals.count(); i+=2 )
1224       {
1225         bool bOk7, bOk8 = false;
1226         QColor c;
1227         double stop = vals[i].toDouble( &bOk7 );
1228         if ( i+1 < vals.count() )
1229           bOk8 = stringToColor( vals[ i+1 ], c );
1230         if ( bOk7 && stop >= 0.0 && stop <= 1.0 && bOk8 && c.isValid() )
1231           stops.append( QGradientStop( stop, c ) );
1232       }
1233       gradient.setStops( stops );
1234       success = true;
1235     }
1236   }
1237   return success;
1238 }
1239
1240 /*!
1241   \brief Create conical gradient from its string representation.
1242   \param str string representation of the conical gradient
1243   \param gradient resulting conical gradient object
1244   \return \c true if the conversion is successful and \c false otherwise
1245   \sa gradientToString()
1246 */
1247 bool Qtx::stringToConicalGradient( const QString& str, QConicalGradient& gradient )
1248 {
1249   bool success = false;
1250   QStringList vals = str.split( "|", QString::SkipEmptyParts );
1251   if ( vals.count() > 3 && vals[0] == "conical" || vals[0] == "cg" ) 
1252   {
1253     // center and angle
1254     double cx, cy, a;
1255     bool bOk1, bOk2, bOk3;
1256     cx = vals[1].toDouble( &bOk1 );
1257     cy = vals[2].toDouble( &bOk2 );
1258     a = vals[3].toDouble( &bOk3 );
1259     if ( bOk1 && bOk2 && bOk3 )
1260     {
1261       gradient = QConicalGradient( cx, cy, a );
1262       // spread type
1263       if ( vals.count() > 4 )
1264       {
1265         QString spread = vals[ 4 ].trimmed().toLower();
1266         if ( spread == "pad" || spread == "0" )
1267           gradient.setSpread( QGradient::PadSpread );
1268         else if ( spread == "repeat" || spread == "2" )
1269           gradient.setSpread( QGradient::RepeatSpread );
1270         else if ( spread == "reflect" || spread == "1" )
1271           gradient.setSpread( QGradient::ReflectSpread );
1272       }
1273       // stop points
1274       QGradientStops stops;
1275       for ( int i = 5; i < vals.count(); i+=2 )
1276       {
1277         bool bOk4, bOk5 = false;
1278         QColor c;
1279         double stop = vals[i].toDouble( &bOk4 );
1280         if ( i+1 < vals.count() )
1281           bOk5 = stringToColor( vals[ i+1 ], c );
1282         if ( bOk4 && stop >= 0.0 && stop <= 1.0 && bOk5 && c.isValid() )
1283           stops.append( QGradientStop( stop, c ) );
1284       }
1285       gradient.setStops( stops );
1286       success = true;
1287     }
1288   }
1289   return success;
1290 }