Salome HOME
Join modifications from BR_Dev_For_4_0 tag V4_1_1.
[modules/gui.git] / src / Qtx / Qtx.cxx
1 // Copyright (C) 2005  OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
2 // 
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either 
6 // version 2.1 of the License.
7 // 
8 // This library is distributed in the hope that it will be useful 
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public  
14 // License along with this library; if not, write to the Free Software 
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // File:      Qtx.cxx
20 // Author:    Sergey TELKOV
21
22 #include "Qtx.h"
23
24 #include <qdir.h>
25 #include <qbitmap.h>
26 #include <qstring.h>
27 #include <qwidget.h>
28 #include <qlayout.h>
29 #include <qpainter.h>
30 #include <qtoolbar.h>
31 #include <qgroupbox.h>
32 #include <qfileinfo.h>
33 #include <qpopupmenu.h>
34 #include <qobjectlist.h>
35 #include <qwidgetlist.h>
36 #include <qapplication.h>
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdarg.h>
41
42 /*!
43         Name: setTabOrder [static public]
44         Desc: Set tab order for specified list of widgets. Last parameter should be null pointer.
45 */
46
47 void Qtx::setTabOrder( QWidget* first, ... )
48 {
49   va_list wids;
50         va_start( wids, first );
51
52         QWidgetList widList;
53
54         QWidget* cur = first;
55         while ( cur )
56         {
57           widList.append( cur );
58                 cur = va_arg( wids, QWidget* );
59   }
60
61         setTabOrder( widList );
62 }
63
64 /*!
65         Name: setTabOrder [static public]
66         Desc: Set tab order for specified list of widgets.
67 */
68
69 void Qtx::setTabOrder( const QWidgetList& widgets )
70 {
71   if ( widgets.count() < 2 )
72     return;
73
74   QWidget* prev = 0;
75   for ( QWidgetListIt it( widgets ); it.current(); ++it )
76   {
77     QWidget* next = it.current();
78     if ( prev && next )
79       QWidget::setTabOrder( prev, next );
80     prev = next;
81   }
82 }
83
84 /*!
85         Name: alignWidget [static public]
86         Desc: Align widget 'src' relative to widget 'ref' acording to alignment flags.
87               Alignment flags:
88                           Qtx::AlignLeft      - Align left side of 'src' to left side of 'ref'.
89                           Qtx::AlignRight     - Align right side of 'src' to right side of 'ref'.
90                           Qtx::AlignTop       - Align top side of 'src' to top side of 'ref'.
91                           Qtx::AlignBottom    - Align bottom side of 'src' to bottom side of 'ref'.
92                           Qtx::AlignHCenter   - Align 'src' to center of 'ref' in horizontal dimension.
93                           Qtx::AlignVCenter   - Align 'src' to center of 'ref' in vertical dimension.
94                           Qtx::AlignCenter    - Align 'src' to center of 'ref' in both dimensions.
95                           Qtx::AlignOutLeft   - Align right side of 'src' to left side of 'ref'.
96                           Qtx::AlignOutRight  - Align left side of 'src' to right side of 'ref'.
97                           Qtx::AlignOutTop    - Align bottom side of 'src' to top side of 'ref'.
98                           Qtx::AlignOutBottom - Align top side of 'src' to bottom side of 'ref'.
99 */
100
101 void Qtx::alignWidget( QWidget* src, const QWidget* ref, const int alignFlags )
102 {
103         if ( !src || !ref || !alignFlags )
104                 return;
105
106         QPoint srcOri = src->pos();
107         QPoint refOri = ref->pos();
108         if ( src->parentWidget() && !src->isTopLevel() )
109                 srcOri = src->parentWidget()->mapToGlobal( srcOri );
110         if ( ref->parentWidget() && !ref->isTopLevel() )
111                 refOri = ref->parentWidget()->mapToGlobal( refOri );
112
113         int x = srcOri.x(), y = srcOri.y();
114         int refWidth = ref->frameGeometry().width(), refHei = ref->frameGeometry().height();
115         int srcWidth = src->frameGeometry().width(), srcHei = src->frameGeometry().height();
116
117         if ( srcWidth <= 0 )
118                 srcWidth = src->sizeHint().width();
119   if ( srcHei <= 0 )
120     srcHei = src->sizeHint().height();
121
122         int border = 0;
123   if ( ref->isTopLevel() && ref->isMaximized() &&
124        src->isTopLevel() && !src->isMaximized() )
125     border = ( src->frameGeometry().width() - src->width() ) / 2;
126
127         if ( alignFlags & Qtx::AlignLeft )
128                 x = refOri.x() + border;
129         if ( alignFlags & Qtx::AlignOutLeft )
130                 x = refOri.x() - srcWidth - border;
131         if ( alignFlags & Qtx::AlignRight )
132                 x = refOri.x() + refWidth - srcWidth - border;
133         if ( alignFlags & Qtx::AlignOutRight )
134                 x = refOri.x() + refWidth + border;
135         if ( alignFlags & Qtx::AlignTop )
136                 y = refOri.y() + border;
137         if ( alignFlags & Qtx::AlignOutTop )
138                 y = refOri.y() - srcHei - border;
139         if ( alignFlags & Qtx::AlignBottom )
140                 y = refOri.y() + refHei - srcHei - border;
141         if ( alignFlags & Qtx::AlignOutBottom )
142                 y = refOri.y() + refHei + border;
143         if ( alignFlags & Qtx::AlignHCenter )
144                 x = refOri.x() + ( refWidth - srcWidth ) / 2;
145         if ( alignFlags & Qtx::AlignVCenter )
146                 y = refOri.y() + ( refHei - srcHei ) / 2;
147
148         if ( src->parentWidget() && !src->isTopLevel() )
149         {
150                 QPoint pos = src->parentWidget()->mapFromGlobal( QPoint( x, y ) );
151                 x = pos.x();
152                 y = pos.y();
153         }
154
155         QWidget* desk = QApplication::desktop();
156         if ( desk && x + srcWidth + border > desk->width() )
157                 x = desk->width() - srcWidth - border;
158         if ( desk && y + srcHei + border > desk->height() )
159                 y = desk->height() - srcHei - border;
160
161         x = QMAX( x, 0 );
162         y = QMAX( y, 0 );
163
164         src->move( x, y );
165 }
166
167 /*!
168         Name: simplifySeparators [static public]
169         Desc: Checks toolbar for unnecessary separators and removes them
170 */
171 void Qtx::simplifySeparators( QToolBar* toolbar )
172 {
173   if ( !toolbar )
174     return;
175
176   const QObjectList* objList = toolbar->children();
177   if ( !objList )
178     return;
179
180   QObjectList delList;
181
182   bool isPrevSep = true;
183   for ( QObjectListIt it( *objList ); it.current(); ++it )
184   {
185     bool isSep = it.current()->isA( "QToolBarSeparator" );
186     if ( isPrevSep && isSep )
187       delList.append( it.current() );
188     isPrevSep = isSep;
189   }
190
191   for ( QObjectListIt itr( delList ); itr.current(); ++itr )
192     delete itr.current();
193
194   if ( toolbar->children() && !toolbar->children()->isEmpty() &&
195        toolbar->children()->getFirst()->isA( "QToolBarSeparator" ) )
196     delete toolbar->children()->getFirst();
197
198   if ( toolbar->children() && !toolbar->children()->isEmpty() &&
199        toolbar->children()->getLast()->isA( "QToolBarSeparator" ) )
200     delete toolbar->children()->getLast();
201 }
202
203 /*!
204         Name: simplifySeparators [static public]
205         Desc: Checks popup menu recursively for unnecessary separators and removes them
206 */
207 void Qtx::simplifySeparators( QPopupMenu* popup, const bool recursive )
208 {
209   if ( !popup || !popup->count() )
210     return;
211
212   QIntList idRemove;
213   for ( uint i = 1; i < popup->count(); i++ )
214   {
215     if ( popup->findItem( popup->idAt( i ) )->isSeparator() &&
216          popup->findItem( popup->idAt( i - 1 ) )->isSeparator() )
217       idRemove.append( popup->idAt( i ) );
218
219     if ( recursive )
220       simplifySeparators( popup->findItem( popup->idAt( i ) )->popup() );
221   }
222
223   for ( QIntList::const_iterator it = idRemove.begin(); it != idRemove.end(); ++it )
224     popup->removeItem( *it );
225
226   if ( popup->count() > 0 && popup->findItem( popup->idAt( 0 ) )->isSeparator() )
227     popup->removeItem( popup->idAt( 0 ) );
228
229   if ( popup->count() > 0 && popup->findItem( popup->idAt( popup->count() - 1 ) )->isSeparator() )
230     popup->removeItem( popup->idAt( popup->count() - 1 ) );
231 }
232
233 /*!
234         Name: isParent [static public]
235         Desc: Returns 'true' if specified 'parent' is parent object of given 'child'.
236 */
237 bool Qtx::isParent( QObject* child, QObject* parent )
238 {
239   if ( !child || !parent )
240     return false;
241
242   bool res = false;
243   QObject* obj = child;
244   while ( !res && obj )
245   {
246     res = obj == parent;
247     obj = obj->parent();
248   }
249   return res;
250 }
251
252 /*!
253         Name: dir [static public]
254         Desc: Returns dir name or null string.
255 */
256 QString Qtx::dir( const QString& path, const bool abs )
257 {
258   QString dirPath = QFileInfo( path ).dirPath( abs );
259   if ( dirPath == QString( "." ) )
260     dirPath = QString::null;
261   return dirPath;
262 }
263
264 /*!
265         Name: file [static public]
266         Desc: Returns file with or without extension.
267 */
268 QString Qtx::file( const QString& path, bool withExt )
269 {
270   if ( withExt )
271     return QFileInfo( path ).fileName();
272   else
273     return QFileInfo( path ).baseName();
274 }
275
276 /*!
277         Name: extension [static public]
278         Desc: Returns the file extension only or null string.
279 */
280 QString Qtx::extension( const QString& path )
281 {
282   return QFileInfo( path ).extension(false); // after the last dot
283 }
284
285 /*!
286         Name: library [static public]
287         Desc: Generate library file name.
288         Append required prefix (lib) and suffix (.dll/.so) to the library file name.
289 */
290 QString Qtx::library( const QString& str )
291 {
292   QString path = dir( str, false );
293   QString name = file( str, false );
294   QString ext  = extension( str );
295
296 #ifndef WIN32
297   if ( !name.startsWith( "lib" ) )
298     name = QString( "lib" ) + name;
299 #endif
300
301 #ifdef WIN32
302   QString libExt( "dll" );
303 #else
304   QString libExt( "so" );
305 #endif
306
307   if ( ext.lower() != QString( "so" ) && ext.lower() != QString( "dll" ) )
308   {
309     if ( !name.isEmpty() && !ext.isEmpty() )
310       name += QString( "." );
311     name += ext;
312   }
313
314   ext = libExt;
315
316   QString fileName = addSlash( path ) + name + QString( "." ) + ext;
317
318   return fileName;
319 }
320
321 /*!
322         Name: tmpDir [static public]
323         Desc: Returns path to temporary directory.
324 */
325 QString Qtx::tmpDir()
326 {
327         char* tmpdir = ::getenv( "TEMP" );
328         if ( !tmpdir )
329                 tmpdir = ::getenv ( "TMP" );
330         if ( !tmpdir )
331         {
332 #ifdef WIN32
333                 tmpdir = "C:\\";
334 #else
335                 tmpdir = "/tmp";
336 #endif
337         }
338         return QString( tmpdir );
339 }
340
341 /*!
342         Name: mkDir [static public]
343         Desc: Creates directory with intermediate perent directories.
344                     Returns true in successfull case.
345 */
346 bool Qtx::mkDir( const QString& dirPath )
347 {
348         QString path = QDir::convertSeparators( dirPath );
349
350 #ifdef WIN32
351         while ( !path.isEmpty() && path.at( path.length() - 1 ) == QDir::separator() )
352                 path.remove( path.length() - 1, 1 );
353
354         if ( path.at( path.length() - 1 ) == ':' )
355                 return QFileInfo( path ).exists();
356 #endif
357
358         QFileInfo fInfo( path );
359         if ( fInfo.exists() )
360                 return fInfo.isDir();
361
362         if ( !mkDir( fInfo.dirPath() ) )
363                 return false;
364
365         return QDir( fInfo.dirPath() ).mkdir( fInfo.fileName() );
366 }
367
368 /*!
369         Name: rmDir [static public]
370         Desc: Removes directory with its subdirectories and files.
371                     Returns true in successfull case.
372 */
373 bool Qtx::rmDir( const QString& thePath )
374 {
375         QFileInfo fi( thePath );
376         if ( !fi.exists() )
377                 return true;
378
379         bool stat = true;
380         if ( fi.isFile() )
381                 stat = QFile::remove( thePath );
382         else if ( fi.isDir() )
383         {
384                 QDir aDir( thePath );
385                 const QFileInfoList* anEntries = aDir.entryInfoList();
386                 if ( anEntries )
387                 {
388                         for ( QPtrListIterator<QFileInfo> it( *anEntries ); it.current(); ++it )
389                         {
390                                 if ( it.current()->fileName() == "." || it.current()->fileName() == ".." )
391                                         continue;
392                                 stat = stat && rmDir( it.current()->absFilePath() );
393                         }
394                 }
395                 stat = stat && aDir.rmdir( thePath );
396         }
397         return stat;
398 }
399
400 /*!
401         Name: addSlash [static public]
402         Desc: Adds a slash to the end of 'path' if it is not already there.
403 */
404 QString Qtx::addSlash( const QString& path )
405 {
406         QString res = path;
407   if ( !res.isEmpty() && res.at( res.length() - 1 ) != QChar( '/' ) &&
408           res.at( res.length() - 1 ) != QChar( '\\' ) )
409   res += QDir::separator();
410   return res;
411 }
412
413 /*!
414         Name: dos2unix [static public]
415         Desc: Convert text file. Replace symbols "LF/CR" by symbol "LF".
416 */
417 bool Qtx::dos2unix( const QString& absName )
418 {
419   FILE* src = ::fopen( absName, "rb" );
420   if ( !src )
421                 return false;
422
423   /* we'll use temporary file */
424   char temp[512] = { '\0' };
425   QString dir = Qtx::dir( absName );
426   FILE* tgt = ::fopen( strcpy( temp, ::tempnam( dir, "__x" ) ), "wb" );
427   if ( !tgt )
428                 return false;
429
430   /* temp -> result of conversion */
431   const char CR = 0x0d;
432   const char LF = 0x0a;
433   bool waitingLF = false;
434
435   while( true )
436   {
437     int  outcnt = 0;
438     char inbuf[512], outbuf[512];
439
440     /* convert buffer */
441     int nbread = ::fread( inbuf, 1, sizeof( inbuf ), src );
442     for ( int incnt = 0; incnt < nbread; incnt++  )
443     {
444       if ( waitingLF )
445       {
446         waitingLF = false;
447         if ( inbuf[incnt] == LF )
448           outbuf[outcnt++] = LF;
449         else
450           outbuf[outcnt++] = CR;
451       }
452       else if ( inbuf[incnt] == CR )
453         waitingLF = true;
454       else
455         outbuf[outcnt++] = inbuf[incnt];
456     }
457
458     /* check last sym in buffer */
459     waitingLF = ( inbuf[nbread - 1] == CR );
460
461     /* write converted buffer to temp file */
462     int nbwri = ::fwrite( outbuf, 1, outcnt, tgt );
463     if ( nbwri != outcnt )
464     {
465       ::fclose( src );
466                         ::fclose( tgt );
467       QFile::remove( QString( temp ) );
468       return false;
469     }
470     if ( nbread != sizeof( inbuf ) )
471       break;              /* converted ok */
472   }
473   ::fclose( src );
474         ::fclose( tgt );
475
476   /* rename temp -> src */
477   if ( !QFile::remove( absName ) )
478     return false;
479
480   return QDir().rename( QString( temp ), absName );
481 }
482
483 /*!
484         Name: rgbSet [static public]
485         Desc: Pack the specified color into one integer RGB set.
486 */
487 int Qtx::rgbSet( const QColor& c )
488 {
489   return rgbSet( c.red(), c.green(), c.blue() );
490 }
491
492 /*!
493         Name: rgbSet [static public]
494         Desc: Pack the specified color components into one integer RGB set.
495 */
496 int Qtx::rgbSet( const int r, const int g, const int b )
497 {
498   return ( ( ( 0xff & r ) << 16 ) + ( ( 0xff & g ) << 8 ) + ( 0xff & b ) );
499 }
500
501 /*!
502         Name: rgbSet [static public]
503         Desc: Unpack the specified integer RGB set into the color.
504 */
505 void Qtx::rgbSet( const int rgb, QColor& c )
506 {
507   int r, g, b;
508   rgbSet( rgb, r, g, b );
509   c = QColor( r, g, b );
510 }
511
512 /*!
513         Name: rgbSet [static public]
514         Desc: Unpack the specified integer RGB set into the color components.
515 */
516 void Qtx::rgbSet( const int rgb, int& r, int& g, int& b )
517 {
518   r = ( rgb >> 16 ) & 0xff;
519   g = ( rgb >> 8 ) & 0xff;
520   b = rgb & 0xff;
521 }
522
523 /*!
524         Name: scaleColor [static public]
525         Desc: Returns the color specified by the index between min (blue) and max (red).
526 */
527 QColor Qtx::scaleColor( const int index, const int min, const int max )
528 {
529   static const int HUE[10] = {230, 210, 195, 180, 160, 80, 60, 50, 30, 0};
530
531   int hue = HUE[0];
532
533         if ( min != max )
534   {
535     double aPosition = 9.0 * ( index - min ) / ( max - min );
536     if ( aPosition > 0.0 )
537     {
538       if ( aPosition >= 9.0 )
539         hue = HUE[9];
540       else
541       {
542         int idx = (int)aPosition;
543         hue = HUE[idx] + int( ( aPosition - idx ) * ( HUE[idx + 1] - HUE[idx] ) );
544       }
545     }
546   }
547
548   return QColor( hue, 255, 255, QColor::Hsv );
549 }
550
551 /*!
552         Name: scaleColors [static public]
553         Desc: Returns the 'num' number of colors from blue to red.
554 */
555 void Qtx::scaleColors( const int num, QValueList<QColor>& lst )
556 {
557   lst.clear();
558   for ( int i = 0; i < num; i++ )
559     lst.append( scaleColor( i, 0, num - 1 ) );
560 }
561
562 /*!
563         Name: grayscale [static public]
564         Desc: Convert color image to grayscale image.
565 */
566 QImage Qtx::grayscale( const QImage& img )
567 {
568   QImage res = img;
569
570   int colNum = res.numColors();
571   if ( colNum )
572   {
573     for ( int i = 0; i < colNum; i++ )
574       res.setColor( i, qGray( res.color( i ) ) );
575   }
576   else
577   {
578     for ( int y = 0; y < res.height(); y++ )
579     {
580       for ( int x = 0; x < res.width(); x++ )
581       {
582         QRgb pix = res.pixel( x, y );
583         res.setPixel( x, y, qRgba( qGray( pix ), qGray( pix ), qGray( pix ), qAlpha( pix ) ) );
584       }
585     }
586   }
587
588   return res;
589 }
590
591 /*!
592         Name: grayscale [static public]
593         Desc: Convert color pixmap to grayscale pixmap.
594 */
595 QPixmap Qtx::grayscale( const QPixmap& pix )
596 {
597   QPixmap res;
598   res.convertFromImage( grayscale( pix.convertToImage() ) );
599   return res;
600 }
601
602 /*!
603         Name: transparentImage [static public]
604         Desc: Create transparent image with specified width \aw, height \ah and color depth \ad.
605 */
606 QImage Qtx::transparentImage( const int w, const int h, const int d )
607 {
608   QImage img;
609   if ( img.create( w, h, d < 0 ? QPixmap::defaultDepth() : d ) )
610   {
611     img.setAlphaBuffer( true );
612     for ( int i = 0; i < img.height(); i++ )
613       for ( int j = 0; j < img.width(); j++ )
614         img.setPixel( j, i, qRgba( 0, 0, 0, 0 ) );
615   }
616   return img;
617 }
618
619 /*!
620         Name: transparentPixmap [static public]
621         Desc: Create transparent pixmap with specified width \aw, height \ah and color depth \ad.
622 */
623 QPixmap Qtx::transparentPixmap( const int w, const int h, const int d )
624 {
625   QPixmap pix;
626   QImage img = transparentImage( w, h, d );
627   if ( !img.isNull() )
628     pix.convertFromImage( img );
629   return pix;
630 }
631
632 /*!
633         Name: composite [static public]
634         Desc: Create composite pixmap. Pixmap 'pix' draws over pixmap 'dest' with coordinates
635         specified relative upper left corner of 'dest'. If 'dest' not given then new empty
636         pixmap with appropriate size created.
637 */
638 QPixmap Qtx::composite( const QPixmap& pix, const int x, const int y, const QPixmap& dest )
639 {
640   if ( pix.isNull() )
641     return dest;
642
643   int width = QMAX( pix.width() + x, dest.width() );
644   int height = QMAX( pix.height() + y, dest.height() );
645
646   QPixmap res( width, height );
647   QImage img = transparentImage( width, height, 32 );
648
649   QPainter p;
650   p.begin( &res );
651   p.fillRect( 0, 0, width, height, QBrush( white ) );
652
653   if ( !dest.isNull() )
654   {
655     p.drawPixmap( 0, 0, dest );
656     QImage temp = dest.convertToImage();
657     for ( int i = 0; i < temp.width() && i < img.width(); i++ )
658     {
659       for ( int j = 0; j < temp.height() && j < img.height(); j++ )
660       {
661         if ( temp.hasAlphaBuffer() )
662           img.setPixel( i, j, temp.pixel( i, j ) );
663         else
664         {
665           QRgb p = temp.pixel( i, j );
666           img.setPixel( i, j, qRgba( qRed( p ), qGreen( p ), qBlue( p ), 255 ) );
667         }
668       }
669     }
670   }
671
672   p.drawPixmap( x, y, pix );
673   QImage temp = pix.convertToImage();
674   for ( int c = x; c < temp.width() + x && c < img.width(); c++ )
675   {
676     for ( int r = y; r < temp.height() + y && r < img.height(); r++ )
677     {
678       if ( qAlpha( temp.pixel( c - x, r - y ) ) > 0 )
679         img.setPixel( c, r, temp.pixel( c - x, r - y ) );
680     }
681   }
682
683   p.end();
684
685   for ( int ai = 0; ai < img.width(); ai++ )
686   {
687     for ( int aj = 0; aj < img.height(); aj++ )
688     {
689       if ( qAlpha( img.pixel( ai, aj ) ) < 1 )
690         img.setPixel( ai, aj, qRgba( 255, 255, 255, 255 ) );
691       else
692         img.setPixel( ai, aj, qRgba( 0, 0, 0, 0 ) );
693     }
694   }
695
696   QBitmap bmp( width, height );
697   bmp.convertFromImage( img, Qt::ColorMode_Mask | Qt::ThresholdDither );
698   res.setMask( bmp );
699
700   return res;
701 }