00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024
00025 #include <fstream>
00026 #include <iostream>
00027 using std::cerr;
00028 using std::endl;
00029 #include <sstream>
00030 #include <string>
00031
00032 #include <sys/types.h>
00033 #include <sys/wait.h>
00034 #include <dirent.h>
00035 #ifndef _GNU_SOURCE
00036 #define _GNU_SOURCE
00037 #endif
00038 #include <string.h>
00039 #include <stdarg.h>
00040 #include <time.h>
00041 #include <libxml/nanohttp.h>
00042 #include <libxml/parser.h>
00043 #include <libxml/tree.h>
00044 #include <libxml/xpath.h>
00045
00046 #include "kino_common.h"
00047 #include "page_editor.h"
00048 #include "page_capture.h"
00049 #include "page_timeline.h"
00050 #include "page_undefined.h"
00051 #include "page_export.h"
00052 #include "page_bttv.h"
00053 #include "page_trim.h"
00054 #include "page_magick.h"
00055 #include "preferences.h"
00056 #include "message.h"
00057 #include "frame.h"
00058 #include "displayer.h"
00059 #include "storyboard.h"
00060 #include "preferences.h"
00061 #include "riff.h"
00062 #include "avi.h"
00063 #include "filehandler.h"
00064 #include "ieee1394io.h"
00065 #include "error.h"
00066 #include "stringutils.h"
00067
00068
00069 extern "C"
00070 {
00071 #include <pthread.h>
00072 }
00073
00074 GtkWindow * KinoCommon::getWidgetWindow(GtkWidget *widget)
00075 {
00076 if(widget)
00077 if(GTK_IS_WINDOW(widget))
00078 return GTK_WINDOW(widget);
00079 else
00080 return getWidgetWindow(widget->parent);
00081 else
00082 return NULL;
00083 }
00084
00090 KinoCommon::KinoCommon( GtkWidget *widget ) : last_directory( "" )
00091 {
00092 cerr << "> Kino Common being built" << endl;
00093
00094
00095 this->widget = widget;
00096 this->edit_menu = lookup_widget( widget, "edit" );
00097 this->view_menu = lookup_widget( widget, "view1" );
00098 this->notebook = GTK_NOTEBOOK( lookup_widget( widget, "main_notebook" ) );
00099 this->video_start_movie_button = GTK_BUTTON( lookup_widget( widget, "video_start_movie_button" ) );
00100 this->video_start_scene_button = GTK_BUTTON( lookup_widget( widget, "video_start_scene_button" ) );
00101 this->video_rewind_button = GTK_BUTTON( lookup_widget( widget, "video_rewind_button" ) );
00102 this->video_back_button = GTK_BUTTON( lookup_widget( widget, "video_back_button" ) );
00103 this->video_play_button = GTK_BUTTON( lookup_widget( widget, "video_play_button" ) );
00104 this->video_stop_button = GTK_BUTTON( lookup_widget( widget, "video_stop_button" ) );
00105 this->video_forward_button = GTK_BUTTON( lookup_widget( widget, "video_forward_button" ) );
00106 this->video_fast_forward_button = GTK_BUTTON( lookup_widget( widget, "video_fast_forward_button" ) );
00107 this->video_end_scene_button = GTK_BUTTON( lookup_widget( widget, "video_end_scene_button" ) );
00108 this->video_end_movie_button = GTK_BUTTON( lookup_widget( widget, "video_end_movie_button" ) );
00109 this->video_shuttle = GTK_RANGE( lookup_widget( widget, "hscale_shuttle" ) );
00110 this->statusbar = GTK_STATUSBAR( lookup_widget( widget, "statusbar" ) );
00111
00112
00113 this->editor = new PageEditor( this );
00114 this->capture = new PageCapture( this );
00115 this->timeline = new PageTimeline( this );
00116 this->undefined = new PageUndefined( this );
00117 this->bttv = new PageBttv( this );
00118 this->exportPage = new PageExport( this );
00119 this->trimPage = new PageTrim( this );
00120 this->magickPage = new PageMagick( this );
00121
00122
00123 strcpy( this->tempFileName, "" );
00124 this->g_currentFrame = -1;
00125 this->hasListChanged = TRUE;
00126 this->currentScene = -1;
00127 this->currentPage = -1;
00128
00129 this->component_state = VIDEO_STOP;
00130 this->is_component_state_changing = false;
00131
00132 SMIL::Time::TimeFormat timeFormat = static_cast< SMIL::Time::TimeFormat >( Preferences::getInstance().timeFormat );
00133 GtkWidget *timemenu = lookup_widget( widget, "optionmenu_time_format" );
00134 if ( timeFormat == SMIL::Time::TIME_FORMAT_NONE )
00135 setTimeFormat( SMIL::Time::TIME_FORMAT_FRAMES );
00136 gtk_option_menu_set_history( GTK_OPTION_MENU( timemenu ), Preferences::getInstance().timeFormat - 1 );
00137
00138 }
00139
00143 KinoCommon::~KinoCommon()
00144 {
00145 cerr << "> Kino Common being destroyed" << endl;
00146 clean();
00147 delete this->editor;
00148 delete this->capture;
00149 delete this->timeline;
00150 delete this->undefined;
00151 delete this->bttv;
00152 delete this->exportPage;
00153 delete this->trimPage;
00154 delete this->magickPage;
00155 GetFileMap() ->Clear();
00156 cerr << "> Kino Common destroyed" << endl;
00157 }
00158
00168 void KinoCommon::fetchProjectMetadata( const std::string& projectKey )
00169 {
00170 if ( projectKey == "" )
00171 return;
00172
00173
00174 char uri[1024];
00175 uri[1023] = '\0';
00176 snprintf( uri, 1023, Preferences::getInstance().newProjectURI, projectKey.c_str() );
00177 cerr << "Composed Project URI " << uri << endl;
00178 xmlNanoHTTPInit();
00179 void *httpContext = xmlNanoHTTPOpen( uri, NULL );
00180 int code = 0;
00181 if ( httpContext && ( code = xmlNanoHTTPReturnCode( httpContext ) ) == 200 )
00182 {
00183 char *buffer = (char*) calloc( 1, 1000 );
00184 int size = 0;
00185 int nread;
00186 while ( ( nread = xmlNanoHTTPRead( httpContext, buffer + size, 999 ) ) > 0 )
00187 {
00188 size += nread;
00189 if ( nread == 999 )
00190 {
00191 buffer = ( char* )realloc( buffer, size + 999 );
00192 buffer[ size + 1 ] = '\0';
00193 }
00194 }
00195
00196
00197 xmlDocPtr doc = xmlParseMemory( buffer, size );
00198 xmlXPathInit();
00199 xmlXPathContextPtr xpathContext = xmlXPathNewContext( doc );
00200 if ( xpathContext )
00201 {
00202 xmlXPathObjectPtr xpathResult;
00203
00204
00205 if ( strcmp( Preferences::getInstance().newProjectXPath, "" ) )
00206 {
00207 xpathResult = xmlXPathEval( (xmlChar*) Preferences::getInstance().newProjectXPath, xpathContext );
00208 if ( xpathResult )
00209 {
00210 if ( xpathResult->type == XPATH_NODESET && !xmlXPathNodeSetIsEmpty( xpathResult->nodesetval ) )
00211 {
00212 getPlayList()->SetDocId( (char*) xmlXPathCastToString( xpathResult ) );
00213 cerr << "newProject ID = " << xmlXPathCastToString( xpathResult ) << endl;
00214 if ( strcmp( reinterpret_cast< char* >( xmlXPathCastToString( xpathResult ) ), "" ) == 0 )
00215 modal_message( _("The server returned an empty response.\n\nPlease choose File/New to query the server again.") );
00216 }
00217 else
00218 modal_message( _("Failed to parse project metadata:\nthe result of newProjectXPath is empty") );
00219 xmlXPathFreeObject( xpathResult );
00220 }
00221 else
00222 modal_message( _("Failed to parse project metadata:\nbad newProjectXPath expression") );
00223 }
00224
00225
00226 map< string, vector< std::pair< std::string, std::string > > >& metaValuesMap = Preferences::getInstance().metaValues;
00227 map< string, vector< std::pair< std::string, std::string > > >::iterator metaValuesMapIter;
00228 for ( metaValuesMapIter = metaValuesMap.begin(); metaValuesMapIter != metaValuesMap.end(); ++metaValuesMapIter )
00229 {
00230 if ( metaValuesMapIter->second.size() > 0 )
00231 {
00232 const string labelPath = metaValuesMapIter->second[0].first;
00233 const string valuePath = metaValuesMapIter->second[0].second;
00234
00235
00236 if ( labelPath.length() > 5 && labelPath.substr( 0, 6 ) == "xpath:" )
00237 {
00238
00239 metaValuesMapIter->second.erase( metaValuesMapIter->second.begin() );
00240
00241 xpathResult = xmlXPathEval( (xmlChar*) labelPath.substr( 6 ).c_str(), xpathContext );
00242 if ( xpathResult )
00243 {
00244 if ( xpathResult->type == XPATH_NODESET )
00245 {
00246 for ( int i = 0; i < xmlXPathNodeSetGetLength( xpathResult->nodesetval ); i++ )
00247 {
00248 string label( (char *) xmlXPathCastNodeToString( xmlXPathNodeSetItem( xpathResult->nodesetval, i ) ) );
00249 metaValuesMapIter->second.push_back( make_pair( label, label ) );
00250 }
00251 }
00252 xmlXPathFreeObject( xpathResult );
00253 }
00254 else
00255 modal_message( _("Failed to parse project metadata:\nbad metaValues XPath expression") );
00256 }
00257
00258
00259 if ( valuePath.length() > 5 && valuePath.substr( 0, 6 ) == "xpath:" )
00260 {
00261 xpathResult = xmlXPathEval( (xmlChar*) valuePath.substr( 6 ).c_str(), xpathContext );
00262 if ( xpathResult )
00263 {
00264 if ( xpathResult->type == XPATH_NODESET )
00265 {
00266 for ( int i = 0; i < xmlXPathNodeSetGetLength( xpathResult->nodesetval ); i++ )
00267 metaValuesMapIter->second[ i ].second =
00268 (char *) xmlXPathCastNodeToString( xmlXPathNodeSetItem( xpathResult->nodesetval, i ) );
00269 }
00270 xmlXPathFreeObject( xpathResult );
00271 }
00272 else
00273 modal_message( _("Failed to parse project metadata:\nbad metaValues XPath expression") );
00274 }
00275 }
00276 }
00277
00278 xmlXPathFreeContext( xpathContext );
00279 }
00280
00281 free( buffer );
00282 }
00283 else if ( httpContext )
00284 {
00285 modal_message( "Server responded with error %d", code );
00286 }
00287 if ( httpContext )
00288 xmlNanoHTTPClose( httpContext );
00289
00290 xmlNanoHTTPCleanup();
00291 }
00292
00297 bool KinoCommon::newFile( bool prompt )
00298 {
00299 bool result = true;
00300
00301 cerr << ">> Kino Common newFile" << endl;
00302
00303 this->getPageMagick()->Stop();
00304 this->changePageRequest( PAGE_EDITOR );
00305 if ( getPlayList() ->IsDirty() )
00306 {
00307 switch ( modal_confirm( GTK_STOCK_SAVE, GTK_STOCK_CLOSE, _("Save changes to project \"%s\" before closing?"),
00308 getPlayList()->GetDocName() == "" ? _("Untitled") : getPlayList()->GetDocName().c_str() ) )
00309 {
00310 case GTK_RESPONSE_ACCEPT:
00311 result = savePlayList( );
00312 break;
00313 case GTK_RESPONSE_CLOSE:
00314 result = true;
00315 break;
00316 default:
00317
00318 result = false;
00319 break;
00320 }
00321 }
00322 if ( result )
00323 {
00324
00325 vector <string> list;
00326 PlayList clean;
00327
00328 getPlayList( ) ->GetLastCleanPlayList( clean );
00329 GetFileMap() ->GetUnusedFxFiles( clean, list );
00330
00331 if ( list.begin() != list.end() )
00332 {
00333 string buffer;
00334
00335 buffer = _( "The following fx rendered files are no longer used: \n\n" );
00336 vector < string >::iterator it;
00337 for ( it = list.begin(); it != list.end(); it ++ )
00338 buffer += *it + "\n";
00339 buffer += _( "\nDo you want me to delete them now?" );
00340
00341 switch ( modal_confirm( GTK_STOCK_DELETE, GTK_STOCK_CLOSE, buffer.c_str() ) )
00342 {
00343 case GTK_RESPONSE_ACCEPT:
00344 for ( it = list.begin(); it != list.end(); it ++ )
00345 unlink( ( *it ).c_str() );
00346 result = true;
00347 break;
00348 case GTK_RESPONSE_CLOSE:
00349
00350 result = true;
00351 break;
00352 default:
00353
00354 result = false;
00355 break;
00356 }
00357 }
00358
00359 if ( result )
00360 {
00361 getPlayList() ->CleanPlayList( );
00362 GetFileMap() ->Clear();
00363 this->g_currentFrame = -1;
00364 this->currentScene = -1;
00365 this->setWindowTitle( );
00366
00367
00368 Page *page = NULL;
00369 for ( int index = 0; ( page = getPage( index ) ) != NULL; index ++ )
00370 page->newFile();
00371
00372 GetStoryboard() ->reset();
00373 GetStoryboard() ->redraw();
00374 this->windowMoved();
00375
00376 GetEditorBackup() ->Clear();
00377 getPageEditor() ->snapshot();
00378 }
00379 }
00380
00381
00382
00383 if ( prompt && strcmp( Preferences::getInstance().newProjectURI, "" ) )
00384 {
00385 if ( strstr( Preferences::getInstance().newProjectURI, "%" ) )
00386 {
00387 char *response = modal_prompt( _("Enter a project name") );
00388 if ( response )
00389 {
00390 getPlayList()->SetDocTitle( response );
00391 fetchProjectMetadata( response );
00392 free( response );
00393 }
00394 }
00395 else
00396 {
00397 fetchProjectMetadata( "_" );
00398 }
00399 }
00400 return result;
00401 }
00402
00408 void KinoCommon::changePageRequest( int page )
00409 {
00410 gtk_notebook_set_page( notebook, page );
00411 }
00412
00419 void KinoCommon::setCurrentPage( int page )
00420 {
00421 if ( this->currentPage != page )
00422 {
00423 if ( this->currentPage != -1 )
00424 clean();
00425 this->currentPage = page;
00426 start();
00427 moveToFrame();
00428 gtk_widget_queue_draw( getWidget() );
00429 }
00430 }
00431
00438 Page *KinoCommon::getPage( int page )
00439 {
00440 Page * ret = NULL;
00441
00442 switch ( page )
00443 {
00444 case PAGE_EDITOR:
00445 ret = getPageEditor();
00446 break;
00447 case PAGE_CAPTURE:
00448 ret = getPageCapture();
00449 break;
00450 case PAGE_TIMELINE:
00451 ret = getPageTimeline();
00452 break;
00453 case PAGE_TRIM:
00454 ret = getPageTrim();
00455 break;
00456 case PAGE_EXPORT:
00457 ret = getPageExport();
00458 break;
00459 case PAGE_MAGICK:
00460 ret = getPageMagick();
00461 break;
00462 case PAGE_BTTV:
00463 ret = getPageBttv();
00464 break;
00465 }
00466
00467 return ret;
00468 }
00469
00475 Page *KinoCommon::getCurrentPage( )
00476 {
00477 Page * ret = getPage( this->currentPage );
00478 if ( ret == NULL )
00479 ret = getPageUndefined();
00480 return ret;
00481 }
00482
00488 PageEditor *KinoCommon::getPageEditor( )
00489 {
00490 return editor;
00491 }
00492
00498 PageCapture *KinoCommon::getPageCapture( )
00499 {
00500 return capture;
00501 }
00502
00508 PageTimeline *KinoCommon::getPageTimeline( )
00509 {
00510 return timeline;
00511 }
00512
00518 PageUndefined *KinoCommon::getPageUndefined( )
00519 {
00520 return undefined;
00521 }
00522
00528 PageBttv *KinoCommon::getPageBttv( )
00529 {
00530 return bttv;
00531 }
00532
00538 PageExport *KinoCommon::getPageExport( )
00539 {
00540 return exportPage;
00541 }
00542
00548 PageTrim *KinoCommon::getPageTrim( )
00549 {
00550 return trimPage;
00551 }
00552
00558 PageMagick *KinoCommon::getPageMagick( )
00559 {
00560 return magickPage;
00561 }
00562
00566 std::string KinoCommon::importFile( const char *filename )
00567 {
00568 std::string importedFile( filename );
00569
00570 if ( modal_confirm( GTK_STOCK_OK, NULL,
00571 _("\"%s\" is not a DV file. Do you want to import it?"), filename )
00572 == GTK_RESPONSE_ACCEPT )
00573 {
00574 char * args[ 7 ];
00575 int pid, status = -1;
00576 GError *gerror = NULL;
00577 gboolean visible = TRUE;
00578 GtkWidget *dialog = lookup_widget( widget, "import_window" );
00579 GtkProgressBar *progressBar = GTK_PROGRESS_BAR( lookup_widget( widget, "progressbar_import" ) );
00580 char *kinoHome = getenv("KINO_HOME");
00581 string homeFile;
00582 int testHomeFile = 0;
00583
00584 if ( kinoHome )
00585 homeFile = string( kinoHome ) + string( "/import/media.sh" );
00586 else
00587 homeFile = string( getenv( "HOME" ) ) + string( "/kino/import/media.sh" );
00588
00589 gtk_window_set_transient_for( GTK_WINDOW( dialog ), GTK_WINDOW( getWidgetWindow( getWidget() ) ) );
00590 gtk_progress_bar_pulse( progressBar );
00591 gtk_widget_show_all( dialog );
00592
00593
00594 if ( g_currentFrame == -1 )
00595 {
00596
00597 if ( Preferences::getInstance().defaultNormalisation == NORM_UNSPECIFIED )
00598 {
00599 switch ( modal_confirm( "PAL", "NTSC", _("Please choose a video standard") ) )
00600 {
00601 case GTK_RESPONSE_ACCEPT:
00602 Preferences::getInstance().defaultNormalisation = NORM_PAL;
00603 break;
00604 case GTK_RESPONSE_CLOSE:
00605 Preferences::getInstance().defaultNormalisation = NORM_NTSC;
00606 break;
00607 default:
00608
00609 gtk_widget_hide( dialog );
00610 return importedFile;
00611 }
00612 }
00613 args[ 3 ] = const_cast<char*>(
00614 Preferences::getInstance().defaultNormalisation == NORM_PAL ? "pal" : "ntsc" );
00615 args[ 4 ] = const_cast<char*>(
00616 Preferences::getInstance().defaultAspect == ASPECT_169 ? "16:9" : "4:3" );
00617 switch ( Preferences::getInstance().defaultAudio )
00618 {
00619 case AUDIO_32KHZ:
00620 args[ 5 ] = "32000";
00621 break;
00622 case AUDIO_44KHZ:
00623 args[ 5 ] = "44100";
00624 break;
00625 case AUDIO_48KHZ:
00626 args[ 5 ] = "48000";
00627 break;
00628 }
00629 }
00630 else
00631 {
00632
00633 Frame& frame = *GetFramePool()->GetFrame();
00634 AudioInfo ainfo;
00635 std::ostringstream strstream;
00636
00637 this->getPlayList() ->GetFrame( 0, frame );
00638 frame.GetAudioInfo( ainfo );
00639 strstream << ainfo.frequency;
00640 args[ 3 ] = const_cast<char*>( frame.IsPAL() ? "pal" : "ntsc" );
00641 args[ 4 ] = const_cast<char*>( frame.IsWide() ? "16:9" : "4:3" );
00642 args[ 5 ] = const_cast<char*>( strstream.str().c_str() );
00643 GetFramePool()->DoneWithFrame( &frame );
00644 }
00645 importedFile = StringUtils::replaceAll( importedFile, "\"", "\\\"") + std::string( ".dv" );
00646
00647 args[ 0 ] = DATADIR "/kino/scripts/import/media.sh";
00648 args[ 1 ] = const_cast<char*>( filename );
00649 args[ 2 ] = const_cast<char*>( importedFile.c_str() );
00650 args[ 6 ] = NULL;
00651
00652
00653 testHomeFile = open( homeFile.c_str(), O_RDONLY );
00654 if ( testHomeFile > -1 )
00655 {
00656 close( testHomeFile );
00657 args[ 0 ] = const_cast< char* >( homeFile.c_str() );
00658 }
00659
00660 std::ostringstream command;
00661 command << "\"" << args[0] << "\" \"" << args[1] << "\" \"" << args[2] << "\" " << args[3] << " " << args[4] << " " << args[5];
00662 args[ 0 ] = "/bin/sh";
00663 args[ 1 ] = "-c";
00664 args[ 2 ] = const_cast< char* >( command.str().c_str() );
00665 args[ 3 ] = NULL;
00666
00667 g_spawn_async_with_pipes( ".", args, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
00668 NULL, NULL, &pid, NULL, NULL, NULL, &gerror );
00669
00670
00671 while ( ! WIFEXITED( status ) && visible )
00672 {
00673 timespec ts = { 0, 100000000L };
00674 while ( gtk_events_pending() )
00675 gtk_main_iteration();
00676 nanosleep( &ts, NULL );
00677 gtk_progress_bar_pulse( progressBar );
00678 g_object_get( G_OBJECT( dialog ), "visible", &visible, static_cast<gchar*>( NULL ) );
00679 waitpid( pid, &status, WNOHANG );
00680 }
00681 if ( ! WIFEXITED( status ) )
00682 {
00683 kill( pid, SIGTERM );
00684 waitpid( pid, &status, WNOHANG );
00685 if ( ! WIFEXITED( status ) )
00686 {
00687 timespec ts = { 0, 100000000L };
00688 nanosleep( &ts, NULL );
00689 kill( pid, SIGKILL );
00690 waitpid( pid, &status, 0 );
00691 }
00692 }
00693 gtk_widget_hide( dialog );
00694 }
00695 return importedFile;
00696 }
00697
00701 void KinoCommon::insertFile( )
00702 {
00703 changePageRequest( PAGE_EDITOR );
00704 char * filename = this->getFileToOpen( _( "Choose a DV or SMIL file to insert" ) );
00705 if ( filename && strcmp( filename, "" ) )
00706 {
00707 switch ( checkFile( filename ) )
00708 {
00709 case AVI:
00710 case RAW_DV:
00711 case QT:
00712 if ( loadMediaObject( filename, g_currentFrame == -1 ? 0 : g_currentFrame ) )
00713 {
00714 hasListChanged = TRUE;
00715 getPlayList() ->SetDirty( true );
00716 break;
00717 }
00718
00719 case UNKNOWN_FORMAT:
00720 {
00721 const std::string& importedFile = importFile( filename );
00722 if ( loadMediaObject( const_cast<char*>( importedFile.c_str() ), g_currentFrame == -1 ? 0 : g_currentFrame ) )
00723 {
00724 hasListChanged = TRUE;
00725 getPlayList() ->SetDirty( true );
00726 }
00727 else
00728 {
00729 modal_message( _( "Failed to load media file \"%s\"" ), importedFile.c_str() );
00730 }
00731 break;
00732 }
00733 case PLAYLIST:
00734 if ( loadPlayList( filename, g_currentFrame ) )
00735 {
00736 hasListChanged = TRUE;
00737 getPlayList() ->SetDirty( true );
00738 getPageEditor() ->snapshot();
00739 }
00740 break;
00741 }
00742 }
00743
00744 setWindowTitle();
00745 moveToFrame( );
00746 }
00747
00751 void KinoCommon::appendFile( )
00752 {
00753 changePageRequest( PAGE_EDITOR );
00754 char * filename = this->getFileToOpen( _( "Choose a DV or SMIL file to append" ) );
00755 if ( filename && strcmp( filename, "" ) )
00756 {
00757 switch ( checkFile( filename ) )
00758 {
00759 case AVI:
00760 case RAW_DV:
00761 case QT:
00762 if ( loadMediaObject( filename, g_currentFrame + 1 ) )
00763 {
00764 g_currentFrame++;
00765 hasListChanged = TRUE;
00766 getPlayList() ->SetDirty( true );
00767 break;
00768 }
00769
00770 case UNKNOWN_FORMAT:
00771 {
00772 const std::string& importedFile = importFile( filename );
00773 if ( loadMediaObject( const_cast<char*>( importedFile.c_str() ), g_currentFrame + 1 ) )
00774 {
00775 g_currentFrame++;
00776 hasListChanged = TRUE;
00777 getPlayList() ->SetDirty( true );
00778 break;
00779 }
00780 else
00781 {
00782 modal_message( _( "Failed to load media file \"%s\"" ), importedFile.c_str() );
00783 }
00784 break;
00785 }
00786 case PLAYLIST:
00787
00788 if ( loadPlayList( filename, g_currentFrame + 1 ) )
00789 {
00790 g_currentFrame++;
00791 hasListChanged = TRUE;
00792 getPlayList() ->SetDirty( true );
00793 getPageEditor() ->snapshot();
00794 }
00795 break;
00796 }
00797 }
00798
00799 setWindowTitle();
00800 moveToFrame( );
00801 }
00802
00807 void KinoCommon::loadFile( )
00808 {
00809 changePageRequest( PAGE_EDITOR );
00810 char * filename = this->getFileToOpen( _( "Choose a DV or SMIL file" ) );
00811 bool askNewFile = true;
00812
00813 if ( filename && strcmp( filename, "" ) )
00814 {
00815 switch ( checkFile( filename ) )
00816 {
00817 case AVI:
00818 case RAW_DV:
00819 case QT:
00820 askNewFile = false;
00821 if ( newFile( ) )
00822 {
00823 GetEditorBackup() ->Clear();
00824 if ( loadMediaObject( filename, 0 ) )
00825 {
00826 g_currentFrame = 0;
00827 hasListChanged = TRUE;
00828 break;
00829 }
00830
00831 }
00832 else
00833 break;
00834 case UNKNOWN_FORMAT:
00835 if ( ( askNewFile && newFile( ) ) || ! askNewFile )
00836 {
00837 const std::string& importedFile = importFile( filename );
00838 GetEditorBackup() ->Clear();
00839 if ( loadMediaObject( const_cast<char*>( importedFile.c_str() ), 0 ) )
00840 {
00841 g_currentFrame = 0;
00842 hasListChanged = TRUE;
00843 }
00844 else
00845 {
00846 modal_message( _( "Failed to load media file \"%s\"" ), importedFile.c_str() );
00847 }
00848 }
00849 break;
00850 case PLAYLIST:
00851 if ( newFile( false ) )
00852 {
00853 GetEditorBackup() ->Clear();
00854 if ( loadPlayList( filename, 0 ) )
00855 {
00856 g_currentFrame = 0;
00857 hasListChanged = TRUE;
00858 getPageEditor( ) ->snapshot( );
00859 getPlayList( ) ->SetDocName( filename );
00860 getPlayList( ) ->SetDirty( false );
00861 fetchProjectMetadata( getPlayList()->GetDocId() );
00862 }
00863 }
00864 break;
00865 }
00866 }
00867 setWindowTitle();
00868 this->currentScene = 0;
00869 this->getCurrentPage()->clean();
00870 this->getCurrentPage()->start();
00871 moveToFrame( );
00872 }
00873
00878 bool KinoCommon::savePlayListAs( )
00879 {
00880 bool result = false;
00881 int format = 1;
00882 char *filename = this->getFileToSaveFormat( _( "Save the movie" ), format );
00883
00884 if ( filename && strcmp( filename, "" ) )
00885 {
00886
00887 char* extension = strrchr( filename, '.' );
00888 switch ( format )
00889 {
00890 case 0:
00891 case 1:
00892 if ( !extension || ( strcmp( extension, ".kino" ) &&
00893 strcmp( extension, ".smil" ) && strcmp( extension, ".xml" ) ) )
00894 strcat( filename, ".kino" );
00895 break;
00896 case 2:
00897 if ( !extension || strcmp( extension, ".eli" ) )
00898 strcat( filename, ".eli" );
00899 break;
00900 case 3:
00901 if ( !extension || strcmp( extension, ".srt" ) )
00902 strcat( filename, ".srt" );
00903 break;
00904 }
00905
00906
00907 FILE *f = fopen( filename, "r" );
00908 if ( f )
00909 {
00910 fclose( f );
00911 if ( modal_confirm( GTK_STOCK_SAVE, NULL, _("The file \"%s\" already exists. Do you want to continue?\n"), filename ) != GTK_RESPONSE_ACCEPT )
00912 return result;
00913 }
00914 switch ( format )
00915 {
00916 case 0:
00917 playlist.SetDocName( filename );
00918 result = savePlayList( filename );
00919 break;
00920 case 1:
00921 if ( !this->getPlayList()->SavePlayList( filename, true ) )
00922 {
00923 modal_message( _( "Could not save the XML" ) );
00924 result = false;
00925 }
00926 break;
00927 case 2:
00928 if ( g_currentFrame != -1 )
00929 {
00930 Frame& frame = *GetFramePool()->GetFrame();
00931 this->getPlayList() ->GetFrame( g_currentFrame, frame );
00932 if ( !this->getPlayList() ->SavePlayListEli( filename, frame.IsPAL() ) )
00933 {
00934 modal_message( _( "Could not save the ELI" ) );
00935 result = false;
00936 }
00937 GetFramePool()->DoneWithFrame( &frame );
00938 }
00939 else
00940 {
00941 modal_message( _( "Could not save the ELI" ) );
00942 result = false;
00943 }
00944 break;
00945 case 3:
00946 if ( !this->getPlayList()->SavePlayListSrt( filename ) )
00947 {
00948 modal_message( _( "Could not save the subtitle" ) );
00949 result = false;
00950 }
00951 break;
00952 }
00953 }
00954 return result;
00955 }
00956
00961 bool KinoCommon::savePlayList( )
00962 {
00963 bool result = true;
00964 if ( getPlayList() ->GetDocName() == "" )
00965 result = savePlayListAs( );
00966 else
00967 result = savePlayList( ( char * ) ( getPlayList() ->GetDocName().c_str() ) );
00968 return result;
00969 }
00970
00971
00975 void KinoCommon::saveFrame( )
00976 {
00977 if ( currentPage == PAGE_CAPTURE )
00978 {
00979 getPageCapture()->saveFrame();
00980 }
00981 else
00982 {
00983 char * filename = common->getFileToSave( _( "Save Still Frame" ) );
00984 if ( filename && strcmp( filename, "" ) )
00985 common->saveFrame( common->g_currentFrame, filename );
00986 }
00987 }
00988
00994 gboolean generate_file_preview( GtkWidget *preview, const char *filename )
00995 {
00996 FileHandler *mediaFile = 0;
00997 try
00998 {
00999 if ( strcmp( filename, "" ) )
01000 {
01001 switch ( common->checkFile( const_cast<char*>( filename ) ) )
01002 {
01003 case AVI:
01004 mediaFile = new AVIHandler();
01005 break;
01006 case RAW_DV:
01007 mediaFile = new RawHandler();
01008 break;
01009 case QT:
01010 #ifdef HAVE_LIBQUICKTIME
01011 mediaFile = new QtHandler();
01012 #endif
01013 break;
01014 }
01015 }
01016
01017 if ( mediaFile && mediaFile->Open( filename ) )
01018 {
01019
01020
01021 Frame& frame = *GetFramePool()->GetFrame();
01022 int frameNum = mediaFile->GetTotalFrames() < 61 ? ( mediaFile->GetTotalFrames() / 2 ) : 60;
01023
01024 if ( mediaFile->GetFrame( frame, frameNum ) >= 0 )
01025 {
01026 GdkDisplayer* display = new GdkDisplayer( preview, frame.GetWidth(), frame.GetHeight(), frame.IsWide() );
01027 unsigned char *pixels = new unsigned char[ FRAME_MAX_WIDTH * FRAME_MAX_HEIGHT * 4 ];
01028 frame.ExtractPreviewRGB( pixels );
01029 gdk_window_clear( preview->window );
01030 display->put( pixels );
01031 delete[] pixels;
01032 delete display;
01033 }
01034 GetFramePool()->DoneWithFrame( &frame );
01035 delete mediaFile;
01036 mediaFile = 0;
01037
01038 return true;
01039 }
01040 }
01041 catch ( string s )
01042 {
01043 cerr << "generate_file_preview exception: " << s << endl;
01044 }
01045 delete mediaFile;
01046
01047 return false;
01048 }
01049
01056 void update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
01057 {
01058 GtkWidget *preview;
01059 char *filename;
01060 gboolean have_preview;
01061
01062 preview = GTK_WIDGET( data );
01063 filename = gtk_file_chooser_get_preview_filename( file_chooser );
01064
01065 if ( filename && !g_file_test( filename, G_FILE_TEST_IS_DIR )) {
01066 have_preview = generate_file_preview( preview, filename );
01067 gtk_file_chooser_set_preview_widget_active( file_chooser,
01068 have_preview );
01069 }
01070 }
01071
01079 char *KinoCommon::getFileToOpen( char *title, bool isDVFile, GtkWidget *widget )
01080 {
01081 GtkWidget *dialog;
01082 GtkDrawingArea *preview;
01083 GtkFileFilter *filter;
01084
01085 dialog = gtk_file_chooser_dialog_new( title,
01086 getWidgetWindow(widget),
01087 GTK_FILE_CHOOSER_ACTION_OPEN,
01088 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
01089 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
01090 static_cast<gchar*>( NULL ) );
01091 gtk_dialog_set_alternative_button_order( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1 );
01092
01093
01094 if ( isDVFile )
01095 {
01096 preview = ( GtkDrawingArea* )gtk_drawing_area_new();
01097 gtk_widget_set_size_request( GTK_WIDGET(preview), 160, 120 );
01098 gtk_file_chooser_set_preview_widget( GTK_FILE_CHOOSER(dialog),
01099 GTK_WIDGET(preview) );
01100 g_signal_connect( dialog, "update-preview",
01101 G_CALLBACK(update_preview_cb), preview );
01102
01103 filter = gtk_file_filter_new();
01104 gtk_file_filter_set_name( filter, "All Files" );
01105 gtk_file_filter_add_pattern( filter, "*" );
01106 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
01107
01108 filter = gtk_file_filter_new();
01109 gtk_file_filter_set_name( filter, "SMIL Files (*.smil, *.kino)" );
01110 gtk_file_filter_add_pattern( filter, "*.smil" );
01111 gtk_file_filter_add_pattern( filter, "*.kino" );
01112 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
01113
01114 filter = gtk_file_filter_new();
01115 gtk_file_filter_set_name( filter, "Raw DV Files (*.dv, *.dif)" );
01116 gtk_file_filter_add_pattern( filter, "*.dv" );
01117 gtk_file_filter_add_pattern( filter, "*.dif" );
01118 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
01119
01120 filter = gtk_file_filter_new();
01121 gtk_file_filter_set_name( filter, "AVI Files (*.avi)" );
01122 gtk_file_filter_add_pattern( filter, "*.avi" );
01123 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
01124
01125 #ifdef HAVE_LIBQUICKTIME
01126 filter = gtk_file_filter_new();
01127 gtk_file_filter_set_name( filter, "Quicktime Files (*.mov)" );
01128 gtk_file_filter_add_pattern( filter, "*.mov" );
01129 gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter );
01130 #endif
01131 }
01132
01133 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
01134 if ( last_directory == "" )
01135 {
01136 string directory = getPlayList() ->GetProjectDirectory( );
01137 if ( directory != "" )
01138 gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( dialog ), ( char * ) ( directory + "/" ).c_str() );
01139 last_directory = directory;
01140 }
01141 else
01142 {
01143 gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( dialog ), ( char * ) ( last_directory + "/" ).c_str() );
01144 }
01145 if (gtk_dialog_run( GTK_DIALOG( dialog ) ) == GTK_RESPONSE_ACCEPT) {
01146 gchar *filename = gtk_file_chooser_get_filename(
01147 GTK_FILE_CHOOSER( dialog ));
01148 if ( filename )
01149 {
01150 strncpy( tempFileName, filename, sizeof(tempFileName) );
01151 last_directory = directory_utils::get_directory_from_file( filename );
01152 g_free( filename );
01153 }
01154 else
01155 strcpy( tempFileName, "" );
01156 } else
01157 strcpy( tempFileName, "" );
01158
01159 gtk_widget_destroy( dialog );
01160 return tempFileName;
01161 }
01162
01163 char *KinoCommon::getFileToSaveFormat( char *title, GtkWidget *widget, int& format )
01164 {
01165 GtkWidget *dialog;
01166 GtkComboBox* formatCombo = NULL;
01167
01168 dialog = gtk_file_chooser_dialog_new( title,
01169 getWidgetWindow(widget),
01170 GTK_FILE_CHOOSER_ACTION_SAVE,
01171 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
01172 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
01173 static_cast<gchar*>( NULL ) );
01174 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1);
01175 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
01176 gtk_dialog_set_default_response( GTK_DIALOG( dialog ), GTK_RESPONSE_ACCEPT );
01177
01178 if ( format )
01179 {
01180 GtkWidget* hbox = gtk_hbox_new( FALSE, 6 );
01181 GtkWidget* label = gtk_label_new_with_mnemonic( _("F_ormat:") );
01182
01183 formatCombo = GTK_COMBO_BOX( gtk_combo_box_new_text() );
01184 gtk_widget_show( hbox );
01185 gtk_widget_show( label );
01186 gtk_widget_show( GTK_WIDGET( formatCombo ) );
01187
01188 gtk_combo_box_append_text( formatCombo, "SMIL 2.0" );
01189 gtk_combo_box_append_text( formatCombo, "Kino < 0.9.1 XML" );
01190 gtk_combo_box_append_text( formatCombo, "MJPEG Tools ELI" );
01191 gtk_combo_box_append_text( formatCombo, "SRT subtitle" );
01192 gtk_combo_box_set_active( formatCombo, 0 );
01193 gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, FALSE, 0 );
01194 gtk_box_pack_start( GTK_BOX(hbox), GTK_WIDGET(formatCombo), FALSE, FALSE, 0 );
01195 gtk_file_chooser_set_extra_widget( GTK_FILE_CHOOSER( dialog ), hbox );
01196 }
01197
01198 if ( last_directory == "" )
01199 {
01200 string directory = getPlayList() ->GetProjectDirectory( );
01201 if ( directory != "" )
01202 gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( dialog ), ( char * ) ( directory + "/" ).c_str() );
01203 last_directory = directory;
01204 }
01205 else
01206 {
01207 gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER( dialog ), ( char * ) ( last_directory + "/" ).c_str() );
01208 }
01209 if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
01210 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
01211 if ( filename )
01212 {
01213 strncpy (tempFileName, filename, sizeof(tempFileName));
01214 g_free (filename);
01215 }
01216 else
01217 strcpy( tempFileName, "" );
01218 } else
01219 strcpy(tempFileName, "");
01220 if ( format )
01221 format = gtk_combo_box_get_active( GTK_COMBO_BOX(formatCombo) );
01222
01223 gtk_widget_destroy (dialog);
01224 return tempFileName;
01225 }
01226
01240 int KinoCommon::checkFile( char *FileName )
01241 {
01242
01243 std::ifstream file( FileName );
01244 std::vector<char> buffer( 22, '\0' );
01245 file.read( &buffer[ 0 ], buffer.size() );
01246
01247
01248 if ( file.bad() )
01249 {
01250 cerr << "> Error reading file: " << FileName << endl;
01251 return UNKNOWN_FORMAT;
01252 }
01253
01254
01255 if ( file.eof() )
01256 {
01257 cerr << "> File size < " << buffer.size() << " bytes: " << FileName << endl;
01258 return UNKNOWN_FORMAT;
01259 }
01260
01261
01262 const std::string filename( FileName );
01263 const std::string suffix( filename.begin() + filename.rfind( "." ), filename.end() );
01264
01265 if ( suffix == ".avi" )
01266 {
01267 return AVI;
01268 }
01269 else if ( suffix == ".dv" || suffix == ".dif" )
01270 {
01271
01272 const unsigned char * const p = reinterpret_cast<unsigned char*>( &buffer[ 0 ] );
01273 const int section_type = p[ 0 ] >> 5;
01274 const int dif_sequence = p[ 1 ] >> 4;
01275
01276 if ( 0 == section_type && 0 == dif_sequence )
01277 return RAW_DV;
01278
01279 return UNKNOWN_FORMAT;
01280 }
01281 else if ( suffix == ".mov" )
01282 {
01283 return QT;
01284 }
01285
01286
01287 const std::string smil_magic( "<?xml version=\"1.0\"?>" );
01288 if ( std::string( buffer.begin(), buffer.begin() + smil_magic.size() ) == smil_magic )
01289 return PLAYLIST;
01290
01291
01292 return UNKNOWN_FORMAT;
01293 }
01294
01297 void KinoCommon::setWindowTitle( )
01298 {
01299 GtkWidget * mainWindow = lookup_widget( this->getWidget(), "main_window" );
01300 if ( mainWindow )
01301 {
01302 char strbuf[ 1024 ];
01303 if ( getPlayList() ->GetDocName( ) == "" )
01304 strcpy( strbuf, _( "Untitled" ) );
01305 else
01306 strcpy( strbuf, getPlayList() ->GetDocName( ).c_str() );
01307 if ( getPlayList( ) ->IsDirty( ) )
01308 strcat( strbuf, _(" (modified) ") );
01309 strcat( strbuf, " - Kino" );
01310 gtk_window_set_title( GTK_WINDOW( mainWindow ), strbuf );
01311 }
01312 if ( getPlayList()->GetDocName( ) != "" && !getPlayList()->IsDirty() )
01313 {
01314 Preferences::getInstance().addRecentFile( getPlayList()->GetDocName() );
01315 updateRecentFiles();
01316 }
01317 }
01318
01325 bool KinoCommon::loadMediaObject( char *file, int before )
01326 {
01327
01328 PlayList newList;
01329 bool result = true;
01330
01331 try
01332 {
01333 if ( newList.LoadMediaObject( file ) )
01334 {
01335 this->getPlayList() ->InsertPlayList( newList, before );
01336 getPageEditor() ->snapshot();
01337 }
01338 else
01339 {
01340 result = false;
01341 }
01342 }
01343 catch ( string s )
01344 {
01345 cerr << "Could not load file " << file << ", because an exception has occurred: " << endl;
01346 cerr << s << endl;
01347 result = false;
01348 }
01349
01350 return result;
01351 }
01352
01353
01360 bool KinoCommon::loadPlayList( char *file, int before )
01361 {
01362
01363 PlayList newList;
01364 bool result = newList.LoadPlayList( file );
01365
01366 if ( result )
01367 {
01368 this->getPlayList() ->InsertPlayList( newList, before );
01369
01370
01371 if ( newList.GetDocId() != "" )
01372 this->getPlayList()->SetDocId( newList.GetDocId().c_str() );
01373 if ( newList.GetDocTitle() != "" )
01374 this->getPlayList()->SetDocTitle( newList.GetDocTitle().c_str() );
01375 }
01376 else
01377 modal_message( _( "Could not load the SMIL file \"%s\"" ), file );
01378
01379 return result;
01380 }
01381
01387 bool KinoCommon::savePlayList( char *file )
01388 {
01389 bool result = false;
01390 result = getPlayList() ->SavePlayList( file );
01391
01392 if ( result )
01393 setWindowTitle( );
01394 else
01395 modal_message( _( "Could not save the SMIL" ) );
01396
01397 return result;
01398 }
01399
01400 static int tohex( char p )
01401 {
01402 return isdigit( p ) ? p - '0' : tolower( p ) - 'a' + 10;
01403 }
01404
01405 static char *url_decode( char *dest, char *src )
01406 {
01407 char *p = dest;
01408
01409 while ( *src )
01410 {
01411 if ( *src == '%' )
01412 {
01413 *p ++ = ( tohex( *( src + 1 ) ) << 4 ) | tohex( *( src + 2 ) );
01414 src += 3;
01415 }
01416 else
01417 {
01418 *p ++ = *src ++;
01419 }
01420 }
01421
01422 *p = *src;
01423
01424 return dest;
01425 }
01426
01433 void KinoCommon::bulkLoad( int argc, char* argv[] )
01434 {
01435 char temp[ PATH_MAX + NAME_MAX ];
01436 char filename[ PATH_MAX + NAME_MAX ];
01437
01438 for ( int i = 1; i < argc; ++i )
01439 {
01440
01441
01442 if ( NULL != realpath( url_decode( temp, argv[ i ] ), filename ) )
01443 {
01444
01445 switch ( checkFile( filename ) )
01446 {
01447 case AVI:
01448 case RAW_DV:
01449 case QT:
01450
01451 if ( currentPage == PAGE_TRIM )
01452 {
01453 gtk_entry_set_text( GTK_ENTRY( lookup_widget( widget, "entry_trim_clip" ) ), filename );
01454 }
01455 else if ( loadMediaObject( filename, this->getPlayList() ->GetNumFrames() ) )
01456 {
01457 if ( g_currentFrame == -1 )
01458 g_currentFrame = 0;
01459 this->hasListChanged = TRUE;
01460 }
01461 else
01462 {
01463 cerr << "KinoCommon::bulkLoad: Failed to load " << filename << endl;
01464 }
01465 break;
01466 case PLAYLIST:
01467 {
01468 int last_count = getPlayList( ) ->GetNumFrames( );
01469 string last_doc_name = getPlayList( ) ->GetDocName( );
01470 if ( loadPlayList( filename, last_count ) )
01471 {
01472 if ( last_count == 0 && last_doc_name == "" )
01473 {
01474 getPlayList() ->SetDocName( filename );
01475 getPlayList() ->SetDirty( false );
01476 }
01477 else
01478 {
01479 getPlayList() ->SetDirty( true );
01480 }
01481 getPageEditor() ->snapshot();
01482 if ( g_currentFrame == -1 )
01483 g_currentFrame = 0;
01484 this->hasListChanged = TRUE;
01485 }
01486 }
01487 break;
01488 case UNKNOWN_FORMAT:
01489 {
01490 const std::string& importedFile = importFile( filename );
01491 if ( currentPage == PAGE_TRIM )
01492 {
01493 gtk_entry_set_text( GTK_ENTRY( lookup_widget( widget, "entry_trim_clip" ) ), importedFile.c_str() );
01494 }
01495 else if ( loadMediaObject( const_cast<char*>( importedFile.c_str() ), this->getPlayList() ->GetNumFrames() ) )
01496 {
01497 if ( g_currentFrame == -1 )
01498 g_currentFrame = 0;
01499 hasListChanged = TRUE;
01500 getPlayList() ->SetDirty( true );
01501 }
01502 else
01503 {
01504 modal_message( _( "Failed to load media file \"%s\"" ), importedFile.c_str() );
01505 }
01506 }
01507 break;
01508 }
01509 }
01510 else
01511 {
01512 cerr << "KinoCommon::bulkLoad: Unable to resolve " << filename << endl;
01513 }
01514 }
01515
01516 setWindowTitle( );
01517 }
01518
01525 void KinoCommon::saveFrame( int abs_frame_num, char *file )
01526 {
01527 Frame *frame = GetFramePool()->GetFrame();
01528 if ( frame != NULL )
01529 {
01530 unsigned char pixels[ FRAME_MAX_WIDTH * FRAME_MAX_HEIGHT * 4 ];
01531 GError *gerror = NULL;
01532 this->getPlayList()->GetFrame( this->g_currentFrame, *frame );
01533
01534 frame->ExtractPreviewRGB( pixels );
01535 GdkPixbuf *im = gdk_pixbuf_new_from_data
01536 ( pixels, GDK_COLORSPACE_RGB, FALSE, 8,
01537 frame->GetWidth(), frame->GetHeight(),
01538 frame->GetWidth() * 3, NULL, NULL );
01539
01540
01541 {
01542 int width = frame->GetWidth();
01543 if ( frame->IsWide() )
01544 width = frame->IsPAL() ? 1024 : 854;
01545 AspectRatioCalculator calc( width, frame->GetHeight(),
01546 frame->GetWidth(), frame->GetHeight(),
01547 frame->IsPAL(), frame->IsWide() );
01548 GdkPixbuf *scaled = gdk_pixbuf_scale_simple( im, calc.width, calc.height, GDK_INTERP_HYPER );
01549 g_object_unref( im );
01550 im = scaled;
01551 }
01552
01553 const std::string FileName( file );
01554 const std::string suffix( FileName.begin() + FileName.rfind( "." ), FileName.end() );
01555 if ( suffix == ".png" )
01556 gdk_pixbuf_save( im, file, "png", &gerror, static_cast<gchar*>( NULL ) );
01557 else
01558 gdk_pixbuf_save( im, file, "jpeg", &gerror, "quality", "80", static_cast<gchar*>( NULL ) );
01559 if ( gerror != NULL )
01560 {
01561 modal_message( gerror->message );
01562 g_error_free( gerror );
01563 }
01564 g_object_unref( im );
01565 GetFramePool()->DoneWithFrame( frame );
01566 }
01567 }
01568
01569
01576 void KinoCommon::keyboardFeedback( const char *cmd, const char *msg )
01577 {
01578 char s[ 256 ];
01579
01580 strcpy( s, cmd );
01581 strcat( s, " " );
01582 strcat( s, msg );
01583
01584 setStatusBar( s );
01585 }
01586
01595 int KinoCommon::moveToFrame( )
01596 {
01597 if ( currentPage == PAGE_TRIM )
01598 {
01599 getPageTrim() ->movedToFrame( getPageTrim() ->getPosition() );
01600 return getPageTrim() ->getPosition();
01601 }
01602 else if ( currentPage == PAGE_MAGICK )
01603 {
01604 return moveToFrame( g_currentFrame - playlist.FindStartOfScene( g_currentFrame ) );
01605 }
01606 return moveToFrame( g_currentFrame );
01607 }
01608
01617 int KinoCommon::moveToFrame( int frame )
01618 {
01619 if ( currentPage == PAGE_TRIM || currentPage == PAGE_MAGICK )
01620 {
01621 getCurrentPage()->movedToFrame( frame );
01622 return frame;
01623 }
01624
01625 if ( frame >= 0 && frame < getPlayList() ->GetNumFrames() )
01626 g_currentFrame = frame;
01627 else if ( frame >= getPlayList() ->GetNumFrames() )
01628 g_currentFrame = getPlayList() ->GetNumFrames() - 1;
01629 else if ( frame < 0 && getPlayList() ->GetNumFrames() > 0 )
01630 g_currentFrame = 0;
01631 else
01632 g_currentFrame = -1;
01633
01634 getCurrentPage() ->movedToFrame( g_currentFrame );
01635
01636 return g_currentFrame;
01637 }
01638
01645 int KinoCommon::moveByFrames( int frames )
01646 {
01647 if ( frames == 0 )
01648 return g_currentFrame;
01649
01650 int frame = g_currentFrame + frames;
01651
01652 if ( currentPage == PAGE_TRIM )
01653 frame = getPageTrim() ->getPosition() + frames;
01654
01655 return moveToFrame( frame );
01656 }
01657
01663 void KinoCommon::showFrameInfo( int i )
01664 {
01665
01666 getCurrentPage() ->showFrameInfo( i );
01667 }
01668
01674 void KinoCommon::showFrameMoreInfo( Frame &frame, FileHandler *media )
01675 {
01676 if ( showMoreInfo )
01677 {
01678 GtkLabel *label = GTK_LABEL( lookup_widget( widget, "label_properties" ) );
01679 char* s = new char[ 2048 ];
01680
01681 if ( media != NULL )
01682 {
01683 TimeCode tc;
01684 AudioInfo ainfo;
01685 string format;
01686
01687 frame.GetTimeCode( tc );
01688 frame.GetAudioInfo( ainfo );
01689
01690 string ext = media->GetExtension();
01691 if ( ext == ".dv" || ext == ".dif" )
01692 format = "Raw DV";
01693 else if ( ext == ".avi" )
01694 {
01695 AVIHandler * avi = dynamic_cast< AVIHandler* >( media );
01696 format = ( avi->GetFormat() == AVI_DV1_FORMAT ) ? "AVI, Type 1" : "AVI, Type 2";
01697 if ( avi->GetOpenDML() )
01698 format += ", OpenDML";
01699 }
01700 else if ( ext == ".mov" )
01701 format = "Quicktime";
01702 else
01703 format = "unknown";
01704
01705
01706 snprintf( s, 2048, _( "%s\n%s\n%2.2d:%2.2d:%2.2d:%2.2d\n%s\n%d bit, %d KHz, %d samples\n%d x %d, %s, %s, %2.2f fps" ),
01707 basename( media->GetFilename().c_str() ),
01708 frame.GetRecordingDate().c_str(),
01709 tc.hour, tc.min, tc.sec, tc.frame,
01710 format.c_str(),
01711 ainfo.quantization, ainfo.frequency / 1000, ainfo.samples,
01712 frame.GetWidth(), frame.GetHeight(),
01713 frame.IsPAL() ? "PAL" : "NTSC",
01714 frame.IsWide() ? "16:9" : "4:3",
01715 frame.GetFrameRate()
01716 );
01717 }
01718 else
01719 sprintf( s, "\n\n\n" );
01720
01721 gtk_label_set_text( label, s );
01722 delete[] s;
01723 }
01724 }
01725
01730 void KinoCommon::start()
01731 {
01732 gtk_label_set_text( GTK_LABEL( lookup_widget( getWidget(), "position_label_current" ) ), "" );
01733 gtk_label_set_text( GTK_LABEL( lookup_widget( getWidget(), "position_label_total" ) ), "" );
01734
01735 getCurrentPage() ->start();
01736 activateWidgets();
01737
01738 toggleComponents( getComponentState(), false );
01739 if ( currentPage != PAGE_MAGICK )
01740 toggleComponents( VIDEO_STOP, true );
01741 commitComponentState();
01742 }
01743
01747 void KinoCommon::clean()
01748 {
01749 getCurrentPage() ->clean();
01750 }
01751
01755 gboolean KinoCommon::processKeyboard( GdkEventKey *event )
01756 {
01757 return getCurrentPage() ->processKeyboard( event );
01758 }
01759
01764 gboolean KinoCommon::processCommand( char *cmd )
01765 {
01766 return getCurrentPage() ->processCommand( cmd );
01767 }
01768
01772 void KinoCommon::selectScene( int scene )
01773 {
01774 getCurrentPage() ->selectScene( scene );
01775 }
01776
01780 void KinoCommon::videoStartOfMovie()
01781 {
01782 if ( ! is_component_state_changing )
01783 {
01784
01785 getCurrentPage() ->videoStartOfMovie();
01786
01787 commitComponentState();
01788 }
01789 }
01790
01794 void KinoCommon::videoPreviousScene()
01795 {
01796 if ( ! is_component_state_changing )
01797 {
01798
01799 getCurrentPage() ->videoPreviousScene();
01800
01801 commitComponentState();
01802 }
01803 }
01804
01808 void KinoCommon::videoStartOfScene()
01809 {
01810 if ( ! is_component_state_changing )
01811 {
01812
01813 getCurrentPage() ->videoStartOfScene();
01814
01815 commitComponentState();
01816 }
01817 }
01818
01822 void KinoCommon::videoRewind()
01823 {
01824 if ( ! is_component_state_changing )
01825 {
01826
01827 getCurrentPage() ->videoRewind();
01828
01829 commitComponentState();
01830 }
01831 }
01832
01836 void KinoCommon::videoBack(int step)
01837 {
01838 if ( ! is_component_state_changing )
01839 {
01840
01841 getCurrentPage() ->videoBack(step);
01842
01843 commitComponentState();
01844 }
01845 }
01846
01850 void KinoCommon::videoPlay()
01851 {
01852 if ( ! is_component_state_changing )
01853 {
01854
01855 getCurrentPage() ->videoPlay();
01856
01857 commitComponentState();
01858 }
01859 }
01860
01864 void KinoCommon::videoForward(int step)
01865 {
01866 if ( ! is_component_state_changing )
01867 {
01868
01869 getCurrentPage() ->videoForward(step);
01870
01871 commitComponentState();
01872 }
01873 }
01874
01878 void KinoCommon::videoFastForward()
01879 {
01880 if ( ! is_component_state_changing )
01881 {
01882
01883 getCurrentPage() ->videoFastForward();
01884
01885 commitComponentState();
01886 }
01887 }
01888
01892 void KinoCommon::videoNextScene()
01893 {
01894 if ( ! is_component_state_changing )
01895 {
01896
01897 getCurrentPage() ->videoNextScene();
01898
01899 commitComponentState();
01900 }
01901 }
01902
01906 void KinoCommon::videoEndOfScene()
01907 {
01908 if ( ! is_component_state_changing )
01909 {
01910
01911 getCurrentPage() ->videoEndOfScene();
01912
01913 commitComponentState();
01914 }
01915 }
01916
01920 void KinoCommon::videoEndOfMovie()
01921 {
01922 if ( ! is_component_state_changing )
01923 {
01924
01925 getCurrentPage() ->videoEndOfMovie();
01926
01927 commitComponentState();
01928 }
01929 }
01930
01934 void KinoCommon::videoPause()
01935 {
01936 if ( ! is_component_state_changing )
01937 {
01938
01939 getCurrentPage() ->videoPause();
01940
01941 commitComponentState();
01942 }
01943 }
01944
01948 void KinoCommon::videoStop()
01949 {
01950 if ( ! is_component_state_changing )
01951 {
01952
01953 getCurrentPage() ->videoStop();
01954
01955 commitComponentState();
01956 }
01957 }
01958
01964 void KinoCommon::videoShuttle( int angle )
01965 {
01966 if ( ! is_component_state_changing )
01967 {
01968 toggleComponents( getComponentState(), false );
01969
01970 if ( ! Preferences::getInstance().dropFrame )
01971
01972 gtk_range_set_value( this->video_shuttle, angle );
01973 getCurrentPage() ->videoShuttle( angle );
01974
01975 commitComponentState();
01976 }
01977 }
01978
01979 void KinoCommon::windowMoved()
01980 {
01981 getCurrentPage() ->windowMoved();
01982 }
01983
01984 void KinoCommon::visibilityChanged( gboolean visible )
01985 {
01986 getCurrentPage() ->visibilityChanged( visible );
01987 }
01988
01994 void KinoCommon::activateWidgets()
01995 {
01996 component_enum pattern = ( component_enum ) ( this->getCurrentPage()->activate() ^ this->getCurrentPage()->deactivate() );
01997 gtk_widget_set_sensitive( lookup_widget( widget, "undo" ), pattern & EDIT_MENU );
01998 gtk_widget_set_sensitive( lookup_widget( widget, "redo" ), pattern & EDIT_MENU );
01999 gtk_widget_set_sensitive( lookup_widget( widget, "copy_current_scene" ), pattern & EDIT_MENU );
02000 gtk_widget_set_sensitive( lookup_widget( widget, "cut_current_scene" ), pattern & EDIT_MENU );
02001 gtk_widget_set_sensitive( lookup_widget( widget, "paste_before_current_frame" ), pattern & EDIT_MENU );
02002 gtk_widget_set_sensitive( lookup_widget( widget, "split_scene" ), pattern & EDIT_MENU );
02003 gtk_widget_set_sensitive( lookup_widget( widget, "join_scenes" ), pattern & EDIT_MENU );
02004 gtk_widget_set_sensitive( lookup_widget( widget, "button_undo" ), pattern & EDIT_MENU );
02005 gtk_widget_set_sensitive( lookup_widget( widget, "button_redo" ), pattern & EDIT_MENU );
02006 gtk_widget_set_sensitive( lookup_widget( widget, "button_cut" ), pattern & EDIT_MENU );
02007 gtk_widget_set_sensitive( lookup_widget( widget, "button_copy" ), pattern & EDIT_MENU );
02008 gtk_widget_set_sensitive( lookup_widget( widget, "button_paste" ), pattern & EDIT_MENU );
02009 gtk_widget_set_sensitive( lookup_widget( widget, "button_split" ), pattern & EDIT_MENU );
02010 gtk_widget_set_sensitive( lookup_widget( widget, "button_join" ), pattern & EDIT_MENU );
02011 GetStoryboard() ->setSensitive( pattern & SCENE_LIST );
02012 gtk_widget_set_sensitive( GTK_WIDGET( video_start_movie_button ), pattern & VIDEO_START_OF_MOVIE );
02013 gtk_widget_set_sensitive( GTK_WIDGET( video_start_scene_button ), pattern & VIDEO_START_OF_SCENE );
02014 gtk_widget_set_sensitive( GTK_WIDGET( video_rewind_button ), pattern & VIDEO_REWIND );
02015 gtk_widget_set_sensitive( GTK_WIDGET( video_back_button ), pattern & VIDEO_BACK );
02016 gtk_widget_set_sensitive( GTK_WIDGET( video_play_button ), pattern & VIDEO_PLAY );
02017 gtk_widget_set_sensitive( GTK_WIDGET( video_stop_button ), pattern & VIDEO_STOP );
02018 gtk_widget_set_sensitive( GTK_WIDGET( video_forward_button ), pattern & VIDEO_FORWARD );
02019 gtk_widget_set_sensitive( GTK_WIDGET( video_fast_forward_button ), pattern & VIDEO_FAST_FORWARD );
02020 gtk_widget_set_sensitive( GTK_WIDGET( video_end_scene_button ), pattern & VIDEO_NEXT_SCENE );
02021 gtk_widget_set_sensitive( GTK_WIDGET( video_end_movie_button ), pattern & VIDEO_END_OF_MOVIE );
02022 gtk_widget_set_sensitive( GTK_WIDGET( video_shuttle ), pattern & VIDEO_SHUTTLE );
02023 }
02024
02025
02038 void KinoCommon::toggleComponents( component_enum pattern, bool state )
02039 {
02040
02041 this->is_component_state_changing = true;
02042
02043 if ( state )
02044 this->component_state |= pattern;
02045 else
02046 this->component_state ^= pattern;
02047
02048 if ( pattern & VIDEO_START_OF_MOVIE )
02049 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( video_start_movie_button ), state );
02050 if ( pattern & VIDEO_START_OF_SCENE )
02051 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( video_start_scene_button ), state );
02052 if ( pattern & VIDEO_REWIND )
02053 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( video_rewind_button ), state );
02054 if ( pattern & VIDEO_BACK )
02055 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( video_back_button ), state );
02056 if ( pattern & VIDEO_PLAY )
02057 {
02058 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( video_play_button ), state );
02059 if ( state )
02060 gtk_image_set_from_file( GTK_IMAGE( lookup_widget( widget, "pixmap_play_pause" ) ), DATADIR "/kino/stock_media-pause-16.png" );
02061 else
02062 gtk_image_set_from_file( GTK_IMAGE( lookup_widget( widget, "pixmap_play_pause" ) ), DATADIR "/kino/stock_media-play-16.png" );
02063 }
02064 if ( pattern & VIDEO_STOP )
02065 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( video_stop_button ), state );
02066 if ( pattern & VIDEO_FORWARD )
02067 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( video_forward_button ), state );
02068 if ( pattern & VIDEO_FAST_FORWARD )
02069 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( video_fast_forward_button ), state );
02070 if ( pattern & VIDEO_NEXT_SCENE )
02071 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( video_end_scene_button ), state );
02072 if ( pattern & VIDEO_END_OF_MOVIE )
02073 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( video_end_movie_button ), state );
02074
02075 }
02076
02079 component_enum KinoCommon::getComponentState()
02080 {
02081 return ( component_enum ) this->component_state;
02082 }
02083
02091 void KinoCommon::commitComponentState( component_enum pattern )
02092 {
02093 this->component_state |= pattern;
02094 this->is_component_state_changing = false;
02095 }
02096
02097
02106 void KinoCommon::setPreviewSize( float factor, bool noWarning )
02107 {
02108 static bool skip = false;
02109 int width = 720;
02110 int height = -1;
02111 bool isWide;
02112 bool isPAL;
02113
02114 if ( skip )
02115 return;
02116
02117
02118 if ( factor < 0 )
02119 {
02120 factor = float( Preferences::getInstance().previewSize ) / 10.0;
02121 factor += 0.5;
02122 if ( factor > 1.0 )
02123 factor = 0.0;
02124 }
02125
02126
02127 Preferences::getInstance().previewSize = int( factor * 10 );
02128
02129
02130 if ( Preferences::getInstance().displayFixed != ( factor > 0 ) )
02131 {
02132 Preferences::getInstance().displayFixed = ( factor > 0 );
02133 packIt( "packer_edit", "packer_edit_outer" );
02134 packIt( "packer_capture", "packer_capture_outer" );
02135 packIt( "packer_trim", "packer_trim_outer" );
02136 }
02137
02138 skip = true;
02139
02140 if ( factor == 0 )
02141 gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( lookup_widget( widget, "menuitem_zoom_fit" ) ), TRUE );
02142 else if ( factor == 0.5 )
02143 gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( lookup_widget( widget, "menuitem_zoom_50percent" ) ), TRUE );
02144 else
02145 gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( lookup_widget( widget, "menuitem_zoom_100percent" ) ), TRUE );
02146 skip = false;
02147
02148
02149 if ( factor == 0 )
02150 factor = 0.5;
02151
02152 if ( currentPage == PAGE_CAPTURE )
02153 {
02154 Frame *frame = getPageCapture()->getFrame();
02155 if ( frame != NULL )
02156 {
02157 width = frame->GetWidth();
02158 if ( frame->IsWide() )
02159 width = frame->IsPAL() ? 1024 : 854;
02160 AspectRatioCalculator calc( width, frame->GetHeight(),
02161 frame->GetWidth(), frame->GetHeight(),
02162 frame->IsPAL(), frame->IsWide() );
02163
02164 width = ( int ) ( calc.width * factor );
02165 height = ( int ) ( calc.height * factor );
02166 }
02167 }
02168 else if ( this->g_currentFrame != -1 )
02169 {
02170 Frame& frame = *GetFramePool()->GetFrame();
02171 this->getPlayList() ->GetFrame( this->g_currentFrame, frame );
02172 width = frame.GetWidth();
02173 if ( frame.IsWide() )
02174 width = frame.IsPAL() ? 1024 : 854;
02175 AspectRatioCalculator calc( width, frame.GetHeight(),
02176 frame.GetWidth(), frame.GetHeight(),
02177 frame.IsPAL(), frame.IsWide() );
02178 GetFramePool()->DoneWithFrame( &frame );
02179
02180 width = ( int ) ( calc.width * factor );
02181 height = ( int ) ( calc.height * factor );
02182 }
02183 else if ( Preferences::getInstance().defaultNormalisation != NORM_UNSPECIFIED )
02184 {
02185 height = Preferences::getInstance().defaultNormalisation == NORM_PAL ? 576 : 480;
02186 isPAL = Preferences::getInstance().defaultNormalisation == NORM_PAL;
02187 isWide = Preferences::getInstance().defaultAspect == ASPECT_169;
02188 AspectRatioCalculator calc( width, height, width, height,
02189 isPAL, isWide );
02190
02191 width = ( int ) ( calc.width * factor );
02192 height = ( int ) ( calc.height * factor );
02193 }
02194
02195 if ( height == -1 && ! noWarning )
02196 {
02197 modal_message( _( "The project is empty and the default preferences for video creation have not been specified." ) );
02198 }
02199 else if ( height != -1 )
02200 {
02201 cerr << "> setting video preview size to " << width << "x" << height << endl;
02202 GtkWidget *frameArea = lookup_widget( getWidget(), "packer_edit" );
02203 gtk_widget_set_size_request( frameArea, width + 4, height + 4 );
02204 frameArea = lookup_widget( getWidget(), "packer_capture" );
02205 gtk_widget_set_size_request( frameArea, width + 4, height + 4 );
02206 frameArea = lookup_widget( getWidget(), "packer_trim" );
02207 gtk_widget_set_size_request( frameArea, width + 4, height + 4 );
02208 }
02209 }
02210
02215 void KinoCommon::loadSplash( GtkDrawingArea *drawable )
02216 {
02217 if ( drawable && ( ( GtkWidget* ) drawable )->window )
02218 {
02219 GdkGC *gc = gdk_gc_new( ( ( GtkWidget* ) drawable ) ->window );
02220 if ( gc )
02221 {
02222 GdkPixbuf *splash = create_pixbuf( "about.jpeg" );
02223
02224 if ( splash )
02225 {
02226 int width = ( ( GtkWidget* ) drawable ) ->allocation.width;
02227 int height = ( ( GtkWidget* ) drawable ) ->allocation.height;
02228 GdkPixbuf *image = gdk_pixbuf_scale_simple( splash, width, height, GDK_INTERP_BILINEAR );
02229 if ( image )
02230 {
02231 gdk_draw_pixbuf( ( ( GtkWidget * ) drawable ) ->window, gc, image,
02232 0, 0, 0, 0, -1, -1, GDK_RGB_DITHER_NORMAL, 0, 0 );
02233 g_object_unref( image );
02234 }
02235 g_object_unref( splash );
02236 }
02237 g_object_unref( gc );
02238 }
02239 }
02240 }
02241
02242
02247 void KinoCommon::clearPreview( GtkDrawingArea *drawable )
02248 {
02249 GtkWidget * widget = GTK_WIDGET( drawable );
02250 if ( widget->window && GDK_IS_DRAWABLE( widget->window ) )
02251 {
02252 gdk_draw_rectangle ( widget->window, widget->style->black_gc, TRUE,
02253 widget->allocation.x, widget->allocation.y,
02254 widget->allocation.width, widget->allocation.height );
02255 }
02256 }
02257
02258
02264 void KinoCommon::packIt( const char *packerNameInner, const char* packerNameOuter )
02265 {
02266 GdkColor color;
02267 gdk_color_parse ( "black", &color );
02268
02269 GtkWidget *packer = lookup_widget( getWidget(), packerNameOuter );
02270 gtk_widget_modify_bg ( packer, GTK_STATE_NORMAL, &color );
02271
02272 packer = lookup_widget( getWidget(), packerNameInner );
02273 gtk_widget_modify_bg ( packer, GTK_STATE_NORMAL, &color );
02274
02275 GtkWidget *parent = gtk_widget_get_parent( packer );
02276 gtk_box_set_child_packing( GTK_BOX( parent ), packer,
02277 TRUE ,
02278 Preferences::getInstance().displayFixed ? FALSE : TRUE ,
02279 0 , GTK_PACK_START );
02280 }
02281
02282
02284 void KinoCommon::setStatusBar( const char * msg, ... )
02285 {
02286 va_list list;
02287 va_start( list, msg );
02288 static char prevMsg[ 1024 ] = "";
02289
02290 if ( strcmp( msg, "" ) == 0 )
02291 gtk_statusbar_pop( statusbar, 1 );
02292 else if ( strncmp( prevMsg, msg, 1023 ) != 0 )
02293 {
02294 if ( vsnprintf( prevMsg, 1023, msg, list ) != 0 )
02295 {
02296 gtk_statusbar_pop( statusbar, 1 );
02297 gtk_statusbar_push( statusbar, 1, prevMsg );
02298 }
02299 }
02300 }
02301
02302
02303 bool KinoCommon::exitKino( )
02304 {
02305 return newFile( false );
02306 }
02307
02308 void KinoCommon::setTimeFormat( SMIL::Time::TimeFormat format )
02309 {
02310 Preferences::getInstance().timeFormat = static_cast< int >( format );
02311 if ( g_currentFrame != -1 )
02312 {
02313 common->moveToFrame();
02314 GetStoryboard() ->reset();
02315 GetStoryboard() ->redraw();
02316 getCurrentPage() ->timeFormatChanged();
02317 }
02318 }
02319
02320
02321 void KinoCommon::setCurrentScene( int frame )
02322 {
02323 if ( frame > -1 )
02324 {
02325 int pos = 0;
02326
02327 vector <int> scenes = getPageEditor() ->GetScene();
02328 for ( pos = 0; pos < ( int ) scenes.size() && frame >= scenes[ pos ]; pos++ )
02329 ;
02330 if ( pos != currentScene && pos > -1 )
02331 {
02332 currentScene = pos;
02333 GetStoryboard() ->select( currentScene );
02334 }
02335 }
02336 }
02337
02338 void KinoCommon::publishPlayList()
02339 {
02340 std::ostringstream command;
02341 GError *gerror = NULL;
02342
02343 if ( playlist.GetDocName() == "" )
02344 {
02345 PlayList* copy = new PlayList( playlist );
02346 char filename[] = "/tmp/kino.XXXXXX";
02347 mkstemp( filename );
02348 copy->SavePlayList( filename );
02349 delete copy;
02350 command << "\"" << DATADIR << "/kino/scripts/publish/project.sh\" ";
02351 command << "\"" << filename << "\" ";
02352 command << "\"" << playlist.GetDocId().c_str() << "\" ";
02353 command << "\"" << playlist.GetDocTitle().c_str() << "\" ";
02354 char * args[ 4 ];
02355 args[ 0 ] = "/bin/sh";
02356 args[ 1 ] = "-c";
02357 args[ 2 ] = const_cast< char* >( command.str().c_str() );
02358 args[ 3 ] = NULL;
02359 g_spawn_sync( ".", args, NULL, (GSpawnFlags)0, NULL, NULL, NULL, NULL, NULL, &gerror );
02360 unlink( filename );
02361 }
02362 else
02363 {
02364 savePlayList();
02365 command << "\"" << DATADIR << "/kino/scripts/publish/project.sh\" ";
02366 command << "\"" << playlist.GetDocName() << "\" ";
02367 command << "\"" << playlist.GetDocId().c_str() << "\" ";
02368 command << "\"" << playlist.GetDocTitle().c_str() << "\" ";
02369 char * args[ 4 ];
02370 args[ 0 ] = "/bin/sh";
02371 args[ 1 ] = "-c";
02372 args[ 2 ] = const_cast< char* >( command.str().c_str() );
02373 args[ 3 ] = NULL;
02374 g_spawn_async_with_pipes( ".", args, NULL, (GSpawnFlags)0, NULL, NULL, NULL, NULL, NULL, NULL, &gerror );
02375 }
02376 }
02377
02378 void KinoCommon::publishFrame()
02379 {
02380 if ( currentPage == PAGE_CAPTURE )
02381 {
02382 modal_message( _("Sorry, you can not publish still frames from Capture.") );
02383 }
02384 else
02385 {
02386 char * filename = common->getFileToSave( _( "Save Still Frame" ) );
02387 if ( strcmp( filename, "" ) )
02388 {
02389 std::ostringstream command;
02390 GError *gerror = NULL;
02391
02392 common->saveFrame( common->g_currentFrame, filename );
02393
02394 command << "\"" << DATADIR << "/kino/scripts/publish/frame.sh\" ";
02395 command << "\"" << filename << "\" ";
02396 command << "\"" << getPlayList()->GetDocId().c_str() << "\" ";
02397 command << "\"" << getPlayList()->GetSeqAttribute( g_currentFrame, "title" ) << "\" ";
02398 command << "\"" << getPlayList()->GetDocTitle().c_str() << "\" ";
02399 char * args[ 4 ];
02400 args[ 0 ] = "/bin/sh";
02401 args[ 1 ] = "-c";
02402 args[ 2 ] = const_cast< char* >( command.str().c_str() );
02403 args[ 3 ] = NULL;
02404 g_spawn_async_with_pipes( ".", args, NULL, (GSpawnFlags)0, NULL, NULL, NULL, NULL, NULL, NULL, &gerror );
02405 }
02406 }
02407
02408 }
02409
02410 void KinoCommon::loadPlayList( char* filename )
02411 {
02412 if ( newFile( false ) )
02413 {
02414 GetEditorBackup() ->Clear();
02415 if ( loadPlayList( filename, 0 ) )
02416 {
02417 g_currentFrame = 0;
02418 hasListChanged = TRUE;
02419 getPageEditor( ) ->snapshot( );
02420 getPlayList( ) ->SetDocName( filename );
02421 getPlayList( ) ->SetDirty( false );
02422 fetchProjectMetadata( getPlayList()->GetDocId() );
02423 }
02424 }
02425 setWindowTitle();
02426 this->currentScene = 0;
02427 this->getCurrentPage()->clean();
02428 this->getCurrentPage()->start();
02429 moveToFrame( );
02430 setLastDirectory( directory_utils::get_directory_from_file( filename ) );
02431 }
02432
02433
02434 static void on_open_recent_activate ( GtkWidget *menuitem, gpointer user_data )
02435 {
02436 common->loadPlayList( static_cast< char* >( user_data ) );
02437 }
02438
02439 void KinoCommon::updateRecentFiles()
02440 {
02441 GtkWidget* menu = lookup_widget( widget, "file_menu" );
02442 vector< std::string >& recentFiles = Preferences::getInstance().recentFiles;
02443 int count = recentFiles.size();
02444 const int menuPosition = Preferences::getInstance().enablePublish ? 12 : 10;
02445
02446 if ( recentMenuItems.empty() && !recentFiles.empty() )
02447 {
02448 GtkWidget* widget = gtk_separator_menu_item_new();
02449 gtk_menu_shell_insert( GTK_MENU_SHELL( menu ), widget, menuPosition );
02450 gtk_widget_show( widget );
02451 }
02452 for ( vector< GtkWidget* >::iterator i = recentMenuItems.begin(); i != recentMenuItems.end(); i++ )
02453 {
02454 gtk_container_remove( GTK_CONTAINER( menu ), *i );
02455 gtk_widget_destroy( *i );
02456 }
02457 recentMenuItems.clear();
02458 for ( vector< std::string >::iterator i = recentFiles.begin(); i != recentFiles.end(); i++, count-- )
02459 {
02460 std::ostringstream ss;
02461 ss << "_" << count << ". "
02462 << StringUtils::replaceAll( basename( ( *i ).c_str() ), "_", "__" )
02463 << std::ends;
02464 GtkWidget* widget = gtk_menu_item_new_with_mnemonic( ss.str().c_str() );
02465 g_signal_connect( G_OBJECT( widget ), "activate", G_CALLBACK( on_open_recent_activate ), gpointer( (*i).c_str() ) );
02466 gtk_menu_shell_insert( GTK_MENU_SHELL( menu ), widget, menuPosition );
02467 gtk_widget_show( widget );
02468 recentMenuItems.push_back( widget );
02469 }
02470 }