Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  plugins.cpp   Sprache: C

 

/*****************************************************************************
 * plugins.cpp : Plug-ins and extensions listing
 ****************************************************************************
 * Copyright (C) 2008-2010 the VideoLAN team
 *
 * Authors: Jean-Baptiste Kempf <jb (at) videolan.org>
 *          Jean-Philippe André <jpeg (at) videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "plugins.hpp"

#include "widgets/native/searchlineedit.hpp"
#include "dialogs/extensions/extensions_manager.hpp"
#include "network/addonsmodel.hpp"
#include "widgets/native/animators.hpp"
#include "util/imagehelper.hpp"
#include "util/colorizedsvgicon.hpp"

#include <cassert>

#include <vlc_modules.h>
#include <vlc_extensions.h>

#include <QTreeWidget>
#include <QStringList>
#include <QTabWidget>
#include <QHeaderView>
#include <QDialogButtonBox>
#include <QLineEdit>
#include <QLabel>
#include <QVBoxLayout>
#include <QComboBox>
#include <QHBoxLayout>
#include <QSpacerItem>
#include <QListView>
#include <QPainter>
#include <QStyleOptionViewItem>
#include <QKeyEvent>
#include <QPushButton>
#include <QCheckBox>
#include <QPixmap>
#include <QStylePainter>
#include <QProgressBar>
#include <QTextEdit>
#include <QUrl>
#include <QMimeData>
#include <QSplitter>
#include <QToolButton>
#include <QStackedWidget>
#include <QPainterPath>
#include <QSignalMapper>
#include <QtQml/QQmlFile>

//match the image source (width/height)
#define SCORE_ICON_WIDTH_SCALE 4
#define SPINNER_SIZE 32

static QPixmap *loadPixmapFromData( char *, int size );


PluginDialog::PluginDialog( qt_intf_t *_p_intf ) : QVLCFrame( _p_intf )
{
    setWindowTitle( qtr( "Plugins and extensions" ) );
    setWindowRole( "vlc-plugins" );

    QVBoxLayout *layout = new QVBoxLayout( this );
    tabs = new QTabWidget( this );
    tabs->addTab( addonsTab = new AddonsTab( p_intf ),
                  qtr( "Addons Manager" ) );
    tabs->addTab( extensionTab = new ExtensionTab( p_intf ),
                  qtr( "Active Extensions" ) );
    tabs->addTab( pluginTab = new PluginTab( p_intf ),
                  qtr( "Plugins" ) );
    layout->addWidget( tabs );

    QDialogButtonBox *box = new QDialogButtonBox;
    QPushButton *okButton = new QPushButton( qtr( "&Close" ), this );
    box->addButton( okButton, QDialogButtonBox::RejectRole );
    layout->addWidget( box );
    BUTTONACT( okButton, &PluginDialog::close );
    restoreWidgetPosition( "PluginsDialog", QSize( 435, 280 ) );
}

PluginDialog::~PluginDialog()
{
    delete pluginTab;
    delete extensionTab;
    delete addonsTab;
    saveWidgetPosition( "PluginsDialog" );
}

/* Plugins tab */

PluginTab::PluginTab( qt_intf_t *p_intf_ )
        : QVLCFrame( p_intf_ )
{
    QGridLayout *layout = new QGridLayout( this );

    /* Main Tree for modules */
    treePlugins = new QTreeWidget;
    layout->addWidget( treePlugins, 0, 0, 1, -1 );

    /* Users cannot move the columns around but we need to sort */
    treePlugins->header()->setSectionsMovable( false );
    treePlugins->header()->setSortIndicatorShown( true );
    //    treePlugins->header()->setResizeMode( QHeaderView::ResizeToContents );
    treePlugins->setAlternatingRowColors( true );
    treePlugins->setColumnWidth( 0, 200 );

    QStringList headerNames;
    headerNames << qtr("Name") << qtr("Capability" ) << qtr( "Score" );
    treePlugins->setHeaderLabels( headerNames );

    FillTree();

    /* Set capability column to the correct Size*/
    treePlugins->resizeColumnToContents( 1 );
    treePlugins->header()->restoreState(
            getSettings()->value( "Plugins/Header-State" ).toByteArray() );

    treePlugins->setSortingEnabled( true );
    treePlugins->sortByColumn( 1, Qt::AscendingOrder );

    QLabel *label = new QLabel( qtr("&Search:"), this );
    edit = new SearchLineEdit( this );
    label->setBuddy( edit );

    layout->addWidget( label, 1, 0 );
    layout->addWidget( edit, 1, 1, 1, 1 );
    connect( edit, &SearchLineEdit::textChanged, this, &PluginTab::search );

    setMinimumSize( 500, 300 );
    restoreWidgetPosition( "Plugins", QSize( 540, 400 ) );
}

inline void PluginTab::FillTree()
{
    size_t count;
    module_t **p_list = module_list_get( &count );

    for( unsigned int i = 0; i < count; i++ )
    {
        module_t *p_module = p_list[i];

        QStringList qs_item;
        qs_item << qfu( module_GetLongName( p_module ) )
                << qfu( module_get_capability( p_module ) )
                << QString::number( module_get_score( p_module ) );
#ifndef DEBUG
        if( qs_item.at(1).isEmpty() ) continue;
#endif

        QTreeWidgetItem *item = new PluginTreeItem( qs_item );
        treePlugins->addTopLevelItem( item );
    }
    module_list_free( p_list );
}

void PluginTab::search( const QString& qs )
{
    QList<QTreeWidgetItem *> items = treePlugins->findItems( qs, Qt::MatchContains );
    items += treePlugins->findItems( qs, Qt::MatchContains, 1 );

    QTreeWidgetItem *item = NULL;
    for( int i = 0; i < treePlugins->topLevelItemCount(); i++ )
    {
        item = treePlugins->topLevelItem( i );
        item->setHidden( !items.contains( item ) );
    }
}

PluginTab::~PluginTab()
{
    saveWidgetPosition( "Plugins" );
    getSettings()->setValue( "Plugins/Header-State",
                             treePlugins->header()->saveState() );
}

void PluginTab::keyPressEvent( QKeyEvent *keyEvent )
{
    if( keyEvent->key() == Qt::Key_Return ||
        keyEvent->key() == Qt::Key_Enter )
        keyEvent->accept();
    else
        keyEvent->ignore();
}

bool PluginTreeItem::operator< ( const QTreeWidgetItem & other ) const
{
    int col = treeWidget()->sortColumn();
    if( col == PluginTab::SCORE )
        return text( col ).toInt() < other.text( col ).toInt();
    else if ( col == PluginTab::CAPABILITY )
    {
        if ( text( PluginTab::CAPABILITY ) == other.text( PluginTab::CAPABILITY ) )
            return text( PluginTab::NAME ) < other.text( PluginTab::NAME );
        else
            return text( PluginTab::CAPABILITY ) < other.text( PluginTab::CAPABILITY );
    }
    return text( col ) < other.text( col );
}

/* Extensions tab */
ExtensionTab::ExtensionTab( qt_intf_t *p_intf_ )
        : QVLCFrame( p_intf_ )
{
    // Layout
    QVBoxLayout *layout = new QVBoxLayout( this );

    // ListView
    extList = new QListView( this );
    connect( extList, &QListView::activated, this, &ExtensionTab::moreInformation );
    layout->addWidget( extList );

    // List item delegate
    ExtensionItemDelegate *itemDelegate = new ExtensionItemDelegate( extList );
    extList->setItemDelegate( itemDelegate );

    // Extension list look & feeling
    extList->setAlternatingRowColors( true );
    extList->setSelectionMode( QAbstractItemView::SingleSelection );

    // Model
    ExtensionListModel *model =
      new ExtensionListModel( extList, ExtensionsManager::getInstance( p_intf ) );
    extList->setModel( model );

    // Buttons' layout
    QDialogButtonBox *buttonsBox = new QDialogButtonBox;

    // More information button
    butMoreInfo = new QPushButton( QIcon( ":/menu/info.svg" ),
                                   qtr( "More information..." ),
                                   this );
    connect( butMoreInfo, &QPushButton::clicked, this, &ExtensionTab::moreInformation );
    buttonsBox->addButton( butMoreInfo, QDialogButtonBox::ActionRole );

    // Reload button
    ExtensionsManager *EM = ExtensionsManager::getInstance( p_intf );
    QPushButton *reload = new QPushButton( QIcon( ":/menu/update.svg" ),
                                           qtr( "Reload extensions" ),
                                           this );
    connect( reload, &QPushButton::clicked, [this, EM](){
        extList->clearSelection();
        EM->reloadExtensions();
    });
    connect( reload, &QPushButton::clicked, this, &ExtensionTab::updateButtons );
    connect( extList->selectionModel(), &QItemSelectionModel::selectionChanged,
             this, &ExtensionTab::updateButtons );
    buttonsBox->addButton( reload, QDialogButtonBox::ResetRole );

    layout->addWidget( buttonsBox );
    updateButtons();
}

ExtensionTab::~ExtensionTab()
{
}

void ExtensionTab::updateButtons()
{
    butMoreInfo->setEnabled( extList->selectionModel()->hasSelection() );
}

// Do not close on ESC or ENTER
void ExtensionTab::keyPressEvent( QKeyEvent *keyEvent )
{
    if( keyEvent->key() == Qt::Key_Return ||
        keyEvent->key() == Qt::Key_Enter )
        keyEvent->accept();
    else
        keyEvent->ignore();
}

// Show more information
void ExtensionTab::moreInformation()
{
    QModelIndex index = extList->selectionModel()->selectedIndexes().first();

    if( !index.isValid() )
        return;

    QWidget* windowWidget = window();
    QWindow* parentWindow = windowWidget ? windowWidget->windowHandle() : nullptr;
    ExtensionInfoDialog dlg( index, p_intf, parentWindow );
    dlg.exec();
}

/* Add-ons tab */
AddonsTab::AddonsTab( qt_intf_t *p_intf_ ) : QVLCFrame( p_intf_ )
{
    b_localdone = false;
    QSplitter *splitter = new QSplitter( this );
    setLayout( new QHBoxLayout() );
    layout()->addWidget( splitter );

    QWidget *leftPane = new QWidget();
    splitter->addWidget( leftPane );
    leftPane->setLayout( new QVBoxLayout() );

    QWidget *rightPane = new QWidget();
    splitter->addWidget( rightPane );

    splitter->setCollapsible( 0, false );
    splitter->setCollapsible( 1, false );
    splitter->setSizeIncrement( 32, 1 );

    // Layout
    QVBoxLayout *layout = new QVBoxLayout( rightPane );

    // Left Pane
    leftPane->layout()->setContentsMargins(0, 0, 0, 0);
    leftPane->layout()->setSpacing(0);

    SearchLineEdit *searchInput = new SearchLineEdit();
    leftPane->layout()->addWidget( searchInput );
    leftPane->layout()->addItem( new QSpacerItem( 0, 10 ) );

    signalMapper = new QSignalMapper();

    auto addCategory = [this, leftPane]( const QString& label, const QString& ltooltip,  AddonsModel::Type type) {
        auto button = new QToolButton( this );

        QString iconpath = QQmlFile::urlToLocalFileOrQrc(AddonsModel::getIconForType( type ));
        button->setIcon( QIcon{ iconpath } );
        button->setText( label );
        button->setToolTip( ltooltip );
        button->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
        button->setIconSize( QSize( 32, 32 ) );
        button->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::Maximum) ;
        button->setMinimumSize( 32, 32 );
        button->setAutoRaise( true );
        button->setCheckable( true );
        if ( type == AddonsModel::Type::TYPE_NONE )
            button->setChecked( true );
        button->setAutoExclusive( true );
        connect( button, &QToolButton::clicked, signalMapper, QOverload<>::of(&QSignalMapper::map) );
        signalMapper->setMapping( button, static_cast<int>(type) );
        leftPane->layout()->addWidget( button );
    };

    addCategory( qtr("All"), qtr("Interface Settings"),
                  AddonsModel::Type::TYPE_NONE );
    addCategory( qtr("Skins"),
                  qtr( "Skins customize player's appearance."
                       " You can activate them through preferences." ),
                  AddonsModel::Type::TYPE_SKIN2 );
    addCategory( qtr("Playlist parsers"),
                  qtr( "Playlist parsers add new capabilities to read"
                       " internet streams or extract meta data." ),
                  AddonsModel::Type::TYPE_PLAYLIST_PARSER );
    addCategory( qtr("Service Discovery"),
                  qtr( "Service discoveries adds new sources to your play queue"
                       " such as web radios, video websites, ..." ),
                  AddonsModel::Type::TYPE_SERVICE_DISCOVERY );
    addCategory( qtr("Interfaces"),
                  "",
                  AddonsModel::Type::TYPE_INTERFACE );
    addCategory( qtr("Art and meta fetchers"),
                  qtr( "Retrieves extra info and art for play queue items" ),
                  AddonsModel::Type::TYPE_META );
    addCategory( qtr("Extensions"),
                  qtr( "Extensions brings various enhancements."
                       " Check descriptions for more details" ),
                  AddonsModel::Type::TYPE_EXTENSION );

    // Right Pane
    rightPane->layout()->setContentsMargins(0, 0, 0, 0);
    rightPane->layout()->setSpacing(0);

    // Splitter sizes init
    QList<int> sizes;
    int width = leftPane->sizeHint().width();
    sizes << width << size().width() - width;
    splitter->setSizes( sizes );

    // Filters
    leftPane->layout()->addItem( new QSpacerItem( 0, 30 ) );

    QStackedWidget *switchStack = new QStackedWidget();
    switchStack->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Maximum );
    leftPane->layout()->addWidget( switchStack );

    QCheckBox *installedOnlyBox = new QCheckBox( qtr("Only installed") );
    installedOnlyBox->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Preferred );
    switchStack->insertWidget( WITHONLINEADDONS, installedOnlyBox );
    connect( installedOnlyBox, &QtCheckboxChanged, this, &AddonsTab::installChecked );

    // Model
    m_model = std::make_unique<AddonsModel>( );
    //model expect QMLlike behavior
    m_model->classBegin();
    m_model->setCtx(p_intf->p_mi);
    connect( signalMapper, &QSignalMapper::mappedInt,
            m_model.get(), [model = m_model.get()](int mapped){
                model->setTypeFilter(static_cast<AddonsModel::Type>(mapped));
            });

    connect( searchInput, &SearchLineEdit::textChanged,
            m_model.get(), &AddonsModel::setSearchPattern );


    // Update button
    QPushButton *reposyncButton = new QPushButton( QIcon( ":/menu/update.svg" ),
                                              qtr("Find more addons online") );
    reposyncButton->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Preferred );
    switchStack->insertWidget( ONLYLOCALADDONS, reposyncButton );
    switchStack->setCurrentIndex( ONLYLOCALADDONS );
    connect( reposyncButton, &QPushButton::clicked, this, &AddonsTab::reposync );
    connect(
        m_model.get(), &AddonsModel::loadingChanged,
        this, [this]() {
            if ( !m_model->loading()) {
                spinnerAnimation->stop();
                addonsView->viewport()->update();
            }
        });

    leftPane->layout()->addItem( new QSpacerItem( 0, 0, QSizePolicy::Maximum, QSizePolicy::Expanding ) );

    // Main View

    // ListView
    addonsView = new QListView( this );
    addonsView->setModel(m_model.get());

    connect( addonsView, &QListView::activated, this, &AddonsTab::moreInformation );
    layout->addWidget( addonsView );

    // List item delegate
    AddonItemDelegate *addonsDelegate = new AddonItemDelegate( addonsView );
    addonsView->setItemDelegate( addonsDelegate );
    addonsDelegate->setAnimator( new DelegateAnimationHelper( addonsView ) );
    connect( addonsDelegate, &AddonItemDelegate::showInfo, this, &AddonsTab::moreInformation );

    // Extension list look & feeling
    addonsView->setAlternatingRowColors( true );
    addonsView->setSelectionMode( QAbstractItemView::SingleSelection );

    // Drop packages
    addonsView->setAcceptDrops( true );
    addonsView->setDefaultDropAction( Qt::CopyAction );
    addonsView->setDropIndicatorShown( true );
    addonsView->setDragDropMode( QAbstractItemView::DropOnly );

    connect( addonsView->selectionModel(), &QItemSelectionModel::currentChanged,
             addonsView, QOverload<const QModelIndex&>::of(&QListView::edit) );


    QList<QString> frames;
    frames << ":/misc/wait1.svg";
    frames << ":/misc/wait2.svg";
    frames << ":/misc/wait3.svg";
    frames << ":/misc/wait4.svg";
    spinnerAnimation = new PixmapAnimator( this, std::move(frames), SPINNER_SIZE, SPINNER_SIZE );
    connect( spinnerAnimation, &PixmapAnimator::pixmapReady,
             addonsView->viewport(), QOverload<>::of(&QWidget::update) );
    addonsView->viewport()->installEventFilter( this );

    //model expect QMLlike behavior
    m_model->componentComplete();
}

AddonsTab::~AddonsTab()
{
    delete spinnerAnimation;
    delete signalMapper;
}

// Do not close on ESC or ENTER
void AddonsTab::keyPressEvent( QKeyEvent *keyEvent )
{
    if( keyEvent->key() == Qt::Key_Return ||
        keyEvent->key() == Qt::Key_Enter )
        keyEvent->accept();
    else
        keyEvent->ignore();
}

bool AddonsTab::eventFilter( QObject *obj, QEvent *event )
{
    if ( obj != addonsView->viewport() )
        return false;

    switch( event->type() )
    {
    case QEvent::Paint:
        if ( spinnerAnimation->state() == PixmapAnimator::Running )
        {
            QWidget *viewport = qobject_cast<QWidget *>( obj );
            if ( !viewport ) break;
            QStylePainter painter( viewport );
            const QPixmap& spinner = spinnerAnimation->getPixmap();
            QPoint point = viewport->geometry().center();
            point -= QPoint( spinner.width() / 2, spinner.height() / 2 );
            painter.drawPixmap( point, spinner );
            QString text = qtr("Retrieving addons...");
            QSize textsize = fontMetrics().size( 0, text );
            point = viewport->geometry().center();
            point -= QPoint( textsize.width() / 2, -spinner.height() );
            painter.drawText( point, text );
        }
        else if ( m_model->rowCount() == 0 )
        {
            QWidget *viewport = qobject_cast<QWidget *>( obj );
            if ( !viewport ) break;
            QStylePainter painter( viewport );
            QString text = qtr("No addons found");
            QSize size = fontMetrics().size( 0, text );
            QPoint point = viewport->geometry().center();
            point -= QPoint( size.width() / 2, size.height() / 2 );
            painter.drawText( point, text );
        }
        break;
    case QEvent::DragEnter:
    {
        QDragEnterEvent *dragEvent = static_cast<QDragEnterEvent *>(event);
        if ( !dragEvent ) break;
        QList<QUrl> urls = dragEvent->mimeData()->urls();
        if ( dragEvent->proposedAction() != Qt::CopyAction
          || urls.count() != 1
          || urls.first().scheme() != "file"
          || ! urls.first().path().endsWith(".vlp") )
            return false;
        dragEvent->acceptProposedAction();
        return true;
    }
    case QEvent::DragMove:
    {
        QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
        if ( !moveEvent ) break;
        if ( moveEvent->proposedAction() != Qt::CopyAction )
            return false;
        moveEvent->acceptProposedAction();
        return true;
    }
    case QEvent::Drop:
    {
        QDropEvent *dropEvent = static_cast<QDropEvent *>(event);
        if ( !dropEvent ) break;
        if ( dropEvent->proposedAction() != Qt::CopyAction )
            return false;
        if ( dropEvent->mimeData()->urls().count() )
        {
            m_model->loadFromExternalRepository(dropEvent->mimeData()->urls().first());
            dropEvent->acceptProposedAction();
        }
        return true;
    }
    default:
        break;
    }
    return false;
}

void AddonsTab::moreInformation()
{
    QModelIndex index = addonsView->selectionModel()->selectedIndexes().first();
    if( !index.isValid() ) return;
    QWidget* windowWidget = window();
    QWindow* parentWindow = windowWidget ? windowWidget->windowHandle() : nullptr;
    AddonInfoDialog dlg( index, p_intf, parentWindow );
    dlg.exec();
}

void AddonsTab::installChecked( int i )
{
    if ( i == Qt::Checked )
        m_model->setStateFilter( AddonsModel::State::STATE_INSTALLED );
    else
        m_model->setStateFilter( AddonsModel::State::STATE_NONE );
}

void AddonsTab::reposync()
{
    QStackedWidget *tab = qobject_cast<QStackedWidget *>(sender()->parent());
    if ( tab )
    {
        tab->setCurrentIndex( WITHONLINEADDONS );

        spinnerAnimation->start();

        m_model->loadFromDefaultRepository();
    }
}

/* Safe copy of the extension_t struct */
ExtensionListModel::ExtensionCopy::ExtensionCopy( extension_t *p_ext )
{
    name = qfu( p_ext->psz_name );
    description = qfu( p_ext->psz_description );
    shortdesc = qfu( p_ext->psz_shortdescription );
    if( description.isEmpty() )
        description = shortdesc;
    if( shortdesc.isEmpty() && !description.isEmpty() )
        shortdesc = description;
    title = qfu( p_ext->psz_title );
    author = qfu( p_ext->psz_author );
    version = qfu( p_ext->psz_version );
    url = qfu( p_ext->psz_url );
    icon = loadPixmapFromData( p_ext->p_icondata, p_ext->i_icondata_size );
}

ExtensionListModel::ExtensionCopy::~ExtensionCopy()
{
    delete icon;
}

QVariant ExtensionListModel::ExtensionCopy::data( int role ) const
{
    switch( role )
    {
    case Qt::DisplayRole:
        return title;
    case Qt::DecorationRole:
        if ( !icon ) return QPixmap( ":/logo/vlc48.png" );
        return *icon;
    case SummaryRole:
        return shortdesc;
    case VersionRole:
        return version;
    case AuthorRole:
        return author;
    case LinkRole:
        return url;
    case FilenameRole:
        return name;
    default:
        return QVariant();
    }
}

/* Extensions list model for the QListView */
ExtensionListModel::ExtensionListModel( QObject *parent )
    : QAbstractListModel( parent ), EM( NULL )
{

}

ExtensionListModel::ExtensionListModel( QObject *parent, ExtensionsManager* EM_ )
        : QAbstractListModel( parent ), EM( EM_ )
{
    // Connect to ExtensionsManager::extensionsUpdated()
    connect( EM, &ExtensionsManager::extensionsUpdated, this, &ExtensionListModel::updateList );

    // Load extensions now if not already loaded
    EM->loadExtensions();
}

ExtensionListModel::~ExtensionListModel()
{
    // Clear extensions list
    while( !extensions.isEmpty() )
        delete extensions.takeLast();
}

void ExtensionListModel::updateList()
{
    ExtensionCopy *ext;

    // Clear extensions list
    while( !extensions.isEmpty() )
    {
        ext = extensions.takeLast();
        delete ext;
    }

    // Find new extensions
    extensions_manager_t *p_mgr = EM->getManager();
    if( !p_mgr )
        return;

    vlc_mutex_lock( &p_mgr->lock );
    extension_t *p_ext;
    ARRAY_FOREACH( p_ext, p_mgr->extensions )
    {
        ext = new ExtensionCopy( p_ext );
        extensions.append( ext );
    }
    vlc_mutex_unlock( &p_mgr->lock );

    emit dataChanged( index( 0 ), index( rowCount() - 1 ) );
}

int ExtensionListModel::rowCount( const QModelIndex& ) const
{
    int count = 0;
    extensions_manager_t *p_mgr = EM->getManager();
    if( !p_mgr )
        return 0;

    vlc_mutex_lock( &p_mgr->lock );
    count = p_mgr->extensions.i_size;
    vlc_mutex_unlock( &p_mgr->lock );

    return count;
}

QVariant ExtensionListModel::data( const QModelIndex& index, int role ) const
{
    if( !index.isValid() )
        return QVariant();

    ExtensionCopy * extension =
            static_cast<ExtensionCopy *>(index.internalPointer());
    if ( !extension )
        return QVariant();
    else
        return extension->data( role );
}

QModelIndex ExtensionListModel::index( int row, int column,
                                       const QModelIndex& ) const
{
    if( column != 0 )
        return QModelIndex();
    if( row < 0 || row >= extensions.count() )
        return QModelIndex();

    return createIndex( row, 0, extensions.at( row ) );
}

static void commonPaint(
    QPainter *painter,
    const QStyleOptionViewItem &option,
    const QMargins& margins,
    const QPixmap& icon,
    const QString& name,
    const QString& description
    )
{
    QStyleOptionViewItem opt = option;

    // Draw background
    if ( opt.state & QStyle::State_Selected )
        painter->fillRect( opt.rect, opt.palette.highlight() );

    // Icon
    if( !icon.isNull() )
    {
        painter->drawPixmap( opt.rect.left() + margins.left(),
                             opt.rect.top() + margins.top(),
                             icon.scaled( opt.decorationSize,
                                          Qt::KeepAspectRatio,
                                          Qt::SmoothTransformation )
        );
    }

    painter->save();
    painter->setRenderHint( QPainter::TextAntialiasing );

    if ( opt.state & QStyle::State_Selected )
        painter->setPen( opt.palette.highlightedText().color() );

    QFont font( option.font );
    font.setBold( true );
    painter->setFont( font );
    QRect textrect( opt.rect );
    textrect.adjust( 2 * margins.left() + margins.right() + opt.decorationSize.width(),
                     margins.top(),
                     - margins.right(),
                     - margins.bottom() - opt.fontMetrics.height() );

    painter->drawText( textrect, Qt::AlignLeft, name);

    font.setBold( false );
    painter->setFont( font );
    painter->drawText( textrect.translated( 0, option.fontMetrics.height() ),
                       Qt::AlignLeft,
                       description );

    painter->restore();
}

/* Extension List Widget Item */
ExtensionItemDelegate::ExtensionItemDelegate( QObject *parent )
        : QStyledItemDelegate( parent )
{
    margins = QMargins( 4, 4, 4, 4 );
}

ExtensionItemDelegate::~ExtensionItemDelegate()
{
}

void ExtensionItemDelegate::paint( QPainter *painter,
                                   const QStyleOptionViewItem &option,
                                   const QModelIndex &index ) const
{
    QStyleOptionViewItem opt = option;
    initStyleOption( &opt, index );

    /* Draw common base  */
    QString name = index.data( Qt::DisplayRole ).toString();
    QPixmap icon = index.data( Qt::DecorationRole ).value<QPixmap>();
    QString description = index.data( ExtensionListModel::SummaryRole ).toString();

    commonPaint(
        painter,
        option,
        margins,
        icon,
        name,
        description
        );
}

QSize ExtensionItemDelegate::sizeHint( const QStyleOptionViewItem &option,
                                       const QModelIndex &index ) const
{
    if ( index.isValid() )
    {
        return QSize( 200, 2 * option.fontMetrics.height()
                      + margins.top() + margins.bottom() );
    }
    else
        return QSize();
}

void ExtensionItemDelegate::initStyleOption( QStyleOptionViewItem *option,
                                             const QModelIndex &index ) const
{
    QStyledItemDelegate::initStyleOption( option, index );
    option->decorationSize = QSize( option->rect.height(), option->rect.height() );
    option->decorationSize -= QSize( margins.left() + margins.right(),
                                     margins.top() + margins.bottom() );
}

AddonItemDelegate::AddonItemDelegate( QObject *parent )
    : QStyledItemDelegate( parent )
    , margins( 4,4,4,4 )
{ }

AddonItemDelegate::~AddonItemDelegate()
{
    delete progressbar;
}

void AddonItemDelegate::paint( QPainter *painter,
                               const QStyleOptionViewItem &option,
                               const QModelIndex &index ) const
{
    QStyleOptionViewItem newopt = option;
    auto i_state = index.data( AddonsModel::Role::STATE ).value<AddonsModel::State>();
    auto i_type = index.data( AddonsModel::Role::TYPE ).value<AddonsModel::Type>();

    /* Draw Background gradient by addon type */
    QColor backgroundColor = AddonsModel::getColorForType( i_type );

    if ( backgroundColor.isValid() )
    {
        painter->save();
        int i_corner = qMin( (int)(option.rect.width() * .05), 30 );
        QLinearGradient gradient(
                    QPoint( option.rect.right() - i_corner, option.rect.bottom() - i_corner ),
                    option.rect.bottomRight() );
        gradient.setColorAt( 0, Qt::transparent );
        gradient.setColorAt( 1.0, backgroundColor );
        painter->fillRect( option.rect, gradient );
        painter->restore();
    }

    /* Draw base info from parent */
    initStyleOption( &newopt, index );

    /* Draw common base  */
    QString name = index.data( Qt::DisplayRole ).toString();
    QPixmap icon = index.data( Qt::DecorationRole ).value<QPixmap>();
    QString description = index.data( AddonsModel::Role::SUMMARY ).toString();

    commonPaint(
        painter,
        newopt,
        margins,
        icon,
        name,
        description
        );

    painter->save();
    painter->setRenderHint( QPainter::TextAntialiasing );

    /* Addon status */
    if ( i_state == AddonsModel::State::STATE_INSTALLED )
    {
        painter->save();
        painter->setRenderHint( QPainter::Antialiasing );
        QMargins statusMargins( 5, 2, 5, 2 );
        QFont font( newopt.font );
        font.setBold( true );
        QFontMetrics metrics( font );
        painter->setFont( font );
        QRect statusRect = metrics.boundingRect( qtr("Installed") );
        statusRect.translate( newopt.rect.width() - statusRect.width(),
                              newopt.rect.top() + statusRect.height() );
        statusRect.adjust( - statusMargins.left() - statusMargins.right(),
                           0, 0,
                           statusMargins.top() + statusMargins.bottom() );
        QPainterPath path;
        path.addRoundedRect( statusRect, 2.0, 2.0 );
        painter->fillPath( path, QColor( Qt::green ).darker( 125 ) );
        painter->setPen( Qt::white );
        painter->drawText(
            statusRect.adjusted( statusMargins.left(), statusMargins.top(),
                                 -statusMargins.right(), -statusMargins.bottom() ),
            qtr("Installed") );
        painter->restore();
    }

    if ( newopt.state & QStyle::State_Selected )
        painter->setPen( newopt.palette.highlightedText().color() );

    /* Start below text */
    QRect textrect( newopt.rect );
    textrect.adjust( 2 * margins.left() + margins.right() + newopt.decorationSize.width(),
                     margins.top(),
                     - margins.right(),
                     - margins.bottom() - newopt.fontMetrics.height() );
    textrect.translate( 0, newopt.fontMetrics.height() * 2 );

    /* Version */
    QString version = index.data( AddonsModel::Role::ADDON_VERSION).toString();
    if ( !version.isEmpty() )
        painter->drawText( textrect, Qt::AlignLeft, qtr("Version %1").arg( version ) );

    textrect.translate( 0, newopt.fontMetrics.height() );

    /* Score */
    int i_score = index.data( AddonsModel::Role::SCORE).toInt();
    QPixmap scoreicon;
    if ( i_score )
    {
        int i_scoreicon_height = newopt.fontMetrics.height();
        int i_scoreicon_width = i_scoreicon_height * SCORE_ICON_WIDTH_SCALE;
        scoreicon = ImageHelper::loadSvgToPixmap( ":/addons/addon_score.svg",
                    i_scoreicon_width, i_scoreicon_height );
        int i_width = ( (float) i_score / AddonsModel::getMaxScore() ) * i_scoreicon_width;
        /* Erase the end (value) of our pixmap with a shadow */
        QPainter erasepainter( &scoreicon );
        erasepainter.setCompositionMode( QPainter::CompositionMode_SourceIn );
        erasepainter.fillRect( QRect( i_width, 0,
                                      scoreicon.width() - i_width, scoreicon.height() ),
                               newopt.palette.color( QPalette::Dark ) );
        erasepainter.end();
        painter->drawPixmap( textrect.topLeft(), scoreicon );
    }

    /* Downloads # */
    int i_downloads = index.data( AddonsModel::DOWNLOAD_COUNT).toInt();
    if ( i_downloads )
        painter->drawText( textrect.translated( scoreicon.width() + margins.left(), 0 ),
                           Qt::AlignLeft, qtr("%1 downloads").arg( i_downloads ) );

    painter->restore();

    if ( animator )
    {
        if ( animator->isRunning() && animator->getIndex() == index )
        {
            if ( i_state != AddonsModel::State::STATE_INSTALLING
                && i_state != AddonsModel::State::STATE_UNINSTALLING )
                animator->run( false );
        }
        /* Create our installation progress overlay */

        if ( i_state == AddonsModel::State::STATE_INSTALLING
            || i_state == AddonsModel::State::STATE_UNINSTALLING )
        {
            painter->save();
            painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
            painter->fillRect( newopt.rect, QColor( 255, 255, 255, 128 ) );
            if ( animator && index.isValid() )
            {
                animator->setIndex( index );
                animator->run( true );
                QSize adjustment = newopt.rect.size() / 4;
                progressbar->setGeometry(
                    newopt.rect.adjusted( adjustment.width(), adjustment.height(),
                                          -adjustment.width(), -adjustment.height() ) );
                painter->drawPixmap( newopt.rect.left() + adjustment.width(),
                                     newopt.rect.top() + adjustment.height(),
                                     progressbar->grab() );
            }
            painter->restore();
        }
    }
}

QSize AddonItemDelegate::sizeHint( const QStyleOptionViewItem &option,
                                   const QModelIndex &index ) const
{
    if ( index.isValid() )
    {
        return QSize( 200, 4 * option.fontMetrics.height()
                      + margins.top() + margins.bottom() );
    }
    else
        return QSize();
}

void AddonItemDelegate::initStyleOption( QStyleOptionViewItem *option,
                                            const QModelIndex &index ) const
{
    QStyledItemDelegate::initStyleOption( option, index );
    option->decorationSize = QSize( option->rect.height(), option->rect.height() );
    option->decorationSize -= QSize( margins.left() + margins.right(),
                                    margins.top() + margins.bottom() );
}

QWidget *AddonItemDelegate::createEditor( QWidget *parent,
                                          const QStyleOptionViewItem &option,
                                          const QModelIndex &index) const
{
    Q_UNUSED( option );
    QWidget *editorWidget = new QWidget( parent );
    QPushButton *installButton;
    QPushButton *infoButton;

    editorWidget->setLayout( new QHBoxLayout() );
    editorWidget->layout()->setContentsMargins(0, 0, 0, 0);

    infoButton = new QPushButton( QIcon( ":/menu/info.svg" ),
                                  qtr( "More information..." ) );
    connect( infoButton, &QPushButton::clicked, this, &AddonItemDelegate::showInfo );
    editorWidget->layout()->addWidget( infoButton );

    if ( index.data( AddonsModel::Role::MANAGEABLE).toBool() )
    {
        if ( index.data( AddonsModel::Role::STATE ).value<AddonsModel::State>()
            == AddonsModel::State::STATE_INSTALLED )
            installButton = new QPushButton( QIcon( ":/menu/remove.svg" ),
                                             qtr("&Uninstall"), parent );
        else
        {
            installButton = new QPushButton( qtr("&Install"), parent );
            installButton->setIcon( ColorizedSvgIcon::colorizedIconForWidget( ":/menu/add.svg", installButton ) );
        }
        connect( installButton, &QPushButton::clicked, this, &AddonItemDelegate::editButtonClicked );
        editorWidget->layout()->addWidget( installButton );
    }

    editorWidget->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );

    return editorWidget;
}

void AddonItemDelegate::updateEditorGeometry( QWidget *editor,
                                              const QStyleOptionViewItem &option,
                                              const QModelIndex &index) const
{
    Q_UNUSED( index );
    QSize size = editor->sizeHint();
    editor->setGeometry( option.rect.right() - size.width(),
                         option.rect.top() + ( option.rect.height() - size.height()),
                         size.width(),
                         size.height() );
}

void AddonItemDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
                                      const QModelIndex &index ) const
{
    model->setData( index, editor->property("Addon::state"), AddonsModel::Role::STATE);
}

void AddonItemDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
{
    editor->setProperty("Addon::state", index.data( AddonsModel::Role::STATE ) );
}

void AddonItemDelegate::setAnimator( DelegateAnimationHelper *animator_ )
{
    if ( !progressbar )
    {
        QProgressBar *progress = new QProgressBar(  );
        progress->setMinimum( 0 );
        progress->setMaximum( 0 );
        progress->setTextVisible( false );
        progressbar = progress;
    }
    animator = animator_;
}

void AddonItemDelegate::editButtonClicked()
{
    QWidget *editor = qobject_cast<QWidget *>(sender()->parent());
    if ( !editor ) return;
    auto value = editor->property("Addon::state").value<AddonsModel::State>();
    if ( value == AddonsModel::State::STATE_INSTALLED )
        /* uninstall */
        editor->setProperty("Addon::state", QVariant::fromValue(AddonsModel::State::STATE_UNINSTALLING) );
    else
        /* install */
        editor->setProperty("Addon::state", QVariant::fromValue(AddonsModel::State::STATE_INSTALLING) );
    emit commitData( editor );
    emit closeEditor( editor );
}

/* "More information" dialog */

ExtensionInfoDialog::ExtensionInfoDialog( const QModelIndex &index,
                                          qt_intf_t *p_intf,
                                          QWindow *parent )
       : QVLCDialog( parent, p_intf )
{
    // Let's be a modal dialog
    setWindowModality( Qt::WindowModal );

    // Window title
    setWindowTitle( qtr( "About" ) + " " + index.data(Qt::DisplayRole).toString() );

    // Layout
    QGridLayout *layout = new QGridLayout( this );

    // Icon
    QLabel *icon = new QLabel( this );
    QPixmap pix = index.data(Qt::DecorationRole).value<QPixmap>();
    Q_ASSERT( !pix.isNull() );
    icon->setPixmap( pix );
    icon->setAlignment( Qt::AlignCenter );
    icon->setFixedSize( 48, 48 );
    layout->addWidget( icon, 1, 0, 2, 1 );

    // Title
    QLabel *label = new QLabel( index.data(Qt::DisplayRole).toString(), this );
    QFont font = label->font();
    font.setBold( true );
    font.setPointSizeF( font.pointSizeF() * 1.3f );
    label->setFont( font );
    layout->addWidget( label, 0, 0, 1, -1 );

    // Version
    label = new QLabel( "<b>" + qtr( "Version" ) + ":</b>", this );
    layout->addWidget( label, 1, 1, 1, 1, Qt::AlignBottom );
    label = new QLabel( index.data(ExtensionListModel::VersionRole).toString(), this );
    layout->addWidget( label, 1, 2, 1, 2, Qt::AlignBottom );

    // Author
    label = new QLabel( "<b>" + qtr( "Author" ) + ":</b>", this );
    layout->addWidget( label, 2, 1, 1, 1, Qt::AlignTop );
    label = new QLabel( index.data(ExtensionListModel::AuthorRole).toString(), this );
    layout->addWidget( label, 2, 2, 1, 2, Qt::AlignTop );


    // Description
    label = new QLabel( this );
    label->setText( index.data(ExtensionListModel::SummaryRole).toString() );
    label->setWordWrap( true );
    label->setOpenExternalLinks( true );
    layout->addWidget( label, 4, 0, 1, -1 );

    // URL
    label = new QLabel( "<b>" + qtr( "Website" ) + ":</b>", this );
    layout->addWidget( label, 5, 0, 1, 2 );
    label = new QLabel( QString("<a href=\"%1\">%2</a>")
                        .arg( index.data(ExtensionListModel::LinkRole).toString() )
                        .arg( index.data(ExtensionListModel::LinkRole).toString() )
                        , this );
    label->setOpenExternalLinks( true );
    layout->addWidget( label, 5, 2, 1, -1 );

    // Script file
    label = new QLabel( "<b>" + qtr( "File" ) + ":</b>", this );
    layout->addWidget( label, 6, 0, 1, 2 );
    QLineEdit *line =
            new QLineEdit( index.data(ExtensionListModel::FilenameRole).toString(), this );
    line->setReadOnly( true );
    layout->addWidget( line, 6, 2, 1, -1 );

    // Close button
    QDialogButtonBox *group = new QDialogButtonBox( this );
    QPushButton *closeButton = new QPushButton( qtr( "&Close" ) );
    group->addButton( closeButton, QDialogButtonBox::RejectRole );
    BUTTONACT( closeButton, &ExtensionInfoDialog::close );

    layout->addWidget( group, 7, 0, 1, -1 );

    // Fix layout
    layout->setColumnStretch( 2, 1 );
    layout->setRowStretch( 4, 1 );
    setMinimumSize( 450, 350 );
}


AddonInfoDialog::AddonInfoDialog( const QModelIndex &index,
                                  qt_intf_t *p_intf, QWindow *parent )
       : QVLCDialog( parent, p_intf )
{
    // Let's be a modal dialog
    setWindowModality( Qt::WindowModal );

    // Window title
    setWindowTitle( qtr( "About" ) + " " + index.data(AddonsModel::Role::NAME).toString() );

    // Layout
    QGridLayout *layout = new QGridLayout( this );
    QLabel *label;

    // Icon
    QLabel *iconLabel = new QLabel( this );
    iconLabel->setFixedSize( 100, 100 );
    QPixmap icon = index.data( Qt::DecorationRole ).value<QPixmap>();
    icon.scaled( iconLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
    iconLabel->setPixmap( icon );
    iconLabel->setAlignment( Qt::AlignCenter | Qt::AlignTop );
    layout->addWidget( iconLabel, 1, 0, 2, 1 );

    // Title
    label = new QLabel( index.data(AddonsModel::Role::NAME).toString(), this );
    QFont font = label->font();
    font.setBold( true );
    font.setPointSizeF( font.pointSizeF() * 1.3f );
    label->setFont( font );
    layout->addWidget( label, 0, 0, 1, -1 );

    // HTML Content on right side
    QTextEdit *textContent = new QTextEdit();
    textContent->viewport()->setAutoFillBackground( false );
    textContent->setAcceptRichText( true );
    textContent->setBackgroundRole( QPalette::Window );
    textContent->setFrameStyle( QFrame::NoFrame );
    textContent->setAutoFillBackground( false );
    textContent->setReadOnly( true );
    layout->addWidget( textContent, 1, 1, 4, -1 );

    // Type
    QString type = AddonsModel::getLabelForType( index.data(AddonsModel::Role::TYPE).value<AddonsModel::Type>() );
    textContent->append( QString("<b>%1:</b> %2<br/>")
                         .arg( qtr("Type") ).arg( type ) );

    // Version
    QString version = index.data(AddonsModel::Role::ADDON_VERSION).toString();
    if ( !version.isEmpty() )
    {
        textContent->append( QString("<b>%1:</b> %2<br/>")
                             .arg( qtr("Version") ).arg( version ) );
    }

    // Author
    QString author = index.data(AddonsModel::Role::AUTHOR).toString();
    if ( !author.isEmpty() )
    {
        textContent->append( QString("<b>%1:</b> %2<br/>")
                             .arg( qtr("Author") ).arg( author ) );
    }

    // Summary
    textContent->append( QString("%1<br/>\n")
                .arg( index.data(AddonsModel::Role::SUMMARY).toString() ) );

    // Description
    QString description = index.data(AddonsModel::Role::DESCRIPTION).toString();
    if ( !description.isEmpty() )
    {
        textContent->append( QString("<hr/>\n%1")
                             .arg( description.replace("\n", "<br/>") ) );
    }

    // URL
    QString sourceUrl = index.data(AddonsModel::Role::LINK).toString();
    if ( !sourceUrl.isEmpty() )
    {
        label = new QLabel( "<b>" + qtr( "Website" ) + ":</b>", this );
        layout->addWidget( label, 5, 0, 1, 2 );
        label = new QLabel( QString("<a href=\"%1\">%2</a>")
                            .arg( sourceUrl ).arg( sourceUrl ), this );
        label->setOpenExternalLinks( true );
        layout->addWidget( label, 5, 2, 1, -1 );
    }

    // Script files
    QList<QVariant> list = index.data(AddonsModel::Role::FILENAME).toList();
    if ( ! list.empty() )
    {
        label = new QLabel( "<b>" + qtr( "Files" ) + ":</b>", this );
        layout->addWidget( label, 6, 0, 1, 2 );
        QComboBox *filesCombo = new QComboBox();
        Q_FOREACH( const QVariant & file, list )
            filesCombo->addItem( file.toString() );
        layout->addWidget( filesCombo, 6, 2, 1, -1 );
    }

    // Close button
    QDialogButtonBox *group = new QDialogButtonBox( this );
    QPushButton *closeButton = new QPushButton( qtr( "&Close" ) );
    group->addButton( closeButton, QDialogButtonBox::RejectRole );
    BUTTONACT( closeButton, &AddonInfoDialog::close );

    layout->addWidget( group, 7, 0, 1, -1 );

    // Fix layout
    layout->setColumnStretch( 2, 1 );
    layout->setRowStretch( 4, 1 );
    setMinimumSize( 640, 480 );
}

static QPixmap *loadPixmapFromData( char *data, int size )
{
    if( !data || size <= 0 )
        return NULL;
    QPixmap *pixmap = new QPixmap();
    if( !pixmap->loadFromData( (const uchar*) data, (uint) size ) )
    {
        delete pixmap;
        return NULL;
    }
    return pixmap;
}

Messung
CHZG
851009493

¤ Dauer der Verarbeitung: 0.8 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104857
#Domains=418424