Salome HOME
Updated copyright comment
[modules/gui.git] / src / Qtx / QtxDockWidget.cxx
1 // Copyright (C) 2007-2024  CEA, EDF, 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:      QtxDockWidget.cxx
24 // Author:    Sergey TELKOV
25 //
26 #include "QtxDockWidget.h"
27
28 #include <QAction>
29 #include <QLayout>
30 #include <QMainWindow>
31 #include <QResizeEvent>
32 #include <QApplication>
33
34 /*!
35   \class QtxDockWidget::Watcher
36   \internal
37   \brief Internal class which goal is to watch parent dockable widget state changing.
38 */
39
40 class QtxDockWidget::Watcher : public QObject
41 {
42 public:
43   Watcher( QtxDockWidget* );
44
45   void           shown( QtxDockWidget* );
46   void           hidden( QtxDockWidget* );
47
48   virtual bool   eventFilter( QObject*, QEvent* );
49
50   bool           isEmpty() const;
51   bool           isVisible() const;
52
53 protected:
54   enum { Update = QEvent::User, Remove };
55
56   virtual void   customEvent( QEvent* );
57
58 private:
59   void           installFilters();
60
61   void           showContainer();
62   void           hideContainer();
63
64   void           updateIcon();
65   void           updateCaption();
66   void           updateVisibility();
67
68   void           setEmpty( const bool );
69   void           setVisible( const bool );
70
71 private:
72   QtxDockWidget* myCont;
73   bool           myState;
74   bool           myEmpty;
75   bool           myBlock;
76   bool           myShown;
77 };
78
79 /*!
80   \brief Constructor.
81   \param cont dockable widget to be watched
82 */
83 QtxDockWidget::Watcher::Watcher( QtxDockWidget* cont )
84 : QObject( cont ), myCont( cont ),
85   myState( true ),
86   myEmpty( false ),
87   myBlock( false )
88 {
89   myCont->installEventFilter( this );
90
91   installFilters();
92
93   myShown = myCont->isVisibleTo( myCont->parentWidget() );
94 }
95
96 /*!
97   \brief Custom event filter.
98   \param o event receiver object
99   \param e event sent to object
100   \return \c true if further event processing should be stopped
101 */
102 bool QtxDockWidget::Watcher::eventFilter( QObject* o, QEvent* e )
103 {
104   if ( o == myCont && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ||
105                         e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
106   {
107     installFilters();
108   }
109
110   if ( o == myCont && e->type() == QEvent::ChildAdded )
111   {
112     QChildEvent* ce = (QChildEvent*)e;
113     if ( ce->child()->isWidgetType() )
114       ce->child()->installEventFilter( this );
115
116     QApplication::postEvent( this, new QEvent( (QEvent::Type)Update ) );
117   }
118
119   if ( o != myCont && e->type() == QEvent::WindowIconChange )
120     updateIcon();
121
122   if ( o != myCont && e->type() == QEvent::WindowTitleChange )
123     updateCaption();
124
125   if ( o != myCont && ( e->type() == QEvent::HideToParent || e->type() == QEvent::ShowToParent ) )
126     updateVisibility();
127
128   if ( o == myCont && e->type() == QEvent::ChildRemoved )
129   {
130     QApplication::postEvent( this, new QEvent( (QEvent::Type)Remove ) );
131   }
132
133   return false;
134 }
135
136 /*!
137   \brief Set internal status to "shown"
138   \param dw dockable widget
139 */
140 void QtxDockWidget::Watcher::shown( QtxDockWidget* dw )
141 {
142   if ( dw != myCont )
143     return;
144
145   setVisible( true );
146 }
147
148 /*!
149   \brief Set internal status to "hidden"
150   \param dw dockable widget
151 */
152 void QtxDockWidget::Watcher::hidden( QtxDockWidget* dw )
153 {
154   if ( dw != myCont )
155     return;
156
157   setVisible( false );
158 }
159
160 bool QtxDockWidget::Watcher::isEmpty() const
161 {
162   return myEmpty;
163 }
164
165 bool QtxDockWidget::Watcher::isVisible() const
166 {
167   return myShown;
168 }
169
170 void QtxDockWidget::Watcher::setEmpty( const bool on )
171 {
172   myEmpty = on;
173 }
174
175 void QtxDockWidget::Watcher::setVisible( const bool on )
176 {
177   myShown = on;
178 }
179
180 /*!
181   \brief Show the dock window being watched
182 */
183 void QtxDockWidget::Watcher::showContainer()
184 {
185   if ( !myCont )
186     return;
187
188   bool vis = isVisible();
189
190   QtxDockWidget* cont = myCont;
191   myCont = 0;
192   cont->show();
193   myCont = cont;
194
195   setVisible( vis );
196 }
197
198 /*!
199   \brief Hide the dock window being watched
200 */
201 void QtxDockWidget::Watcher::hideContainer()
202 {
203   if ( !myCont )
204     return;
205
206   bool vis = isVisible();
207
208   QtxDockWidget* cont = myCont;
209   myCont = 0;
210   cont->hide();
211   myCont = cont;
212
213   setVisible( vis );
214 }
215
216 /*!
217   \brief Proces custom events.
218   \param e custom event (not used)
219 */
220 void QtxDockWidget::Watcher::customEvent( QEvent* e )
221 {
222   if ( e->type() == (QEvent::Type)Update )
223   {
224     updateIcon();
225     updateCaption();
226     updateVisibility();
227   }
228   else if ( myCont && e->type() == (QEvent::Type)Remove && !myCont->widget() )
229   {
230     myCont->deleteLater();
231     myCont = 0;
232   }
233 }
234
235 /*!
236   \brief Install this object as event filter to all children widgets
237          of the dockable widget being watched.
238 */
239 void QtxDockWidget::Watcher::installFilters()
240 {
241   if ( !myCont )
242     return;
243
244   QLayout* l = myCont->layout();
245   if ( !l )
246     return;
247
248   for ( int i = 0; i < (int)l->count(); i++ )
249   {
250     if ( l->itemAt( i ) && l->itemAt( i )->widget() )
251       l->itemAt( i )->widget()->installEventFilter( this );
252   }
253 }
254
255 /*!
256   \brief Update visibility state of all children widgets of the dockable widget
257          being watched.
258 */
259 void QtxDockWidget::Watcher::updateVisibility()
260 {
261   if ( !myCont )
262     return;
263
264   bool vis = false;
265   if ( myCont->widget() )
266     vis = myCont->widget()->isVisibleTo( myCont );
267   else
268   {
269     QLayout* l = myCont->layout();
270     if ( l )
271     {
272       for ( int i = 0; i < (int)l->count() && !vis; i++ )
273         vis = l->itemAt( i ) && l->itemAt( i )->widget() && l->itemAt( i )->widget()->isVisibleTo( myCont );
274     }
275   }
276
277   bool empty = isEmpty();
278   if ( empty == vis )
279   {
280     empty = !vis;
281     setEmpty( empty );
282     if ( !empty )
283       myCont->toggleViewAction()->setVisible( myState );
284     else
285     {
286       myState = myCont->toggleViewAction()->isVisible();
287       myCont->toggleViewAction()->setVisible( false );
288     }
289   }
290
291   vis = !empty && isVisible();
292   if ( vis != myCont->isVisibleTo( myCont->parentWidget() ) )
293     vis ? showContainer() : hideContainer();
294 }
295
296 /*!
297   \brief Update the icon of dockable window being watched
298 */
299 void QtxDockWidget::Watcher::updateIcon()
300 {
301   if ( !myCont || !myCont->widget() || myBlock )
302     return;
303
304   myBlock = true;
305   myCont->setWindowIcon( myCont->widget()->windowIcon() );
306   myBlock = false;
307 }
308
309 /*!
310   \brief Update the title of dockable window being watched
311 */
312 void QtxDockWidget::Watcher::updateCaption()
313 {
314   if ( myCont && myCont->widget() && !myCont->widget()->windowTitle().isNull() )
315     myCont->setWindowTitle( myCont->widget()->windowTitle() );
316 }
317
318 /*!
319   \class QtxDockWidget
320   \brief Enhanced dockable widget class.
321 */
322
323 /*!
324   \brief Constructor.
325   \param title dockable widget title
326   \param parent parent widget
327   \param f widget flags
328 */
329 QtxDockWidget::QtxDockWidget( const QString& title, QWidget* parent, Qt::WindowFlags f )
330 : QDockWidget( title, parent, f ),
331   myWatcher( 0 ),
332   myOrientation( (Qt::Orientation)-1 )
333 {
334   updateState();
335 }
336
337 /*!
338   \brief Constructor.
339   \param watch if \c true the event filter is installed to watch wigdet state changes
340          to update it properly
341   \param parent parent widget
342   \param f widget flags
343 */
344 QtxDockWidget::QtxDockWidget( const bool watch, QWidget* parent, Qt::WindowFlags f )
345 : QDockWidget( parent, f ),
346   myWatcher( 0 ),
347   myOrientation( (Qt::Orientation)-1 )
348 {
349   if ( watch )
350     myWatcher = new Watcher( this );
351
352   updateState();
353 }
354
355 /*!
356   \brief Constructor.
357   \param parent parent widget
358   \param f widget flags
359 */
360 QtxDockWidget::QtxDockWidget( QWidget* parent, Qt::WindowFlags f )
361 : QDockWidget( parent, f ),
362 myWatcher( 0 )
363 {
364 }
365
366 /*!
367   \brief Destructor.
368 */
369 QtxDockWidget::~QtxDockWidget()
370 {
371   myWatcher->setParent(nullptr);
372   delete myWatcher;
373   myWatcher = 0;
374 }
375
376 /*!
377   \brief Get the recommended size for the widget.
378   \return recommended dockable widget size
379 */
380 QSize QtxDockWidget::sizeHint() const
381 {
382   return QSize( 500, 100 );
383 }
384
385 /*!
386   \brief Get the recommended minimum size for the widget.
387   \return recommended dockable widget minimum size
388 */
389 QSize QtxDockWidget::minimumSizeHint() const
390 {
391   return QDockWidget::minimumSizeHint();
392 }
393
394 /*!
395   \brief Show/hide the dockable window.
396   \param on new visibility state
397 */
398 void QtxDockWidget::setVisible( bool on )
399 {
400   updateGeometry();
401   if ( widget() )
402     widget()->updateGeometry();
403
404   QDockWidget::setVisible( on && ( myWatcher ? !myWatcher->isEmpty() : true )  );
405
406   if ( myWatcher )
407   {
408     if ( on )
409       myWatcher->shown( this );
410     else
411       myWatcher->hidden( this );
412   }
413
414   if ( on )
415     emit( aboutToShow() );
416 }
417
418 /*!
419   \brief Process resize event
420   \param e event
421 */
422 void QtxDockWidget::resizeEvent( QResizeEvent* e )
423 {
424   QDockWidget::resizeEvent( e );
425   updateState();
426 }
427
428 /*!
429   \brief Get dockable window orientation.
430   \return orientation type
431 */
432 Qt::Orientation QtxDockWidget::orientation() const
433 {
434   QMainWindow* mw = 0;
435   QWidget* wid = parentWidget();
436   while ( wid && !mw )
437   {
438     mw = ::qobject_cast<QMainWindow*>( wid );
439     wid = wid->parentWidget();
440   }
441
442   Qt::Orientation res = (Qt::Orientation)-1;
443
444   if ( !mw )
445     return res;
446
447   Qt::DockWidgetArea area = mw->dockWidgetArea( (QtxDockWidget*)this );
448   switch ( area )
449   {
450   case Qt::LeftDockWidgetArea:
451   case Qt::RightDockWidgetArea:
452     res = Qt::Vertical;
453     break;
454   case Qt::TopDockWidgetArea:
455   case Qt::BottomDockWidgetArea:
456     res = Qt::Horizontal;
457     break;
458   default:
459     break;
460   }
461
462   return res;
463 }
464
465 /*!
466   \brief Update dockable window state.
467 */
468 void QtxDockWidget::updateState()
469 {
470   Qt::Orientation o = orientation();
471   if ( myOrientation == o )
472     return;
473
474   myOrientation = o;
475
476   emit orientationChanged( myOrientation );
477 }
478
479 /*!
480   \fn QtxDockWidget::orientationChanged(Qt::Orientation o)
481   \brief Emitted when the dockable window orientation is changed.
482   \param o new window orientation
483 */