Salome HOME
4da1f00adff15d29e71346bf315682e3c7d97fd2
[modules/gui.git] / src / Qtx / QtxTreeView.cxx
1 // Copyright (C) 2007-2016  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:      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 QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
111       if ( iconData.canConvert( QVariant::Icon ) )
112         icon = iconData.value<QIcon>();
113       else if ( iconData.canConvert( QVariant::Pixmap ) )
114         icon = iconData.value<QPixmap>();
115 #else
116       if ( iconData.canConvert( QMetaType::QIcon ) )
117         icon = iconData.value<QIcon>();
118       else if ( iconData.canConvert( QMetaType::QPixmap ) )
119         icon = iconData.value<QPixmap>();
120 #endif
121     }
122     if( ( !lab.isEmpty() || !icon.isNull() ) && 
123             appropriate.isValid() ? appropriate.toInt()==Qtx::Toggled : true )
124         {
125       QAction* a = menu.addAction( icon, lab );
126       a->setCheckable( true );
127       a->setChecked( !isSectionHidden( i ) );
128       actionMap.insert( a, i );
129     }
130   }
131   QAction* sortAction = 0;
132   if ( count() > 0 && myEnableSortMenu ) {
133     menu.addSeparator();
134     sortAction = menu.addAction( QtxTreeView::tr( "Enable sorting" ) );
135     sortAction->setCheckable( true );
136     sortAction->setChecked( isSortIndicatorShown() );
137   }
138   if ( !menu.isEmpty() ) {
139     Qtx::simplifySeparators( &menu );
140     QAction* a = menu.exec( e->globalPos() );
141     if ( a && actionMap.contains( a ) ) {
142       setSectionHidden( actionMap[ a ], !isSectionHidden( actionMap[ a ] ) );
143     }
144     else if ( a && a == sortAction ) {
145       setSortIndicatorShown( a->isChecked() );
146 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
147       setClickable( a->isChecked() );
148 #else
149       setSectionsClickable( a->isChecked() );
150 #endif
151       QtxTreeView* view = qobject_cast<QtxTreeView*>( parent() );
152       if ( view ) {
153         view->emitSortingEnabled( a->isChecked() );
154         if ( a->isChecked() ) {
155           connect( this, SIGNAL( sectionClicked( int ) ), view, SLOT( onHeaderClicked( int ) ) );
156           setSortIndicator( 0, Qt::AscendingOrder );
157           view->sortByColumn( sortIndicatorSection(), sortIndicatorOrder() );
158         }
159         else {
160           disconnect( this, SIGNAL( sectionClicked( int ) ), view, SLOT( onHeaderClicked( int ) ) );
161           view->sortByColumn( 0, Qt::AscendingOrder );
162         }
163       }
164     }
165   }
166   e->accept();
167 }
168
169
170 /*!
171   \class QtxTreeView
172   \brief Tree view class with possibility to display columns popup menu.
173
174   The QtxTreeView class represents a customized tree view class. In addition to the
175   base functionality inherited from the QTreeView class, clicking at the tree view 
176   header with the right mouse button displays the popup menu allowing the user
177   to show/hide specified columns.
178
179   By default the popup menu contains items corresponding to all the tree view columns.
180   In order to disable some columns from being shown in the popup menu one may customize
181   the data model (see QAbstractItemModel class). The custom model should implement
182   headerData() method and return \c true for the Qtx::AppropriateRole role for
183   those columns which should be available in the popup menu and \c false for the columns
184   which should not be added to it. 
185 */
186
187 /*!
188   \brief Constructor.
189   \param parent parent widget
190 */
191 QtxTreeView::QtxTreeView( QWidget* parent )
192 : QTreeView( parent )
193 {
194   setHeader( new Header( false, this ) );
195 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
196   header()->setMovable( true );
197 #else
198   header()->setSectionsMovable( true );
199 #endif
200 }
201
202 /*!
203   \brief Constructor.
204   \param enableSortMenu show "Sorting" header menu command if \c true
205   \param parent parent widget
206 */
207 QtxTreeView::QtxTreeView( const bool enableSortMenu, QWidget* parent )
208 : QTreeView( parent )
209 {
210   setHeader( new Header( enableSortMenu, this ) );
211 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
212   header()->setMovable( true );
213 #else
214   header()->setSectionsMovable( true );
215 #endif
216 }
217
218 /*!
219   \brief Destructor.
220 */
221 QtxTreeView::~QtxTreeView()
222 {
223 }
224
225 /*!
226   \brief Expand all branches for specified number of levels.
227   
228   If \c levels < 0, all branches are expanded (the same results can
229   be achieved with expandAll() method).
230
231   \param levels number of levels to be opened
232   \sa collapseLevels(), setOpened()
233 */
234 void QtxTreeView::expandLevels( const int levels )
235 {
236   setOpened( rootIndex(), levels+1, true );
237 }
238
239 /*!
240   \brief Collapse all branches for specified number of levels.
241   
242   If \c levels < 0, all branches are collapsed (the same results can
243   be achieved with collapseAll() method).
244
245   \param levels number of levels to be collapsed
246   \sa expandLevels(), setOpened()
247 */
248 void QtxTreeView::collapseLevels( const int levels )
249 {
250   setOpened( rootIndex(), levels+1, false );
251 }
252
253 /*!
254   \brief Expand the branch specifed by the \index and all its
255   children recursively.
256   \param index model index to be expanded
257   \sa collapseAll()
258 */
259 void QtxTreeView::expandAll( const QModelIndex& index )
260 {
261   setOpened( index, -1, true );
262 }
263
264 /*!
265   \brief Collapse the branch specifed by the \index and all its
266   children recursively.
267   \param index model index to be collapsed
268   \sa expandAll()
269 */
270 void QtxTreeView::collapseAll( const QModelIndex& index )
271 {
272   setOpened( index, -1, false );
273 }
274
275 /*
276   \brief Enable/disable "Sorting" popup menu command for the header.
277   \param enableSortMenu if \c true, enable "Sorting" menu command
278   \sa sortMenuEnabled()
279 */
280 void QtxTreeView::setSortMenuEnabled( const bool enableSortMenu )
281 {
282   Header* h = dynamic_cast<Header*>( header() );
283   if ( h )
284     h->setSortMenuEnabled( enableSortMenu );
285 }
286
287 /*
288   \brief Check if "Sorting" popup menu command for the header is enabled.
289   \return \c true if "Sorting" menu command is enabled
290   \sa setSortMenuEnabled()
291 */
292 bool QtxTreeView::sortMenuEnabled() const
293 {
294   Header* h = dynamic_cast<Header*>( header() );
295   return h ? h->sortMenuEnabled() : false;
296 }
297
298 /*!
299   \brief Resizes the given column in order to enclose its contents.
300   The size will be changed only if it is smaller than the size of
301   contents.
302   \param column number of column
303 */
304 void QtxTreeView::resizeColumnToEncloseContents( int column )
305 {
306   if (column < 0 || column >= header()->count())
307     return;
308
309   int contentsSizeHint = sizeHintForColumn(column);
310   int headerSizeHint = header()->isHidden() ? 0 : header()->sectionSizeHint(column);
311   int sizeHint = qMax(contentsSizeHint, headerSizeHint);
312
313   int currentSize = columnWidth( column );
314   if (currentSize < sizeHint)
315     setColumnWidth( column, sizeHint );
316 }
317
318 /*
319   \brief Called when the header section is clicked.
320   \param column header column index
321 */
322 void QtxTreeView::onHeaderClicked( int column )
323 {
324   sortByColumn( column, header()->sortIndicatorOrder() );
325 }
326
327 /*!
328   \brief Called when the selection is changed.
329   
330   Emits selectionChanged() signal.
331   
332   \param selected new selection
333   \param deselected previous selection
334 */
335 void QtxTreeView::selectionChanged( const QItemSelection& selected, 
336                                     const QItemSelection& deselected )
337 {
338   QTreeView::selectionChanged( selected, deselected );
339   emit( selectionChanged() );
340 }
341
342 /*!
343   \brief Called when rows are about to be removed.
344   \param parent model index
345   \param start first row to remove
346   \param end last row to remove
347 */
348 void QtxTreeView::rowsAboutToBeRemoved( const QModelIndex& parent, int start, int end )
349 {
350   QModelIndex curIndex = currentIndex();
351   while ( curIndex.isValid() && curIndex.parent() != parent )
352     curIndex = curIndex.parent();
353   if ( curIndex.isValid() && curIndex.row() >= start && curIndex.row() <= end )
354     setCurrentIndex( QModelIndex() );
355   QTreeView::rowsAboutToBeRemoved( parent, start, end );
356 }
357
358 /*!
359   \brief Expand/collapse the specified item (recursively).
360   \param index model index
361   \param levels number of levels to be expanded/collapsed
362   \param open if \c true, item is expanded, otherwise it is collapsed
363   \sa expandLevels(), collapseLevels()
364 */
365 void QtxTreeView::setOpened( const QModelIndex& index, const int levels, bool open )
366 {
367   if ( !levels )
368     return;
369
370   if ( !index.isValid() && index != rootIndex() )
371     return;
372
373   setExpanded( index, open );
374
375   for ( int i = 0; i < model()->rowCount( index ); i++ ) {
376     QModelIndex child = model()->index( i, 0, index );
377     setOpened( child, levels-1, open );
378   }
379 }
380
381 /*!
382   \fn QtxTreeView::sortingEnabled( bool on );
383   \brief Emitted when "Sorting" commans is enabled/disabled from the popup menu.
384   \param on \c true if sorting is enabled and \c false otherwise
385 */
386
387 /*!
388   \fn QtxTreeView::selectionChanged();
389   \brief Emitted when selection is changed in the tree view.
390 */
391
392 /*!
393   \brief Emit sortingEnabled(bool) signal.
394   \param enabled "enable sorting" flag state
395 */
396 void QtxTreeView::emitSortingEnabled( bool enabled )
397 {
398   emit( sortingEnabled( enabled ) );
399 }
400
401 void QtxTreeView::setModel( QAbstractItemModel* m )
402 {
403   if( model() )
404     disconnect( model(), SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
405                 this, SLOT( onAppropriate( Qt::Orientation, int, int ) ) );
406   QTreeView::setModel( m );
407   if ( model() )
408     connect( model(), SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
409              this, SLOT( onAppropriate( Qt::Orientation, int, int ) ) );
410 }
411
412 void QtxTreeView::onAppropriate( Qt::Orientation orient, int first, int last )
413 {
414   if( orient==Qt::Horizontal )
415     for( int i=first; i<=last; i++ )
416         {
417           int appr = model()->headerData( i, orient, Qtx::AppropriateRole ).toInt();
418           header()->setSectionHidden( i, appr==Qtx::Hidden );
419         }
420 }