You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

509 lines
14 KiB

/*****************************************************************************
* ctrl_list.cpp
*****************************************************************************
* Copyright (C) 2003 the VideoLAN team
* $Id$
*
* Authors: Cyril Deguet <asmax@via.ecp.fr>
* Olivier Teulière <ipkiss@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include <math.h>
#include "ctrl_list.hpp"
#include "../src/os_factory.hpp"
#include "../src/os_graphics.hpp"
#include "../src/generic_bitmap.hpp"
#include "../src/generic_font.hpp"
#include "../src/scaled_bitmap.hpp"
#include "../utils/position.hpp"
#include "../utils/ustring.hpp"
#include "../events/evt_key.hpp"
#include "../events/evt_mouse.hpp"
#include "../events/evt_scroll.hpp"
#include "vlc_keys.h"
#ifdef sun
# include "solaris_specific.h" // for lrint
#endif
#define SCROLL_STEP 0.05f
#define LINE_INTERVAL 1 // Number of pixels inserted between 2 lines
CtrlList::CtrlList( intf_thread_t *pIntf, VarList &rList,
const GenericFont &rFont, const GenericBitmap *pBitmap,
uint32_t fgColor, uint32_t playColor, uint32_t bgColor1,
uint32_t bgColor2, uint32_t selColor,
const UString &rHelp, VarBool *pVisible ):
CtrlGeneric( pIntf, rHelp, pVisible ), m_rList( rList ), m_rFont( rFont ),
m_pBitmap( pBitmap ), m_fgColor( fgColor ), m_playColor( playColor ),
m_bgColor1( bgColor1 ), m_bgColor2( bgColor2 ), m_selColor( selColor ),
m_pLastSelected( NULL ), m_pImage( NULL ), m_lastPos( 0 )
{
// Observe the list and position variables
m_rList.addObserver( this );
m_rList.getPositionVar().addObserver( this );
makeImage();
}
CtrlList::~CtrlList()
{
m_rList.getPositionVar().delObserver( this );
m_rList.delObserver( this );
if( m_pImage )
{
delete m_pImage;
}
}
void CtrlList::onUpdate( Subject<VarList, void*> &rList, void *arg )
{
autoScroll();
m_pLastSelected = NULL;
}
void CtrlList::onUpdate( Subject<VarPercent, void*> &rPercent, void *arg )
{
// Get the size of the control
const Position *pPos = getPosition();
if( !pPos )
{
return;
}
int height = pPos->getHeight();
// How many lines can be displayed ?
int itemHeight = m_rFont.getSize() + LINE_INTERVAL;
int maxItems = height / itemHeight;
// Determine what is the first item to display
VarPercent &rVarPos = m_rList.getPositionVar();
int firstItem = 0;
int excessItems = m_rList.size() - maxItems;
if( excessItems > 0 )
{
// a simple (int)(...) causes rounding errors !
#ifdef _MSC_VER
# define lrint (int)
#endif
firstItem = lrint( (1.0 - rVarPos.get()) * (double)excessItems );
}
if( m_lastPos != firstItem )
{
// Redraw the control if the position has changed
m_lastPos = firstItem;
makeImage();
notifyLayout();
}
}
void CtrlList::onResize()
{
// Get the size of the control
const Position *pPos = getPosition();
if( !pPos )
{
return;
}
int height = pPos->getHeight();
// How many lines can be displayed ?
int itemHeight = m_rFont.getSize() + LINE_INTERVAL;
int maxItems = height / itemHeight;
// Update the position variable
VarPercent &rVarPos = m_rList.getPositionVar();
int excessItems = m_rList.size() - maxItems;
if( excessItems > 0 )
{
double newVal = 1.0 - (double)m_lastPos / excessItems;
if( newVal >= 0 )
{
// Change the position to keep the same first displayed item
rVarPos.set( 1.0 - (double)m_lastPos / excessItems );
}
else
{
// We cannot keep the current first item
m_lastPos = excessItems;
}
}
makeImage();
notifyLayout();
}
void CtrlList::onPositionChange()
{
makeImage();
notifyLayout();
}
void CtrlList::handleEvent( EvtGeneric &rEvent )
{
if( rEvent.getAsString().find( "key:down" ) != string::npos )
{
int key = ((EvtKey&)rEvent).getKey();
VarList::Iterator it = m_rList.begin();
bool previousWasSelected = false;
while( it != m_rList.end() )
{
VarList::Iterator next = it;
++next;
if( key == KEY_UP )
{
// Scroll up one item
if( it != m_rList.begin() || &*it != m_pLastSelected )
{
bool nextWasSelected = ( &*next == m_pLastSelected );
(*it).m_selected = nextWasSelected;
if( nextWasSelected )
{
m_pLastSelected = &*it;
}
}
}
else if( key == KEY_DOWN )
{
// Scroll down one item
if( next != m_rList.end() || &*it != m_pLastSelected )
{
(*it).m_selected = previousWasSelected;
}
if( previousWasSelected )
{
m_pLastSelected = &*it;
previousWasSelected = false;
}
else
{
previousWasSelected = ( &*it == m_pLastSelected );
}
}
it = next;
}
// Redraw the control
makeImage();
notifyLayout();
}
else if( rEvent.getAsString().find( "mouse:left" ) != string::npos )
{
EvtMouse &rEvtMouse = (EvtMouse&)rEvent;
const Position *pos = getPosition();
int yPos = m_lastPos + ( rEvtMouse.getYPos() - pos->getTop() ) /
(m_rFont.getSize() + LINE_INTERVAL);
VarList::Iterator it;
int index = 0;
if( rEvent.getAsString().find( "mouse:left:down:ctrl,shift" ) !=
string::npos )
{
// Flag to know if the current item must be selected
bool select = false;
for( it = m_rList.begin(); it != m_rList.end(); it++ )
{
bool nextSelect = select;
if( index == yPos || &*it == m_pLastSelected )
{
if( select )
{
nextSelect = false;
}
else
{
select = true;
nextSelect = true;
}
}
(*it).m_selected = (*it).m_selected || select;
select = nextSelect;
index++;
}
}
else if( rEvent.getAsString().find( "mouse:left:down:ctrl" ) !=
string::npos )
{
for( it = m_rList.begin(); it != m_rList.end(); it++ )
{
if( index == yPos )
{
(*it).m_selected = ! (*it).m_selected;
m_pLastSelected = &*it;
break;
}
index++;
}
}
else if( rEvent.getAsString().find( "mouse:left:down:shift" ) !=
string::npos )
{
// Flag to know if the current item must be selected
bool select = false;
for( it = m_rList.begin(); it != m_rList.end(); it++ )
{
bool nextSelect = select;
if( index == yPos || &*it == m_pLastSelected )
{
if( select )
{
nextSelect = false;
}
else
{
select = true;
nextSelect = true;
}
}
(*it).m_selected = select;
select = nextSelect;
index++;
}
}
else if( rEvent.getAsString().find( "mouse:left:down" ) !=
string::npos )
{
for( it = m_rList.begin(); it != m_rList.end(); it++ )
{
if( index == yPos )
{
(*it).m_selected = true;
m_pLastSelected = &*it;
}
else
{
(*it).m_selected = false;
}
index++;
}
}
else if( rEvent.getAsString().find( "mouse:left:dblclick" ) !=
string::npos )
{
for( it = m_rList.begin(); it != m_rList.end(); it++ )
{
if( index == yPos )
{
(*it).m_selected = true;
m_pLastSelected = &*it;
// Execute the action associated to this item
m_rList.action( &*it );
}
else
{
(*it).m_selected = false;
}
index++;
}
}
// Redraw the control
makeImage();
notifyLayout();
}
else if( rEvent.getAsString().find( "scroll" ) != string::npos )
{
int direction = ((EvtScroll&)rEvent).getDirection();
double percentage = m_rList.getPositionVar().get();
double step = 2.0 / (double)m_rList.size();
if( direction == EvtScroll::kUp )
{
percentage += step;
}
else
{
percentage -= step;
}
m_rList.getPositionVar().set( percentage );
}
}
bool CtrlList::mouseOver( int x, int y ) const
{
const Position *pPos = getPosition();
if( pPos )
{
int width = pPos->getWidth();
int height = pPos->getHeight();
return ( x >= 0 && x <= width && y >= 0 && y <= height );
}
return false;
}
void CtrlList::draw( OSGraphics &rImage, int xDest, int yDest )
{
if( m_pImage )
{
rImage.drawGraphics( *m_pImage, 0, 0, xDest, yDest );
}
}
void CtrlList::autoScroll()
{
// Get the size of the control
const Position *pPos = getPosition();
if( !pPos )
{
return;
}
int height = pPos->getHeight();
// How many lines can be displayed ?
int itemHeight = m_rFont.getSize() + LINE_INTERVAL;
int maxItems = height / itemHeight;
// Find the current playing stream
int playIndex = 0;
VarList::ConstIterator it;
for( it = m_rList.begin(); it != m_rList.end(); it++ )
{
if( (*it).m_playing )
{
break;
}
playIndex++;
}
if( it != m_rList.end() &&
( playIndex < m_lastPos || playIndex >= m_lastPos + maxItems ) )
{
// Scroll the list to have the playing stream visible
VarPercent &rVarPos = m_rList.getPositionVar();
rVarPos.set( 1.0 - (float)playIndex / (float)m_rList.size() );
// The image will be changed by onUpdate(VarPercent&)
}
else
{
makeImage();
notifyLayout();
}
}
void CtrlList::makeImage()
{
if( m_pImage )
{
delete m_pImage;
}
// Get the size of the control
const Position *pPos = getPosition();
if( !pPos )
{
return;
}
int width = pPos->getWidth();
int height = pPos->getHeight();
int itemHeight = m_rFont.getSize() + LINE_INTERVAL;
// Create an image
OSFactory *pOsFactory = OSFactory::instance( getIntf() );
m_pImage = pOsFactory->createOSGraphics( width, height );
VarList::ConstIterator it = m_rList[m_lastPos];
// Draw the background
if( m_pBitmap )
{
// A background bitmap is given, so we scale it, ignoring the
// background colors
ScaledBitmap bmp( getIntf(), *m_pBitmap, width, height );
m_pImage->drawBitmap( bmp, 0, 0 );
// Take care of the selection color
for( int yPos = 0; yPos < height; yPos += itemHeight )
{
int rectHeight = __MIN( itemHeight, height - yPos );
if( it != m_rList.end() )
{
if( (*it).m_selected )
{
m_pImage->fillRect( 0, yPos, width, rectHeight,
m_selColor );
}
it++;
}
}
}
else
{
// No background bitmap, so use the 2 background colors
// Current background color
uint32_t bgColor = m_bgColor1;
for( int yPos = 0; yPos < height; yPos += itemHeight )
{
int rectHeight = __MIN( itemHeight, height - yPos );
if( it != m_rList.end() )
{
uint32_t color = ( (*it).m_selected ? m_selColor : bgColor );
m_pImage->fillRect( 0, yPos, width, rectHeight, color );
it++;
}
else
{
m_pImage->fillRect( 0, yPos, width, rectHeight, bgColor );
}
// Flip the background color
bgColor = ( bgColor == m_bgColor1 ? m_bgColor2 : m_bgColor1 );
}
}
// Draw the items
int yPos = 0;
for( it = m_rList[m_lastPos]; it != m_rList.end() && yPos < height; it++ )
{
UString *pStr = (UString*)(it->m_cString.get());
uint32_t color = ( it->m_playing ? m_playColor : m_fgColor );
// Draw the text
GenericBitmap *pText = m_rFont.drawString( *pStr, color, width );
if( !pText )
{
return;
}
yPos += itemHeight - pText->getHeight();
int ySrc = 0;
if( yPos < 0 )
{
ySrc = - yPos;
yPos = 0;
}
int lineHeight = __MIN( pText->getHeight() - ySrc, height - yPos );
m_pImage->drawBitmap( *pText, 0, ySrc, 0, yPos, pText->getWidth(),
lineHeight, true );
yPos += (pText->getHeight() - ySrc );
delete pText;
}
}