File indexing completed on 2024-05-05 04:49:28
0001 /**************************************************************************************** 0002 * Copyright (c) 2008 Nikolaj Hald Nielsen <nhn@kde.org> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #include "PrettyTreeView.h" 0018 0019 #include "PaletteHandler.h" 0020 #include "SvgHandler.h" 0021 #include "widgets/PrettyTreeRoles.h" 0022 #include "widgets/PrettyTreeDelegate.h" 0023 0024 #include <QAction> 0025 #include <QMouseEvent> 0026 #include <QPainter> 0027 #include <QToolTip> 0028 #include <QApplication> 0029 0030 Q_DECLARE_METATYPE( QAction* ) 0031 Q_DECLARE_METATYPE( QList<QAction*> ) 0032 0033 using namespace Amarok; 0034 0035 PrettyTreeView::PrettyTreeView( QWidget *parent ) 0036 : QTreeView( parent ) 0037 , m_decoratorActionPressed( nullptr ) 0038 { 0039 setAlternatingRowColors( true ); 0040 setFrameStyle( QFrame::StyledPanel | QFrame::Sunken ); 0041 0042 The::paletteHandler()->updateItemView( this ); 0043 connect( The::paletteHandler(), &PaletteHandler::newPalette, this, &PrettyTreeView::newPalette ); 0044 0045 #ifdef Q_WS_MAC 0046 // for some bizarre reason w/ some styles on mac per-pixel scrolling is slower than 0047 // per-item 0048 setVerticalScrollMode( QAbstractItemView::ScrollPerItem ); 0049 setHorizontalScrollMode( QAbstractItemView::ScrollPerItem ); 0050 #else 0051 // Scrolling per item is really not smooth and looks terrible 0052 setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); 0053 setHorizontalScrollMode( QAbstractItemView::ScrollPerPixel ); 0054 #endif 0055 0056 setAnimated( true ); 0057 } 0058 0059 PrettyTreeView::~PrettyTreeView() 0060 { 0061 } 0062 0063 void 0064 PrettyTreeView::edit( const QModelIndex &index ) 0065 { 0066 QTreeView::edit( index ); 0067 } 0068 0069 bool 0070 PrettyTreeView::edit( const QModelIndex &index, QAbstractItemView::EditTrigger trigger, QEvent *event ) 0071 { 0072 QModelIndex parent = index.parent(); 0073 while( parent.isValid() ) 0074 { 0075 expand( parent ); 0076 parent = parent.parent(); 0077 } 0078 return QAbstractItemView::edit( index, trigger, event ); 0079 } 0080 0081 void 0082 PrettyTreeView::drawRow( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const 0083 { 0084 QTreeView::drawRow( painter, option, index ); 0085 0086 const int width = option.rect.width(); 0087 const int height = option.rect.height(); 0088 0089 if( height > 0 ) 0090 { 0091 QPixmap background = The::svgHandler()->renderSvgWithDividers( 0092 "service_list_item", width, height, "service_list_item" ); 0093 0094 painter->save(); 0095 painter->drawPixmap( option.rect.topLeft().x(), option.rect.topLeft().y(), background ); 0096 painter->restore(); 0097 } 0098 } 0099 0100 void 0101 PrettyTreeView::mouseMoveEvent( QMouseEvent *event ) 0102 { 0103 // swallow the mouse move event in case the press was started on decorator action icon 0104 if( m_decoratorActionPressed ) 0105 event->accept(); 0106 else 0107 QTreeView::mouseMoveEvent( event ); 0108 0109 // Make sure we repaint the item for the collection action buttons 0110 const QModelIndex index = indexAt( event->pos() ); 0111 const int actionsCount = index.data( PrettyTreeRoles::DecoratorRoleCount ).toInt(); 0112 if( actionsCount ) 0113 update( index ); 0114 } 0115 0116 void 0117 PrettyTreeView::mousePressEvent( QMouseEvent *event ) 0118 { 0119 const QModelIndex index = indexAt( event->pos() ); 0120 0121 // reset state variables on every mouse button press 0122 m_expandCollapsePressedAt.reset(); 0123 m_decoratorActionPressed = nullptr; 0124 0125 // if root is decorated, it doesn't show any actions 0126 QAction *action = rootIsDecorated() ? nullptr : decoratorActionAt( index, event->pos() ); 0127 if( action && 0128 event->button() == Qt::LeftButton && 0129 event->modifiers() == Qt::NoModifier && 0130 state() == QTreeView::NoState ) 0131 { 0132 m_decoratorActionPressed = action; 0133 update( index ); // trigger repaint to change icon effect 0134 event->accept(); 0135 return; 0136 } 0137 0138 bool prevExpandState = isExpanded( index ); 0139 0140 // This will toggle the expansion of the current item when clicking 0141 // on the fold marker but not on the item itself. Required here to 0142 // enable dragging. 0143 QTreeView::mousePressEvent( event ); 0144 0145 // if we press left mouse button on valid item which did not cause the expansion, 0146 // set m_expandCollapsePressedAt so that mouseReleaseEvent can perform the 0147 // expansion/collapsing 0148 if( index.isValid() && 0149 prevExpandState == isExpanded( index ) && 0150 event->button() == Qt::LeftButton && 0151 event->modifiers() == Qt::NoModifier && 0152 state() == QTreeView::NoState ) 0153 { 0154 m_expandCollapsePressedAt.reset( new QPoint( event->pos() ) ); 0155 } 0156 } 0157 0158 void 0159 PrettyTreeView::mouseReleaseEvent( QMouseEvent *event ) 0160 { 0161 const QModelIndex index = indexAt( event->pos() ); 0162 // we want to reset m_expandCollapsePressedAt in either case, but still need its value 0163 QScopedPointer<QPoint> expandCollapsePressedAt( m_expandCollapsePressedAt.take() ); 0164 // ditto for m_decoratorActionPressed 0165 QAction *decoratorActionPressed = m_decoratorActionPressed; 0166 m_decoratorActionPressed = nullptr; 0167 0168 // if root is decorated, it doesn't show any actions 0169 QAction *action = rootIsDecorated() ? nullptr : decoratorActionAt( index, event->pos() ); 0170 if( action && 0171 action == decoratorActionPressed && 0172 event->button() == Qt::LeftButton && 0173 event->modifiers() == Qt::NoModifier ) 0174 { 0175 action->trigger(); 0176 update( index ); // trigger repaint to change icon effect 0177 event->accept(); 0178 return; 0179 } 0180 0181 if( index.isValid() && 0182 event->button() == Qt::LeftButton && 0183 event->modifiers() == Qt::NoModifier && 0184 state() == QTreeView::NoState && 0185 expandCollapsePressedAt && 0186 ( *expandCollapsePressedAt - event->pos() ).manhattanLength() < QApplication::startDragDistance() && 0187 style()->styleHint( QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this ) && 0188 model()->hasChildren( index ) ) 0189 { 0190 setExpanded( index, !isExpanded( index ) ); 0191 event->accept(); 0192 return; 0193 } 0194 0195 QTreeView::mouseReleaseEvent( event ); 0196 } 0197 0198 bool 0199 PrettyTreeView::viewportEvent( QEvent *event ) 0200 { 0201 if( event->type() == QEvent::ToolTip ) 0202 { 0203 QHelpEvent *helpEvent = static_cast<QHelpEvent *>( event ); 0204 const QModelIndex index = indexAt( helpEvent->pos() ); 0205 // if root is decorated, it doesn't show any actions 0206 QAction *action = rootIsDecorated() ? nullptr : decoratorActionAt( index, helpEvent->pos() ); 0207 if( action ) 0208 { 0209 QToolTip::showText( helpEvent->globalPos(), action->toolTip() ); 0210 event->accept(); 0211 return true; 0212 } 0213 } 0214 0215 // swallow the mouse hover event in case the press was started on decorator action icon 0216 // friend mouse move event is handled in mouseMoveEvent and triggers repaints 0217 if( event->type() == QEvent::HoverMove && m_decoratorActionPressed ) 0218 { 0219 event->accept(); 0220 return true; 0221 } 0222 0223 return QAbstractItemView::viewportEvent( event ); 0224 } 0225 0226 QAction * 0227 PrettyTreeView::decoratorActionAt( const QModelIndex &index, const QPoint &pos ) 0228 { 0229 const int actionsCount = index.data( PrettyTreeRoles::DecoratorRoleCount ).toInt(); 0230 if( actionsCount <= 0 ) 0231 return nullptr; 0232 0233 PrettyTreeDelegate* ptd = qobject_cast<PrettyTreeDelegate*>( itemDelegate( index ) ); 0234 if( !ptd ) 0235 return nullptr; 0236 0237 const QList<QAction *> actions = index.data( PrettyTreeRoles::DecoratorRole ).value<QList<QAction *> >(); 0238 QRect rect = visualRect( index ); 0239 0240 for( int i = 0; i < actions.count(); i++ ) 0241 if( ptd->decoratorRect( rect, i ).contains( pos ) ) 0242 return actions.at( i ); 0243 0244 return nullptr; 0245 } 0246 0247 QAction * 0248 PrettyTreeView::pressedDecoratorAction() const 0249 { 0250 return m_decoratorActionPressed; 0251 } 0252 0253 void 0254 PrettyTreeView::newPalette( const QPalette & palette ) 0255 { 0256 Q_UNUSED( palette ) 0257 The::paletteHandler()->updateItemView( this ); 0258 reset(); // redraw all potential delegates 0259 }