Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

ctrl_list.cpp

00001 /*****************************************************************************
00002  * ctrl_list.cpp
00003  *****************************************************************************
00004  * Copyright (C) 2003 the VideoLAN team
00005  * $Id: ctrl_list.cpp 12285 2005-08-20 09:26:46Z dionoea $
00006  *
00007  * Authors: Cyril Deguet     <[email protected]>
00008  *          Olivier Teulière <[email protected]>
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00023  *****************************************************************************/
00024 
00025 #include <math.h>
00026 #include "ctrl_list.hpp"
00027 #include "../src/os_factory.hpp"
00028 #include "../src/os_graphics.hpp"
00029 #include "../src/generic_bitmap.hpp"
00030 #include "../src/generic_font.hpp"
00031 #include "../src/scaled_bitmap.hpp"
00032 #include "../utils/position.hpp"
00033 #include "../utils/ustring.hpp"
00034 #include "../events/evt_key.hpp"
00035 #include "../events/evt_mouse.hpp"
00036 #include "../events/evt_scroll.hpp"
00037 #include "vlc_keys.h"
00038 #ifdef sun
00039 #   include "solaris_specific.h" // for lrint
00040 #endif
00041 
00042 #define SCROLL_STEP 0.05f
00043 #define LINE_INTERVAL 1  // Number of pixels inserted between 2 lines
00044 
00045 
00046 CtrlList::CtrlList( intf_thread_t *pIntf, VarList &rList,
00047                     const GenericFont &rFont, const GenericBitmap *pBitmap,
00048                     uint32_t fgColor, uint32_t playColor, uint32_t bgColor1,
00049                     uint32_t bgColor2, uint32_t selColor,
00050                     const UString &rHelp, VarBool *pVisible ):
00051     CtrlGeneric( pIntf, rHelp, pVisible ), m_rList( rList ), m_rFont( rFont ),
00052     m_pBitmap( pBitmap ), m_fgColor( fgColor ), m_playColor( playColor ),
00053     m_bgColor1( bgColor1 ), m_bgColor2( bgColor2 ), m_selColor( selColor ),
00054     m_pLastSelected( NULL ), m_pImage( NULL ), m_lastPos( 0 )
00055 {
00056     // Observe the list and position variables
00057     m_rList.addObserver( this );
00058     m_rList.getPositionVar().addObserver( this );
00059 
00060     makeImage();
00061 }
00062 
00063 
00064 CtrlList::~CtrlList()
00065 {
00066     m_rList.getPositionVar().delObserver( this );
00067     m_rList.delObserver( this );
00068     if( m_pImage )
00069     {
00070         delete m_pImage;
00071     }
00072 }
00073 
00074 
00075 void CtrlList::onUpdate( Subject<VarList> &rList )
00076 {
00077     autoScroll();
00078     m_pLastSelected = NULL;
00079 }
00080 
00081 
00082 void CtrlList::onUpdate( Subject<VarPercent> &rPercent )
00083 {
00084     // Get the size of the control
00085     const Position *pPos = getPosition();
00086     if( !pPos )
00087     {
00088         return;
00089     }
00090     int height = pPos->getHeight();
00091 
00092     // How many lines can be displayed ?
00093     int itemHeight = m_rFont.getSize() + LINE_INTERVAL;
00094     int maxItems = height / itemHeight;
00095 
00096     // Determine what is the first item to display
00097     VarPercent &rVarPos = m_rList.getPositionVar();
00098     int firstItem = 0;
00099     int excessItems = m_rList.size() - maxItems;
00100     if( excessItems > 0 )
00101     {
00102         // a simple (int)(...) causes rounding errors !
00103 #ifdef _MSC_VER
00104 #   define lrint (int)
00105 #endif
00106         firstItem = lrint( (1.0 - rVarPos.get()) * (double)excessItems );
00107     }
00108     if( m_lastPos != firstItem )
00109     {
00110         // Redraw the control if the position has changed
00111         m_lastPos = firstItem;
00112         makeImage();
00113         notifyLayout();
00114     }
00115 }
00116 
00117 
00118 void CtrlList::onResize()
00119 {
00120     // Get the size of the control
00121     const Position *pPos = getPosition();
00122     if( !pPos )
00123     {
00124         return;
00125     }
00126     int height = pPos->getHeight();
00127 
00128     // How many lines can be displayed ?
00129     int itemHeight = m_rFont.getSize() + LINE_INTERVAL;
00130     int maxItems = height / itemHeight;
00131 
00132     // Update the position variable
00133     VarPercent &rVarPos = m_rList.getPositionVar();
00134     int excessItems = m_rList.size() - maxItems;
00135     if( excessItems > 0 )
00136     {
00137         double newVal = 1.0 - (double)m_lastPos / excessItems;
00138         if( newVal >= 0 )
00139         {
00140             // Change the position to keep the same first displayed item
00141             rVarPos.set( 1.0 - (double)m_lastPos / excessItems );
00142         }
00143         else
00144         {
00145             // We cannot keep the current first item
00146             m_lastPos = excessItems;
00147         }
00148     }
00149 
00150     makeImage();
00151     notifyLayout();
00152 }
00153 
00154 
00155 void CtrlList::onPositionChange()
00156 {
00157     makeImage();
00158     notifyLayout();
00159 }
00160 
00161 
00162 void CtrlList::handleEvent( EvtGeneric &rEvent )
00163 {
00164     if( rEvent.getAsString().find( "key:down" ) != string::npos )
00165     {
00166         int key = ((EvtKey&)rEvent).getKey();
00167         VarList::Iterator it = m_rList.begin();
00168         bool previousWasSelected = false;
00169         while( it != m_rList.end() )
00170         {
00171             VarList::Iterator next = it;
00172             ++next;
00173             if( key == KEY_UP )
00174             {
00175                 // Scroll up one item
00176                 if( it != m_rList.begin() || &*it != m_pLastSelected )
00177                 {
00178                     bool nextWasSelected = ( &*next == m_pLastSelected );
00179                     (*it).m_selected = nextWasSelected;
00180                     if( nextWasSelected )
00181                     {
00182                         m_pLastSelected = &*it;
00183                     }
00184                 }
00185             }
00186             else if( key == KEY_DOWN )
00187             {
00188                 // Scroll down one item
00189                 if( next != m_rList.end() || &*it != m_pLastSelected )
00190                 {
00191                     (*it).m_selected = previousWasSelected;
00192                 }
00193                 if( previousWasSelected )
00194                 {
00195                     m_pLastSelected = &*it;
00196                     previousWasSelected = false;
00197                 }
00198                 else
00199                 {
00200                     previousWasSelected = ( &*it == m_pLastSelected );
00201                 }
00202             }
00203             it = next;
00204         }
00205 
00206         // Redraw the control
00207         makeImage();
00208         notifyLayout();
00209     }
00210 
00211     else if( rEvent.getAsString().find( "mouse:left" ) != string::npos )
00212     {
00213         EvtMouse &rEvtMouse = (EvtMouse&)rEvent;
00214         const Position *pos = getPosition();
00215         int yPos = m_lastPos + ( rEvtMouse.getYPos() - pos->getTop() ) /
00216             (m_rFont.getSize() + LINE_INTERVAL);
00217         VarList::Iterator it;
00218         int index = 0;
00219 
00220         if( rEvent.getAsString().find( "mouse:left:down:ctrl,shift" ) !=
00221                  string::npos )
00222         {
00223             // Flag to know if the current item must be selected
00224             bool select = false;
00225             for( it = m_rList.begin(); it != m_rList.end(); it++ )
00226             {
00227                 bool nextSelect = select;
00228                 if( index == yPos || &*it == m_pLastSelected )
00229                 {
00230                     if( select )
00231                     {
00232                         nextSelect = false;
00233                     }
00234                     else
00235                     {
00236                         select = true;
00237                         nextSelect = true;
00238                     }
00239                 }
00240                 (*it).m_selected = (*it).m_selected || select;
00241                 select = nextSelect;
00242                 index++;
00243             }
00244         }
00245 
00246         else if( rEvent.getAsString().find( "mouse:left:down:ctrl" ) !=
00247                  string::npos )
00248         {
00249             for( it = m_rList.begin(); it != m_rList.end(); it++ )
00250             {
00251                 if( index == yPos )
00252                 {
00253                     (*it).m_selected = ! (*it).m_selected;
00254                     m_pLastSelected = &*it;
00255                     break;
00256                 }
00257                 index++;
00258             }
00259         }
00260 
00261         else if( rEvent.getAsString().find( "mouse:left:down:shift" ) !=
00262                  string::npos )
00263         {
00264             // Flag to know if the current item must be selected
00265             bool select = false;
00266             for( it = m_rList.begin(); it != m_rList.end(); it++ )
00267             {
00268                 bool nextSelect = select;
00269                 if( index == yPos ||  &*it == m_pLastSelected )
00270                 {
00271                     if( select )
00272                     {
00273                         nextSelect = false;
00274                     }
00275                     else
00276                     {
00277                         select = true;
00278                         nextSelect = true;
00279                     }
00280                 }
00281                 (*it).m_selected = select;
00282                 select = nextSelect;
00283                 index++;
00284             }
00285         }
00286 
00287         else if( rEvent.getAsString().find( "mouse:left:down" ) !=
00288                  string::npos )
00289         {
00290             for( it = m_rList.begin(); it != m_rList.end(); it++ )
00291             {
00292                 if( index == yPos )
00293                 {
00294                     (*it).m_selected = true;
00295                     m_pLastSelected = &*it;
00296                 }
00297                 else
00298                 {
00299                     (*it).m_selected = false;
00300                 }
00301                 index++;
00302             }
00303         }
00304 
00305         else if( rEvent.getAsString().find( "mouse:left:dblclick" ) !=
00306                  string::npos )
00307         {
00308             for( it = m_rList.begin(); it != m_rList.end(); it++ )
00309             {
00310                 if( index == yPos )
00311                 {
00312                     (*it).m_selected = true;
00313                     m_pLastSelected = &*it;
00314                     // Execute the action associated to this item
00315                     m_rList.action( &*it );
00316                 }
00317                 else
00318                 {
00319                     (*it).m_selected = false;
00320                 }
00321                 index++;
00322             }
00323         }
00324 
00325         // Redraw the control
00326         makeImage();
00327         notifyLayout();
00328     }
00329 
00330     else if( rEvent.getAsString().find( "scroll" ) != string::npos )
00331     {
00332         int direction = ((EvtScroll&)rEvent).getDirection();
00333 
00334         double percentage = m_rList.getPositionVar().get();
00335         double step = 2.0 / (double)m_rList.size();
00336         if( direction == EvtScroll::kUp )
00337         {
00338             percentage += step;
00339         }
00340         else
00341         {
00342             percentage -= step;
00343         }
00344         m_rList.getPositionVar().set( percentage );
00345     }
00346 }
00347 
00348 
00349 bool CtrlList::mouseOver( int x, int y ) const
00350 {
00351     const Position *pPos = getPosition();
00352     if( pPos )
00353     {
00354         int width = pPos->getWidth();
00355         int height = pPos->getHeight();
00356         return ( x >= 0 && x <= width && y >= 0 && y <= height );
00357     }
00358     return false;
00359 }
00360 
00361 
00362 void CtrlList::draw( OSGraphics &rImage, int xDest, int yDest )
00363 {
00364     if( m_pImage )
00365     {
00366         rImage.drawGraphics( *m_pImage, 0, 0, xDest, yDest );
00367     }
00368 }
00369 
00370 
00371 void CtrlList::autoScroll()
00372 {
00373     // Get the size of the control
00374     const Position *pPos = getPosition();
00375     if( !pPos )
00376     {
00377         return;
00378     }
00379     int height = pPos->getHeight();
00380 
00381     // How many lines can be displayed ?
00382     int itemHeight = m_rFont.getSize() + LINE_INTERVAL;
00383     int maxItems = height / itemHeight;
00384 
00385     // Find the current playing stream
00386     int playIndex = 0;
00387     VarList::ConstIterator it;
00388     for( it = m_rList.begin(); it != m_rList.end(); it++ )
00389     {
00390         if( (*it).m_playing )
00391         {
00392             break;
00393         }
00394         playIndex++;
00395     }
00396     if( it != m_rList.end() &&
00397         ( playIndex < m_lastPos || playIndex >= m_lastPos + maxItems ) )
00398     {
00399         // Scroll the list to have the playing stream visible
00400         VarPercent &rVarPos = m_rList.getPositionVar();
00401         rVarPos.set( 1.0 - (float)playIndex / (float)m_rList.size() );
00402         // The image will be changed by onUpdate(VarPercent&)
00403     }
00404     else
00405     {
00406         makeImage();
00407         notifyLayout();
00408     }
00409 }
00410 
00411 
00412 void CtrlList::makeImage()
00413 {
00414     if( m_pImage )
00415     {
00416         delete m_pImage;
00417     }
00418 
00419     // Get the size of the control
00420     const Position *pPos = getPosition();
00421     if( !pPos )
00422     {
00423         return;
00424     }
00425     int width = pPos->getWidth();
00426     int height = pPos->getHeight();
00427     int itemHeight = m_rFont.getSize() + LINE_INTERVAL;
00428 
00429     // Create an image
00430     OSFactory *pOsFactory = OSFactory::instance( getIntf() );
00431     m_pImage = pOsFactory->createOSGraphics( width, height );
00432 
00433     VarList::ConstIterator it = m_rList[m_lastPos];
00434 
00435     // Draw the background
00436     if( m_pBitmap )
00437     {
00438         // A background bitmap is given, so we scale it, ignoring the
00439         // background colors
00440         ScaledBitmap bmp( getIntf(), *m_pBitmap, width, height );
00441         m_pImage->drawBitmap( bmp, 0, 0 );
00442 
00443         // Take care of the selection color
00444         for( int yPos = 0; yPos < height; yPos += itemHeight )
00445         {
00446             int rectHeight = __MIN( itemHeight, height - yPos );
00447             if( it != m_rList.end() )
00448             {
00449                 if( (*it).m_selected )
00450                 {
00451                     m_pImage->fillRect( 0, yPos, width, rectHeight,
00452                                         m_selColor );
00453                 }
00454                 it++;
00455             }
00456         }
00457     }
00458     else
00459     {
00460         // No background bitmap, so use the 2 background colors
00461         // Current background color
00462         uint32_t bgColor = m_bgColor1;
00463         for( int yPos = 0; yPos < height; yPos += itemHeight )
00464         {
00465             int rectHeight = __MIN( itemHeight, height - yPos );
00466             if( it != m_rList.end() )
00467             {
00468                 uint32_t color = ( (*it).m_selected ? m_selColor : bgColor );
00469                 m_pImage->fillRect( 0, yPos, width, rectHeight, color );
00470                 it++;
00471             }
00472             else
00473             {
00474                 m_pImage->fillRect( 0, yPos, width, rectHeight, bgColor );
00475             }
00476             // Flip the background color
00477             bgColor = ( bgColor == m_bgColor1 ? m_bgColor2 : m_bgColor1 );
00478         }
00479     }
00480 
00481     // Draw the items
00482     int yPos = 0;
00483     for( it = m_rList[m_lastPos]; it != m_rList.end() && yPos < height; it++ )
00484     {
00485         UString *pStr = (UString*)(it->m_cString.get());
00486         uint32_t color = ( it->m_playing ? m_playColor : m_fgColor );
00487 
00488         // Draw the text
00489         GenericBitmap *pText = m_rFont.drawString( *pStr, color, width );
00490         if( !pText )
00491         {
00492             return;
00493         }
00494         yPos += itemHeight - pText->getHeight();
00495         int ySrc = 0;
00496         if( yPos < 0 )
00497         {
00498             ySrc = - yPos;
00499             yPos = 0;
00500         }
00501         int lineHeight = __MIN( pText->getHeight() - ySrc, height - yPos );
00502         m_pImage->drawBitmap( *pText, 0, ySrc, 0, yPos, pText->getWidth(),
00503                               lineHeight, true );
00504         yPos += (pText->getHeight() - ySrc );
00505         delete pText;
00506 
00507     }
00508 }
00509 

Generated on Tue Dec 20 10:14:41 2005 for vlc-0.8.4a by  doxygen 1.4.2