Commit ca08ed0e authored by François Cartegnie's avatar François Cartegnie 🤞

Qt: Set popup entries logic into models, and keep interaction outside.

- popup now created according to the selected items and models.
- fixes view/popup model abstraction.
- allows introducing new methods/models.
parent 3de79f56
......@@ -41,6 +41,7 @@
#include "components/playlist/sorting.h"
#include "dialogs_provider.hpp"
#include "input_manager.hpp" /* THEMIM */
#include "util/qt_dirs.hpp"
#include <assert.h>
#include <vlc_intf_strings.h>
......@@ -99,7 +100,7 @@ MLModel::~MLModel()
var_DelCallback( p_ml, "media-added", mediaAdded, this );
}
void MLModel::clearPlaylist()
void MLModel::removeAll()
{
vlc_array_t* p_where = vlc_array_new();
if ( !p_where ) return;
......@@ -499,32 +500,116 @@ void MLModel::activateItem( const QModelIndex &idx )
AddItemToPlaylist( itemId( idx, MLMEDIA_ID ), true, p_ml, true );
}
void MLModel::action( QAction *action, const QModelIndexList &indexes )
bool MLModel::action( QAction *action, const QModelIndexList &indexes )
{
actionsContainerType a = action->data().value<actionsContainerType>();
input_item_t *p_input;
switch ( a.action )
{
case actionsContainerType::ACTION_PLAY:
case ACTION_PLAY:
if ( ! indexes.empty() && indexes.first().isValid() )
{
activateItem( indexes.first() );
return true;
}
break;
case actionsContainerType::ACTION_ADDTOPLAYLIST:
case ACTION_ADDTOPLAYLIST:
foreach( const QModelIndex &index, indexes )
{
if( !index.isValid() ) break;
if( !index.isValid() ) return false;
AddItemToPlaylist( itemId( index, MLMEDIA_ID ), false, p_ml, true );
}
break;
return true;
case actionsContainerType::ACTION_REMOVE:
case ACTION_REMOVE:
doDelete( indexes );
return true;
case ACTION_SORT:
break;
case actionsContainerType::ACTION_SORT:
case ACTION_CLEAR:
removeAll();
return true;
case ACTION_ENQUEUEFILE:
foreach( const QString &uri, a.uris )
playlist_Add( THEPL, uri.toAscii().constData(),
NULL, PLAYLIST_APPEND | PLAYLIST_PREPARSE,
PLAYLIST_END, false, pl_Unlocked );
return true;
case ACTION_ENQUEUEDIR:
if( a.uris.isEmpty() ) return false;
p_input = input_item_New( a.uris.first().toAscii().constData(), NULL );
if( unlikely( p_input == NULL ) ) return false;
/* FIXME: playlist_AddInput() can fail */
playlist_AddInput( THEPL, p_input,
PLAYLIST_APPEND,
PLAYLIST_END, true, pl_Unlocked );
vlc_gc_decref( p_input );
return true;
case ACTION_ENQUEUEGENERIC:
foreach( const QString &uri, a.uris )
{
p_input = input_item_New( qtu( uri ), NULL );
/* Insert options */
foreach( const QString &option, a.options.split( " :" ) )
{
QString temp = colon_unescape( option );
if( !temp.isEmpty() )
input_item_AddOption( p_input, qtu( temp ),
VLC_INPUT_OPTION_TRUSTED );
}
/* FIXME: playlist_AddInput() can fail */
playlist_AddInput( THEPL, p_input,
PLAYLIST_APPEND | PLAYLIST_PREPARSE,
PLAYLIST_END, false, pl_Unlocked );
vlc_gc_decref( p_input );
}
return true;
default:
break;
}
return false;
}
bool MLModel::isSupportedAction( actions action, const QModelIndex &index ) const
{
switch ( action )
{
case ACTION_ADDTOPLAYLIST:
return index.isValid();
case ACTION_SORT:
return false;
case ACTION_PLAY:
case ACTION_STREAM:
case ACTION_SAVE:
case ACTION_INFO:
case ACTION_REMOVE:
return index.isValid();
case ACTION_EXPLORE:
if( index.isValid() )
return getURI( index ).startsWith( "file://" );
case ACTION_CREATENODE:
return false;
case ACTION_CLEAR:
return rowCount() && canEdit();
case ACTION_ENQUEUEFILE:
case ACTION_ENQUEUEDIR:
case ACTION_ENQUEUEGENERIC:
return canEdit();
default:
return false;
}
return false;
}
QModelIndex MLModel::rootIndex() const
......@@ -545,13 +630,6 @@ bool MLModel::canEdit() const
return true;
}
bool MLModel::isCurrentItem( const QModelIndex &index, playLocation where ) const
{
if ( where == IN_SQLMEDIALIB )
return index.isValid();
return false;
}
QModelIndex MLModel::getIndexByMLID( int id ) const
{
for( int i = 0; i < rowCount( ); i++ )
......
......@@ -82,6 +82,7 @@ public:
Qt::ItemFlags flags( const QModelIndex& ) const;
QMimeData* mimeData( const QModelIndexList & indexes ) const;
virtual bool removeRows( int row, int count, const QModelIndex & parent = QModelIndex() );
virtual void sort( const int column, Qt::SortOrder order = Qt::AscendingOrder );
// Custom functions
bool isEditable( const QModelIndex& ) const;
......@@ -94,22 +95,21 @@ public:
virtual void rebuild( playlist_item_t * p = NULL );
virtual void doDelete( QModelIndexList selected );
virtual void createNode( QModelIndex, QString ) {};
virtual void removeAll();
virtual QModelIndex rootIndex() const;
virtual void filter( const QString& search_text, const QModelIndex & root, bool b_recursive );
virtual void sort( const int column, Qt::SortOrder order = Qt::AscendingOrder );
virtual QModelIndex currentIndex() const;
virtual QModelIndex indexByPLID( const int i_plid, const int c ) const;
virtual QModelIndex indexByInputItemID( const int i_inputitem_id, const int c ) const;
virtual bool isTree() const;
virtual bool canEdit() const;
virtual bool isCurrentItem( const QModelIndex &index, playLocation where ) const;
virtual void action( QAction *action, const QModelIndexList &indexes );
virtual bool action( QAction *action, const QModelIndexList &indexes );
virtual bool isSupportedAction( actions action, const QModelIndex & ) const;
/* VLCModelSubInterface virtual slots */
virtual void activateItem( const QModelIndex &index );
virtual void clearPlaylist();
protected:
......
......@@ -30,6 +30,7 @@
#include "qt4.hpp"
#include "components/playlist/playlist_model.hpp"
#include "input_manager.hpp" /* THEMIM */
#include "util/qt_dirs.hpp"
#include <vlc_intf_strings.h> /* I_DIR */
......@@ -90,6 +91,16 @@ QModelIndexList VLCProxyModel::mapListToSource( const QModelIndexList& list )
return newlist;
}
void VLCProxyModel::sort( const int column, Qt::SortOrder order )
{
/* sorting on PLModel affects playlist order. */
if ( model() == sourcemodels[ PL_MODEL ] )
model()->sort( column, order );
else
/* otherwise we just use native proxy sorting */
QSortFilterProxyModel::sort( column, order );
}
/*************************************************************************
* Playlist model implementation
*************************************************************************/
......@@ -479,20 +490,6 @@ PLItem* PLModel::getItem( const QModelIndex & index ) const
return item;
}
bool PLModel::isCurrentItem( const QModelIndex &index, playLocation where ) const
{
if ( where == IN_PLAYLIST )
{
return itemId( index, PLAYLIST_ID ) == THEPL->p_playing->i_id;
}
else if ( where == IN_MEDIALIBRARY )
{
return ( p_playlist->p_media_library &&
rootItem->inputItem() == p_playlist->p_media_library->p_input );
}
return false;
}
QModelIndex PLModel::index( const int row, const int column, const QModelIndex &parent )
const
{
......@@ -615,6 +612,15 @@ PLItem * PLModel::findInner( PLItem *root, int i_id, bool b_isinputid ) const
return NULL;
}
PLModel::pl_nodetype PLModel::getPLRootType() const
{
if ( rootItem->id( PLAYLIST_ID ) == 3 )
return ROOTTYPE_MEDIA_LIBRARY;
else
return ROOTTYPE_CURRENT_PLAYING; // id == 2
/* FIXME: handle all cases */
}
bool PLModel::canEdit() const
{
return (
......@@ -1006,7 +1012,7 @@ void PLModel::filter( const QString& search_text, const QModelIndex & idx, bool
rebuild();
}
void PLModel::clearPlaylist()
void PLModel::removeAll()
{
if( rowCount() < 1 ) return;
......@@ -1032,19 +1038,24 @@ void PLModel::createNode( QModelIndex index, QString name )
PL_UNLOCK;
}
void PLModel::action( QAction *action, const QModelIndexList &indexes )
bool PLModel::action( QAction *action, const QModelIndexList &indexes )
{
QModelIndex index;
actionsContainerType a = action->data().value<actionsContainerType>();
input_item_t *p_input;
switch ( a.action )
{
case actionsContainerType::ACTION_PLAY:
case ACTION_PLAY:
if ( !indexes.empty() && indexes.first().isValid() )
{
activateItem( indexes.first() );
return true;
}
break;
case actionsContainerType::ACTION_ADDTOPLAYLIST:
case ACTION_ADDTOPLAYLIST:
PL_LOCK;
foreach( const QModelIndex &currentIndex, indexes )
{
......@@ -1056,22 +1067,107 @@ void PLModel::action( QAction *action, const QModelIndexList &indexes )
PLAYLIST_END );
}
PL_UNLOCK;
break;
return true;
case actionsContainerType::ACTION_REMOVE:
case ACTION_REMOVE:
doDelete( indexes );
break;
return true;
case actionsContainerType::ACTION_SORT:
case ACTION_SORT:
if ( indexes.empty() ) break;
index = indexes.first().parent();
if( !index.isValid() ) index = rootIndex();
sort( indexes.first(), index,
a.column > 0 ? a.column - 1 : -a.column - 1,
a.column > 0 ? Qt::AscendingOrder : Qt::DescendingOrder );
return true;
case ACTION_CLEAR:
removeAll();
return true;
case ACTION_ENQUEUEFILE:
foreach( const QString &uri, a.uris )
playlist_Add( THEPL, uri.toAscii().constData(),
NULL, PLAYLIST_APPEND | PLAYLIST_PREPARSE,
PLAYLIST_END,
getPLRootType() == ROOTTYPE_CURRENT_PLAYING,
pl_Unlocked );
return true;
case ACTION_ENQUEUEDIR:
if( a.uris.isEmpty() ) break;
p_input = input_item_New( a.uris.first().toAscii().constData(), NULL );
if( unlikely( p_input == NULL ) ) break;
/* FIXME: playlist_AddInput() can fail */
playlist_AddInput( THEPL, p_input,
PLAYLIST_APPEND,
PLAYLIST_END,
getPLRootType() == ROOTTYPE_CURRENT_PLAYING,
pl_Unlocked );
vlc_gc_decref( p_input );
return true;
case ACTION_ENQUEUEGENERIC:
foreach( const QString &uri, a.uris )
{
p_input = input_item_New( qtu( uri ), NULL );
/* Insert options */
foreach( const QString &option, a.options.split( " :" ) )
{
QString temp = colon_unescape( option );
if( !temp.isEmpty() )
input_item_AddOption( p_input, qtu( temp ),
VLC_INPUT_OPTION_TRUSTED );
}
/* FIXME: playlist_AddInput() can fail */
playlist_AddInput( THEPL, p_input,
PLAYLIST_APPEND | PLAYLIST_PREPARSE,
PLAYLIST_END, true, pl_Unlocked );
vlc_gc_decref( p_input );
}
return true;
default:
break;
}
return false;
}
bool PLModel::isSupportedAction( actions action, const QModelIndex &index ) const
{
switch ( action )
{
case ACTION_ADDTOPLAYLIST:
/* Only if we are not already in Current Playing */
if ( getPLRootType() == ROOTTYPE_CURRENT_PLAYING ) return false;
if( index.isValid() && index != rootIndex() )
return ( itemId( index, PLAYLIST_ID ) != THEPL->p_playing->i_id );
case ACTION_SORT:
return rowCount();
case ACTION_PLAY:
case ACTION_STREAM:
case ACTION_SAVE:
case ACTION_INFO:
case ACTION_REMOVE:
return index.isValid() && index != rootIndex();
case ACTION_EXPLORE:
if( index.isValid() )
return getURI( index ).startsWith( "file://" );
case ACTION_CREATENODE:
return ( canEdit() && isTree() );
case ACTION_CLEAR:
return rowCount() && canEdit();
case ACTION_ENQUEUEFILE:
case ACTION_ENQUEUEDIR:
case ACTION_ENQUEUEGENERIC:
return canEdit();
default:
return false;
}
return false;
}
/******************* Drag and Drop helper class ******************/
......
......@@ -60,12 +60,13 @@ public:
return qobject_cast<VLCModel *>( sourceModel() );
}
/* Different Models Handling */
enum models
{
PL_MODEL = 0,
SQLML_MODEL /* note: keep it last */
};
bool switchToModel( models type );
void setModel( models type, VLCModel *model )
{
......@@ -73,24 +74,17 @@ public:
}
QModelIndexList mapListToSource( const QModelIndexList& list );
/* Different Models Handling */
/* VLCModelSubInterface Methods */
virtual void rebuild( playlist_item_t * p = NULL ) { model()->rebuild( p ); }
virtual void doDelete( QModelIndexList list ) { model()->doDelete( mapListToSource( list ) ); }
virtual void createNode( QModelIndex a, QString b ) { model()->createNode( mapToSource( a ), b ); }
virtual void removeAll() { model()->removeAll(); }
virtual QModelIndex rootIndex() const { return mapFromSource( model()->rootIndex() ); }
virtual void filter( const QString& text, const QModelIndex & root, bool b_recursive )
{
model()->filter( text, mapToSource( root ), b_recursive );
}
virtual void sort( const int column, Qt::SortOrder order = Qt::AscendingOrder )
{
/* use native */
QSortFilterProxyModel::sort( column, order );
}
virtual QModelIndex currentIndex() const { return mapFromSource( model()->currentIndex() ); }
virtual QModelIndex indexByPLID( const int i_plid, const int c ) const { return mapFromSource( model()->indexByPLID( i_plid, c ) ); }
......@@ -99,19 +93,20 @@ public:
virtual bool isTree() const { return model()->isTree(); }
virtual bool canEdit() const { return model()->canEdit(); }
virtual bool isCurrentItem( const QModelIndex &index, playLocation where ) const { return model()->isCurrentItem( mapToSource( index ), where ); }
virtual QString getURI( const QModelIndex &index ) const { return model()->getURI( mapToSource( index ) ); }
virtual input_item_t *getInputItem( const QModelIndex &index ) const { return model()->getInputItem( mapToSource( index ) ); }
virtual QString getTitle( const QModelIndex &index ) const { return model()->getTitle( mapToSource( index ) ); }
virtual void action( QAction *action, const QModelIndexList &indexes )
virtual bool action( QAction *action, const QModelIndexList &indexes )
{
model()->action( action, mapListToSource( indexes ) );
return model()->action( action, mapListToSource( indexes ) );
}
virtual bool isSupportedAction( actions action, const QModelIndex &index ) const { return model()->isSupportedAction( action, mapToSource( index ) ); }
/* Indirect slots handlers */
virtual void activateItem( const QModelIndex &index ) { model()->activateItem( mapToSource( index ) ); }
virtual void ensureArtRequested( const QModelIndex &index ) { model()->ensureArtRequested( mapToSource( index ) ); }
virtual void clearPlaylist() { model()->clearPlaylist(); }
/* AbstractItemModel subclassing */
virtual void sort( const int column, Qt::SortOrder order = Qt::AscendingOrder );
/* Local signals for index conversion */
public slots:
......@@ -172,6 +167,7 @@ public:
virtual void rebuild( playlist_item_t * p = NULL );
virtual void doDelete( QModelIndexList selected );
virtual void createNode( QModelIndex index, QString name );
virtual void removeAll();
/* Lookups */
virtual QModelIndex rootIndex() const;
......@@ -181,12 +177,11 @@ public:
virtual QModelIndex indexByInputItemID( const int i_inputitem_id, const int c ) const;
virtual bool isTree() const;
virtual bool canEdit() const;
virtual bool isCurrentItem( const QModelIndex &index, playLocation where ) const;
virtual void action( QAction *action, const QModelIndexList &indexes );
virtual bool action( QAction *action, const QModelIndexList &indexes );
virtual bool isSupportedAction( actions action, const QModelIndex & ) const;
/* VLCModelSubInterface indirect slots */
virtual void activateItem( const QModelIndex &index );
virtual void clearPlaylist();
protected:
/* VLCModel subclassing */
......@@ -235,6 +230,12 @@ private:
PLItem *findByPLId( PLItem *, int i_plitemid ) const;
PLItem *findByInputId( PLItem *, int i_input_itemid ) const;
PLItem *findInner(PLItem *, int i_id, bool b_isinputid ) const;
enum pl_nodetype
{
ROOTTYPE_CURRENT_PLAYING,
ROOTTYPE_MEDIA_LIBRARY
};
pl_nodetype getPLRootType() const;
/* */
QString latestSearch;
......
......@@ -37,8 +37,10 @@
#include "menus.hpp" /* Popup */
#include "input_manager.hpp" /* THEMIM */
#include "dialogs_provider.hpp" /* THEDP */
#include "recents.hpp" /* RecentMRL */
#include "dialogs/playlist.hpp" /* Playlist Dialog */
#include "dialogs/mediainfo.hpp" /* MediaInfoDialog */
#include "util/qt_dirs.hpp"
#include <vlc_services_discovery.h> /* SD_CMD_SEARCH */
#include <vlc_intf_strings.h> /* POP_ */
......@@ -140,6 +142,16 @@ void StandardPLPanel::handleExpansion( const QModelIndex& index )
void StandardPLPanel::popupPlView( const QPoint &point )
{
QPoint globalPoint = currentView->viewport()->mapToGlobal( point );
QModelIndex index = currentView->indexAt( point );
if ( !index.isValid() )
{
currentView->clearSelection();
}
else if ( ! currentView->selectionModel()->selectedIndexes().contains( index ) )
{
currentView->selectionModel()->select( index, QItemSelectionModel::Select );
}
if( !popup( globalPoint ) ) VLCMenuBar::PopupMenu( p_intf, true );
}
......@@ -147,10 +159,15 @@ void StandardPLPanel::popupPlView( const QPoint &point )
bool StandardPLPanel::popup( const QPoint &point )
{
QModelIndex index = popupIndex( currentView ); /* index for menu logic only. Do not store.*/
VLCProxyModel *model = qobject_cast<VLCProxyModel *>(currentView->model());
#define ADD_MENU_ENTRY( icon, title, act ) \
if ( model->isSupportedAction( act, index ) )\
{\
action = menu.addAction( icon, title ); \
container.action = act; \
action->setData( QVariant::fromValue( container ) )
action->setData( QVariant::fromValue( container ) );\
}
/* */
QMenu menu;
......@@ -158,93 +175,75 @@ bool StandardPLPanel::popup( const QPoint &point )
VLCModelSubInterface::actionsContainerType container;
/* Play/Stream/Info static actions */
if( index.isValid() )
{
ADD_MENU_ENTRY( QIcon( ":/menu/play" ), qtr(I_POP_PLAY),
container.ACTION_PLAY );
menu.addAction( QIcon( ":/menu/stream" ), qtr(I_POP_STREAM),
this, SLOT( popupStream() ) );
ADD_MENU_ENTRY( QIcon( ":/menu/play" ), qtr(I_POP_PLAY),
VLCModelSubInterface::ACTION_PLAY )
menu.addAction( QIcon(), qtr(I_POP_SAVE),
this, SLOT( popupSave() ) );
ADD_MENU_ENTRY( QIcon( ":/menu/stream" ), qtr(I_POP_STREAM),
VLCModelSubInterface::ACTION_STREAM )
menu.addAction( QIcon( ":/menu/info" ), qtr(I_POP_INFO),
this, SLOT( popupInfoDialog() ) );
ADD_MENU_ENTRY( QIcon(), qtr(I_POP_SAVE),
VLCModelSubInterface::ACTION_SAVE );
menu.addSeparator();
ADD_MENU_ENTRY( QIcon( ":/menu/info" ), qtr(I_POP_INFO),
VLCModelSubInterface::ACTION_INFO );
if( model->getURI( index ).startsWith( "file://" ) )
menu.addAction( QIcon( ":/type/folder-grey" ), qtr(I_POP_EXPLORE),
this, SLOT( popupExplore() ) );
}
menu.addSeparator();
ADD_MENU_ENTRY( QIcon( ":/type/folder-grey" ), qtr(I_POP_EXPLORE),
VLCModelSubInterface::ACTION_EXPLORE );
QIcon addIcon( ":/buttons/playlist/playlist_add" );
ADD_MENU_ENTRY( addIcon, qtr(I_POP_NEWFOLDER),
VLCModelSubInterface::ACTION_CREATENODE )
menu.addSeparator();
/* In PL or ML, allow to add a file/folder */
if( model->canEdit() )
{
QIcon addIcon( ":/buttons/playlist/playlist_add" );
ADD_MENU_ENTRY( addIcon, qtr(I_PL_ADDF),
VLCModelSubInterface::ACTION_ENQUEUEFILE )
if( model->isTree() )
menu.addAction( addIcon, qtr(I_POP_NEWFOLDER),
this, SLOT( popupPromptAndCreateNode() ) );
ADD_MENU_ENTRY( addIcon, qtr(I_PL_ADDDIR),
VLCModelSubInterface::ACTION_ENQUEUEDIR )
menu.addSeparator();
if( model->isCurrentItem( model->rootIndex(), VLCModelSubInterface::IN_PLAYLIST ) )
{
menu.addAction( addIcon, qtr(I_PL_ADDF), THEDP, SLOT( simplePLAppendDialog()) );
menu.addAction( addIcon, qtr(I_PL_ADDDIR), THEDP, SLOT( PLAppendDir()) );
menu.addAction( addIcon, qtr(I_OP_ADVOP), THEDP, SLOT( PLAppendDialog()) );
}
else if( model->isCurrentItem( model->rootIndex(), VLCModelSubInterface::IN_MEDIALIBRARY ) )
{
menu.addAction( addIcon, qtr(I_PL_ADDF), THEDP, SLOT( simpleMLAppendDialog()) );
menu.addAction( addIcon, qtr(I_PL_ADDDIR), THEDP, SLOT( MLAppendDir() ) );
menu.addAction( addIcon, qtr(I_OP_ADVOP), THEDP, SLOT( MLAppendDialog() ) );
}
}
ADD_MENU_ENTRY( addIcon, qtr(I_OP_ADVOP),