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