Salome HOME
Copyright update 2022
[modules/gui.git] / src / Qtx / QtxSplash.cxx
1 // Copyright (C) 2007-2022  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   myShowProgress( true ),
204   myShowMessage( true ),
205   myShowPercent( 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     qCritical( "QtxSplash::error: %s\n", qPrintable( error ) );
276   }
277 }
278
279 /*!
280   \brief Set fixed size of splash screen.
281
282   If size is not set explicitly, it is inherited from image being used.
283   Otherwise, image is rescaled to given size.
284
285   Width and height can be given indepdently; not given dimension
286   (zero of negative value) is ignored.
287
288   \param size fixed splash size
289   \sa fixedSize()
290 */
291 void QtxSplash::setSize( const QSize& size )
292 {
293   mySize = size;
294   setPixmap( pixmap() );
295 }
296
297 /*!
298   \brief This is an overloaded function.
299
300   \param w width
301   \param h height
302   \sa fixedSize()
303 */
304 void QtxSplash::setSize( int w, int h )
305 {
306   setSize( QSize( w, h ) );
307 }
308
309 /*!
310   \brief Get fixed splash screen's size if it was set.
311   \sa setSize()
312 */
313 QSize QtxSplash::fixedSize() const
314 {
315   return mySize;
316 }
317
318
319 /*!
320   \brief Set the pixmap that will be used as the splash screen's image.
321   \param pixmap spash screen image pixmap
322   \sa pixmap()
323 */
324 void QtxSplash::setPixmap( const QPixmap& pixmap )
325 {
326   if ( pixmap.isNull() )
327     return;
328
329   if ( pixmap.hasAlpha() ) {
330     QPixmap opaque( pixmap.size() );
331     QPainter p( &opaque );
332     p.fillRect( 0, 0, pixmap.width(), pixmap.height(), palette().background() );
333     p.drawPixmap( 0, 0, pixmap );
334     p.end();
335     myPixmap = opaque;
336   } 
337   else {
338     myPixmap = pixmap;
339   }
340
341   int width = fixedSize().width();
342   int height = fixedSize().height();
343   if ( width > 0 || height > 0 ) {
344     QImage img = myPixmap.toImage();
345     img = img.scaled( width > 0 ? width : myPixmap.width(),
346                       height > 0 ? height : myPixmap.height(),
347                       Qt::IgnoreAspectRatio,
348                       Qt::SmoothTransformation );
349     myPixmap = QPixmap::fromImage( img );
350   }
351
352   QRect r( 0, 0, myPixmap.size().width(), myPixmap.size().height() );
353   resize( myPixmap.size() );
354   move( QApplication::desktop()->screenGeometry().center() - r.center() );
355   if ( !isVisible() )
356     drawContents();
357   else
358     repaint();
359 }
360
361 /*!
362   \brief Get the pixmap that is used as the splash screen's image.
363   \return spash screen image pixmap
364   \sa setPixmap()
365 */
366 QPixmap QtxSplash::pixmap() const
367 {
368   return myPixmap;
369 }
370
371 /*!
372   \brief Set/clear the 'hide on mouse click' flag.
373
374   When this flag is set, user can hide the splash screen window
375   by clicking on it with mouse.
376   But for this to work it is necessary to call periodically
377   QApplication::processEvents() in order to allow event loop to process
378   events because usually main application loop is not yet started
379   at that moment.
380
381   By default this flag is set to \c false.
382
383   \param on new flag state
384   \sa hideOnClick()
385 */
386 void QtxSplash::setHideOnClick( const bool on )
387 {
388   myHideOnClick = on;
389 }
390
391 /*!
392   \brief Get the 'hide on mouse click' flag.
393   \return 'hide on mouse click' flag
394   \sa setHideOnClick()
395 */
396 bool QtxSplash::hideOnClick() const
397 {
398   return myHideOnClick;
399 }
400
401 /*!
402   \brief Enable/disable displaying of the progress bar.
403   \param on if \c true, progress bar will be enabled
404   \sa progressVisible(), setMessageVisible()
405 */
406 void QtxSplash::setProgressVisible( const bool on )
407 {
408   myShowProgress = on;
409   repaint();
410 }
411
412 /*!
413   \brief Check if the progress bar is displayed.
414   \return \c true if progress bar is enabled
415   \sa setProgressVisible()
416 */
417 bool QtxSplash::progressVisible() const
418 {
419   return myShowProgress;
420 }
421
422 /*!
423   \brief Enable/disable displaying of the status message.
424   \param on if \c true, status message will be enabled
425   \sa messageVisible(), setProgressVisible()
426 */
427 void QtxSplash::setMessageVisible( const bool on )
428 {
429   myShowMessage = on;
430   repaint();
431 }
432
433 /*!
434   \brief Check if the status message is displayed.
435   \return \c true if status message is enabled
436   \sa setMessageVisible()
437 */
438 bool QtxSplash::messageVisible() const
439 {
440   return myShowMessage;
441 }
442
443 /*!
444   \brief Enable/disable displaying progress percentage.
445   \param enable if \c true, percentage will be displayed
446   \sa percentageVisible()
447 */
448 void QtxSplash::setPercentageVisible( const bool enable )
449 {
450   myShowPercent = enable;
451   repaint();
452 }
453
454 /*!
455   \brief Check if the progress percentage is displayed.
456   \return \c true if percentage displaying is enabled
457   \sa setPercentageVisible()
458 */
459 bool QtxSplash::percentageVisible() const
460 {
461   return myShowPercent;
462 }
463
464 /*!
465   \brief Set total progress steps to \a total.
466   \param total total number of progress steps
467   \sa totalSteps(), setProgress(), progress()
468 */
469 void QtxSplash::setTotalSteps( const int total )
470 {
471   myTotal = total;
472   repaint();
473 }
474
475 /*!
476   \brief Get total progress steps number.
477   \return total number of progress steps
478   \sa setTotalSteps(), setProgress(), progress()
479 */
480 int QtxSplash::totalSteps() const
481 {
482   return myTotal;
483 }
484     
485 /*!
486   \brief Set current progress.
487   \param progress current progress
488   \sa progress(), setTotalSteps(), setTotalSteps(), 
489 */
490 void QtxSplash::setProgress( const int progress )
491 {
492   myProgress = progress > 0 ? progress : 0;
493   repaint();
494 }
495
496 /*!
497   \brief Get current progress.
498   \return current progress
499   \sa setProgress(), setTotalSteps(), setTotalSteps(), 
500 */
501 int QtxSplash::progress() const
502 {
503   return myProgress;
504 }
505
506 /*!
507   \brief Set current progress to \a progress and total number of 
508   progress steps to \a total.
509   \param progress current progress
510   \param total total number of progress steps
511 */
512 void QtxSplash::setProgress( const int progress, const int total )
513 {
514   myTotal    = total;
515   myProgress = progress > 0 ? progress : 0;
516   repaint();
517 }
518
519 /*!
520   \brief Set splash window margin (a border width).
521
522   Note, that margin is used only for drawing the progress bar and status 
523   messages.
524
525   \param margin new margin width
526   \sa margin()
527 */
528 void QtxSplash::setMargin( const int margin )
529 {
530   myMargin = margin > 0 ? margin : 0;
531   repaint();
532 }
533
534 /*!
535   \brief Get splash window margin (a border width).
536   \return current margin width
537   \sa setMargin()
538 */
539 int QtxSplash::margin() const
540 {
541   return myMargin;
542 }
543
544 /*!
545   \brief Set progress bar width.
546   \param width new progress bar width
547   \sa progressWidth()
548 */
549 void QtxSplash::setProgressWidth( const int width )
550 {
551   myProgressWidth = width > 0 ? width : 0;
552   repaint();
553 }
554
555 /*!
556   \brief Get progress bar width.
557   \return current progress bar width
558   \sa setProgressWidth()
559 */
560 int QtxSplash::progressWidth() const
561 {
562   return myProgressWidth;
563 }
564
565 /*!
566   \brief Set progress bar position and direction.
567
568   By default, progress bar is displayed at the bottom side and
569   shows progress from left to right but this behaviour can be changed.
570
571   \param flags ORed progress bar flags (QtxSplash::ProgressBarFlags)
572   \sa progressFlags()
573 */
574 void QtxSplash::setProgressFlags( const int flags )
575 {
576   myProgressFlags = flags;
577   if ( !( myProgressFlags & ( LeftSide | RightSide | TopSide | BottomSide ) ) )
578     myProgressFlags |= BottomSide;
579   if ( !( myProgressFlags & ( LeftToRight | RightToLeft ) ) )
580     myProgressFlags |= LeftToRight ;
581   repaint();
582 }
583
584 /*!
585   \brief Get progress bar flags: position and direction.
586   \return ORed progress bar flags (QtxSplash::ProgressBarFlags)
587   \sa setProgressFlags()
588 */
589 int QtxSplash::progressFlags() const
590 {
591   return myProgressFlags;
592 }
593
594 /*!
595   \brief Set progress bar colors.
596
597   If the colors differ the two-colored gradient bar is drawn.
598   
599   If the \a endColor is not valid, \a startColor is used instead
600   (i.e. simple, one-colored progress bar is drawn).
601   
602   The parameter \a orientation defines the type of gradient 
603   to be drawn - horizontal or vertical. Default is vertical.
604
605   \param startColor start gradient color (or mono-color)
606   \param endColor end gradient color
607   \param orientation gradient type (Qt::Orientation)
608   \sa progressColors()
609 */
610 void QtxSplash::setProgressColors( const QColor&         startColor, 
611                                    const QColor&         endColor,
612                                    const Qt::Orientation orientation )
613 {
614   if ( !startColor.isValid() )
615     return;
616
617   QLinearGradient l;
618   if ( orientation == Qt::Vertical ) {
619     l.setStart( 0., 0. );
620     l.setFinalStop( 0., 1. );
621   }
622   else {
623     l.setStart( 0., 0. );
624     l.setFinalStop( 1., 0. );
625   }
626   l.setColorAt( 0., startColor );
627   l.setColorAt( 1., endColor.isValid() ? endColor : startColor );
628
629   setProgressColors( l );
630 }
631
632 /*!
633   \brief Set progress bar colors.
634
635   Use this method to display multi-colored gradient progress bar.
636   You have to use QLinearGradient, QRadialGradient or QConicalGradient
637   classes to define the gradient.
638
639   Note, that progress bar coordinates can be defined in absolute or
640   relative mode.
641   In absolute mode the actual coordinates of the gradient key points
642   (like start and final point for linear gradient, center and focal point
643   for radial gradient, etc) are calculated from the top-left progress bar's corner.
644   In relative mode you have to use values from 0 to 1 (including) to define
645   the key points positions.
646   
647   For example:
648   \code
649   QLinearGradient lg(0.5, 0, 1, 1);
650   lg.setColorAt(0.2, Qt::blue);
651   lg.setColorAt(0.6, Qt::red);
652   lg.setSpread(QGradient::RepeatSpread);
653   splash->setProgressGradient(lg);
654   \endcode
655   The above code creates linear gradient, which sets start stop to the
656   center of the progress bar; the final stop is assigned to its right-bottom corner.
657   The color scale (blue to red) is changed by the progress bar diagonal.
658
659   \param gradient color gradient to be used for progress bar coloring
660   \sa progressColors()
661 */
662 void QtxSplash::setProgressColors( const QGradient& gradient )
663 {
664   myGradient = gradient;
665   repaint();
666 }
667
668 /*!
669   \brief Get custom progress bar colors.
670   \return color gradient used for progress bar coloring
671   \sa setProgressColors()
672 */
673 const QGradient* QtxSplash::progressColors() const
674 {
675   return &myGradient;
676 }
677
678 /*!
679   \brief Set progress bar and status text message opacity.
680
681   The value should be in the range 0.0 to 1.0, where 0.0 is fully 
682   transparent and 1.0 is fully opaque.
683
684   \param opacity new opacity value
685   \sa opacity()
686 */
687 void QtxSplash::setOpacity( const double opacity )
688 {
689   myOpacity = opacity < 0.0 ? 0.0 : ( opacity > 1.0 ? 1.0 : opacity );
690   repaint();
691 }
692
693 /*!
694   \brief Get progress bar and status text message opacity.
695   \return current opacity value
696   \sa setOpacity()
697 */
698 double QtxSplash::opacity() const
699 {
700   return myOpacity;
701 }
702
703 /*!
704   \brief Set message text alignment flags.
705
706   Default flags are Qt::AlignBottom | Qt::AlignRight.
707
708   \param alignment alignment flags (Qt::Alignment)
709   \sa textAlignment()
710 */
711 void QtxSplash::setTextAlignment( const int alignment )
712 {
713   myAlignment = alignment;
714   repaint();
715 }
716
717 /*!
718   \brief Get message text alignment flags.
719   \return alignment flags (Qt::Alignment)
720   \sa setTextAlignment()
721 */
722 int QtxSplash::textAlignment() const
723 {
724   return myAlignment;
725 }
726
727 /*!
728   \brief Set message text colors.
729
730   If \a shadow parameter is invalid color, the simple one-colored
731   text is drawn. Otherwise, second parameter is used to draw the text
732   shadow.
733
734   \param color message text color
735   \param shadow message text shadow color
736   \sa textColors()
737 */
738 void QtxSplash::setTextColors( const QColor& color, const QColor& shadow )
739 {
740   if ( !myColor.isValid() )
741     return;
742
743   myColor = color;
744   myShadowColor = shadow;
745
746   repaint();
747 }
748
749 /*!
750   \brief Get message text colors.
751   \param color message text color
752   \param shadow message text shadow color
753   \sa setTextColors()
754 */
755 void QtxSplash::textColors( QColor& color, QColor& shadow ) const
756 {
757   color  = myColor;
758   shadow = myShadowColor;
759 }
760
761 /*!
762   \brief Set constant info text to be displayed on the splash screen.
763
764   The displayed text includes constant info and status message.
765   The constant message is set by setConstantInfo() method and status
766   message is set by setMessage().
767
768   \param info constant info text
769   \sa constantInfo(), message(), setMessage(), option(), setOption()
770 */
771 void QtxSplash::setConstantInfo( const QString& info )
772 {
773   myInfo = info;
774   repaint();
775 }
776
777 /*!
778   \brief Get constant info text.
779   \return constant info text
780   \sa setConstantInfo(), message(), setMessage()
781 */
782 QString QtxSplash::constantInfo() const
783 {
784   return myInfo;
785 }
786
787 /*!
788   \brief Set constant information option value.
789
790   The option is a part of the constant information text,
791   which is replaced at the time of the displaying.
792
793   The following options are supported:
794   - \c \%A - could be used as application name
795   - \c \%V - could be used as application version
796   - \c \%L - could be used as application license information
797   - \c \%C - could be used as application copyright information
798
799   For example,
800   \code
801   splash->setContantInfo("%A [%V]\n%C");
802   splash->setOption("%A", "MyApplication" );
803   splash->setOption("%V", "Version 1.0" );
804   splash->setOption("%C", "Copyright (C) MyCompany 2008" );
805   \endcode
806   
807   \param name option name
808   \param option value
809   \sa option(), setConstantInfo(), constantInfo()
810 */
811 void QtxSplash::setOption( const QString& name, const QString& value )
812 {
813   myOptions[ name ] = value;
814   repaint();
815 }
816
817 /*!
818   \brief Get constant information option value.
819   \param name option name
820   \return option value or empty string if option is not set
821   \sa setOption(), setConstantInfo(), constantInfo()
822 */
823 QString QtxSplash::option( const QString& name ) const
824 {
825   QString val;
826   if ( myOptions.contains( name ) )
827     val = myOptions[ name ];
828   return val;
829 }
830
831 /*!
832   \brief Get current status message.
833   \return status message
834   \sa setMessage(), constantInfo(), setConstantInfo()
835 */
836 QString QtxSplash::message() const
837 {
838   return myMessage;
839 }
840
841 /*!
842   \brief Get error code.
843
844   This function returns error code, set previously with 
845   setError() method.
846   If no error code has been set, 0 is returned.
847
848   \return last error code
849   \sa setError()
850 */
851 int QtxSplash::error() const
852 {
853   return myError;
854 }
855
856 /*!
857   \brief Wait until widget \a mainWin is displayed.
858
859   Makes the splash screen wait until the widget \a mainWin is displayed
860   and then hide and close splash window.
861
862   \param mainWin application main window 
863 */
864 void QtxSplash::finish( QWidget* mainWin )
865 {
866   if ( mainWin ) {
867 #if defined(Q_WS_X11)
868     extern void qt_x11_wait_for_window_manager(QWidget *mainWin);
869     qt_x11_wait_for_window_manager(mainWin);
870 #endif
871   }
872   close();
873 }
874
875 /*!
876   \brief Repaint the splash screen.
877 */
878 void QtxSplash::repaint()
879 {
880   drawContents();
881   QWidget::repaint();
882   QApplication::flush();
883 }
884
885 /*!
886   \brief Read splash settings from the resources manager.
887   
888   This method can be used to setup the splash screen look-n-feel.
889   By default, "splash" section of the resources file is used, but you can
890   use any other section.
891   All the splash screen parameters can be defined via resources file:
892   - \c "image" : splash screen image, see setPixmap()
893   - \c "margin" : splash window margin, see setMargin()
894   - \c "show_progress" : show progress bar flag, see setProgressVisible()
895   - \c "show_message" : show status messages flag, see setMessageVisible()
896   - \c "show_percents" : show progress percentage flag, see setPercentageVisible()
897   - \c "progress_width" : progress bar width(), see setProgressWidth()
898   - \c "progress_flags" : progress bar position and direction, see setProgressFlags()
899   - \c "constant_info" : status messages constant info, see setConstantInfo()
900   - \c "text_colors" : status messages color(s), see setTextColors()
901   - \c "progress_colors" : progress bar color(s), see setProgressColors()
902   - \c "opacity" : progress bar and status messages opacity, see setOpacity()
903   - \c "font" : status messages font
904   - \c "alignment" : status messages alignment flags, see setTextAlignment()
905   - \c "hide_on_click" : hide-on-click flag, see setHideOnClick()
906
907   \param resMgr resources manager
908   \param section resources file section name (if empty, the default "splash"
909   section is used).
910 */
911 void QtxSplash::readSettings( QtxResourceMgr* resMgr, const QString& section )
912 {
913   QString resSection = section.isEmpty() ? QString( "splash" ) : section;
914
915   // size
916   setSize( resMgr->integerValue( resSection, "width", 0 ),
917            resMgr->integerValue( resSection, "height", 0 ) );
918
919   // pixmap
920   QString pxname;
921   if ( resMgr->value( resSection, "image", pxname ) ) {
922     QPixmap px( pxname );
923     if ( !px.isNull() )
924       setPixmap( px );
925   }
926
927   // hide-on-click
928 #ifdef _DEBUG_
929   setHideOnClick( true );
930 #else
931   bool bHide;
932   if ( resMgr->value( resSection, "hide_on_click", bHide ) ) {
933     setHideOnClick( bHide );
934   }
935 #endif
936
937   // enable progress bar
938   bool bShowProgress;
939   if ( resMgr->value( resSection, "show_progress", bShowProgress ) ) {
940     setProgressVisible( bShowProgress );
941   }
942   
943   // enable status message
944   bool bShowMessage;
945   if ( resMgr->value( resSection, "show_message", bShowMessage ) ) {
946     setMessageVisible( bShowMessage );
947   }
948   
949   // margin
950   int m;
951   if ( resMgr->value( resSection, "margin", m ) ) {
952     setMargin( m );
953   }
954
955   // progress bar width
956   int pw;
957   if ( resMgr->value( resSection, "progress_width", pw ) ) {
958     setProgressWidth( pw );
959   }
960
961   // progress bar position and direction
962   QString pf;
963   if ( resMgr->value( resSection, "progress_flags", pf ) ) {
964     bool bOk;
965     int fl = pf.toInt( &bOk );
966     if ( !bOk ) {
967       fl = 0;
968       QStringList opts = pf.split( QRegExp( "," ), QString::SkipEmptyParts );
969       for ( int i = 0; i < opts.count(); i++ ) {
970         QString opt = opts[i].trimmed().toLower();
971         if ( opt == "left" )
972           fl = fl | LeftSide;
973         else if ( opt == "right" )
974           fl = fl | RightSide;
975         else if ( opt == "top" )
976           fl = fl | TopSide;
977         else if ( opt == "bottom" )
978           fl = fl | BottomSide;
979         else if ( opt == "left_to_right" )
980           fl = fl | LeftToRight;
981         else if ( opt == "right_to_left" )
982           fl = fl | RightToLeft;
983       }
984     }
985     setProgressFlags( fl );
986   }
987   
988   // opacity
989   double op;
990   if ( resMgr->value( resSection, "opacity", op ) ) {
991     setOpacity( op );
992   }
993
994   // font
995   QFont f;
996   if ( resMgr->value( resSection, "font", f ) ) {
997     setFont( f );
998   }
999
1000   // text alignment
1001   QString al;
1002   if ( resMgr->value( resSection, "alignment", al ) ) {
1003     bool bOk;
1004     int fl = al.toInt( &bOk );
1005     if ( !bOk ) {
1006       fl = 0;
1007       QStringList opts = al.split( QRegExp( "," ), QString::SkipEmptyParts );
1008       for ( int i = 0; i < opts.count(); i++ ) {
1009         QString opt = opts[i].trimmed().toLower();
1010         if ( opt == "left" )
1011           fl = fl | Qt::AlignLeft;
1012         else if ( opt == "right" )
1013           fl = fl | Qt::AlignRight;
1014         else if ( opt == "top" )
1015           fl = fl | Qt::AlignTop;
1016         else if ( opt == "bottom" )
1017           fl = fl | Qt::AlignBottom;
1018         else if ( opt == "hcenter" )
1019           fl = fl | Qt::AlignHCenter;
1020         else if ( opt == "vcenter" )
1021           fl = fl | Qt::AlignVCenter;
1022         else if ( opt == "justify" )
1023           fl = fl | Qt::AlignJustify;
1024         else if ( opt == "center" )
1025           fl = fl | Qt::AlignCenter;
1026       }
1027     }
1028     setTextAlignment( fl );
1029   }
1030   // progress color(s)
1031   QString pc;
1032   QLinearGradient  lgrad;
1033   QRadialGradient  rgrad;
1034   QConicalGradient cgrad;
1035   if ( resMgr->value( resSection, "progress_color",  lgrad ) || 
1036        resMgr->value( resSection, "progress_colors", lgrad ) ) {
1037     // linear gradient-colored progress bar
1038     setProgressColors( lgrad );
1039   }
1040   else if ( resMgr->value( resSection, "progress_color",  rgrad ) || 
1041             resMgr->value( resSection, "progress_colors", rgrad ) ) {
1042     // radial gradient-colored progress bar
1043     setProgressColors( rgrad );
1044   }
1045   else if ( resMgr->value( resSection, "progress_color",  cgrad ) || 
1046             resMgr->value( resSection, "progress_colors", cgrad ) ) {
1047     // conical gradient-colored progress bar
1048     setProgressColors( cgrad );
1049   }
1050   else if ( resMgr->value( resSection, "progress_color",  pc ) || 
1051             resMgr->value( resSection, "progress_colors", pc ) ) {
1052     // one/two-colored progress bar
1053     QStringList colors = pc.split( "|", QString::SkipEmptyParts );
1054     QColor c1, c2;
1055     Qt::Orientation o = Qt::Vertical;
1056     if ( colors.count() > 0 ) c1 = QColor( colors[0] );
1057     if ( colors.count() > 1 ) c2 = QColor( colors[1] );
1058     int gt;
1059     if ( colors.count() > 2 ) {
1060       bool bOk;
1061       gt = colors[2].toInt( &bOk );
1062       if ( bOk ) {
1063         if ( gt == 0 )
1064           o = Qt::Horizontal;
1065       }
1066       else {
1067         if ( colors[2].toLower().startsWith( "h" ) )
1068           o = Qt::Horizontal;
1069       }
1070     }
1071     setProgressColors( c1, c2, o );
1072   }
1073   // show percents
1074   bool bPercent;
1075   if ( resMgr->value( resSection, "show_percents", bPercent ) ) {
1076     setPercentageVisible( bPercent );
1077   }
1078
1079   // text color(s)
1080   QString tc;
1081   if ( resMgr->value( resSection, "text_color",  tc ) || 
1082        resMgr->value( resSection, "text_colors", tc ) ) {
1083     QStringList colors = tc.split( "|", QString::SkipEmptyParts );
1084     QColor c1, c2;
1085     if ( colors.count() > 0 )
1086       c1 = QColor( colors[0] );
1087     if ( colors.count() > 1 )
1088       c2 = QColor( colors[1] );
1089     setTextColors( c1, c2 );
1090   }
1091
1092   // const info
1093   QString cinfo;
1094   if ( resMgr->value( resSection, "constant_info", cinfo, false ) ||
1095        resMgr->value( resSection, "info", cinfo, false ) ) {
1096     setConstantInfo( cinfo.split( "|", QString::KeepEmptyParts ).join( "\n" ) );
1097   }
1098 }
1099
1100 /*!
1101   \brief Set status message for the splash screen and define its color 
1102   and aligment flags.
1103   \param msg status message
1104   \param alignment message text alignment flags (Qt::Alignment)
1105   \param color message text color
1106   \sa message(), constantInfo(), setConstantInfo()
1107 */
1108 void QtxSplash::setMessage( const QString& msg, 
1109                             int            alignment,
1110                             const QColor&  color )
1111 {
1112   myMessage   = msg;
1113   myAlignment = alignment;
1114   if ( color.isValid() )
1115     myColor = color;
1116   repaint();
1117 }
1118
1119 /*!
1120   \overload
1121   \brief Set status message for the splash screen.
1122   \param msg status message
1123   \sa message(), constantInfo(), setConstantInfo()
1124 */
1125 void QtxSplash::setMessage( const QString& msg )
1126 {
1127   myMessage = msg;
1128   repaint();
1129 }
1130
1131 /*!
1132   \brief Remove the message being displayed on the splash screen.
1133
1134   This is equivalent to setMessage("");
1135
1136   \sa message(), setMessage()
1137 */
1138 void QtxSplash::clear()
1139 {
1140   myMessage.clear();
1141   repaint();
1142 }
1143
1144 /*!
1145   \brief Draw the contents of the splash screen.
1146   \param p painter
1147 */
1148 void QtxSplash::drawContents( QPainter* p )
1149 {
1150   // draw progress bar
1151   if ( myTotal > 0 && progressVisible() ) {
1152     p->save();
1153     drawProgressBar( p );
1154     p->restore();
1155   }
1156
1157   // draw status message
1158   if ( !fullMessage().isEmpty() && messageVisible() ) {
1159     p->save();
1160     drawMessage( p );
1161     p->restore();
1162   }
1163 }
1164
1165 /*!
1166   \brief Process mouse button pressing event.
1167
1168   Hides splash screen if the 'hide on mouse click' flag is set.
1169
1170   \param me mouse event (not used)
1171   \sa hideOnClick(), setHideOnClick()
1172 */
1173 void QtxSplash::mousePressEvent( QMouseEvent* /*me*/ )
1174 {
1175   if ( myHideOnClick )
1176     hide();
1177 }
1178
1179 /*!
1180   \brief Customize paint event.
1181
1182   This function is implemented to work-around the Qt bug
1183   on some Linux distribututions when the drawing on the 
1184   splash screen widget is not allowed.
1185
1186   \param pe paint event (not used)
1187 */
1188 void QtxSplash::paintEvent( QPaintEvent* /*pe*/ )
1189 {
1190   QPainter p( this );
1191   QPixmap pix = palette().brush( backgroundRole() ).texture();
1192   p.drawPixmap( 0, 0, pix );
1193 }
1194
1195 /*!
1196   \brief Process custom event sent by setStatus() method.
1197   \param ce custom event
1198   \sa setStatus().
1199 */
1200 void QtxSplash::customEvent( QEvent* ce )
1201 {
1202   if ( ce->type() == ProgressEvent::id() ) {
1203     ProgressEvent* pe = (ProgressEvent*)ce;
1204     pe->message().isEmpty() ? clear() : setMessage( pe->message() );
1205     if ( pe->progress() >= 0 )
1206       setProgress( pe->progress() );
1207     QApplication::instance()->processEvents();
1208   }
1209 }
1210
1211 /*!
1212   \brief Check if the gradient is defined in the relative coordinates [static].
1213   \internal
1214   \return \c true if gradient is defined in the relative coordinates
1215 */
1216 static bool checkGradient( const QGradient* g )
1217 {
1218 #define BOUNDED( a, min, max ) ( a >= min && a <= max )
1219   if ( g->type() == QGradient::LinearGradient ) {
1220     const QLinearGradient* lg = static_cast<const QLinearGradient*>( g );
1221     return BOUNDED( lg->start().x(), 0.0, 1.0 ) && 
1222            BOUNDED( lg->start().y(), 0.0, 1.0 ) && 
1223            BOUNDED( lg->finalStop().x(), 0.0, 1.0 ) && 
1224            BOUNDED( lg->finalStop().y(), 0.0, 1.0 );
1225   }
1226   if ( g->type() == QGradient::RadialGradient ) {
1227     const QRadialGradient* rg = static_cast<const QRadialGradient*>( g );
1228     return BOUNDED( rg->center().x(), 0.0, 1.0 ) && 
1229            BOUNDED( rg->center().y(), 0.0, 1.0 ) && 
1230            BOUNDED( rg->focalPoint().x(), 0.0, 1.0 ) && 
1231            BOUNDED( rg->focalPoint().y(), 0.0, 1.0 ); // && BOUNDED( rg->radius(), 0.0, 1.0 );
1232   }
1233   if ( g->type() == QGradient::ConicalGradient ) {
1234     const QConicalGradient* cg = static_cast<const QConicalGradient*>( g );
1235     return BOUNDED( cg->center().x(), 0.0, 1.0 ) && 
1236            BOUNDED( cg->center().y(), 0.0, 1.0 );
1237   }
1238   return false;
1239 }
1240
1241 /*!
1242   \brief Draw progress bar.
1243   \param p painter
1244   \sa drawMessage()
1245 */
1246 void QtxSplash::drawProgressBar( QPainter* p )
1247 {
1248   // get rect, margin, progress bar width
1249   QRect r = rect();
1250   int m   = margin();
1251   int pw  = progressWidth();
1252
1253   // calculate drawing rect
1254   // ... first set default position (if none or wrong position is set)
1255   if ( myProgressFlags & BottomSide )
1256     r = QRect( r.x() + m, r.height() - (m + pw), r.width() - 2 * m, pw );
1257   else if ( myProgressFlags & TopSide )
1258     r = QRect( r.x() + m, r.y() + m, r.width() - 2 * m, pw );
1259   else if ( myProgressFlags & LeftSide )
1260     r = QRect( r.x() + m, r.y() + m, pw, r.height() - 2 * m );
1261   else if ( myProgressFlags & RightSide )
1262     r = QRect( r.width() - (m + pw), r.y() + m, pw, r.height() - 2 * m );
1263
1264   QRect cr = r;
1265   if ( myProgressFlags & TopSide || myProgressFlags & BottomSide ) {
1266     cr.setWidth( (int)( r.width() * ( myProgress > 0 ? myProgress : 0 ) / myTotal ) );
1267     if ( myProgressFlags & RightToLeft )
1268       cr.translate( r.width() - cr.width(), 0 );
1269   }
1270   else if ( myProgressFlags & LeftSide || myProgressFlags & RightSide ) {
1271     cr.setHeight( (int)( r.height() * ( myProgress > 0 ? myProgress : 0 ) / myTotal ) );
1272     if ( myProgressFlags & RightToLeft)
1273       cr.translate( 0, r.height() - cr.height() );
1274   }
1275   QBrush b;
1276   switch ( progressColors()->type() ) {
1277     case QGradient::LinearGradient:
1278     {
1279       QLinearGradient lg;
1280       const QLinearGradient* other = static_cast<const QLinearGradient*>( progressColors() );
1281       if ( checkGradient( other ) ) {
1282         // gradient is defined in relative coordinates [0.0 - 1.0]
1283         lg.setStart( r.left() + r.width()  * other->start().x(), 
1284                      r.top()  + r.height() * other->start().y() );
1285         lg.setFinalStop( r.left() + r.width()  * other->finalStop().x(), 
1286                          r.top()  + r.height() * other->finalStop().y() );
1287       }
1288       else {
1289         // gradient is defined in absolute coordinates
1290         // according to its dimensions
1291         lg.setStart( r.topLeft() + other->start() );
1292         lg.setFinalStop( r.topLeft() + other->finalStop() );
1293       }
1294       
1295       lg.setStops( other->stops() );
1296       lg.setSpread( other->spread() );
1297       
1298       b = QBrush( lg );
1299       
1300       break;
1301     } // case QGradient::LinearGradient
1302     case QGradient::RadialGradient:
1303     {
1304       QRadialGradient rg;
1305       const QRadialGradient* other = static_cast<const QRadialGradient*>( progressColors() );
1306       if ( checkGradient( other ) ) {
1307         // gradient is defined in relative coordinates [0.0 - 1.0]
1308         rg.setCenter( r.left() + r.width()  * other->center().x(),
1309                       r.top()  + r.height() * other->center().y() );
1310         rg.setFocalPoint( r.left() + r.width()  * other->focalPoint().x(),
1311                           r.top()  + r.height() * other->focalPoint().y() );
1312       }
1313       else {
1314         // gradient is defined in absolute coordinates
1315         // according to its dimensions
1316         rg.setCenter( r.topLeft() + other->center() );
1317         rg.setFocalPoint( r.topLeft() + other->focalPoint() );
1318       }
1319       
1320       // only width is taken into account for the radius in relative mode
1321       rg.setRadius( other->radius() > 1.0 ? other->radius() : r.width() * other->radius() );
1322       
1323       rg.setStops( other->stops() );
1324       rg.setSpread( other->spread() );
1325       
1326       b = QBrush( rg );
1327       
1328       break;
1329     } // case QGradient::RadialGradient
1330     case QGradient::ConicalGradient:
1331     {
1332       QConicalGradient cg;
1333       const QConicalGradient* other = static_cast<const QConicalGradient*>( progressColors() );
1334       if ( checkGradient( other ) ) {
1335         // gradient is defined in relative coordinates [0.0 - 1.0]
1336         cg.setCenter( r.left() + r.width()  * other->center().x(),
1337                       r.top()  + r.height() * other->center().y() );
1338       }
1339       else {
1340         // gradient is defined in absolute coordinates
1341         // according to its dimensions
1342         cg.setCenter( r.topLeft() + other->center() );
1343       }
1344
1345       cg.setAngle( other->angle() );
1346       cg.setStops( other->stops() );
1347       cg.setSpread( other->spread() );
1348       
1349       b = QBrush( cg );
1350       
1351       break;
1352     } // case QGradient::RadialGradient
1353   default:
1354     b = QBrush( Qt::red ); // default is simple red-colored progress bar
1355     break;
1356   }
1357   
1358   p->setOpacity( myOpacity );
1359
1360   // draw progress bar outline rectangle
1361   p->setPen( palette().color( QPalette::Dark ) );
1362   p->drawLine( r.left(), r.top(), r.right(), r.top() );
1363   p->drawLine( r.left(), r.top(), r.left(), r.bottom() );
1364   p->setPen( palette().color( QPalette::Light ) );
1365   p->drawLine( r.left(), r.bottom(), r.right(), r.bottom() );
1366   p->drawLine( r.right(), r.top(), r.right(), r.bottom() );
1367
1368   r.setCoords( r.left()+1, r.top()+1, r.right()-1, r.bottom()-1 );
1369   p->setClipRect( cr );
1370   p->fillRect( r, b );
1371   p->setClipping( false );
1372
1373   if ( myShowPercent ) {
1374     int percent = ( int )( ( myProgress > 0 ? myProgress : 0 ) * 100 / myTotal );
1375     QFont f = font();
1376     f.setPixelSize( r.height() - 4 );
1377     p->setFont( f );
1378     // draw shadow status text
1379     if ( myShadowColor.isValid() ) {
1380       QRect rs = r;
1381       rs.moveTopLeft( rs.topLeft() + QPoint( 1,1 ) );
1382       p->setPen( myShadowColor );
1383       p->drawText( rs, Qt::AlignCenter, QString( "%1%" ).arg( percent ) );
1384     }
1385     p->setPen( myColor );
1386     p->drawText( r, Qt::AlignCenter, QString( "%1%" ).arg( percent ) );
1387   }
1388 }
1389
1390 /*!
1391   \brief Draw status message.
1392   \param p painter
1393   \sa drawProgressBar()
1394 */
1395 void QtxSplash::drawMessage( QPainter* p )
1396 {
1397   // get rect, margin, progress bar width
1398   QRect r = rect();
1399   int m   = margin();
1400   int pw  = progressVisible() ? progressWidth() : 0;
1401
1402   // calculate drawing rect
1403   QFontMetrics f( font() );
1404   int spacing = f.lineSpacing();
1405   // ... base rect
1406   QRect r1( r.x() + m, r.y() + m, r.width() - 2 * m, r.height() - 2 * m );
1407   r1.setY( r1.y() - f.leading() );
1408   // ... take into account progress bar
1409   if ( 1 ) {              // if ( myTotal > 0 ) : vsr changed: otherwise text is jumping
1410     if ( myProgressFlags & BottomSide )
1411       r1.setHeight( r1.height() - pw );
1412     else if ( myProgressFlags & TopSide )
1413       r1.setY( r1.y() + pw );
1414     else if ( myProgressFlags & LeftSide )
1415       r1.setX( r1.x() + pw );
1416     else if ( myProgressFlags & RightSide )
1417       r1.setWidth( r1.width() - pw );
1418   }
1419   
1420   // ... take into account trailing '\n' symbols
1421   int shift = 0;
1422   QString msg = fullMessage();
1423   int i = msg.length() - 1;
1424   while( i >= 0 && msg[ i-- ] == '\n' )
1425     shift += spacing;
1426   r1.setHeight( r1.height() - shift );
1427
1428   p->setOpacity( myOpacity );
1429
1430   // draw shadow status text
1431   if ( myShadowColor.isValid() ) {
1432     QRect r2 = r1;
1433     if ( myAlignment & Qt::AlignLeft   ) r2.setLeft  ( r2.left()   + 1 );
1434     if ( myAlignment & Qt::AlignTop    ) r2.setTop   ( r2.top()    + 1 );
1435     if ( myAlignment & Qt::AlignRight  ) r2.setRight ( r2.right()  + 1 );
1436     if ( myAlignment & Qt::AlignBottom ) r2.setBottom( r2.bottom() + 1 );
1437     p->setPen( myShadowColor );
1438     p->drawText( r2, myAlignment, msg );
1439   }
1440
1441   // draw foreground status text
1442   p->setPen( myColor );
1443   p->drawText( r1, myAlignment, msg );
1444 }
1445
1446 /*!
1447   \brief Draw the splash screen window contents.
1448   \internal
1449 */
1450 void QtxSplash::drawContents()
1451 {
1452   QPixmap textPix = myPixmap;
1453   QPainter painter( &textPix );
1454   painter.initFrom( this );
1455   drawContents( &painter );
1456   QPalette pal = palette();
1457   pal.setBrush( backgroundRole(), QBrush( textPix ) );
1458   setPalette( pal );
1459 }
1460
1461 /*!
1462   \brief Sets error code.
1463   \param code error code
1464   \internal
1465 */
1466 void QtxSplash::setError( const int code )
1467 {
1468   myError = code;
1469 }
1470
1471 /*!
1472   \brief Get full message which includes constant info and status message.
1473   \return get fill message text
1474   \sa constantInfo(), setConstantInfo(), message(), setMessage()
1475   \internal
1476 */
1477 QString QtxSplash::fullMessage() const
1478 {
1479   QStringList info;
1480
1481   QString cinfo = myInfo;
1482   cinfo = cinfo.replace( QRegExp( "%A" ), option( "%A" ) );
1483   cinfo = cinfo.replace( QRegExp( "%V" ), option( "%V" ) );
1484   cinfo = cinfo.replace( QRegExp( "%L" ), option( "%L" ) );
1485   cinfo = cinfo.replace( QRegExp( "%C" ), option( "%C" ) );
1486
1487   if ( !cinfo.isEmpty() )
1488     info << cinfo;
1489   if ( !myMessage.isEmpty() )
1490     info << myMessage;
1491   return info.join( "\n" );
1492 }