Qt: split logically renderer widget and improve
authorFrancois Cartegnie <fcvlcdev@free.fr>
Sun, 10 Jun 2018 20:02:49 +0000 (22:02 +0200)
committerFrancois Cartegnie <fcvlcdev@free.fr>
Wed, 13 Jun 2018 13:29:00 +0000 (15:29 +0200)
modules/gui/qt/Makefile.am
modules/gui/qt/actions_manager.cpp
modules/gui/qt/actions_manager.hpp
modules/gui/qt/components/custom_menus.cpp [new file with mode: 0644]
modules/gui/qt/components/custom_menus.hpp [new file with mode: 0644]
modules/gui/qt/main_interface.cpp
modules/gui/qt/managers/renderer_manager.cpp [new file with mode: 0644]
modules/gui/qt/managers/renderer_manager.hpp [new file with mode: 0644]
modules/gui/qt/menus.cpp
modules/gui/qt/menus.hpp
po/POTFILES.in

index 6da5f5b..a37baeb 100644 (file)
@@ -48,6 +48,7 @@ libqt_plugin_la_SOURCES = \
        gui/qt/actions_manager.cpp gui/qt/actions_manager.hpp \
        gui/qt/extensions_manager.cpp gui/qt/extensions_manager.hpp \
        gui/qt/managers/addons_manager.cpp gui/qt/managers/addons_manager.hpp \
+       gui/qt/managers/renderer_manager.cpp gui/qt/managers/renderer_manager.hpp \
        gui/qt/recents.cpp gui/qt/recents.hpp \
        gui/qt/adapters/seekpoints.cpp gui/qt/adapters/seekpoints.hpp \
        gui/qt/adapters/chromaprint.cpp gui/qt/adapters/chromaprint.hpp \
@@ -92,6 +93,8 @@ libqt_plugin_la_SOURCES = \
        gui/qt/components/controller.cpp gui/qt/components/controller.hpp \
        gui/qt/components/controller_widget.cpp \
        gui/qt/components/controller_widget.hpp \
+       gui/qt/components/custom_menus.cpp \
+       gui/qt/components/custom_menus.hpp \
        gui/qt/components/epg/EPGChannels.cpp \
        gui/qt/components/epg/EPGChannels.hpp \
        gui/qt/components/epg/EPGItem.cpp \
@@ -169,6 +172,7 @@ nodist_libqt_plugin_la_SOURCES = \
        gui/qt/actions_manager.moc.cpp \
        gui/qt/extensions_manager.moc.cpp \
        gui/qt/managers/addons_manager.moc.cpp \
+       gui/qt/managers/renderer_manager.moc.cpp \
        gui/qt/recents.moc.cpp \
        gui/qt/adapters/seekpoints.moc.cpp \
        gui/qt/adapters/chromaprint.moc.cpp \
@@ -204,6 +208,7 @@ nodist_libqt_plugin_la_SOURCES = \
        gui/qt/components/interface_widgets.moc.cpp \
        gui/qt/components/controller.moc.cpp \
        gui/qt/components/controller_widget.moc.cpp \
+       gui/qt/components/custom_menus.moc.cpp \
        gui/qt/components/epg/EPGChannels.moc.cpp \
        gui/qt/components/epg/EPGProgram.moc.cpp \
        gui/qt/components/epg/EPGRuler.moc.cpp \
index e614e42..4b9d4e6 100644 (file)
@@ -27,7 +27,6 @@
 
 #include <vlc_vout.h>
 #include <vlc_actions.h>
-#include <vlc_renderer_discovery.h>
 
 #include "actions_manager.hpp"
 
 
 ActionsManager::ActionsManager( intf_thread_t * _p_i )
     : p_intf( _p_i )
-    , m_scanning( false )
 {
-    CONNECT( this, rendererItemAdded( vlc_renderer_item_t* ),
-             this, onRendererItemAdded( vlc_renderer_item_t* ) );
-    CONNECT( this, rendererItemRemoved( vlc_renderer_item_t* ),
-             this, onRendererItemRemoved( vlc_renderer_item_t* ) );
-    m_stop_scan_timer.setSingleShot( true );
-    CONNECT( &m_stop_scan_timer, timeout(), this, StopRendererScan() );
 }
 
 ActionsManager::~ActionsManager()
 {
-    StopRendererScan();
-    /* reset the list of renderers */
-    foreach (QAction* action, VLCMenuBar::rendererMenu->actions())
-    {
-        QVariant data = action->data();
-        if (!data.canConvert<QVariantHash>())
-            continue;
-        VLCMenuBar::rendererMenu->removeAction(action);
-        VLCMenuBar::rendererGroup->removeAction(action);
-    }
 }
 
 void ActionsManager::doAction( int id_action )
@@ -224,154 +206,3 @@ void ActionsManager::skipBackward()
     if( p_input )
         THEMIM->getIM()->jumpBwd();
 }
-
-vlc_renderer_item_t* ActionsManager::getMatchingRenderer( const QVariant &obj, vlc_renderer_item_t* p_item )
-{
-    if (!obj.canConvert<QVariantHash>())
-        return NULL;
-    QVariantHash qvh = obj.value<QVariantHash>();
-    if (!qvh.contains( "sout" ))
-        return NULL;
-    vlc_renderer_item_t* p_existing =
-            reinterpret_cast<vlc_renderer_item_t*>( qvh["sout"].value<void*>() );
-    if ( !strcasecmp(vlc_renderer_item_sout( p_existing ),
-                    vlc_renderer_item_sout( p_item ) ) )
-        return p_existing;
-    return NULL;
-}
-
-void ActionsManager::onRendererItemAdded(vlc_renderer_item_t* p_item)
-{
-    QAction *firstSeparator = NULL;
-
-    foreach (QAction* action, VLCMenuBar::rendererMenu->actions())
-    {
-        if (action->isSeparator())
-        {
-            firstSeparator = action;
-            break;
-        }
-        if (getMatchingRenderer( action->data(), p_item ))
-        {
-            vlc_renderer_item_release( p_item );
-            return; /* we already have this item */
-        }
-    }
-
-    QAction *action = new QAction( vlc_renderer_item_flags(p_item) & VLC_RENDERER_CAN_VIDEO ? QIcon( ":/sidebar/movie.svg" ) : QIcon( ":/sidebar/music.svg" ),
-                                   vlc_renderer_item_name(p_item), VLCMenuBar::rendererMenu );
-    action->setCheckable(true);
-
-    QVariantHash data;
-    data.insert( "sout", QVariant::fromValue( reinterpret_cast<void*>( p_item ) ) );
-    action->setData( data );
-    if (firstSeparator != NULL)
-    {
-        VLCMenuBar::rendererMenu->insertAction( firstSeparator, action );
-        VLCMenuBar::rendererGroup->addAction(action);
-    }
-    else
-    {
-        vlc_renderer_item_release( p_item );
-        delete action;
-    }
-}
-
-void ActionsManager::onRendererItemRemoved( vlc_renderer_item_t* p_item )
-{
-    foreach (QAction* action, VLCMenuBar::rendererMenu->actions())
-    {
-        if (action->isSeparator())
-            continue;
-        vlc_renderer_item_t *p_existing = getMatchingRenderer( action->data(), p_item );
-        if (p_existing)
-        {
-            VLCMenuBar::rendererMenu->removeAction( action );
-            VLCMenuBar::rendererGroup->removeAction( action );
-            vlc_renderer_item_release( p_existing );
-            break;
-        }
-    }
-    // Always release the item as we acquired it before emiting the signal
-    vlc_renderer_item_release( p_item );
-}
-
-void ActionsManager::renderer_event_item_added( vlc_renderer_discovery_t* p_rd,
-                                                vlc_renderer_item_t *p_item )
-{
-    ActionsManager *self = reinterpret_cast<ActionsManager*>( p_rd->owner.sys );
-    vlc_renderer_item_hold( p_item );
-    self->emit rendererItemAdded( p_item );
-}
-
-void ActionsManager::renderer_event_item_removed( vlc_renderer_discovery_t *p_rd,
-                                                  vlc_renderer_item_t *p_item )
-{
-    ActionsManager *self = reinterpret_cast<ActionsManager*>( p_rd->owner.sys );
-    vlc_renderer_item_hold( p_item );
-    self->emit rendererItemRemoved( p_item );
-}
-
-void ActionsManager::StartRendererScan()
-{
-    m_stop_scan_timer.stop();
-    if( m_scanning )
-        return;
-
-    /* SD subnodes */
-    char **ppsz_longnames;
-    char **ppsz_names;
-    if( vlc_rd_get_names( THEPL, &ppsz_names, &ppsz_longnames ) != VLC_SUCCESS )
-        return;
-
-    struct vlc_renderer_discovery_owner owner =
-    {
-        this,
-        renderer_event_item_added,
-        renderer_event_item_removed,
-    };
-
-    char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
-    for( ; *ppsz_name; ppsz_name++, ppsz_longname++ )
-    {
-        msg_Dbg( p_intf, "starting renderer discovery service %s", *ppsz_longname );
-        vlc_renderer_discovery_t* p_rd = vlc_rd_new( VLC_OBJECT(p_intf), *ppsz_name, &owner );
-        if( p_rd != NULL )
-            m_rds.push_back( p_rd );
-        free( *ppsz_name );
-        free( *ppsz_longname );
-    }
-    free( ppsz_names );
-    free( ppsz_longnames );
-    m_scanning = true;
-}
-
-void ActionsManager::RendererMenuCountdown()
-{
-    m_stop_scan_timer.start( 20000 );
-}
-
-void ActionsManager::StopRendererScan()
-{
-    foreach ( vlc_renderer_discovery_t* p_rd, m_rds )
-        vlc_rd_release( p_rd );
-    m_rds.clear();
-    m_scanning = false;
-}
-
-void ActionsManager::RendererSelected( QAction *selected )
-{
-    QVariant data = selected->data();
-    vlc_renderer_item_t *p_item = NULL;
-    if (data.canConvert<QVariantHash>())
-    {
-        QVariantHash hash = data.value<QVariantHash>();
-        if ( hash.contains( "sout" ) )
-            p_item = reinterpret_cast<vlc_renderer_item_t*>(
-                        hash["sout"].value<void*>() );
-    }
-    // If we failed to convert the action data to a vlc_renderer_item_t,
-    // assume the selected item was invalid, or most likely that "Local" was selected
-    playlist_SetRenderer( THEPL, p_item );
-}
-
index f82f6ee..c244699 100644 (file)
 
 #include "qt.hpp"
 #include "util/singleton.hpp"
-#include <QVector>
 
 #include <QObject>
-#include <QTimer>
-class QAction;
 
 typedef enum actionType_e
 {
@@ -76,16 +73,6 @@ private:
     virtual ~ActionsManager();
 
     intf_thread_t* const p_intf;
-    QVector<vlc_renderer_discovery_t*> m_rds;
-    QTimer m_stop_scan_timer;
-    bool m_scanning;
-
-    static void renderer_event_item_added( vlc_renderer_discovery_t *,
-                                           vlc_renderer_item_t * );
-    static void renderer_event_item_removed( vlc_renderer_discovery_t *,
-                                             vlc_renderer_item_t * );
-    static vlc_renderer_item_t* getMatchingRenderer( const QVariant &m_obj,
-                                                     vlc_renderer_item_t* p_item );
 
 public slots:
     void toggleMuteAudio();
@@ -95,24 +82,14 @@ public slots:
     void record();
     void skipForward();
     void skipBackward();
-    void StartRendererScan();
-    void RendererMenuCountdown();
-    void StopRendererScan();
-    void RendererSelected( QAction * );
 
 protected slots:
-    void onRendererItemAdded( vlc_renderer_item_t* );
-    void onRendererItemRemoved( vlc_renderer_item_t* );
     void fullscreen();
     void snapshot();
     void playlist();
     void frame();
 
     virtual void doAction( int );
-
-signals:
-    void rendererItemAdded( vlc_renderer_item_t* );
-    void rendererItemRemoved( vlc_renderer_item_t* );
 };
 
 #endif
diff --git a/modules/gui/qt/components/custom_menus.cpp b/modules/gui/qt/components/custom_menus.cpp
new file mode 100644 (file)
index 0000000..c791d3d
--- /dev/null
@@ -0,0 +1,198 @@
+/*****************************************************************************
+ * custom_menus.cpp : Qt custom menus classes
+ *****************************************************************************
+ * Copyright © 2006-2018 VideoLAN authors
+ *                  2018 VideoLabs
+ *
+ * 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 <vlc_common.h>
+#include <vlc_renderer_discovery.h>
+
+#include "custom_menus.hpp"
+#include "../managers/renderer_manager.hpp"
+
+#include <QMenu>
+#include <QAction>
+#include <QActionGroup>
+#include <QWidgetAction>
+#include <QVBoxLayout>
+#include <QLabel>
+#include <QProgressBar>
+
+RendererAction::RendererAction( vlc_renderer_item_t *p_item_ )
+    : QAction()
+{
+    p_item = p_item_;
+    vlc_renderer_item_hold( p_item );
+    if( vlc_renderer_item_flags( p_item ) & VLC_RENDERER_CAN_VIDEO )
+        setIcon( QIcon( ":/sidebar/movie.svg" ) );
+    else
+        setIcon( QIcon( ":/sidebar/music.svg" ) );
+    setText( vlc_renderer_item_name( p_item ) );
+    setCheckable(true);
+}
+
+RendererAction::~RendererAction()
+{
+    vlc_renderer_item_release( p_item );
+}
+
+vlc_renderer_item_t * RendererAction::getItem()
+{
+    return p_item;
+}
+
+RendererMenu::RendererMenu( QMenu *parent, intf_thread_t *p_intf_ )
+    : QMenu( parent ), p_intf( p_intf_ )
+{
+    setTitle( qtr("&Renderer") );
+
+    group = new QActionGroup( this );
+
+    QAction *action = new QAction( qtr("<Local>") );
+    action->setCheckable(true);
+    addAction( action );
+    group->addAction(action);
+
+    char *psz_renderer = var_InheritString( THEPL, "sout" );
+    if ( psz_renderer == NULL )
+        action->setChecked( true );
+    else
+        free( psz_renderer );
+
+    addSeparator();
+
+    QWidget *statusWidget = new QWidget();
+    statusWidget->setLayout( new QVBoxLayout );
+    QLabel *label = new QLabel();
+    label->setObjectName( "statuslabel" );
+    statusWidget->layout()->addWidget( label );
+    QProgressBar *pb = new QProgressBar();
+    pb->setObjectName( "statusprogressbar" );
+    pb->setMaximumHeight( 10 );
+    pb->setStyleSheet( QString("\
+        QProgressBar:horizontal {\
+            border: none;\
+            background: transparent;\
+            padding: 1px;\
+        }\
+        QProgressBar::chunk:horizontal {\
+            background: qlineargradient(x1: 0, y1: 0.5, x2: 1, y2: 0.5, \
+                        stop: 0 white, stop: 0.4 orange, stop: 0.6 orange, stop: 1 white);\
+        }") );
+    pb->setRange( 0, 0 );
+    pb->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Maximum );
+    statusWidget->layout()->addWidget( pb );
+    QWidgetAction *qwa = new QWidgetAction( this );
+    qwa->setDefaultWidget( statusWidget );
+    qwa->setDisabled( true );
+    addAction( qwa );
+    status = qwa;
+
+    RendererManager *manager = RendererManager::getInstance( p_intf );
+    CONNECT( this, aboutToShow(), manager, StartScan() );
+    CONNECT( group, triggered(QAction*), this, RendererSelected( QAction* ) );
+    DCONNECT( manager, rendererItemAdded( vlc_renderer_item_t * ),
+              this, addRendererItem( vlc_renderer_item_t * ) );
+    DCONNECT( manager, rendererItemRemoved( vlc_renderer_item_t * ),
+              this, removeRendererItem( vlc_renderer_item_t * ) );
+    CONNECT( manager, statusUpdated( int ),
+             this, updateStatus( int ) );
+}
+
+RendererMenu::~RendererMenu()
+{
+    reset();
+}
+
+void RendererMenu::updateStatus( int val )
+{
+    QProgressBar *pb = findChild<QProgressBar *>("statusprogressbar");
+    QLabel *label = findChild<QLabel *>("statuslabel");
+    if( val >= RendererManager::RendererStatus::RUNNING )
+    {
+        label->setText( qtr("Scanning...").
+               append( QString(" (%1s)").arg( val ) ) );
+        pb->setVisible( true );
+        status->setVisible( true );
+    }
+    else if( val == RendererManager::RendererStatus::FAILED )
+    {
+        label->setText( "Failed (no discovery module available)" );
+        pb->setVisible( false );
+        status->setVisible( true );
+    }
+    else status->setVisible( false );
+}
+
+void RendererMenu::addRendererItem( vlc_renderer_item_t *p_item )
+{
+    QAction *action = new RendererAction( p_item );
+    insertAction( status, action );
+    group->addAction( action );
+}
+
+void RendererMenu::removeRendererItem( vlc_renderer_item_t *p_item )
+{
+    foreach (QAction* action, group->actions())
+    {
+        RendererAction *ra = qobject_cast<RendererAction *>( action );
+        if( !ra || ra->getItem() != p_item )
+            continue;
+        removeRendererAction( ra );
+        delete ra;
+        break;
+    }
+}
+
+void RendererMenu::addRendererAction(QAction *action)
+{
+    insertAction( status, action );
+    group->addAction( action );
+}
+
+void RendererMenu::removeRendererAction(QAction *action)
+{
+    removeAction( action );
+    group->removeAction( action );
+}
+
+void RendererMenu::reset()
+{
+    /* reset the list of renderers */
+    foreach (QAction* action, group->actions())
+    {
+        RendererAction *ra = qobject_cast<RendererAction *>( action );
+        if( ra )
+        {
+            removeRendererAction( ra );
+            delete ra;
+        }
+    }
+}
+
+void RendererMenu::RendererSelected(QAction *action)
+{
+    RendererAction *ra = qobject_cast<RendererAction *>( action );
+    if( ra )
+        RendererManager::getInstance( p_intf )->SelectRenderer( ra->getItem() );
+    else
+        RendererManager::getInstance( p_intf )->SelectRenderer( NULL );
+}
diff --git a/modules/gui/qt/components/custom_menus.hpp b/modules/gui/qt/components/custom_menus.hpp
new file mode 100644 (file)
index 0000000..45c8c90
--- /dev/null
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ * custom_menus.hpp : Qt custom menus classes
+ *****************************************************************************
+ * Copyright © 2006-2018 VideoLAN authors
+ *                  2018 VideoLabs
+ *
+ * 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.
+ *****************************************************************************/
+#ifndef CUSTOM_MENUS_HPP
+#define CUSTOM_MENUS_HPP
+
+#include "qt.hpp"
+
+#include <QMenu>
+
+class RendererAction : public QAction
+{
+    Q_OBJECT
+
+    public:
+        RendererAction( vlc_renderer_item_t * );
+        ~RendererAction();
+        vlc_renderer_item_t *getItem();
+
+    private:
+        vlc_renderer_item_t *p_item;
+};
+
+class RendererMenu : public QMenu
+{
+    Q_OBJECT
+
+public:
+    RendererMenu( QMenu *, intf_thread_t * );
+    virtual ~RendererMenu();
+    void reset();
+
+private slots:
+    void addRendererItem( vlc_renderer_item_t * );
+    void removeRendererItem( vlc_renderer_item_t * );
+    void updateStatus( int );
+    void RendererSelected( QAction* );
+
+private:
+    void addRendererAction( QAction * );
+    void removeRendererAction( QAction * );
+    static vlc_renderer_item_t* getMatchingRenderer( const QVariant &,
+                                                     vlc_renderer_item_t* );
+    QAction *status;
+    QActionGroup *group;
+    intf_thread_t *p_intf;
+};
+
+
+#endif // CUSTOM_MENUS_HPP
index 8b87e29..0cb322b 100644 (file)
@@ -32,6 +32,7 @@
 #include "main_interface.hpp"
 #include "input_manager.hpp"                    // Creation
 #include "actions_manager.hpp"                  // killInstance
+#include "managers/renderer_manager.hpp"
 
 #include "util/customwidgets.hpp"               // qtEventToVLCKey, QVLCStackedWidget
 #include "util/qt_dirs.hpp"                     // toNativeSeparators
@@ -280,6 +281,8 @@ MainInterface::~MainInterface()
     /* Delete the FSC controller */
     delete fullscreenControls;
 
+    RendererManager::killInstance();
+
     /* Save states */
 
     settings->beginGroup("MainWindow");
diff --git a/modules/gui/qt/managers/renderer_manager.cpp b/modules/gui/qt/managers/renderer_manager.cpp
new file mode 100644 (file)
index 0000000..6a46270
--- /dev/null
@@ -0,0 +1,170 @@
+#include "renderer_manager.hpp"
+
+#include <QApplication>
+
+const QEvent::Type RendererManagerEvent::AddedEvent =
+        (QEvent::Type)QEvent::registerEventType();
+const QEvent::Type RendererManagerEvent::RemovedEvent =
+        (QEvent::Type)QEvent::registerEventType();
+
+RendererManager::RendererManager( intf_thread_t *p_intf_ ) :
+    p_intf( p_intf_ ), p_selected_item( NULL )
+{
+    CONNECT( &m_stop_scan_timer, timeout(), this, RendererMenuCountdown() );
+}
+
+RendererManager::~RendererManager()
+{
+    StopScan();
+    foreach( ItemEntry entry, m_items )
+    {
+        emit rendererItemRemoved( entry.second );
+        vlc_renderer_item_release( entry.second );
+    }
+}
+
+void RendererManager::customEvent( QEvent *event )
+{
+    if( event->type() == RendererManagerEvent::AddedEvent ||
+        event->type() == RendererManagerEvent::RemovedEvent )
+    {
+        RendererManagerEvent *ev = static_cast<RendererManagerEvent *>(event);
+        QString souturi( vlc_renderer_item_sout( ev->getItem() ) );
+        vlc_renderer_item_t *p_item = ev->getItem();
+
+        if( event->type() == RendererManagerEvent::AddedEvent )
+        {
+            if( !m_items.contains( souturi ) )
+            {
+                vlc_renderer_item_hold( p_item );
+                ItemEntry entry( true, p_item );
+                m_items.insert( souturi, entry );
+                emit rendererItemAdded( p_item );
+            }
+            else /* incref for now */
+            {
+                ItemEntry &entry = m_items[ souturi ];
+                entry.first = true; /* to be kept */
+            }
+        }
+        else /* remove event */
+        {
+            if( m_items.contains( souturi ) )
+            {
+                p_item = m_items[ souturi ].second;
+                if( p_selected_item != p_item )
+                {
+                    m_items.remove( souturi );
+                    emit rendererItemRemoved( p_item );
+                    vlc_renderer_item_release( p_item );
+                }
+                else m_items[ souturi ].first = true; /* keep */
+            }
+            /* else ignore */
+        }
+    }
+}
+
+void RendererManager::StartScan()
+{
+    if( m_stop_scan_timer.isActive() )
+        return;
+
+    /* SD subnodes */
+    char **ppsz_longnames;
+    char **ppsz_names;
+    if( vlc_rd_get_names( THEPL, &ppsz_names, &ppsz_longnames ) != VLC_SUCCESS )
+    {
+        emit statusUpdated( RendererManager::RendererStatus::FAILED );
+        return;
+    }
+
+    struct vlc_renderer_discovery_owner owner =
+    {
+        this,
+        renderer_event_item_added,
+        renderer_event_item_removed,
+    };
+
+    char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
+    for( ; *ppsz_name; ppsz_name++, ppsz_longname++ )
+    {
+        msg_Dbg( p_intf, "starting renderer discovery service %s", *ppsz_longname );
+        vlc_renderer_discovery_t* p_rd = vlc_rd_new( VLC_OBJECT(p_intf), *ppsz_name, &owner );
+        if( p_rd != NULL )
+            m_rds.push_back( p_rd );
+        free( *ppsz_name );
+        free( *ppsz_longname );
+    }
+    free( ppsz_names );
+    free( ppsz_longnames );
+
+    emit statusUpdated( RendererManager::RendererStatus::RUNNING );
+    m_scan_remain = 20000;
+    m_stop_scan_timer.setInterval( 1000 );
+    m_stop_scan_timer.start();
+}
+
+void RendererManager::StopScan()
+{
+    m_stop_scan_timer.stop();
+    foreach ( vlc_renderer_discovery_t* p_rd, m_rds )
+        vlc_rd_release( p_rd );
+    /* Cleanup of outdated items, and notify removal */
+    QHash<QString, ItemEntry>::iterator it = m_items.begin();
+    while ( it != m_items.end() )
+    {
+        ItemEntry &entry = it.value();
+        if( !entry.first /* don't keep */ && entry.second != p_selected_item )
+        {
+            emit rendererItemRemoved( entry.second );
+            vlc_renderer_item_release( entry.second );
+            it = m_items.erase( it );
+        }
+        else
+        {
+            entry.first = false; /* don't keep if not updated by new detect */
+            assert( it.value().first == false );
+            ++it;
+        }
+    }
+    m_rds.clear();
+    emit statusUpdated( RendererManager::RendererStatus::IDLE );
+}
+
+void RendererManager::RendererMenuCountdown()
+{
+    if( m_stop_scan_timer.isActive() && m_scan_remain > 0 )
+    {
+        m_scan_remain -= 1000;
+        emit statusUpdated( RendererManager::RendererStatus::RUNNING +  m_scan_remain / 1000 );
+    }
+    else
+    {
+        StopScan();
+    }
+}
+
+void RendererManager::SelectRenderer( vlc_renderer_item_t *p_item )
+{
+    p_selected_item = p_item;
+    playlist_SetRenderer( THEPL, p_item );
+}
+
+void RendererManager::renderer_event_item_added( vlc_renderer_discovery_t* p_rd,
+                                                 vlc_renderer_item_t *p_item )
+{
+    RendererManager *self = reinterpret_cast<RendererManager*>( p_rd->owner.sys );
+    QEvent *ev = new RendererManagerEvent( RendererManagerEvent::AddedEvent,
+                                           p_item );
+    QApplication::postEvent( self, ev );
+}
+
+void RendererManager::renderer_event_item_removed( vlc_renderer_discovery_t *p_rd,
+                                                   vlc_renderer_item_t *p_item )
+{
+    RendererManager *self = reinterpret_cast<RendererManager*>( p_rd->owner.sys );
+    QEvent *ev = new RendererManagerEvent( RendererManagerEvent::RemovedEvent,
+                                           p_item );
+    QApplication::postEvent( self, ev );
+}
diff --git a/modules/gui/qt/managers/renderer_manager.hpp b/modules/gui/qt/managers/renderer_manager.hpp
new file mode 100644 (file)
index 0000000..c812676
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef RENDERER_MANAGER_HPP
+#define RENDERER_MANAGER_HPP
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "qt.hpp"
+#include "util/singleton.hpp"
+
+#include <vlc_common.h>
+#include <vlc_renderer_discovery.h>
+
+#include <QObject>
+#include <QEvent>
+#include <QTimer>
+#include <QVector>
+#include <QHash>
+#include <QPair>
+
+class RendererManagerEvent : public QEvent
+{
+public:
+    static const QEvent::Type AddedEvent;
+    static const QEvent::Type RemovedEvent;
+
+    RendererManagerEvent( QEvent::Type type, vlc_renderer_item_t *p_item_ )
+        : QEvent( type ), p_item( p_item_ )
+    {
+        vlc_renderer_item_hold( p_item );
+    }
+    virtual ~RendererManagerEvent()
+    {
+        vlc_renderer_item_release( p_item );
+    }
+
+    vlc_renderer_item_t * getItem() const { return p_item; }
+
+private:
+    vlc_renderer_item_t *p_item;
+};
+
+class RendererManager : public QObject, public Singleton<RendererManager>
+{
+    Q_OBJECT
+    friend class Singleton<RendererManager>;
+
+public:
+    enum RendererStatus
+    {
+        FAILED = -2,
+        IDLE = -1,
+        RUNNING,
+    };
+    RendererManager( intf_thread_t * );
+    virtual ~RendererManager();
+    void customEvent( QEvent * );
+
+public slots:
+    void SelectRenderer( vlc_renderer_item_t * );
+    void StartScan();
+    void StopScan();
+
+signals:
+    void rendererItemAdded( vlc_renderer_item_t * );   /* For non queued only */
+    void rendererItemRemoved( vlc_renderer_item_t * ); /* For non queued only */
+    void statusUpdated( int );
+
+private:
+    static void renderer_event_item_added( vlc_renderer_discovery_t *,
+                                           vlc_renderer_item_t * );
+    static void renderer_event_item_removed( vlc_renderer_discovery_t *,
+                                             vlc_renderer_item_t * );
+
+    typedef std::pair<bool, vlc_renderer_item_t *> ItemEntry;
+    intf_thread_t* const p_intf;
+    const vlc_renderer_item_t *p_selected_item;
+    QVector<vlc_renderer_discovery_t*> m_rds;
+    QHash<QString, ItemEntry> m_items;
+    QTimer m_stop_scan_timer;
+    unsigned m_scan_remain;
+
+private slots:
+    void RendererMenuCountdown();
+};
+
+#endif // RENDERER_MANAGER_HPP
index 14e07e4..1f877cb 100644 (file)
@@ -80,8 +80,7 @@ enum
 static QActionGroup *currentGroup;
 
 QMenu *VLCMenuBar::recentsMenu = NULL;
-QMenu *VLCMenuBar::rendererMenu = NULL;
-QActionGroup *VLCMenuBar::rendererGroup = NULL;
+RendererMenu *VLCMenuBar::rendererMenu = NULL;
 
 /**
  * @brief Add static entries to DP in menus
@@ -733,10 +732,10 @@ QMenu *VLCMenuBar::NavigMenu( intf_thread_t *p_intf, QMenu *menu )
 
     menu->addSeparator();
 
-    if ( !rendererMenu )
-        rendererMenu = RendererMenu( p_intf );
+    if ( !VLCMenuBar::rendererMenu )
+        VLCMenuBar::rendererMenu = new RendererMenu( menu, p_intf );
 
-    menu->addMenu( rendererMenu );
+    menu->addMenu( VLCMenuBar::rendererMenu );
     menu->addSeparator();
 
 
@@ -1630,33 +1629,3 @@ void VLCMenuBar::updateRecents( intf_thread_t *p_intf )
         }
     }
 }
-
-QMenu *VLCMenuBar::RendererMenu(intf_thread_t *p_intf, QMenu *menu )
-{
-    QMenu *submenu = new QMenu( qtr("&Renderer"), menu );
-
-    rendererGroup = new QActionGroup(submenu);
-
-    QAction *action = new QAction( qtr("<Local>"), submenu );
-    action->setCheckable(true);
-    submenu->addAction( action );
-    rendererGroup->addAction(action);
-
-    char *psz_renderer = var_InheritString( THEPL, "sout" );
-    if ( psz_renderer == NULL )
-        action->setChecked( true );
-    else
-        free( psz_renderer );
-
-    submenu->addSeparator();
-
-    action = new QAction( qtr("Scanning..."), submenu );
-    action->setEnabled( false );
-    submenu->addAction( action );
-
-    CONNECT( submenu, aboutToShow(), ActionsManager::getInstance( p_intf ), StartRendererScan() );
-    CONNECT( submenu, aboutToHide(), ActionsManager::getInstance( p_intf ), RendererMenuCountdown() );
-    CONNECT( rendererGroup, triggered(QAction*), ActionsManager::getInstance( p_intf ), RendererSelected( QAction* ) );
-
-    return submenu;
-}
index 1bc6519..bfa0c81 100644 (file)
@@ -27,6 +27,8 @@
 
 #include "qt.hpp"
 
+#include "components/custom_menus.hpp"
+
 #include <QObject>
 #include <QMenu>
 #include <QVector>
@@ -94,8 +96,7 @@ public:
     };
     Q_DECLARE_FLAGS(actionflags, actionflag)
 
-    static QMenu *rendererMenu;
-    static QActionGroup *rendererGroup;
+    static RendererMenu *rendererMenu;
 
 private:
     /* All main Menus */
@@ -131,7 +132,6 @@ private:
     }
 
     static QMenu *HelpMenu( QWidget * );
-    static QMenu *RendererMenu(intf_thread_t *p_intf , QMenu *menu = NULL );
 
     /* Popups Menus */
     static void PopupMenuStaticEntries( QMenu *menu );
index 7683c43..4428605 100644 (file)
@@ -595,6 +595,8 @@ modules/gui/qt/components/controller.cpp
 modules/gui/qt/components/controller.hpp
 modules/gui/qt/components/controller_widget.cpp
 modules/gui/qt/components/controller_widget.hpp
+modules/gui/qt/components/custom_menus.cpp
+modules/gui/qt/components/custom_menus.hpp
 modules/gui/qt/components/epg/EPGChannels.cpp
 modules/gui/qt/components/epg/EPGChannels.hpp
 modules/gui/qt/components/epg/EPGItem.cpp