Salome HOME
Merge from V6_main_20120808 08Aug12
[modules/gui.git] / src / Qtx / QtxTreeView.cxx
1 // Copyright (C) 2007-2012  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.
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:      QtxTreeView.cxx
21 // Author:    Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
22 //
23 #include "QtxTreeView.h"
24
25 #include <QHeaderView>
26 #include <QMenu>
27 #include <QMouseEvent>
28
29 /*!
30   \class QtxTreeView::Header
31   \brief Custom tree view header class.
32   \internal
33 */
34
35 class QtxTreeView::Header : public QHeaderView
36 {
37 public:
38   Header( const bool, QWidget* = 0 );
39   ~Header();
40
41   void     setSortMenuEnabled( const bool );
42   bool     sortMenuEnabled() const;
43
44 protected:
45   void     contextMenuEvent( QContextMenuEvent* );
46
47 private:
48   bool     myEnableSortMenu;
49 };
50
51 /*!
52   \brief Constructor
53   \param enableSortMenu show "Sorting" menu if \c true
54   \param parent parent widget
55   \internal
56 */
57 QtxTreeView::Header::Header( const bool enableSortMenu, QWidget* parent )
58 : QHeaderView( Qt::Horizontal, parent ),
59   myEnableSortMenu( enableSortMenu )
60 {
61 }
62
63 /*!
64   \brief Destructor
65   \internal
66 */
67 QtxTreeView::Header::~Header()
68 {
69 }
70
71 /*
72   \brief Enable/disable "Sorting" popup menu command for the header.
73   \param enableSortMenu if \c true, enable "Sorting" menu command
74   \internal
75 */
76 void QtxTreeView::Header::setSortMenuEnabled( const bool enableSortMenu )
77 {
78   myEnableSortMenu = enableSortMenu;
79 }
80
81 /*
82   \brief Check if "Sorting" popup menu command for the header is enabled.
83   \return \c true if "Sorting" menu command is enabled
84   \internal
85 */
86 bool QtxTreeView::Header::sortMenuEnabled() const
87 {
88   return myEnableSortMenu;
89 }
90
91 /*!
92   \brief Customize context menu event.
93   \internal
94
95   Shows popup menu with the list of the available columns allowing the user to
96   show/hide the specified column.
97
98   \param e context menu event
99 */
100 void QtxTreeView::Header::contextMenuEvent( QContextMenuEvent* e )
101 {
102   QMenu menu;
103   QMap<QAction*, int> actionMap;
104   for ( int i = 0; i < count(); i++ ) {
105     QString  lab         = model()->headerData( i, orientation(), Qt::DisplayRole ).toString();
106     QVariant iconData    = model()->headerData( i, orientation(), Qt::DecorationRole );
107     QVariant appropriate = model()->headerData( i, orientation(), Qtx::AppropriateRole );
108     QIcon icon;
109     if ( iconData.isValid() ) {
110       if ( qVariantCanConvert<QIcon>( iconData ) )
111         icon = qVariantValue<QIcon>( iconData );
112       else if ( qVariantCanConvert<QPixmap>( iconData ) )
113         icon = qVariantValue<QPixmap>( iconData );
114     }
115     if( ( !lab.isEmpty() || !icon.isNull() ) && 
116             appropriate.isValid() ? appropriate.toInt()==Qtx::Toggled : true )
117         {
118       QAction* a = menu.addAction( icon, lab );
119       a->setCheckable( true );
120       a->setChecked( !isSectionHidden( i ) );
121       actionMap.insert( a, i );
122     }
123   }
124   QAction* sortAction = 0;
125   if ( count() > 0 && myEnableSortMenu ) {
126     menu.addSeparator();
127     sortAction = menu.addAction( QtxTreeView::tr( "Enable sorting" ) );
128     sortAction->setCheckable( true );
129     sortAction->setChecked( isSortIndicatorShown() );
130   }
131   if ( !menu.isEmpty() ) {
132     Qtx::simplifySeparators( &menu );
133     QAction* a = menu.exec( e->globalPos() );
134     if ( a && actionMap.contains( a ) ) {
135       setSectionHidden( actionMap[ a ], !isSectionHidden( actionMap[ a ] ) );
136     }
137     else if ( a && a == sortAction ) {
138       setSortIndicatorShown( a->isChecked() );
139       setClickable( a->isChecked() );
140       QtxTreeView* view = qobject_cast<QtxTreeView*>( parent() );
141       if ( view ) {
142         view->emitSortingEnabled( a->isChecked() );
143         if ( a->isChecked() ) {
144           connect( this, SIGNAL( sectionClicked( int ) ), view, SLOT( onHeaderClicked( int ) ) );
145           view->sortByColumn( sortIndicatorSection(), sortIndicatorOrder() );
146         }
147         else {
148           disconnect( this, SIGNAL( sectionClicked( int ) ), view, SLOT( onHeaderClicked( int ) ) );
149           view->sortByColumn( 0, Qt::AscendingOrder );
150         }
151       }
152     }
153   }
154   e->accept();
155 }
156
157
158 /*!
159   \class QtxTreeView
160   \brief Tree view class with possibility to display columns popup menu.
161
162   The QtxTreeView class represents a customized tree view class. In addition to the
163   base functionality inherited from the QTreeView class, clicking at the tree view 
164   header with the right mouse button displays the popup menu allowing the user
165   to show/hide specified columns.
166
167   By default the popup menu contains items corresponding to all the tree view columns.
168   In order to disable some columns from being shown in the popup menu one may customize
169   the data model (see QAbstractItemModel class). The custom model should implement
170   headerData() method and return \c true for the Qtx::AppropriateRole role for
171   those columns which should be available in the popup menu and \c false for the columns
172   which should not be added to it. 
173 */
174
175 /*!
176   \brief Constructor.
177   \param parent parent widget
178 */
179 QtxTreeView::QtxTreeView( QWidget* parent )
180 : QTreeView( parent )
181 {
182   setHeader( new Header( false, this ) );
183   header()->setMovable( true );
184 }
185
186 /*!
187   \brief Constructor.
188   \param enableSortMenu show "Sorting" header menu command if \c true
189   \param parent parent widget
190 */
191 QtxTreeView::QtxTreeView( const bool enableSortMenu, QWidget* parent )
192 : QTreeView( parent )
193 {
194   setHeader( new Header( enableSortMenu, this ) );
195   header()->setMovable( true );
196 }
197
198 /*!
199   \brief Destructor.
200 */
201 QtxTreeView::~QtxTreeView()
202 {
203 }
204
205 /*!
206   \brief Expand all branches for specified number of levels.
207   
208   If \c levels < 0, all branches are expanded (the same results can
209   be achieved with expandAll() method).
210
211   \param levels number of levels to be opened
212   \sa collapseLevels(), setOpened()
213 */
214 void QtxTreeView::expandLevels( const int levels )
215 {
216   setOpened( rootIndex(), levels+1, true );
217 }
218
219 /*!
220   \brief Collapse all branches for specified number of levels.
221   
222   If \c levels < 0, all branches are collapsed (the same results can
223   be achieved with collapseAll() method).
224
225   \param levels number of levels to be collapsed
226   \sa expandLevels(), setOpened()
227 */
228 void QtxTreeView::collapseLevels( const int levels )
229 {
230   setOpened( rootIndex(), levels+1, false );
231 }
232
233 /*!
234   \brief Expand the branch specifed by the \index and all its
235   children recursively.
236   \param index model index to be expanded
237   \sa collapseAll()
238 */
239 void QtxTreeView::expandAll( const QModelIndex& index )
240 {
241   setOpened( index, -1, true );
242 }
243
244 /*!
245   \brief Collapse the branch specifed by the \index and all its
246   children recursively.
247   \param index model index to be collapsed
248   \sa expandAll()
249 */
250 void QtxTreeView::collapseAll( const QModelIndex& index )
251 {
252   setOpened( index, -1, false );
253 }
254
255 /*
256   \brief Enable/disable "Sorting" popup menu command for the header.
257   \param enableSortMenu if \c true, enable "Sorting" menu command
258   \sa sortMenuEnabled()
259 */
260 void QtxTreeView::setSortMenuEnabled( const bool enableSortMenu )
261 {
262   Header* h = dynamic_cast<Header*>( header() );
263   if ( h )
264     h->setSortMenuEnabled( enableSortMenu );
265 }
266
267 /*
268   \brief Check if "Sorting" popup menu command for the header is enabled.
269   \return \c true if "Sorting" menu command is enabled
270   \sa setSortMenuEnabled()
271 */
272 bool QtxTreeView::sortMenuEnabled() const
273 {
274   Header* h = dynamic_cast<Header*>( header() );
275   return h ? h->sortMenuEnabled() : false;
276 }
277
278 /*!
279   \brief Resizes the given column in order to enclose its contents.
280   The size will be changed only if it is smaller than the size of
281   contents.
282   \param column number of column
283 */
284 void QtxTreeView::resizeColumnToEncloseContents( int column )
285 {
286   if (column < 0 || column >= header()->count())
287     return;
288
289   int contentsSizeHint = sizeHintForColumn(column);
290   int headerSizeHint = header()->isHidden() ? 0 : header()->sectionSizeHint(column);
291   int sizeHint = qMax(contentsSizeHint, headerSizeHint);
292
293   int currentSize = columnWidth( column );
294   if (currentSize < sizeHint)
295     setColumnWidth( column, sizeHint );
296 }
297
298 /*
299   \brief Called when the header section is clicked.
300   \param column header column index
301 */
302 void QtxTreeView::onHeaderClicked( int column )
303 {
304   sortByColumn( column, header()->sortIndicatorOrder() );
305 }
306
307 /*!
308   \brief Called when the selection is changed.
309   
310   Emits selectionChanged() signal.
311   
312   \param selected new selection
313   \param deselected previous selection
314 */
315 void QtxTreeView::selectionChanged( const QItemSelection& selected, 
316                                     const QItemSelection& deselected )
317 {
318   QTreeView::selectionChanged( selected, deselected );
319   emit( selectionChanged() );
320 }
321
322 /*!
323   \brief Called when rows are about to be removed.
324   \param parent model index
325   \param start first row to remove
326   \param end last row to remove
327 */
328 void QtxTreeView::rowsAboutToBeRemoved( const QModelIndex& parent, int start, int end )
329 {
330   QModelIndex curIndex = currentIndex();
331   while ( curIndex.isValid() && curIndex.parent() != parent )
332     curIndex = curIndex.parent();
333   if ( curIndex.isValid() && curIndex.row() >= start && curIndex.row() <= end )
334     setCurrentIndex( QModelIndex() );
335   QTreeView::rowsAboutToBeRemoved( parent, start, end );
336 }
337
338 /*!
339   \brief Expand/collapse the specified item (recursively).
340   \param index model index
341   \param levels number of levels to be expanded/collapsed
342   \param open if \c true, item is expanded, otherwise it is collapsed
343   \sa expandLevels(), collapseLevels()
344 */
345 void QtxTreeView::setOpened( const QModelIndex& index, const int levels, bool open )
346 {
347   if ( !levels )
348     return;
349
350   if ( !index.isValid() && index != rootIndex() )
351     return;
352
353   setExpanded( index, open );
354
355   for ( int i = 0; i < model()->rowCount( index ); i++ ) {
356     QModelIndex child = model()->index( i, 0, index );
357     setOpened( child, levels-1, open );
358   }
359 }
360
361 /*!
362   \fn QtxTreeView::sortingEnabled( bool on );
363   \brief Emitted when "Sorting" commans is enabled/disabled from the popup menu.
364   \param on \c true if sorting is enabled and \c false otherwise
365 */
366
367 /*!
368   \fn QtxTreeView::selectionChanged();
369   \brief Emitted when selection is changed in the tree view.
370 */
371
372 /*!
373   \brief Emit sortingEnabled(bool) signal.
374   \param enabled "enable sorting" flag state
375 */
376 void QtxTreeView::emitSortingEnabled( bool enabled )
377 {
378   emit( sortingEnabled( enabled ) );
379 }
380
381 void QtxTreeView::setModel( QAbstractItemModel* m )
382 {
383   if( model() )
384     disconnect( model(), SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
385                 this, SLOT( onAppropriate( Qt::Orientation, int, int ) ) );
386   QTreeView::setModel( m );
387   if ( model() )
388     connect( model(), SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
389              this, SLOT( onAppropriate( Qt::Orientation, int, int ) ) );
390 }
391
392 void QtxTreeView::onAppropriate( Qt::Orientation orient, int first, int last )
393 {
394   if( orient==Qt::Horizontal )
395     for( int i=first; i<=last; i++ )
396         {
397           int appr = model()->headerData( i, orient, Qtx::AppropriateRole ).toInt();
398           header()->setSectionHidden( i, appr==Qtx::Hidden );
399         }
400 }