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