Salome HOME
Update copyrights
[modules/gui.git] / src / SUIT / SUIT_DataBrowser.cxx
1 // Copyright (C) 2007-2019  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File   : SUIT_DataBrowser.cxx
21 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
22 //
23 #include "SUIT_DataBrowser.h"
24 #include "SUIT_TreeModel.h"
25 #include <QtxTreeView.h>
26
27 #include <QShortcut>
28
29 /*!
30   \class SUIT_DataBrowser
31   \brief Object browser customization.
32 */
33
34 /*!
35   \brief Constructor.
36   \param parent parent widget
37 */
38 SUIT_DataBrowser::SUIT_DataBrowser( QWidget* parent )
39 : OB_Browser( parent )
40 {
41   init( 0 );
42 }
43
44 /*!
45   \brief Constructor.
46   \param root root data object
47   \param parent parent widget
48 */
49 SUIT_DataBrowser::SUIT_DataBrowser( SUIT_DataObject* root, QWidget* parent )
50 : OB_Browser( parent )
51 {
52   init( root );
53 }
54
55 /*!
56   \brief Destructor.
57 */
58 SUIT_DataBrowser::~SUIT_DataBrowser()
59 {
60 }
61
62 /*!
63   \brief Get popup menu client type.
64   \return popup client type
65 */
66 QString SUIT_DataBrowser::popupClientType() const
67 {
68   return "ObjectBrowser";
69 }
70
71 /*!
72   \brief Get root object.
73   \return root object
74 */
75 SUIT_DataObject* SUIT_DataBrowser::root() const
76 {
77   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
78   return m ? m->root() : 0;
79 }
80
81 /*!
82   \brief Set root object.
83   \param r new root object
84 */
85 void SUIT_DataBrowser::setRoot( SUIT_DataObject* r )
86 {
87   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
88   if ( m ) 
89     m->setRoot( r );
90 }
91
92 /*!
93   \brief Get 'auto-update tree' flag value.
94   \return 'auto-update tree' flag value
95   \sa setAutoUpdate(), updateTree()
96 */
97 bool SUIT_DataBrowser::autoUpdate() const
98 {
99   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
100   return m ? m->autoUpdate() : false;
101 }
102
103 /*!
104   \brief Set 'auto-update tree' flag value.
105
106   If this flag is set to \c true (by default), the object browser is updated
107   automatically when data tree is changed.
108
109   \param on 'auto-update tree' flag value
110   \sa autoUpdate(), updateTree()
111 */
112 void SUIT_DataBrowser::setAutoUpdate( const bool on )
113 {
114   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
115   if ( m ) 
116     m->setAutoUpdate( on );
117 }
118
119 /*!
120   \brief Get 'updateModified' flag value.
121   \return 'updateModified' flag value
122 */
123 bool SUIT_DataBrowser::updateModified() const
124 {
125   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
126   return m ? m->updateModified() : false;
127 }
128
129 /*!
130   \brief Set 'updateModified' flag value.
131   \param on 'updateModified' flag value
132 */
133 void SUIT_DataBrowser::setUpdateModified( const bool on )
134 {
135   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
136   if ( m ) 
137     m->setUpdateModified( on );
138 }
139
140 /*!
141   \brief Update object browser starting from the object \obj;
142   open all branches automatically if \a autoOpen is \c true.
143   \param obj starting object for updating
144   \param autoOpen if \c true automatically open branches
145 */
146 void SUIT_DataBrowser::updateTree( SUIT_DataObject* obj, const bool autoOpen )
147 {
148   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
149   if ( m ) {
150     m->updateTree( obj );
151     openLevels();
152
153     if (myAutoSizeFirstColumn)
154       adjustFirstColumnWidth();
155     if (myAutoSizeColumns)
156       adjustColumnsWidth();
157   }
158   emit updated();
159 }
160
161 /*!
162   \brief Get current key accelerator by id.
163   \return current key accelerator
164   \sa setShortcutKey(), requestUpdate(), requestRename()
165 */
166 int SUIT_DataBrowser::shortcutKey(const int id) const
167 {
168   return myShortcutMap.value(id)->key()[0];
169 }
170
171 /*!
172   \brief Assign the key accelerator for the shortcut.
173   
174   \param id id of the shortcut
175   \param key new key accelerator
176   \sa shortcutKey(), requestUpdate(), requestRename()
177 */
178 void SUIT_DataBrowser::setShortcutKey( const int id, const int key )
179
180   ShortcutMap::iterator it = myShortcutMap.find( id );
181   if( it != myShortcutMap.end() )
182     (*it)->setKey(key);
183 }
184
185 /*!
186   \brief Get list of selected data objects.
187   \return list of the currently selected data objects
188 */
189 DataObjectList SUIT_DataBrowser::getSelected() const
190 {
191   DataObjectList lst;
192   getSelected( lst );
193   return lst;
194 }
195
196 /*!
197   \brief Get list of selected data objects.
198   \overload
199   \param lst list to be filled with the currently selected data objects
200 */
201 void SUIT_DataBrowser::getSelected( DataObjectList& lst ) const
202 {
203   lst.clear();
204
205   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
206
207   if ( m ) {
208     QModelIndexList sel = selectedIndexes();
209     QModelIndex idx;
210   
211     foreach( idx, sel ) {
212       SUIT_DataObject* obj = m->object( idx );
213       if ( obj )
214         lst.append( obj );
215     }
216   }
217 }
218
219 /*!
220   \brief Set selected object.
221   \param obj data object to set selected
222   \param append if \c true, the object is added to the current selection;
223   otherwise the previous selection is first cleared
224 */
225 void SUIT_DataBrowser::setSelected( const SUIT_DataObject* obj, const bool append )
226 {
227   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
228
229   if ( m ) {
230     QModelIndex index = m->index( obj );
231     if ( index.isValid() )
232       select( index, true, append );
233   }
234 }
235
236 /*!
237   \brief function to sort QModelIndexList with qSort
238 */
239 bool modelIndexLessThan(const QModelIndex& lhs, const QModelIndex& rhs)
240 {
241   QModelIndex lhs_parent=lhs.parent();
242   QModelIndex rhs_parent=rhs.parent();
243   if(lhs_parent < rhs_parent)return true;
244   if(lhs_parent == rhs_parent) return lhs < rhs;
245   return false;
246 }
247
248 /*!
249   \brief Set list of selected data objects.
250   \param lst list of the data object to set selected
251   \param append if \c true, the objects are added to the current selection;
252   otherwise the previous selection is first cleared
253 */
254 void SUIT_DataBrowser::setSelected( const DataObjectList& lst, const bool append )
255 {
256   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
257
258   if ( m ) {
259     QModelIndexList indexes;
260     SUIT_DataObject* obj;
261
262     foreach( obj, lst ) {
263       QModelIndex index = m->index( obj );
264       if ( index.isValid() )
265         indexes.append( index );
266     }
267     qSort(indexes.begin(), indexes.end(), modelIndexLessThan);
268
269     select( indexes, true, append ); // if !indexes.isEmpty() ???
270   }
271 }
272
273 /*!
274   \brief Make the view item for specified data object is visible.
275   \param obj data object
276 */
277 void SUIT_DataBrowser::ensureVisible( SUIT_DataObject* obj )
278 {
279   if ( !obj )
280     return;
281
282   DataObjectList lst;
283   lst.append( obj );
284   ensureVisible( lst );
285 }
286
287 /*!
288   \brief Make the view items for specified data objects is visible.
289   \param lst data object list
290 */
291 void SUIT_DataBrowser::ensureVisible( const DataObjectList& lst )
292 {
293   QtxTreeView* tv = treeView();
294   SUIT_AbstractModel* treeModel = dynamic_cast<SUIT_AbstractModel*>( model() );
295   if ( !tv || !treeModel )
296     return;
297
298   for ( DataObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it )
299   {
300     QModelIndex idx = treeModel->index( *it );
301     if ( idx.isValid() )
302       tv->scrollTo( idx );
303   }
304 }
305
306 /*!
307   \brief Add custom actions to the popup menu.
308   \param menu popup menu
309 */
310 void SUIT_DataBrowser::contextMenuPopup( QMenu* menu )
311 {
312   createPopupMenu( menu );
313 }
314
315 /*!
316   \brief Set 'auto-size first column' flag value.
317
318   If this flag is set to \c true (by default), the first column width is resized
319   to its contents.
320
321   \param on 'auto-size first column' flag value
322   \sa setAutoSizeColumns()
323 */
324 void SUIT_DataBrowser::setAutoSizeFirstColumn( const bool on )
325 {
326   myAutoSizeFirstColumn = on;
327 }
328
329 /*!
330   \brief Set 'auto-size columns' flag value.
331
332   If this flag is set to \c true (by default is false), columns width except 
333   the first column is resized to its contents.
334
335   \param on 'auto-size columns' flag value
336   \sa setAutoSizeFirstColumn()
337 */
338 void SUIT_DataBrowser::setAutoSizeColumns( const bool on )
339 {
340   myAutoSizeColumns = on;
341 }
342
343 /*!
344   \brief Process context menu request event.
345   \param e context menu event
346 */
347 void SUIT_DataBrowser::contextMenuEvent( QContextMenuEvent* e )
348 {
349   contextMenuRequest( e );
350 }
351
352 /*!
353   \brief Set 'resize on expand item' flag value.
354
355   If this flag is set to \c true (by default is false), after
356   expanding an item columns will be resized to its contents.
357
358   \param on 'resize on expand item' flag value
359 */
360 void SUIT_DataBrowser::setResizeOnExpandItem( const bool on )
361 {
362   myResizeOnExpandItem = on;
363 }
364
365 /*!
366   \brief Initialize object browser.
367   \param root root data object
368 */
369 void SUIT_DataBrowser::init( SUIT_DataObject* root )
370 {
371   SUIT_ProxyModel* m = new SUIT_ProxyModel( root, this );
372   connect( m, SIGNAL( modelUpdated() ), this, SLOT( onModelUpdated() ) );
373   
374   setModel( m );
375   setItemDelegate( qobject_cast<SUIT_ProxyModel*>( model() )->delegate() );
376   connect( treeView(), SIGNAL( sortingEnabled( bool ) ), 
377            model(),    SLOT( setSortingEnabled( bool ) ) );
378   connect( treeView(), SIGNAL( clicked( const QModelIndex& ) ), 
379            this,       SLOT( onClicked( const QModelIndex& ) ) );
380   connect( treeView(), SIGNAL( doubleClicked( const QModelIndex& ) ), 
381            this,       SLOT( onDblClicked( const QModelIndex& ) ) );
382   connect( treeView(), SIGNAL( expanded( const QModelIndex& ) ), 
383            this,       SLOT( onExpanded( const QModelIndex& ) ) );
384   connect( this      , SIGNAL( requestRename() ),
385            this      , SLOT ( onStartEditing() ));
386
387   myShortcutMap.insert(UpdateShortcut , new QShortcut( Qt::Key_F5, this, SIGNAL( requestUpdate() ), SIGNAL( requestUpdate() ) ) );
388   myShortcutMap.insert(RenameShortcut , new QShortcut( Qt::Key_F2, this, SIGNAL( requestRename() ), SIGNAL( requestRename() ) ) );
389
390   myAutoSizeFirstColumn = true;
391   myAutoSizeColumns = false;
392   myResizeOnExpandItem = false;
393 }
394
395 /*!
396   \fn void SUIT_DataBrowser::requestUpdate();
397   \brief The signal is emitted when the key accelerator
398   assigned for the update operation is pressed by the user.
399
400   By default, \c [F5] key is assigned for the update operation.
401   The key accelerator can be changed with the setShortcutKey() method.
402
403   \sa shortcutKey(), setShortcutKey()
404 */
405
406 /*!
407   \fn void SUIT_DataBrowser::clicked( SUIT_DataObject* o );
408   \brief This signal is emitted when a mouse button is clicked.
409
410   The data object the mouse was clicked on is specified by \a o.
411   The signal is only emitted when the object is valid.
412
413   \param o data object which is clicked
414 */
415
416 /*!
417   \fn void SUIT_DataBrowser::doubleClicked( SUIT_DataObject* o );
418   \brief This signal is emitted when a mouse button is double-clicked.
419
420   The data object the mouse was double-clicked on is specified by \a o.
421   The signal is only emitted when the object is valid.
422
423   \param o data object which is double-clicked
424 */
425
426 /*!
427   \brief Update internal modification time just after data model update
428 */
429 void SUIT_DataBrowser::onModelUpdated()
430 {
431   setModified();
432 }
433
434 /*!
435   \brief Called when item is clicked in the tree view
436   \internal
437   
438   Emits signal clicked( SUIT_DataObject* );
439 */
440 void SUIT_DataBrowser::onClicked( const QModelIndex& index )
441 {
442   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
443
444   if ( m ) {
445     SUIT_DataObject* obj = m->object( index );
446     if ( obj ) { 
447       emit( clicked( obj ) );
448       m->emitClicked(obj, index);
449     }
450   }
451 }
452
453 /*!
454   \brief Called when item is double-clicked in the tree view
455   \internal
456   
457   Emits signal doubleClicked( SUIT_DataObject* );
458 */
459 void SUIT_DataBrowser::onDblClicked( const QModelIndex& index )
460 {
461   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
462
463   if ( m ) {
464     SUIT_DataObject* obj = m->object( index );
465     if ( obj ) emit( doubleClicked( obj ) );
466   }
467 }
468
469 /*!
470   \brief Called when item specified by index is expanded.
471   \internal
472 */
473 void SUIT_DataBrowser::onExpanded( const QModelIndex& index )
474 {
475   if (myResizeOnExpandItem) {
476     adjustFirstColumnWidth();
477     adjustColumnsWidth();
478   }
479 }
480
481 /*!
482   \brief Make editable selected item in place.
483   \internal
484 */
485 void SUIT_DataBrowser::onStartEditing() {
486   DataObjectList sel = getSelected();
487   SUIT_ProxyModel* m = qobject_cast<SUIT_ProxyModel*>( model() );
488   if(treeView() && m && sel.count() == 1){
489     treeView()->edit(m->index( sel.first(), SUIT_DataObject::NameId ));
490   }
491 }
492