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