QModelIndex FlatProxyModel::mapToSource(const QModelIndex &proxyIndex) const { if (!proxyIndex.isValid()) return QModelIndex(); Q_ASSERT(proxyIndex.model() == this); Q_ASSERT(_rootSourceItem); int row = proxyIndex.row(); QModelIndex sourceParent; SourceItem *sourceItem = _rootSourceItem->findChild(row); while (sourceItem) { if (sourceItem->pos() == row) { return sourceModel()->index(sourceItem->sourceRow(), proxyIndex.column(), sourceParent); } else { sourceParent = sourceModel()->index(sourceItem->sourceRow(), 0, sourceParent); sourceItem = sourceItem->findChild(row); } } qWarning() << "FlatProxyModel::mapToSource(): couldn't find source index for" << proxyIndex; Q_ASSERT(false); return QModelIndex(); // make compilers happy :) }
void SourceView::updateSourceItems() { setColumnWidth(1, 50); setColumnWidth(2, _costType2 ? 50:0); // Allow resizing of column 2 setColumnWidthMode(2, QListView::Maximum); if (_costType) setColumnText(1, _costType->name()); if (_costType2) setColumnText(2, _costType2->name()); SourceItem* si; QListViewItem* item = firstChild(); for (;item;item = item->nextSibling()) { si = (SourceItem*)item; TraceLine* l = si->line(); if (!l) continue; si->updateCost(); QListViewItem *next, *i = si->firstChild(); for (;i;i = next) { next = i->nextSibling(); ((SourceItem*)i)->updateCost(); } } if (!_costType2) { setColumnWidthMode(2, QListView::Manual); setColumnWidth(2, 0); } }
QModelIndex FlatProxyModel::mapFromSource(const QModelIndex &sourceIndex) const { if (!sourceIndex.isValid()) return QModelIndex(); SourceItem *sourceItem = sourceToInternal(sourceIndex); Q_ASSERT(sourceItem); return createIndex(sourceItem->pos(), sourceIndex.column(), sourceItem); }
FlatProxyModel::SourceItem *FlatProxyModel::insertSubTreeHelper(SourceItem *parentItem, SourceItem *lastItem_, const QModelIndex &source_idx) { SourceItem *lastItem = lastItem_; SourceItem *newItem = 0; for (int row = 0; row < sourceModel()->rowCount(source_idx); row++) { newItem = new SourceItem(row, parentItem); newItem->setPos(lastItem->pos() + 1); lastItem->setNext(newItem); lastItem = insertSubTreeHelper(newItem, newItem, sourceModel()->index(row, 0, source_idx)); } return lastItem; }
void SourceView::doUpdate(int changeType) { // Special case ? if (changeType == selectedItemChanged) { if (!_selectedItem) { clearSelection(); return; } TraceLine* sLine = 0; if (_selectedItem->type() == TraceItem::Line) sLine = (TraceLine*) _selectedItem; if (_selectedItem->type() == TraceItem::Instr) sLine = ((TraceInstr*)_selectedItem)->line(); SourceItem* si = (SourceItem*)QListView::selectedItem(); if (si) { if (si->line() == sLine) return; if (si->lineCall() && (si->lineCall()->call()->called() == _selectedItem)) return; } QListViewItem *item, *item2; for (item = firstChild();item;item = item->nextSibling()) { si = (SourceItem*)item; if (si->line() == sLine) { ensureItemVisible(item); _inSelectionUpdate = true; setCurrentItem(item); _inSelectionUpdate = false; break; } item2 = item->firstChild(); for (;item2;item2 = item2->nextSibling()) { si = (SourceItem*)item2; if (!si->lineCall()) continue; if (si->lineCall()->call()->called() == _selectedItem) { ensureItemVisible(item2); _inSelectionUpdate = true; setCurrentItem(item2); _inSelectionUpdate = false; break; } } if (item2) break; } return; } if (changeType == groupTypeChanged) { QListViewItem *item, *item2; for (item = firstChild();item;item = item->nextSibling()) for (item2 = item->firstChild();item2;item2 = item2->nextSibling()) ((SourceItem*)item2)->updateGroup(); } refresh(); }
void SourceItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { int column = index.column(); SourceItem* item = static_cast<SourceItem*>(index.internalPointer()); QColor color; if ( !item->inside() || ((column==1) || (column==2))) color = option.palette.color( QPalette::Button ); else if ((item->lineCall() || item->lineJump()) && column>2) color = option.palette.color( QPalette::Midlight ); if (color.isValid()) _parent->model()->setData(index, color, Qt::BackgroundRole); if(column==3) paintArrows(painter, option, index); else QItemDelegate::paint(painter, option, index); }
void LovedTracksItem::activate() { if ( !m_lovedTracksPage ) { SourceItem* par = dynamic_cast< SourceItem* >( parent() ); PlaylistViewPage* pv = new PlaylistViewPage( ViewManager::instance()->widget() ); pv->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::LovedPlaylist, TomahawkUtils::Original, QSize( 128, 128 ) ) ); TopLovedTracksModel* raModel = new TopLovedTracksModel( pv ); raModel->setTitle( text() ); TrackItemDelegate* del = new TrackItemDelegate( TrackItemDelegate::LovedTracks, pv->view()->trackView(), pv->view()->trackView()->proxyModel() ); pv->view()->trackView()->setPlaylistItemDelegate( del ); pv->view()->trackView()->setEmptyTip( tr( "Sorry, we could not find any of your Favorites!" ) ); if ( !par ) { raModel->setDescription( tr( "The most loved tracks from all your friends" ) ); pv->view()->setGuid( QString( "lovedtracks" ) ); } else { if ( par->source()->isLocal() ) raModel->setDescription( tr( "All of your loved tracks" ) ); else raModel->setDescription( tr( "All of %1's loved tracks" ).arg( par->source()->friendlyName() ) ); pv->view()->setGuid( QString( "lovedtracks/%1" ).arg( par->source()->nodeId() ) ); } pv->view()->trackView()->setPlayableModel( raModel ); raModel->setSource( !par ? source_ptr() : par->source() ); m_lovedTracksPage = pv; } ViewManager::instance()->show( m_lovedTracksPage ); model()->linkSourceItemToPage( this, m_lovedTracksPage ); }
QSize SourceDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const { SourceTreeItem* item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >(); SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() ); if ( type == SourcesModel::Source || type == SourcesModel::ScriptCollection ) { SourceItem* colItem = qobject_cast< SourceItem* >( item ); return QSize( option.rect.width(), ( colItem && colItem->source() && colItem->source()->isLocal() ) ? 0 : option.fontMetrics.height() * 3.2 ); } else if ( type == SourcesModel::Divider ) { return QSize( option.rect.width(), TomahawkUtils::DpiScaler::scaledY( m_parent, 6 ) ); } else if ( type == SourcesModel::Group ) { const int groupSpacer = index.row() > 0 ? option.fontMetrics.height() * 2.5 : option.fontMetrics.height() * 1.8; return QSize( option.rect.width(), option.fontMetrics.height() + groupSpacer ); } else return QSize( option.rect.width(), option.fontMetrics.height() * 1.8 ); //QStyledItemDelegate::sizeHint( option, index ) ); }
QModelIndex FlatProxyModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid()) { qWarning() << "FlatProxyModel::index() called with valid parent:" << parent; return QModelIndex(); } if (!_rootSourceItem) { qWarning() << "FlatProxyModel::index() while model has no root Item"; return QModelIndex(); } SourceItem *item = _rootSourceItem; while (item->pos() != row) { item = item->findChild(row); if (!item) { qWarning() << "FlatProxyModel::index() no such row:" << row; return QModelIndex(); } } Q_ASSERT(item->pos() == row); return createIndex(row, column, item); }
QItemSelection FlatProxyModel::mapSelectionToSource(const QItemSelection &proxySelection) const { QItemSelection sourceSelection; for (int i = 0; i < proxySelection.count(); i++) { const QItemSelectionRange &range = proxySelection[i]; SourceItem *topLeftItem = 0; SourceItem *bottomRightItem = 0; SourceItem *currentItem = static_cast<SourceItem *>(range.topLeft().internalPointer()); int row = range.topLeft().row(); int left = range.topLeft().column(); int right = range.bottomRight().column(); while (currentItem && row <= range.bottomRight().row()) { Q_ASSERT(currentItem->pos() == row); if (!topLeftItem) topLeftItem = currentItem; if (currentItem->parent() == topLeftItem->parent()) { bottomRightItem = currentItem; } else { Q_ASSERT(topLeftItem && bottomRightItem); sourceSelection << QItemSelectionRange(mapToSource(createIndex(topLeftItem->pos(), left, topLeftItem)), mapToSource(createIndex(bottomRightItem->pos(), right, bottomRightItem))); topLeftItem = 0; bottomRightItem = 0; } // update loop vars currentItem = currentItem->next(); row++; } if (topLeftItem && bottomRightItem) { // there should be one range left. sourceSelection << QItemSelectionRange(mapToSource(createIndex(topLeftItem->pos(), left, topLeftItem)), mapToSource(createIndex(bottomRightItem->pos(), right, bottomRightItem))); } } return sourceSelection; }
void SourceTreeView::setupMenus() { m_playlistMenu.clear(); m_roPlaylistMenu.clear(); m_latchMenu.clear(); m_privacyMenu.clear(); bool readonly = true; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); if ( type == SourcesModel::StaticPlaylist || type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) { const PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex ); const playlist_ptr playlist = item->playlist(); if ( !playlist.isNull() ) { readonly = !playlist->author()->isLocal(); } } QAction* latchOnAction = ActionCollection::instance()->getAction( "latchOn" ); m_latchMenu.addAction( latchOnAction ); m_privacyMenu.addAction( ActionCollection::instance()->getAction( "togglePrivacy" ) ); if ( type == SourcesModel::Collection ) { SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex ); source_ptr source = item->source(); if ( !source.isNull() ) { if ( m_latchManager->isLatched( source ) ) { QAction *latchOffAction = ActionCollection::instance()->getAction( "latchOff" ); m_latchMenu.addAction( latchOffAction ); connect( latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); m_latchMenu.addSeparator(); QAction *latchRealtimeAction = ActionCollection::instance()->getAction( "realtimeFollowingAlong" ); latchRealtimeAction->setChecked( source->playlistInterface()->latchMode() == Tomahawk::PlaylistModes::RealTime ); m_latchMenu.addAction( latchRealtimeAction ); connect( latchRealtimeAction, SIGNAL( toggled( bool ) ), SLOT( latchModeToggled( bool ) ) ); } } } QAction *loadPlaylistAction = ActionCollection::instance()->getAction( "loadPlaylist" ); m_playlistMenu.addAction( loadPlaylistAction ); QAction *renamePlaylistAction = ActionCollection::instance()->getAction( "renamePlaylist" ); m_playlistMenu.addAction( renamePlaylistAction ); m_playlistMenu.addSeparator(); QAction *copyPlaylistAction = m_playlistMenu.addAction( tr( "&Copy Link" ) ); QAction *deletePlaylistAction = m_playlistMenu.addAction( tr( "&Delete %1" ).arg( SourcesModel::rowTypeToString( type ) ) ); QString addToText; if ( type == SourcesModel::StaticPlaylist ) addToText = tr( "Add to my Playlists" ); if ( type == SourcesModel::AutomaticPlaylist ) addToText = tr( "Add to my Automatic Playlists" ); else if ( type == SourcesModel::Station ) addToText = tr( "Add to my Stations" ); QAction *addToLocalAction = m_roPlaylistMenu.addAction( addToText ); m_roPlaylistMenu.addAction( copyPlaylistAction ); deletePlaylistAction->setEnabled( !readonly ); renamePlaylistAction->setEnabled( !readonly ); addToLocalAction->setEnabled( readonly ); // Handle any custom actions registered for playlists if ( type == SourcesModel::StaticPlaylist && !readonly && !ActionCollection::instance()->getAction( ActionCollection::LocalPlaylists ).isEmpty() ) { m_playlistMenu.addSeparator(); const PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex ); const playlist_ptr playlist = item->playlist(); foreach ( QAction* action, ActionCollection::instance()->getAction( ActionCollection::LocalPlaylists ) ) { if ( QObject* notifier = ActionCollection::instance()->actionNotifier( action ) ) { QMetaObject::invokeMethod( notifier, "aboutToShow", Qt::DirectConnection, Q_ARG( QAction*, action ), Q_ARG( Tomahawk::playlist_ptr, playlist ) ); } action->setProperty( "payload", QVariant::fromValue< playlist_ptr >( playlist ) ); m_playlistMenu.addAction( action ); }
void SourceTreeView::setupMenus() { m_playlistMenu.clear(); m_roPlaylistMenu.clear(); m_latchMenu.clear(); m_privacyMenu.clear(); bool readonly = true; SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt(); if ( type == SourcesModel::StaticPlaylist || type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station ) { PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex ); playlist_ptr playlist = item->playlist(); if ( !playlist.isNull() ) { readonly = !playlist->author()->isLocal(); } } QAction* latchOnAction = ActionCollection::instance()->getAction( "latchOn" ); m_latchMenu.addAction( latchOnAction ); m_privacyMenu.addAction( ActionCollection::instance()->getAction( "togglePrivacy" ) ); if ( type == SourcesModel::Collection ) { SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex ); source_ptr source = item->source(); if ( !source.isNull() ) { if ( m_latchManager->isLatched( source ) ) { QAction *latchOffAction = ActionCollection::instance()->getAction( "latchOff" ); m_latchMenu.addAction( latchOffAction ); connect( latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) ); m_latchMenu.addSeparator(); QAction *latchRealtimeAction = ActionCollection::instance()->getAction( "realtimeFollowingAlong" ); latchRealtimeAction->setChecked( source->playlistInterface()->latchMode() == Tomahawk::PlaylistInterface::RealTime ); m_latchMenu.addAction( latchRealtimeAction ); connect( latchRealtimeAction, SIGNAL( toggled( bool ) ), SLOT( latchModeToggled( bool ) ) ); } } } QAction *loadPlaylistAction = ActionCollection::instance()->getAction( "loadPlaylist" ); m_playlistMenu.addAction( loadPlaylistAction ); QAction *renamePlaylistAction = ActionCollection::instance()->getAction( "renamePlaylist" ); m_playlistMenu.addAction( renamePlaylistAction ); m_playlistMenu.addSeparator(); QAction *copyPlaylistAction = m_playlistMenu.addAction( tr( "&Copy Link" ) ); QAction *deletePlaylistAction = m_playlistMenu.addAction( tr( "&Delete %1" ).arg( SourcesModel::rowTypeToString( type ) ) ); QString addToText = QString( "Add to my %1" ); if ( type == SourcesModel::StaticPlaylist ) addToText = addToText.arg( "Playlists" ); if ( type == SourcesModel::AutomaticPlaylist ) addToText = addToText.arg( "Automatic Playlists" ); else if ( type == SourcesModel::Station ) addToText = addToText.arg( "Stations" ); QAction *addToLocalAction = m_roPlaylistMenu.addAction( tr( addToText.toUtf8(), "Adds the given playlist, dynamic playlist, or station to the users's own list" ) ); m_roPlaylistMenu.addAction( copyPlaylistAction ); deletePlaylistAction->setEnabled( !readonly ); renamePlaylistAction->setEnabled( !readonly ); addToLocalAction->setEnabled( readonly ); if ( type == SourcesModel::StaticPlaylist ) copyPlaylistAction->setText( tr( "&Export Playlist" ) ); connect( loadPlaylistAction, SIGNAL( triggered() ), SLOT( loadPlaylist() ) ); connect( renamePlaylistAction, SIGNAL( triggered() ), SLOT( renamePlaylist() ) ); connect( deletePlaylistAction, SIGNAL( triggered() ), SLOT( deletePlaylist() ) ); connect( copyPlaylistAction, SIGNAL( triggered() ), SLOT( copyPlaylistLink() ) ); connect( addToLocalAction, SIGNAL( triggered() ), SLOT( addToLocal() ) ); connect( latchOnAction, SIGNAL( triggered() ), SLOT( latchOnOrCatchUp() ), Qt::QueuedConnection ); }
void SourceItemDelegate::paintArrows(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { QTreeWidget *lv = _parent; if ( !lv ) return; SourceView* sv = (SourceView*) lv; SourceItem* item = static_cast<SourceItem*>(index.internalPointer()); const QRect& rect = option.rect; int height = rect.height(); p->save(); drawBackground(p, option, index); p->translate(rect.topLeft()); int marg = 1; int yy = height/2, y1, y2; QColor c; int start = -1, end = -1; TraceLineJump* lineJump = item->lineJump(); uint lineno = item->lineno(); TraceLineCall* lineCall = item->lineCall(); // draw line borders, detect start/stop of a line for(int i=0; i< item->jumpCount(); i++) { TraceLineJump* jump = item->jump(i); if (jump == 0) continue; y1 = 0; y2 = height; if (lineJump && (lineJump->lineTo() == jump->lineTo()) && (jump->lineFrom()->lineno() == lineno)) { if (start<0) start = i; if (lineJump == jump) { if (jump->lineTo()->lineno() <= lineno) y2 = yy; else y1 = yy; } } else if (!lineJump && !lineCall && (jump->lineTo()->lineno() == lineno)) { if (end<0) end = i; if (jump->lineFrom()->lineno() < lineno) y2 = yy; else y1 = yy; } c = jump->isCondJump() ? Qt::red : Qt::blue; p->fillRect( marg + 6*i, y1, 4, y2, c); p->setPen(c.light()); p->drawLine( marg + 6*i, y1, marg + 6*i, y2); p->setPen(c.dark()); p->drawLine( marg + 6*i +3, y1, marg + 6*i +3, y2); } // draw start/stop horizontal line int x, y = yy-2, w, h = 4; if (start >= 0) { c = item->jump(start)->isCondJump() ? Qt::red : Qt::blue; x = marg + 6*start; w = 6*(sv->arrowLevels() - start) + 10; p->fillRect( x, y, w, h, c); p->setPen(c.light()); p->drawLine(x, y, x+w-1, y); p->drawLine(x, y, x, y+h-1); p->setPen(c.dark()); p->drawLine(x+w-1, y, x+w-1, y+h-1); p->drawLine(x+1, y+h-1, x+w-1, y+h-1); } if (end >= 0) { c = item->jump(end)->isCondJump() ? Qt::red : Qt::blue; x = marg + 6*end; w = 6*(sv->arrowLevels() - end) + 10; QPolygon a; a.putPoints(0, 8, x,y+h, x,y, x+w-8,y, x+w-8,y-2, x+w,yy, x+w-8,y+h+2, x+w-8,y+h, x,y+h); p->setBrush(c); p->drawConvexPolygon(a); p->setPen(c.light()); p->drawPolyline(a.constData(), 5); p->setPen(c.dark()); p->drawPolyline(a.constData() + 4, 2); p->setPen(c.light()); p->drawPolyline(a.constData() + 5, 2); p->setPen(c.dark()); p->drawPolyline(a.constData() + 6, 2); } // draw inner vertical line for start/stop // this overwrites borders of horizontal line for(int i=0;i< item->jumpCount();i++) { TraceLineJump* jump = item->jump(i); if (jump == 0) continue; c = jump->isCondJump() ? Qt::red : Qt::blue; if (jump->lineFrom()->lineno() == lineno) { bool drawUp = true; if (jump->lineTo()->lineno() == lineno) if (start<0) drawUp = false; if (jump->lineTo()->lineno() > lineno) drawUp = false; if (drawUp) p->fillRect( marg + 6*i +1, 0, 2, yy, c); else p->fillRect( marg + 6*i +1, yy, 2, height-yy, c); } else if (jump->lineTo()->lineno() == lineno) { if (end<0) end = i; if (jump->lineFrom()->lineno() < lineno) p->fillRect( marg + 6*i +1, 0, 2, yy, c); else p->fillRect( marg + 6*i +1, yy, 2, height-yy, c); } } p->restore(); }
void SourceDelegate::paintSource( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { painter->save(); painter->setPen( Qt::black ); QFont font = painter->font(); font.setPointSize( TomahawkUtils::defaultFontSize() ); painter->setFont( font ); SourceTreeItem* item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >(); SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() ); const int iconRectVertMargin = m_margin / 4; const QRect iconRect = option.rect.adjusted( m_margin / 2 + m_margin / 4, iconRectVertMargin, -option.rect.width() + option.rect.height() + m_margin / 4, -iconRectVertMargin ); QString name = index.data().toString(); QPixmap avatar; int figWidth = 0; bool isPlaying = false; QString desc; QString tracks; bool shouldDrawDropHint = false; if ( type == SourcesModel::Source ) { // If the user is about to drop a track on a peer QRect itemsRect = option.rect; QPoint cursorPos = m_parent->mapFromGlobal( QCursor::pos() ); bool cursorInRect = itemsRect.contains( cursorPos ); if ( cursorInRect && m_dropHoverIndex.isValid() && m_dropHoverIndex == index ) shouldDrawDropHint = true; SourceItem* colItem = qobject_cast< SourceItem* >( item ); Q_ASSERT( colItem ); bool status = !( !colItem->source() || !colItem->source()->isOnline() ); if ( colItem->source() && !colItem->source()->isLocal() ) { if ( status ) { tracks = QString::number( colItem->source()->trackCount() ); if ( shouldDrawDropHint ) figWidth = iconRect.width(); name = colItem->source()->friendlyName(); } avatar = colItem->pixmap( iconRect.size() ); isPlaying = !colItem->source()->currentTrack().isNull(); desc = colItem->source()->textStatus(); if ( colItem->source().isNull() ) desc = tr( "All available tracks" ); } } else if ( type == SourcesModel::ScriptCollection ) { ScriptCollectionItem* scItem = qobject_cast< ScriptCollectionItem* >( item ); Q_ASSERT( scItem ); if ( !scItem->collection().isNull() ) { int trackCount = scItem->collection()->trackCount(); if ( trackCount >= 0 ) { tracks = QString::number( trackCount ); figWidth = painter->fontMetrics().width( tracks ); } name = scItem->collection()->itemName(); } avatar = scItem->icon().pixmap( iconRect.size() ); desc = qobject_cast< Tomahawk::ScriptCollection* >( scItem->collection().data() )->description(); } painter->setOpacity( 1.0 ); painter->drawPixmap( iconRect, avatar ); QRect textRect = option.rect.adjusted( iconRect.width() + m_margin, m_margin / 6 + m_margin / 32, -figWidth - ( figWidth ? m_margin : 0 ), 0 ); QString text = painter->fontMetrics().elidedText( name, Qt::ElideRight, textRect.width() ); { QTextOption to; to.setWrapMode( QTextOption::NoWrap ); painter->setOpacity( 0.7 ); painter->drawText( textRect, text, to ); } textRect = option.rect.adjusted( iconRect.width() + m_margin, option.rect.height() / 2, -figWidth - ( figWidth ? m_margin : 0 ), -m_margin / 4 ); if ( type == SourcesModel::Source ) { SourceItem* colItem = qobject_cast< SourceItem* >( item ); Q_ASSERT( colItem ); bool privacyOn = TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::FullyPrivate; if ( !colItem->source().isNull() && colItem->source()->isLocal() && privacyOn ) { QRect pmRect = textRect; pmRect.setRight( pmRect.left() + pmRect.height() ); ActionCollection::instance()->getAction( "togglePrivacy" )->icon().paint( painter, pmRect ); textRect.adjust( pmRect.width() + m_margin / 8, 0, 0, 0 ); } if ( ( isPlaying || ( !colItem->source().isNull() && colItem->source()->isLocal() ) ) && !shouldDrawDropHint ) { // Show a listen icon TomahawkUtils::ImageType listenAlongPixmap = TomahawkUtils::Invalid; TomahawkUtils::ImageType realtimeListeningAlongPixmap = TomahawkUtils::Invalid; if ( index.data( SourcesModel::LatchedOnRole ).toBool() ) { // Currently listening along listenAlongPixmap = TomahawkUtils::HeadphonesOn; if ( !colItem->source()->isLocal() ) { realtimeListeningAlongPixmap = colItem->source()->playlistInterface()->latchMode() == Tomahawk::PlaylistModes::RealTime ? TomahawkUtils::PadlockClosed : TomahawkUtils::PadlockOpen; } } else if ( !colItem->source()->isLocal() ) { listenAlongPixmap = TomahawkUtils::HeadphonesOff; } if ( listenAlongPixmap != TomahawkUtils::Invalid ) { QRect pmRect = textRect; pmRect.setRight( pmRect.left() + pmRect.height() ); painter->drawPixmap( pmRect, TomahawkUtils::defaultPixmap( listenAlongPixmap, TomahawkUtils::Original, pmRect.size() ) ); textRect.adjust( pmRect.width() + m_margin / 8, 0, 0, 0 ); m_headphoneRects[ index ] = pmRect; } else m_headphoneRects.remove( index ); if ( realtimeListeningAlongPixmap != TomahawkUtils::Invalid ) { QRect pmRect = textRect; pmRect.setRight( pmRect.left() + pmRect.height() ); painter->drawPixmap( pmRect, TomahawkUtils::defaultPixmap( realtimeListeningAlongPixmap, TomahawkUtils::Original, pmRect.size() ) ); textRect.adjust( pmRect.width() + m_margin / 8, 0, 0, 0 ); m_lockRects[ index ] = pmRect; } else m_lockRects.remove( index ); } } painter->save(); if ( m_trackHovered == index ) { QFont font = painter->font(); font.setUnderline( true ); painter->setFont( font ); } textRect.adjust( 0, 0, 0, m_margin / 16 ); if ( shouldDrawDropHint ) { desc = tr( "Drop to send tracks" ); } text = painter->fontMetrics().elidedText( desc, Qt::ElideRight, textRect.width() - m_margin / 4 ); { QTextOption to( Qt::AlignVCenter ); to.setWrapMode( QTextOption::NoWrap ); painter->setOpacity( 0.4 ); painter->drawText( textRect, text, to ); } painter->restore(); bool shouldPaintTrackCount = false; if ( type == SourcesModel::Source ) { SourceItem* colItem = qobject_cast< SourceItem* >( item ); Q_ASSERT( colItem ); if ( colItem->source() && colItem->source()->currentTrack() && colItem->source()->state() == Tomahawk::SYNCED ) m_trackRects[ index ] = textRect.adjusted( 0, 0, -textRect.width() + painter->fontMetrics().width( text ), 0 ); else m_trackRects.remove( index ); } else if ( type == SourcesModel::ScriptCollection ) { if ( !tracks.isEmpty() ) shouldPaintTrackCount = true; } if ( shouldPaintTrackCount || shouldDrawDropHint ) { if ( shouldDrawDropHint ) { const QRect figRect = option.rect.adjusted( option.rect.width() - figWidth - iconRectVertMargin, iconRectVertMargin, -iconRectVertMargin, -iconRectVertMargin ); painter->drawPixmap( figRect, TomahawkUtils::defaultPixmap( TomahawkUtils::Inbox, TomahawkUtils::Original, figRect.size() ) ); } else { const QRect figRect = option.rect.adjusted( option.rect.width() - figWidth - m_margin / 2, 0, -m_margin / 2, 0 ); painter->drawText( figRect, tracks, QTextOption( Qt::AlignVCenter | Qt::AlignRight ) ); } } painter->restore(); }
QItemSelection FlatProxyModel::mapSelectionFromSource(const QItemSelection &sourceSelection) const { QList<_RangeRect> newRanges; QHash<QModelIndex, SourceItem *> itemLookup; // basics steps of the loop: // 1. convert each source ItemSelectionRange to a mapped Range (internal one for easier post processing) // 2. insert it into the list of previously mapped Ranges sorted by top location for (int i = 0; i < sourceSelection.count(); i++) { const QItemSelectionRange ¤tRange = sourceSelection[i]; QModelIndex currentParent = currentRange.topLeft().parent(); Q_ASSERT(currentParent == currentRange.bottomRight().parent()); SourceItem *parentItem = 0; if (!itemLookup.contains(currentParent)) { parentItem = sourceToInternal(currentParent); itemLookup[currentParent] = parentItem; } else { parentItem = itemLookup[currentParent]; } _RangeRect newRange = { currentRange.topLeft().column(), currentRange.bottomRight().column(), currentRange.topLeft().row(), currentRange.bottomRight().row(), parentItem->child(currentRange.topLeft().row()), parentItem->child(currentRange.bottomRight().row()) }; if (newRanges.isEmpty()) { newRanges << newRange; continue; } _RangeRect &first = newRanges[0]; if (newRange < first) { newRanges.prepend(newRange); continue; } bool inserted = false; for (int j = 0; j < newRanges.count() - 1; j++) { _RangeRect &a = newRanges[j]; _RangeRect &b = newRanges[j + 1]; if (a < newRange && newRange < b) { newRanges[j + 1] = newRange; inserted = true; break; } } if (inserted) continue; _RangeRect &last = newRanges[newRanges.count() - 1]; if (last < newRange) { newRanges.append(newRange); continue; } Q_ASSERT(false); } // we've got a sorted list of ranges now. so we can easily check if there is a possibility to merge for (int i = newRanges.count() - 1; i > 0; i--) { _RangeRect &a = newRanges[i - 1]; _RangeRect &b = newRanges[i]; if (a.left != b.left || a.right != b.right) continue; if (a.bottom < b.top - 1) { continue; } // all merge checks passed! if (b.bottom > a.bottom) { a.bottom = b.bottom; a.bottomItem = b.bottomItem; } // otherwise b is totally enclosed in a -> nothing to do but drop b. newRanges.removeAt(i); } QItemSelection proxySelection; for (int i = 0; i < newRanges.count(); i++) { _RangeRect &r = newRanges[i]; proxySelection << QItemSelectionRange(createIndex(r.top, r.left, r.topItem), createIndex(r.bottom, r.right, r.bottomItem)); } return proxySelection; }
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 FlatProxyModel::removeSubTree(const QModelIndex &source_idx, bool emitRemove) { SourceItem *sourceItem = sourceToInternal(source_idx); if (!sourceItem) return; SourceItem *prevItem = sourceItem->parent(); if (sourceItem->sourceRow() > 0) { prevItem = prevItem->child(sourceItem->sourceRow() - 1); while (prevItem->childCount() > 0) { prevItem = prevItem->child(prevItem->childCount() - 1); } } SourceItem *lastItem = sourceItem; while (lastItem->childCount() > 0) { lastItem = lastItem->child(lastItem->childCount() - 1); } if (emitRemove) beginRemoveRows(QModelIndex(), sourceItem->pos(), lastItem->pos()); int nextPos = 0; if (prevItem) { prevItem->setNext(lastItem->next()); nextPos = prevItem->pos() + 1; } SourceItem *nextItem = lastItem->next(); while (nextItem) { nextItem->setPos(nextPos); nextPos++; nextItem = nextItem->next(); } sourceItem->parent()->removeChild(sourceItem); delete sourceItem; if (emitRemove) endRemoveRows(); }
void FlatProxyModel::insertSubTree(const QModelIndex &source_idx, bool emitInsert) { SourceItem *newSubTree = new SourceItem(source_idx.row(), sourceToInternal(sourceModel()->parent(source_idx))); if (newSubTree->parent()) { newSubTree->setPos(newSubTree->parent()->pos() + source_idx.row() + 1); } SourceItem *lastItem = insertSubTreeHelper(newSubTree, newSubTree, source_idx); Q_ASSERT(lastItem); Q_ASSERT(lastItem->next() == 0); if (emitInsert) beginInsertRows(QModelIndex(), newSubTree->pos(), lastItem->pos()); if (newSubTree->parent()) { if (newSubTree->parent()->childCount() > source_idx.row()) { SourceItem *next = newSubTree->parent()->child(source_idx.row()); lastItem->setNext(next); int nextPos = lastItem->pos() + 1; while (next) { next->setPos(nextPos); next = next->next(); nextPos++; } } if (source_idx.row() > 0) { SourceItem *previous = newSubTree->parent()->child(source_idx.row() - 1); while (previous->childCount() > 0) { previous = previous->child(previous->childCount() - 1); } previous->setNext(newSubTree); } else { newSubTree->parent()->setNext(newSubTree); } } else { _rootSourceItem = newSubTree; } if (emitInsert) endInsertRows(); }