Salome HOME
Update from BR_V5_DEV 13Feb2009
[modules/gui.git] / src / Qtx / QtxTreeView.cxx
1 //  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 //  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 //  This library is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU Lesser General Public
8 //  License as published by the Free Software Foundation; either
9 //  version 2.1 of the License.
10 //
11 //  This library is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 //  Lesser General Public License for more details.
15 //
16 //  You should have received a copy of the GNU Lesser General Public
17 //  License along with this library; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 // File:      QtxTreeView.cxx
23 // Author:    Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
24 //
25 #include "QtxTreeView.h"
26
27 #include <QHeaderView>
28 #include <QMenu>
29 #include <QMouseEvent>
30
31 /*!
32   \class QtxTreeView::Header
33   \brief Custom tree view header class.
34   \internal
35 */
36
37 class QtxTreeView::Header : public QHeaderView
38 {
39 public:
40   Header( const bool, QWidget* = 0 );
41   ~Header();
42
43   void     setSortMenuEnabled( const bool );
44   bool     sortMenuEnabled() const;
45
46 protected:
47   void     contextMenuEvent( QContextMenuEvent* );
48
49 private:
50   bool     myEnableSortMenu;
51 };
52
53 /*!
54   \brief Constructor
55   \param enableSortMenu show "Sorting" menu if \c true
56   \param parent parent widget
57   \internal
58 */
59 QtxTreeView::Header::Header( const bool enableSortMenu, QWidget* parent )
60 : QHeaderView( Qt::Horizontal, parent ),
61   myEnableSortMenu( enableSortMenu )
62 {
63 }
64
65 /*!
66   \brief Destructor
67   \internal
68 */
69 QtxTreeView::Header::~Header()
70 {
71 }
72
73 /*
74   \brief Enable/disable "Sorting" popup menu command for the header.
75   \param enableSortMenu if \c true, enable "Sorting" menu command
76   \internal
77 */
78 void QtxTreeView::Header::setSortMenuEnabled( const bool enableSortMenu )
79 {
80   myEnableSortMenu = enableSortMenu;
81 }
82
83 /*
84   \brief Check if "Sorting" popup menu command for the header is enabled.
85   \return \c true if "Sorting" menu command is enabled
86   \internal
87 */
88 bool QtxTreeView::Header::sortMenuEnabled() const
89 {
90   return myEnableSortMenu;
91 }
92
93 /*!
94   \brief Customize context menu event.
95   \internal
96
97   Shows popup menu with the list of the available columns allowing the user to
98   show/hide the specified column.
99
100   \param e context menu event
101 */
102 void QtxTreeView::Header::contextMenuEvent( QContextMenuEvent* e )
103 {
104   QMenu menu;
105   QMap<QAction*, int> actionMap;
106   for ( int i = 0; i < count(); i++ ) {
107     QString  lab         = model()->headerData( i, orientation(), Qt::DisplayRole ).toString();
108     QVariant iconData    = model()->headerData( i, orientation(), Qt::DecorationRole );
109     QVariant appropriate = model()->headerData( i, orientation(), Qtx::AppropriateRole );
110     QIcon icon;
111     if ( iconData.isValid() ) {
112       if ( qVariantCanConvert<QIcon>( iconData ) )
113         icon = qVariantValue<QIcon>( iconData );
114       else if ( qVariantCanConvert<QPixmap>( iconData ) )
115         icon = qVariantValue<QPixmap>( iconData );
116     }
117     if( ( !lab.isEmpty() || !icon.isNull() ) && 
118             appropriate.isValid() ? appropriate.toInt()==Qtx::Toggled : true )
119         {
120       QAction* a = menu.addAction( icon, lab );
121       a->setCheckable( true );
122       a->setChecked( !isSectionHidden( i ) );
123       actionMap.insert( a, i );
124     }
125   }
126   QAction* sortAction = 0;
127   if ( count() > 0 && myEnableSortMenu ) {
128     menu.addSeparator();
129     sortAction = menu.addAction( tr( "Enable sorting" ) );
130     sortAction->setCheckable( true );
131     sortAction->setChecked( isSortIndicatorShown() );
132   }
133   if ( !menu.isEmpty() ) {
134     Qtx::simplifySeparators( &menu );
135     QAction* a = menu.exec( e->globalPos() );
136     if ( a && actionMap.contains( a ) ) {
137       setSectionHidden( actionMap[ a ], !isSectionHidden( actionMap[ a ] ) );
138     }
139     else if ( a && a == sortAction ) {
140       setSortIndicatorShown( a->isChecked() );
141       setClickable( a->isChecked() );
142       QtxTreeView* view = qobject_cast<QtxTreeView*>( parent() );
143       if ( view ) {
144         view->emitSortingEnabled( a->isChecked() );
145         if ( a->isChecked() ) {
146           connect( this, SIGNAL( sectionClicked( int ) ), view, SLOT( onHeaderClicked( int ) ) );
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()->setMovable( 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()->setMovable( 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   setCurrentIndex( QModelIndex() );
333   QTreeView::rowsAboutToBeRemoved( parent, start, end );
334 }
335
336 /*!
337   \brief Expand/collapse the specified item (recursively).
338   \param index model index
339   \param levels number of levels to be expanded/collapsed
340   \param open if \c true, item is expanded, otherwise it is collapsed
341   \sa expandLevels(), collapseLevels()
342 */
343 void QtxTreeView::setOpened( const QModelIndex& index, const int levels, bool open )
344 {
345   if ( !levels )
346     return;
347
348   if ( !index.isValid() && index != rootIndex() )
349     return;
350
351   setExpanded( index, open );
352
353   for ( int i = 0; i < model()->rowCount( index ); i++ ) {
354     QModelIndex child = model()->index( i, 0, index );
355     setOpened( child, levels-1, open );
356   }
357 }
358
359 /*!
360   \fn QtxTreeView::sortingEnabled( bool on );
361   \brief Emitted when "Sorting" commans is enabled/disabled from the popup menu.
362   \param on \c true if sorting is enabled and \c false otherwise
363 */
364
365 /*!
366   \fn QtxTreeView::selectionChanged();
367   \brief Emitted when selection is changed in the tree view.
368 */
369
370 /*!
371   \brief Emit sortingEnabled(bool) signal.
372   \param enabled "enable sorting" flag state
373 */
374 void QtxTreeView::emitSortingEnabled( bool enabled )
375 {
376   emit( sortingEnabled( enabled ) );
377 }
378
379 void QtxTreeView::setModel( QAbstractItemModel* m )
380 {
381   if( model() )
382     disconnect( model(), SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
383                 this, SLOT( onAppropriate( Qt::Orientation, int, int ) ) );
384   QTreeView::setModel( m );
385   if ( model() )
386     connect( model(), SIGNAL( headerDataChanged( Qt::Orientation, int, int ) ),
387              this, SLOT( onAppropriate( Qt::Orientation, int, int ) ) );
388 }
389
390 void QtxTreeView::onAppropriate( Qt::Orientation orient, int first, int last )
391 {
392   if( orient==Qt::Horizontal )
393     for( int i=first; i<=last; i++ )
394         {
395           int appr = model()->headerData( i, orient, Qtx::AppropriateRole ).toInt();
396           header()->setSectionHidden( i, appr==Qtx::Hidden );
397         }
398 }