00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <math.h>
00025 #include "ctrl_tree.hpp"
00026 #include "../src/os_factory.hpp"
00027 #include "../src/os_graphics.hpp"
00028 #include "../src/generic_bitmap.hpp"
00029 #include "../src/generic_font.hpp"
00030 #include "../src/scaled_bitmap.hpp"
00031 #include "../utils/position.hpp"
00032 #include "../utils/ustring.hpp"
00033 #include "../events/evt_key.hpp"
00034 #include "../events/evt_mouse.hpp"
00035 #include "../events/evt_scroll.hpp"
00036 #include "vlc_keys.h"
00037 #ifdef sun
00038 # include "solaris_specific.h"
00039 #endif
00040
00041 #define SCROLL_STEP 0.05
00042 #define LINE_INTERVAL 1 // Number of pixels inserted between 2 lines
00043
00044
00045 CtrlTree::CtrlTree( intf_thread_t *pIntf,
00046 VarTree &rTree,
00047 const GenericFont &rFont,
00048 const GenericBitmap *pBgBitmap,
00049 const GenericBitmap *pItemBitmap,
00050 const GenericBitmap *pOpenBitmap,
00051 const GenericBitmap *pClosedBitmap,
00052 uint32_t fgColor,
00053 uint32_t playColor,
00054 uint32_t bgColor1,
00055 uint32_t bgColor2,
00056 uint32_t selColor,
00057 const UString &rHelp,
00058 VarBool *pVisible ):
00059 CtrlGeneric( pIntf,rHelp, pVisible), m_rTree( rTree), m_rFont( rFont ),
00060 m_pBgBitmap( pBgBitmap ), m_pItemBitmap( pItemBitmap ),
00061 m_pOpenBitmap( pOpenBitmap ), m_pClosedBitmap( pClosedBitmap ),
00062 m_fgColor( fgColor ), m_playColor( playColor ), m_bgColor1( bgColor1 ),
00063 m_bgColor2( bgColor2 ), m_selColor( selColor ),
00064 m_pLastSelected( NULL ), m_pImage( NULL )
00065 {
00066
00067 m_rTree.addObserver( this );
00068 m_rTree.getPositionVar().addObserver( this );
00069
00070 m_lastPos = m_rTree.begin();
00071
00072 makeImage();
00073 }
00074
00075 CtrlTree::~CtrlTree()
00076 {
00077 m_rTree.getPositionVar().delObserver( this );
00078 m_rTree.delObserver( this );
00079 if( m_pImage )
00080 {
00081 delete m_pImage;
00082 }
00083 }
00084
00085 int CtrlTree::itemHeight()
00086 {
00087 int itemHeight = m_rFont.getSize();
00088 if( m_pClosedBitmap )
00089 {
00090 itemHeight = __MAX( m_pClosedBitmap->getHeight(), itemHeight );
00091 }
00092 if( m_pOpenBitmap )
00093 {
00094 itemHeight = __MAX( m_pOpenBitmap->getHeight(), itemHeight );
00095 }
00096 if( m_pItemBitmap )
00097 {
00098 itemHeight = __MAX( m_pItemBitmap->getHeight(), itemHeight );
00099 }
00100 itemHeight += LINE_INTERVAL;
00101 return itemHeight;
00102 }
00103
00104 int CtrlTree::itemImageWidth()
00105 {
00106 int bitmapWidth = 5;
00107 if( m_pClosedBitmap )
00108 {
00109 bitmapWidth = __MAX( m_pClosedBitmap->getWidth(), bitmapWidth );
00110 }
00111 if( m_pOpenBitmap )
00112 {
00113 bitmapWidth = __MAX( m_pOpenBitmap->getWidth(), bitmapWidth );
00114 }
00115 if( m_pItemBitmap )
00116 {
00117 bitmapWidth = __MAX( m_pItemBitmap->getWidth(), bitmapWidth );
00118 }
00119 return bitmapWidth + 2;
00120 }
00121
00122 int CtrlTree::maxItems()
00123 {
00124 const Position *pPos = getPosition();
00125 if( !pPos )
00126 {
00127 return -1;
00128 }
00129 return pPos->getHeight() / itemHeight();
00130 }
00131
00132
00133 void CtrlTree::onUpdate( Subject<VarTree> &rTree )
00134 {
00135
00136 m_lastPos = m_rTree.begin();
00137
00138 autoScroll();
00139 m_pLastSelected = NULL;
00140 }
00141
00142 void CtrlTree::onUpdate( Subject<VarPercent> &rPercent )
00143 {
00144
00145 VarTree::Iterator it = m_rTree.begin();
00146
00147 int excessItems = m_rTree.visibleItems() - maxItems();
00148
00149 if( excessItems > 0)
00150 {
00151 VarPercent &rVarPos = m_rTree.getPositionVar();
00152
00153 #ifdef _MSC_VER
00154 # define lrint (int)
00155 #endif
00156 it = m_rTree.getVisibleItem(lrint( (1.0 - rVarPos.get()) * (double)excessItems ) + 1);
00157 }
00158 if( m_lastPos != it )
00159 {
00160
00161 m_lastPos = it;
00162 makeImage();
00163 notifyLayout();
00164 }
00165 }
00166
00167 void CtrlTree::onResize()
00168 {
00169
00170
00171 VarTree::Iterator it = m_rTree.begin();
00172
00173 int excessItems = m_rTree.visibleItems() - maxItems();
00174
00175 if( excessItems > 0)
00176 {
00177 VarPercent &rVarPos = m_rTree.getPositionVar();
00178
00179 #ifdef _MSC_VER
00180 # define lrint (int)
00181 #endif
00182 it = m_rTree.getVisibleItem(lrint( (1.0 - rVarPos.get()) * (double)excessItems ) + 1);
00183 }
00184
00185 m_lastPos = it;
00186 makeImage();
00187 notifyLayout();
00188 #if 0
00189
00190 VarTree::Iterator it = m_rTree.begin();
00191
00192 int excessItems = m_rTree.visibleItems() - maxItems();
00193
00194 if( excessItems > 0)
00195 {
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208 it = m_rTree.getVisibleItem( excessItems );
00209 }
00210 makeImage();
00211 notifyLayout();
00212 #endif
00213 }
00214
00215 void CtrlTree::onPositionChange()
00216 {
00217 makeImage();
00218 notifyLayout();
00219 }
00220
00221 void CtrlTree::handleEvent( EvtGeneric &rEvent )
00222 {
00223 if( rEvent.getAsString().find( "key:down" ) != string::npos )
00224 {
00225 int key = ((EvtKey&)rEvent).getKey();
00226 VarTree::Iterator it;
00227 bool previousWasSelected = false;
00228 for( it = m_rTree.begin(); it != m_rTree.end();
00229 it = m_rTree.getNextVisibleItem( it ) )
00230 {
00231 VarTree::Iterator next = m_rTree.getNextVisibleItem( it );
00232 if( key == KEY_UP )
00233 {
00234
00235 if( ( it->parent()
00236 && it != it->parent()->begin() )
00237 || &*it != m_pLastSelected )
00238 {
00239 bool nextWasSelected = ( &*next == m_pLastSelected );
00240 it->m_selected = nextWasSelected;
00241 if( nextWasSelected )
00242 {
00243 m_pLastSelected = &*it;
00244 }
00245 }
00246 }
00247 else if( key == KEY_DOWN )
00248 {
00249
00250 if( ( it->parent()
00251 && next != it->parent()->end() )
00252 || &*it != m_pLastSelected )
00253 {
00254 (*it).m_selected = previousWasSelected;
00255 }
00256 if( previousWasSelected )
00257 {
00258 m_pLastSelected = &*it;
00259 previousWasSelected = false;
00260 }
00261 else
00262 {
00263 previousWasSelected = ( &*it == m_pLastSelected );
00264 }
00265 }
00266 else if( key == KEY_RIGHT )
00267 {
00268
00269 if( &*it == m_pLastSelected )
00270 {
00271 if( it->m_expanded )
00272 {
00273 if( it->size() )
00274 {
00275 it->m_selected = false;
00276 it->begin()->m_selected = true;
00277 m_pLastSelected = &*(it->begin());
00278 }
00279 else
00280 {
00281 m_rTree.action( &*it );
00282 }
00283 }
00284 else
00285 {
00286 it->m_expanded = true;
00287 }
00288 }
00289 }
00290 else if( key == KEY_LEFT )
00291 {
00292
00293 if( &*it == m_pLastSelected )
00294 {
00295 if( it->m_expanded && it->size() )
00296 {
00297 it->m_expanded = false;
00298 }
00299 else
00300 {
00301 if( it->parent() && it->parent() != &m_rTree)
00302 {
00303 it->m_selected = false;
00304 m_pLastSelected = it->parent();
00305 m_pLastSelected->m_selected = true;
00306 }
00307 }
00308 }
00309 }
00310 else if( key == KEY_ENTER || key == KEY_SPACE )
00311 {
00312
00313 if( &*it == m_pLastSelected )
00314 {
00315 m_rTree.action( &*it );
00316 }
00317 }
00318 }
00319
00320
00321 makeImage();
00322 notifyLayout();
00323 }
00324
00325 else if( rEvent.getAsString().find( "mouse:left" ) != string::npos )
00326 {
00327 EvtMouse &rEvtMouse = (EvtMouse&)rEvent;
00328 const Position *pos = getPosition();
00329 int yPos = ( rEvtMouse.getYPos() - pos->getTop() ) / itemHeight();
00330 int xPos = rEvtMouse.getXPos() - pos->getLeft();
00331 VarTree::Iterator it;
00332
00333 if( rEvent.getAsString().find( "mouse:left:down:ctrl,shift" ) !=
00334 string::npos )
00335 {
00336 VarTree::Iterator itClicked = findItemAtPos( yPos );
00337
00338 bool select = false;
00339 for( it = m_rTree.begin(); it != m_rTree.end();
00340 it = m_rTree.getNextVisibleItem( it ) )
00341 {
00342 bool nextSelect = select;
00343 if( it == itClicked || &*it == m_pLastSelected )
00344 {
00345 if( select )
00346 {
00347 nextSelect = false;
00348 }
00349 else
00350 {
00351 select = true;
00352 nextSelect = true;
00353 }
00354 }
00355 it->m_selected = (*it).m_selected || select;
00356 select = nextSelect;
00357 }
00358 }
00359 else if( rEvent.getAsString().find( "mouse:left:down:ctrl" ) !=
00360 string::npos )
00361 {
00362
00363 it = findItemAtPos( yPos );
00364 if( it != m_rTree.end() )
00365 {
00366 it->m_selected = !it->m_selected;
00367 m_pLastSelected = &*it;
00368 }
00369 }
00370 else if( rEvent.getAsString().find( "mouse:left:down:shift" ) !=
00371 string::npos )
00372 {
00373 VarTree::Iterator itClicked = findItemAtPos( yPos );
00374
00375 bool select = false;
00376 for( it = m_rTree.begin(); it != m_rTree.end();
00377 it = m_rTree.getNextVisibleItem( it ) )
00378 {
00379 bool nextSelect = select;
00380 if( it == itClicked || &*it == m_pLastSelected )
00381 {
00382 if( select )
00383 {
00384 nextSelect = false;
00385 }
00386 else
00387 {
00388 select = true;
00389 nextSelect = true;
00390 }
00391 }
00392 it->m_selected = select;
00393 select = nextSelect;
00394 }
00395 }
00396 else if( rEvent.getAsString().find( "mouse:left:down" ) !=
00397 string::npos )
00398 {
00399 it = findItemAtPos(yPos);
00400 if( it->size() && xPos > (it->depth() - 1) * itemImageWidth()
00401 && xPos < it->depth() * itemImageWidth() )
00402 {
00403
00404 it->m_expanded = !it->m_expanded;
00405 }
00406 else
00407 {
00408
00409 for( it = m_rTree.begin(); it != m_rTree.end();
00410 it = m_rTree.getNextVisibleItem( it ) )
00411 {
00412 it->m_selected = false;
00413 }
00414
00415 if( it != m_rTree.end() )
00416 {
00417 it->m_selected = true;
00418 m_pLastSelected = &*it;
00419 }
00420 }
00421 }
00422
00423 else if( rEvent.getAsString().find( "mouse:left:dblclick" ) !=
00424 string::npos )
00425 {
00426 it = findItemAtPos(yPos);
00427 if( it != m_rTree.end() )
00428 {
00429
00430 m_rTree.action( &*it );
00431 }
00432 }
00433
00434
00435 makeImage();
00436 notifyLayout();
00437 }
00438
00439 else if( rEvent.getAsString().find( "scroll" ) != string::npos )
00440 {
00441 int direction = ((EvtScroll&)rEvent).getDirection();
00442
00443 double percentage = m_rTree.getPositionVar().get();
00444 double step = 2.0 / (double)m_rTree.visibleItems();
00445 if( direction == EvtScroll::kUp )
00446 {
00447 percentage += step;
00448 }
00449 else
00450 {
00451 percentage -= step;
00452 }
00453 m_rTree.getPositionVar().set( percentage );
00454 }
00455 }
00456
00457 bool CtrlTree::mouseOver( int x, int y ) const
00458 {
00459 const Position *pPos = getPosition();
00460 return ( pPos
00461 ? x >= 0 && x <= pPos->getWidth() && y >= 0 && y <= pPos->getHeight()
00462 : false);
00463 }
00464
00465 void CtrlTree::draw( OSGraphics &rImage, int xDest, int yDest )
00466 {
00467 if( m_pImage )
00468 {
00469 rImage.drawGraphics( *m_pImage, 0, 0, xDest, yDest );
00470 }
00471 }
00472
00473 void CtrlTree::autoScroll()
00474 {
00475
00476 int playIndex = 0;
00477 VarTree::Iterator it;
00478 for( it = m_rTree.begin(); it != m_rTree.end();
00479 it = m_rTree.getNextVisibleItem( it ) )
00480 {
00481 if( it->m_playing ) break;
00482 playIndex++;
00483 }
00484
00485 if( it == m_rTree.end() ) return;
00486
00487
00488 int lastPosIndex = 0;
00489 for( it = m_rTree.begin(); it != m_rTree.end();
00490 it = m_rTree.getNextVisibleItem( it ) )
00491 {
00492 if( it == m_lastPos ) break;
00493 lastPosIndex++;
00494 }
00495
00496 if( it == m_rTree.end() ) return;
00497
00498
00499 if( it != m_rTree.end()
00500 && ( playIndex < lastPosIndex
00501 || playIndex > lastPosIndex + maxItems() ) )
00502 {
00503
00504 VarPercent &rVarPos = m_rTree.getPositionVar();
00505 rVarPos.set( 1.0 - (double)playIndex / (double)m_rTree.visibleItems() );
00506 }
00507 else
00508 {
00509 makeImage();
00510 notifyLayout();
00511 }
00512 }
00513
00514 void CtrlTree::makeImage()
00515 {
00516 if( m_pImage )
00517 {
00518 delete m_pImage;
00519 }
00520
00521
00522 const Position *pPos = getPosition();
00523 if( !pPos )
00524 {
00525 return;
00526 }
00527 int width = pPos->getWidth();
00528 int height = pPos->getHeight();
00529
00530 int i_itemHeight = itemHeight();
00531
00532
00533 OSFactory *pOsFactory = OSFactory::instance( getIntf() );
00534 m_pImage = pOsFactory->createOSGraphics( width, height );
00535
00536 VarTree::Iterator it = m_lastPos;
00537
00538 if( m_pBgBitmap )
00539 {
00540
00541 ScaledBitmap bmp( getIntf(), *m_pBgBitmap, width, height );
00542 m_pImage->drawBitmap( bmp, 0, 0 );
00543
00544 for( int yPos = 0; yPos < height; yPos += i_itemHeight )
00545 {
00546 if( it != m_rTree.end() )
00547 {
00548 if( (*it).m_selected )
00549 {
00550 int rectHeight = __MIN( i_itemHeight, height - yPos );
00551 m_pImage->fillRect( 0, yPos, width, rectHeight,
00552 m_selColor );
00553 }
00554 it = m_rTree.getNextVisibleItem( it );
00555 }
00556 }
00557 }
00558 else
00559 {
00560
00561
00562 uint32_t bgColor = m_bgColor1;
00563 m_pImage->fillRect( 0, 0, width, height, bgColor );
00564 for( int yPos = 0; yPos < height; yPos += i_itemHeight )
00565 {
00566 int rectHeight = __MIN( i_itemHeight, height - yPos );
00567 if( it != m_rTree.end() )
00568 {
00569 uint32_t color = ( it->m_selected ? m_selColor : bgColor );
00570 m_pImage->fillRect( 0, yPos, width, rectHeight, color );
00571 it = m_rTree.getNextVisibleItem( it );
00572 }
00573 else
00574 {
00575 m_pImage->fillRect( 0, yPos, width, rectHeight, bgColor );
00576 }
00577 bgColor = ( bgColor == m_bgColor1 ? m_bgColor2 : m_bgColor1 );
00578 }
00579 }
00580
00581
00582 int bitmapWidth = itemImageWidth();
00583
00584 int yPos = 0;
00585 it = m_lastPos;
00586 while( it != m_rTree.end() && yPos < height )
00587 {
00588 const GenericBitmap *m_pCurBitmap;
00589 UString *pStr = (UString*)(it->m_cString.get());
00590 uint32_t color = ( it->m_playing ? m_playColor : m_fgColor );
00591
00592 if( pStr != NULL )
00593 {
00594 int depth = it->depth();
00595 GenericBitmap *pText = m_rFont.drawString( *pStr, color, width - bitmapWidth * depth );
00596 if( !pText )
00597 {
00598 return;
00599 }
00600 if( it->size() )
00601 m_pCurBitmap = it->m_expanded ? m_pOpenBitmap : m_pClosedBitmap;
00602 else
00603 m_pCurBitmap = m_pItemBitmap;
00604
00605 if( m_pCurBitmap )
00606 {
00607 int yPos2 = yPos+(i_itemHeight-m_pCurBitmap->getHeight()+1)/2;
00608 m_pImage->drawBitmap( *m_pCurBitmap, 0, 0,
00609 bitmapWidth * (depth - 1 ), yPos2,
00610 m_pCurBitmap->getWidth(),
00611 __MIN( m_pCurBitmap->getHeight(),
00612 height - yPos2), true );
00613 }
00614 else
00615 {
00616
00617 }
00618 yPos += i_itemHeight - pText->getHeight();
00619 int ySrc = 0;
00620 if( yPos < 0 )
00621 {
00622 ySrc = - yPos;
00623 yPos = 0;
00624 }
00625 int lineHeight = __MIN( pText->getHeight() - ySrc, height - yPos );
00626 m_pImage->drawBitmap( *pText, 0, ySrc, bitmapWidth * depth, yPos,
00627 pText->getWidth(),
00628 lineHeight, true );
00629 yPos += (pText->getHeight() - ySrc );
00630 delete pText;
00631 }
00632 it = m_rTree.getNextVisibleItem( it );
00633 }
00634 }
00635
00636 VarTree::Iterator CtrlTree::findItemAtPos( int pos )
00637 {
00638
00639
00640 VarTree::Iterator it;
00641 for( it = m_lastPos; it != m_rTree.end() && pos != 0;
00642 it = m_rTree.getNextVisibleItem( it ) )
00643 {
00644 pos--;
00645 }
00646
00647 return it;
00648 }
00649