Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

page_timeline.cc

Go to the documentation of this file.
00001 /*
00002 * page_timeline.cc Notebook Timeline Page Object
00003 * Copyright (C) 2001 Charles Yates <charles.yates@pandora.be>
00004 * Copyright (C) 2001-2007 Dan Dennedy <dan@dennedy.org>
00005 *
00006 * This program is free software; you can redistribute it and/or modify
00007 * it under the terms of the GNU General Public License as published by
00008 * the Free Software Foundation; either version 2 of the License, or
00009 * (at your option) any later version.
00010 *
00011 * This program is distributed in the hope that it will be useful,
00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 * GNU General Public License for more details.
00015 *
00016 * You should have received a copy of the GNU General Public License
00017 * along with this program; if not, write to the Free Software Foundation,
00018 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include <iostream>
00026 #include <pthread.h>
00027 
00028 #include "page_timeline.h"
00029 #include "page_editor.h"
00030 #include "commands.h"
00031 
00032 enum
00033 {
00034     COLUMN_ICON = 0,
00035     COLUMN_TIME,
00036     COLUMN_FRAME,
00037     N_COLUMNS
00038 };
00039 
00040 /*
00041  * Callbacks
00042  */
00043 
00044 extern "C"
00045 {
00046 
00047     extern KinoCommon *common;
00048     extern navigate_control g_nav_ctl;
00049 
00050     int getOneSecond( void )
00051     {
00052         Frame & frame = *( GetFramePool() ->GetFrame( ) );
00053         common->getPlayList() ->GetFrame( common->g_currentFrame, frame );
00054         int value = ( frame.IsPAL() ? 25 : 30 );
00055         GetFramePool( ) ->DoneWithFrame( &frame );
00056         return value;
00057     }
00058 
00059     static unsigned char    pixels[ FRAME_MAX_WIDTH * FRAME_MAX_HEIGHT * 4 ];
00060 
00061     void
00062     on_iconview_timeline_selection_changed( GtkIconView  *iconview,
00063                                             gpointer user_data )
00064     {
00065         PageTimeline* page = static_cast< PageTimeline* >( user_data );
00066         GtkTreeIter iter;
00067         GValue val = {0, };
00068         
00069         GList* items = gtk_icon_view_get_selected_items( page->getView() );
00070         if ( items )
00071         {
00072             gtk_tree_model_get_iter( GTK_TREE_MODEL( page->getModel() ), &iter,
00073                 static_cast< GtkTreePath* >( items->data ) );
00074             gtk_tree_model_get_value( GTK_TREE_MODEL( page->getModel() ), &iter, COLUMN_FRAME, &val );
00075             common->moveToFrame( g_value_get_int( &val ) );
00076             g_value_unset( &val );
00077             g_list_foreach( items, reinterpret_cast< GFunc >( gtk_tree_path_free ), NULL );
00078             g_list_free( items );
00079     
00080             common->changePageRequest( PAGE_EDITOR );
00081         }
00082     }
00083 
00084     void
00085     on_iconview_timeline_item_activated( GtkIconView     *iconview,
00086                                          GtkTreePath     *path,
00087                                          gpointer         user_data)
00088     {
00089         PageTimeline *page = static_cast< PageTimeline* >( user_data );
00090         GtkTreeIter iter;
00091         GValue val = {0, };
00092 
00093         gtk_tree_model_get_iter( GTK_TREE_MODEL( page->getModel() ), &iter, path );
00094         gtk_tree_model_get_value( GTK_TREE_MODEL( page->getModel() ), &iter, COLUMN_FRAME, &val );
00095         common->moveToFrame( g_value_get_int( &val ) );
00096 //      std::cerr << "on_iconview_timeline_item_activated " << g_value_get_int( &val ) << std::endl;
00097         g_value_unset( &val );
00098         common->changePageRequest( PAGE_EDITOR );
00099     }
00100     
00101     
00102     void
00103     on_timeline_ok_button_pressed ( GtkButton * button,
00104                                     gpointer user_data )
00105     {
00106         common->getPageTimeline( ) ->showIcons( );
00107     }
00108 
00109     static gboolean on_iconlist_refresh_required( GtkWidget * some_widget, void * some_event, gpointer user_data )
00110     {
00111         ( ( PageTimeline * ) user_data ) ->refresh( );
00112         return false;
00113     }
00114     
00115     void
00116     on_start_spin_value_changed            (GtkSpinButton   *spinbutton,
00117                                             gpointer         user_data)
00118     {
00119         gtk_entry_set_text( GTK_ENTRY( lookup_widget( GTK_WIDGET( spinbutton ), "entry_timeline_start" ) ),
00120             common->getTime().parseFramesToString( ( int )gtk_spin_button_get_value( spinbutton ),
00121             common->getTimeFormat() ).c_str() );
00122     }
00123 
00124     void
00125     on_entry_timeline_start_activate       (GtkEntry        *entry,
00126                                             gpointer         user_data)
00127     {
00128         common->getTime().parseValueToString( gtk_entry_get_text( entry ), common->getTimeFormat() );
00129         GtkSpinButton *spinbutton = GTK_SPIN_BUTTON( lookup_widget( GTK_WIDGET( entry ), "start_spin" ) );
00130         gtk_spin_button_set_value( spinbutton, common->getTime().getFrames() );
00131         on_start_spin_value_changed( spinbutton, NULL );
00132     }
00133     
00134     gboolean
00135     on_entry_timeline_start_focus_out_event
00136                                             (GtkWidget       *widget,
00137                                             GdkEventFocus   *event,
00138                                             gpointer         user_data)
00139     {
00140         on_entry_timeline_start_activate( GTK_ENTRY( widget ), NULL );
00141         g_nav_ctl.escaped = FALSE;
00142         return FALSE;
00143     }
00144 
00145     void
00146     on_end_spin_value_changed              (GtkSpinButton   *spinbutton,
00147                                             gpointer         user_data)
00148     {
00149         gtk_entry_set_text( GTK_ENTRY( lookup_widget( GTK_WIDGET( spinbutton ), "entry_timeline_end" ) ),
00150             common->getTime().parseFramesToString( ( int )gtk_spin_button_get_value( spinbutton ),
00151             common->getTimeFormat() ).c_str() );
00152     }
00153 
00154     void
00155     on_entry_timeline_end_activate         (GtkEntry        *entry,
00156                                             gpointer         user_data)
00157     {
00158         common->getTime().parseValueToString( gtk_entry_get_text( entry ), common->getTimeFormat() );
00159         GtkSpinButton *spinbutton = GTK_SPIN_BUTTON( lookup_widget( GTK_WIDGET( entry ), "end_spin" ) );
00160         gtk_spin_button_set_value( spinbutton, common->getTime().getFrames() );
00161         on_end_spin_value_changed( spinbutton, NULL );
00162     }
00163     
00164     gboolean
00165     on_entry_timeline_end_focus_out_event  (GtkWidget       *widget,
00166                                             GdkEventFocus   *event,
00167                                             gpointer         user_data)
00168     {
00169         on_entry_timeline_end_activate( GTK_ENTRY( widget ), NULL );
00170         g_nav_ctl.escaped = FALSE;
00171         return FALSE;
00172     }
00173 }
00174 
00175 PageTimeline::PageTimeline( KinoCommon *common ) :
00176         refresh_required( false ), action( 0 ),
00177         last_begin( 0 ), last_end( 0 ), scene( -1 ),
00178         showIconsRunning( TIMELINE_INIT )
00179 {
00180     this->common = common;
00181 
00182     view = GTK_ICON_VIEW( lookup_widget( common->getWidget(), "iconview_timeline" ) );
00183     g_signal_connect( G_OBJECT( view ), "expose_event", G_CALLBACK( on_iconlist_refresh_required ), this );
00184     g_signal_connect( G_OBJECT( view ), "item-activated", G_CALLBACK( on_iconview_timeline_item_activated ), this );
00185     g_signal_connect( G_OBJECT( view ), "selection-changed", G_CALLBACK( on_iconview_timeline_selection_changed ), this );
00186     model = gtk_list_store_new( N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT );
00187     gtk_icon_view_set_model( view, GTK_TREE_MODEL( model ) );
00188     gtk_icon_view_set_pixbuf_column( view, COLUMN_ICON );
00189     gtk_icon_view_set_text_column( view, COLUMN_TIME );
00190     pthread_mutex_init( &key, NULL );
00191     pthread_mutex_init( &mutex, NULL );
00192 }
00193 
00194 void PageTimeline::start()
00195 {
00196     std::cerr << ">> Starting timeline" << std::endl;
00197 
00198     if ( common->getPlayList() ->GetNumFrames() > 0 )
00199     {
00200         int begin = common->getPlayList() ->FindStartOfScene( common->g_currentFrame );
00201         int end = common->getPlayList() ->FindEndOfScene( common->g_currentFrame );
00202 
00203         if ( last_begin != begin || last_end != end )
00204         {
00205             GtkEntry *startSpin = GTK_ENTRY( lookup_widget( common->getWidget(), "start_spin" ) );
00206             GtkEntry *endSpin = GTK_ENTRY( lookup_widget( common->getWidget(), "end_spin" ) );
00207             GtkAdjustment *adjust = gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON( startSpin ) );
00208 
00209             adjust->lower = 0;
00210             adjust->upper = common->getPlayList() ->GetNumFrames();
00211             gtk_spin_button_set_value( GTK_SPIN_BUTTON( startSpin ), begin );
00212             g_signal_emit_by_name( adjust, "changed" );
00213 
00214             adjust = gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON( endSpin ) );
00215             adjust->lower = 0;
00216             adjust->upper = common->getPlayList() ->GetNumFrames();
00217             gtk_spin_button_set_value( GTK_SPIN_BUTTON( endSpin ), end );
00218             g_signal_emit_by_name( adjust, "changed" );
00219 
00220             last_begin = begin;
00221             last_end = end;
00222 
00223             scene = 0;
00224             vector <int> scenes = common->getPageEditor() ->GetScene();
00225             while ( begin > scenes[ scene++ ] )
00226                 ;
00227             scene--;
00228 
00229             refresh_required = true;
00230         }
00231     }
00232     gtk_notebook_set_page( GTK_NOTEBOOK( lookup_widget( common->getWidget(), "notebook_keyhelp" ) ), 3 );
00233     gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( lookup_widget( common->getWidget(), "menuitem_timeline" ) ), TRUE );
00234 }
00235 
00236 void PageTimeline::selectScene( int i )
00237 {
00238     std::cerr << "Time line scene " << i << std::endl;
00239     GtkWidget *start_spin = lookup_widget( common->getWidget(), "start_spin" );
00240     GtkWidget *end_spin = lookup_widget( common->getWidget(), "end_spin" );
00241     GtkAdjustment *adjust = gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON( start_spin ) );
00242     vector <int> scenes = common->getPageEditor() ->GetScene();
00243 
00244     if ( i < 0 )
00245         i = 0;
00246     if ( i >= ( int ) scenes.size() )
00247         i = scenes.size() - 1;
00248     int value = i == 0 ? 0 : scenes[ i - 1 ];
00249     adjust->lower = 0;
00250     adjust->upper = scenes[ scenes.size() - 1 ];
00251     gtk_spin_button_set_value( GTK_SPIN_BUTTON( start_spin ), value );
00252 
00253     adjust = gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON( end_spin ) );
00254 
00255     adjust->lower = 0;
00256     adjust->upper = scenes[ scenes.size() - 1 ];
00257     gtk_spin_button_set_value( GTK_SPIN_BUTTON( end_spin ), scenes[ i ] - 1 );
00258 
00259     last_begin = value;
00260     last_end = scenes[ i ] - 1;
00261     scene = i;
00262 
00263     common->g_currentFrame = value;
00264 
00265     Frame &frame = *( GetFramePool( ) ->GetFrame( ) );
00266     FileHandler *media;
00267     common->getPlayList() ->GetMediaObject( value, &media );
00268     common->getPlayList() ->GetFrame( value, frame );
00269     common->showFrameMoreInfo( frame, media );
00270     GetFramePool( ) ->DoneWithFrame( &frame );
00271     common->setCurrentScene( value );
00272 
00273     showIcons( );
00274 }
00275 
00276 gulong PageTimeline::activate()
00277 {
00278     return SCENE_LIST;
00279 }
00280 
00281 void PageTimeline::showIcons( )
00282 {
00283     pthread_mutex_lock( &mutex );
00284 
00285     // If running then, set as RESTART otherwise start the thread,
00286     // otherwise we're already waiting on a restart so do nothing
00287     if ( showIconsRunning == TIMELINE_RUNNING )
00288     {
00289         showIconsRunning = TIMELINE_RESTART;
00290     }
00291     else if ( showIconsRunning == TIMELINE_INIT )
00292     {
00293         showIconsRunning = TIMELINE_STARTING;
00294         pthread_create( &thread, NULL, ThreadProxy, this );
00295     }
00296     else if ( showIconsRunning == TIMELINE_IDLE )
00297     {
00298         pthread_join( thread, NULL );
00299         showIconsRunning = TIMELINE_STARTING;
00300         pthread_create( &thread, NULL, ThreadProxy, this );
00301     }
00302 
00303     pthread_mutex_unlock( &mutex );
00304 }
00305 
00306 void* PageTimeline::ThreadProxy( void* arg )
00307 {
00308     PageTimeline* self = static_cast< PageTimeline* >( arg );
00309     return self->Thread();
00310 }
00311 
00312 void* PageTimeline::Thread()
00313 {
00314     Frame &frame = *GetFramePool( ) ->GetFrame( );
00315 
00316     // Set the lowest coloured quality for decode
00317     frame.decoder->quality = DV_QUALITY_DC;
00318     if ( Preferences::getInstance().displayQuality < 4 )
00319         frame.decoder->quality |= DV_QUALITY_COLOR;
00320 
00321     while ( showIconsRunning != TIMELINE_IDLE )
00322     {
00323         GtkTreeIter iter;
00324 
00325         showIconsRunning = TIMELINE_RUNNING;
00326         refresh_required = false;
00327         gdk_threads_enter();
00328     
00329         // Increment and store the number of times this is called
00330         int my_action = ++ action;
00331     
00332         // Lock the mutex to ensure only one scene is rendered
00333         pthread_mutex_lock( &key );
00334     
00335         // Obtain the start and end widgets
00336         GtkWidget *start_spin = lookup_widget( common->getWidget(), "start_spin" );
00337         GtkWidget *end_spin = lookup_widget( common->getWidget(), "end_spin" );
00338     
00339         // Get the start and end frames from the widgets
00340         int start = atoi( gtk_entry_get_text( GTK_ENTRY( start_spin ) ) );
00341         int end = atoi( gtk_entry_get_text( GTK_ENTRY( end_spin ) ) );
00342     
00343         // Calculate the number of thumbs per line
00344         int items_per_row = ( GTK_WIDGET( view )->allocation.width - 12 ) / 96;
00345         int number_of_rows = GTK_WIDGET( view )->allocation.height / 98;
00346         int total_possible = items_per_row * number_of_rows;
00347         int total_required = end - start + 1;
00348     
00349         // Calculate the increment
00350         double increment = 1;
00351         if ( total_required == 1 )
00352             increment = 2;
00353         else if ( total_possible < total_required )
00354             increment = 1 / ( ( double ) total_possible - 1 );
00355         else
00356             increment = 1 / ( ( double ) total_required );
00357     
00358         // Wipe the previous contents
00359         gtk_list_store_clear( model );
00360     
00361         // Hmm - avoid rounding errors?
00362         int count = 0;
00363     
00364         // Loop for each thumb nail
00365         for ( double position = 0;
00366                 showIconsRunning == TIMELINE_RUNNING &&
00367                     action == my_action && count ++ < total_possible;
00368                 position += increment )
00369         {
00370             // Calculate the index
00371             int index = start + ( int ) ( position * total_required );
00372     
00373             if ( position >= 1 )
00374                 index = start + total_required - 1;
00375     
00376             // Define the label text variable
00377             string text = "";
00378     
00379             // Get the frame at the index
00380             common->getPlayList() ->GetFrame( ( int ) index, frame );
00381     
00382             // Extract the RGB image
00383             frame.ExtractRGB( pixels );
00384     
00385             // Create gdk pixbuf image to allow rescaling
00386             GdkPixbuf *image = gdk_pixbuf_new_from_data( pixels, GDK_COLORSPACE_RGB, FALSE, 8,
00387                             frame.GetWidth(), frame.GetHeight(), frame.GetWidth() * 3, NULL, NULL );
00388     
00389             // Rescale
00390             GdkPixbuf *scaled = gdk_pixbuf_scale_simple( image, 90, frame.IsWide() ? 51 : 68, GDK_INTERP_NEAREST );
00391     
00392             // Set the label
00393             char label[ 256 ] = "";
00394             sprintf( label, "%d", ( int ) index + 1 );
00395             common->getTime().setFramerate( frame.GetFrameRate() );
00396             text = common->getTime().parseFramesToString( index, common->getTimeFormat() );
00397     
00398             // Place image in icon list
00399             gtk_list_store_append( model, &iter );
00400             gtk_list_store_set( model, &iter, COLUMN_ICON, scaled, 
00401                 COLUMN_TIME, text.c_str(), COLUMN_FRAME, index, -1 );
00402     
00403             // Delete the original, unscaled pixbuf
00404             g_object_unref( image );
00405             g_object_unref( scaled );
00406     
00407             // Process any pending events
00408             while ( action == my_action && gtk_events_pending() )
00409             {
00410                 pthread_mutex_unlock( &key );
00411                 gtk_main_iteration();
00412                 pthread_mutex_lock( &key );
00413             }
00414     
00415             if ( position >= 1 )
00416                 break;
00417         }
00418     
00419         // Unlock the mutex
00420         pthread_mutex_unlock( &key );
00421         gdk_threads_leave();
00422     
00423         // If we're still running now, it's OK to idle again.
00424         if ( showIconsRunning == TIMELINE_RUNNING )
00425             showIconsRunning = TIMELINE_IDLE;
00426     }
00427     GetFramePool( ) ->DoneWithFrame( &frame );
00428 
00429     return NULL;
00430 }
00431 
00432 void PageTimeline::refresh( )
00433 {
00434     if ( refresh_required )
00435     {
00436         refresh_required = false;
00437         showIcons( );
00438     }
00439 }
00440 
00441 gboolean PageTimeline::processKeyboard( GdkEventKey *event )
00442 {
00443     gboolean ret = FALSE;
00444 
00445     // Translate special keys to equivalent command
00446     switch ( event->keyval )
00447     {
00448     case GDK_k:
00449     case GDK_Up:
00450         selectScene( scene - 1 );
00451         break;
00452     case GDK_j:
00453     case GDK_Down:
00454         selectScene( scene + 1 );
00455         break;
00456     case GDK_Return:
00457         selectScene( scene );
00458         break;
00459     case GDK_Escape:
00460         common->changePageRequest( PAGE_EDITOR );
00461         break;
00462     default:
00463         break;
00464     }
00465 
00466     return ret;
00467 }
00468 
00469 gboolean PageTimeline::processCommand( char *cmd )
00470 {
00471     if ( strcmp( cmd, "F2" ) == 0 )
00472     {
00473         common->keyboardFeedback( cmd, _( "Edit" ) );
00474         common->changePageRequest( PAGE_TIMELINE );
00475     }
00476 
00477     else if ( strcmp( cmd, "A" ) == 0 )
00478     {
00479         common->keyboardFeedback( cmd, _( "Capture, append to movie" ) );
00480         common->moveToFrame( common->getPlayList() ->GetNumFrames() );
00481         FileTracker::GetInstance().SetMode( CAPTURE_MOVIE_APPEND );
00482         common->changePageRequest( PAGE_CAPTURE );
00483     }
00484 
00485     else if ( strcmp( cmd, "a" ) == 0 )
00486     {
00487         common->keyboardFeedback( cmd, _( "Capture, insert after frame" ) );
00488         common->moveToFrame( common->getPlayList() ->FindEndOfScene( common->g_currentFrame ) );
00489         FileTracker::GetInstance().SetMode( CAPTURE_FRAME_APPEND );
00490         common->changePageRequest( PAGE_CAPTURE );
00491     }
00492 
00493     else if ( strcmp( cmd, "t" ) == 0 )
00494     {
00495         common->keyboardFeedback( cmd, _( "Trim" ) );
00496         common->changePageRequest( PAGE_TRIM );
00497     }
00498 
00499     else if ( strcmp( cmd, "v" ) == 0 )
00500     {
00501         common->keyboardFeedback( cmd, _( "Timeline" ) );
00502         common->changePageRequest( PAGE_TIMELINE );
00503     }
00504 
00505     else if ( strcmp( cmd, "C" ) == 0 )
00506     {
00507         common->keyboardFeedback( cmd, _( "FX" ) );
00508         common->changePageRequest( PAGE_MAGICK );
00509     }
00510 
00511     else if ( strcmp( cmd, ":W" ) == 0 )
00512     {
00513         common->keyboardFeedback( cmd, _( "Export" ) );
00514         common->changePageRequest( PAGE_TIMELINE );
00515     }
00516 
00517     /* write PlayList */
00518 
00519     else if ( strcmp( cmd, ":w" ) == 0 )
00520     {
00521         common->keyboardFeedback( cmd, _( "Write playlist" ) );
00522         common->savePlayList( );
00523     }
00524 
00525     /* quit */
00526 
00527     else if ( strcmp( cmd, ":q" ) == 0 )
00528     {
00529         common->keyboardFeedback( cmd, _( "quit" ) );
00530         kinoDeactivate();
00531     }
00532 
00533     return FALSE;
00534 }
00535 
00536 void PageTimeline::timeFormatChanged()
00537 {
00538     on_start_spin_value_changed( GTK_SPIN_BUTTON( lookup_widget( common->getWidget(), "start_spin" ) ), NULL );
00539     on_end_spin_value_changed( GTK_SPIN_BUTTON( lookup_widget( common->getWidget(), "end_spin" ) ), NULL );
00540     showIcons();
00541 }

Generated on Sun Mar 11 22:11:46 2007 for Kino by  doxygen 1.4.2