Commit a9beb407 authored by Erwan Tulou's avatar Erwan Tulou

skins2: fix some important playlist limitations or bugs

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