Salome HOME
Update from BR_V5_DEV 13Feb2009
[modules/gui.git] / src / Qtx / QtxSplash.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   : QtxSplash.cxx
23 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
24 //
25 #include "QtxSplash.h"
26 #include "QtxResourceMgr.h"
27
28 #include <QApplication>
29 #include <QPainter>
30 #include <QMessageBox>
31 #include <QDesktopWidget>
32
33 /*!
34   \class ProgressEvent
35   \internal
36   \brief Progress change custom event.
37 */
38
39 class ProgressEvent: public QEvent
40 {
41 public:
42   /*!
43     \brief Constructor.
44     \param msg progress message
45     \param progress current progress (for example, in %)
46   */
47   ProgressEvent( const QString& msg, const int progress )
48     : QEvent( (QEvent::Type)id() ),
49       myMessage( msg ),
50       myProgress( progress )
51   {}
52   /*!
53     \brief Get progress message.
54     \return message
55   */
56   QString    message()  const { return myMessage; } 
57   /*!
58     \brief Get current progress.
59     \return current progress
60   */
61   int        progress() const { return myProgress; }
62   /*!
63     \brief Get message identifier.
64     \return custom message ID
65   */
66   static int id()             { return QEvent::User + 10; }
67
68 private:
69   QString myMessage;
70   int     myProgress;
71 };
72
73 /*!
74   \class QtxSplash
75   \brief The QtxSplash widget provides a splash screen that can be shown
76   during application startup.
77
78   A splash screen is a widget that is usually displayed when an application
79   is being started. 
80   Splash screens are often used for applications that have long start up times
81   to provide the user with feedback that the application is loading.
82
83   Only one instance of the QtxSplash widget can be created. To access the splash
84   screen widget, use static method QtxSplash::splash(), which creates an
85   instance of the QtxSplash widget (if it is not yet creaed) and returns a
86   pointer to it.
87   You should not destroy this instance - it is done automatically after
88   application main window is shown. Just use methods finish() to make splash
89   screen wait untill main window is shown.
90
91   The splash screen appears in the center of the screen. 
92   The most common usage is to show a splash screen before the main widget
93   is displayed on the screen. 
94   For example,
95   \code
96   int main(int argc, char *argv[])
97   {
98     QApplication app(argc, argv);
99     QPixmap pixmap(":/splash.png");
100     QtxSplash* splash = QtxsSplash::splash(pixmap);
101     splash->show();
102     app.processEvents();
103     ... // do application loading and initialization
104     MainWindow window;
105     window.show();
106     splash->finish(&window);
107     return app.exec();
108   }
109   \endcode
110   
111   The user can hide the splash screen by clicking on it with the mouse.
112   Since the splash screen is typically displayed before the event loop
113   has started running, it is necessary to periodically call
114   QApplication::processEvents() to receive the mouse clicks.
115   To activate the possibility of hiding the splash screen on the mouse
116   click, use setHideOnClick() method passing \c true as parameter.
117   By default, this feature is switched off.
118
119   It is sometimes useful to update the splash screen with any status messages
120   and/or progress information, for example, announcing connections established
121   or modules loaded as the application starts up.
122   QtxSplash class provides the functionality to show status messages
123   and(or) progress bar.
124
125   \code
126   QPixmap pixmap(":/splash.png");
127   QtxSplash* splash = QtxSplash::splash(pixmap);
128   splash->setProgress(0, 5); // progress from 0 to 5
129   splash->show();
130   app.processEvents();
131   // doing first step
132   splash->setMessage("Step 1");
133   splash->setProgress(1); // progress is 20%
134   qApp->processEvents();
135   // ... perform some actions
136   // doing second step
137   splash->setMessage("Step 2");
138   splash->setProgress(2); // progress is 40%
139   qApp->processEvents();
140   // ... perform some actions
141   ... et cetera
142   \endcode
143
144   There is a static function QtxSplash::setStatus() which allows to put the
145   next status message and current progress with one call.
146   It can substitue two calls: setMessage() and setProgress().
147
148   QtxSplash class provides also a lot of functions to customize its behavior.
149   Set progress bar width with setProgressWidth() method, its position and
150   direction with setProgressFlags().
151   It can be single-colored or gradient-colored. Use setProgressColors() methods
152   for this. You can even set your own gradient scale with QLinearGradient, 
153   QRadialGradient or QConicalGradient and use it for the progress
154   bar coloring. In addition, it is possible to enable/disable displaying
155   of the progress percentage with setPercentageVisible() method.
156
157   Displaying of the progress bar and status messages can be switched on/off with
158   setProgressVisible() and setMessageVisible() methods.
159
160   To change the progress bar and status message transparency, use
161   setOpacity() function. The methods setTextAlignment() and setTextColors()
162   can be used to change the attributes of the status messages.
163
164   The displayed message text can include constant info and status message.
165   The constant info is set by setConstantInfo() method and status message
166   is set by setMessage().
167
168   Sometimes it is useful to display an error message above the splash screen
169   window. For example, it can be necessary if an error occurs when loading
170   the application. Method setError() can be used to show the error message
171   and set the error code which can be then retrieved with the error() function.
172
173   There is one more helpful feature. The QtxSplash class can read all the
174   settings from the resource file with help of resource manager 
175   (QtxResourceMgr class). Refer to the method readSettings() for more details.
176 */
177
178 //! The only one instance of splash screen
179 QtxSplash* QtxSplash::mySplash = 0;
180
181 /*!
182   \brief Constructor.
183   
184   Construct a splash screen that will display the \a pixmap.
185   
186   \param pixmap splash screen pixmap
187   \sa setPixmap(), pixmap()
188 */
189 QtxSplash::QtxSplash( const QPixmap& pixmap )
190 : QWidget( 0, Qt::SplashScreen | Qt::WindowStaysOnTopHint ),
191   myAlignment( Qt::AlignBottom | Qt::AlignRight ),
192   myColor( Qt::white ),
193   myHideOnClick( false ),
194   myProgress( 0 ),
195   myTotal( 0 ),
196   myProgressWidth( 10 ),
197   myProgressFlags( BottomSide | LeftToRight ),
198   myMargin( 5 ),
199   myOpacity( 1.0 ),
200   myError( 0 ),
201   myShowPercent( true ),
202   myShowProgress( true ),
203   myShowMessage( true )
204 {
205   setAttribute( Qt::WA_DeleteOnClose, true );
206   setPixmap( pixmap );
207 }
208
209 /*!
210   \brief Destructor.
211 */
212 QtxSplash::~QtxSplash()
213 {
214   mySplash = 0;
215 }
216
217 /*!
218   \brief Get the only instance of the splash screen widget.
219
220   If the splash screen widget does not exist yet, it is created with specified
221   pixmap. Otherwise, pixmap \a px is set to existing widget.
222
223   \param px splash screen pixmap
224   \return splash screen widget
225   \sa setPixmap(), pixmap()
226 */
227 QtxSplash* QtxSplash::splash( const QPixmap& px )
228 {
229   if ( !mySplash )
230     mySplash = new QtxSplash( px );
231   else if ( !px.isNull() )
232     mySplash->setPixmap( px );
233   return mySplash;
234 }
235
236 /*!
237   \brief Send the status message and (optionally) current progress 
238   to the splash screen.
239
240   If the second parameter is less than 0 (default) than it is ignored
241   and only the status message is changed. If you want to modify progress
242   also, pass positive value to the \a progress parameter explicitly.
243
244   \param msg progress status message
245   \param progress current progress
246   \sa setMessage(), setProgress()
247 */
248 void QtxSplash::setStatus( const QString& msg, const int progress )
249 {
250   if ( mySplash ) {
251     QApplication::postEvent( mySplash, new ProgressEvent( msg, progress ) );
252     QApplication::instance()->processEvents();
253   }
254 }
255
256 /*!
257   \brief Set error status and show error message box to the user.
258   \param error error message
259   \param title message box title
260   \param code error code
261   \sa error()
262 */
263 void QtxSplash::setError( const QString& error, const QString& title, const int code )
264 {
265   if ( mySplash ) {
266     mySplash->setError( code );
267     QMessageBox::critical( mySplash, 
268                            title.isEmpty() ? tr( "Error" ) : title,
269                            error,
270                            tr( "&OK" ) );
271   }
272   else {
273     printf( "QtxSplash::error: %s\n",error.toLatin1().constData() );
274   }
275 }
276
277 /*!
278   \brief Set the pixmap that will be used as the splash screen's image.
279   \param pixmap spash screen image pixmap
280   \sa pixmap()
281 */
282 void QtxSplash::setPixmap( const QPixmap& pixmap )
283 {
284   if ( pixmap.hasAlpha() ) {
285     QPixmap opaque( pixmap.size() );
286     QPainter p( &opaque );
287     p.fillRect( 0, 0, pixmap.width(), pixmap.height(), palette().background() );
288     p.drawPixmap( 0, 0, pixmap );
289     p.end();
290     myPixmap = opaque;
291   } 
292   else {
293     myPixmap = pixmap;
294   }
295   QRect r( 0, 0, myPixmap.size().width(), myPixmap.size().height() );
296   resize( myPixmap.size() );
297   move( QApplication::desktop()->screenGeometry().center() - r.center() );
298   if ( !isVisible() )
299     drawContents();
300   else
301     repaint();
302 }
303
304 /*!
305   \brief Get the pixmap that is used as the splash screen's image.
306   \return spash screen image pixmap
307   \sa setPixmap()
308 */
309 QPixmap QtxSplash::pixmap() const
310 {
311   return myPixmap;
312 }
313
314 /*!
315   \brief Set/clear the 'hide on mouse click' flag.
316
317   When this flag is set, user can hide the splash screen window
318   by clicking on it with mouse.
319   But for this to work it is necessary to call periodically
320   QApplication::processEvents() in order to allow event loop to process
321   events because usually main application loop is not yet started
322   at that moment.
323
324   By default this flag is set to \c false.
325
326   \param on new flag state
327   \sa hideOnClick()
328 */
329 void QtxSplash::setHideOnClick( const bool on )
330 {
331   myHideOnClick = on;
332 }
333
334 /*!
335   \brief Get the 'hide on mouse click' flag.
336   \return 'hide on mouse click' flag
337   \sa setHideOnClick()
338 */
339 bool QtxSplash::hideOnClick() const
340 {
341   return myHideOnClick;
342 }
343
344 /*!
345   \brief Enable/disable displaying of the progress bar.
346   \param on if \c true, progress bar will be enabled
347   \sa progressVisible(), setMessageVisible()
348 */
349 void QtxSplash::setProgressVisible( const bool on )
350 {
351   myShowProgress = on;
352   repaint();
353 }
354
355 /*!
356   \brief Check if the progress bar is displayed.
357   \return \c true if progress bar is enabled
358   \sa setProgressVisible()
359 */
360 bool QtxSplash::progressVisible() const
361 {
362   return myShowProgress;
363 }
364
365 /*!
366   \brief Enable/disable displaying of the status message.
367   \param on if \c true, status message will be enabled
368   \sa messageVisible(), setProgressVisible()
369 */
370 void QtxSplash::setMessageVisible( const bool on )
371 {
372   myShowMessage = on;
373   repaint();
374 }
375
376 /*!
377   \brief Check if the status message is displayed.
378   \return \c true if status message is enabled
379   \sa setMessageVisible()
380 */
381 bool QtxSplash::messageVisible() const
382 {
383   return myShowMessage;
384 }
385
386 /*!
387   \brief Enable/disable displaying progress percentage.
388   \param enable if \c true, percentage will be displayed
389   \sa percentageVisible()
390 */
391 void QtxSplash::setPercentageVisible( const bool enable )
392 {
393   myShowPercent = enable;
394   repaint();
395 }
396
397 /*!
398   \brief Check if the progress percentage is displayed.
399   \return \c true if percentage displaying is enabled
400   \sa setPercentageVisible()
401 */
402 bool QtxSplash::percentageVisible() const
403 {
404   return myShowPercent;
405 }
406
407 /*!
408   \brief Set total progress steps to \a total.
409   \param total total number of progress steps
410   \sa totalSteps(), setProgress(), progress()
411 */
412 void QtxSplash::setTotalSteps( const int total )
413 {
414   myTotal = total;
415   repaint();
416 }
417
418 /*!
419   \brief Get total progress steps number.
420   \return total number of progress steps
421   \sa setTotalSteps(), setProgress(), progress()
422 */
423 int QtxSplash::totalSteps() const
424 {
425   return myTotal;
426 }
427     
428 /*!
429   \brief Set current progress.
430   \param progress current progress
431   \sa progress(), setTotalSteps(), setTotalSteps(), 
432 */
433 void QtxSplash::setProgress( const int progress )
434 {
435   myProgress = progress > 0 ? progress : 0;
436   repaint();
437 }
438
439 /*!
440   \brief Get current progress.
441   \return current progress
442   \sa setProgress(), setTotalSteps(), setTotalSteps(), 
443 */
444 int QtxSplash::progress() const
445 {
446   return myProgress;
447 }
448
449 /*!
450   \brief Set current progress to \a progress and total number of 
451   progress steps to \a total.
452   \param progress current progress
453   \param total total number of progress steps
454 */
455 void QtxSplash::setProgress( const int progress, const int total )
456 {
457   myTotal    = total;
458   myProgress = progress > 0 ? progress : 0;
459   repaint();
460 }
461
462 /*!
463   \brief Set splash window margin (a border width).
464
465   Note, that margin is used only for drawing the progress bar and status 
466   messages.
467
468   \param margin new margin width
469   \sa margin()
470 */
471 void QtxSplash::setMargin( const int margin )
472 {
473   myMargin = margin > 0 ? margin : 0;
474   repaint();
475 }
476
477 /*!
478   \brief Get splash window margin (a border width).
479   \return current margin width
480   \sa setMargin()
481 */
482 int QtxSplash::margin() const
483 {
484   return myMargin;
485 }
486
487 /*!
488   \brief Set progress bar width.
489   \param width new progress bar width
490   \sa progressWidth()
491 */
492 void QtxSplash::setProgressWidth( const int width )
493 {
494   myProgressWidth = width > 0 ? width : 0;
495   repaint();
496 }
497
498 /*!
499   \brief Get progress bar width.
500   \return current progress bar width
501   \sa setProgressWidth()
502 */
503 int QtxSplash::progressWidth() const
504 {
505   return myProgressWidth;
506 }
507
508 /*!
509   \brief Set progress bar position and direction.
510
511   By default, progress bar is displayed at the bottom side and
512   shows progress from left to right but this behaviour can be changed.
513
514   \param flags ORed progress bar flags (QtxSplash::ProgressBarFlags)
515   \sa progressFlags()
516 */
517 void QtxSplash::setProgressFlags( const int flags )
518 {
519   myProgressFlags = flags;
520   if ( !( myProgressFlags & ( LeftSide | RightSide | TopSide | BottomSide ) ) )
521     myProgressFlags |= BottomSide;
522   if ( !( myProgressFlags & ( LeftToRight | RightToLeft ) ) )
523     myProgressFlags |= LeftToRight ;
524   repaint();
525 }
526
527 /*!
528   \brief Get progress bar flags: position and direction.
529   \return ORed progress bar flags (QtxSplash::ProgressBarFlags)
530   \sa setProgressFlags()
531 */
532 int QtxSplash::progressFlags() const
533 {
534   return myProgressFlags;
535 }
536
537 /*!
538   \brief Set progress bar colors.
539
540   If the colors differ the two-colored gradient bar is drawn.
541   
542   If the \a endColor is not valid, \a startColor is used instead
543   (i.e. simple, one-colored progress bar is drawn).
544   
545   The parameter \a orientation defines the type of gradient 
546   to be drawn - horizontal or vertical. Default is vertical.
547
548   \param startColor start gradient color (or mono-color)
549   \param endColor end gradient color
550   \param orientation gradient type (Qt::Orientation)
551   \sa progressColors()
552 */
553 void QtxSplash::setProgressColors( const QColor&         startColor, 
554                                    const QColor&         endColor,
555                                    const Qt::Orientation orientation )
556 {
557   if ( !startColor.isValid() )
558     return;
559
560   QLinearGradient l;
561   if ( orientation == Qt::Vertical ) {
562     l.setStart( 0., 0. );
563     l.setFinalStop( 0., 1. );
564   }
565   else {
566     l.setStart( 0., 0. );
567     l.setFinalStop( 1., 0. );
568   }
569   l.setColorAt( 0., startColor );
570   l.setColorAt( 1., endColor.isValid() ? endColor : startColor );
571
572   setProgressColors( l );
573 }
574
575 /*!
576   \brief Set progress bar colors.
577
578   Use this method to display multi-colored gradient progress bar.
579   You have to use QLinearGradient, QRadialGradient or QConicalGradient
580   classes to define the gradient.
581
582   Note, that progress bar coordinates can be defined in absolute or
583   relative mode.
584   In absolute mode the actual coordinates of the gradient key points
585   (like start and final point for linear gradient, center and focal point
586   for radial gradient, etc) are calculated from the top-left progress bar's corner.
587   In relative mode you have to use values from 0 to 1 (including) to define
588   the key points positions.
589   
590   For example:
591   \code
592   QLinearGradient lg(0.5, 0, 1, 1);
593   lg.setColorAt(0.2, Qt::blue);
594   lg.setColorAt(0.6, Qt::red);
595   lg.setSpread(QGradient::RepeatSpread);
596   splash->setProgressGradient(lg);
597   \endcode
598   The above code creates linear gradient, which sets start stop to the
599   center of the progress bar; the final stop is assigned to its right-bottom corner.
600   The color scale (blue to red) is changed by the progress bar diagonal.
601
602   \param gradient color gradient to be used for progress bar coloring
603   \sa progressColors()
604 */
605 void QtxSplash::setProgressColors( const QGradient& gradient )
606 {
607   myGradient = gradient;
608   repaint();
609 }
610
611 /*!
612   \brief Get custom progress bar colors.
613   \return color gradient used for progress bar coloring
614   \sa setProgressColors()
615 */
616 const QGradient* QtxSplash::progressColors() const
617 {
618   return &myGradient;
619 }
620
621 /*!
622   \brief Set progress bar and status text message opacity.
623
624   The value should be in the range 0.0 to 1.0, where 0.0 is fully 
625   transparent and 1.0 is fully opaque.
626
627   \param opacity new opacity value
628   \sa opacity()
629 */
630 void QtxSplash::setOpacity( const double opacity )
631 {
632   myOpacity = opacity < 0.0 ? 0.0 : ( opacity > 1.0 ? 1.0 : opacity );
633   repaint();
634 }
635
636 /*!
637   \brief Get progress bar and status text message opacity.
638   \return current opacity value
639   \sa setOpacity()
640 */
641 double QtxSplash::opacity() const
642 {
643   return myOpacity;
644 }
645
646 /*!
647   \brief Set message text alignment flags.
648
649   Default flags are Qt::AlignBottom | Qt::AlignRight.
650
651   \param alignment alignment flags (Qt::Alignment)
652   \sa textAlignment()
653 */
654 void QtxSplash::setTextAlignment( const int alignment )
655 {
656   myAlignment = alignment;
657   repaint();
658 }
659
660 /*!
661   \brief Get message text alignment flags.
662   \return alignment flags (Qt::Alignment)
663   \sa setTextAlignment()
664 */
665 int QtxSplash::textAlignment() const
666 {
667   return myAlignment;
668 }
669
670 /*!
671   \brief Set message text colors.
672
673   If \a shadow parameter is invalid color, the simple one-colored
674   text is drawn. Otherwise, second parameter is used to draw the text
675   shadow.
676
677   \param color message text color
678   \param shadow message text shadow color
679   \sa textColors()
680 */
681 void QtxSplash::setTextColors( const QColor& color, const QColor& shadow )
682 {
683   if ( !myColor.isValid() )
684     return;
685
686   myColor = color;
687   myShadowColor = shadow;
688
689   repaint();
690 }
691
692 /*!
693   \brief Get message text colors.
694   \param color message text color
695   \param shadow message text shadow color
696   \sa setTextColors()
697 */
698 void QtxSplash::textColors( QColor& color, QColor& shadow ) const
699 {
700   color  = myColor;
701   shadow = myShadowColor;
702 }
703
704 /*!
705   \brief Set constant info text to be displayed on the splash screen.
706
707   The displayed text includes constant info and status message.
708   The constant message is set by setConstantInfo() method and status
709   message is set by setMessage().
710
711   \param info constant info text
712   \sa constantInfo(), message(), setMessage(), option(), setOption()
713 */
714 void QtxSplash::setConstantInfo( const QString& info )
715 {
716   myInfo = info;
717   repaint();
718 }
719
720 /*!
721   \brief Get constant info text.
722   \return constant info text
723   \sa setConstantInfo(), message(), setMessage()
724 */
725 QString QtxSplash::constantInfo() const
726 {
727   return myInfo;
728 }
729
730 /*!
731   \brief Set constant information option value.
732
733   The option is a part of the constant information text,
734   which is replaced at the time of the displaying.
735
736   The following options are supported:
737   - \c \%A - could be used as application name
738   - \c \%V - could be used as application version
739   - \c \%L - could be used as application license information
740   - \c \%C - could be used as application copyright information
741
742   For example,
743   \code
744   splash->setContantInfo("%A [%V]\n%C");
745   splash->setOption("%A", "MyApplication" );
746   splash->setOption("%V", "Version 1.0" );
747   splash->setOption("%C", "Copyright (C) MyCompany 2008" );
748   \endcode
749   
750   \param name option name
751   \param option value
752   \sa option(), setConstantInfo(), constantInfo()
753 */
754 void QtxSplash::setOption( const QString& name, const QString& value )
755 {
756   myOptions[ name ] = value;
757   repaint();
758 }
759
760 /*!
761   \brief Get constant information option value.
762   \param name option name
763   \return option value or empty string if option is not set
764   \sa setOption(), setConstantInfo(), constantInfo()
765 */
766 QString QtxSplash::option( const QString& name ) const
767 {
768   QString val;
769   if ( myOptions.contains( name ) )
770     val = myOptions[ name ];
771   return val;
772 }
773
774 /*!
775   \brief Get current status message.
776   \return status message
777   \sa setMessage(), constantInfo(), setConstantInfo()
778 */
779 QString QtxSplash::message() const
780 {
781   return myMessage;
782 }
783
784 /*!
785   \brief Get error code.
786
787   This function returns error code, set previously with 
788   setError() method.
789   If no error code has been set, 0 is returned.
790
791   \return last error code
792   \sa setError()
793 */
794 int QtxSplash::error() const
795 {
796   return myError;
797 }
798
799 /*!
800   \brief Wait until widget \a mainWin is displayed.
801
802   Makes the splash screen wait until the widget \a mainWin is displayed
803   and then hide and close splash window.
804
805   \param mainWin application main window 
806 */
807 void QtxSplash::finish( QWidget* mainWin )
808 {
809   if ( mainWin ) {
810 #if defined(Q_WS_X11)
811     extern void qt_x11_wait_for_window_manager(QWidget *mainWin);
812     qt_x11_wait_for_window_manager(mainWin);
813 #endif
814   }
815   close();
816 }
817
818 /*!
819   \brief Repaint the splash screen.
820 */
821 void QtxSplash::repaint()
822 {
823   drawContents();
824   QWidget::repaint();
825   QApplication::flush();
826 }
827
828 /*!
829   \brief Read splash settings from the resources manager.
830   
831   This method can be used to setup the splash screen look-n-feel.
832   By default, "splash" section of the resources file is used, but you can
833   use any other section.
834   All the splash screen parameters can be defined via resources file:
835   - \c "image" : splash screen image, see setPixmap()
836   - \c "margin" : splash window margin, see setMargin()
837   - \c "show_progress" : show progress bar flag, see setProgressVisible()
838   - \c "show_message" : show status messages flag, see setMessageVisible()
839   - \c "show_percents" : show progress percentage flag, see setPercentageVisible()
840   - \c "progress_width" : progress bar width(), see setProgressWidth()
841   - \c "progress_flags" : progress bar position and direction, see setProgressFlags()
842   - \c "constant_info" : status messages constant info, see setConstantInfo()
843   - \c "text_colors" : status messages color(s), see setTextColors()
844   - \c "progress_colors" : progress bar color(s), see setProgressColors()
845   - \c "opacity" : progress bar and status messages opacity, see setOpacity()
846   - \c "font" : status messages font
847   - \c "alignment" : status messages alignment flags, see setTextAlignment()
848   - \c "hide_on_click" : hide-on-click flag, see setHideOnClick()
849
850   \param resMgr resources manager
851   \param section resources file section name (if empty, the default "splash"
852   section is used).
853 */
854 void QtxSplash::readSettings( QtxResourceMgr* resMgr, const QString& section )
855 {
856   QString resSection = section.isEmpty() ? QString( "splash" ) : section;
857   
858   // pixmap
859   QString pxname;
860   if ( resMgr->value( resSection, "image", pxname ) ) {
861     QPixmap px( pxname );
862     if ( !px.isNull() )
863       setPixmap( px );
864   }
865
866   // hide-on-click
867 #ifdef _DEBUG_
868   setHideOnClick( true );
869 #else
870   bool bHide;
871   if ( resMgr->value( resSection, "hide_on_click", bHide ) ) {
872     setHideOnClick( bHide );
873   }
874 #endif
875
876   // enable progress bar
877   bool bShowProgress;
878   if ( resMgr->value( resSection, "show_progress", bShowProgress ) ) {
879     setProgressVisible( bShowProgress );
880   }
881   
882   // enable status message
883   bool bShowMessage;
884   if ( resMgr->value( resSection, "show_message", bShowMessage ) ) {
885     setMessageVisible( bShowMessage );
886   }
887   
888   // margin
889   int m;
890   if ( resMgr->value( resSection, "margin", m ) ) {
891     setMargin( m );
892   }
893
894   // progress bar width
895   int pw;
896   if ( resMgr->value( resSection, "progress_width", pw ) ) {
897     setProgressWidth( pw );
898   }
899
900   // progress bar position and direction
901   QString pf;
902   if ( resMgr->value( resSection, "progress_flags", pf ) ) {
903     bool bOk;
904     int fl = pf.toInt( &bOk );
905     if ( !bOk ) {
906       fl = 0;
907       QStringList opts = pf.split( QRegExp( "," ), QString::SkipEmptyParts );
908       for ( int i = 0; i < opts.count(); i++ ) {
909         QString opt = opts[i].trimmed().toLower();
910         if ( opt == "left" )
911           fl = fl | LeftSide;
912         else if ( opt == "right" )
913           fl = fl | RightSide;
914         else if ( opt == "top" )
915           fl = fl | TopSide;
916         else if ( opt == "bottom" )
917           fl = fl | BottomSide;
918         else if ( opt == "left_to_right" )
919           fl = fl | LeftToRight;
920         else if ( opt == "right_to_left" )
921           fl = fl | RightToLeft;
922       }
923     }
924     setProgressFlags( fl );
925   }
926   
927   // opacity
928   double op;
929   if ( resMgr->value( resSection, "opacity", op ) ) {
930     setOpacity( op );
931   }
932
933   // font
934   QFont f;
935   if ( resMgr->value( resSection, "font", f ) ) {
936     setFont( f );
937   }
938
939   // text alignment
940   QString al;
941   if ( resMgr->value( resSection, "alignment", al ) ) {
942     bool bOk;
943     int fl = al.toInt( &bOk );
944     if ( !bOk ) {
945       fl = 0;
946       QStringList opts = al.split( QRegExp( "," ), QString::SkipEmptyParts );
947       for ( int i = 0; i < opts.count(); i++ ) {
948         QString opt = opts[i].trimmed().toLower();
949         if ( opt == "left" )
950           fl = fl | Qt::AlignLeft;
951         else if ( opt == "right" )
952           fl = fl | Qt::AlignRight;
953         else if ( opt == "top" )
954           fl = fl | Qt::AlignTop;
955         else if ( opt == "bottom" )
956           fl = fl | Qt::AlignBottom;
957         else if ( opt == "hcenter" )
958           fl = fl | Qt::AlignHCenter;
959         else if ( opt == "vcenter" )
960           fl = fl | Qt::AlignVCenter;
961         else if ( opt == "justify" )
962           fl = fl | Qt::AlignJustify;
963         else if ( opt == "center" )
964           fl = fl | Qt::AlignCenter;
965       }
966     }
967     setTextAlignment( fl );
968   }
969   // progress color(s)
970   QString pc;
971   QLinearGradient  lgrad;
972   QRadialGradient  rgrad;
973   QConicalGradient cgrad;
974   if ( resMgr->value( resSection, "progress_color",  lgrad ) || 
975        resMgr->value( resSection, "progress_colors", lgrad ) ) {
976     // linear gradient-colored progress bar
977     setProgressColors( lgrad );
978   }
979   else if ( resMgr->value( resSection, "progress_color",  rgrad ) || 
980             resMgr->value( resSection, "progress_colors", rgrad ) ) {
981     // radial gradient-colored progress bar
982     setProgressColors( rgrad );
983   }
984   else if ( resMgr->value( resSection, "progress_color",  cgrad ) || 
985             resMgr->value( resSection, "progress_colors", cgrad ) ) {
986     // conical gradient-colored progress bar
987     setProgressColors( cgrad );
988   }
989   else if ( resMgr->value( resSection, "progress_color",  pc ) || 
990             resMgr->value( resSection, "progress_colors", pc ) ) {
991     // one/two-colored progress bar
992     QStringList colors = pc.split( "|", QString::SkipEmptyParts );
993     QColor c1, c2;
994     Qt::Orientation o = Qt::Vertical;
995     if ( colors.count() > 0 ) c1 = QColor( colors[0] );
996     if ( colors.count() > 1 ) c2 = QColor( colors[1] );
997     int gt;
998     if ( colors.count() > 2 ) {
999       bool bOk;
1000       gt = colors[2].toInt( &bOk );
1001       if ( bOk ) {
1002         if ( gt == 0 )
1003           o = Qt::Horizontal;
1004       }
1005       else {
1006         if ( colors[2].toLower().startsWith( "h" ) )
1007           o = Qt::Horizontal;
1008       }
1009     }
1010     setProgressColors( c1, c2, o );
1011   }
1012   // show percents
1013   bool bPercent;
1014   if ( resMgr->value( resSection, "show_percents", bPercent ) ) {
1015     setPercentageVisible( bPercent );
1016   }
1017
1018   // text color(s)
1019   QString tc;
1020   if ( resMgr->value( resSection, "text_color",  tc ) || 
1021        resMgr->value( resSection, "text_colors", tc ) ) {
1022     QStringList colors = tc.split( "|", QString::SkipEmptyParts );
1023     QColor c1, c2;
1024     if ( colors.count() > 0 )
1025       c1 = QColor( colors[0] );
1026     if ( colors.count() > 1 )
1027       c2 = QColor( colors[1] );
1028     setTextColors( c1, c2 );
1029   }
1030
1031   // const info
1032   QString cinfo;
1033   if ( resMgr->value( resSection, "constant_info", cinfo, false ) ||
1034        resMgr->value( resSection, "info", cinfo, false ) ) {
1035     setConstantInfo( cinfo.split( "|", QString::KeepEmptyParts ).join( "\n" ) );
1036   }
1037 }
1038
1039 /*!
1040   \brief Set status message for the splash screen and define its color 
1041   and aligment flags.
1042   \param msg status message
1043   \param alignment message text alignment flags (Qt::Alignment)
1044   \param color message text color
1045   \sa message(), constantInfo(), setConstantInfo()
1046 */
1047 void QtxSplash::setMessage( const QString& msg, 
1048                             int            alignment,
1049                             const QColor&  color )
1050 {
1051   myMessage   = msg;
1052   myAlignment = alignment;
1053   if ( color.isValid() )
1054     myColor = color;
1055   repaint();
1056 }
1057
1058 /*!
1059   \overload
1060   \brief Set status message for the splash screen.
1061   \param msg status message
1062   \sa message(), constantInfo(), setConstantInfo()
1063 */
1064 void QtxSplash::setMessage( const QString& msg )
1065 {
1066   myMessage = msg;
1067   repaint();
1068 }
1069
1070 /*!
1071   \brief Remove the message being displayed on the splash screen.
1072
1073   This is equivalent to setMessage("");
1074
1075   \sa message(), setMessage()
1076 */
1077 void QtxSplash::clear()
1078 {
1079   myMessage.clear();
1080   repaint();
1081 }
1082
1083 /*!
1084   \brief Draw the contents of the splash screen.
1085   \param p painter
1086 */
1087 void QtxSplash::drawContents( QPainter* p )
1088 {
1089   // draw progress bar
1090   if ( myTotal > 0 && progressVisible() ) {
1091     p->save();
1092     drawProgressBar( p );
1093     p->restore();
1094   }
1095
1096   // draw status message
1097   if ( !fullMessage().isEmpty() && messageVisible() ) {
1098     p->save();
1099     drawMessage( p );
1100     p->restore();
1101   }
1102 }
1103
1104 /*!
1105   \brief Process mouse button pressing event.
1106
1107   Hides splash screen if the 'hide on mouse click' flag is set.
1108
1109   \param me mouse event (not used)
1110   \sa hideOnClick(), setHideOnClick()
1111 */
1112 void QtxSplash::mousePressEvent( QMouseEvent* /*me*/ )
1113 {
1114   if ( myHideOnClick )
1115     hide();
1116 }
1117
1118 /*!
1119   \brief Customize paint event.
1120
1121   This function is implemented to work-around the Qt bug
1122   on some Linux distribututions when the drawing on the 
1123   splash screen widget is not allowed.
1124
1125   \param pe paint event (not used)
1126 */
1127 void QtxSplash::paintEvent( QPaintEvent* /*pe*/ )
1128 {
1129   QPainter p( this );
1130   QPixmap pix = palette().brush( backgroundRole() ).texture();
1131   p.drawPixmap( 0, 0, pix );
1132 }
1133
1134 /*!
1135   \brief Process custom event sent by setStatus() method.
1136   \param ce custom event
1137   \sa setStatus().
1138 */
1139 void QtxSplash::customEvent( QEvent* ce )
1140 {
1141   if ( ce->type() == ProgressEvent::id() ) {
1142     ProgressEvent* pe = (ProgressEvent*)ce;
1143     pe->message().isEmpty() ? clear() : setMessage( pe->message() );
1144     if ( pe->progress() >= 0 )
1145       setProgress( pe->progress() );
1146     QApplication::instance()->processEvents();
1147   }
1148 }
1149
1150 /*!
1151   \brief Check if the gradient is defined in the relative coordinates [static].
1152   \internal
1153   \return \c true if gradient is defined in the relative coordinates
1154 */
1155 static bool checkGradient( const QGradient* g )
1156 {
1157 #define BOUNDED( a, min, max ) ( a >= min && a <= max )
1158   if ( g->type() == QGradient::LinearGradient ) {
1159     const QLinearGradient* lg = static_cast<const QLinearGradient*>( g );
1160     return BOUNDED( lg->start().x(), 0.0, 1.0 ) && 
1161            BOUNDED( lg->start().y(), 0.0, 1.0 ) && 
1162            BOUNDED( lg->finalStop().x(), 0.0, 1.0 ) && 
1163            BOUNDED( lg->finalStop().y(), 0.0, 1.0 );
1164   }
1165   if ( g->type() == QGradient::RadialGradient ) {
1166     const QRadialGradient* rg = static_cast<const QRadialGradient*>( g );
1167     return BOUNDED( rg->center().x(), 0.0, 1.0 ) && 
1168            BOUNDED( rg->center().y(), 0.0, 1.0 ) && 
1169            BOUNDED( rg->focalPoint().x(), 0.0, 1.0 ) && 
1170            BOUNDED( rg->focalPoint().y(), 0.0, 1.0 ); // && BOUNDED( rg->radius(), 0.0, 1.0 );
1171   }
1172   if ( g->type() == QGradient::ConicalGradient ) {
1173     const QConicalGradient* cg = static_cast<const QConicalGradient*>( g );
1174     return BOUNDED( cg->center().x(), 0.0, 1.0 ) && 
1175            BOUNDED( cg->center().y(), 0.0, 1.0 );
1176   }
1177   return false;
1178 }
1179
1180 /*!
1181   \brief Draw progress bar.
1182   \param p painter
1183   \sa drawMessage()
1184 */
1185 void QtxSplash::drawProgressBar( QPainter* p )
1186 {
1187   // get rect, margin, progress bar width
1188   QRect r = rect();
1189   int m   = margin();
1190   int pw  = progressWidth();
1191
1192   // calculate drawing rect
1193   // ... first set default position (if none or wrong position is set)
1194   if ( myProgressFlags & BottomSide )
1195     r = QRect( r.x() + m, r.height() - (m + pw), r.width() - 2 * m, pw );
1196   else if ( myProgressFlags & TopSide )
1197     r = QRect( r.x() + m, r.y() + m, r.width() - 2 * m, pw );
1198   else if ( myProgressFlags & LeftSide )
1199     r = QRect( r.x() + m, r.y() + m, pw, r.height() - 2 * m );
1200   else if ( myProgressFlags & RightSide )
1201     r = QRect( r.width() - (m + pw), r.y() + m, pw, r.height() - 2 * m );
1202
1203   QRect cr = r;
1204   if ( myProgressFlags & TopSide || myProgressFlags & BottomSide ) {
1205     cr.setWidth( (int)( r.width() * ( myProgress > 0 ? myProgress : 0 ) / myTotal ) );
1206     if ( myProgressFlags & RightToLeft )
1207       cr.translate( r.width() - cr.width(), 0 );
1208   }
1209   else if ( myProgressFlags & LeftSide || myProgressFlags & RightSide ) {
1210     cr.setHeight( (int)( r.height() * ( myProgress > 0 ? myProgress : 0 ) / myTotal ) );
1211     if ( myProgressFlags & RightToLeft)
1212       cr.translate( 0, r.height() - cr.height() );
1213   }
1214   QBrush b;
1215   switch ( progressColors()->type() ) {
1216     case QGradient::LinearGradient:
1217     {
1218       QLinearGradient lg;
1219       const QLinearGradient* other = static_cast<const QLinearGradient*>( progressColors() );
1220       if ( checkGradient( other ) ) {
1221         // gradient is defined in relative coordinates [0.0 - 1.0]
1222         lg.setStart( r.left() + r.width()  * other->start().x(), 
1223                      r.top()  + r.height() * other->start().y() );
1224         lg.setFinalStop( r.left() + r.width()  * other->finalStop().x(), 
1225                          r.top()  + r.height() * other->finalStop().y() );
1226       }
1227       else {
1228         // gradient is defined in absolute coordinates
1229         // according to its dimensions
1230         lg.setStart( r.topLeft() + other->start() );
1231         lg.setFinalStop( r.topLeft() + other->finalStop() );
1232       }
1233       
1234       lg.setStops( other->stops() );
1235       lg.setSpread( other->spread() );
1236       
1237       b = QBrush( lg );
1238       
1239       break;
1240     } // case QGradient::LinearGradient
1241     case QGradient::RadialGradient:
1242     {
1243       QRadialGradient rg;
1244       const QRadialGradient* other = static_cast<const QRadialGradient*>( progressColors() );
1245       if ( checkGradient( other ) ) {
1246         // gradient is defined in relative coordinates [0.0 - 1.0]
1247         rg.setCenter( r.left() + r.width()  * other->center().x(),
1248                       r.top()  + r.height() * other->center().y() );
1249         rg.setFocalPoint( r.left() + r.width()  * other->focalPoint().x(),
1250                           r.top()  + r.height() * other->focalPoint().y() );
1251       }
1252       else {
1253         // gradient is defined in absolute coordinates
1254         // according to its dimensions
1255         rg.setCenter( r.topLeft() + other->center() );
1256         rg.setFocalPoint( r.topLeft() + other->focalPoint() );
1257       }
1258       
1259       // only width is taken into account for the radius in relative mode
1260       rg.setRadius( other->radius() > 1.0 ? other->radius() : r.width() * other->radius() );
1261       
1262       rg.setStops( other->stops() );
1263       rg.setSpread( other->spread() );
1264       
1265       b = QBrush( rg );
1266       
1267       break;
1268     } // case QGradient::RadialGradient
1269     case QGradient::ConicalGradient:
1270     {
1271       QConicalGradient cg;
1272       const QConicalGradient* other = static_cast<const QConicalGradient*>( progressColors() );
1273       if ( checkGradient( other ) ) {
1274         // gradient is defined in relative coordinates [0.0 - 1.0]
1275         cg.setCenter( r.left() + r.width()  * other->center().x(),
1276                       r.top()  + r.height() * other->center().y() );
1277       }
1278       else {
1279         // gradient is defined in absolute coordinates
1280         // according to its dimensions
1281         cg.setCenter( r.topLeft() + other->center() );
1282       }
1283
1284       cg.setAngle( other->angle() );
1285       cg.setStops( other->stops() );
1286       cg.setSpread( other->spread() );
1287       
1288       b = QBrush( cg );
1289       
1290       break;
1291     } // case QGradient::RadialGradient
1292   default:
1293     b = QBrush( Qt::red ); // default is simple red-colored progress bar
1294     break;
1295   }
1296   
1297   p->setOpacity( myOpacity );
1298
1299   // draw progress bar outline rectangle
1300   p->setPen( palette().color( QPalette::Dark ) );
1301   p->drawLine( r.left(), r.top(), r.right(), r.top() );
1302   p->drawLine( r.left(), r.top(), r.left(), r.bottom() );
1303   p->setPen( palette().color( QPalette::Light ) );
1304   p->drawLine( r.left(), r.bottom(), r.right(), r.bottom() );
1305   p->drawLine( r.right(), r.top(), r.right(), r.bottom() );
1306
1307   r.setCoords( r.left()+1, r.top()+1, r.right()-1, r.bottom()-1 );
1308   p->setClipRect( cr );
1309   p->fillRect( r, b );
1310   p->setClipping( false );
1311
1312   if ( myShowPercent ) {
1313     int percent = ( int )( ( myProgress > 0 ? myProgress : 0 ) * 100 / myTotal );
1314     QFont f = font();
1315     f.setPixelSize( r.height() - 4 );
1316     p->setFont( f );
1317     // draw shadow status text
1318     if ( myShadowColor.isValid() ) {
1319       QRect rs = r;
1320       rs.moveTopLeft( rs.topLeft() + QPoint( 1,1 ) );
1321       p->setPen( myShadowColor );
1322       p->drawText( rs, Qt::AlignCenter, QString( "%1%" ).arg( percent ) );
1323     }
1324     p->setPen( myColor );
1325     p->drawText( r, Qt::AlignCenter, QString( "%1%" ).arg( percent ) );
1326   }
1327 }
1328
1329 /*!
1330   \brief Draw status message.
1331   \param p painter
1332   \sa drawProgressBar()
1333 */
1334 void QtxSplash::drawMessage( QPainter* p )
1335 {
1336   // get rect, margin, progress bar width
1337   QRect r = rect();
1338   int m   = margin();
1339   int pw  = progressVisible() ? progressWidth() : 0;
1340
1341   // calculate drawing rect
1342   QFontMetrics f( font() );
1343   int spacing = f.lineSpacing();
1344   // ... base rect
1345   QRect r1( r.x() + m, r.y() + m, r.width() - 2 * m, r.height() - 2 * m );
1346   r1.setY( r1.y() - f.leading() );
1347   // ... take into account progress bar
1348   if ( 1 ) {              // if ( myTotal > 0 ) : vsr changed: otherwise text is jumping
1349     if ( myProgressFlags & BottomSide )
1350       r1.setHeight( r1.height() - pw );
1351     else if ( myProgressFlags & TopSide )
1352       r1.setY( r1.y() + pw );
1353     else if ( myProgressFlags & LeftSide )
1354       r1.setX( r1.x() + pw );
1355     else if ( myProgressFlags & RightSide )
1356       r1.setWidth( r1.width() - pw );
1357   }
1358   
1359   // ... take into account trailing '\n' symbols
1360   int shift = 0;
1361   QString msg = fullMessage();
1362   int i = msg.length() - 1;
1363   while( i >= 0 && msg[ i-- ] == '\n' )
1364     shift += spacing;
1365   r1.setHeight( r1.height() - shift );
1366
1367   p->setOpacity( myOpacity );
1368
1369   // draw shadow status text
1370   if ( myShadowColor.isValid() ) {
1371     QRect r2 = r1;
1372     if ( myAlignment & Qt::AlignLeft   ) r2.setLeft  ( r2.left()   + 1 );
1373     if ( myAlignment & Qt::AlignTop    ) r2.setTop   ( r2.top()    + 1 );
1374     if ( myAlignment & Qt::AlignRight  ) r2.setRight ( r2.right()  + 1 );
1375     if ( myAlignment & Qt::AlignBottom ) r2.setBottom( r2.bottom() + 1 );
1376     p->setPen( myShadowColor );
1377     p->drawText( r2, myAlignment, msg );
1378   }
1379
1380   // draw foreground status text
1381   p->setPen( myColor );
1382   p->drawText( r1, myAlignment, msg );
1383 }
1384
1385 /*!
1386   \brief Draw the splash screen window contents.
1387   \internal
1388 */
1389 void QtxSplash::drawContents()
1390 {
1391   QPixmap textPix = myPixmap;
1392   QPainter painter( &textPix );
1393   painter.initFrom( this );
1394   drawContents( &painter );
1395   QPalette pal = palette();
1396   pal.setBrush( backgroundRole(), QBrush( textPix ) );
1397   setPalette( pal );
1398 }
1399
1400 /*!
1401   \brief Sets error code.
1402   \param code error code
1403   \internal
1404 */
1405 void QtxSplash::setError( const int code )
1406 {
1407   myError = code;
1408 }
1409
1410 /*!
1411   \brief Get full message which includes constant info and status message.
1412   \return get fill message text
1413   \sa constantInfo(), setConstantInfo(), message(), setMessage()
1414   \internal
1415 */
1416 QString QtxSplash::fullMessage() const
1417 {
1418   QStringList info;
1419
1420   QString cinfo = myInfo;
1421   cinfo = cinfo.replace( QRegExp( "%A" ), option( "%A" ) );
1422   cinfo = cinfo.replace( QRegExp( "%V" ), option( "%V" ) );
1423   cinfo = cinfo.replace( QRegExp( "%L" ), option( "%L" ) );
1424   cinfo = cinfo.replace( QRegExp( "%C" ), option( "%C" ) );
1425
1426   if ( !cinfo.isEmpty() )
1427     info << cinfo;
1428   if ( !myMessage.isEmpty() )
1429     info << myMessage;
1430   return info.join( "\n" );
1431 }