Salome HOME
Update French translation files
[modules/gui.git] / src / SUIT / SUIT_ViewWindow.cxx
1 // Copyright (C) 2007-2013  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
23 // SUIT_ViewWindow.cxx: implementation of the SUIT_ViewWindow class.
24 //
25 #include "SUIT_ViewWindow.h"
26
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 "QtxActionToolMgr.h"
36 #include "QtxMultiAction.h"
37
38 #include <QEvent>
39 #include <QIcon>
40 #include <QMenu>
41 #include <QApplication>
42 #include <QContextMenuEvent>
43
44 /*!\class SUIT_ViewWindow
45  * Class provide view window.
46  */
47
48 /*! Dump view custom event*/
49 const int DUMP_EVENT = QEvent::User + 123;
50
51 /*! Constructor.*/
52 SUIT_ViewWindow::SUIT_ViewWindow( SUIT_Desktop* theDesktop )
53   : QMainWindow( theDesktop ), myManager( 0 ), myIsDropDown( true ), mySyncAction( 0 )
54 {
55   myDesktop = theDesktop;
56
57   setWindowIcon( myDesktop ? myDesktop->windowIcon() : QApplication::windowIcon() );
58
59   setAttribute( Qt::WA_DeleteOnClose );
60
61   myToolMgr = new QtxActionToolMgr( this );
62
63   setProperty( "VectorsMode", false );
64 }
65
66 /*! Destructor.*/
67 SUIT_ViewWindow::~SUIT_ViewWindow()
68 {
69 }
70
71 /*!
72   Sets new view manager for window
73   \param theManager - new view manager
74 */
75 void SUIT_ViewWindow::setViewManager( SUIT_ViewManager* theManager )
76 {
77   myManager = theManager;
78 }
79
80 /*!
81   \return view manager of window
82 */
83 SUIT_ViewManager* SUIT_ViewWindow::getViewManager() const
84 {
85   return myManager;
86 }
87
88 /*!
89   \return QImage, containing all scene rendering in window
90 */
91 QImage SUIT_ViewWindow::dumpView()
92 {
93   return QImage();
94 }
95
96 /*!
97   Saves image to file according to the format
98   \param image - image
99   \param fileName - name of file
100   \param format - string contains name of format (for example, "BMP"(default) or "JPEG", "JPG")
101 */
102 bool SUIT_ViewWindow::dumpViewToFormat( const QImage& img, const QString& fileName, const QString& format )
103 {
104   if( img.isNull() )
105     return false;
106
107   QString fmt = format;
108   if( fmt.isEmpty() )
109     fmt = QString( "BMP" ); // default format
110   else if( fmt == "JPG" )
111     fmt = "JPEG";
112
113   QApplication::setOverrideCursor( Qt::WaitCursor );
114   bool res = img.save( fileName, fmt.toLatin1() );
115   QApplication::restoreOverrideCursor();
116   return res;
117 }
118
119 /*!
120   Saves scene rendering in window to file
121   \param fileName - name of file
122   \param format - string contains name of format (for example, "BMP"(default) or "JPEG", "JPG")
123 */
124 bool SUIT_ViewWindow::dumpViewToFormat( const QString& fileName, const QString& format )
125 {
126   Qtx::Localizer loc;
127   return dumpViewToFormat( dumpView(), fileName, format );
128 }
129
130 /*!
131   Set or clear flag Qt::WDestructiveClose
132 */
133 void SUIT_ViewWindow::setDestructiveClose( const bool on )
134 {
135   setAttribute( Qt::WA_DeleteOnClose, on );
136 }
137
138 /*! Close event \a theEvent.
139 */
140 void SUIT_ViewWindow::closeEvent( QCloseEvent* e )
141 {
142   e->ignore();
143   emit tryClosing( this );
144   if ( closable() ) emit closing( this );
145 }
146
147 /*! Context menu requested for event \a e.
148 */
149 void SUIT_ViewWindow::contextMenuEvent( QContextMenuEvent* e )
150 {
151   e->ignore();
152
153   QMainWindow::contextMenuEvent( e );
154
155   if ( e->isAccepted() )
156     return;
157
158   if ( e->reason() != QContextMenuEvent::Mouse )
159     emit contextMenuRequested( e );
160 }
161
162 /*! Post events on dump view.
163 */
164 void SUIT_ViewWindow::onDumpView()
165 {
166   // VSV (TRIPOLI dev): next line commented: causes error messages
167   //QApplication::postEvent( this, new QPaintEvent( QRect( 0, 0, width(), height() ) ) );
168   QApplication::postEvent( this, new QEvent( (QEvent::Type)DUMP_EVENT ) );
169 }
170
171 /*!
172   \return filters for image files
173 */
174 QString SUIT_ViewWindow::filter() const
175 {
176   return tr( "TLT_IMAGE_FILES" );
177 }
178
179 /*! Reaction view window on event \a e.
180 */
181 bool SUIT_ViewWindow::event( QEvent* e )
182 {
183   if ( e->type() == DUMP_EVENT )
184   {
185     bool bOk = false;
186     if ( myManager && myManager->study() && myManager->study()->application() )
187     {
188       // get file name
189       SUIT_Application* app = myManager->study()->application();
190       QString fileName = app->getFileName( false, QString(), filter(), tr( "TLT_DUMP_VIEW" ), 0 );
191       if ( !fileName.isEmpty() )
192       {
193         QImage im = dumpView();
194         QString fmt = SUIT_Tools::extension( fileName ).toUpper();
195         Qtx::Localizer loc;
196         bOk = dumpViewToFormat( im, fileName, fmt );
197       }
198       else
199         bOk = true; // cancelled
200     }
201     if ( !bOk )
202       SUIT_MessageBox::critical( this, tr( "ERROR" ), tr( "ERR_CANT_DUMP_VIEW" ) );
203
204     return true;
205   }
206   return QMainWindow::event( e );
207 }
208
209 /*! Called by SUIT_Accel::onActivated() when a key accelerator was activated and this window was active
210 */
211 bool SUIT_ViewWindow::onAccelAction( int _action )
212 {
213   return action( _action );
214 }
215
216 /*! action  handle standard action (zoom, pan) or custom action.  to be redefined in successors.
217 */
218 bool SUIT_ViewWindow::action( const int  )
219 {
220   return true;
221 }
222
223 /*! Returns \c true if view window can be closed by the user
224 */
225 bool SUIT_ViewWindow::closable() const
226 {
227   QVariant val = property( "closable" );
228   return !val.isValid() || val.toBool();
229 }
230
231 /*! Set / reset "closable" option of the view window
232 */
233 bool SUIT_ViewWindow::setClosable( const bool on )
234 {
235   bool prev = closable();
236   setProperty( "closable", on );
237   return prev;
238 }
239
240 /*!
241   \return string containing visual parameters of window
242 */
243 QString SUIT_ViewWindow::getVisualParameters()
244 {
245   return "empty";
246 }
247
248 /*!
249   Sets visual parameters of window by its string representation
250   \param parameters - string with visual parameters
251 */
252 void SUIT_ViewWindow::setVisualParameters( const QString& /*parameters*/ )
253 {
254 }
255
256 /*!
257   \return associated tool bar manager
258 */
259 QtxActionToolMgr* SUIT_ViewWindow::toolMgr() const
260 {
261   return myToolMgr;
262 }
263
264 /*!
265   \brief Set buttons mode to drop-down (\a on = \c true) or ligned (\a on = \c false)
266   \param on new buttons mode
267   \sa dropDownButtons()
268 */
269 void SUIT_ViewWindow::setDropDownButtons( bool on )
270 {
271   if ( myIsDropDown != on ) {
272     myIsDropDown = on;
273     if ( myIsDropDown ) {
274       ActionsMap::const_iterator it;
275       for( it = myMultiActions.constBegin(); it != myMultiActions.constEnd(); ++it )
276       {
277         int tid = it.key();
278         const QList<QtxMultiAction*>& mlist = it.value();
279         QList<QtxMultiAction*>::const_iterator mit;
280         for ( mit = mlist.constBegin(); mit != mlist.constEnd(); ++mit )
281         {
282           QtxMultiAction* ma = *mit;
283           const QList<QAction*> alist = ma->actions();
284           if ( alist.isEmpty() ) continue;
285           int idx = toolMgr()->index( toolMgr()->actionId( alist[0] ), tid );
286           if ( idx == -1 ) continue;
287           foreach ( QAction* a, alist ) toolMgr()->remove( toolMgr()->actionId( a ), tid );
288           toolMgr()->insert( ma, tid, idx );
289         }
290       }
291       myMultiActions.clear();
292     }
293     else {
294       QIntList tblist = toolMgr()->toolBarsIds();
295       QIntList alist  = toolMgr()->idList();
296       foreach( int aid, alist )
297       {
298         QtxMultiAction* ma = qobject_cast<QtxMultiAction*>( toolMgr()->action( aid ) );
299         if ( !ma ) continue;
300         foreach( int tid, tblist )
301         {
302           int idx = toolMgr()->index( aid, tid );
303           if ( idx >= 0 )
304           {
305             myMultiActions[ tid ].append( ma );
306             toolMgr()->remove( aid, tid );
307             foreach( QAction* a, ma->actions() ) toolMgr()->insert( a, tid, idx++ );
308           }
309         }
310       }
311     }
312   }
313 }
314
315 /*!
316   \brief Get current buttons mode
317   \return current buttons mode
318   \sa setDropDownButtons()
319 */
320 bool SUIT_ViewWindow::dropDownButtons() const
321 {
322   return myIsDropDown;
323 }
324
325 /*!
326   \return window unique identifier
327 */
328 int SUIT_ViewWindow::getId() const
329 {
330   return int(long(this));
331 }
332
333 /*!
334   Get camera properties for the view window.
335   \return shared pointer on camera properties. Base implementation
336           returns null properties.
337 */
338 SUIT_CameraProperties SUIT_ViewWindow::cameraProperties()
339 {
340   return SUIT_CameraProperties();
341 }
342
343 /*!
344   Synchronize this view window's camera properties with specified
345   view window.
346
347   This method is a part of general views synchronization mechanism.
348   It should be redefined in successors. Base imlementation does nothing.
349
350   \param otherWindow other view window
351 */
352 void SUIT_ViewWindow::synchronize( SUIT_ViewWindow* /*otherWindow*/ )
353 {
354   // base implementation does nothing
355 }
356
357 /*!
358   Get action for views syncronization.
359
360   This method is a part of general views synchronization mechanism.
361   It creates an action that can be inserted, for instance, to the toolbar.
362
363   \return action for views synchronization
364 */
365 QAction* SUIT_ViewWindow::synchronizeAction()
366 {
367   if ( !mySyncAction ) {
368     SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
369     mySyncAction = new QtxAction( tr( "MNU_SYNCHRONIZE_VIEW" ),
370                                   resMgr->loadPixmap( "SUIT", tr( "ICON_VIEW_SYNC" ) ),
371                                   tr( "MNU_SYNCHRONIZE_VIEW" ), 0, this );
372     mySyncAction->setStatusTip( tr( "DSC_SYNCHRONIZE_VIEW" ) );
373     mySyncAction->setMenu( new QMenu( this ) );
374     mySyncAction->setCheckable( true );
375     connect( mySyncAction->menu(), SIGNAL( aboutToShow() ),     this, SLOT( updateSyncViews() ) );
376     connect( mySyncAction,         SIGNAL( triggered( bool ) ), this, SLOT( onSynchronizeView( bool ) ) );
377   }
378   return mySyncAction;
379 }
380
381 /*!
382   Emit notification signal that the view is transformed.
383   Other views can use the signal for synchronization.
384 */
385 void SUIT_ViewWindow::emitViewModified()
386 {
387   emit viewModified( this );
388 }
389
390 /*!
391   Update list of available view for the "Synchronize View" action
392 */
393 void SUIT_ViewWindow::updateSyncViews()
394 {
395   SUIT_CameraProperties props = cameraProperties();
396   if ( !props.isValid() )
397     return;
398
399   QAction* anAction = synchronizeAction();
400   if ( anAction && anAction->menu() ) {
401     int currentId = anAction->data().toInt();
402     anAction->menu()->clear();
403     SUIT_Application* app = SUIT_Session::session()->activeApplication();
404     if ( app ) {
405       SUIT_Desktop* d = app->desktop();
406       QList<SUIT_ViewWindow*> allViews = qFindChildren<SUIT_ViewWindow*>( d );
407       foreach( SUIT_ViewWindow* vw, allViews ) {
408         if ( !vw || vw == this ) continue; // skip invalid views and this one
409         SUIT_CameraProperties otherProps = vw->cameraProperties();
410         if ( otherProps.isCompatible( props ) ) {
411           QAction* a = anAction->menu()->addAction( vw->windowTitle() );
412           if ( vw->getId() == currentId ) {
413             QFont f = a->font();
414             f.setBold( true );
415             a->setFont( f );
416           }
417           a->setData( vw->getId() );
418           connect( a, SIGNAL( triggered( bool ) ), this, SLOT( onSynchronizeView( bool ) ) );
419         }
420         else if ( vw->getId() == currentId ) {
421           // other view, this one is being currently synchronized to, seems has become incompatible
422           // we have to break synchronization
423           vw->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), this, SLOT( synchronize( SUIT_ViewWindow* ) ) );
424           this->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), vw, SLOT( synchronize( SUIT_ViewWindow* ) ) );
425           // 
426           bool blocked = anAction->blockSignals( true );
427           anAction->setChecked( false );
428           anAction->blockSignals( blocked );
429           anAction->setData( 0 );
430           //
431           QAction* a = vw->synchronizeAction();
432           if ( a ) {
433             blocked = a->blockSignals( true );
434             a->setChecked( false );
435             a->blockSignals( blocked );
436           }
437         }
438       }
439     }
440     if ( anAction->menu()->actions().isEmpty() ) {
441       anAction->setData( 0 );
442       anAction->menu()->addAction( tr( "MNU_SYNC_NO_VIEW" ) );
443     }
444   }
445 }
446
447 /*!
448   "Synchronize View" action slot.
449 */
450 void SUIT_ViewWindow::onSynchronizeView( bool checked )
451 {
452   QAction* a = qobject_cast<QAction*>( sender() );
453   if ( a ) {
454     synchronizeView( this, a->data().toInt() );
455   }
456 }
457
458 /*!
459   Synchronize camera properties of view \a viewWindow with
460   camera properties of view specified via \a id
461 */
462 void SUIT_ViewWindow::synchronizeView( SUIT_ViewWindow* viewWindow, int id )
463 {
464   SUIT_ViewWindow* sourceView = 0;
465   QList<SUIT_ViewWindow*> otherViews;
466
467   bool isSync = viewWindow->synchronizeAction() && viewWindow->synchronizeAction()->isChecked();
468
469   int vwid = viewWindow->getId();
470
471   SUIT_Application* app = SUIT_Session::session()->activeApplication();
472   if ( !app ) return;
473   SUIT_Desktop* d = app->desktop();
474   if ( !d ) return;
475
476   QList<SUIT_ViewWindow*> allViews = qFindChildren<SUIT_ViewWindow*>( d );
477   foreach( SUIT_ViewWindow* vw, allViews ) {
478     if ( !vw->cameraProperties().isValid() )
479       continue;                    // omit views not supporting camera properties
480     if ( vw->getId() == id )
481       sourceView = vw;             // remember source view
482     else if ( vw != viewWindow )
483       otherViews.append( vw );     // collect all remaining views
484   }
485
486   if ( isSync && id ) {
487     // remove all possible disconnections
488     foreach( SUIT_ViewWindow* vw, otherViews ) {
489       // disconnect target view
490       vw->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), viewWindow, SLOT( synchronize( SUIT_ViewWindow* ) ) );
491       viewWindow->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), vw, SLOT( synchronize( SUIT_ViewWindow* ) ) );
492       if ( sourceView ) {
493         // disconnect source view
494         vw->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), sourceView, SLOT( synchronize( SUIT_ViewWindow* ) ) );
495         sourceView->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), vw, SLOT( synchronize( SUIT_ViewWindow* ) ) );
496       }
497       QAction* a = vw->synchronizeAction();
498       if ( a ) {
499         int anid = a->data().toInt();
500         if ( a->isChecked() && ( anid == id || anid == vwid ) ) {
501           bool blocked = a->blockSignals( true );
502           a->setChecked( false );
503           a->blockSignals( blocked );
504         }
505       }
506     }
507     if ( sourceView ) {
508       // reconnect source and target views
509       sourceView->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), viewWindow, SLOT( synchronize( SUIT_ViewWindow* ) ) );
510       viewWindow->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), sourceView, SLOT( synchronize( SUIT_ViewWindow* ) ) );
511       sourceView->connect( viewWindow, SIGNAL( viewModified( SUIT_ViewWindow* ) ), SLOT( synchronize( SUIT_ViewWindow* ) ) );
512       viewWindow->connect( sourceView, SIGNAL( viewModified( SUIT_ViewWindow* ) ), SLOT( synchronize( SUIT_ViewWindow* ) ) );
513       // synchronize target view with source view
514       viewWindow->synchronize( sourceView );
515       if ( viewWindow->synchronizeAction() )
516         viewWindow->synchronizeAction()->setData( sourceView->getId() );
517       QAction* sourceAction = sourceView->synchronizeAction();
518       if ( sourceAction ) {
519         sourceAction->setData( viewWindow->getId() );
520         if ( !sourceAction->isChecked() ) {
521           bool blocked = sourceAction->blockSignals( true );
522           sourceAction->setChecked( true );
523           sourceAction->blockSignals( blocked );
524         }
525       }
526     }
527   }
528   else if ( sourceView ) {
529     // reconnect source and target view
530     sourceView->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), viewWindow, SLOT( synchronize( SUIT_ViewWindow* ) ) );
531     viewWindow->disconnect( SIGNAL( viewModified( SUIT_ViewWindow* ) ), sourceView, SLOT( synchronize( SUIT_ViewWindow* ) ) );
532     viewWindow->synchronize( sourceView );
533     if ( viewWindow->synchronizeAction() )
534       viewWindow->synchronizeAction() ->setData( sourceView->getId() );
535     QAction* sourceAction = sourceView->synchronizeAction();
536     if ( sourceAction ) {
537       if ( sourceAction->data().toInt() == viewWindow->getId() && sourceAction->isChecked() ) {
538         bool blocked = sourceAction->blockSignals( true );
539         sourceAction->setChecked( false );
540         sourceAction->blockSignals( blocked );
541       }
542     }
543   }
544 }