1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
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.
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.
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
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // SUIT_ViewWindow.cxx: implementation of the SUIT_ViewWindow class.
25 #include "SUIT_ViewWindow.h"
27 #include "SUIT_Tools.h"
28 #include "SUIT_Session.h"
29 #include "SUIT_Study.h"
30 #include "SUIT_Desktop.h"
31 #include "SUIT_MessageBox.h"
32 #include "SUIT_Application.h"
33 #include "SUIT_ViewManager.h"
34 #include "SUIT_ResourceMgr.h"
35 #include "SUIT_FileDlg.h"
36 #include "QtxActionToolMgr.h"
37 #include "QtxMultiAction.h"
42 #include <QApplication>
43 #include <QContextMenuEvent>
45 /*!\class SUIT_ViewWindow
46 * Class provide view window.
49 /*! Dump view custom event*/
50 const int DUMP_EVENT = QEvent::User + 123;
53 SUIT_ViewWindow::SUIT_ViewWindow( SUIT_Desktop* theDesktop )
54 : QMainWindow( theDesktop ), myManager( 0 ), myIsDropDown( true ), mySyncAction( 0 )
56 myDesktop = theDesktop;
58 setWindowIcon( myDesktop ? myDesktop->windowIcon() : QApplication::windowIcon() );
60 setAttribute( Qt::WA_DeleteOnClose );
62 myToolMgr = new QtxActionToolMgr( this );
64 setProperty( "VectorsMode", false );
68 SUIT_ViewWindow::~SUIT_ViewWindow()
73 Sets new view manager for window
74 \param theManager - new view manager
76 void SUIT_ViewWindow::setViewManager( SUIT_ViewManager* theManager )
78 myManager = theManager;
82 \return view manager of window
84 SUIT_ViewManager* SUIT_ViewWindow::getViewManager() const
90 \return QImage, containing all scene rendering in window
92 QImage SUIT_ViewWindow::dumpView()
98 Saves image to file according to the format
100 \param fileName - name of file
101 \param format - string contains name of format (for example, "BMP"(default) or "JPEG", "JPG")
103 bool SUIT_ViewWindow::dumpViewToFormat( const QImage& img, const QString& fileName, const QString& format )
108 QString fmt = format;
110 fmt = QString( "BMP" ); // default format
111 else if( fmt == "JPG" )
114 QApplication::setOverrideCursor( Qt::WaitCursor );
115 bool res = img.save( fileName, fmt.toLatin1() );
116 QApplication::restoreOverrideCursor();
121 Saves scene rendering in window to file
122 \param fileName - name of file
123 \param format - string contains name of format (for example, "BMP"(default) or "JPEG", "JPG")
125 bool SUIT_ViewWindow::dumpViewToFormat( const QString& fileName, const QString& format )
128 return dumpViewToFormat( dumpView(), fileName, format );
132 Set or clear flag Qt::WDestructiveClose
134 void SUIT_ViewWindow::setDestructiveClose( const bool on )
136 setAttribute( Qt::WA_DeleteOnClose, on );
139 /*! Close event \a theEvent.
141 void SUIT_ViewWindow::closeEvent( QCloseEvent* e )
144 emit tryClosing( this );
145 if ( closable() ) emit closing( this );
148 /*! Context menu requested for event \a e.
150 void SUIT_ViewWindow::contextMenuEvent( QContextMenuEvent* e )
154 QMainWindow::contextMenuEvent( e );
156 if ( e->isAccepted() )
159 if ( e->reason() != QContextMenuEvent::Mouse )
160 emit contextMenuRequested( e );
163 /*! Post events on dump view.
165 void SUIT_ViewWindow::onDumpView()
167 // VSV (TRIPOLI dev): next line commented: causes error messages
168 //QApplication::postEvent( this, new QPaintEvent( QRect( 0, 0, width(), height() ) ) );
169 QApplication::postEvent( this, new QEvent( (QEvent::Type)DUMP_EVENT ) );
173 \return filters for image files
175 QString SUIT_ViewWindow::filter() const
177 return tr( "TLT_IMAGE_FILES" );
180 /*! Reaction view window on event \a e.
182 bool SUIT_ViewWindow::event( QEvent* e )
184 if ( e->type() == DUMP_EVENT )
187 SUIT_Application* app = NULL;
188 if (myManager && myManager->study() && myManager->study()->application())
189 app = myManager->study()->application();
192 fileName = app->getFileName( false, QString(), filter(), tr( "TLT_DUMP_VIEW" ), 0 ); //old way
195 QStringList fls = filter().split( ";;", QString::SkipEmptyParts );
196 fileName = SUIT_FileDlg::getFileName( NULL, QString(), fls, tr( "TLT_DUMP_VIEW" ), false, true );
198 if ( !fileName.isEmpty() )
200 QString fmt = SUIT_Tools::extension( fileName ).toUpper();
201 QImage im = dumpView();
203 bOk = dumpViewToFormat( im, fileName, fmt );
206 bOk = true; // cancelled
209 SUIT_MessageBox::critical( this, tr( "ERROR" ), tr( "ERR_CANT_DUMP_VIEW" ) );
213 return QMainWindow::event( e );
216 /*! Called by SUIT_Accel::onActivated() when a key accelerator was activated and this window was active
218 bool SUIT_ViewWindow::onAccelAction( int _action )
220 return action( _action );
223 /*! action handle standard action (zoom, pan) or custom action. to be redefined in successors.
225 bool SUIT_ViewWindow::action( const int )
230 /*! Returns \c true if view window can be closed by the user
232 bool SUIT_ViewWindow::closable() const
234 QVariant val = property( "closable" );
235 return !val.isValid() || val.toBool();
238 /*! Set / reset "closable" option of the view window
240 bool SUIT_ViewWindow::setClosable( const bool on )
242 bool prev = closable();
243 setProperty( "closable", on );
248 \return string containing visual parameters of window
250 QString SUIT_ViewWindow::getVisualParameters()
256 Sets visual parameters of window by its string representation
257 \param parameters - string with visual parameters
259 void SUIT_ViewWindow::setVisualParameters( const QString& /*parameters*/ )
264 \return associated tool bar manager
266 QtxActionToolMgr* SUIT_ViewWindow::toolMgr() const
272 \brief Set buttons mode to drop-down (\a on = \c true) or ligned (\a on = \c false)
273 \param on new buttons mode
274 \sa dropDownButtons()
276 void SUIT_ViewWindow::setDropDownButtons( bool on )
278 if ( myIsDropDown != on ) {
280 if ( myIsDropDown ) {
281 ActionsMap::const_iterator it;
282 for( it = myMultiActions.constBegin(); it != myMultiActions.constEnd(); ++it )
285 const QList<QtxMultiAction*>& mlist = it.value();
286 QList<QtxMultiAction*>::const_iterator mit;
287 for ( mit = mlist.constBegin(); mit != mlist.constEnd(); ++mit )
289 QtxMultiAction* ma = *mit;
290 const QList<QAction*> alist = ma->actions();
291 if ( alist.isEmpty() ) continue;
292 int idx = toolMgr()->index( toolMgr()->actionId( alist[0] ), tid );
293 if ( idx == -1 ) continue;
294 foreach ( QAction* a, alist ) toolMgr()->remove( toolMgr()->actionId( a ), tid );
295 toolMgr()->insert( ma, tid, idx );
298 myMultiActions.clear();
301 QIntList tblist = toolMgr()->toolBarsIds();
302 QIntList alist = toolMgr()->idList();
303 foreach( int aid, alist )
305 QtxMultiAction* ma = qobject_cast<QtxMultiAction*>( toolMgr()->action( aid ) );
307 foreach( int tid, tblist )
309 int idx = toolMgr()->index( aid, tid );
312 myMultiActions[ tid ].append( ma );
313 toolMgr()->remove( aid, tid );
314 foreach( QAction* a, ma->actions() ) toolMgr()->insert( a, tid, idx++ );
323 \brief Get current buttons mode
324 \return current buttons mode
325 \sa setDropDownButtons()
327 bool SUIT_ViewWindow::dropDownButtons() const
333 \return window unique identifier
335 int SUIT_ViewWindow::getId() const
337 return int(long(this));
341 Get camera properties for the view window.
342 \return shared pointer on camera properties. Base implementation
343 returns null properties.
345 SUIT_CameraProperties SUIT_ViewWindow::cameraProperties()
347 return SUIT_CameraProperties();
351 Synchronize this view window's camera properties with specified
354 This method is a part of general views synchronization mechanism.
355 It should be redefined in successors. Base imlementation does nothing.
357 \param otherWindow other view window
359 void SUIT_ViewWindow::synchronize( SUIT_ViewWindow* /*otherWindow*/ )
361 // base implementation does nothing
365 Get action for views syncronization.
367 This method is a part of general views synchronization mechanism.
368 It creates an action that can be inserted, for instance, to the toolbar.
370 \return action for views synchronization
372 QAction* SUIT_ViewWindow::synchronizeAction()
374 if ( !mySyncAction ) {
375 SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
376 mySyncAction = new QtxAction( tr( "MNU_SYNCHRONIZE_VIEW" ),
377 resMgr->loadPixmap( "SUIT", tr( "ICON_VIEW_SYNC" ) ),
378 tr( "MNU_SYNCHRONIZE_VIEW" ), 0, this );
379 mySyncAction->setStatusTip( tr( "DSC_SYNCHRONIZE_VIEW" ) );
380 mySyncAction->setMenu( new QMenu( this ) );
381 mySyncAction->setCheckable( true );
382 connect( mySyncAction->menu(), SIGNAL( aboutToShow() ), this, SLOT( updateSyncViews() ) );
383 connect( mySyncAction, SIGNAL( triggered( bool ) ), this, SLOT( onSynchronizeView( bool ) ) );
389 Emit notification signal that the view is transformed.
390 Other views can use the signal for synchronization.
392 void SUIT_ViewWindow::emitViewModified()
394 emit viewModified( this );
398 Update list of available view for the "Synchronize View" action
400 void SUIT_ViewWindow::updateSyncViews()
402 SUIT_CameraProperties props = cameraProperties();
403 if ( !props.isValid() )
406 QAction* anAction = synchronizeAction();
407 if ( anAction && anAction->menu() ) {
408 int currentId = anAction->data().toInt();
409 anAction->menu()->clear();
410 SUIT_Application* app = SUIT_Session::session()->activeApplication();
412 SUIT_Desktop* d = app->desktop();
413 QList<SUIT_ViewWindow*> allViews = d->findChildren<SUIT_ViewWindow*>();
414 foreach( SUIT_ViewWindow* vw, allViews ) {
415 if ( !vw || vw == this ) continue; // skip invalid views and this one
416 SUIT_CameraProperties otherProps = vw->cameraProperties();
417 if ( otherProps.isCompatible( props ) ) {
418 QAction* a = anAction->menu()->addAction( vw->windowTitle() );
419 if ( vw->getId() == currentId ) {
424 a->setData( vw->getId() );
425 connect( a, SIGNAL( triggered( bool ) ), this, SLOT( onSynchronizeView( bool ) ) );
427 else if ( vw->getId() == currentId ) {
428 // other view, this one is being currently synchronized to, seems has become incompatible
429 // we have to break synchronization
430 vw->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), this, SLOT( synchronize( SUIT_ViewWindow* ) ) );
431 this->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), vw, SLOT( synchronize( SUIT_ViewWindow* ) ) );
433 bool blocked = anAction->blockSignals( true );
434 anAction->setChecked( false );
435 anAction->blockSignals( blocked );
436 anAction->setData( 0 );
438 QAction* a = vw->synchronizeAction();
440 blocked = a->blockSignals( true );
441 a->setChecked( false );
442 a->blockSignals( blocked );
447 if ( anAction->menu()->actions().isEmpty() ) {
448 anAction->setData( 0 );
449 anAction->menu()->addAction( tr( "MNU_SYNC_NO_VIEW" ) );
455 "Synchronize View" action slot.
457 void SUIT_ViewWindow::onSynchronizeView( bool checked )
459 QAction* a = qobject_cast<QAction*>( sender() );
461 synchronizeView( this, a->data().toInt() );
466 Synchronize camera properties of view \a viewWindow with
467 camera properties of view specified via \a id
469 void SUIT_ViewWindow::synchronizeView( SUIT_ViewWindow* viewWindow, int id )
471 SUIT_ViewWindow* sourceView = 0;
472 QList<SUIT_ViewWindow*> otherViews;
474 bool isSync = viewWindow->synchronizeAction() && viewWindow->synchronizeAction()->isChecked();
476 int vwid = viewWindow->getId();
478 SUIT_Application* app = SUIT_Session::session()->activeApplication();
480 SUIT_Desktop* d = app->desktop();
483 QList<SUIT_ViewWindow*> allViews = d->findChildren<SUIT_ViewWindow*>();
484 foreach( SUIT_ViewWindow* vw, allViews ) {
485 if ( !vw->cameraProperties().isValid() )
486 continue; // omit views not supporting camera properties
487 if ( vw->getId() == id )
488 sourceView = vw; // remember source view
489 else if ( vw != viewWindow )
490 otherViews.append( vw ); // collect all remaining views
493 if ( isSync && id ) {
494 // remove all possible disconnections
495 foreach( SUIT_ViewWindow* vw, otherViews ) {
496 // disconnect target view
497 vw->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), viewWindow, SLOT( synchronize( SUIT_ViewWindow* ) ) );
498 viewWindow->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), vw, SLOT( synchronize( SUIT_ViewWindow* ) ) );
500 // disconnect source view
501 vw->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), sourceView, SLOT( synchronize( SUIT_ViewWindow* ) ) );
502 sourceView->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), vw, SLOT( synchronize( SUIT_ViewWindow* ) ) );
504 QAction* a = vw->synchronizeAction();
506 int anid = a->data().toInt();
507 if ( a->isChecked() && ( anid == id || anid == vwid ) ) {
508 bool blocked = a->blockSignals( true );
509 a->setChecked( false );
510 a->blockSignals( blocked );
515 // reconnect source and target views
516 sourceView->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), viewWindow, SLOT( synchronize( SUIT_ViewWindow* ) ) );
517 viewWindow->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), sourceView, SLOT( synchronize( SUIT_ViewWindow* ) ) );
518 sourceView->connect( viewWindow, SIGNAL( viewModified( SUIT_ViewWindow* ) ), SLOT( synchronize( SUIT_ViewWindow* ) ) );
519 viewWindow->connect( sourceView, SIGNAL( viewModified( SUIT_ViewWindow* ) ), SLOT( synchronize( SUIT_ViewWindow* ) ) );
520 // synchronize target view with source view
521 viewWindow->synchronize( sourceView );
522 if ( viewWindow->synchronizeAction() )
523 viewWindow->synchronizeAction()->setData( sourceView->getId() );
524 QAction* sourceAction = sourceView->synchronizeAction();
525 if ( sourceAction ) {
526 sourceAction->setData( viewWindow->getId() );
527 if ( !sourceAction->isChecked() ) {
528 bool blocked = sourceAction->blockSignals( true );
529 sourceAction->setChecked( true );
530 sourceAction->blockSignals( blocked );
535 else if ( sourceView ) {
536 // reconnect source and target view
537 sourceView->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), viewWindow, SLOT( synchronize( SUIT_ViewWindow* ) ) );
538 viewWindow->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), sourceView, SLOT( synchronize( SUIT_ViewWindow* ) ) );
539 viewWindow->synchronize( sourceView );
540 if ( viewWindow->synchronizeAction() )
541 viewWindow->synchronizeAction() ->setData( sourceView->getId() );
542 QAction* sourceAction = sourceView->synchronizeAction();
543 if ( sourceAction ) {
544 if ( sourceAction->data().toInt() == viewWindow->getId() && sourceAction->isChecked() ) {
545 bool blocked = sourceAction->blockSignals( true );
546 sourceAction->setChecked( false );
547 sourceAction->blockSignals( blocked );
553 void SUIT_ViewWindow::setVisible( bool on )
555 // This is a workaround to avoid showing view window as a top-level window
556 // before re-parenting it to workstack (issue #23467).
557 // See SUIT_Desktop::childEvent().
558 QApplication::sendPostedEvents( 0, QEvent::ChildRemoved );
559 QApplication::sendPostedEvents( 0, QEvent::ChildAdded );
560 QApplication::sendPostedEvents( 0, QEvent::ChildPolished );
561 if ( !property( "blockShow" ).toBool() )
562 QMainWindow::setVisible( on );