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