]> git.sesse.net Git - vlc/commitdiff
skins2: fix some important playlist limitations or bugs
authorErwan Tulou <erwan10@videolan.org>
Tue, 17 May 2011 15:12:24 +0000 (17:12 +0200)
committerErwan Tulou <erwan10@videolan.org>
Mon, 6 Jun 2011 20:14:09 +0000 (22:14 +0200)
This patch includes the following :
  - fix item misplacement (items were only appended instead of being inserted)
  - fix slider scrolling that could not adapt to the real size of the playlist
  - enhance drag&drop by allowing users to finely insert item being dropped
    into either the playlist or the media library.
  - optimise refresh (only rebuild playtree in case of visible item)
  - remove keeping a reference to a playlist_item_t* (since it is not
    refcounted, a lookup from the playlist with proper lock mechanism
    is needed)
  - remove the m_deleted flag (corner cases were never dealt with) and
    replace it with a notification prior to deletion
  - implement operator++ to simplify iterating visible items (cosmetics)

A deeper redesign/simplification and support for the new sql playlist would be a good thing though.

modules/gui/skins2/controls/ctrl_tree.cpp
modules/gui/skins2/controls/ctrl_tree.hpp
modules/gui/skins2/utils/var_percent.hpp
modules/gui/skins2/utils/var_tree.cpp
modules/gui/skins2/utils/var_tree.hpp
modules/gui/skins2/vars/playtree.cpp
modules/gui/skins2/vars/playtree.hpp

index 48d694d3bd1ad3b73716dea3cd0639cdb0842146..f86ac4bb12eb985075dd368bb562ca5e2887feed 100644 (file)
@@ -6,6 +6,7 @@
  *
  * Authors: Antoine Cellerier <dionoea@videolan.org>
  *          Clément Stenac <zorglub@videolan.org>
+ *          Erwan Tulou  <erwan10 At videolan DoT 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
 #include "../events/evt_key.hpp"
 #include "../events/evt_mouse.hpp"
 #include "../events/evt_scroll.hpp"
+#include "../events/evt_dragndrop.hpp"
+#include "../vars/playtree.hpp"
 #include <vlc_keys.h>
 
-#define SCROLL_STEP 0.05
 #define LINE_INTERVAL 1  // Number of pixels inserted between 2 lines
 
 
@@ -59,28 +61,23 @@ CtrlTree::CtrlTree( intf_thread_t *pIntf,
     CtrlGeneric( pIntf,rHelp, pVisible), m_rTree( rTree), m_rFont( rFont ),
     m_pBgBitmap( pBgBitmap ), m_pItemBitmap( pItemBitmap ),
     m_pOpenBitmap( pOpenBitmap ), m_pClosedBitmap( pClosedBitmap ),
-    m_pScaledBitmap( NULL ),
-    m_fgColor( fgColor ), m_playColor( playColor ), m_bgColor1( bgColor1 ),
-    m_bgColor2( bgColor2 ), m_selColor( selColor ),
-    m_pLastSelected( NULL ), m_pImage( NULL ), m_dontMove( false )
+    m_pScaledBitmap( NULL ), m_pImage( NULL ),
+    m_fgColor( fgColor ), m_playColor( playColor ),
+    m_bgColor1( bgColor1 ), m_bgColor2( bgColor2 ), m_selColor( selColor ),
+    m_firstPos( m_rTree.end() ), m_lastClicked( m_rTree.end() ),
+    m_itOver( m_rTree.end() ), m_flat( pFlat->get() ), m_capacity( -1.0 ),
+    m_bRefreshOnDelete( false )
 {
-    // Observe the tree and position variables
+    // Observe the tree
     m_rTree.addObserver( this );
-    m_rTree.getPositionVar().addObserver( this );
-
-    m_flat = pFlat->get();
-
-    m_firstPos = m_flat ? m_rTree.firstLeaf() : m_rTree.begin();
-
-    makeImage();
+    m_rTree.setFlat( m_flat );
 }
 
 CtrlTree::~CtrlTree()
 {
-    m_rTree.getPositionVar().delObserver( this );
     m_rTree.delObserver( this );
-    delete m_pScaledBitmap;
     delete m_pImage;
+    delete m_pScaledBitmap;
 }
 
 int CtrlTree::itemHeight()
@@ -126,356 +123,260 @@ int CtrlTree::itemImageWidth()
     return bitmapWidth + 2;
 }
 
-int CtrlTree::maxItems()
+float CtrlTree::maxItems()
 {
     const Position *pPos = getPosition();
     if( !pPos )
     {
         return -1;
     }
-    return pPos->getHeight() / itemHeight();
+    return (float)pPos->getHeight() / itemHeight();
 }
 
-
 void CtrlTree::onUpdate( Subject<VarTree, tree_update> &rTree,
                          tree_update *arg )
 {
     (void)rTree;
-    if( arg->type == arg->UpdateItem ) // Item update
+    if( arg->type == arg->ItemInserted )
     {
-        if( arg->b_active_item )
-            autoScroll();
-        if( isItemVisible( arg->i_id ) )
+        if( isItemVisible( arg->it ) )
         {
             makeImage();
             notifyLayout();
         }
+        setSliderFromFirst();
     }
-    else if ( arg->type == arg->ResetAll ) // Global change or deletion
-    {
-        m_firstPos = m_flat ? m_rTree.firstLeaf() : m_rTree.begin();
-
-        makeImage();
-        notifyLayout();
-    }
-    else if ( arg->type == arg->AppendItem ) // Item-append
+    else if( arg->type == arg->ItemUpdated )
     {
-        if( m_flat && m_firstPos->size() )
+        if( arg->it->isPlaying() )
         {
-            m_firstPos = m_rTree.getNextLeaf( m_firstPos );
+            m_rTree.ensureExpanded( arg->it );
+            ensureVisible( arg->it );
 
             makeImage();
             notifyLayout();
+            setSliderFromFirst();
         }
-        else if( isItemVisible( arg->i_id ) )
+        else if( isItemVisible( arg->it ) )
         {
             makeImage();
             notifyLayout();
         }
     }
-    else if( arg->type == arg->DeleteItem ) // item-del
+    else if( arg->type == arg->DeletingItem )
     {
-        /* Make sure firstPos is valid */
-        VarTree::Iterator it_old = m_firstPos;
-        while( m_firstPos->isDeleted() &&
-               m_firstPos != (m_flat ? m_rTree.firstLeaf()
-                                     : m_rTree.begin()) )
+        if( isItemVisible( arg->it ) )
+            m_bRefreshOnDelete = true;
+        // remove all references to arg->it
+        // if it is the one about to be deleted
+        if( m_firstPos == arg->it )
         {
-            m_firstPos = m_flat ? m_rTree.getPrevLeaf( m_firstPos )
-                                : m_rTree.getPrevVisibleItem( m_firstPos );
+            m_firstPos = getNearestItem( arg->it );
         }
-        if( m_firstPos->isDeleted() )
-            m_firstPos = m_rTree.begin();
-
-        if( m_firstPos != it_old || isItemVisible( arg->i_id ) )
+        if( m_lastClicked == arg->it )
         {
-            makeImage();
-            notifyLayout();
+            m_lastClicked = getNearestItem( arg->it );
+            m_lastClicked->setSelected( arg->it->isSelected() );
         }
     }
-}
-
-void CtrlTree::onUpdate( Subject<VarPercent> &rPercent, void* arg)
-{
-    (void)rPercent; (void)arg;
-    // Determine what is the first item to display
-    VarTree::Iterator it = m_flat ? m_rTree.firstLeaf() : m_rTree.begin();
-
-    if( m_dontMove ) return;
-
-    int excessItems;
-    if( m_flat )
-        excessItems = m_rTree.countLeafs() - maxItems();
-    else
-        excessItems = m_rTree.visibleItems() - maxItems();
-
-    if( excessItems > 0)
+    else if( arg->type == arg->ItemDeleted )
     {
-        VarPercent &rVarPos = m_rTree.getPositionVar();
-        // a simple (int)(...) causes rounding errors !
-#ifdef _MSC_VER
-#   define lrint (int)
-#endif
-        if( m_flat )
-            it = m_rTree.getLeaf(lrint( (1.0 - rVarPos.get()) * (double)excessItems ) + 1 );
-        else
-            it = m_rTree.getVisibleItem(lrint( (1.0 - rVarPos.get()) * (double)excessItems ) + 1 );
+        if( m_bRefreshOnDelete )
+        {
+            m_bRefreshOnDelete = false;
+
+            makeImage();
+            notifyLayout();
+        }
+        setSliderFromFirst();
     }
-    if( m_firstPos != it )
+    else if( arg->type == arg->ResetAll )
     {
-        // Redraw the control if the position has changed
-        m_firstPos = it;
+        m_lastClicked = m_rTree.end();
+        m_firstPos = getFirstFromSlider();
+
         makeImage();
         notifyLayout();
+        setSliderFromFirst();
+    }
+    else if( arg->type == arg->SliderChanged )
+    {
+        Iterator it = getFirstFromSlider();
+        if( m_firstPos != it )
+        {
+            m_firstPos = it;
+            makeImage();
+            notifyLayout();
+        }
     }
 }
 
 void CtrlTree::onResize()
 {
-    // Determine what is the first item to display
-    VarTree::Iterator it = m_flat ? m_rTree.firstLeaf() : m_rTree.begin();
-
-    int excessItems;
-    if( m_flat )
-        excessItems = m_rTree.countLeafs() - maxItems();
-    else
-        excessItems = m_rTree.visibleItems() - maxItems();
-
-    if( excessItems > 0)
-    {
-        VarPercent &rVarPos = m_rTree.getPositionVar();
-        // a simple (int)(...) causes rounding errors !
-#ifdef _MSC_VER
-#   define lrint (int)
-#endif
-        if( m_flat )
-            it = m_rTree.getLeaf(lrint( (1.0 - rVarPos.get()) * (double)excessItems ) + 1 );
-        else
-            it = m_rTree.getVisibleItem(lrint( (1.0 - rVarPos.get()) * (double)excessItems ) + 1 );
-    }
-    // Redraw the control if the position has changed
-    m_firstPos = it;
-    makeImage();
+    onPositionChange();
 }
 
 void CtrlTree::onPositionChange()
 {
+    m_capacity = maxItems();
+    setScrollStep();
+    m_firstPos = getFirstFromSlider();
     makeImage();
 }
 
 void CtrlTree::handleEvent( EvtGeneric &rEvent )
 {
-    bool bChangedPosition = false;
-    VarTree::Iterator toShow; bool needShow = false;
+    bool needShow = false;
+    bool needRefresh = false;
+    Iterator toShow = m_firstPos;
     if( rEvent.getAsString().find( "key:down" ) != string::npos )
     {
         int key = ((EvtKey&)rEvent).getKey();
-        VarTree::Iterator it;
-        bool previousWasSelected = false;
 
         /* Delete the selection */
         if( key == KEY_DELETE )
         {
-            /* Find first non selected item before m_pLastSelected */
-            VarTree::Iterator it_sel = m_flat ? m_rTree.firstLeaf()
-                                              : m_rTree.begin();
-            for( it = (m_flat ? m_rTree.firstLeaf() : m_rTree.begin());
-                 it != m_rTree.end();
-                 it = (m_flat ? m_rTree.getNextLeaf( it )
-                              : m_rTree.getNextVisibleItem( it )) )
-            {
-                if( &*it == m_pLastSelected ) break;
-                if( !it->isSelected() ) it_sel = it;
-            }
-
             /* Delete selected stuff */
             m_rTree.delSelected();
-
-            /* Verify if there is still sthg selected (e.g read-only items) */
-            m_pLastSelected = NULL;
-            for( it = (m_flat ? m_rTree.firstLeaf() : m_rTree.begin());
-                 it != m_rTree.end();
-                 it = (m_flat ? m_rTree.getNextLeaf( it )
-                              : m_rTree.getNextVisibleItem( it )) )
-            {
-                if( it->isSelected() )
-                    m_pLastSelected = &*it;
-            }
-
-            /* if everything was deleted, use it_sel as last selection */
-            if( !m_pLastSelected )
-            {
-                it_sel->setSelected( true );
-                m_pLastSelected = &*it_sel;
-            }
-
-            // Redraw the control
-            makeImage();
-            notifyLayout();
         }
         else if( key == KEY_PAGEDOWN )
         {
-            it = m_firstPos;
-            int i = (int)(maxItems()*1.5);
-            while( i >= 0 )
+            int numSteps = (int)m_capacity / 2;
+            VarPercent &rVarPos = m_rTree.getPositionVar();
+            rVarPos.increment( -numSteps );
+        }
+        else if( key == KEY_PAGEUP )
+        {
+            int numSteps = (int)m_capacity / 2;
+            VarPercent &rVarPos = m_rTree.getPositionVar();
+            rVarPos.increment( numSteps );
+        }
+        else if( key == KEY_UP )
+        {
+            // Scroll up one item
+            m_rTree.unselectTree();
+            if( m_lastClicked != m_rTree.end() )
             {
-                VarTree::Iterator it_old = it;
-                it = m_flat ? m_rTree.getNextLeaf( it )
-                            : m_rTree.getNextVisibleItem( it );
-                /* End is already visible, dont' scroll */
-                if( it == m_rTree.end() )
+                if( --m_lastClicked != m_rTree.end() )
                 {
-                    it = it_old;
-                    break;
+                    m_lastClicked->setSelected( true );
                 }
-                needShow = true;
-                i--;
             }
-            if( needShow )
+            if( m_lastClicked == m_rTree.end() )
             {
-                ensureVisible( it );
-                makeImage();
-                notifyLayout();
+                m_lastClicked = m_firstPos;
+                if( m_lastClicked != m_rTree.end() )
+                    m_lastClicked->setSelected( true );
             }
+            needRefresh = true;
+            needShow = true; toShow = m_lastClicked;
         }
-        else if (key == KEY_PAGEUP )
+        else if( key == KEY_DOWN )
         {
-            it = m_firstPos;
-            int i = maxItems();
-            while( i >= maxItems()/2 )
+            // Scroll down one item
+            m_rTree.unselectTree();
+            if( m_lastClicked != m_rTree.end() )
             {
-                it = m_flat ? m_rTree.getPrevLeaf( it )
-                            : m_rTree.getPrevVisibleItem( it );
-                /* End is already visible, dont' scroll */
-                if( it == ( m_flat ? m_rTree.firstLeaf() : m_rTree.begin() ) )
+                Iterator it_old = m_lastClicked;
+                if( ++m_lastClicked != m_rTree.end() )
                 {
-                    break;
+                    m_lastClicked->setSelected( true );
+                }
+                else
+                {
+                    it_old->setSelected( true );
+                    m_lastClicked = it_old;
                 }
-                i--;
             }
-            ensureVisible( it );
-            makeImage();
-            notifyLayout();
+            else
+            {
+                m_lastClicked = m_firstPos;
+                if( m_lastClicked != m_rTree.end() )
+                    m_lastClicked->setSelected( true );
+            }
+            needRefresh = true;
+            needShow = true; toShow = m_lastClicked;
         }
-        else if ( key == KEY_UP ||
-                  key == KEY_DOWN ||
-                  key == KEY_LEFT ||
-                  key == KEY_RIGHT ||
-                  key == KEY_ENTER ||
-                  key == ' ' )
+        else if( key == KEY_RIGHT )
         {
-            for( it = m_flat ? m_rTree.firstLeaf() : m_rTree.begin();
-                 it != m_rTree.end();
-                 it = m_flat ? m_rTree.getNextLeaf( it )
-                             : m_rTree.getNextVisibleItem( it ) )
+            // Go down one level (and expand node)
+            Iterator& it = m_lastClicked;
+            if( it != m_rTree.end() )
             {
-                VarTree::Iterator next = m_flat ?
-                                         m_rTree.getNextLeaf( it ) :
-                                         m_rTree.getNextVisibleItem( it );
-                if( key == KEY_UP )
+                if( !m_flat && !it->isExpanded() && it->size() )
                 {
-                    // Scroll up one item
-                    if( ( it->parent()
-                          && it != it->parent()->begin() )
-                        || &*it != m_pLastSelected )
-                    {
-                        bool nextWasSelected = ( &*next == m_pLastSelected );
-                        it->setSelected( nextWasSelected );
-                        if( nextWasSelected )
-                        {
-                            m_pLastSelected = &*it;
-                            needShow = true; toShow = it;
-                        }
-                    }
+                    it->setExpanded( true );
+                    needRefresh = true;
                 }
-                else if( key == KEY_DOWN )
+                else
                 {
-                    // Scroll down one item
-                    if( ( it->parent()
-                          && next != it->parent()->end() )
-                        || &*it != m_pLastSelected )
+                    m_rTree.unselectTree();
+                    Iterator it_old = m_lastClicked;
+                    if( ++m_lastClicked != m_rTree.end() )
                     {
-                        it->setSelected( previousWasSelected );
+                        m_lastClicked->setSelected( true );
                     }
-                    if( previousWasSelected )
+                    else
                     {
-                        m_pLastSelected = &*it;
-                        needShow = true; toShow = it;
-                        previousWasSelected = false;
+                        it_old->setSelected( true );
+                        m_lastClicked = it_old;
                     }
-                    else
+                    needRefresh = true;
+                    needShow = true; toShow = m_lastClicked;
+                }
+            }
+        }
+        else if( key == KEY_LEFT )
+        {
+            // Go up one level (and close node)
+            Iterator& it = m_lastClicked;
+            if( it != m_rTree.end() )
+            {
+                if( m_flat )
+                {
+                    m_rTree.unselectTree();
+                    if( --m_lastClicked != m_rTree.end() )
                     {
-                        previousWasSelected = ( &*it == m_pLastSelected );
+                        m_lastClicked->setSelected( true );
                     }
-
-                    // Fix last tree item selection
-                    if( ( m_flat ? m_rTree.getNextLeaf( it )
-                        : m_rTree.getNextVisibleItem( it ) ) == m_rTree.end()
-                     && &*it == m_pLastSelected )
+                    else
                     {
-                        it->setSelected( true );
+                        m_lastClicked = m_firstPos;
+                        if( m_lastClicked != m_rTree.end() )
+                            m_lastClicked->setSelected( true );
                     }
+                    needRefresh = true;
+                    needShow = true; toShow = m_lastClicked;
                 }
-                else if( key == KEY_RIGHT )
+                else
                 {
-                    // Go down one level (and expand node)
-                    if( &*it == m_pLastSelected )
+                    if( it->isExpanded() )
                     {
-                        if( it->isExpanded() )
-                        {
-                            if( it->size() )
-                            {
-                                it->setSelected( false );
-                                it->begin()->setSelected( true );
-                                m_pLastSelected = &*(it->begin());
-                            }
-                            else
-                            {
-                                m_rTree.action( &*it );
-                            }
-                        }
-                        else
-                        {
-                            it->setExpanded( true );
-                            bChangedPosition = true;
-                        }
+                        it->setExpanded( false );
+                        needRefresh = true;
                     }
-                }
-                else if( key == KEY_LEFT )
-                {
-                    // Go up one level (and close node)
-                    if( &*it == m_pLastSelected )
+                    else
                     {
-                        if( it->isExpanded() && it->size() )
-                        {
-                            it->setExpanded( false );
-                            bChangedPosition = true;
-                        }
-                        else
+                        Iterator it_parent = it.getParent();
+                        if( it_parent != m_rTree.end() )
                         {
-                            if( it->parent() && it->parent() != &m_rTree)
-                            {
-                                it->setSelected( false );
-                                m_pLastSelected = it->parent();
-                                m_pLastSelected->setSelected( true );
-                            }
+                            it->setSelected( false );
+                            m_lastClicked = it_parent;
+                            m_lastClicked->setSelected( true );
+                            needRefresh = true;
+                            needShow = true; toShow = m_lastClicked;
                         }
                     }
                 }
-                else if( key == KEY_ENTER || key == ' ' )
-                {
-                    // Go up one level (and close node)
-                    if( &*it == m_pLastSelected )
-                    {
-                        m_rTree.action( &*it );
-                    }
-                }
             }
-            if( needShow )
-                ensureVisible( toShow );
-            // Redraw the control
-            makeImage();
-            notifyLayout();
+        }
+        else if( key == KEY_ENTER || key == ' ' )
+        {
+            // Go up one level (and close node)
+            if( m_lastClicked != m_rTree.end() )
+            {
+                m_rTree.action( &*m_lastClicked );
+            }
         }
         else
         {
@@ -484,188 +385,159 @@ void CtrlTree::handleEvent( EvtGeneric &rEvent )
             var_SetInteger( getIntf()->p_libvlc, "key-pressed",
                             rEvtKey.getModKey() );
         }
-
     }
 
     else if( rEvent.getAsString().find( "mouse:left" ) != string::npos )
     {
         EvtMouse &rEvtMouse = (EvtMouse&)rEvent;
         const Position *pos = getPosition();
-        int yPos = ( rEvtMouse.getYPos() - pos->getTop() ) / itemHeight();
         int xPos = rEvtMouse.getXPos() - pos->getLeft();
-        VarTree::Iterator it;
+        int yPos = ( rEvtMouse.getYPos() - pos->getTop() ) / itemHeight();
 
-        if( rEvent.getAsString().find( "mouse:left:down:ctrl,shift" ) !=
-            string::npos )
+        Iterator itClicked = findItemAtPos( yPos );
+        if( itClicked != m_rTree.end() )
         {
-            VarTree::Iterator itClicked = findItemAtPos( yPos );
-            // Flag to know if the current item must be selected
-            bool select = false;
-            for( it = m_flat ? m_rTree.firstLeaf() : m_rTree.begin();
-                 it != m_rTree.end();
-                 it = m_flat ? m_rTree.getNextLeaf( it )
-                             : m_rTree.getNextVisibleItem( it ) )
+            if( rEvent.getAsString().find( "mouse:left:down:ctrl,shift" ) !=
+                string::npos )
             {
-                bool nextSelect = select;
-                if( it == itClicked || &*it == m_pLastSelected )
+                // Flag to know if the current item must be selected
+                bool select = false;
+                for( Iterator it = m_firstPos; it != m_rTree.end(); ++it )
                 {
-                    if( select )
-                    {
-                        nextSelect = false;
-                    }
-                    else
+                    bool nextSelect = select;
+                    if( it == itClicked || it == m_lastClicked )
                     {
-                        select = true;
-                        nextSelect = true;
+                        if( select )
+                        {
+                            nextSelect = false;
+                        }
+                        else
+                        {
+                            select = true;
+                            if( itClicked != m_lastClicked )
+                                nextSelect = true;
+                        }
                     }
+                    it->setSelected( it->isSelected() || select );
+                    select = nextSelect;
+                    needRefresh = true;
                 }
-                it->setSelected( it->isSelected() || select );
-                select = nextSelect;
             }
-            // Redraw the control
-            makeImage();
-            notifyLayout();
-        }
-        else if( rEvent.getAsString().find( "mouse:left:down:ctrl" ) !=
-                 string::npos )
-        {
-            // Invert the selection of the item
-            it = findItemAtPos( yPos );
-            if( it != m_rTree.end() )
+            else if( rEvent.getAsString().find( "mouse:left:down:ctrl" ) !=
+                     string::npos )
             {
-                it->toggleSelected();
-                m_pLastSelected = &*it;
+                // Invert the selection of the item
+                itClicked->toggleSelected();
+                m_lastClicked = itClicked;
+                needRefresh = true;
             }
-            // Redraw the control
-            makeImage();
-            notifyLayout();
-        }
-        else if( rEvent.getAsString().find( "mouse:left:down:shift" ) !=
-                 string::npos )
-        {
-            VarTree::Iterator itClicked = findItemAtPos( yPos );
-            // Flag to know if the current item must be selected
-            bool select = false;
-            for( it = m_flat ? m_rTree.firstLeaf() : m_rTree.begin();
-                 it != m_rTree.end();
-                 it = m_flat ? m_rTree.getNextLeaf( it )
-                             : m_rTree.getNextVisibleItem( it ) )
+            else if( rEvent.getAsString().find( "mouse:left:down:shift" ) !=
+                     string::npos )
             {
-                bool nextSelect = select;
-                if( it == itClicked || &*it == m_pLastSelected )
+                bool select = false;
+                for( Iterator it = m_firstPos; it != m_rTree.end(); ++it )
                 {
-                    if( select )
-                    {
-                        nextSelect = false;
-                    }
-                    else
+                    bool nextSelect = select;
+                    if( it == itClicked || it == m_lastClicked )
                     {
-                        select = true;
-                        nextSelect = true;
+                        if( select )
+                        {
+                            nextSelect = false;
+                        }
+                        else
+                        {
+                            select = true;
+                            if( itClicked != m_lastClicked )
+                                nextSelect = true;
+                        }
                     }
+                    it->setSelected( select );
+                    select = nextSelect;
                 }
-                it->setSelected( select );
-                select = nextSelect;
+                needRefresh = true;
             }
-            // Redraw the control
-            makeImage();
-            notifyLayout();
-        }
-        else if( rEvent.getAsString().find( "mouse:left:down" ) !=
-                 string::npos )
-        {
-            it = findItemAtPos(yPos);
-            if( it != m_rTree.end() )
+            else if( rEvent.getAsString().find( "mouse:left:down" ) !=
+                     string::npos )
             {
-                if( ( it->size() && xPos > (it->depth() - 1) * itemImageWidth()
-                      && xPos < it->depth() * itemImageWidth() )
-                 && !m_flat )
+                if( !m_flat &&
+                    itClicked->size() &&
+                    xPos > (itClicked->depth() - 1) * itemImageWidth() &&
+                    xPos < itClicked->depth() * itemImageWidth() )
                 {
                     // Fold/unfold the item
-                    it->toggleExpanded();
-                    bChangedPosition = true;
+                    itClicked->toggleExpanded();
                 }
                 else
                 {
                     // Unselect any previously selected item
-                    VarTree::Iterator it2;
-                    for( it2 = m_flat ? m_rTree.firstLeaf() : m_rTree.begin();
-                         it2 != m_rTree.end();
-                         it2 = m_flat ? m_rTree.getNextLeaf( it2 )
-                                      : m_rTree.getNextVisibleItem( it2 ) )
-                    {
-                        it2->setSelected( false );
-                    }
+                    m_rTree.unselectTree();
                     // Select the new item
-                    if( it != m_rTree.end() )
-                    {
-                        it->setSelected( true );
-                        m_pLastSelected = &*it;
-                    }
+                    itClicked->setSelected( true );
+                    m_lastClicked = itClicked;
                 }
+                needRefresh = true;
             }
-            // Redraw the control
-            makeImage();
-            notifyLayout();
-        }
-        else if( rEvent.getAsString().find( "mouse:left:dblclick" ) !=
-                 string::npos )
-        {
-            it = findItemAtPos(yPos);
-            if( it != m_rTree.end() )
+            else if( rEvent.getAsString().find( "mouse:left:dblclick" ) !=
+                     string::npos )
             {
                // Execute the action associated to this item
-               m_rTree.action( &*it );
+               m_rTree.action( &*itClicked );
             }
-            // Redraw the control
-            makeImage();
-            notifyLayout();
         }
     }
 
     else if( rEvent.getAsString().find( "scroll" ) != string::npos )
     {
-        // XXX ctrl_slider.cpp has two more (but slightly different)
-        // XXX implementations of `scroll'. Figure out where it belongs.
-
         int direction = static_cast<EvtScroll&>(rEvent).getDirection();
-
-        double percentage = m_rTree.getPositionVar().get();
-        double step = 2.0 / (double)( m_flat ? m_rTree.countLeafs()
-                                             : m_rTree.visibleItems() );
         if( direction == EvtScroll::kUp )
-        {
-            percentage += step;
-        }
+            m_rTree.getPositionVar().increment( +1 );
         else
-        {
-            percentage -= step;
-        }
-        m_rTree.getPositionVar().set( percentage );
+            m_rTree.getPositionVar().increment( -1 );
     }
 
-    /* We changed the nodes, let's fix the position var */
-    if( bChangedPosition )
+    else if( rEvent.getAsString().find( "drag:over" ) != string::npos )
     {
-        VarTree::Iterator it;
-        int iFirst = 0;
-        for( it = m_flat ? m_rTree.firstLeaf() : m_rTree.begin();
-             it != m_rTree.end();
-             it = m_flat ? m_rTree.getNextLeaf( it )
-                         : m_rTree.getNextVisibleItem( it ) )
+        EvtDragOver& evt = static_cast<EvtDragOver&>(rEvent);
+        const Position *pos = getPosition();
+        int yPos = ( evt.getYPos() - pos->getTop() ) / itemHeight();
+
+        Iterator it = findItemAtPos( yPos );
+        if( it != m_itOver )
         {
-            if( it == m_firstPos )
-                break;
-            iFirst++;
+            if( it != m_rTree.end() )
+                it->setExpanded( true );
+            m_itOver = it;
+            needRefresh = true;
         }
+    }
+
+    else if( rEvent.getAsString().find( "drag:drop" ) != string::npos )
+    {
+        EvtDragDrop& evt = static_cast<EvtDragDrop&>(rEvent);
+        Playtree& rPlaytree = static_cast<Playtree&>(m_rTree);
+        rPlaytree.insertItems( *m_itOver, evt.getFiles(), false );
+        m_itOver = m_rTree.end();
+        needRefresh = true;
+    }
 
-        int indexMax = ( m_flat ? m_rTree.countLeafs()
-                                : m_rTree.visibleItems() ) - 1;
-        float f_new = (float)iFirst / (float)indexMax;
+    else if( rEvent.getAsString().find( "drag:leave" ) != string::npos )
+    {
+        m_itOver = m_rTree.end();
+        needRefresh = true;
+    }
 
-        m_dontMove = true;
-        m_rTree.getPositionVar().set( 1.0 - f_new );
-        m_dontMove = false;
+    if( needShow )
+    {
+        if( toShow == m_rTree.end() ||
+            !ensureVisible( toShow ) )
+            needRefresh = true;
+    }
+    if( needRefresh )
+    {
+        setSliderFromFirst();
+
+        makeImage();
+        notifyLayout();
     }
 }
 
@@ -691,45 +563,6 @@ void CtrlTree::draw( OSGraphics &rImage, int xDest, int yDest, int w, int h)
                       inter.x, inter.y, inter.width, inter.height );
 }
 
-bool CtrlTree::ensureVisible( VarTree::Iterator item )
-{
-    m_rTree.ensureExpanded( item );
-
-    int firstPosIndex = m_rTree.getRank( m_firstPos, m_flat) - 1;
-    int focusItemIndex = m_rTree.getRank( item, m_flat) - 1;
-
-    if( focusItemIndex < firstPosIndex ||
-        focusItemIndex > firstPosIndex + maxItems() - 1 )
-    {
-        // Scroll to have the wanted stream visible
-        VarPercent &rVarPos = m_rTree.getPositionVar();
-        int indexMax = ( m_flat ? m_rTree.countLeafs()
-                                : m_rTree.visibleItems() ) - 1;
-        rVarPos.set( 1.0 - (double)focusItemIndex / (double)indexMax );
-        return true;
-    }
-    return false;
-}
-
-void CtrlTree::autoScroll()
-{
-    // Find the current playing stream
-    VarTree::Iterator it;
-
-    for( it = m_flat ? m_rTree.firstLeaf() : m_rTree.begin();
-         it != m_rTree.end();
-         it = m_flat ? m_rTree.getNextLeaf( it )
-                     : m_rTree.getNextItem( it ) )
-    {
-        if( it->isPlaying() )
-        {
-           ensureVisible( it );
-           break;
-        }
-    }
-}
-
-
 void CtrlTree::makeImage()
 {
     stats_TimerStart( getIntf(), "[Skins] Playlist image",
@@ -752,7 +585,7 @@ void CtrlTree::makeImage()
     OSFactory *pOsFactory = OSFactory::instance( getIntf() );
     m_pImage = pOsFactory->createOSGraphics( width, height );
 
-    VarTree::Iterator it = m_firstPos;
+    Iterator it = m_firstPos;
 
     if( m_pBgBitmap )
     {
@@ -767,30 +600,23 @@ void CtrlTree::makeImage()
         }
         m_pImage->drawBitmap( *m_pScaledBitmap, 0, 0 );
 
-        for( int yPos = 0; yPos < height; yPos += i_itemHeight )
+        for( int yPos = 0;
+             yPos < height && it != m_rTree.end();
+             yPos += i_itemHeight, ++it )
         {
-            if( it != m_rTree.end() )
+            if( it->isSelected() )
             {
-                if( it->isSelected() )
-                {
-                    int rectHeight = __MIN( i_itemHeight, height - yPos );
-                    m_pImage->fillRect( 0, yPos, width, rectHeight,
-                                        m_selColor );
-                }
-                do
-                {
-                    it = m_flat ? m_rTree.getNextLeaf( it )
-                                : m_rTree.getNextVisibleItem( it );
-                } while( it != m_rTree.end() && it->isDeleted() );
+                int rectHeight = __MIN( i_itemHeight, height - yPos );
+                m_pImage->fillRect( 0, yPos, width, rectHeight, m_selColor );
             }
         }
     }
     else
     {
-        // FIXME (TRYME)
         // Fill background with background color
         uint32_t bgColor = m_bgColor1;
         m_pImage->fillRect( 0, 0, width, height, bgColor );
+        // Overwrite with alternate colors (bgColor1, bgColor2)
         for( int yPos = 0; yPos < height; yPos += i_itemHeight )
         {
             int rectHeight = __MIN( i_itemHeight, height - yPos );
@@ -800,11 +626,7 @@ void CtrlTree::makeImage()
             {
                 uint32_t color = ( it->isSelected() ? m_selColor : bgColor );
                 m_pImage->fillRect( 0, yPos, width, rectHeight, color );
-                do
-                {
-                    it = m_flat ? m_rTree.getNextLeaf( it )
-                                : m_rTree.getNextVisibleItem( it );
-                } while( it != m_rTree.end() && it->isDeleted() );
+                ++it;
             }
             bgColor = ( bgColor == m_bgColor1 ? m_bgColor2 : m_bgColor1 );
         }
@@ -812,26 +634,25 @@ void CtrlTree::makeImage()
 
     int bitmapWidth = itemImageWidth();
 
-    int yPos = 0;
     it = m_firstPos;
-    while( it != m_rTree.end() && yPos < height )
+    for( int yPos = 0; yPos < height && it != m_rTree.end(); ++it )
     {
         const GenericBitmap *m_pCurBitmap;
         UString *pStr = it->getString();
-        uint32_t color = ( it->isPlaying() ? m_playColor : m_fgColor );
-
-        // Draw the text
         if( pStr != NULL )
         {
+            uint32_t color = it->isPlaying() ? m_playColor : m_fgColor;
             int depth = m_flat ? 1 : it->depth();
-            GenericBitmap *pText = m_rFont.drawString( *pStr, color, width - bitmapWidth * depth );
+            GenericBitmap *pText =
+                m_rFont.drawString( *pStr, color, width-bitmapWidth*depth );
             if( !pText )
             {
                 stats_TimerStop( getIntf(), STATS_TIMER_SKINS_PLAYTREE_IMAGE );
                 return;
             }
             if( it->size() )
-                m_pCurBitmap = it->isExpanded() ? m_pOpenBitmap : m_pClosedBitmap;
+                m_pCurBitmap =
+                    it->isExpanded() ? m_pOpenBitmap : m_pClosedBitmap;
             else
                 m_pCurBitmap = m_pItemBitmap;
 
@@ -844,6 +665,7 @@ void CtrlTree::makeImage()
                     delete pText;
                     break;
                 }
+                // Draw the icon in front of the text
                 m_pImage->drawBitmap( *m_pCurBitmap, 0, 0,
                                       bitmapWidth * (depth - 1 ), yPos2,
                                       m_pCurBitmap->getWidth(),
@@ -858,41 +680,141 @@ void CtrlTree::makeImage()
                 yPos = 0;
             }
             int lineHeight = __MIN( pText->getHeight() - ySrc, height - yPos );
+            // Draw the text
             m_pImage->drawBitmap( *pText, 0, ySrc, bitmapWidth * depth, yPos,
                                   pText->getWidth(),
                                   lineHeight, true );
             yPos += (pText->getHeight() - ySrc );
+
+            if( it == m_itOver )
+            {
+                // Draw the underline bar below the text for drag&drop
+                m_pImage->fillRect(
+                    bitmapWidth * (depth - 1 ), yPos - 2,
+                    bitmapWidth + pText->getWidth(), __MAX( lineHeight/5, 3 ),
+                    m_selColor );
+            }
             delete pText;
         }
-        do
-        {
-            it = m_flat ? m_rTree.getNextLeaf( it )
-                : m_rTree.getNextVisibleItem( it );
-        } while( it != m_rTree.end() && it->isDeleted() );
     }
     stats_TimerStop( getIntf(), STATS_TIMER_SKINS_PLAYTREE_IMAGE );
 }
 
-VarTree::Iterator CtrlTree::findItemAtPos( int pos )
+CtrlTree::Iterator CtrlTree::findItemAtPos( int pos )
 {
     // The first item is m_firstPos.
     // We decrement pos as we try the other items, until pos == 0.
-    VarTree::Iterator it;
-    for( it = m_firstPos; it != m_rTree.end() && pos != 0;
-         it = m_flat ? m_rTree.getNextLeaf( it )
-                     : m_rTree.getNextVisibleItem( it ) )
+    Iterator it = m_firstPos;
+    for( ; it != m_rTree.end() && pos != 0; ++it, pos-- );
+
+    return it;
+}
+
+CtrlTree::Iterator CtrlTree::getFirstFromSlider()
+{
+    // a simple (int)(...) causes rounding errors !
+#ifdef _MSC_VER
+#       define lrint (int)
+#endif
+    VarPercent &rVarPos = m_rTree.getPositionVar();
+    double percentage = rVarPos.get();
+
+    int excessItems = m_flat ? (m_rTree.countLeafs() - (int)m_capacity)
+                             : (m_rTree.visibleItems() - (int)m_capacity);
+
+    int index = (excessItems > 0 ) ?
+        lrint( (1.0 - percentage)*(double)excessItems ) :
+        0;
+
+    Iterator it_first = m_rTree.getItem( index );
+
+    if( m_lastClicked == m_rTree.end() )
     {
-        pos--;
+        m_lastClicked = it_first;
+        if( m_lastClicked != m_rTree.end() )
+            m_lastClicked->setSelected( true );
     }
 
-    return it;
+    return it_first;
 }
 
-bool CtrlTree::isItemVisible( int id )
+void CtrlTree::setScrollStep()
 {
-    VarTree::Iterator it = m_rTree.findById( id );
+    VarPercent &rVarPos = m_rTree.getPositionVar();
 
-    int rank1 = m_rTree.getRank( m_firstPos, m_flat );
-    int rank2 = m_rTree.getRank( it, m_flat );
-    return ( rank2 >= rank1 && rank2 <= rank1 + maxItems() -1 );
+    int excessItems = m_flat ? (m_rTree.countLeafs() - (int)m_capacity)
+                             : (m_rTree.visibleItems() - (int)m_capacity);
+
+    if( excessItems > 0 )
+        rVarPos.setStep( (float)1 / excessItems );
+    else
+        rVarPos.setStep( 1.0 );
+}
+
+void CtrlTree::setSliderFromFirst()
+{
+    VarPercent &rVarPos = m_rTree.getPositionVar();
+
+    int excessItems = m_flat ? (m_rTree.countLeafs() - (int)m_capacity)
+                             : (m_rTree.visibleItems() - (int)m_capacity);
+
+    int index = m_rTree.getIndex( m_firstPos );
+    if( excessItems > 0 )
+    {
+        rVarPos.set( 1.0 - (float)index/(float)excessItems );
+        rVarPos.setStep( 1.0 / excessItems );
+    }
+    else
+    {
+        rVarPos.set( 1.0 );
+        rVarPos.setStep( 1.0 );
+    }
+}
+
+bool CtrlTree::isItemVisible( const Iterator& it_ref )
+{
+    if( it_ref == m_rTree.end() )
+        return false;
+
+    Iterator it = m_firstPos;
+    if( it == m_rTree.end() )
+        return true;
+
+    // Ensure a partially visible last item is taken into account
+    int max = (int)m_capacity;
+    if( (float)max < m_capacity )
+        max++;
+
+    for( int i = 0; i < max && it != m_rTree.end(); ++it, i++ )
+    {
+        if( it == it_ref )
+            return true;
+    }
+    return false;
+}
+
+bool CtrlTree::ensureVisible( const Iterator& item )
+{
+    Iterator it = m_firstPos;
+    int max = (int)m_capacity;
+    for( int i = 0; i < max && it != m_rTree.end(); ++it, i++ )
+    {
+        if( it == item )
+            return false;
+    }
+
+    m_rTree.setSliderFromItem( item );
+    return true;
+}
+
+CtrlTree::Iterator CtrlTree::getNearestItem( const Iterator& item )
+{
+    // return the previous item if it exists
+    Iterator newItem = item;
+    if( --newItem != m_rTree.end() && newItem != item )
+        return newItem;
+
+    // return the next item if no previous item found
+    newItem = item;
+    return ++newItem;
 }
index 28a646e3c66a3af8036cb58b22871517f8c7c8c4..cc5972c8a0b2e34ad017907c6461faff0120bbe6 100644 (file)
@@ -34,10 +34,11 @@ class GenericFont;
 class GenericBitmap;
 
 /// Class for control tree
-class CtrlTree: public CtrlGeneric, public Observer<VarTree, tree_update>,
-    public Observer<VarPercent>
+class CtrlTree: public CtrlGeneric, public Observer<VarTree, tree_update>
 {
 public:
+    typedef VarTree::IteratorVisible Iterator;
+
     CtrlTree( intf_thread_t *pIntf,
               VarTree &rTree,
               const GenericFont &rFont,
@@ -76,7 +77,7 @@ public:
     /// Make sure an item is visible
     /// \param item an iterator to a tree item
     /// \return true if it changed the position
-    bool ensureVisible( VarTree::Iterator item );
+    bool ensureVisible( const Iterator& it );
 
 private:
     /// Tree associated to the control
@@ -95,6 +96,9 @@ private:
     const GenericBitmap *m_pClosedBitmap;
     /// scaled bitmap
     GenericBitmap *m_pScaledBitmap;
+    /// Image of the control
+    OSGraphics *m_pImage;
+
     /// Color of normal test
     uint32_t m_fgColor;
     /// Color of the playing item
@@ -103,31 +107,30 @@ private:
     uint32_t m_bgColor1, m_bgColor2;
     /// Background of selected items
     uint32_t m_selColor;
-    /// Pointer on the last selected item in the tree
-    VarTree *m_pLastSelected;
-    /// Image of the control
-    OSGraphics *m_pImage;
-    /// First item in the visible area
-    VarTree::Iterator m_firstPos;
 
-    /// Don't move if the position variable is updated
-    bool m_dontMove;
+    /// First item in the visible area
+    Iterator m_firstPos;
+    /// Pointer on the last clicked item in the tree
+    Iterator m_lastClicked;
+    ///
+    Iterator m_itOver;
 
     /// Do we want to "flaten" the tree ?
     bool m_flat;
+    /// Number of visible lines
+    float m_capacity;
+    /// flag for item deletion
+    bool m_bRefreshOnDelete;
 
     /// Method called when the tree variable is modified
-    virtual void onUpdate( Subject<VarTree, tree_update> &rTree ,
+    virtual void onUpdate( Subject<VarTree, tree_update> &rTree,
                            tree_update *);
 
-    // Method called when the position variable of the tree is modified
-    virtual void onUpdate( Subject<VarPercent> &rPercent , void *);
-
     /// Called when the position is set
     virtual void onPositionChange();
 
     /// Compute the number of lines that can be displayed
-    int maxItems();
+    float maxItems();
 
     /// Compute the item's height (depends on fonts and images used)
     int itemHeight();
@@ -135,21 +138,21 @@ private:
     /// Compute the width of an item's bitmap
     int itemImageWidth();
 
-    /// Check if the tree must be scrolled
-    void autoScroll();
-
     /// Draw the image of the control
     void makeImage();
 
     /// Return the n'th displayed item (starting at position 0)
-    /**
-     *  Return m_rTree.end() if such an item cannot be found (n < 0, or
-     *  n too big)
-     */
-    VarTree::Iterator findItemAtPos( int n );
-
-    /// check if id is within the visible control
-    bool isItemVisible( int id );
+    Iterator findItemAtPos( int n );
+
+    /// return the nearest item
+    Iterator getNearestItem( const Iterator& it );
+
+    /// return whether the item is visible or not
+    bool isItemVisible( const Iterator& it );
+
+    void setSliderFromFirst();
+    Iterator getFirstFromSlider();
+    void setScrollStep();
 };
 
 #endif
index 829a12b1fd61887439e6de039b89dde9df2d8d4b..abf656922c298c6b8c0c8a23b39b591163f0ef72 100644 (file)
@@ -35,7 +35,8 @@ class VarPercent;
 class VarPercent: public Variable, public Subject<VarPercent>
 {
 public:
-    VarPercent( intf_thread_t *pIntf ): Variable( pIntf ), m_value( 0 ) { }
+    VarPercent( intf_thread_t *pIntf ) :
+        Variable( pIntf ), m_value( 0 ), m_step( .05f ) {}
     virtual ~VarPercent() { }
 
     /// Get the variable type
@@ -46,13 +47,19 @@ public:
     virtual float get() const { return m_value; }
 
     /// Get the variable preferred step
-    virtual float getStep() const { return .05f; }
+    virtual float getStep() const { return m_step; }
+    virtual void setStep( float val ) { m_step = val; }
+
+    /// Increment or decrement variable
+    void increment( int num ) { return set( m_value + num * m_step ); }
 
 private:
     /// Variable type
     static const string m_type;
     /// Percent value
     float m_value;
+    /// preferred step (for scrolling)
+    float m_step;
 };
 
 #endif
index f2c0f87338e268c6ddb0dbd993a1f3d5521dd414..bf476858331243ba1166a38d52dbec4f2d86ef6d 100644 (file)
@@ -6,6 +6,7 @@
  *
  * Authors: Antoine Cellerier <dionoea@videolan.org>
  *          Clément Stenac <zorglub@videolan.org>
+ *          Erwan   Tulou  <erwan10@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
  *****************************************************************************/
 
 #include "var_tree.hpp"
-
+#include <math.h>
 
 const string VarTree::m_type = "tree";
 
 VarTree::VarTree( intf_thread_t *pIntf )
-    : Variable( pIntf ), m_pParent( NULL ), m_id( 0 ), m_pData( NULL ),
-      m_readonly( false ), m_selected( false ), m_playing( false ),
-      m_expanded( false ), m_deleted( false )
+    : Variable( pIntf ), m_pParent( NULL ), m_id( 0 ),
+      m_readonly( false ), m_selected( false ),
+      m_playing( false ), m_expanded( false ),
+      m_flat( false ), m_dontMove( false )
 {
     // Create the position variable
     m_cPosition = VariablePtr( new VarPercent( pIntf ) );
     getPositionVar().set( 1.0 );
+
+    getPositionVar().addObserver( this );
 }
 
 VarTree::VarTree( intf_thread_t *pIntf, VarTree *pParent, int id,
                   const UStringPtr &rcString, bool selected, bool playing,
-                  bool expanded, bool readonly, void *pData )
+                  bool expanded, bool readonly )
     : Variable( pIntf ), m_pParent( pParent ),
-      m_id( id ), m_pData( pData ), m_cString( rcString ),
-      m_readonly( readonly ), m_selected( selected ), m_playing( playing ),
-      m_expanded( expanded ), m_deleted( false )
+      m_id( id ), m_cString( rcString ),
+      m_readonly( readonly ), m_selected( selected ),
+      m_playing( playing ), m_expanded( expanded ),
+      m_flat( false ), m_dontMove( false )
 {
     // Create the position variable
     m_cPosition = VariablePtr( new VarPercent( pIntf ) );
     getPositionVar().set( 1.0 );
+
+    getPositionVar().addObserver( this );
+}
+
+VarTree::VarTree( const VarTree& v )
+    : Variable( v.getIntf() ), m_pParent( v.m_pParent ),
+      m_id( v.m_id ), m_cString( v.m_cString ),
+      m_readonly( v.m_readonly ), m_selected( v.m_selected ),
+      m_playing( v.m_playing ), m_expanded( v.m_expanded ),
+      m_flat( false ), m_dontMove( false )
+{
+    // Create the position variable
+    m_cPosition = VariablePtr( new VarPercent( getIntf() ) );
+    getPositionVar().set( 1.0 );
+
+    getPositionVar().addObserver( this );
 }
 
 VarTree::~VarTree()
 {
-/// \todo check that children are deleted
+    getPositionVar().delObserver( this );
 }
 
-void VarTree::add( int id, const UStringPtr &rcString, bool selected,
-                   bool playing, bool expanded, bool readonly, void *pData )
+VarTree::Iterator VarTree::add( int id, const UStringPtr &rcString,
+                  bool selected, bool playing, bool expanded, bool readonly,
+                  int pos )
 {
-    m_children.push_back( VarTree( getIntf(), this, id, rcString, selected,
-                                   playing, expanded, readonly,
-                                   pData ) );
+    Iterator it;
+    if( pos == -1 )
+    {
+        it = m_children.end();
+    }
+    else
+    {
+        it = m_children.begin();
+        for( int i = 0; i < pos && it != m_children.end(); ++it, i++ );
+    }
+
+    return m_children.insert( it,
+                              VarTree( getIntf(), this, id, rcString,
+                                       selected, playing,
+                                       expanded, readonly ) );
 }
 
 void VarTree::delSelected()
 {
-    Iterator it = begin();
-    while( it != end() )
+    for( Iterator it = m_children.begin(); it != m_children.end(); )
     {
-        //dig down the tree
-        if( size() ) it->delSelected();
-        //stay on some level
         if( it->m_selected )
         {
             Iterator oldIt = it;
             ++it;
             m_children.erase( oldIt );
         }
-        else
-        {
-            ++it;
-        }
     }
 }
 
@@ -89,49 +115,32 @@ void VarTree::clear()
     m_children.clear();
 }
 
-VarTree::Iterator VarTree::operator[]( int n )
-{
-    Iterator it;
-    int i;
-    for( it = begin(), i = 0;
-         i < n && it != end();
-         ++it, i++ );
-    return it;
-}
-
-VarTree::ConstIterator VarTree::operator[]( int n ) const
+VarTree::Iterator VarTree::getNextSiblingOrUncle()
 {
-    ConstIterator it;
-    int i;
-    for( it = begin(), i = 0;
-         i < n && it != end();
-         ++it, i++ );
-    return it;
+    VarTree *p_parent = parent();
+    if( p_parent )
+    {
+        Iterator it = ++(getSelf());
+        if( it != p_parent->m_children.end() )
+            return it;
+        else
+            return next_uncle();
+    }
+    return root()->m_children.end();
 }
 
-VarTree::Iterator VarTree::getNextSiblingOrUncle()
+VarTree::Iterator VarTree::getPrevSiblingOrUncle()
 {
     VarTree *p_parent = parent();
     if( p_parent )
     {
-        Iterator it = p_parent->begin();
-        while( it != p_parent->end() && &(*it) != this ) ++it;
-        if( it != p_parent->end() )
-        {
-            Iterator current = it;
-            ++it;
-            if( it != p_parent->end() )
-                return it;
-            else
-                return current->next_uncle();
-        }
+        Iterator it = getSelf();
+        if( it != p_parent->m_children.begin() )
+            return --it;
         else
-        {
-            msg_Err( getIntf(), "should never occur" );
-            return end();
-        }
+            return prev_uncle();
     }
-    return end();
+    return root()->m_children.end();
 }
 
 /* find iterator to next ancestor
@@ -139,86 +148,60 @@ VarTree::Iterator VarTree::getNextSiblingOrUncle()
 VarTree::Iterator VarTree::next_uncle()
 {
     VarTree *p_parent = parent();
-    if( p_parent != NULL )
+    if( p_parent )
     {
         VarTree *p_grandparent = p_parent->parent();
-        while( p_grandparent != NULL )
+        while( p_grandparent )
         {
-            Iterator it = p_grandparent->begin();
-            while( it != p_grandparent->end() && &(*it) != p_parent ) ++it;
-            if( it != p_grandparent->end() )
-            {
-                ++it;
-                if( it != p_grandparent->end() )
-                {
-                    return it;
-                }
-            }
-            if( p_grandparent->parent() )
-            {
-                p_parent = p_grandparent;
-                p_grandparent = p_parent->parent();
-            }
-            else
-                p_grandparent = NULL;
+            Iterator it = ++(p_parent->getSelf());
+            if( it != p_grandparent->m_children.end() )
+                return it;
+            p_parent = p_grandparent;
+            p_grandparent = p_parent->parent();
         }
     }
 
     /* if we didn't return before, it means that we've reached the end */
-    return root()->end();
+    return root()->m_children.end();
 }
 
 VarTree::Iterator VarTree::prev_uncle()
 {
     VarTree *p_parent = parent();
-    if( p_parent != NULL )
+    if( p_parent )
     {
         VarTree *p_grandparent = p_parent->parent();
-        while( p_grandparent != NULL )
+        while( p_grandparent )
         {
-            Iterator it = p_grandparent->end();
-            while( it != p_grandparent->begin() && &(*it) != p_parent ) --it;
-            if( it != p_grandparent->begin() )
-            {
-                --it;
-                if( it != p_grandparent->begin() )
-                {
-                    return it;
-                }
-            }
-            if( p_grandparent->parent() )
-            {
-                p_parent = p_grandparent;
-                p_grandparent = p_parent->parent();
-            }
-            else
-                p_grandparent = NULL;
+            Iterator it = p_parent->getSelf();
+            if( it != p_grandparent->m_children.begin() )
+                return --it;
+            p_parent = p_grandparent;
+            p_grandparent = p_parent->parent();
         }
     }
 
     /* if we didn't return before, it means that we've reached the end */
-    return root()->begin();
+    return root()->m_children.end();
 }
 
 int VarTree::visibleItems()
 {
     int i_count = size();
-    Iterator it = begin();
-    while( it != end() )
+    for( Iterator it = m_children.begin(); it != m_children.end(); ++it )
     {
         if( it->m_expanded )
         {
             i_count += it->visibleItems();
         }
-        ++it;
     }
     return i_count;
 }
 
 VarTree::Iterator VarTree::getVisibleItem( int n )
 {
-    Iterator it = begin();
-    while( it != end() )
+    Iterator it = m_children.begin();
+    while( it != m_children.end() )
     {
         n--;
         if( n <= 0 )
@@ -232,13 +215,13 @@ VarTree::Iterator VarTree::getVisibleItem( int n )
         }
         ++it;
     }
-    return end();
+    return m_children.end();
 }
 
 VarTree::Iterator VarTree::getLeaf( int n )
 {
-    Iterator it = begin();
-    while( it != end() )
+    Iterator it = m_children.begin();
+    while( it != m_children.end() )
     {
         if( it->size() )
         {
@@ -255,21 +238,21 @@ VarTree::Iterator VarTree::getLeaf( int n )
         }
         ++it;
     }
-    return end();
+    return m_children.end();
 }
 
 VarTree::Iterator VarTree::getNextVisibleItem( Iterator it )
 {
     if( it->m_expanded && it->size() )
     {
-        it = it->begin();
+        it = it->m_children.begin();
     }
     else
     {
         Iterator it_old = it;
         ++it;
         // Was 'it' the last brother? If so, look for uncles
-        if( it_old->parent() && it_old->parent()->end() == it )
+        if( it_old->parent() && it_old->parent()->m_children.end() == it )
         {
             it = it_old->next_uncle();
         }
@@ -279,23 +262,30 @@ VarTree::Iterator VarTree::getNextVisibleItem( Iterator it )
 
 VarTree::Iterator VarTree::getPrevVisibleItem( Iterator it )
 {
-    Iterator it_old = it;
-    if( it == root()->begin() || it == ++(root()->begin()) ) return it;
+    if( it == root()->m_children.begin() )
+        return it;
+
+    if( it == root()->m_children.end() )
+    {
+        --it;
+        while( it->size() && it->m_expanded )
+            it = --(it->m_children.end());
+        return it;
+    }
 
     /* Was it the first child of its parent ? */
-    if( it->parent() && it == it->parent()->begin() )
+    VarTree *p_parent = it->parent();
+    if( it == p_parent->m_children.begin() )
     {
-        /* Yes, get previous uncle */
-        it = it_old->prev_uncle();
+        /* Yes, get its parent's it */
+        it = p_parent->getSelf();
     }
     else
-        --it;
-
-    /* We have found an expanded uncle, take its last child */
-    while( it != root()->begin() && it->size() && it->m_expanded )
     {
-            it = it->end();
-            --it;
+        --it;
+        /* We have found an older brother, take its last visible child */
+        while( it->size() && it->m_expanded )
+            it = --(it->m_children.end());
     }
     return it;
 }
@@ -304,14 +294,14 @@ VarTree::Iterator VarTree::getNextItem( Iterator it )
 {
     if( it->size() )
     {
-        it = it->begin();
+        it = it->m_children.begin();
     }
     else
     {
         Iterator it_old = it;
         ++it;
         // Was 'it' the last brother? If so, look for uncles
-        if( it_old->parent() && it_old->parent()->end() == it )
+        if( it_old->parent() && it_old->parent()->m_children.end() == it )
         {
             it = it_old->next_uncle();
         }
@@ -321,23 +311,29 @@ VarTree::Iterator VarTree::getNextItem( Iterator it )
 
 VarTree::Iterator VarTree::getPrevItem( Iterator it )
 {
-    Iterator it_old = it;
-    if( it == root()->begin() || it == ++(root()->begin()) ) return it;
+    if( it == root()->m_children.begin() )
+        return it;
 
+    if( it == root()->m_children.end() )
+    {
+        --it;
+        while( it->size() )
+            it = --(it->m_children.end());
+        return it;
+    }
     /* Was it the first child of its parent ? */
-    if( it->parent() && it == it->parent()->begin() )
+    VarTree *p_parent = it->parent();
+    if( it == p_parent->m_children.begin() )
     {
-        /* Yes, get previous uncle */
-        it = it_old->prev_uncle();
+        /* Yes, get its parent's it */
+        it = p_parent->getSelf();
     }
     else
-        --it;
-
-    /* We have found an expanded uncle, take its last child */
-    while( it != root()->begin() && it->size() )
     {
-            it = it->end();
-            --it;
+        --it;
+        /* We have found an older brother, take its last child */
+        while( it->size() )
+            it = --(it->m_children.end());
     }
     return it;
 }
@@ -348,42 +344,35 @@ VarTree::Iterator VarTree::getNextLeaf( Iterator it )
     {
         it = getNextItem( it );
     }
-    while( it != root()->end() && it->size() );
+    while( it != root()->m_children.end() && it->size() );
     return it;
 }
 
 VarTree::Iterator VarTree::getPrevLeaf( Iterator it )
 {
-    do
-    {
-        it = getPrevItem( it );
-    }
-    while( it != root()->begin() && it->size() ); /* FIXME ? */
-    if( it == root()->begin() ) it = firstLeaf();
-    return it;
+    Iterator it_new = it->getPrevSiblingOrUncle();
+    if( it_new == root()->end() )
+        return it_new;
+    while( it_new->size() )
+        it_new = --(it_new->m_children.end());
+    return it_new;
 }
 
-VarTree::Iterator VarTree::findById( int id )
+VarTree::Iterator VarTree::getParent( Iterator it )
 {
-    for (Iterator it = begin(); it != end(); ++it )
+    if( it->parent() )
     {
-        if( it->m_id == id )
-        {
-            return it;
-        }
-        Iterator result = it->findById( id );
-        if( result != it->end() ) return result;
+        return it->parent()->getSelf();
     }
-    return end();
+    return m_children.end();
 }
 
-
 void VarTree::ensureExpanded( const Iterator& it )
 {
     /// Don't expand ourselves, only our parents
     VarTree *current = &(*it);
     current = current->parent();
-    while( current->parent() != NULL )
+    while( current->parent() )
     {
         current->m_expanded = true;
         current = current->parent();
@@ -392,47 +381,91 @@ void VarTree::ensureExpanded( const Iterator& it )
 
 int VarTree::countLeafs()
 {
-    if( size() == 0 ) return 1;
+    if( size() == 0 )
+        return 1;
 
     int i_count = 0;
-    Iterator it = begin();
-    while( it != end() )
+    for( Iterator it = m_children.begin(); it != m_children.end(); ++it )
     {
         i_count += it->countLeafs();
-        ++it;
     }
     return i_count;
 }
 
 VarTree::Iterator VarTree::firstLeaf()
 {
-    Iterator b = root()->begin();
+    Iterator b = root()->m_children.begin();
     if( b->size() ) return getNextLeaf( b );
     return b;
 }
 
-void VarTree::cascadeDelete()
-{
-    m_deleted = true;
-    for( Iterator it = begin(); it != end(); ++it )
-    {
-        it->cascadeDelete();
-    }
-}
-
-int VarTree::getRank( const Iterator& item, bool flat )
+int VarTree::getIndex( const Iterator& item )
 {
-    int index = 1;
+    int index = 0;
     Iterator it;
-    for( it = flat ? firstLeaf() : begin();
-         it != end();
-         it = flat ? getNextLeaf( it ) : getNextVisibleItem( it ) )
+    for( it = m_flat ? firstLeaf() : m_children.begin();
+         it != m_children.end();
+         it = m_flat ? getNextLeaf( it ) : getNextVisibleItem( it ) )
     {
-        if( it->isDeleted() )
-            continue;
         if( it == item )
             break;
         index++;
     }
     return (it == item) ? index : -1;
 }
+
+VarTree::Iterator VarTree::getItemFromSlider()
+{
+    // a simple (int)(...) causes rounding errors !
+#ifdef _MSC_VER
+#       define lrint (int)
+#endif
+    VarPercent &rVarPos = getPositionVar();
+    double percentage = rVarPos.get();
+
+    int indexMax = m_flat ? (countLeafs() - 1)
+                          : (visibleItems() - 1);
+
+    int index = lrint( (1.0 - percentage)*(double)indexMax );
+
+    Iterator it_first = m_flat ? getLeaf( index + 1 )
+                               : getVisibleItem( index + 1 );
+    return it_first;
+}
+
+void VarTree::setSliderFromItem( const Iterator& it )
+{
+    VarPercent &rVarPos = getPositionVar();
+
+    int indexMax = m_flat ? (countLeafs() - 1)
+                          : (visibleItems() - 1);
+
+    int index = getIndex( it );
+    double percentage = (1.0 - (double)index/(double)indexMax);
+
+    m_dontMove = true;
+    rVarPos.set( (float)percentage );
+    m_dontMove = false;
+}
+
+void VarTree::onUpdate( Subject<VarPercent> &rPercent, void* arg )
+{
+    (void)rPercent; (void)arg;
+    onUpdateSlider();
+}
+
+void VarTree::unselectTree()
+{
+    m_selected = false;
+    for( Iterator it = m_children.begin(); it != m_children.end(); ++it )
+        it->unselectTree();
+}
+
+VarTree::IteratorVisible VarTree::getItem( int index )
+{
+   Iterator it =
+        m_flat ? getLeaf( index + 1 )
+               : getVisibleItem( index + 1 );
+
+   return IteratorVisible( it, this );
+}
index 5934c4fd1986daaf747cb3adf12b9bfa551bad76..7e2b689fa9b8f2c834392284fec2341467152738 100644 (file)
 #define VAR_TREE_HPP
 
 #include <list>
+#include <assert.h>
 
 #include "variable.hpp"
 #include "observer.hpp"
 #include "ustring.hpp"
 #include "var_percent.hpp"
 
-/// Description of an update to the tree
-typedef struct tree_update
-{
-    enum type_t
-    {
-        UpdateItem,
-        AppendItem,
-        DeleteItem,
-        ResetAll,
-    };
-
-    enum type_t type;
-    int i_id;
-    bool b_active_item;
-
-} tree_update;
+class VarTree;
+struct tree_update;
 
 /// Tree variable
-class VarTree: public Variable, public Subject<VarTree, tree_update>
+class VarTree: public Variable,
+               public Subject<VarTree, tree_update>,
+               public Observer<VarPercent>
 {
 public:
     VarTree( intf_thread_t *pIntf );
 
     VarTree( intf_thread_t *pIntf, VarTree *pParent, int id,
              const UStringPtr &rcString, bool selected, bool playing,
-             bool expanded, bool readonly, void *pData );
+             bool expanded, bool readonly );
+    VarTree( const VarTree& );
 
     virtual ~VarTree();
 
+    /// Iterators
+    typedef list<VarTree>::iterator Iterator;
+    typedef list<VarTree>::const_iterator ConstIterator;
+
     /// Get the variable type
     virtual const string &getType() const { return m_type; }
 
     /// Add a pointer on string in the children's list
-    virtual void add( int id, const UStringPtr &rcString, bool selected,
-                      bool playing, bool expanded, bool readonly, void *pData );
+    virtual Iterator add( int id, const UStringPtr &rcString, bool selected,
+                    bool playing, bool expanded, bool readonly, int pos = -1 );
 
     /// Remove the selected item from the children's list
     virtual void delSelected();
@@ -75,7 +69,6 @@ public:
     virtual void clear();
 
     inline int  getId() { return m_id; }
-    inline void *getData() { return m_pData; }
     inline UString* getString() {return (UString*)m_cString.get(); }
     inline void setString( UStringPtr val ) { m_cString = val; }
 
@@ -83,43 +76,101 @@ public:
     inline bool isSelected() { return m_selected; };
     inline bool isPlaying() { return m_playing; };
     inline bool isExpanded() { return m_expanded; };
-    inline bool isDeleted() { return m_deleted; };
+    inline bool isFlat() { return m_flat; };
 
     inline void setSelected( bool val ) { m_selected = val; }
     inline void setPlaying( bool val ) { m_playing = val; }
     inline void setExpanded( bool val ) { m_expanded = val; }
-    inline void setDeleted( bool val ) { m_deleted = val; }
+    inline void setFlat( bool val ) { m_flat = val; }
 
     inline void toggleSelected() { m_selected = !m_selected; }
-    inline void toggleExpanded() { m_expanded = !m_expanded; }
+    inline void toggleExpanded() { setExpanded( !m_expanded ); }
 
     /// Get the number of children
     int size() const { return m_children.size(); }
 
-    /// Iterators
-    typedef list<VarTree>::iterator Iterator;
-    typedef list<VarTree>::const_iterator ConstIterator;
+    /// iterator over visible items
+    class IteratorVisible : public Iterator
+    {
+        public:
+        IteratorVisible( const VarTree::Iterator& it, VarTree* pRootTree )
+            : VarTree::Iterator( it ), m_pRootTree( pRootTree ) {}
+
+        IteratorVisible& operator++()
+        {
+            Iterator& it = *this;
+            assert( it != end() );
+            it = isFlat() ? m_pRootTree->getNextLeaf( it ) :
+                            m_pRootTree->getNextVisibleItem( it );
+            return *this;
+        }
+
+        IteratorVisible& operator--()
+        {
+            Iterator& it = *this;
+            it = isFlat() ? m_pRootTree->getPrevLeaf( it ) :
+                            m_pRootTree->getPrevVisibleItem( it );
+            return *this;
+        }
+
+        IteratorVisible getParent()
+        {
+            IteratorVisible& it = *this;
+            if( it->parent() && it->parent() != m_pRootTree )
+            {
+                return IteratorVisible( it->parent()->getSelf(), m_pRootTree );
+            }
+            return end();
+        }
+
+        private:
+        inline IteratorVisible begin() { return m_pRootTree->begin(); }
+        inline IteratorVisible end()   { return m_pRootTree->end(); }
+        inline bool isFlat()           { return m_pRootTree->m_flat; }
+        VarTree* m_pRootTree;
+    };
 
-    /// Begining of the children's list
-    Iterator begin() { return m_children.begin(); }
-    ConstIterator begin() const { return m_children.begin(); }
+    /// Beginning of the children's list
+    IteratorVisible begin()
+    {
+        return IteratorVisible(
+               m_flat ? firstLeaf() : m_children.begin(), this );
+    }
 
     /// End of children's list
-    Iterator end() { return m_children.end(); }
-    ConstIterator end() const { return m_children.end(); }
+    IteratorVisible end() { return IteratorVisible( m_children.end(), this ); }
 
     /// Back of children's list
     VarTree &back() { return m_children.back(); }
 
-    /// Return an iterator on the n'th element of the children's list
-    Iterator operator[]( int n );
-    ConstIterator operator[]( int n ) const;
-
     /// Parent node
     VarTree *parent() { return m_pParent; }
 
     /// Get next sibling
     Iterator getNextSiblingOrUncle();
+    Iterator getPrevSiblingOrUncle();
+
+    Iterator getSelf()
+    {
+        assert( m_pParent );
+        Iterator it = m_pParent->m_children.begin();
+        for( ; &*it != this && it != m_pParent->m_children.end(); ++it );
+        assert( it != m_pParent->m_children.end() );
+        return it;
+    }
+
+    int getIndex()
+    {
+        if( m_pParent )
+        {
+            int i_pos = 0;
+            for( Iterator it = m_pParent->m_children.begin();
+                 it != m_pParent->m_children.end(); ++it, i_pos++ )
+                if( &(*it) == this )
+                    return i_pos;
+        }
+        return -1;
+    }
 
     Iterator next_uncle();
     Iterator prev_uncle();
@@ -131,7 +182,7 @@ public:
     void removeChild( Iterator it ) { m_children.erase( it ); }
 
     /// Execute the action associated to this item
-    virtual void action( VarTree *pItem ) { (void)pItem; }
+    virtual void action( VarTree *pItem ) { VLC_UNUSED(pItem); }
 
     /// Get a reference on the position variable
     VarPercent &getPositionVar() const
@@ -171,17 +222,21 @@ public:
     /// Given an iterator to an item, return the previous leaf
     Iterator getPrevLeaf( Iterator it );
 
-    /// return rank of visible item starting from 1
-    int getRank( const Iterator& it, bool flat );
+    /// Given an iterator to an item, return the parent item
+    Iterator getParent( Iterator it );
 
-    /// Find a children node with the given id
-    Iterator findById( int id );
+    /// return index of visible item (starting from 0)
+    int getIndex( const Iterator& it );
 
     /// Ensure an item is expanded
     void ensureExpanded( const Iterator& it );
 
-    /// flag a whole subtree for deletion
-    void cascadeDelete();
+    ///
+    Iterator getItemFromSlider();
+    void setSliderFromItem( const Iterator& it );
+
+    ///
+    void onUpdate( Subject<VarPercent> &rPercent, void* arg);
 
     /// Get depth (root depth is 0)
     int depth()
@@ -193,6 +248,17 @@ public:
         return depth;
     }
 
+    virtual void onUpdateSlider() {}
+
+    void unselectTree();
+
+    VarTree::IteratorVisible getItem( int index );
+
+protected:
+
+    /// List of children
+    list<VarTree> m_children;
+
 private:
 
     /// Get root node
@@ -204,14 +270,10 @@ private:
         return parent;
     }
 
-    /// List of children
-    list<VarTree> m_children;
-
     /// Pointer to parent node
     VarTree *m_pParent;
 
     int m_id;
-    void *m_pData;
     UStringPtr m_cString;
 
     /// indicators
@@ -219,7 +281,8 @@ private:
     bool m_selected;
     bool m_playing;
     bool m_expanded;
-    bool m_deleted;
+    bool m_flat;
+    bool m_dontMove;
 
     /// Variable type
     static const string m_type;
@@ -228,4 +291,23 @@ private:
     VariablePtr m_cPosition;
 };
 
+/// Description of an update to the tree
+typedef struct tree_update
+{
+    enum type_t
+    {
+        ItemUpdated,
+        ItemInserted,
+        ItemDeleted,
+        DeletingItem,
+        ResetAll,
+        SliderChanged,
+    };
+    enum type_t type;
+    VarTree::IteratorVisible it;
+
+    tree_update( enum type_t t, VarTree::IteratorVisible item ) :
+        type( t ), it( item ) {}
+} tree_update;
+
 #endif
index 4e8ba7b38e88ab2510f3296c69f7ccc77990c23a..89d2900403eeedfd84530d98908816fea4b129cc 100644 (file)
@@ -6,6 +6,7 @@
  *
  * Authors: Antoine Cellerier <dionoea@videolan.org>
  *          Clément Stenac <zorglub@videolan.org>
+ *          Erwan Tulou    <erwan10@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
 
 #include "playtree.hpp"
 #include <vlc_playlist.h>
+#include <vlc_url.h>
 #include "../utils/ustring.hpp"
 
-Playtree::Playtree( intf_thread_t *pIntf ): VarTree( pIntf )
+Playtree::Playtree( intf_thread_t *pIntf )
+    : VarTree( pIntf ), m_pPlaylist( pIntf->p_sys->p_playlist )
 {
-    // Get the VLC playlist object
-    m_pPlaylist = pIntf->p_sys->p_playlist;
-
+    getPositionVar().addObserver( this );
     buildTree();
 }
 
 Playtree::~Playtree()
 {
+    getPositionVar().delObserver( this );
 }
 
 void Playtree::delSelected()
 {
-    Iterator it = begin();
-    playlist_Lock( getIntf()->p_sys->p_playlist );
-    for( it = begin(); it != end(); it = getNextItem( it ) )
+    for( Iterator it = m_children.begin(); it != m_children.end(); )
     {
         if( it->isSelected() && !it->isReadonly() )
         {
-            it->cascadeDelete();
-        }
-    }
-    /// \todo Do this better (handle item-deleted)
-    tree_update descr;
-    descr.type = tree_update::DeleteItem;
-    notify( &descr );
-    it = begin();
-    while( it != end() )
-    {
-        if( it->isDeleted() )
-        {
-            VarTree::Iterator it2;
-            playlist_item_t *p_item = (playlist_item_t *)(it->getData());
-            if( p_item->i_children == -1 )
-            {
-                playlist_DeleteFromInput( getIntf()->p_sys->p_playlist,
-                                          p_item->p_input, pl_Locked );
-                it2 = getNextItem( it ) ;
-            }
-            else
+            playlist_Lock( m_pPlaylist );
+
+            playlist_item_t *pItem =
+                playlist_ItemGetById( m_pPlaylist, it->getId() );
+            if( pItem )
             {
-                playlist_NodeDelete( getIntf()->p_sys->p_playlist, p_item,
-                                     true, false );
-                it2 = it->getNextSiblingOrUncle();
+                if( pItem->i_children == -1 )
+                {
+                    playlist_DeleteFromInput( m_pPlaylist, pItem->p_input,
+                                              pl_Locked );
+                }
+                else
+                {
+                    playlist_NodeDelete( m_pPlaylist, pItem, true, false );
+                }
             }
-            it->parent()->removeChild( it );
-            it = it2;
+            playlist_Unlock( m_pPlaylist );
+
+            it = it->getNextSiblingOrUncle();
         }
         else
         {
             it = getNextItem( it );
         }
     }
-    playlist_Unlock( getIntf()->p_sys->p_playlist );
 }
 
-void Playtree::action( VarTree *pItem )
+void Playtree::action( VarTree *pElem )
 {
     playlist_Lock( m_pPlaylist );
-    VarTree::Iterator it;
 
-    playlist_item_t *p_item = (playlist_item_t *)pItem->getData();
-    playlist_item_t *p_parent = p_item;
-    while( p_parent )
+    playlist_item_t *pItem =
+        playlist_ItemGetById( m_pPlaylist, pElem->getId() );
+    if( pItem )
     {
-        if( p_parent == m_pPlaylist->p_root_category )
-            break;
-        p_parent = p_parent->p_parent;
+        playlist_Control( m_pPlaylist, PLAYLIST_VIEWPLAY,
+                          pl_Locked, pItem->p_parent, pItem );
     }
 
-    if( p_parent )
-    {
-        playlist_Control( m_pPlaylist, PLAYLIST_VIEWPLAY, pl_Locked, p_parent, p_item );
-    }
     playlist_Unlock( m_pPlaylist );
 }
 
 void Playtree::onChange()
 {
     buildTree();
-    tree_update descr;
-    descr.type = tree_update::ResetAll;
+    tree_update descr( tree_update::ResetAll, end() );
     notify( &descr );
 }
 
 void Playtree::onUpdateItem( int id )
 {
     Iterator it = findById( id );
-    if( it != end() )
+    if( it != m_children.end() )
     {
         // Update the item
-        playlist_item_t* pNode = (playlist_item_t*)(it->getData());
+        playlist_Lock( m_pPlaylist );
+        playlist_item_t *pNode =
+            playlist_ItemGetById( m_pPlaylist, it->getId() );
+        if( !pNode )
+        {
+            playlist_Unlock( m_pPlaylist );
+            return;
+        }
+
         UString *pName = new UString( getIntf(), pNode->p_input->psz_name );
-        it->setString( UStringPtr( pName ) );
+        playlist_Unlock( m_pPlaylist );
 
-        tree_update descr;
-        descr.type = tree_update::UpdateItem;
-        descr.i_id = id;
-        descr.b_active_item = false;
-        notify( &descr );
+        if( *pName != *(it->getString()) )
+        {
+            it->setString( UStringPtr( pName ) );
+
+            tree_update descr(
+                tree_update::ItemUpdated, IteratorVisible( it, this ) );
+            notify( &descr );
+        }
     }
     else
     {
-        msg_Warn(getIntf(), "cannot find node with id %d", id );
+        msg_Warn( getIntf(), "cannot find node with id %d", id );
     }
 }
 
 void Playtree::onUpdateCurrent( bool b_active )
 {
-    for( VarTree::Iterator it = begin(); it != end(); it = getNextItem( it ) )
-    {
-       if( it->isPlaying() )
-       {
-           it->setPlaying( false );
-
-           tree_update descr;
-           descr.type = tree_update::UpdateItem;
-           descr.i_id = it->getId();
-           descr.b_active_item = false;
-           notify( &descr );
-           break;
-       }
-    }
-
     if( b_active )
     {
         playlist_Lock( m_pPlaylist );
@@ -169,81 +148,107 @@ void Playtree::onUpdateCurrent( bool b_active )
         }
 
         Iterator it = findById( current->i_id );
-        if( it != end() )
+        if( it != m_children.end() )
+        {
             it->setPlaying( true );
 
+            tree_update descr(
+                tree_update::ItemUpdated, IteratorVisible( it, this ) );
+            notify( &descr );
+        }
+
         playlist_Unlock( m_pPlaylist );
+    }
+    else
+    {
+        for( Iterator it = m_children.begin(); it != m_children.end();
+             it = getNextItem( it ) )
+        {
+            if( it->isPlaying() )
+            {
+                it->setPlaying( false );
 
-        tree_update descr;
-        descr.type = tree_update::UpdateItem;
-        descr.i_id = current->i_id;
-        descr.b_active_item = true;
-        notify( &descr );
+                tree_update descr(
+                    tree_update::ItemUpdated, IteratorVisible( it, this ) );
+                notify( &descr );
+                break;
+            }
+        }
     }
 }
 
 void Playtree::onDelete( int i_id )
 {
-    Iterator item = findById( i_id ) ;
-    if( item != end() )
+    Iterator it = findById( i_id ) ;
+    if( it != m_children.end() )
     {
-        VarTree* parent = item->parent();
+        VarTree* parent = it->parent();
+        if( parent )
+        {
+            tree_update descr(
+                tree_update::DeletingItem, IteratorVisible( it, this ) );
+            notify( &descr );
 
-        item->setDeleted( true );
+            parent->removeChild( it );
+            m_allItems.erase( i_id );
 
-        tree_update descr;
-        descr.type = tree_update::DeleteItem;
-        descr.i_id = i_id;
-        notify( &descr );
-
-        if( parent )
-            parent->removeChild( item );
+            tree_update descr2(
+                tree_update::ItemDeleted, end() );
+            notify( &descr2 );
+        }
     }
 }
 
 void Playtree::onAppend( playlist_add_t *p_add )
 {
-    Iterator node = findById( p_add->i_node );
-    if( node != end() )
+    Iterator it_node = findById( p_add->i_node );
+    if( it_node != m_children.end() )
     {
         playlist_Lock( m_pPlaylist );
-        playlist_item_t *p_item =
+        playlist_item_t *pItem =
             playlist_ItemGetById( m_pPlaylist, p_add->i_item );
-        if( !p_item )
+        if( !pItem )
         {
             playlist_Unlock( m_pPlaylist );
             return;
         }
 
+        int pos;
+        for( pos = 0; pos < pItem->p_parent->i_children; pos++ )
+            if( pItem->p_parent->pp_children[pos] == pItem ) break;
+
         UString *pName = new UString( getIntf(),
-                                      p_item->p_input->psz_name );
-        node->add( p_add->i_item, UStringPtr( pName ),
-                   false,false, false, p_item->i_flags & PLAYLIST_RO_FLAG,
-                   p_item );
+                                      pItem->p_input->psz_name );
+
+        playlist_item_t* current = playlist_CurrentPlayingItem( m_pPlaylist );
+
+        Iterator it = it_node->add(
+            p_add->i_item, UStringPtr( pName ), false, pItem == current,
+            false, pItem->i_flags & PLAYLIST_RO_FLAG, pos );
+
+        m_allItems[pItem->i_id] = &*it;
+
         playlist_Unlock( m_pPlaylist );
 
-        tree_update descr;
-        descr.type = tree_update::AppendItem;
-        descr.i_id = p_add->i_item;
+        tree_update descr(
+            tree_update::ItemInserted,
+            IteratorVisible( it, this ) );
         notify( &descr );
     }
 }
 
 void Playtree::buildNode( playlist_item_t *pNode, VarTree &rTree )
 {
+    UString *pName = new UString( getIntf(), pNode->p_input->psz_name );
+    Iterator it = rTree.add(
+        pNode->i_id, UStringPtr( pName ), false,
+        playlist_CurrentPlayingItem(m_pPlaylist) == pNode,
+        false, pNode->i_flags & PLAYLIST_RO_FLAG );
+    m_allItems[pNode->i_id] = &*it;
+
     for( int i = 0; i < pNode->i_children; i++ )
     {
-        UString *pName = new UString( getIntf(),
-                                   pNode->pp_children[i]->p_input->psz_name );
-        rTree.add(
-            pNode->pp_children[i]->i_id, UStringPtr( pName ), false,
-            playlist_CurrentPlayingItem(m_pPlaylist) == pNode->pp_children[i],
-            false, pNode->pp_children[i]->i_flags & PLAYLIST_RO_FLAG,
-            pNode->pp_children[i] );
-        if( pNode->pp_children[i]->i_children > 0 )
-        {
-            buildNode( pNode->pp_children[i], rTree.back() );
-        }
+        buildNode( pNode->pp_children[i], *it );
     }
 }
 
@@ -252,17 +257,84 @@ void Playtree::buildTree()
     clear();
     playlist_Lock( m_pPlaylist );
 
-    clear();
+    for( int i = 0; i < m_pPlaylist->p_root->i_children; i++ )
+    {
+        buildNode( m_pPlaylist->p_root->pp_children[i], *this );
+    }
 
-    /* TODO: Let user choose view - Stick with category ATM */
+    playlist_Unlock( m_pPlaylist );
+}
 
-    /* Set the root's name */
-    UString *pName = new UString( getIntf(),
-                             m_pPlaylist->p_root_category->p_input->psz_name );
-    setString( UStringPtr( pName ) );
+void Playtree::onUpdateSlider()
+{
+    tree_update descr( tree_update::SliderChanged, end() );
+    notify( &descr );
+}
 
-    buildNode( m_pPlaylist->p_root_category, *this );
+void Playtree::insertItems( VarTree& elem, const list<string>& files, bool start )
+{
+    bool first = true;
+    VarTree* p_elem = &elem;
+    playlist_item_t* p_node = NULL;
+    int i_pos = -1;
+
+    playlist_Lock( m_pPlaylist );
 
+    if( p_elem->getId() == m_pPlaylist->p_local_category->i_id )
+    {
+        p_node = m_pPlaylist->p_local_category;
+        i_pos = 0;
+    }
+    else if( p_elem->getId() == m_pPlaylist->p_ml_category->i_id )
+    {
+        p_node = m_pPlaylist->p_ml_category;
+        i_pos = 0;
+    }
+    else if( p_elem->size() )
+    {
+        p_node = playlist_ItemGetById( m_pPlaylist, p_elem->getId() );
+        i_pos = 0;
+    }
+    else
+    {
+        p_node = playlist_ItemGetById( m_pPlaylist,
+                                       p_elem->parent()->getId() );
+        i_pos = p_elem->getIndex();
+        i_pos++;
+    }
+
+    if( !p_node )
+        goto fin;
+
+    for( list<string>::const_iterator it = files.begin();
+         it != files.end(); ++it, i_pos++, first = false )
+    {
+        char* psz_uri = make_URI( it->c_str(), NULL );
+        if( !psz_uri )
+            continue;
+
+        input_item_t* pItem = input_item_New( m_pPlaylist, psz_uri, NULL );
+        if( pItem )
+        {
+            int i_mode = PLAYLIST_APPEND;
+            if( first && start )
+                i_mode |= PLAYLIST_GO;
+
+            playlist_NodeAddInput( m_pPlaylist, pItem, p_node,
+                                   i_mode, i_pos, pl_Locked );
+        }
+        free( psz_uri );
+    }
+
+fin:
     playlist_Unlock( m_pPlaylist );
 }
 
+VarTree::Iterator Playtree::findById( int id )
+{
+    map<int,VarTree*>::iterator it = m_allItems.find( id );
+    if( it == m_allItems.end() )
+        return m_children.end();
+    else
+        return it->second->getSelf();
+}
index c7bde2bd61d0e16caf1032dc1ddc9f15d7ead2e8..78d33fe0b0cec02c48df7bfa56fa32728f8b2bb6 100644 (file)
@@ -28,6 +28,8 @@
 #include <vlc_playlist.h>
 #include "../utils/var_tree.hpp"
 
+#include <map>
+
 /// Variable for VLC playlist (new tree format)
 class Playtree: public VarTree
 {
@@ -56,13 +58,25 @@ public:
     /// Function called to notify playlist item delete
     void onDelete( int );
 
+    ///
+    void onUpdateSlider();
+
+    ///
+    void insertItems( VarTree& item, const list<string>& files, bool start );
+
 private:
     /// VLC playlist object
     playlist_t *m_pPlaylist;
 
+    ///
+    map< int, VarTree* > m_allItems;
+
     /// Build the list from the VLC playlist
     void buildTree();
 
+    /// Retrieve an iterator from playlist_item_t->id
+    Iterator findById( int id );
+
     /// Update Node's children
     void buildNode( playlist_item_t *p_node, VarTree &m_pNode );
 };