QDomElement LayoutManager::createItemElement( QDomDocument doc, const QString &name, const LayoutItemConfig & item ) const
{
	QDomElement element = doc.createElement( name );

	QString showIcon = item.showIcon() ? "true" : "false";
	element.setAttribute ( "show_metacontact_icon", showIcon );

	for( int i = 0; i < item.rows(); i++ )
	{
		LayoutItemConfigRow row = item.row( i );

		QDomElement rowElement = doc.createElement( "row" );
		element.appendChild( rowElement );

		for( int j = 0; j < row.count(); j++ ) {
			LayoutItemConfigRowElement element = row.element( j );
			QDomElement elementElement = doc.createElement( "element" );

			elementElement.setAttribute ( "value", m_tokens.at(element.value()).mConfigName );
			elementElement.setAttribute ( "size", QString::number( element.size() ) );
			elementElement.setAttribute ( "bold", element.bold() ? "true" : "false" );
			elementElement.setAttribute ( "italic", element.italic() ? "true" : "false" );
			elementElement.setAttribute ( "small", element.small() ? "true" : "false" );
			elementElement.setAttribute ( "optimalSize", element.optimalSize() ? "true" : "false" );

			QString alignmentString;
			if ( element.alignment() & Qt::AlignLeft )
				alignmentString = "left";
			else  if ( element.alignment() & Qt::AlignRight )
				alignmentString = "right";
			else
				alignmentString = "center";

			elementElement.setAttribute ( "alignment", alignmentString );

			rowElement.appendChild( elementElement );
		}
	}

	return element;
}
Exemplo n.º 2
0
void InlineEditorWidget::createChildWidgets()
{
    QBoxLayout* boxLayout = qobject_cast<QBoxLayout*>( layout() );
    Q_ASSERT( boxLayout );
    boxLayout->setSpacing( 0 );

    //For now, we don't allow editing of the "head" data, just the body
    LayoutItemConfig config = m_layout.layoutForItem( m_index );

    const int rowCount = config.rows();
    if( rowCount == 0 )
        return;

    // we have to use the same metrics as the PrettyItemDelegate or else
    // the widgets will change places when editing
    const int horizontalSpace = style()->pixelMetric( QStyle::PM_LayoutHorizontalSpacing );
    const int frameHMargin = style()->pixelMetric( QStyle::PM_FocusFrameHMargin );
    const int frameVMargin = style()->pixelMetric( QStyle::PM_FocusFrameVMargin );

    int rowOffsetX = frameHMargin; // keep the text a little bit away from the border

    const int coverHeight = m_widgetHeight - frameVMargin * 2;

    const bool showCover = config.showCover();
    if( showCover )
        rowOffsetX += coverHeight + horizontalSpace/* + frameHMargin * 2*/;

    const int contentHeight = m_widgetHeight - frameVMargin * 2;
    int rowHeight = contentHeight / rowCount;
    const int rowWidth = m_widgetWidth - rowOffsetX - frameHMargin * 2;

    if( showCover )
    {
        QModelIndex coverIndex = m_index.model()->index( m_index.row(), CoverImage );
        QPixmap albumPixmap = coverIndex.data( Qt::DisplayRole ).value<QPixmap>();

        if( !albumPixmap.isNull() )
        {
            if( albumPixmap.width() > albumPixmap.height() )
                albumPixmap = albumPixmap.scaledToWidth( coverHeight );
            else
                albumPixmap = albumPixmap.scaledToHeight( coverHeight );

            QLabel *coverLabel = new QLabel( this );
            coverLabel->setPixmap( albumPixmap );
            if( albumPixmap.width() < coverHeight )
                coverLabel->setContentsMargins( ( coverHeight - albumPixmap.width()     ) / 2, 0,
                                                ( coverHeight - albumPixmap.width() + 1 ) / 2, 0 );
            boxLayout->setStretchFactor( coverLabel, 0 );

            boxLayout->addSpacing( horizontalSpace );
        }
    }

    KVBox *rowsWidget = new KVBox( this );

    // --- paint all the rows
    for( int i = 0; i < rowCount; i++ )
    {
        LayoutItemConfigRow row = config.row( i );
        const int elementCount = row.count();

        QSplitter *rowWidget = new QSplitter( rowsWidget );
        connect( rowWidget, SIGNAL(splitterMoved(int,int)), this, SLOT(splitterMoved(int,int)) );

        m_splitterRowMap.insert( rowWidget, i );

        //we need to do a quick pass to figure out how much space is left for auto sizing elements
        qreal spareSpace = 1.0;
        int autoSizeElemCount = 0;
        for( int k = 0; k < elementCount; ++k )
        {
            spareSpace -= row.element( k ).size();
            if( row.element( k ).size() < 0.001 )
                autoSizeElemCount++;
        }

        const qreal spacePerAutoSizeElem = spareSpace / (qreal)autoSizeElemCount;

        //give left over pixels to the first rows. Widgets are doing it the same.
        if( i == 0 )
            rowHeight++;
        if( i == ( contentHeight % rowCount ) )
            rowHeight--;

        QList<int> itemWidths;
        int currentItemX = 0;
        for( int j = 0; j < elementCount; ++j )
        {
            LayoutItemConfigRowElement element = row.element( j );

            // -- calculate the size
            qreal size;
            if( element.size() < 0.001 )
                size = spacePerAutoSizeElem;
            else
                size = element.size();

            int itemWidth;
            if( j == elementCount - 1 )
                // use the full with for the last item
                itemWidth = rowWidth - currentItemX;
            else
                itemWidth = rowWidth * size;

            itemWidths.append( itemWidth );

            int value = element.value();

            QModelIndex textIndex = m_index.model()->index( m_index.row(), value );
            QString text = textIndex.data( Qt::DisplayRole ).toString();
            m_orgValues.insert( value, text );

            QWidget *widget = 0;
            //special case for painting the rating...
            if( value == Rating )
            {
                int rating = textIndex.data( Qt::DisplayRole ).toInt();

                KRatingWidget* ratingWidget = new KRatingWidget( 0 );
                ratingWidget->setAlignment( element.alignment() );
                ratingWidget->setRating( rating );
                ratingWidget->setAttribute( Qt::WA_NoMousePropagation, true );

                connect( ratingWidget, SIGNAL(ratingChanged(uint)), this, SLOT(ratingValueChanged()) );

                m_editorRoleMap.insert( ratingWidget, value );
                widget = ratingWidget;
            }
            else if( value == Divider )
            {
                QPixmap left = The::svgHandler()->renderSvg( "divider_left",
                                                             1, rowHeight,
                                                             "divider_left" );

                QPixmap right = The::svgHandler()->renderSvg( "divider_right",
                                                              1, rowHeight,
                                                              "divider_right" );

                QPixmap dividerPixmap( 2, rowHeight );
                dividerPixmap.fill( Qt::transparent );

                QPainter painter( &dividerPixmap );
                painter.drawPixmap( 0, 0, left );
                painter.drawPixmap( 1, 0, right );

                QLabel* dividerLabel = new QLabel( 0 );
                dividerLabel->setPixmap( dividerPixmap );
                dividerLabel->setAlignment( element.alignment() );

                widget = dividerLabel;
            }
            else if( value == Moodbar )
            {
                //we cannot ask the model for the moodbar directly as we have no
                //way of asking for a specific size. Instead just get the track from
                //the model and ask the moodbar manager ourselves.
                Meta::TrackPtr track = m_index.data( TrackRole ).value<Meta::TrackPtr>();

                QLabel* moodbarLabel = new QLabel( 0 );
                moodbarLabel->setScaledContents( true );
                if( The::moodbarManager()->hasMoodbar( track ) )
                {
                    QPixmap moodbar = The::moodbarManager()->getMoodbar( track, itemWidth, rowHeight - 8 );
                    moodbarLabel->setPixmap( moodbar );
                }
                widget = moodbarLabel;
            }
            //actual playlist item text is drawn here
            else
            {
                QLineEdit * edit = new QLineEdit( text, 0 );
                edit->setFrame( false );
                edit->setAlignment( element.alignment() );
                edit->installEventFilter(this);

                // -- set font
                bool bold = element.bold();
                bool italic = element.italic();
                bool underline = element.underline();

                QFont font = edit->font();
                font.setBold( bold );
                font.setItalic( italic );
                font.setUnderline( underline );
                edit->setFont( font );

                connect( edit, SIGNAL(editingFinished()), this, SLOT(editValueChanged()) );

                //check if this is a column that is editable. If not, make the
                //line edit read only.
                if( !isEditableColumn( static_cast<Playlist::Column>(value) ) )
                {
                    edit->setReadOnly( true );
                    edit->setDisabled( true );
                }
                m_editorRoleMap.insert( edit, value );
                widget = edit;
            }

            widget->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ); // or else the widget size hint influences the space it get's which messes up the layout
            rowWidget->addWidget( widget );

            // handles are nice, but if we don't compensate for their sizes the layout
            // would be different from the item delegate
            if( j > 0 )
                widget->setContentsMargins( -( ( rowWidget->handleWidth() + 1 ) / 2 ), 0, 0, 0 );
            if( j < elementCount - 1 )
                widget->setContentsMargins( 0, 0, -( rowWidget->handleWidth() / 2 ), 0 );

            currentItemX += itemWidth;
        }
        rowWidget->setSizes( itemWidths );
    }
}
Exemplo n.º 3
0
void Playlist::PrettyItemDelegate::paintItem( const LayoutItemConfig &config,
                                              QPainter* painter,
                                              const QStyleOptionViewItem& option,
                                              const QModelIndex& index,
                                              bool headerRow ) const
{
    const int rowCount = config.rows();
    if( rowCount == 0 )
        return;

    QStyle *style;
    if( QWidget *w = qobject_cast<QWidget*>(parent()) )
        style = w->style();
    else
        style = QApplication::style();

    // we have to use the same metrics as the InlineEditorWidget or else
    // the widgets will change places when editing
    const int horizontalSpace = style->pixelMetric( QStyle::PM_LayoutHorizontalSpacing );
    const int smallIconSize = style->pixelMetric( QStyle::PM_SmallIconSize );
    const int frameHMargin = style->pixelMetric( QStyle::PM_FocusFrameHMargin );
    const int frameVMargin = style->pixelMetric( QStyle::PM_FocusFrameVMargin );
    const int iconSpacing = style->pixelMetric( QStyle::PM_ToolBarItemSpacing );

    int rowOffsetX = frameHMargin; // keep the text a little bit away from the border
    int rowOffsetY = frameVMargin;

    const int coverHeight = option.rect.height() - frameVMargin * 2;
    QRectF nominalImageRect( frameHMargin, frameVMargin, coverHeight, coverHeight );

    const bool showCover = config.showCover();
    if( showCover )
        rowOffsetX += coverHeight + horizontalSpace/* + frameHMargin * 2*/;

    const int contentHeight = option.rect.height() - frameVMargin * 2;
    int rowHeight = contentHeight / rowCount;
    const int rowWidth = option.rect.width() - rowOffsetX - frameHMargin * 2;

    // --- paint the active track background
    // We do not want to paint this for head items.
    if( !headerRow && index.data( ActiveTrackRole ).toBool() )
    {
        //paint this in 3 parts to solve stretching issues with wide playlists
        //TODO: propper 9 part painting, but I don't want to bother with this until we
        //get some new graphics anyway...

        // -- try not to highlight the indicator row
        int overlayXOffset = 0;
        int overlayYOffset = config.activeIndicatorRow() * rowHeight;
        int overlayHeight = option.rect.height() - overlayYOffset;
        int overlayLength = option.rect.width();

        int endWidth = overlayHeight / 4;

        if( m_animationTimeLine->currentFrame() == m_animationTimeLine->startFrame() && EngineController::instance()->isPlaying() ) {
            m_animationTimeLine->setDirection( QTimeLine::Forward );
            if( m_animationTimeLine->state() == QTimeLine::NotRunning )
                m_animationTimeLine->start();
        }
        else if( m_animationTimeLine->currentFrame() == m_animationTimeLine->endFrame() ) {
            m_animationTimeLine->setDirection( QTimeLine::Backward );
            m_animationTimeLine->start();
        }

        // Opacity is used for animating the active track item
        const qreal opacity = qreal( m_animationTimeLine->currentFrame() ) / 1000;

        // If opacity is not the default value we cannot render from cache
        const bool skipCache = opacity == 1.0 ? false : true;

        painter->drawPixmap( overlayXOffset, overlayYOffset,
                             The::svgHandler()->renderSvg( "active_overlay_left",
                                                           endWidth,
                                                           overlayHeight,
                                                           "active_overlay_left",
                                                           skipCache,
                                                           opacity ) );

        painter->drawPixmap( overlayXOffset + endWidth, overlayYOffset,
                             The::svgHandler()->renderSvg( "active_overlay_mid",
                                                           overlayLength - endWidth * 2,
                                                           overlayHeight,
                                                           "active_overlay_mid",
                                                           skipCache,
                                                           opacity ) );

        painter->drawPixmap( overlayXOffset + ( overlayLength - endWidth ), overlayYOffset,
                             The::svgHandler()->renderSvg( "active_overlay_right",
                                                           endWidth,
                                                           overlayHeight,
                                                           "active_overlay_right",
                                                           skipCache,
                                                           opacity ) );
    }

    // --- paint the cover
    if( showCover )
    {
        QModelIndex coverIndex = index.model()->index( index.row(), CoverImage );
        QPixmap albumPixmap = coverIndex.data( Qt::DisplayRole ).value<QPixmap>();

        if( !albumPixmap.isNull() )
        {
            //offset cover if non square
            QPointF offset = centerImage( albumPixmap, nominalImageRect );
            QRectF imageRect( nominalImageRect.x() + offset.x(),
                              nominalImageRect.y() + offset.y(),
                              nominalImageRect.width() - offset.x() * 2,
                              nominalImageRect.height() - offset.y() * 2 );

            painter->drawPixmap( imageRect, albumPixmap, QRectF( albumPixmap.rect() ) );
        }

        QModelIndex emblemIndex = index.model()->index( index.row(), SourceEmblem );
        QPixmap emblemPixmap = emblemIndex.data( Qt::DisplayRole ).value<QPixmap>();

        if( !emblemPixmap.isNull() )
            painter->drawPixmap( QRectF( nominalImageRect.x(), nominalImageRect.y() , 16, 16 ), emblemPixmap, QRectF( 0, 0 , 16, 16 ) );
    }

    // --- paint the markers
    int markerOffsetX = frameHMargin;
    const int rowOffsetXBeforeMarkers = rowOffsetX;
    if( !headerRow )
    {
        const int queuePosition = index.data( QueuePositionRole ).toInt();
        if( queuePosition > 0 )
        {
            const int x = markerOffsetX;
            const int y = nominalImageRect.y() + ( coverHeight - smallIconSize );
            const QString number = QString::number( queuePosition );
            const int iconWidth = option.fontMetrics.boundingRect( number ).width() + smallIconSize / 2;

            const QRect rect( x, y, iconWidth, smallIconSize ); // shift text by 1 pixel left

            const KColorScheme colorScheme( option.palette.currentColorGroup() );
            QPen rectanglePen = painter->pen();
            rectanglePen.setColor( colorScheme.foreground( KColorScheme::PositiveText ).color() );
            QBrush rectangleBrush = colorScheme.background( KColorScheme::PositiveBackground );

            painter->save();
            painter->setPen( rectanglePen );
            painter->setBrush( rectangleBrush );
            painter->setRenderHint( QPainter::Antialiasing, true );
            painter->drawRoundedRect( QRect( x, y - 1, iconWidth, smallIconSize ), smallIconSize / 3, smallIconSize / 3 );
            painter->drawText( rect, Qt::AlignCenter, number );
            painter->restore();

            markerOffsetX += ( iconWidth + iconSpacing );

            if ( !showCover )
                rowOffsetX += ( iconWidth + iconSpacing );
            else
                rowOffsetX += qMax( 0, iconWidth - smallIconSize - iconSpacing );
        }

        if( index.data( MultiSourceRole ).toBool() )
        {
            const int x = markerOffsetX;
            const int y = nominalImageRect.y() + ( coverHeight - smallIconSize );
            painter->drawPixmap( x, y, The::svgHandler()->renderSvg( "multi_marker", smallIconSize, smallIconSize, "multi_marker" ) );

            markerOffsetX += ( smallIconSize + iconSpacing );

            if ( !showCover )
                rowOffsetX += ( smallIconSize + iconSpacing );
        }

        if( index.data( StopAfterTrackRole ).toBool() )
        {
            const int x = markerOffsetX;
            const int y = nominalImageRect.y() + ( coverHeight - smallIconSize );
            painter->drawPixmap( x, y, The::svgHandler()->renderSvg( "stop_button", smallIconSize, smallIconSize, "stop_button" ) );

            markerOffsetX += ( smallIconSize + iconSpacing );

            if ( !showCover )
                rowOffsetX += ( smallIconSize + iconSpacing );
        }
    }
    int markersWidth = rowOffsetX - rowOffsetXBeforeMarkers;

    Meta::TrackPtr trackPtr = index.data( TrackRole ).value<Meta::TrackPtr>();
    QMap<QString, QString> trackArgs;
    if( trackPtr )
        trackArgs = buildTrackArgsMap( trackPtr );

    // --- paint all the rows
    for( int i = 0; i < rowCount; i++ )
    {
        LayoutItemConfigRow row = config.row( i );
        const int elementCount = row.count();

        //we need to do a quick pass to figure out how much space is left for auto sizing elements
        qreal spareSpace = 1.0;
        int autoSizeElemCount = 0;
        for( int k = 0; k < elementCount; ++k )
        {
            spareSpace -= row.element( k ).size();
            if( row.element( k ).size() < 0.001 )
                autoSizeElemCount++;
        }

        const qreal spacePerAutoSizeElem = spareSpace / (qreal)autoSizeElemCount;

        //give left over pixels to the first rows. Widgets are doing it the same.
        if( i == 0 )
            rowHeight++;
        if( i == ( contentHeight % rowCount ) )
            rowHeight--;

        int currentItemX = rowOffsetX;
        for( int j = 0; j < elementCount; ++j )
        {
            LayoutItemConfigRowElement element = row.element( j );

            // -- calculate the size
            qreal size;
            if( element.size() < 0.001 )
                size = spacePerAutoSizeElem;
            else
                size = element.size();

            qreal itemWidth;
            if( j == elementCount - 1 )
                // use the full with for the last item
                itemWidth = rowWidth - (currentItemX - rowOffsetXBeforeMarkers);
            else
                itemWidth = rowWidth * size - markersWidth;

            markersWidth = 0; // leave columns > 0 alone, they are unaffected by markers

            if( itemWidth <= 1 )
                continue; // no sense to paint such small items

            // -- set font
            bool bold = element.bold();
            bool italic = element.italic();
            bool underline = element.underline();
            int alignment = element.alignment();

            QFont font = option.font;
            font.setBold( bold );
            font.setItalic( italic );
            font.setUnderline( underline );
            painter->setFont( font );

            int value = element.value();
            QModelIndex textIndex = index.model()->index( index.row(), value );

            // -- paint the element
            if( value == Rating )
            {
                int rating = textIndex.data( Qt::DisplayRole ).toInt();

                KRatingPainter::paintRating( painter,
                                             QRect( currentItemX, rowOffsetY,
                                                    itemWidth, rowHeight ),
                                             element.alignment(), rating );

            }
            else if( value == Divider )
            {
                QPixmap left = The::svgHandler()->renderSvg( "divider_left",
                                                             1, rowHeight ,
                                                             "divider_left" );

                QPixmap right = The::svgHandler()->renderSvg( "divider_right",
                                                              1, rowHeight,
                                                              "divider_right" );

                if( alignment & Qt::AlignLeft )
                {
                    painter->drawPixmap( currentItemX, rowOffsetY, left );
                    painter->drawPixmap( currentItemX + 1, rowOffsetY, right );
                }
                else if( alignment & Qt::AlignRight )
                {
                    painter->drawPixmap( currentItemX + itemWidth - 1, rowOffsetY, left );
                    painter->drawPixmap( currentItemX + itemWidth, rowOffsetY, right );
                }
                else
                {
                    int center = currentItemX + ( itemWidth / 2 );
                    painter->drawPixmap( center, rowOffsetY, left );
                    painter->drawPixmap( center + 1, rowOffsetY, right );
                }
            }
            else if( value == Moodbar )
            {
                //we cannot ask the model for the moodbar directly as we have no
                //way of asking for a specific size. Instead just get the track from
                //the model and ask the moodbar manager ourselves.
                Meta::TrackPtr track = index.data( TrackRole ).value<Meta::TrackPtr>();

                if( The::moodbarManager()->hasMoodbar( track ) )
                {
                    QPixmap moodbar = The::moodbarManager()->getMoodbar( track, itemWidth, rowHeight - 8 );

                    painter->drawPixmap( currentItemX, rowOffsetY + 4, moodbar );
                }
            }
            //actual playlist item text is drawn here
            else
            {
                //TODO: get rid of passing TrackPtr as data, use custom role instead
                Meta::TrackPtr track = index.data( TrackRole ).value<Meta::TrackPtr>();
                QString text = textIndex.data( Qt::DisplayRole ).toString();
                Amarok::QStringx prefix( element.prefix() );
                Amarok::QStringx suffix( element.suffix() );
                text = prefix.namedOptArgs( trackArgs ) + text + suffix.namedOptArgs( trackArgs );
                text = QFontMetricsF( font ).elidedText( text, Qt::ElideRight, itemWidth );

                //if the track can't be played, it should be grayed out to show that it is unavailable
                if( !track->isPlayable() )
                {
                    painter->save();
                    QPen grayPen = painter->pen();
                    grayPen.setColor( QColor( 127, 127, 127 ) ); // TODO: use disabled role
                    painter->setPen( grayPen );
                    painter->drawText( QRect( currentItemX + frameHMargin, rowOffsetY,
                                              itemWidth - frameHMargin * 2, rowHeight ),
                                       alignment, text );
                    painter->restore();
                }
                else
                {
                    painter->drawText( QRect( currentItemX + frameHMargin, rowOffsetY,
                                              itemWidth - frameHMargin * 2, rowHeight ),
                                       alignment, text );
                }
            }
            currentItemX += itemWidth;
        }
        rowOffsetY += rowHeight;
    }
}