1 /*****************************************************************************
2 * ctrl_tree.cpp
3 *****************************************************************************
4 * Copyright (C) 2003 the VideoLAN team
5 * $Id: f851a25070e4546a181241aa6256260fd51a33c6 $
6 *
7 * Authors: Antoine Cellerier <dionoea@videolan.org>
8 * Clément Stenac <zorglub@videolan.org>
9 * Erwan Tulou <erwan10 At videolan DoT org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
25
26 #include <math.h>
27 #include "../utils/var_bool.hpp"
28 #include "ctrl_tree.hpp"
29 #include "../src/os_factory.hpp"
30 #include "../src/os_graphics.hpp"
31 #include "../src/generic_bitmap.hpp"
32 #include "../src/generic_font.hpp"
33 #include "../src/scaled_bitmap.hpp"
34 #include "../src/dialogs.hpp"
35 #include "../utils/position.hpp"
36 #include "../utils/ustring.hpp"
37 #include "../events/evt_key.hpp"
38 #include "../events/evt_mouse.hpp"
39 #include "../events/evt_scroll.hpp"
40 #include "../events/evt_dragndrop.hpp"
41 #include "../vars/playtree.hpp"
42 #include <vlc_actions.h>
43
44 #define LINE_INTERVAL 1 // Number of pixels inserted between 2 lines
45
46
CtrlTree(intf_thread_t * pIntf,VarTree & rTree,const GenericFont & rFont,const GenericBitmap * pBgBitmap,const GenericBitmap * pItemBitmap,const GenericBitmap * pOpenBitmap,const GenericBitmap * pClosedBitmap,uint32_t fgColor,uint32_t playColor,uint32_t bgColor1,uint32_t bgColor2,uint32_t selColor,const UString & rHelp,VarBool * pVisible,VarBool * pFlat)47 CtrlTree::CtrlTree( intf_thread_t *pIntf,
48 VarTree &rTree,
49 const GenericFont &rFont,
50 const GenericBitmap *pBgBitmap,
51 const GenericBitmap *pItemBitmap,
52 const GenericBitmap *pOpenBitmap,
53 const GenericBitmap *pClosedBitmap,
54 uint32_t fgColor,
55 uint32_t playColor,
56 uint32_t bgColor1,
57 uint32_t bgColor2,
58 uint32_t selColor,
59 const UString &rHelp,
60 VarBool *pVisible,
61 VarBool *pFlat ):
62 CtrlGeneric( pIntf,rHelp, pVisible), m_rTree( rTree), m_rFont( rFont ),
63 m_pBgBitmap( pBgBitmap ), m_pItemBitmap( pItemBitmap ),
64 m_pOpenBitmap( pOpenBitmap ), m_pClosedBitmap( pClosedBitmap ),
65 m_pScaledBitmap( NULL ), m_pImage( NULL ),
66 m_fgColor( fgColor ), m_playColor( playColor ),
67 m_bgColor1( bgColor1 ), m_bgColor2( bgColor2 ), m_selColor( selColor ),
68 m_firstPos( m_rTree.end() ), m_lastClicked( m_rTree.end() ),
69 m_itOver( m_rTree.end() ), m_flat( pFlat->get() ), m_capacity( -1.0 ),
70 m_bRefreshOnDelete( false )
71 {
72 // Observe the tree
73 m_rTree.addObserver( this );
74 m_rTree.setFlat( m_flat );
75 }
76
~CtrlTree()77 CtrlTree::~CtrlTree()
78 {
79 m_rTree.delObserver( this );
80 delete m_pImage;
81 delete m_pScaledBitmap;
82 }
83
itemHeight()84 int CtrlTree::itemHeight()
85 {
86 int itemHeight = m_rFont.getSize();
87 if( !m_flat )
88 {
89 if( m_pClosedBitmap )
90 {
91 itemHeight = __MAX( m_pClosedBitmap->getHeight(), itemHeight );
92 }
93 if( m_pOpenBitmap )
94 {
95 itemHeight = __MAX( m_pOpenBitmap->getHeight(), itemHeight );
96 }
97 }
98 if( m_pItemBitmap )
99 {
100 itemHeight = __MAX( m_pItemBitmap->getHeight(), itemHeight );
101 }
102 itemHeight += LINE_INTERVAL;
103 return itemHeight;
104 }
105
itemImageWidth()106 int CtrlTree::itemImageWidth()
107 {
108 int bitmapWidth = 5;
109 if( !m_flat )
110 {
111 if( m_pClosedBitmap )
112 {
113 bitmapWidth = __MAX( m_pClosedBitmap->getWidth(), bitmapWidth );
114 }
115 if( m_pOpenBitmap )
116 {
117 bitmapWidth = __MAX( m_pOpenBitmap->getWidth(), bitmapWidth );
118 }
119 }
120 if( m_pItemBitmap )
121 {
122 bitmapWidth = __MAX( m_pItemBitmap->getWidth(), bitmapWidth );
123 }
124 return bitmapWidth + 2;
125 }
126
maxItems()127 float CtrlTree::maxItems()
128 {
129 const Position *pPos = getPosition();
130 if( !pPos )
131 {
132 return -1;
133 }
134 return (float)pPos->getHeight() / itemHeight();
135 }
136
onUpdate(Subject<VarTree,tree_update> & rTree,tree_update * arg)137 void CtrlTree::onUpdate( Subject<VarTree, tree_update> &rTree,
138 tree_update *arg )
139 {
140 (void)rTree;
141 if( arg->type == arg->ItemInserted )
142 {
143 if( isItemVisible( arg->it ) )
144 {
145 makeImage();
146 notifyLayout();
147 }
148 setSliderFromFirst();
149 }
150 else if( arg->type == arg->ItemUpdated )
151 {
152 if( arg->it->isPlaying() )
153 {
154 m_rTree.ensureExpanded( arg->it );
155 ensureVisible( arg->it );
156
157 makeImage();
158 notifyLayout();
159 setSliderFromFirst();
160 }
161 else if( isItemVisible( arg->it ) )
162 {
163 makeImage();
164 notifyLayout();
165 }
166 }
167 else if( arg->type == arg->DeletingItem )
168 {
169 if( isItemVisible( arg->it ) )
170 m_bRefreshOnDelete = true;
171 // remove all references to arg->it
172 // if it is the one about to be deleted
173 if( m_firstPos == arg->it )
174 {
175 m_firstPos = getNearestItem( arg->it );
176 }
177 if( m_lastClicked == arg->it )
178 {
179 m_lastClicked = getNearestItem( arg->it );
180 m_lastClicked->setSelected( arg->it->isSelected() );
181 }
182 }
183 else if( arg->type == arg->ItemDeleted )
184 {
185 if( m_bRefreshOnDelete )
186 {
187 m_bRefreshOnDelete = false;
188
189 makeImage();
190 notifyLayout();
191 }
192 setSliderFromFirst();
193 }
194 else if( arg->type == arg->ResetAll )
195 {
196 m_lastClicked = m_rTree.end();
197 m_firstPos = getFirstFromSlider();
198
199 makeImage();
200 notifyLayout();
201 setSliderFromFirst();
202 }
203 else if( arg->type == arg->SliderChanged )
204 {
205 Iterator it = getFirstFromSlider();
206 if( m_firstPos != it )
207 {
208 m_firstPos = it;
209 makeImage();
210 notifyLayout();
211 }
212 }
213 }
214
onResize()215 void CtrlTree::onResize()
216 {
217 onPositionChange();
218 }
219
onPositionChange()220 void CtrlTree::onPositionChange()
221 {
222 m_capacity = maxItems();
223 setScrollStep();
224 m_firstPos = getFirstFromSlider();
225 makeImage();
226 }
227
handleEvent(EvtGeneric & rEvent)228 void CtrlTree::handleEvent( EvtGeneric &rEvent )
229 {
230 bool needShow = false;
231 bool needRefresh = false;
232 Iterator toShow = m_firstPos;
233 if( rEvent.getAsString().find( "key:down" ) != std::string::npos )
234 {
235 int key = ((EvtKey&)rEvent).getKey();
236
237 /* Delete the selection */
238 if( key == KEY_DELETE )
239 {
240 /* Delete selected stuff */
241 m_rTree.delSelected();
242 }
243 else if( key == KEY_PAGEDOWN )
244 {
245 int numSteps = (int)m_capacity / 2;
246 VarPercent &rVarPos = m_rTree.getPositionVar();
247 rVarPos.increment( -numSteps );
248 }
249 else if( key == KEY_PAGEUP )
250 {
251 int numSteps = (int)m_capacity / 2;
252 VarPercent &rVarPos = m_rTree.getPositionVar();
253 rVarPos.increment( numSteps );
254 }
255 else if( key == KEY_UP )
256 {
257 // Scroll up one item
258 m_rTree.unselectTree();
259 if( m_lastClicked != m_rTree.end() )
260 {
261 if( --m_lastClicked != m_rTree.end() )
262 {
263 m_lastClicked->setSelected( true );
264 }
265 }
266 if( m_lastClicked == m_rTree.end() )
267 {
268 m_lastClicked = m_firstPos;
269 if( m_lastClicked != m_rTree.end() )
270 m_lastClicked->setSelected( true );
271 }
272 needRefresh = true;
273 needShow = true; toShow = m_lastClicked;
274 }
275 else if( key == KEY_DOWN )
276 {
277 // Scroll down one item
278 m_rTree.unselectTree();
279 if( m_lastClicked != m_rTree.end() )
280 {
281 Iterator it_old = m_lastClicked;
282 if( ++m_lastClicked != m_rTree.end() )
283 {
284 m_lastClicked->setSelected( true );
285 }
286 else
287 {
288 it_old->setSelected( true );
289 m_lastClicked = it_old;
290 }
291 }
292 else
293 {
294 m_lastClicked = m_firstPos;
295 if( m_lastClicked != m_rTree.end() )
296 m_lastClicked->setSelected( true );
297 }
298 needRefresh = true;
299 needShow = true; toShow = m_lastClicked;
300 }
301 else if( key == KEY_RIGHT )
302 {
303 // Go down one level (and expand node)
304 Iterator& it = m_lastClicked;
305 if( it != m_rTree.end() )
306 {
307 if( !m_flat && !it->isExpanded() && it->size() )
308 {
309 it->setExpanded( true );
310 needRefresh = true;
311 }
312 else
313 {
314 m_rTree.unselectTree();
315 Iterator it_old = m_lastClicked;
316 if( ++m_lastClicked != m_rTree.end() )
317 {
318 m_lastClicked->setSelected( true );
319 }
320 else
321 {
322 it_old->setSelected( true );
323 m_lastClicked = it_old;
324 }
325 needRefresh = true;
326 needShow = true; toShow = m_lastClicked;
327 }
328 }
329 }
330 else if( key == KEY_LEFT )
331 {
332 // Go up one level (and close node)
333 Iterator& it = m_lastClicked;
334 if( it != m_rTree.end() )
335 {
336 if( m_flat )
337 {
338 m_rTree.unselectTree();
339 if( --m_lastClicked != m_rTree.end() )
340 {
341 m_lastClicked->setSelected( true );
342 }
343 else
344 {
345 m_lastClicked = m_firstPos;
346 if( m_lastClicked != m_rTree.end() )
347 m_lastClicked->setSelected( true );
348 }
349 needRefresh = true;
350 needShow = true; toShow = m_lastClicked;
351 }
352 else
353 {
354 if( it->isExpanded() )
355 {
356 it->setExpanded( false );
357 needRefresh = true;
358 }
359 else
360 {
361 Iterator it_parent = it.getParent();
362 if( it_parent != m_rTree.end() )
363 {
364 it->setSelected( false );
365 m_lastClicked = it_parent;
366 m_lastClicked->setSelected( true );
367 needRefresh = true;
368 needShow = true; toShow = m_lastClicked;
369 }
370 }
371 }
372 }
373 }
374 else if( key == KEY_ENTER || key == ' ' )
375 {
376 // Go up one level (and close node)
377 if( m_lastClicked != m_rTree.end() )
378 {
379 m_rTree.action( &*m_lastClicked );
380 }
381 }
382 else
383 {
384 // other keys to be forwarded to vlc core
385 EvtKey& rEvtKey = (EvtKey&)rEvent;
386 getIntf()->p_sys->p_dialogs->sendKey( rEvtKey.getModKey() );
387 }
388 }
389
390 else if( rEvent.getAsString().find( "mouse:left" ) != std::string::npos )
391 {
392 EvtMouse &rEvtMouse = (EvtMouse&)rEvent;
393 const Position *pos = getPosition();
394 int xPos = rEvtMouse.getXPos() - pos->getLeft();
395 int yPos = ( rEvtMouse.getYPos() - pos->getTop() ) / itemHeight();
396
397 Iterator itClicked = findItemAtPos( yPos );
398 if( itClicked != m_rTree.end() )
399 {
400 if( rEvent.getAsString().find( "mouse:left:down:ctrl,shift" ) !=
401 std::string::npos )
402 {
403 // Flag to know if the current item must be selected
404 bool select = false;
405 for( Iterator it = m_firstPos; it != m_rTree.end(); ++it )
406 {
407 bool nextSelect = select;
408 if( it == itClicked || it == m_lastClicked )
409 {
410 if( select )
411 {
412 nextSelect = false;
413 }
414 else
415 {
416 select = true;
417 if( itClicked != m_lastClicked )
418 nextSelect = true;
419 }
420 }
421 it->setSelected( it->isSelected() || select );
422 select = nextSelect;
423 needRefresh = true;
424 }
425 }
426 else if( rEvent.getAsString().find( "mouse:left:down:ctrl" ) !=
427 std::string::npos )
428 {
429 // Invert the selection of the item
430 itClicked->toggleSelected();
431 m_lastClicked = itClicked;
432 needRefresh = true;
433 }
434 else if( rEvent.getAsString().find( "mouse:left:down:shift" ) !=
435 std::string::npos )
436 {
437 bool select = false;
438 for( Iterator it = m_firstPos; it != m_rTree.end(); ++it )
439 {
440 bool nextSelect = select;
441 if( it == itClicked || it == m_lastClicked )
442 {
443 if( select )
444 {
445 nextSelect = false;
446 }
447 else
448 {
449 select = true;
450 if( itClicked != m_lastClicked )
451 nextSelect = true;
452 }
453 }
454 it->setSelected( select );
455 select = nextSelect;
456 }
457 needRefresh = true;
458 }
459 else if( rEvent.getAsString().find( "mouse:left:down" ) !=
460 std::string::npos )
461 {
462 if( !m_flat &&
463 itClicked->size() &&
464 xPos > (itClicked->depth() - 1) * itemImageWidth() &&
465 xPos < itClicked->depth() * itemImageWidth() )
466 {
467 // Fold/unfold the item
468 itClicked->toggleExpanded();
469 }
470 else
471 {
472 // Unselect any previously selected item
473 m_rTree.unselectTree();
474 // Select the new item
475 itClicked->setSelected( true );
476 m_lastClicked = itClicked;
477 }
478 needRefresh = true;
479 }
480 else if( rEvent.getAsString().find( "mouse:left:dblclick" ) !=
481 std::string::npos )
482 {
483 // Execute the action associated to this item
484 m_rTree.action( &*itClicked );
485 }
486 }
487 }
488
489 else if( rEvent.getAsString().find( "scroll" ) != std::string::npos )
490 {
491 int direction = static_cast<EvtScroll&>(rEvent).getDirection();
492 if( direction == EvtScroll::kUp )
493 m_rTree.getPositionVar().increment( +1 );
494 else
495 m_rTree.getPositionVar().increment( -1 );
496 }
497
498 else if( rEvent.getAsString().find( "drag:over" ) != std::string::npos )
499 {
500 EvtDragOver& evt = static_cast<EvtDragOver&>(rEvent);
501 const Position *pos = getPosition();
502 int yPos = ( evt.getYPos() - pos->getTop() ) / itemHeight();
503
504 Iterator it = findItemAtPos( yPos );
505 if( it != m_itOver )
506 {
507 m_itOver = it;
508 needRefresh = true;
509 }
510 }
511
512 else if( rEvent.getAsString().find( "drag:drop" ) != std::string::npos )
513 {
514 EvtDragDrop& evt = static_cast<EvtDragDrop&>(rEvent);
515 Playtree& rPlaytree = static_cast<Playtree&>(m_rTree);
516 VarTree& item = ( m_itOver != m_rTree.end() ) ? *m_itOver : m_rTree;
517 rPlaytree.insertItems( item, evt.getFiles(), false );
518 m_itOver = m_rTree.end();
519 needRefresh = true;
520 }
521
522 else if( rEvent.getAsString().find( "drag:leave" ) != std::string::npos )
523 {
524 m_itOver = m_rTree.end();
525 needRefresh = true;
526 }
527
528 if( needShow )
529 {
530 if( toShow == m_rTree.end() ||
531 !ensureVisible( toShow ) )
532 needRefresh = true;
533 }
534 if( needRefresh )
535 {
536 setSliderFromFirst();
537
538 makeImage();
539 notifyLayout();
540 }
541 }
542
mouseOver(int x,int y) const543 bool CtrlTree::mouseOver( int x, int y ) const
544 {
545 const Position *pPos = getPosition();
546 return !pPos ? false :
547 x >= 0 && x <= pPos->getWidth() && y >= 0 && y <= pPos->getHeight();
548 }
549
draw(OSGraphics & rImage,int xDest,int yDest,int w,int h)550 void CtrlTree::draw( OSGraphics &rImage, int xDest, int yDest, int w, int h)
551 {
552 const Position *pPos = getPosition();
553 rect region( pPos->getLeft(), pPos->getTop(),
554 pPos->getWidth(), pPos->getHeight() );
555 rect clip( xDest, yDest, w, h );
556 rect inter;
557
558 if( rect::intersect( region, clip, &inter ) && m_pImage )
559 rImage.drawGraphics( *m_pImage,
560 inter.x - pPos->getLeft(),
561 inter.y - pPos->getTop(),
562 inter.x, inter.y, inter.width, inter.height );
563 }
564
makeImage()565 void CtrlTree::makeImage()
566 {
567 delete m_pImage;
568
569 // Get the size of the control
570 const Position *pPos = getPosition();
571 if( !pPos )
572 return;
573 int width = pPos->getWidth();
574 int height = pPos->getHeight();
575
576 int i_itemHeight = itemHeight();
577
578 // Create an image
579 OSFactory *pOsFactory = OSFactory::instance( getIntf() );
580 m_pImage = pOsFactory->createOSGraphics( width, height );
581
582 Iterator it = m_firstPos;
583
584 if( m_pBgBitmap )
585 {
586 // Draw the background bitmap
587 if( !m_pScaledBitmap ||
588 m_pScaledBitmap->getWidth() != width ||
589 m_pScaledBitmap->getHeight() != height )
590 {
591 delete m_pScaledBitmap;
592 m_pScaledBitmap =
593 new ScaledBitmap( getIntf(), *m_pBgBitmap, width, height );
594 }
595 m_pImage->drawBitmap( *m_pScaledBitmap, 0, 0 );
596
597 for( int yPos = 0;
598 yPos < height && it != m_rTree.end();
599 yPos += i_itemHeight, ++it )
600 {
601 if( it->isSelected() )
602 {
603 int rectHeight = __MIN( i_itemHeight, height - yPos );
604 m_pImage->fillRect( 0, yPos, width, rectHeight, m_selColor );
605 }
606 }
607 }
608 else
609 {
610 // Fill background with background color
611 uint32_t bgColor = m_bgColor1;
612 m_pImage->fillRect( 0, 0, width, height, bgColor );
613 // Overwrite with alternate colors (bgColor1, bgColor2)
614 for( int yPos = 0; yPos < height; yPos += i_itemHeight )
615 {
616 int rectHeight = __MIN( i_itemHeight, height - yPos );
617 if( it == m_rTree.end() )
618 m_pImage->fillRect( 0, yPos, width, rectHeight, bgColor );
619 else
620 {
621 uint32_t color = ( it->isSelected() ? m_selColor : bgColor );
622 m_pImage->fillRect( 0, yPos, width, rectHeight, color );
623 ++it;
624 }
625 bgColor = ( bgColor == m_bgColor1 ? m_bgColor2 : m_bgColor1 );
626 }
627 }
628
629 int bitmapWidth = itemImageWidth();
630
631 it = m_firstPos;
632 for( int yPos = 0; yPos < height && it != m_rTree.end(); ++it )
633 {
634 const GenericBitmap *m_pCurBitmap;
635 UString *pStr = it->getString();
636 if( pStr != NULL )
637 {
638 uint32_t color = it->isPlaying() ? m_playColor : m_fgColor;
639 int depth = m_flat ? 1 : it->depth();
640 GenericBitmap *pText =
641 m_rFont.drawString( *pStr, color, width-bitmapWidth*depth );
642 if( !pText )
643 {
644 return;
645 }
646 if( it->size() )
647 m_pCurBitmap =
648 it->isExpanded() ? m_pOpenBitmap : m_pClosedBitmap;
649 else
650 m_pCurBitmap = m_pItemBitmap;
651
652 if( m_pCurBitmap )
653 {
654 // Make sure we are centered on the line
655 int yPos2 = yPos+(i_itemHeight-m_pCurBitmap->getHeight()+1)/2;
656 if( yPos2 >= height )
657 {
658 delete pText;
659 break;
660 }
661 // Draw the icon in front of the text
662 m_pImage->drawBitmap( *m_pCurBitmap, 0, 0,
663 bitmapWidth * (depth - 1 ), yPos2,
664 m_pCurBitmap->getWidth(),
665 __MIN( m_pCurBitmap->getHeight(),
666 height - yPos2), true );
667 }
668 yPos += (i_itemHeight - pText->getHeight());
669 if( yPos >= height )
670 {
671 delete pText;
672 break;
673 }
674
675 int ySrc = 0;
676 if( yPos < 0 )
677 {
678 ySrc = - yPos;
679 yPos = 0;
680 }
681 int lineHeight = __MIN( pText->getHeight() - ySrc, height - yPos );
682 // Draw the text
683 m_pImage->drawBitmap( *pText, 0, ySrc, bitmapWidth * depth, yPos,
684 pText->getWidth(),
685 lineHeight, true );
686 yPos += (pText->getHeight() - ySrc );
687
688 if( it == m_itOver )
689 {
690 // Draw the underline bar below the text for drag&drop
691 m_pImage->fillRect(
692 bitmapWidth * (depth - 1 ), yPos - 2,
693 bitmapWidth + pText->getWidth(), __MAX( lineHeight/5, 3 ),
694 m_selColor );
695 }
696 delete pText;
697 }
698 }
699 }
700
findItemAtPos(int pos)701 CtrlTree::Iterator CtrlTree::findItemAtPos( int pos )
702 {
703 // The first item is m_firstPos.
704 // We decrement pos as we try the other items, until pos == 0.
705 Iterator it = m_firstPos;
706 for( ; it != m_rTree.end() && pos != 0; ++it, pos-- );
707
708 return it;
709 }
710
getFirstFromSlider()711 CtrlTree::Iterator CtrlTree::getFirstFromSlider()
712 {
713 // a simple (int)(...) causes rounding errors !
714 #ifdef _MSC_VER
715 # define lrint (int)
716 #endif
717 VarPercent &rVarPos = m_rTree.getPositionVar();
718 double percentage = rVarPos.get();
719
720 int excessItems = m_flat ? (m_rTree.countLeafs() - (int)m_capacity)
721 : (m_rTree.visibleItems() - (int)m_capacity);
722
723 int index = (excessItems > 0 ) ?
724 lrint( (1.0 - percentage)*(double)excessItems ) :
725 0;
726
727 Iterator it_first = m_rTree.getItem( index );
728
729 return it_first;
730 }
731
setScrollStep()732 void CtrlTree::setScrollStep()
733 {
734 VarPercent &rVarPos = m_rTree.getPositionVar();
735
736 int excessItems = m_flat ? (m_rTree.countLeafs() - (int)m_capacity)
737 : (m_rTree.visibleItems() - (int)m_capacity);
738
739 if( excessItems > 0 )
740 rVarPos.setStep( (float)1 / excessItems );
741 else
742 rVarPos.setStep( 1.0 );
743 }
744
setSliderFromFirst()745 void CtrlTree::setSliderFromFirst()
746 {
747 VarPercent &rVarPos = m_rTree.getPositionVar();
748
749 int excessItems = m_flat ? (m_rTree.countLeafs() - (int)m_capacity)
750 : (m_rTree.visibleItems() - (int)m_capacity);
751
752 int index = m_rTree.getIndex( m_firstPos );
753 if( excessItems > 0 )
754 {
755 rVarPos.set( 1.0 - (float)index/(float)excessItems );
756 rVarPos.setStep( 1.0 / excessItems );
757 }
758 else
759 {
760 rVarPos.set( 1.0 );
761 rVarPos.setStep( 1.0 );
762 }
763 }
764
isItemVisible(const Iterator & it_ref)765 bool CtrlTree::isItemVisible( const Iterator& it_ref )
766 {
767 if( it_ref == m_rTree.end() )
768 return false;
769
770 Iterator it = m_firstPos;
771 if( it == m_rTree.end() )
772 return true;
773
774 // Ensure a partially visible last item is taken into account
775 int max = (int)m_capacity;
776 if( (float)max < m_capacity )
777 max++;
778
779 for( int i = 0; i < max && it != m_rTree.end(); ++it, i++ )
780 {
781 if( it == it_ref )
782 return true;
783 }
784 return false;
785 }
786
ensureVisible(const Iterator & item)787 bool CtrlTree::ensureVisible( const Iterator& item )
788 {
789 Iterator it = m_firstPos;
790 int max = (int)m_capacity;
791 for( int i = 0; i < max && it != m_rTree.end(); ++it, i++ )
792 {
793 if( it == item )
794 return false;
795 }
796
797 m_rTree.setSliderFromItem( item );
798 return true;
799 }
800
getNearestItem(const Iterator & item)801 CtrlTree::Iterator CtrlTree::getNearestItem( const Iterator& item )
802 {
803 // return the previous item if it exists
804 Iterator newItem = item;
805 if( --newItem != m_rTree.end() && newItem != item )
806 return newItem;
807
808 // return the next item if no previous item found
809 newItem = item;
810 return ++newItem;
811 }
812