Changeset 890

Show
Ignore:
Timestamp:
07/11/07 11:16:47 (1 year ago)
Author:
ok_computer
Message:

Adding multiple sub-range support to timeline.

Location:
trunk
Files:
1 added
7 modified

Legend:

Unmodified
Added
Removed
  • trunk/jahplayer/control.py

    r511 r890  
    139139                # In/out point handling 
    140140                self.ignore_points = False 
    141                 self.in_point = utilities.create_property_and_attach( self.handle, "timeline:inValue", utilities.call_method_observer( self.in_changed ) ) 
    142                 self.out_point = utilities.create_property_and_attach( self.handle, "timeline:outValue", utilities.call_method_observer( self.out_changed ) ) 
     141                self.in_point = utilities.create_property_and_attach( self.handle, "timeline:activeInValue", utilities.call_method_observer( self.in_changed ) ) 
     142                self.out_point = utilities.create_property_and_attach( self.handle, "timeline:activeOutValue", utilities.call_method_observer( self.out_changed ) ) 
    143143                self.in_prop = utilities.create_property_and_attach( self.handle, "pb_in:clicked", utilities.call_method_observer( self.set_in ) ) 
    144144                self.out_prop = utilities.create_property_and_attach( self.handle, "pb_out:clicked", utilities.call_method_observer( self.set_out ) ) 
  • trunk/jahwidgets/src/qt3/python/py.cpp

    r833 r890  
    4040extern void register_scrollview(); 
    4141extern void register_settings(); 
     42extern void register_timeline(); 
    4243 
    4344#ifdef MAC_OSX 
     
    7980        register_scrollview(); 
    8081        register_settings(); 
     82        register_timeline(); 
    8183         
    8284#ifdef MAC_OSX 
  • trunk/jahwidgets/src/qt3/python/python.pro

    r833 r890  
    6565                   messagebox.cpp \ 
    6666                   scrollview.cpp \ 
    67                    settings.cpp 
     67                   settings.cpp \ 
     68                   timeline.cpp 
    6869 
    6970mac: SOURCES += macutils.cpp 
  • trunk/jahwidgets/src/qt3/python/python_vc80.vcproj

    r865 r890  
    287287                        </File> 
    288288                        <File 
     289                                RelativePath=".\timeline.cpp" 
     290                                > 
     291                        </File> 
     292                        <File 
    289293                                RelativePath=".\splitter.cpp" 
    290294                                > 
  • trunk/jahwidgets/src/qt3/widgets/timelineSlider.cpp

    r849 r890  
    55// Released under the GPL. 
    66// For more information, see http://www.jahshaka.org. 
    7  
    8 // Custom geometry classes, with minimal interfaces. 
    97 
    108#include "timelineSlider.h" 
     
    2624// std 
    2725#include <algorithm> 
     26#include <set> 
     27#include <memory> 
     28 
     29// boost 
     30#include <boost/shared_ptr.hpp> 
    2831 
    2932namespace jahwidgets { namespace qt3 { 
     
    3336public: 
    3437        IconSet() : 
    35                 _isActive(false) 
     38                _isActive(false), 
     39                _isSelected(false) 
    3640                {} 
    3741 
    3842        QPixmap pixmap() const { 
    39                 return (_isActive && !active.isNull()) ? active : normal; 
     43                if ( _isActive && !active.isNull() ) { 
     44                        return active; 
     45                } 
     46                else if ( _isSelected && !selected.isNull() ) { 
     47                        return selected; 
     48                } 
     49                else { 
     50                        return normal; 
     51                } 
    4052        } 
    4153 
     
    5567        } 
    5668 
     69        void setSelected( bool s ) { 
     70                _isSelected = s; 
     71        } 
     72 
     73        bool isSelected() const { 
     74                return _isSelected; 
     75        } 
     76 
    5777        QPixmap normal; 
    5878        QPixmap active; 
     79        QPixmap selected; 
    5980 
    6081private: 
    6182        bool _isActive; 
     83        bool _isSelected; 
    6284}; 
    6385 
    64 struct TimelineSlider::PrivateTimelineSlider 
     86int TimelineSlider::Range::id_count = 0; 
     87 
     88typedef boost::shared_ptr<TimelineSlider::Range> RangePtr; 
     89 
     90struct RangeCompare 
     91{ 
     92        bool operator()( RangePtr lhs, RangePtr rhs ) const 
     93                { 
     94                        // Ordered by in-point - or out point if in points are equal 
     95                        return lhs->in < rhs->in || (lhs->in == rhs->in && lhs->out < rhs->out); 
     96                } 
     97}; 
     98 
     99// We use a multiset because it is legal to contain sets with duplicate in points 
     100typedef std::multiset<RangePtr, RangeCompare> RangeSet; 
     101 
     102struct RangeIdentity 
     103{ 
     104public: 
     105        RangeIdentity( int an_id ) : id(an_id) {} 
     106 
     107        bool operator()( RangePtr range ) const { 
     108                return range->id() == id; 
     109        } 
     110 
     111        int id; 
     112}; 
     113 
     114struct RangeContains 
     115{ 
     116public: 
     117        RangeContains( int an_pt ) : pt(an_pt) {} 
     118 
     119        bool operator()( RangePtr range ) const { 
     120                return range->contains( pt ); 
     121        } 
     122 
     123        int pt; 
     124}; 
     125 
     126struct TimelineSlider::PrivateTimeline 
    65127{ 
    66128        enum Tracking { NONE, VALUE, IN, OUT }; 
    67129 
    68         PrivateTimelineSlider( TimelineSlider* p ) 
     130        PrivateTimeline( TimelineSlider* p ) 
    69131                : parent( p ), 
    70132                  min( 0 ), 
    71133                  max( 1000 ), 
    72134                  v( 0 ), 
    73                   inValue( min ), 
    74                   outValue( max ), 
     135                  thumbnailFrame( -1 ), 
    75136                  showInOut( true ), 
    76137                  fps( 30 ), 
     
    81142                  showTimecodes( false ), 
    82143                  showTicks( false ) 
    83                 {} 
    84  
    85         int clamp( int i ) 
     144                { 
     145                        rangeContainer.insert( RangePtr( Range::create( min, max ) ) ); 
     146                        activeRange = *rangeContainer.begin(); 
     147                } 
     148 
     149        int clamp( int i ) const 
    86150        { 
    87151                return std::max( std::min( i, max ), min ); 
    88152        } 
    89153 
    90         int inClamp( int i ) 
    91         { 
    92                 return std::max( std::min( i, outValue ), min ); 
    93         } 
    94  
    95         int outClamp( int i ) 
    96         { 
    97                 return std::max( std::min( i, max ), inValue ); 
    98         } 
    99  
    100         void updateValue() 
    101         { 
    102                 // ensure min and max are the right way around... 
    103                 if ( min > max ) 
    104                 { 
    105                         std::swap( min, max ); 
    106                 } 
    107  
    108                 // clamp value to range. Perfer in-point over out-point 
    109                 int nv = inClamp( outClamp( clamp( v ) ) ); 
    110                 if ( nv != v ) 
    111                 { 
    112                         v = nv; 
    113                         emit parent->valueChanged( v ); 
    114                         emit parent->value_changed(); 
    115                 } 
     154        int inClamp( int i ) const 
     155        { 
     156                i = std::max( std::min( i, activeRange->out ), min ); 
     157 
     158                // And clamp above neighbouring out point 
     159                for ( RangeSet::const_iterator I = rangeContainer.begin(); 
     160                          I != rangeContainer.end(); 
     161                          ++I )  
     162                { 
     163                        if ( *I == activeRange ) continue; 
     164 
     165                        if ( (*I)->out > activeRange->out ) break; 
     166 
     167                        if ( i <= (*I)->out ) { 
     168                                i = (*I)->out + 1; 
     169                                break; 
     170                        } 
     171                } 
     172 
     173                return i; 
     174        } 
     175 
     176        int outClamp( int i ) const 
     177        { 
     178                i = std::max( std::min( i, max ), activeRange->in ); 
     179 
     180                // And clamp below neighbouring in point 
     181                for ( RangeSet::const_reverse_iterator I = rangeContainer.rbegin(); 
     182                          I != rangeContainer.rend(); 
     183                          ++I )  
     184                { 
     185                        if ( *I == activeRange ) continue; 
     186 
     187                        if ( (*I)->out < activeRange->in ) break; 
     188 
     189                        if ( i >= (*I)->in ) { 
     190                                i = (*I)->in - 1; 
     191                                break; 
     192                        } 
     193                } 
     194 
     195                return i; 
    116196        } 
    117197 
    118198        int convertToValue( int pt ) const 
    119199        { 
    120                 return min + (int)( length() * ((double)(pt - xoffset())/trackLength()) ); 
     200                return clamp( min + (int)( length() * ((double)(pt - xoffset())/trackLength()) ) ); 
    121201        } 
    122202 
     
    137217        { 
    138218                // Should include the yoffset(), but this interacts badly with the outRect() offset... 
    139                 int width = parent->width() - xoffset(); // - yoffset(); 
     219                int width = parent->width() - xoffset() - (outThumb.normal.isNull() ? 0 : outThumb.normal.width() / 2); 
    140220                // Ensure it's a multiple of the thumb width 
    141                 return width - width % thumbWidth(); 
     221                return width; // - width % thumbWidth(); 
    142222        } 
    143223 
    144224        int xoffset() const  
    145225        { 
    146                 return inThumb.normal.isNull() ? 0 : inThumb.normal.width(); 
    147         } 
    148  
    149         int yoffset() const  
    150         { 
    151                 return outThumb.normal.isNull() ? 0 : outThumb.normal.width(); 
     226                return inThumb.normal.isNull() ? 0 : inThumb.normal.width() / 2; 
    152227        } 
    153228 
     
    159234        QRect inRect() const 
    160235        { 
    161                 int v = convertToPixel( inValue ); 
     236                // center in and out rects horizontally to allow for various marker positions 
     237                int v = convertToPixel( activeRange->in ); 
    162238                QSize size = inThumb.normal.isNull() ? inOutSize( parent->height() ) : inThumb.normal.size(); 
    163                 return QRect( QPoint( v - size.width(), (parent->height() - size.height())/2 ), size ); 
     239                return QRect( QPoint( v - size.width() / 2, (parent->height() - size.height())/2 ), size ); 
    164240        } 
    165241 
    166242        QRect outRect() const 
    167243        { 
     244                // center in and out rects horizontally to allow for various marker positions 
    168245                QSize size = outThumb.normal.isNull() ? inOutSize( parent->height() ) : outThumb.normal.size(); 
    169246                int x; 
    170                 if ( outValue != inValue ) 
    171                         x = convertToPixel( outValue ); 
     247                if ( activeRange->out != activeRange->in ) 
     248                        x = convertToPixel( activeRange->out ) - size.width() / 2; 
    172249                else 
    173                         x = parent->width() - size.width(); 
     250                        x = parent->width() - size.width() / 2; 
    174251 
    175252                // x should be v + size.width(), but this interacts badly when trackLength() is calculated correctly... 
     
    177254        } 
    178255 
     256        QRect thumbnailMarkerRect() const  
     257        { 
     258                if ( thumbnailFrame == -1 || thumbnailMarker.normal.isNull() ) return QRect(); 
     259 
     260                QSize size = thumbnailMarker.normal.size(); 
     261                int markerPos = convertToPixel( thumbnailFrame ); 
     262                return QRect( markerPos - size.width() / 2, (parent->height() - size.height()) / 2,  
     263                                          size.width(), size.height() ); 
     264        } 
     265 
    179266        QRect currentMarkerRect() const 
    180267        { 
     
    186273                return QRect( convertToPixel( v ) - width/2, (parent->height()-thumb.normal.height())/2, 
    187274                                          width, thumb.normal.height() ); 
     275        } 
     276 
     277        bool hitInRect( QPoint pos ) const 
     278        { 
     279                // We've got to be in the thumb rect and outside the currentMarkerRect region 
     280                if ( inRect().contains( pos ) == false ) return false; 
     281 
     282                // else check the marker rect 
     283                QRect markerRect = currentMarkerRect(); 
     284                return (pos.y() > markerRect.bottom() || pos.y() < markerRect.top()); 
     285        } 
     286 
     287        bool hitOutRect( QPoint pos ) const 
     288        { 
     289                // We've got to be in the thumb rect and outside the currentMarkerRect region 
     290                if ( outRect().contains( pos ) == false ) return false; 
     291 
     292                // else check the marker rect 
     293                QRect markerRect = currentMarkerRect(); 
     294                return (pos.y() > markerRect.bottom() || pos.y() < markerRect.top()); 
    188295        } 
    189296 
     
    232339        } 
    233340 
     341        void paintTray()  
     342        { 
     343                buffer = QPixmap(); 
     344                tray_full.resize( parent->size() ); 
     345 
     346                if ( !tray.isNull() ) 
     347                { 
     348                        // assume we can split in the middle and stretch 
     349                        // pixmap should always be horizontal; we will rotate  
     350                        // for vertical sliders 
     351                        const int half_width = tray.width()/2; 
     352                        const int height = tray.height(); 
     353                 
     354                        QPixmap filler( 1, height ); 
     355                 
     356                        // get filler 
     357                        copyBlt( &filler, 0, 0, &tray, half_width, 0, 1, height ); 
     358                 
     359                        // work out how much filler we need 
     360                        const int fillerDistance = parent->width() - 2*half_width; 
     361                 
     362                        // get left 
     363                        copyBlt( &tray_full, 0, 0, &tray, 0, 0, half_width, height ); 
     364                 
     365                        // filler 
     366                        int i = 0; 
     367                        while ( i < fillerDistance ) 
     368                        { 
     369                                copyBlt( &tray_full, half_width + i++, 0, &filler, 0, 0, 1, height ); 
     370                        } 
     371                 
     372                        // right 
     373                        copyBlt( &tray_full, half_width + fillerDistance, 0,  
     374                                         &tray, half_width, 0, half_width, height ); 
     375                } 
     376                else 
     377                { 
     378                        tray_full.fill( parent, 0, 0 ); 
     379                } 
     380 
     381                // And the full duration/elapsed pixmaps 
     382                if ( !duration.isNull() ) 
     383                { 
     384                        const int h = duration.height(); 
     385                        const int w = parent->width(); 
     386                        duration_full.resize( w, h ); 
     387                        copyBlt( &duration_full, 0, 0, 
     388                                         &tray_full, 0, (parent->height()-h)/2, w, h ); 
     389 
     390                        QPainter p( &duration_full ); 
     391                        p.drawTiledPixmap( 0, 0, w, h, duration ); 
     392                } 
     393 
     394                if ( !elapsed.isNull() ) 
     395                { 
     396                        const int h = elapsed.height(); 
     397                        const int w = parent->width(); 
     398                        elapsed_full.resize( w, h ); 
     399                        copyBlt( &elapsed_full, 0, 0, 
     400                                         &tray_full, 0, (parent->height()-h)/2, w, h ); 
     401 
     402                        QPainter p( &elapsed_full ); 
     403                        p.drawTiledPixmap( 0, 0, w, h, elapsed ); 
     404                } 
     405        } 
     406 
    234407        TimelineSlider* parent; 
    235408 
     
    239412        int v; 
    240413 
     414        int thumbnailFrame; 
     415 
    241416        // in, out are in frames also 
    242         int inValue; 
    243         int outValue; 
     417        RangeSet rangeContainer; 
     418        RangePtr activeRange; 
     419 
    244420        bool showInOut; 
    245421 
     
    267443        IconSet inThumb; 
    268444        IconSet outThumb; 
     445        IconSet thumbnailMarker; 
    269446        QPixmap duration; 
    270447        QPixmap elapsed; 
     
    278455        // for drawing 
    279456        QPixmap buffer; 
     457 
     458        std::auto_ptr<Delegate> delegate; 
    280459}; 
    281460 
    282461TimelineSlider::TimelineSlider( QWidget* parent, const char* name ) 
    283462        : QWidget( parent, name ), 
    284           m_impl( new PrivateTimelineSlider( this ) ) 
     463          m_impl( new PrivateTimeline( this ) ) 
    285464{ 
    286465        setBackgroundMode( Qt::NoBackground ); 
     
    295474} 
    296475 
    297 void TimelineSlider::setRange( int minval, int maxval ) 
    298 { 
    299         setMinValue( minval ); 
    300         setMaxValue( maxval ); 
     476void TimelineSlider::setMaxRange( int minval, int maxval ) 
     477{ 
     478        if ( minval > maxval ) { 
     479                int tmp = minval; 
     480                minval = maxval; 
     481                maxval = tmp; 
     482        } 
     483 
     484        m_impl->min = minval; 
     485        m_impl->max = maxval; 
     486 
     487        // Reset ranges 
     488        m_impl->rangeContainer.clear(); 
     489        m_impl->rangeContainer.insert( RangePtr(Range::create(minval, maxval)) ); 
     490 
     491        // Update value 
     492        m_impl->v = m_impl->clamp( m_impl->v ); 
     493 
     494        emit valueChanged( m_impl->v ); 
     495        emit value_changed(); 
     496 
     497        emit minValueChanged( minval ); 
     498        emit minimum_changed(); 
     499 
     500        emit maxValueChanged( maxval ); 
     501        emit maximum_changed(); 
     502 
     503        // Update active range 
     504        setActiveRangeId( (*(m_impl->rangeContainer.begin()))->id() ); 
    301505} 
    302506 
     
    308512void TimelineSlider::setLength( int l ) 
    309513{ 
    310         setMinValue( 0 ); 
    311         setMaxValue( l - 1 ); 
     514        setMaxRange( 0, l - 1 ); 
    312515} 
    313516 
     
    319522        } 
    320523 
    321         m_impl->min = val; 
    322         m_impl->inValue = val; 
    323         m_impl->updateValue(); 
     524        setMaxRange( val, m_impl->max ); 
    324525 
    325526        emit minValueChanged( m_impl->min ); 
    326         emit inValueChanged( m_impl->inValue ); 
    327         emit valueChanged( m_impl->v ); 
    328          
    329527        emit minimum_changed(); 
    330         emit value_changed(); 
    331         emit inValue_changed(); 
    332  
    333         update(); 
    334528} 
    335529 
     
    341535        } 
    342536 
    343         m_impl->max = val; 
    344         m_impl->outValue = val; 
    345         m_impl->updateValue(); 
     537        setMaxRange( m_impl->min, val ); 
    346538 
    347539        emit maxValueChanged( m_impl->max ); 
    348         emit outValueChanged( m_impl->outValue ); 
    349         emit valueChanged( m_impl->v ); 
    350  
    351540        emit maximum_changed(); 
    352         emit value_changed(); 
    353         emit outValue_changed(); 
    354  
    355         update(); 
    356541} 
    357542 
     
    371556} 
    372557 
    373 int TimelineSlider::inValue() const 
    374 { 
    375         return m_impl->inValue; 
    376 } 
    377  
    378 void TimelineSlider::setInValue( int v ) 
    379 { 
    380         m_impl->inValue = m_impl->inClamp( v ); 
    381         emit inValueChanged( m_impl->inValue ); 
    382         emit inValue_changed(); 
    383          
    384         update(); 
    385 } 
    386  
    387 int TimelineSlider::outValue() const 
    388 { 
    389         return m_impl->outValue; 
    390 } 
    391  
    392 void TimelineSlider::setOutValue( int v ) 
    393 { 
    394         m_impl->outValue = m_impl->outClamp( v ); 
    395         emit outValueChanged( m_impl->outValue ); 
    396         emit outValue_changed(); 
     558int TimelineSlider::activeInValue() const 
     559{ 
     560        return m_impl->activeRange->in; 
     561} 
     562 
     563void TimelineSlider::setActiveInValue( int v ) 
     564{ 
     565        m_impl->activeRange->in = m_impl->inClamp( v ); 
     566        emit activeInValueChanged( m_impl->activeRange->in ); 
     567        emit activeInValue_changed(); 
     568 
     569        if ( m_impl->inThumb.isSelected() ) { 
     570                emit selectedPointChanged( m_impl->activeRange->in ); 
     571                emit selectedPoint_changed(); 
     572        } 
     573 
     574        update(); 
     575} 
     576 
     577int TimelineSlider::insertRange( int in, int out ) 
     578{ 
     579        qDebug( "insertRange: %d %d", in, out ); 
     580 
     581        // Clamp to min/max, but otherwise leave as-is 
     582        RangePtr range( Range::create( m_impl->clamp(in), m_impl->clamp(out) ) ); 
     583        m_impl->rangeContainer.insert( range ); 
     584 
     585        update(); 
     586 
     587        return range->id(); 
     588} 
     589 
     590void TimelineSlider::removeRangeWithId( int id )  
     591{ 
     592        qDebug( "removeRangeWithId: %d", id ); 
     593 
     594        RangeSet::iterator I = std::find_if( m_impl->rangeContainer.begin(), 
     595                                                                                 m_impl->rangeContainer.end(), 
     596                                                                                 RangeIdentity( id ) ); 
     597 
     598        if ( I == m_impl->rangeContainer.end() ) { 
     599                qWarning( "Remove: unable to find range with id: %d", id ); 
     600                return; 
     601        } 
     602 
     603        m_impl->rangeContainer.erase( I ); 
     604 
     605        if ( m_impl->activeRange->id() == id ) { 
     606                if ( m_impl->rangeContainer.empty() ) { 
     607                        m_impl->rangeContainer.insert( RangePtr( Range::create( m_impl->min, m_impl->max ) ) ); 
     608                } 
     609 
     610                setActiveRangeId( (*m_impl->rangeContainer.begin())->id() ); 
     611        } 
     612 
     613        update(); 
     614} 
     615 
     616TimelineSlider::RangeArray TimelineSlider::allRanges() const 
     617{ 
     618        RangeArray allRanges; 
     619 
     620        for ( RangeSet::const_iterator I = m_impl->rangeContainer.begin(); 
     621                  I != m_impl->rangeContainer.end(); 
     622                  ++I ) 
     623        { 
     624                allRanges.push_back( *(I->get()) ); 
     625        } 
     626 
     627        return allRanges; 
     628} 
     629