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

kino_common.cc

Go to the documentation of this file.
00001 /*
00002 * kino_common.cc KINO GUI Common Object
00003 * Copyright (C) 2001 Charles Yates <charles.yates@pandora.be>
00004 * Copyright (C) 2001-2007 Dan Dennedy <dan@dennedy.org>
00005 *
00006 * This program is free software; you can redistribute it and/or modify
00007 * it under the terms of the GNU General Public License as published by
00008 * the Free Software Foundation; either version 2 of the License, or
00009 * (at your option) any later version.
00010 *
00011 * This program is distributed in the hope that it will be useful,
00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 * GNU General Public License for more details.
00015 *
00016 * You should have received a copy of the GNU General Public License
00017 * along with this program; if not, write to the Free Software Foundation,
00018 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include <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     // Initialise class variables from GUI components
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     // Create page objects
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     // Initialise other class variables
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     // Fetch the project metadata from CMS
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         // Parse the response
00197         xmlDocPtr doc = xmlParseMemory( buffer, size );
00198         xmlXPathInit();
00199         xmlXPathContextPtr xpathContext = xmlXPathNewContext( doc );
00200         if ( xpathContext )
00201         {
00202             xmlXPathObjectPtr xpathResult;
00203 
00204             // Extract the project ID
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             // Expand metaValues
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                     // Expand the label
00236                     if ( labelPath.length() > 5 && labelPath.substr( 0, 6 ) == "xpath:" )
00237                     {
00238                         // remove the xpath from the values
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                     // Expand the value
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             // Do nothing - action cancelled
00318             result = false;
00319             break;
00320         }
00321     }
00322     if ( result )
00323     {
00324         // Specifics for this class
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                 // Do nothing - leave files on the system
00350                 result = true;
00351                 break;
00352             default:
00353                 // Do nothing - action cancelled
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             // Call new File for each page
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     // Prompt for the Project Name/Id if applicable
00382     // added for tagesschau.de
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         // Check if media is loaded
00594         if ( g_currentFrame == -1 )
00595         {
00596             // Use Preferences/Defaults
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                     // Do nothing - action cancelled
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             // Use first frame
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         // Allow a script in home directory to override
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         // Check for cancellation or child exit
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             // Else, ask to import
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             // Else, ask to import
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             // Insert the new smil
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                 // Else, ask to import
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         // Append an extension if not supplied
00887         char* extension = strrchr( filename, '.' );
00888         switch ( format )
00889         {
00890             case 0: // SMIL
00891             case 1: // Legacy XML
00892                 if ( !extension || ( strcmp( extension, ".kino" ) && 
00893                     strcmp( extension, ".smil" ) && strcmp( extension, ".xml" ) ) )
00894                     strcat( filename, ".kino" );
00895                 break;
00896             case 2: // ELI
00897                 if ( !extension || strcmp( extension, ".eli" ) )
00898                     strcat( filename, ".eli" );
00899                 break;
00900             case 3: // SRT - SubRip subtitle
00901                 if ( !extension || strcmp( extension, ".srt" ) )
00902                     strcat( filename, ".srt" );
00903                 break;
00904         }
00905         
00906         // See if it exists already and warn user
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: // SMIL
00917                 playlist.SetDocName( filename );
00918                 result = savePlayList( filename );
00919                 break;
00920             case 1: // Legacy XML
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: // ELI
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: // SRT - SubRip subtitle
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             // Force GDK displayer because freeing the XvDisplayer will
01020             // causing nothing to show
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 //  gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), TRUE );
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     // Try reading a few bytes from the file ...
01243     std::ifstream file( FileName );
01244     std::vector<char> buffer( 22, '\0' );
01245     file.read( &buffer[ 0 ], buffer.size() );
01246 
01247     // If it didn't work, we're done ...
01248     if ( file.bad() )
01249     {
01250         cerr << "> Error reading file: " << FileName << endl;
01251         return UNKNOWN_FORMAT;
01252     }
01253 
01254     // If the file is shorter than our buffer, we're done ...
01255     if ( file.eof() )
01256     {
01257         cerr << "> File size < " << buffer.size() << " bytes: " << FileName << endl;
01258         return UNKNOWN_FORMAT;
01259     }
01260 
01261     // Start looking at file suffixes ...
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         // This bit of magic brought to you from the dvgrab AVI iso handler ...
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     // Check to see if it's SMIL ...
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     // No can do!
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         // propogate these new attributes
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         /* Get the real name of the file, to make sure that
01441         we store absolute paths in smil files */
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                 // a little hook to make the trimmer load the clip for insert edting
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         // resample pixel aspect
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             // this has bad thread interaction with dropFrame playback
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     // handle the cycle case
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     // save the new size
02127     Preferences::getInstance().previewSize = int( factor * 10 );
02128 
02129     // handle fixed size factors
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     // set the menu radio item
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     // this sets a sane factor for zoom to fit
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 /* expand */,
02278                                Preferences::getInstance().displayFixed ? FALSE : TRUE /* fill */,
02279                                0 /* padding */, 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 }

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