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