bool SourceDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index ) { QMouseEvent* mEvent = 0; switch ( event->type() ) { // case QEvent::MouseTrackingChange: case QEvent::MouseButtonDblClick: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseMove: mEvent = static_cast< QMouseEvent* >( event ); default: break; } bool hoveringTrack = false; if ( m_trackRects.contains( index ) && mEvent ) { const QRect trackRect = m_trackRects[ index ]; hoveringTrack = trackRect.contains( mEvent->pos() ); if ( hoveringTrack ) { if ( m_trackHovered != index ) { m_trackHovered = index; QMetaObject::invokeMethod( m_parent, "update", Qt::QueuedConnection, Q_ARG( QModelIndex, index ) ); } } } if ( !hoveringTrack ) { if ( m_trackHovered.isValid() ) QMetaObject::invokeMethod( m_parent, "update", Qt::QueuedConnection, Q_ARG( QModelIndex, m_trackHovered ) ); m_trackHovered = QPersistentModelIndex(); } bool lockRectContainsClick = false, headphonesRectContainsClick = false; if ( m_headphoneRects.contains( index ) && mEvent ) { const QRect headphoneRect = m_headphoneRects[ index ]; headphonesRectContainsClick = headphoneRect.contains( mEvent->pos() ); } if ( m_lockRects.contains( index ) && mEvent ) { const QRect lockRect = m_lockRects[ index ]; lockRectContainsClick = lockRect.contains( mEvent->pos() ); } if ( event->type() == QEvent::MouseMove ) { if ( hoveringTrack || lockRectContainsClick || headphonesRectContainsClick ) m_parent->setCursor( Qt::PointingHandCursor ); else m_parent->setCursor( Qt::ArrowCursor ); } if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonPress ) { SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() ); if ( type == SourcesModel::TemporaryPage || type == SourcesModel::DeletablePage || type == SourcesModel::RemovablePage ) { SourceTreeItem* gpi = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >(); Q_ASSERT( gpi ); QStyleOptionViewItemV4 o = option; initStyleOption( &o, index ); const int padding = m_margin / 8; const QRect r( o.rect.right() - padding - m_iconHeight, padding + o.rect.y(), m_iconHeight, m_iconHeight ); if ( r.contains( mEvent->pos() ) ) { if ( event->type() == QEvent::MouseButtonRelease && mEvent->button() == Qt::LeftButton ) { gpi->removeFromList(); // Send a new mouse event to the view, since if the mouse is now over another item's delete area we want it to show up QMouseEvent* ev = new QMouseEvent( QEvent::MouseMove, m_parent->viewport()->mapFromGlobal( QCursor::pos() ), Qt::NoButton, Qt::NoButton, Qt::NoModifier ); QApplication::postEvent( m_parent->viewport(), ev ); } return true; } } else if ( type == SourcesModel::Source ) { SourceItem* colItem = qobject_cast< SourceItem* >( index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >() ); Q_ASSERT( colItem ); if ( hoveringTrack && colItem->source() && colItem->source()->currentTrack() ) { if ( event->type() == QEvent::MouseButtonRelease && mEvent->button() == Qt::LeftButton ) { ViewManager::instance()->show( colItem->source()->currentTrack() ); return true; } else if ( event->type() == QEvent::MouseButtonPress && mEvent->button() == Qt::RightButton ) { Tomahawk::ContextMenu* contextMenu = new Tomahawk::ContextMenu( m_parent ); contextMenu->setQuery( colItem->source()->currentTrack() ); contextMenu->exec( QCursor::pos() ); return true; } } if ( !colItem->source().isNull() && !colItem->source()->currentTrack().isNull() && !colItem->source()->isLocal() ) { if ( headphonesRectContainsClick || lockRectContainsClick ) { if ( event->type() == QEvent::MouseButtonRelease && mEvent->button() == Qt::LeftButton ) { if ( headphonesRectContainsClick ) { if ( index.data( SourcesModel::LatchedOnRole ).toBool() ) // unlatch emit latchOff( colItem->source() ); else emit latchOn( colItem->source() ); } else // it's in the lock rect emit toggleRealtimeLatch( colItem->source(), !index.data( SourcesModel::LatchedRealtimeRole ).toBool() ); } return true; } } } else if ( event->type() == QEvent::MouseButtonRelease && mEvent->button() == Qt::LeftButton && type == SourcesModel::StaticPlaylist ) { PlaylistItem* plItem = qobject_cast< PlaylistItem* >( index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >() ); Q_ASSERT( plItem ); if ( plItem->canSubscribe() && !plItem->subscribedIcon().isNull() ) { const int padding = m_margin / 16; const int imgWidth = option.rect.height() - 2 * padding; const QRect subRect( option.rect.right() - padding - imgWidth, option.rect.top() + padding, imgWidth, imgWidth ); if ( subRect.contains( mEvent->pos() ) ) { // Toggle playlist subscription plItem->setSubscribed( !plItem->subscribed() ); } } } } // We emit our own clicked() signal instead of relying on QTreeView's, because that is fired whether or not a delegate accepts // a mouse press event. Since we want to swallow click events when they are on headphones other action items, here we make sure we only // emit if we really want to if ( event->type() == QEvent::MouseButtonRelease && mEvent->button() == Qt::LeftButton ) { if ( m_lastClicked == -1 ) { m_lastClicked = QDateTime::currentMSecsSinceEpoch(); emit clicked( index ); } else { qint64 elapsed = QDateTime::currentMSecsSinceEpoch() - m_lastClicked; if ( elapsed < QApplication::doubleClickInterval() ) { m_lastClicked = -1; emit doubleClicked( index ); } else { m_lastClicked = QDateTime::currentMSecsSinceEpoch(); emit clicked( index ); } } } return QStyledItemDelegate::editorEvent( event, model, option, index ); }
void SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { if ( option.rect.height() == 0 ) return; QStyleOptionViewItemV4 optIndentation = option; QStyleOptionViewItemV4 opt = option; painter->save(); painter->setRenderHint( QPainter::TextAntialiasing ); painter->setRenderHint( QPainter::SmoothPixmapTransform ); const bool selected = ( option.state & QStyle::State_Selected ) == QStyle::State_Selected; if ( selected ) painter->setOpacity( 1.0 ); else painter->setOpacity( 0.7 ); SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() ); SourceTreeItem* item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >(); Q_ASSERT( item ); initStyleOption( &opt, index ); opt.icon = QIcon(); opt.text.clear(); // shrink the indentations { int indentMult = 0; QModelIndex counter = index; while ( counter.parent().isValid() ) { indentMult++; counter = counter.parent(); } const int indentDelta = optIndentation.rect.x() - m_parent->viewport()->x(); optIndentation.rect.setX( optIndentation.rect.x() - indentDelta + indentMult * TomahawkUtils::DpiScaler::scaledY( m_parent, TREEVIEW_INDENT_ADD ) ); opt.rect.setX( 0 ); } if ( type == SourcesModel::Source || type == SourcesModel::ScriptCollection ) { paintSource( painter, optIndentation, index ); } else if ( type == SourcesModel::Group ) { paintGroup( painter, opt, index ); } else if ( type == SourcesModel::Category ) { paintCategory( painter, optIndentation, index ); } else if ( type == SourcesModel::Divider ) { const QRect middle = optIndentation.rect.adjusted( 0, m_margin / 16, 0, -m_margin / 16 ); const QColor bgcolor = opt.palette.color( QPalette::Base ); painter->setPen( bgcolor.darker( 120 ) ); painter->drawLine( middle.topLeft(), middle.topRight() ); painter->setPen( bgcolor.lighter( 120 ) ); painter->drawLine( middle.bottomLeft(), middle.bottomRight() ); } else { optIndentation.state &= ~QStyle::State_MouseOver; if ( !index.parent().parent().isValid() ) optIndentation.rect.adjust( m_margin / 4, 0, 0, 0 ); if ( type == SourcesModel::Inbox || type == SourcesModel::Queue || type == SourcesModel::Collection ) { QString count; if ( type == SourcesModel::Inbox ) { InboxItem* ii = qobject_cast< InboxItem* >( item ); if ( ii && ii->unlistenedCount() ) count = QString::number( ii->unlistenedCount() ); } else if ( type == SourcesModel::Queue ) { QueueItem* qi = qobject_cast< QueueItem* >( item ); if ( qi && qi->unlistenedCount() ) count = QString::number( qi->unlistenedCount() ); } else if ( type == SourcesModel::Collection ) { CollectionItem* ci = qobject_cast< CollectionItem* >( item ); if ( ci ) count = QString::number( ci->trackCount() ); } paintStandardItem( painter, optIndentation, index, count ); } else if ( type == SourcesModel::TemporaryPage || type == SourcesModel::DeletablePage || type == SourcesModel::RemovablePage ) { if ( opt.state & QStyle::State_MouseOver ) { m_iconHeight = ( opt.rect.height() / 2 ); paintStandardItem( painter, optIndentation, index ); // draw close icon const QRect r( opt.rect.right() - m_margin / 8 - m_iconHeight, opt.rect.y() + ( opt.rect.height() - m_iconHeight ) / 2, m_iconHeight, m_iconHeight ); painter->drawPixmap( r, TomahawkUtils::defaultPixmap( TomahawkUtils::ListRemove, TomahawkUtils::Original, r.size() ) ); } else paintStandardItem( painter, optIndentation, index ); } else if ( type == SourcesModel::StaticPlaylist ) { paintStandardItem( painter, optIndentation, index ); PlaylistItem* plItem = qobject_cast< PlaylistItem* >( item ); if ( plItem->canSubscribe() && !plItem->subscribedIcon().isNull() ) { const int imgWidth = optIndentation.rect.height() / 2; const QPixmap icon = plItem->subscribedIcon().scaled( imgWidth, imgWidth, Qt::KeepAspectRatio, Qt::SmoothTransformation ); const QRect subRect( optIndentation.rect.right() - m_margin / 2 - imgWidth, optIndentation.rect.top() + ( optIndentation.rect.height() - imgWidth ) / 2, imgWidth, imgWidth ); painter->drawPixmap( subRect, icon ); } if ( plItem->collaborative() ) { const int imgWidth = optIndentation.rect.height() / 2; const QRect subRect( optIndentation.rect.left(), optIndentation.rect.top(), imgWidth, imgWidth ); painter->drawPixmap( subRect, TomahawkUtils::defaultPixmap( TomahawkUtils::GreenDot, TomahawkUtils::Original, subRect.size() ) ); } } else paintStandardItem( painter, optIndentation, index ); } paintDecorations( painter, opt, index ); painter->restore(); }