]> SALOME platform Git repositories - modules/gui.git/blob - src/Qtx/QtxMainWindow.cxx
Salome HOME
d8aa1e33f99d82cd82435a4666f04d8a7bd7b83c
[modules/gui.git] / src / Qtx / QtxMainWindow.cxx
1 // Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 //  File:      QtxMainWindow.cxx
23 //  Author:    Sergey TELKOV
24
25 #include "QtxMainWindow.h"
26
27 #include "QtxToolBar.h"
28
29 #include <QEvent>
30 #include <QPoint>
31 #include <QTimer>
32 #include <QLayout>
33 #include <QMenuBar>
34 #include <QStatusBar>
35 #include <QRubberBand>
36 #include <QMouseEvent>
37 #include <QApplication>
38 #include <QDesktopWidget>
39 #include <cstdio>
40 /*!
41   \class QtxMainWindow::Filter
42   \internal
43   \brief Internal object used to filter child removal events for 
44          specified widget from parent widget.
45 */
46
47 class QtxMainWindow::Filter : public QObject
48 {
49 public:
50   Filter( QWidget*, QtxMainWindow*, QObject* = 0 );
51   virtual ~Filter();
52
53   virtual bool eventFilter( QObject*, QEvent* );
54
55 private:
56   QMainWindow* myMain;      //!< parent main window
57   QWidget*     myWidget;    //!< widget being watched
58 };
59
60 /*!
61   \brief Constructor.
62   \param wid widget to be watched
63   \param mw parent main window
64   \param parent parent object (in terms of QObject)
65 */
66 QtxMainWindow::Filter::Filter( QWidget* wid, QtxMainWindow* mw, QObject* parent )
67 : QObject( parent ),
68   myMain( mw ),
69   myWidget( wid )
70 {
71   QApplication::instance()->installEventFilter( this );
72 };
73
74 /*!
75   \brief Destructor.
76 */
77 QtxMainWindow::Filter::~Filter()
78 {
79 }
80
81 /*!
82   \brief Event filter.
83
84   Watches for the specified widget and prevents its removal from the
85   parent main window.
86
87   \param o recevier object
88   \param e event
89 */
90 bool QtxMainWindow::Filter::eventFilter( QObject* o, QEvent* e )
91 {
92   if ( myMain == o && e->type() == QEvent::ChildRemoved &&
93        myWidget == ((QChildEvent*)e)->child() )
94     return true;
95
96   return QObject::eventFilter( o, e );
97 }
98
99 /*!
100   \class QtxMainWindow::Resizer
101   \internal
102   \brief Internal object used to resize dock widgets.
103 */
104
105 class QtxMainWindow::Resizer : public QObject
106 {
107 public:
108   Resizer( const QPoint&, const Qt::Orientation, QtxMainWindow* );
109   virtual ~Resizer();
110
111   QMouseEvent*    finalEvent() const;
112   void            setFinalEvent( QMouseEvent* );
113
114   void            setPosition( const QPoint& );
115   virtual bool    eventFilter( QObject*, QEvent* );
116
117 private:
118   void            setFilters( bool );
119
120 private:
121   QPoint          myPos;
122   QMainWindow*    myMain;
123   QRubberBand*    myRubber;
124   Qt::Orientation myOrient;
125   QMouseEvent*    myFinEvent;
126 };
127
128 /*!
129   \brief Constructor.
130   \param mw parent main window
131 */
132 QtxMainWindow::Resizer::Resizer( const QPoint& p, const Qt::Orientation o, QtxMainWindow* mw )
133 : QObject( mw ),
134   myMain( mw ),
135   myOrient( o ),
136   myFinEvent( 0 )
137 {
138   setFilters( true );
139
140   myRubber = new QRubberBand( QRubberBand::Line, 0 );
141
142   setPosition( p );
143
144   myRubber->hide();
145 };
146
147 /*!
148   \brief Destructor.
149 */
150 QtxMainWindow::Resizer::~Resizer()
151 {
152   delete myRubber;
153
154   setFilters( false );
155 }
156
157 void QtxMainWindow::Resizer::setPosition( const QPoint& pos )
158 {
159   myPos = pos;
160   if ( myRubber ) {
161     QRect r;
162     QPoint min = myMain->mapToGlobal( myMain->rect().topLeft() );
163     QPoint max = myMain->mapToGlobal( myMain->rect().bottomRight() );
164     if ( myOrient == Qt::Horizontal ) {
165       int p = qMax( qMin( myPos.y(), max.y() ), min.y() );
166       r = QRect( myMain->mapToGlobal( QPoint( 0, 0 ) ).x(), p - 1, myMain->width(), 3 );
167     }
168     else {
169       int p = qMax( qMin( myPos.x(), max.x() ), min.x() );
170       r = QRect( p - 1, myMain->mapToGlobal( QPoint( 0, 0 ) ).y(), 3, myMain->height() );
171     }
172     myRubber->setGeometry( r );
173     if ( !myRubber->isVisible() )
174       myRubber->show();
175   }
176 }
177
178 QMouseEvent* QtxMainWindow::Resizer::finalEvent() const
179 {
180   return myFinEvent;
181 }
182
183 void QtxMainWindow::Resizer::setFinalEvent( QMouseEvent* e )
184 {
185   myFinEvent = e;
186 }
187
188 /*!
189   \brief Event filter.
190
191   \param o recevier object
192   \param e event
193 */
194 bool QtxMainWindow::Resizer::eventFilter( QObject* o, QEvent* e )
195 {
196   if ( e->type() == QEvent::Timer ) {
197     if ( !finalEvent() )
198       return true;
199
200     setFilters( false );
201     QApplication::postEvent( myMain, finalEvent() );
202     myFinEvent = 0;
203     deleteLater();
204   }
205
206   return QObject::eventFilter( o, e );
207 }
208
209 void QtxMainWindow::Resizer::setFilters( bool on )
210 {
211   if ( myMain ) {
212     if ( on )
213       myMain->layout()->installEventFilter( this );
214     else
215       myMain->layout()->removeEventFilter( this );
216   }
217
218   QTimer* t = qFindChild<QTimer*>( myMain->layout() );
219   if ( t ) {
220     if ( on )
221       t->installEventFilter( this );
222     else
223       t->removeEventFilter( this );
224   }
225 }
226
227 /*!
228   \class QtxMainWindow
229   \brief Enhanced main window which supports dockable menubar and status bar
230          plus geometry saving/restoring.
231 */
232
233 /*!
234   \brief Constructor.
235   \param parent parent widget
236   \param f widget flags (Qt::WindowFlags)
237 */
238 QtxMainWindow::QtxMainWindow( QWidget* parent, Qt::WindowFlags f )
239 : QMainWindow( parent, f ),
240   myMenuBar( 0 ),
241   myStatusBar( 0 ),
242   myOpaque( true ),
243   myResizer( 0 ),
244   myMouseMove( 0 ),
245   myFullScreenAllowed(true)
246 {
247   //rnv: Enables tooltips for inactive windows.
248   //rnv: For details see http://bugtracker.opencascade.com/show_bug.cgi?id=20893
249   setAttribute(Qt::WA_AlwaysShowToolTips);
250 }
251
252 /*!
253   \brief Destructor.
254 */
255 QtxMainWindow::~QtxMainWindow()
256 {
257   setDockableMenuBar( false );
258   setDockableStatusBar( false );
259 }
260
261 /*!
262   \brief Check if the menu bar is dockable.
263   \return \c true if dockable menu bar exists
264 */
265 bool QtxMainWindow::isDockableMenuBar() const
266 {
267   return myMenuBar != 0;
268 }
269
270 /*!
271   \brief Set menu bar dockable/undockable.
272   \param on if \c true, make menu bar dockable, otherwise 
273             make menu bar undockable
274 */
275 void QtxMainWindow::setDockableMenuBar( const bool on )
276 {
277   if ( isDockableMenuBar() == on )
278     return;
279
280   QMenuBar* mb = menuBar();
281   if ( !mb )
282     return;
283
284   if ( on && !myMenuBar )
285   {
286     myMenuBar = new QtxToolBar( true, this );
287     new Filter( mb, this, myMenuBar );
288     myMenuBar->setObjectName( "menu_bar_container" );
289     myMenuBar->setWindowTitle( tr( "Menu bar" ) );
290     myMenuBar->addWidget( mb );
291     myMenuBar->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea );
292
293     addToolBarBreak( Qt::TopToolBarArea );
294     addToolBar( Qt::TopToolBarArea, myMenuBar );
295     addToolBarBreak( Qt::TopToolBarArea );
296
297     connect( myMenuBar, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
298   }
299   else if ( !on && myMenuBar )
300   {
301     setMenuBar( mb );
302     disconnect( myMenuBar, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
303     delete myMenuBar;
304     myMenuBar = 0;
305   }
306 }
307
308 /*!
309   \brief Check if the status bar is dockable.
310   \return \c true if dockable status bar exists
311 */
312 bool QtxMainWindow::isDockableStatusBar() const
313 {
314   return myStatusBar;
315 }
316
317 /*!
318   \brief Set status bar dockable/undockable.
319   \param on if \c true, make status bar dockable, otherwise 
320             make status bar undockable
321 */
322 void QtxMainWindow::setDockableStatusBar( const bool on )
323 {
324   if ( isDockableStatusBar() == on )
325     return;
326
327   QStatusBar* sb = statusBar();
328   if ( !sb )
329     return;
330
331   if ( on && !myStatusBar )
332   {
333     sb->setMinimumWidth( 250 );
334     sb->setSizeGripEnabled( false );
335     myStatusBar = new QtxToolBar( true, this );
336     new Filter( sb, this, myStatusBar );
337     myStatusBar->setObjectName( "status_bar_container" );
338     myStatusBar->setWindowTitle( tr( "Status bar" ) );
339     myStatusBar->addWidget( sb );
340     myStatusBar->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea );
341
342     addToolBar( Qt::BottomToolBarArea, myStatusBar );
343
344     connect( myStatusBar, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
345   }
346   else if ( !on && myStatusBar )
347   {
348     setStatusBar( sb );
349     disconnect( myStatusBar, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
350     delete myStatusBar;
351     myStatusBar = 0;
352
353     sb->setSizeGripEnabled( true );
354   }
355 }
356
357 bool QtxMainWindow::isOpaqueResize() const
358 {
359   return myOpaque;
360 }
361
362 void QtxMainWindow::setOpaqueResize( bool on )
363 {
364   myOpaque = on;
365 }
366
367 /*!
368   \brief Dump main window geometry to the string.
369   \return string represenation of the window geometry
370 */
371 QString QtxMainWindow::storeGeometry() const
372 {
373   QRect frame = frameGeometry();
374   QRect screen = QApplication::desktop()->availableGeometry( this );
375
376   QString x;
377   if ( frame.left() == screen.left() )
378     x = QString( "+0" );
379   else if ( frame.right() == screen.right() )
380     x = QString( "-0" );
381   else
382     x = QString( "+%1" ).arg( frame.left() );
383
384   QString y;
385   if ( frame.top() == screen.top() )
386     y = QString( "+0" );
387   else if ( frame.bottom() == screen.bottom() )
388     y = QString( "-0" );
389   else
390     y = QString( "+%1" ).arg( frame.top() );
391
392   QString geom = QString( "%1x%2%3%4" ).arg( width() ).arg( height() ).arg( x ).arg( y );
393
394   QString state;
395   switch ( windowState() )
396   {
397   case Qt::WindowMaximized:
398     state = QString( "max" );
399     break;
400   case Qt::WindowMinimized:
401     state = QString( "min" );
402     break;
403   case Qt::WindowFullScreen:
404     state = isFullScreenAllowed() ? QString( "full" ) : QString( "max" );
405     break;
406   }
407
408   if ( !state.isEmpty() )
409     geom += QString( ":" ) + state;
410
411   return geom;
412 }
413
414 /*!
415   \brief Restore main window geometry from the string.
416   \param str string represenation of the window geometry
417 */
418 void QtxMainWindow::retrieveGeometry( const QString& str )
419 {
420   QString geom = str;
421   geom.remove( '\t' );
422   geom.remove( ' ' );
423
424   QRect rect = geometry();
425   QRect screen = QApplication::desktop()->availableGeometry( this );
426
427   QRegExp szRx( "(\\d+%?)\\s*x\\s*(\\d+%?)" );
428   if ( szRx.indexIn( geom ) != -1 )
429   {
430     int w = -1;
431     bool wp = false;
432     int ws = geometryValue( szRx.cap( 1 ).trimmed(), w, wp );
433     bool wOk = ws != 0;
434     if ( wOk && wp )
435       w = screen.width() * qMax( qMin( w, 100 ), 0 ) / 100;
436     wOk = wOk && w;
437
438     int h = -1;
439     bool hp = false;
440     int hs = geometryValue( szRx.cap( 2 ).trimmed(), h, hp );
441     bool hOk = hs != 0;
442     if ( hOk && hp )
443       h = screen.height() * qMax( qMin( h, 100 ), 0 ) / 100;
444     hOk = hOk && h;
445
446     if ( wOk && hOk )
447       rect.setSize( QSize( w, h ) );
448   }
449
450   QRegExp posRx( "([+|-]\\d+%?)\\s*([+|-]\\d+%?)" );
451   if ( posRx.indexIn( geom ) != -1 )
452   {
453     int x = -1;
454     bool xp = false;
455     int xs = geometryValue( posRx.cap( 1 ).trimmed(), x, xp );
456     bool xOk = xs != 0;
457     if ( xOk )
458     {
459       if ( xp )
460         x = screen.width() * qMax( qMin( x, 100 ), 0 ) / 100;
461       x = ( xs > 0 ? x : screen.right() - x - rect.width() ) + frameGeometry().x() - geometry().x();
462     }
463
464     int y = -1;
465     bool yp = false;
466     int ys = geometryValue( posRx.cap( 2 ).trimmed(), y, yp );
467     bool yOk = ys != 0;
468     if ( yOk )
469     {
470       if ( yp )
471         y = screen.height() * qMax( qMin( y, 100 ), 0 ) / 100;
472       y = ( ys > 0 ? y : screen.bottom() - y - rect.height() ) + frameGeometry().y() - geometry().y();
473     }
474
475     if ( xOk && yOk )
476       rect.moveTo( x, y );
477   }
478
479   Qt::WindowState state = Qt::WindowNoState;
480
481   QRegExp stRx( ":(\\w+)" );
482   if ( stRx.indexIn( geom ) != -1 )
483   {
484     QString stStr = stRx.cap( 1 ).trimmed().toLower();
485     if ( stStr.startsWith( QString( "max" ) ) )
486       state = Qt::WindowMaximized;
487     else if ( stStr.startsWith( QString( "min" ) ) )
488       state = Qt::WindowMinimized;
489     else if ( stStr.startsWith( QString( "full" ) ) )
490       state = Qt::WindowFullScreen;
491   }
492
493   resize( rect.size() );
494   move( rect.topLeft() );
495
496   if ( state != Qt::WindowNoState )
497     setWindowState( state );
498 }
499
500 /*!
501   \brief Retrieve numerical value from the string.
502   
503   Numerical value in the string have the structure [+|-]\d*[%],
504   that is one or more digits which can start from "+" or "-" and
505   can end with "%" symbol.
506
507   \param str string being converted
508   \param num returning value (> 0)
509   \param percent if string ends with "%" this parameter is equal to \c true after
510          returning from the function
511   \return -1 if value < 0, 1 if value > 0 and 0 in case of error
512 */
513 int QtxMainWindow::geometryValue( const QString& str, int& num, bool& percent ) const
514 {
515   num = -1;
516   int res = 1;
517   QString numStr = str;
518   if ( numStr.startsWith( "+" ) || numStr.startsWith( "-" ) )
519   {
520     res = numStr.startsWith( "+" ) ? 1 : -1;
521     numStr = numStr.mid( 1 );
522   }
523
524   percent = numStr.endsWith( "%" );
525   if ( percent )
526     numStr = numStr.mid( 0, numStr.length() - 1 );
527
528   bool ok = false;
529   num = numStr.toInt( &ok );
530   if ( !ok )
531     res = 0;
532
533   return res;
534 }
535
536 /*!
537   \brief Called when child object (menu bar, status bar) is destroyed.
538   
539   Clears internal pointer to prevent crashes.
540
541   \param obj signal sender (object being destroyed)
542 */
543 void QtxMainWindow::onDestroyed( QObject* obj )
544 {
545   QObject* o = 0;
546   if ( obj == myMenuBar )
547   {
548     myMenuBar = 0;
549     o = menuBar();
550   }
551   else if ( obj == myStatusBar )
552   {
553     myStatusBar = 0;
554     o = statusBar();
555   }
556
557   if ( o )
558   {
559     QChildEvent ce( QEvent::ChildRemoved, o );
560     QApplication::sendEvent( this, &ce );
561   }
562 }
563
564 bool QtxMainWindow::event( QEvent* e )
565 {
566 //   if ( e->type() == QEvent::WindowDeactivate ) {
567 //     printf( "----------------> Deactivated\n" );
568 //   }
569
570   if ( myResizer ) {
571     QMouseEvent* me = static_cast<QMouseEvent*>( e );
572     if ( ( e->type() == QEvent::MouseButtonRelease && me->button() == Qt::LeftButton ) || 
573          ( e->type() == QEvent::MouseButtonPress && me->button() != Qt::LeftButton ) ) {
574       if ( me->button() == Qt::LeftButton ) {
575         if ( myMouseMove ) {
576           QMainWindow::event( myMouseMove );
577           delete myMouseMove;
578           myMouseMove = 0;
579         }
580
581         QMouseEvent* me = static_cast<QMouseEvent*>( e );
582         myResizer->setFinalEvent( new QMouseEvent( QEvent::MouseButtonRelease, me->pos(), me->globalPos(),
583                                                    Qt::LeftButton, me->buttons(), me->modifiers() ) );
584         myResizer = 0;
585         return true;
586       }
587     }
588   }
589
590   if ( myResizer && e->type() == QEvent::MouseMove ) {
591     QMouseEvent* me = static_cast<QMouseEvent*>( e );
592     if ( myMouseMove )
593       delete myMouseMove;
594     myMouseMove = new QMouseEvent( me->type(), me->pos(), me->globalPos(),
595                                    me->button(), me->buttons(), me->modifiers() );
596     myResizer->setPosition( me->globalPos() );
597   }
598
599   bool ok = QMainWindow::event( e );
600
601   if ( !myResizer && e->type() == QEvent::MouseButtonPress ) {
602     QMouseEvent* me = static_cast<QMouseEvent*>( e );
603     if ( !isOpaqueResize() && ok && testAttribute( Qt::WA_SetCursor ) && me->button() == Qt::LeftButton ) {
604       bool status = true;
605       Qt::Orientation o;
606       switch ( cursor().shape() )
607         {
608         case Qt::SplitHCursor:
609           o = Qt::Vertical;
610           break;
611         case Qt::SplitVCursor:
612           o = Qt::Horizontal;
613           break;
614         default:
615           status = false;
616           break;
617         }
618       if ( status ) {
619         myResizer = new Resizer( me->globalPos(), o, this );
620         myMouseMove = new QMouseEvent( me->type(), me->pos(), me->globalPos(),
621                                        me->button(), me->buttons(), me->modifiers() );
622       }
623     }
624   }
625
626   return ok;
627 }
628
629 /*!
630   \brief FullScreenAllowed flag allowed dump in the main window geometry 
631          Qt::WindowFullScreen parameter.
632   \return \c fullScreenAllowed flag.
633 */
634 bool QtxMainWindow::isFullScreenAllowed() const {
635   return myFullScreenAllowed;
636 }
637
638
639 /*!
640   \brief Set FullScreenAllowed flag.
641          The default value is true.
642   \param f value of the fullScreenAllowed flag.
643 */
644 void QtxMainWindow::setFullScreenAllowed( const bool f ) {
645     myFullScreenAllowed = f;
646 }