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

playlist.cc

Go to the documentation of this file.
00001 /*
00002 * playlist.cc -- SMIL implementation
00003 * Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
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 // C++ includes
00026 #include <iostream>
00027 #include <fstream>
00028 #include <sstream>
00029 #include <string>
00030 #include <list>
00031 #include <map>
00032 
00033 using std::cerr;
00034 using std::endl;
00035 using std::ends;
00036 using std::ofstream;
00037 using std::ostringstream;
00038 using std::list;
00039 using std::map;
00040 
00041 // C includes
00042 #include <libxml/xmlmemory.h>
00043 #include <libxml/parser.h>
00044 #include <libxml/tree.h>
00045 #include <pthread.h>
00046 #include <unistd.h>
00047 #include <sys/stat.h>
00048 #include <sys/types.h>
00049 #include <dirent.h>
00050 
00051 // local includes
00052 #include "playlist.h"
00053 #include "error.h"
00054 #include "filehandler.h"
00055 #include "frame.h"
00056 #include "stringutils.h"
00057 
00058 const xmlChar* SMIL20_NAMESPACE_HREF = reinterpret_cast< const xmlChar* >( "http://www.w3.org/2001/SMIL20/Language" );
00059 const string KINO_AUTOSAVE_DIR = string( getenv("HOME") ) + string( "/.kino-history" );
00060 
00065 class KinoFileMap : public FileMap
00066 {
00067 private:
00068     map < string, FileHandler* > filemap;
00069 
00070 public:
00071     virtual ~KinoFileMap()
00072     {}
00073 
00074     map<string, FileHandler*> &GetMap()
00075     {
00076         return filemap;
00077     }
00078 
00079     void Clear( )
00080     {
00081         map<string, FileHandler*>::iterator n;
00082         for ( n = filemap.begin(); n != filemap.end(); ++n )
00083             delete ( *n ).second;
00084         filemap.erase( filemap.begin(), filemap.end() );
00085     }
00086 
00087     void GetUnusedFxFiles( PlayList &list, vector< string > &unused )
00088     {
00089         unused.erase( unused.begin(), unused.end() );
00090         map<string, FileHandler*>::iterator n;
00091         for ( n = filemap.begin(); n != filemap.end(); ++n )
00092         {
00093             if ( n->first.find( ".kinofx.dv" ) != string::npos && !list.IsFileUsed( n->first ) )
00094             {
00095                 unused.push_back( n->first );
00096             }
00097         }
00098     }
00099 };
00100 
00104 FileMap *GetFileMap( )
00105 {
00106     static FileMap * thismap = new KinoFileMap( );
00107     return thismap;
00108 }
00109 
00121 string directory_utils::join_file_to_directory( const string directory, const string &file )
00122 {
00123     vector <string> items;
00124 
00125     // Determine if the file is a full file spec or not
00126     if ( file[ 0 ] != '/' && directory[ 0 ] != '/' )
00127     {
00128         char path[ PATH_MAX ];
00129         getcwd( path, PATH_MAX );
00130         StringUtils::split( path, "/", items );
00131     }
00132 
00133     // Now add the directory if file is not absolute
00134     if ( file[ 0 ] != '/' )
00135         StringUtils::split( directory, "/", items );
00136 
00137     // Split the file and append to the directory info
00138     StringUtils::split( file, "/", items );
00139 
00140     // iterate through the items vector
00141     for ( vector< string >::iterator item = items.begin( ); item != items.end( ); )
00142     {
00143         if ( *item == ".." )
00144         {
00145             if ( item == items.begin( ) )
00146             {
00147                 items.erase( item );
00148                 item = items.begin();
00149             }
00150             else
00151             {
00152                 items.erase( -- item + 1 );
00153                 items.erase( -- item + 1 );
00154                 item ++;
00155             }
00156         }
00157         else
00158         {
00159             item ++;
00160         }
00161     }
00162 
00163     return "/" + StringUtils::join( items, "/" );
00164 }
00165 
00173 string directory_utils::get_directory_from_file( const string &file )
00174 {
00175     return join_file_to_directory( "", file + "/.." );
00176 }
00177 
00190 string directory_utils::get_absolute_path_to_file( const string &directory, const string &file )
00191 {
00192     return join_file_to_directory( directory, file );
00193 }
00194 
00201 string directory_utils::get_relative_path_to_file( const string &directory, const string &file )
00202 {
00203     string output = "";
00204     string absolute = join_file_to_directory( directory, file );
00205     vector < string > directory_items;
00206     vector < string > absolute_items;
00207     StringUtils::split( absolute, "/", absolute_items );
00208     StringUtils::split( directory, "/", directory_items );
00209 
00210     vector < string >::iterator directory_item;
00211     vector < string >::iterator absolute_item;
00212 
00213     // While they're both the same, remove from both
00214     for ( directory_item = directory_items.begin(), absolute_item = absolute_items.begin();
00215             directory_item != directory_items.end() && absolute_item != absolute_items.end() && *directory_item == *absolute_item; )
00216     {
00217         directory_items.erase( directory_item );
00218         absolute_items.erase( absolute_item );
00219         directory_item = directory_items.begin();
00220         absolute_item = absolute_items.begin();
00221     }
00222 
00223     // For each item left in the directory_items, output a ../
00224     for ( directory_item = directory_items.begin(); directory_item != directory_items.end() ; directory_item ++ )
00225         output += "../";
00226 
00227     // Now simply join what's left in absolute to output and return
00228     output += StringUtils::join( absolute_items, "/" );
00229     return output;
00230 }
00231 
00235 string directory_utils::expand_directory( const string directory )
00236 {
00237     string output;
00238     vector <string> items;
00239     StringUtils::split( directory, "/", items );
00240     vector< string >::iterator item = items.begin( );
00241 
00242     if ( item != items.end( ) && *item == "~" )
00243     {
00244         output = getenv( "HOME" );
00245         item ++;
00246     }
00247 
00248     for ( ; item != items.end( ); item ++ )
00249         output += "/" + *item;
00250 
00251     return output;
00252 }
00253 
00254 typedef bool ( *callback ) ( xmlNodePtr node, void *p, bool *freed );
00255 
00256 typedef struct
00257 {
00258     int absFrame;
00259     int absBegin;
00260     int absEnd;
00261     int clipFrame;
00262     int clipBegin;
00263     int clipEnd;
00264     int clipNumber;
00265     int clipLength;
00266     char    fileName[ 1024 ];
00267     xmlNodePtr  sequence;
00268     xmlNodePtr  video;
00269 }
00270 MovieInfo;
00271 
00293 static bool findFile( xmlNodePtr node, void *p, bool *freed )
00294 {
00295     MovieInfo * data = ( MovieInfo* ) p;
00296 
00297     if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 )
00298     {
00299         data->sequence = node;
00300         data->clipNumber++;
00301     }
00302 
00303     // if this is a <video> node, calculate its absolute begin and end positions
00304 
00305     else if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00306     {
00307 
00308         data->video = node;
00309 
00310         // Check whether the required properties exist
00311 
00312         xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" );
00313         xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00314         xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00315 
00316         if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00317         {
00318 
00319             data->clipBegin = atoi( ( const char* ) clipBegin );
00320             data->clipEnd = atoi( ( const char* ) clipEnd );
00321 
00322             data->absBegin += data->clipLength; // add length of previous clip
00323             data->clipLength = data->clipEnd - data->clipBegin + 1;
00324             data->absEnd = data->absBegin + data->clipLength - 1;
00325 
00326             // cerr << "Number: " << data->clipNumber << " starts " << data->absBegin << " ends " << data->absEnd << endl;
00327 
00328             // if absFrame is within this scene, we have found the corresponding file.
00329             // Otherwise, add frame count of this scene to absBegin
00330 
00331             if ( data->absFrame <= data->absEnd )
00332             {
00333                 strcpy( data->fileName, ( char * ) src );
00334                 data->clipFrame = data->absFrame - data->absBegin + data->clipBegin;
00335 
00336                 // Free memory used
00337                 xmlFree( src );
00338                 xmlFree( clipEnd );
00339                 xmlFree( clipBegin );
00340 
00341                 // cerr << "Obtaining frame " << data->clipFrame << " from " << data->clipNumber << endl;
00342 
00343                 return true; // true means done traversing xml tree
00344             }
00345         }
00346 
00347         if ( src )
00348             xmlFree( src );
00349         if ( clipEnd )
00350             xmlFree( clipEnd );
00351         if ( clipBegin )
00352             xmlFree( clipBegin );
00353 
00354     }
00355     return false;
00356 }
00357 
00358 
00365 static bool fillMap( xmlNodePtr node, void *p, bool *freed )
00366 {
00367     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00368     {
00369 
00370         // Check whether the required properties exist
00371 
00372         xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" );
00373         xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00374         xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00375 
00376         if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00377         {
00378 
00379             // Determine the absolute path to the file indicated by src
00380             string & directory = *( string * ) p;
00381             string index = directory_utils::get_absolute_path_to_file( directory, ( char * ) src );
00382 
00383             // Internally, we always use absolute paths so convert now, just to be on the safe side
00384             xmlSetProp( node, ( const xmlChar* ) "src", ( xmlChar * ) index.c_str() );
00385 
00386             // Check if the file actually exists in the file map
00387             if ( GetFileMap() ->GetMap().find( index ) == GetFileMap() ->GetMap().end() )
00388             {
00389 
00390                 FileHandler * mediaFile;
00391                 /* determine file type */
00392                 if ( strncasecmp( strrchr( ( char* ) src, '.' ), ".avi", 4 ) == 0 )
00393                     mediaFile = new AVIHandler();
00394                 else if ( strncasecmp( strrchr( ( char* ) src, '.' ), ".dv", 3 ) == 0 ||
00395                           strncasecmp( strrchr( ( char* ) src, '.' ), ".dif", 4 ) == 0 )
00396                     mediaFile = new RawHandler();
00397 #ifdef HAVE_LIBQUICKTIME
00398 
00399                 else if ( strncasecmp( strrchr( ( char* ) src, '.' ), ".mov", 4 ) == 0 )
00400                     mediaFile = new QtHandler();
00401 #endif
00402 
00403                 else
00404                 {
00405                     xmlFree( src );
00406                     xmlFree( clipEnd );
00407                     xmlFree( clipBegin );
00408                     return false;
00409                 }
00410                 /* construct appropriate filehandler */
00411                 if ( mediaFile->Open( index.c_str() ) )
00412                 {
00413                     GetFileMap() ->GetMap() [ index ] = mediaFile;
00414                 }
00415                 else
00416                 {
00417                     cerr << "Unable to open " << ( char * ) src
00418                     << " - removing from list" << endl;
00419                     xmlUnlinkNode( node );
00420                     xmlFreeNode( node );
00421                     *freed = true;
00422                 }
00423             }
00424         }
00425 
00426         xmlFree( src );
00427         xmlFree( clipEnd );
00428         xmlFree( clipBegin );
00429     }
00430     return false;
00431 }
00432 
00433 
00443 static bool findSceneStart( xmlNodePtr node, void *p, bool *freed )
00444 {
00445     int fileCount = 0;
00446     MovieInfo *data = ( MovieInfo* ) p;
00447     int begin = data->absBegin;
00448 
00449     // if this is a <seq> node process all of its <video> child nodes
00450 
00451     if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 )
00452     {
00453 
00454         data->sequence = node;
00455 
00456         node = node->children;
00457         while ( node != NULL )
00458         {
00459             if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00460             {
00461 
00462                 data->video = node;
00463 
00464                 xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" );
00465                 xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00466                 xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00467 
00468                 if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00469                 {
00470 
00471                     data->clipBegin = atoi( ( const char* ) clipBegin );
00472                     data->clipEnd = atoi( ( const char* ) clipEnd );
00473 
00474                     // if this is the first file remember its name and start
00475 
00476                     if ( fileCount == 0 )
00477                     {
00478                         data->clipFrame = data->clipBegin;
00479                         strcpy( data->fileName, ( char * ) src );
00480                     }
00481 
00482                     // if absFrame is within current scene we are done.
00483                     // fine name and relative frame number have been already found (see above)
00484                     // otherwise update absBegin to hold abs frame num of next file
00485 
00486                     if ( data->absFrame <= begin + data->clipEnd - data->clipBegin )
00487                     {
00488                         xmlFree( clipBegin );
00489                         xmlFree( clipEnd );
00490                         xmlFree( src );
00491                         return true;
00492                     }
00493                     else
00494                     {
00495                         begin += ( data->clipEnd - data->clipBegin + 1 );
00496                     }
00497                     fileCount++;
00498                 }
00499                 if ( src )
00500                     xmlFree( src );
00501                 if ( clipEnd )
00502                     xmlFree( clipEnd );
00503                 if ( clipBegin )
00504                     xmlFree( clipBegin );
00505             }
00506             node = node->next;
00507         }
00508     }
00509     data->absBegin = begin;
00510     data->clipFrame = 0;
00511     strcpy( data->fileName, "" );
00512     return false;
00513 }
00514 
00515 
00516 static bool findSceneEnd( xmlNodePtr node, void *p, bool *freed )
00517 {
00518     bool found = false;
00519     xmlChar *src = NULL;
00520     MovieInfo *data = ( MovieInfo* ) p;
00521 
00522     // if this is a <seq> node process all of its <video> child nodes
00523 
00524     if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 )
00525     {
00526 
00527         data->sequence = node;
00528 
00529         node = node->children;
00530         while ( node != NULL )
00531         {
00532             if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00533             {
00534 
00535                 data->video = node;
00536 
00537                 if ( src )
00538                     xmlFree( src );
00539 
00540                 src = xmlGetProp( node, ( const xmlChar* ) "src" );
00541                 xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00542                 xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00543 
00544                 if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00545                 {
00546 
00547                     data->clipBegin = atoi( ( const char* ) clipBegin );
00548                     data->clipEnd = atoi( ( const char* ) clipEnd );
00549                     data->clipFrame = data->clipEnd;
00550 
00551                     if ( data->absFrame <= data->absBegin + data->clipEnd - data->clipBegin )
00552                         found = true;
00553                     data->absBegin += ( data->clipEnd - data->clipBegin + 1 );
00554                 }
00555 
00556                 if ( clipEnd )
00557                     xmlFree( clipEnd );
00558                 if ( clipBegin )
00559                     xmlFree( clipBegin );
00560             }
00561             node = node->next;
00562         }
00563 
00564         if ( found )
00565         {
00566             strcpy( data->fileName, ( char * ) src );
00567             xmlFree( src );
00568             data->absEnd = data->absBegin - 1;
00569             return true;
00570         }
00571 
00572         if ( src )
00573             xmlFree( src );
00574     }
00575     data->clipFrame = 0;
00576     strcpy( data->fileName, "" );
00577     return false;
00578 }
00579 
00594 static bool countFrames( xmlNodePtr node, void *p, bool *freed )
00595 {
00596     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00597     {
00598 
00599         xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" );
00600         xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00601         xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00602 
00603         if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00604             * ( ( int* ) p ) += atoi( ( const char* ) clipEnd ) - atoi( ( const char* ) clipBegin ) + 1;
00605 
00606         if ( clipEnd )
00607             xmlFree( clipEnd );
00608         if ( clipBegin )
00609             xmlFree( clipBegin );
00610         if ( src )
00611             xmlFree( src );
00612     }
00613     return false;
00614 }
00615 
00617 typedef struct
00618 {
00619     string file;   /* Contains filename */
00620     string clipBegin; /* Contains begin counts */
00621     string clipEnd;   /* Continas end count */
00622 }
00623 EliInfo;
00624 
00625 typedef list<EliInfo> EliInfos;
00626 typedef EliInfos::iterator EliInfosIterator;
00627 
00640 static bool convertEli( xmlNodePtr node, void *p, bool *freed )
00641 {
00642     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00643     {
00644 
00645         xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" );
00646         xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00647         xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00648 
00649         if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00650         {
00651             /*
00652             cout << "convertEli : " << src << ", " << clipBegin << ", " << clipEnd
00653             << endl; 
00654             */
00655             EliInfos * Eli = ( EliInfos * ) p;
00656             EliInfo tmp;
00657             tmp.file = ( const char* ) src;
00658             tmp.clipBegin = ( const char* ) clipBegin;
00659             tmp.clipEnd = ( const char* ) clipEnd;
00660             Eli->push_back( tmp );
00661         }
00662 
00663         if ( clipEnd )
00664             xmlFree( clipEnd );
00665         if ( clipBegin )
00666             xmlFree( clipBegin );
00667         if ( src )
00668             xmlFree( src );
00669     }
00670     return false;
00671 }
00672 
00673 class SrtContext
00674 {
00675 public:
00676     ofstream file;
00677     unsigned counter;
00678     xmlChar* title;
00679     xmlChar* abstract;
00680     xmlChar* src;
00681     unsigned begin;
00682     unsigned duration;
00683 
00684     SrtContext( const char* filename ) :
00685         file(filename), counter(0), title(NULL), abstract(NULL), src(NULL),
00686         begin(0), duration(0)
00687     {
00688     }
00689 
00690     ~SrtContext()
00691     {
00692         printEntry();
00693         file.close();
00694     }
00695 
00696     void printEntry(void)
00697     {
00698         if ( title || abstract )
00699         {
00700             Frame *frame = GetFramePool( )->GetFrame( );
00701             FileHandler *mediaFile = GetFileMap()->GetMap() [ string( ( const char* ) src ) ];
00702             SMIL::MediaClippingTime time;
00703 
00704             mediaFile->GetFrame( *frame, 0 );
00705             time.setFramerate( frame->GetFrameRate() );
00706             GetFramePool( )->DoneWithFrame( frame );
00707             string beginString = time.parseFramesToString( begin, SMIL::Time::TIME_FORMAT_CLOCK );
00708             string durationString = time.parseFramesToString( duration - begin, SMIL::Time::TIME_FORMAT_CLOCK );
00709             begin = 0;
00710 
00711             file << ++counter << endl;
00712             file << StringUtils::replaceAll( beginString, ".", "," );
00713             file << " --> " << StringUtils::replaceAll( durationString, ".", "," ) << endl;
00714             if ( title )
00715                 file << title << endl;
00716             if ( abstract )
00717                 file << abstract << endl;
00718             file << endl;
00719         }
00720         if ( src )
00721         {
00722             xmlFree( src );
00723             src = NULL;
00724         }
00725         if ( title )
00726         {
00727             xmlFree( title );
00728             title = NULL;
00729         }
00730         if ( abstract )
00731         {
00732             xmlFree( abstract );
00733             abstract = NULL;
00734         }
00735     }
00736 };
00737 
00738 static bool convertSrt( xmlNodePtr node, void *data, bool *freed )
00739 {
00740     SrtContext* srt = static_cast< SrtContext* >( data );
00741     
00742     if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 )
00743     {
00744         srt->printEntry();
00745         srt->title = xmlGetProp( node, ( const xmlChar * ) "title" );
00746         srt->abstract = xmlGetProp( node, ( const xmlChar * ) "abstract" );
00747     }
00748     else if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00749     {
00750         xmlChar* clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00751         xmlChar* clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00752 
00753         srt->src = xmlGetProp( node, ( const xmlChar* ) "src" );
00754         if ( !srt->begin && ( srt->title || srt->abstract ) )
00755             srt->begin = srt->duration;
00756         srt->duration += atoi( ( const char* ) clipEnd ) - atoi( ( const char* ) clipBegin ) + 1;
00757 
00758         xmlFree( clipBegin );
00759         xmlFree( clipEnd );
00760     }
00761 
00762     return false;
00763 }
00764 
00765 static bool convertFramesToSmilTime( xmlNodePtr node, void *data, bool *freed )
00766 {
00767     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00768     {
00769         Frame *frame = GetFramePool( )->GetFrame( );
00770         xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" );
00771         string index( ( char* ) src );
00772         xmlFree( src );
00773         FileHandler *mediaFile = GetFileMap()->GetMap() [ index ];
00774         mediaFile->GetFrame( *frame, 0 );
00775         SMIL::MediaClippingTime time;
00776         time.setFramerate( frame->GetFrameRate() );
00777         GetFramePool( )->DoneWithFrame( frame );
00778 
00779         xmlChar *prop = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00780         if ( prop )
00781         {
00782             std::string newValue = time.parseFramesToString( atoi( ( const char* ) prop ), SMIL::Time::TIME_FORMAT_CLOCK );
00783             xmlFree( prop );
00784             xmlSetProp( node, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) newValue.c_str() );
00785         }
00786         prop = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00787         if ( prop )
00788         {
00789             std::string newValue = time.parseFramesToString( atoi( ( const char* ) prop ), SMIL::Time::TIME_FORMAT_CLOCK );
00790             xmlFree( prop );
00791             xmlSetProp( node, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) newValue.c_str() );
00792         }
00793     }
00794     return false;
00795 }
00796 
00797 static bool convertSmilTimeToFrames( xmlNodePtr node, void *data, bool *freed )
00798 {
00799     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00800     {
00801         Frame *frame = GetFramePool( )->GetFrame( );
00802         xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" );
00803         string index( ( char* ) src );
00804         xmlFree( src );
00805         FileHandler *mediaFile = GetFileMap()->GetMap() [ index ];
00806         mediaFile->GetFrame( *frame, 0 );
00807         SMIL::MediaClippingTime time;
00808         time.setFramerate( frame->GetFrameRate() );
00809         GetFramePool( )->DoneWithFrame( frame );
00810 
00811         xmlChar *prop = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00812         if ( prop )
00813         {
00814             time.parseValue( ( const char* ) prop );
00815             xmlFree( prop );
00816             std::string newValue = time.toString( SMIL::Time::TIME_FORMAT_FRAMES );
00817             xmlSetProp( node, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) newValue.c_str() );
00818         }
00819         prop = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00820         if ( prop )
00821         {
00822             time.parseValue( ( const char* ) prop );
00823             xmlFree( prop );
00824             std::string newValue = time.toString( SMIL::Time::TIME_FORMAT_FRAMES );
00825             xmlSetProp( node, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) newValue.c_str() );
00826         }
00827     }
00828     return false;
00829 }
00830 
00831 static bool clone( xmlNodePtr node, void *data, bool *freed )
00832 {
00833     xmlNodePtr* parent = (xmlNodePtr*) data;
00834 
00835     xmlNodePtr child = xmlNewNode( NULL, node->name );
00836     xmlAddChild( *parent, child );
00837     for ( xmlAttr* attr = node->properties; attr; attr = attr->next )
00838         xmlNewProp( child, attr->name, xmlGetProp( attr->parent, attr->name) );
00839     if ( node->children ) // next iteration in depth-first traversal will be a child
00840         *parent = child;
00841     else if ( node == node->parent->last ) // last sibling
00842         *parent = (*parent)->parent;
00843     
00844     return false;
00845 }
00846 
00861 static bool parse( xmlNodePtr node, callback func, void *p )
00862 {
00863     bool done = false;
00864 
00865     while ( node != NULL && done == false )
00866     {
00867         bool freed = false;
00868         xmlNodePtr next = node->next;
00869         done = ( *func ) ( node, p, &freed );
00870         if ( !done && !freed && node->children )
00871             done = parse( node->children, func, p );
00872         node = next;
00873     }
00874     return done;
00875 }
00876 
00877 
00880 PlayList::PlayList() : dirty( false ), doc_name( "" ), count( 0 )
00881 {
00882     xmlNsPtr    ns;
00883     xmlNodePtr  root;
00884 
00885     // cerr << "*PlayList::PlayList()" << endl;
00886 
00887     doc = xmlNewDoc( ( const xmlChar* ) "1.0" );
00888     root = xmlNewNode( NULL, ( const xmlChar* ) "smil" );
00889     ns = xmlNewNs( root, SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL );
00890     xmlDocSetRootElement( doc, root );
00891     xmlAddChild( root, xmlNewNode( NULL, ( const xmlChar* ) "body" ) );
00892 }
00893 
00894 
00897 PlayList::PlayList( const PlayList& playList )
00898 {
00899     xmlNsPtr    ns;
00900     xmlNodePtr  root;
00901 
00902     doc = xmlNewDoc( ( const xmlChar* ) "1.0" );
00903     root = xmlNewNode( NULL, ( const xmlChar* ) "smil" );
00904     ns = xmlNewNs( root, ( const xmlChar* ) SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL );
00905     xmlDocSetRootElement( doc, root );
00906     parse( playList.GetBody(), clone, &root );
00907     dirty = playList.dirty;
00908     doc_name = playList.GetDocName( );
00909     RefreshCount( );
00910 }
00911 
00912 
00915 PlayList& PlayList::operator=( const PlayList& playList )
00916 {
00917     // cerr << "*PlayList::operator=(const PlayList& playList)" << endl;
00918 
00919     if ( doc != playList.doc )
00920     {
00921         xmlNsPtr    ns;
00922         xmlNodePtr  root;
00923 
00924         xmlFreeDoc( doc );
00925         doc = xmlNewDoc( ( const xmlChar* ) "1.0" );
00926         root = xmlNewNode( NULL, ( const xmlChar* ) "smil" );
00927         ns = xmlNewNs( root, ( const xmlChar* ) SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL );
00928         xmlDocSetRootElement( doc, root );
00929         parse( playList.GetBody(), clone, &root );
00930         dirty = playList.dirty;
00931         doc_name = playList.GetDocName( );
00932         RefreshCount( );
00933     }
00934     return *this;
00935 }
00936 
00937 
00941 PlayList::~PlayList()
00942 {
00943     // cerr << "*PlayList::~PlayList()" << endl;
00944     if ( doc != NULL )
00945     {
00946         xmlFreeDoc( doc );
00947         doc = NULL;
00948     }
00949 }
00950 
00955 xmlNodePtr PlayList::GetBody( ) const
00956 {
00957     return xmlDocGetRootElement( doc )->children;
00958 }
00959 
00960 
00961 void PlayList::RefreshCount( )
00962 {
00963     count = 0;
00964     if ( doc != NULL )
00965         parse( GetBody(), countFrames, &count );
00966 }
00967 
00972 int PlayList::GetNumFrames() const
00973 {
00974     return count;
00975 }
00976 
00977 
00978 char* PlayList::GetFileNameOfFrame( int frameNum ) const
00979 {
00980     // cerr << "char* PlayList::GetFileNameOfFrame(int frameNum)" << endl;
00981     MovieInfo data;
00982 
00983     memset( &data, 0, sizeof( MovieInfo ) );
00984     data.absBegin = 0;
00985     data.absEnd = 0;
00986     data.absFrame = frameNum;
00987 
00988     parse( GetBody(), findFile, &data );
00989     return strdup( data.fileName );
00990 }
00991 
00992 
01002 bool PlayList::GetFrame( int frameNum, Frame &frame )
01003 {
01004     MovieInfo data;
01005 
01006     // cerr << "bool PlayList::GetFrame(" << frameNum << ", Frame &frame)" << endl;
01007 
01008     memset( &data, 0, sizeof( MovieInfo ) );
01009     data.absBegin = 0;
01010     data.absEnd = 0;
01011     data.absFrame = frameNum;
01012 
01013     parse( GetBody(), findFile, &data );
01014 
01015     if ( strcmp( data.fileName, "" ) )
01016     {
01017         // NB: In our playlist, we enforce an internal absolute path - there is no need
01018         // to convert the file name here
01019         string index( ( char * ) data.fileName );
01020         FileHandler *mediaFile = GetFileMap() ->GetMap() [ index ];
01021         if ( data.clipFrame >= mediaFile->GetTotalFrames() )
01022             data.clipFrame = mediaFile->GetTotalFrames() - 1;
01023         return ( mediaFile->GetFrame( frame, data.clipFrame ) >= 0 );
01024     }
01025 
01026     return false;
01027 }
01028 
01029 
01036 bool PlayList::GetMediaObject( int frameNum, FileHandler **media )
01037 {
01038     MovieInfo data;
01039 
01040     memset( &data, 0, sizeof( MovieInfo ) );
01041     data.absBegin = 0;
01042     data.absEnd = 0;
01043     data.absFrame = frameNum;
01044 
01045     parse( GetBody(), findFile, &data );
01046 
01047     if ( strcmp( data.fileName, "" ) )
01048     {
01049         // Again, the absolute path is ensure internally - no need to convert
01050         string index( ( char * ) data.fileName );
01051         *media = GetFileMap() ->GetMap() [ index ];
01052         return true;
01053     }
01054 
01055     return false;
01056 }
01057 
01058 
01059 int PlayList::GetClipBegin( int frameNum ) const
01060 {
01061     MovieInfo data;
01062 
01063     memset( &data, 0, sizeof( MovieInfo ) );
01064     data.absBegin = 0;
01065     data.absEnd = 0;
01066     data.absFrame = frameNum;
01067 
01068     if ( parse( GetBody(), findSceneStart, &data ) )
01069         return data.clipBegin;
01070     else
01071         return 0;
01072 }
01073 
01074 
01075 int PlayList::GetClipEnd( int frameNum ) const
01076 {
01077     MovieInfo data;
01078 
01079     memset( &data, 0, sizeof( MovieInfo ) );
01080     data.absBegin = 0;
01081     data.absEnd = 0;
01082     data.absFrame = frameNum;
01083 
01084     if ( parse( GetBody(), findSceneEnd, &data ) )
01085         return data.clipEnd;
01086     else
01087         return 0;
01088 }
01089 
01090 
01091 bool PlayList::SetClipBegin( int frameNum, const char* value )
01092 {
01093     MovieInfo data;
01094 
01095     memset( &data, 0, sizeof( MovieInfo ) );
01096     data.absBegin = 0;
01097     data.absEnd = 0;
01098     data.absFrame = frameNum;
01099 
01100     if ( parse( GetBody(), findSceneStart, &data ) )
01101     {
01102         xmlSetProp( data.video, ( const xmlChar * ) "clipBegin", ( const xmlChar * ) value );
01103         RefreshCount( );
01104         return true;
01105     }
01106     else
01107         return false;
01108 }
01109 
01110 
01111 bool PlayList::SetClipEnd( int frameNum, const char* value )
01112 {
01113     MovieInfo data;
01114 
01115     memset( &data, 0, sizeof( MovieInfo ) );
01116     data.absBegin = 0;
01117     data.absEnd = 0;
01118     data.absFrame = frameNum;
01119 
01120     if ( parse( GetBody(), findSceneEnd, &data ) )
01121     {
01122         xmlSetProp( data.video, ( const xmlChar * ) "clipEnd", ( const xmlChar * ) value );
01123         RefreshCount( );
01124         return true;
01125     }
01126     else
01127         return false;
01128 }
01129 
01130 
01131 int PlayList::FindStartOfScene( int frameNum ) const
01132 {
01133     MovieInfo data;
01134 
01135     // cerr << "int PlayList::FindStartOfScene(int frameNum)" << endl;
01136 
01137     memset( &data, 0, sizeof( MovieInfo ) );
01138     data.absBegin = 0;
01139     data.absEnd = 0;
01140     data.absFrame = frameNum;
01141 
01142     parse( GetBody(), findSceneStart, &data );
01143 
01144     if ( strcmp( data.fileName, "" ) )
01145         return data.absBegin;
01146     else
01147         return 0;
01148 }
01149 
01150 
01151 int PlayList::FindEndOfScene( int frameNum ) const
01152 {
01153     MovieInfo data;
01154 
01155     // cerr << "int PlayList::FindEndOfScene(int frameNum)" << endl;
01156 
01157     data.absBegin = 0;
01158     data.absEnd = 0;
01159     data.absFrame = frameNum;
01160 
01161     parse( GetBody(), findSceneEnd, &data );
01162 
01163     if ( strcmp( data.fileName, "" ) )
01164         return data.absEnd;
01165     else
01166         return 999999;
01167 }
01168 
01169 
01170 void PlayList::AutoSplit( int start, int end )
01171 {
01172     MovieInfo firstFile;
01173     MovieInfo lastFile;
01174     Frame *frame = GetFramePool( ) ->GetFrame( );
01175     struct tm   recDate;
01176     time_t  startTime;
01177     time_t  endTime;
01178 
01179     // cerr << "PlayList::AutoSplit(int start=" << start << ", int end=" << end << ")" ;
01180     // cerr << endl;
01181 
01182     memset( &firstFile, 0, sizeof( MovieInfo ) );
01183     firstFile.absBegin = 0;
01184     firstFile.absEnd = 0;
01185     firstFile.absFrame = start;
01186 
01187     parse( GetBody(), findFile, &firstFile );
01188     string index1( ( char* ) firstFile.fileName );
01189     FileHandler *mediaFile1 = GetFileMap() ->GetMap() [ index1 ];
01190     mediaFile1->GetFrame( *frame, firstFile.clipFrame );
01191     frame->GetRecordingDate( recDate );
01192     startTime = mktime( &recDate );
01193 
01194     memset( &lastFile, 0, sizeof( MovieInfo ) );
01195     lastFile.absBegin = 0;
01196     lastFile.absEnd = 0;
01197     lastFile.absFrame = end;
01198 
01199     parse( GetBody(), findFile, &lastFile );
01200 
01201     string index2( ( char* ) lastFile.fileName );
01202     FileHandler *mediaFile2 = GetFileMap() ->GetMap() [ index2 ];
01203     mediaFile2->GetFrame( *frame, lastFile.clipFrame );
01204     frame->GetRecordingDate( recDate );
01205     endTime = mktime( &recDate );
01206 
01207     int fps = frame->IsPAL() ? 25 : 30;
01208 
01209     GetFramePool( ) ->DoneWithFrame( frame );
01210 
01211     // bail out on invalid recording date/time
01212     if ( startTime < 0 || endTime < 0 )
01213         return ;
01214 
01215     AutoSplit ( start, startTime, end, endTime, fps );
01216 }
01217 
01218 
01219 void PlayList::AutoSplit( int start, time_t startTime, int end, time_t endTime, int fps )
01220 {
01221     time_t diffTime = static_cast<time_t>( difftime( endTime, startTime ) );
01222     if ( ( ( diffTime * fps ) - ( end - start ) ) > fps || diffTime < 0.0 )
01223     {
01224         if ( ( end - start ) > 1 )
01225         {
01226             time_t mid = start + ( end - start ) / 2;
01227             time_t midTime;
01228             // reduce the scope of temporary varables here to reduce memory usage when recurring:
01229             {
01230                 MovieInfo midFile;
01231                 struct tm recDate;
01232                 Frame *frame = GetFramePool( ) ->GetFrame( );
01233 
01234                 memset( &midFile, 0, sizeof( MovieInfo ) );
01235                 midFile.absFrame = mid;
01236 
01237                 parse( GetBody(), findFile, &midFile );
01238 
01239                 string index( ( char* ) midFile.fileName );
01240                 FileHandler *mediaFile = GetFileMap() ->GetMap() [ index ];
01241                 mediaFile->GetFrame( *frame, midFile.clipFrame );
01242                 frame->GetRecordingDate( recDate );
01243                 midTime = mktime( &recDate );
01244 
01245                 GetFramePool( ) ->DoneWithFrame( frame );
01246             }
01247 
01248             // bail out on invalid recording date/time
01249             if ( midTime < 0 )
01250                 return ;
01251 
01252             AutoSplit ( start, startTime, mid, midTime, fps );
01253             AutoSplit ( mid, midTime, end, endTime, fps );
01254         }
01255         else
01256         {
01257             SplitSceneBefore( end );
01258         }
01259     }
01260 }
01261 
01262 
01263 bool PlayList::SplitSceneBefore( int frameNum )
01264 {
01265     MovieInfo data;
01266 
01267     // cerr << "PlayList::SplitSceneBefore(int frameNum=" << frameNum << ")" << endl;
01268 
01269     if ( GetNumFrames() == 0 )
01270         return false;
01271 
01272     memset( &data, 0, sizeof( MovieInfo ) );
01273     data.absBegin = 0;
01274     data.absEnd = 0;
01275     data.absFrame = frameNum;
01276     parse( GetBody(), findSceneStart, &data );
01277     int begin = data.absBegin;
01278 
01279     memset( &data, 0, sizeof( MovieInfo ) );
01280     data.absBegin = 0;
01281     data.absEnd = 0;
01282     data.absFrame = frameNum;
01283     parse( GetBody(), findSceneEnd, &data );
01284     int end = data.absEnd;
01285 
01286     if ( strcmp( data.fileName, "" ) && begin != frameNum )
01287     {
01288 
01289         dirty = true;
01290 
01291         // Copy the right hand side of the current scene
01292         xmlNode *firstSequence = data.sequence;
01293         PlayList playlist;
01294         GetPlayList( frameNum, end, playlist );
01295 
01296         // Paste it after the current scene
01297         xmlAddNextSibling( firstSequence, playlist.GetBody()->children );
01298 
01299         // in the first sequence, delete from frameNum to end of scene
01300         Delete( frameNum, end );
01301 
01302         return true;
01303     }
01304     else
01305     {
01306         return false;
01307     }
01308 }
01309 
01310 bool PlayList::JoinScenesAt( int frameNum )
01311 {
01312     MovieInfo scene1;
01313     MovieInfo scene2;
01314     MovieInfo scene2end;
01315 
01316     if ( GetNumFrames() == 0 )
01317         return false;
01318 
01319     // cerr << "PlayList::JoinScenesAt(int frameNum=" << frameNum << ")" << endl;
01320 
01321     memset( &scene1, 0, sizeof( MovieInfo ) );
01322     scene1.absBegin = 0;
01323     scene1.absEnd = 0;
01324     scene1.absFrame = frameNum;
01325     parse( GetBody(), findSceneStart, &scene1 );
01326 
01327     memset( &scene2, 0, sizeof( MovieInfo ) );
01328     scene2.absBegin = 0;
01329     scene2.absEnd = 0;
01330     scene2.absFrame = frameNum;
01331     parse( GetBody(), findSceneEnd, &scene2 );
01332     int end = scene2.absEnd + 1;
01333 
01334     memset( &scene2end, 0, sizeof( MovieInfo ) );
01335     scene2end.absBegin = 0;
01336     scene2end.absEnd = 0;
01337     scene2end.absFrame = end;
01338     parse( GetBody(), findSceneEnd, &scene2end );
01339 
01340     if ( scene1.sequence != scene2end.sequence )
01341     {
01342 
01343         dirty = true;
01344 
01345         // cerr << ">>>> Joining scene at " << scene1.absBegin << " with scene at "
01346         // << scene2.absBegin << " which ends at " << scene2end.absEnd << endl;
01347 
01348         // concatenate the contents of scene2 into scene1
01349         xmlNode *lastchild = xmlGetLastChild( scene1.sequence );
01350         xmlNodePtr next = NULL;
01351         for ( xmlNodePtr ptr = scene2end.sequence->children; ptr != NULL; ptr = next )
01352         {
01353             next = ptr->next;
01354             lastchild = xmlAddNextSibling( lastchild, ptr );
01355             ptr = next;
01356         }
01357         // Merge the metadata properties
01358         for ( xmlAttr* attr = scene2end.sequence->properties; attr; attr = attr->next )
01359         {
01360             const char *valueB = ( const char* ) xmlGetProp( attr->parent, ( const xmlChar * ) attr->name );
01361             // Only if the value is meaningful
01362             if ( valueB && strcmp( valueB, "" ) )
01363             {
01364                 // See if the property exists on clip A
01365                 const char *valueA = ( const char* ) xmlGetProp( scene1.sequence, ( const xmlChar * ) attr->name );
01366                 if ( valueA && strcmp( valueA, "" ) )
01367                 {
01368                     xmlChar* value = new xmlChar[ strlen(valueA) + strlen(valueB) + 1 ];
01369                     strcpy( (char*) value, (const char*) valueA );
01370                     strcat( (char*) value, " " );
01371                     strcat( (char*) value, (const char*) valueB );
01372                     xmlSetProp( scene1.sequence, attr->name, ( const xmlChar * ) value );
01373                     delete[] value;
01374                 }
01375                 else
01376                 {
01377                     xmlSetProp( scene1.sequence, attr->name, ( const xmlChar * ) valueB );
01378                 }
01379             }
01380         }
01381         xmlUnlinkNode( scene2end.sequence );
01382         xmlFreeNode( scene2end.sequence );
01383         RefreshCount( );
01384 
01385         return true;
01386     }
01387     else
01388     {
01389         return false;
01390     }
01391 }
01392 
01393 
01405 bool PlayList::GetPlayList( int first, int last, PlayList &playlist ) const
01406 {
01407     MovieInfo firstFile, lastFile;
01408     bool copyFlag = false;
01409 
01410     if ( GetNumFrames() == 0 )
01411         return false;
01412 
01413     playlist.dirty = false;
01414 
01415     // cerr << " bool PlayList::Copy(int first, int last, PlayList &playlist) " << endl;
01416 
01417     memset( &firstFile, 0, sizeof( MovieInfo ) );
01418     firstFile.absBegin = 0;
01419     firstFile.absEnd = 0;
01420     firstFile.absFrame = first;
01421 
01422     parse( GetBody(), findFile, &firstFile );
01423 
01424     memset( &lastFile, 0, sizeof( MovieInfo ) );
01425     lastFile.absBegin = 0;
01426     lastFile.absEnd = 0;
01427     lastFile.absFrame = last;
01428 
01429     parse( GetBody(), findFile, &lastFile );
01430 
01431     if ( strcmp( firstFile.fileName, "" ) && strcmp( lastFile.fileName, "" ) )
01432     {
01433 
01434         xmlNodePtr srcNode = GetBody();
01435         xmlNodePtr dstNode = playlist.GetBody();
01436         xmlNodePtr nextSeq = NULL;
01437 
01438         for ( xmlNodePtr srcSeq = srcNode->children; srcSeq != NULL; srcSeq = nextSeq )
01439         {
01440             nextSeq = srcSeq->next;
01441             if ( xmlStrcmp( srcSeq->name, ( const xmlChar* ) "seq" ) == 0 )
01442             {
01443                 xmlNodePtr seq = xmlNewNode( NULL, ( const xmlChar* ) "seq" );
01444                 xmlAddChild( dstNode, seq );
01445                 xmlNodePtr nextVideo = NULL;
01446 
01447                 for ( xmlNodePtr srcVideo = srcSeq->children; srcVideo != NULL; srcVideo = nextVideo )
01448                 {
01449                     nextVideo = srcVideo->next;
01450                     if ( xmlStrcmp( srcVideo->name, ( const xmlChar* ) "video" ) == 0 )
01451                     {
01452 
01453                         // case 1: selection contains more than one file. This one is neither the first nor the last.
01454 
01455                         if ( copyFlag && srcVideo != firstFile.video && srcVideo != lastFile.video )
01456                         {
01457                             xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01458                             xmlAddChild( seq, video );
01459                             for ( xmlAttr* attr = srcVideo->properties; attr; attr = attr->next )
01460                                 xmlNewProp( video, attr->name, xmlGetProp( attr->parent, attr->name) );
01461                         }
01462 
01463                         // case 2: selection contains more than one file and this is the first file
01464 
01465                         else if ( srcVideo == firstFile.video && srcVideo != lastFile.video )
01466                         {
01467 
01468                             ostringstream sb1, sb2;
01469 
01470                             xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01471                             xmlNewProp( video, ( const xmlChar* ) "src", ( const xmlChar* ) firstFile.fileName );
01472                             sb1 << firstFile.clipFrame << ends;
01473                             xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() );
01474                             sb2 << firstFile.clipEnd << ends;
01475                             xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() );
01476                             xmlAddChild( seq, video );
01477                             copyFlag = true;
01478                         }
01479 
01480                         // case 3: selection contains more than one file and this is the last file
01481 
01482                         else if ( srcVideo != firstFile.video && srcVideo == lastFile.video )
01483                         {
01484 
01485                             ostringstream sb1, sb2;
01486 
01487                             xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01488                             xmlNewProp( video, ( const xmlChar* ) "src", ( const xmlChar* ) lastFile.fileName );
01489                             sb1 << lastFile.clipBegin << ends;
01490                             xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() );
01491                             sb2 << lastFile.clipFrame << ends;
01492                             xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() );
01493                             xmlAddChild( seq, video );
01494                             copyFlag = false;
01495                         }
01496 
01497                         // case 4: selection contains exactly one file
01498 
01499                         else if ( srcVideo == firstFile.video && srcVideo == lastFile.video )
01500                         {
01501 
01502                             ostringstream sb1, sb2;
01503 
01504                             xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01505                             xmlNewProp( video, ( const xmlChar* ) "src", ( const xmlChar* ) firstFile.fileName );
01506                             sb1 << firstFile.clipFrame << ends;
01507                             xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() );
01508                             sb2 << lastFile.clipFrame << ends;
01509                             xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() );
01510                             xmlAddChild( seq, video );
01511                         }
01512                     }
01513                 }
01514 
01515                 // if this sequence does not have any video clips, remove it
01516 
01517                 if ( seq->children == NULL )
01518                 {
01519                     xmlUnlinkNode( seq );
01520                     xmlFreeNode( seq );
01521                 }
01522                 else
01523                 // copy the seq attributes
01524                 {
01525                     for ( xmlAttr* attr = srcSeq->properties; attr; attr = attr->next )
01526                         xmlNewProp( seq, attr->name, xmlGetProp( attr->parent, attr->name) );
01527                 }
01528             }
01529         }
01530         // PASS PATH
01531         string path = directory_utils::get_directory_from_file( GetDocName() );
01532         parse( playlist.GetBody(), fillMap, &path );
01533     }
01534     playlist.RefreshCount( );
01535     return true;
01536 }
01537 
01538 
01550 bool PlayList::InsertPlayList( PlayList &playlist, int before )
01551 {
01552     // cerr << "bool PlayList::Paste(PlayList &playlist, int before(=" << before << "))" << endl;
01553 
01554     if ( playlist.GetNumFrames() == 0 )
01555         return false;
01556 
01557     // PASS PATH
01558     string path = directory_utils::get_directory_from_file( GetDocName() );
01559     parse( playlist.GetBody(), fillMap, &path );
01560 
01561     MovieInfo file;
01562 
01563     memset( &file, 0, sizeof( MovieInfo ) );
01564     file.absBegin = 0;
01565     file.absEnd = 0;
01566     file.absFrame = before;
01567     file.sequence = NULL;
01568     file.video = NULL;
01569 
01570     // Fill the map with any new files now, before we change the doc
01571     parse( GetBody(), findFile, &file );
01572 
01573     xmlNodePtr node = playlist.GetBody();
01574     bool first = true;
01575     xmlNodePtr next = NULL;
01576     xmlNodePtr sequence = file.sequence;
01577 
01578     if ( GetNumFrames() > 0 )
01579     {
01580         dirty = true;
01581     }
01582     else
01583     {
01584         dirty = playlist.dirty;
01585 
01586         if ( doc_name == "" )
01587             doc_name = playlist.GetDocName( );
01588 
01589     }
01590 
01591     for ( xmlNodePtr ptr = node->children; ptr != NULL; ptr = next )
01592     {
01593 
01594         //cerr << endl << "Sibling" << endl;
01595         //xmlElemDump(stderr, NULL, ptr);
01596         //cerr << endl;
01597 
01598         // Get the next sibling before adding
01599         next = ptr->next;
01600 
01601         // If first and at start of scene insert, otherwise append
01602         // cerr << "Scene i'm pasting into starts at " << file.absBegin << " [" << file.absEnd << "]" << endl;
01603 
01604         if ( first && sequence == NULL )
01605         {
01606             // This strange approach avoids using xmlCopyNode, which adds extra namespace declarations
01607             xmlNodePtr tmp = xmlNewNode( NULL, ( const xmlChar* ) "seq" );
01608             xmlAddChild( GetBody(), tmp );
01609             sequence = xmlAddNextSibling( tmp, ptr );
01610             xmlUnlinkNode( tmp );
01611             xmlFreeNode( tmp );
01612         }
01613         else if ( first && before == file.absBegin && before != ( file.absEnd + 1 ) )
01614         {
01615             // cerr << "Inserting before " << before << endl;
01616             sequence = xmlAddPrevSibling( sequence, ptr );
01617         }
01618         else if ( first && before != ( file.absEnd + 1 ) )
01619         {
01620             // cerr << "Splitting scene that start at " << file.absBegin << " and ends at " << file.absEnd << " at " << before << endl;
01621             // cerr << endl << "Before Split" << endl;
01622             // xmlElemDump(stderr, NULL, sequence);
01623             // cerr << endl;
01624 
01625             // Split the current scene
01626             SplitSceneBefore( before );
01627 
01628             // Find our new position
01629             memset( &file, 0, sizeof( MovieInfo ) );
01630             file.absBegin = 0;
01631             file.absFrame = before;
01632             file.sequence = NULL;
01633             file.video = NULL;
01634 
01635             parse( GetBody(), findFile, &file );
01636 
01637             // cerr << endl << "After Split" << endl;
01638             // xmlElemDump(stderr, NULL, sequence);
01639             // cerr << endl;
01640 
01641             // Add before the scene returned
01642             sequence = xmlAddPrevSibling( file.sequence, ptr );
01643         }
01644         else
01645         {
01646             // cerr << "Inserting after " << before << endl;
01647             sequence = xmlAddNextSibling( sequence, ptr );
01648         }
01649 
01650         // We're definitely no longer first
01651         first = false;
01652     }
01653 
01654     RefreshCount( );
01655     return true;
01656 }
01657 
01658 
01659 bool PlayList::Delete( int first, int last )
01660 {
01661     int absClipBegin;
01662     int clipBegin;
01663     int clipEnd;
01664     static int firstCall = 0;
01665 
01666     // cerr << "bool PlayList::Delete(int first=" << first << ", int last=" << last << ")" << endl;
01667 
01668     // SplitSceneBefore calls Delete, avoid recursion
01669 
01670     if ( GetNumFrames() == 0 )
01671         return false;
01672 
01673     if ( firstCall == 0 )
01674     {
01675         firstCall = 1;
01676         SplitSceneBefore( first );
01677         firstCall = 0;
01678     }
01679 
01680     xmlNodePtr srcNode = GetBody();
01681     absClipBegin = 0;
01682     xmlNodePtr nextSequence = NULL;
01683     for ( xmlNodePtr srcSeq = srcNode->children; srcSeq != NULL; srcSeq = nextSequence )
01684     {
01685 
01686         dirty = true;
01687 
01688         // In case we need to delete this node, get the next pointer before starting
01689         nextSequence = srcSeq->next;
01690 
01691         if ( xmlStrcmp( srcSeq->name, ( const xmlChar* ) "seq" ) == 0 )
01692         {
01693             xmlNodePtr nextVideo = NULL;
01694 
01695             for ( xmlNodePtr srcVideo = srcSeq->children; srcVideo != NULL; srcVideo = nextVideo )
01696             {
01697 
01698                 // In case we have to delete this node
01699                 nextVideo = srcVideo->next;
01700 
01701                 if ( xmlStrcmp( srcVideo->name, ( const xmlChar* ) "video" ) == 0 )
01702                 {
01703 
01704                     ostringstream sb1, sb2;
01705                     xmlChar *s;
01706 
01707                     sb1 << ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipBegin" ) ) << ends;
01708                     clipBegin = atoi( sb1.str().c_str() );
01709                     if ( s )
01710                         xmlFree( s );
01711                     s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipEnd" );
01712                     clipEnd = atoi( ( char * ) s );
01713                     sb2 << ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipEnd" ) ) << ends;
01714                     clipEnd = atoi( sb2.str().c_str() );
01715                     if ( s )
01716                         xmlFree( s );
01717 
01718                     // case 1: selection covers this file completely. Remove this file from playlist.
01719 
01720                     if ( first <= absClipBegin && last >= absClipBegin + clipEnd - clipBegin )
01721                     {
01722                         xmlUnlinkNode( srcVideo );
01723                         xmlFreeNode( srcVideo );
01724                         // cerr << "case 1 " << endl;
01725                     }
01726 
01727                     // case 2: selection starts before or at start of file and ends somewhere in the file.
01728                     // New start of file is now end of selection + 1
01729 
01730                     else if ( first <= absClipBegin && last >= absClipBegin && last <= absClipBegin + clipEnd - clipBegin )
01731                     {
01732 
01733                         ostringstream sb;
01734 
01735                         sb << last - absClipBegin + clipBegin + 1 << ends;
01736                         xmlSetProp( srcVideo, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb.str().c_str() );
01737                         // cerr << "case 2 " << endl;
01738                     }
01739 
01740                     // case 3: selection starts somewhere in the file and ends at or after the file
01741                     // New end of file is now start of selection - 1
01742 
01743                     else if ( first > absClipBegin && first <= absClipBegin + clipEnd - clipBegin && last >= absClipBegin + clipEnd - clipBegin )
01744                     {
01745 
01746                         ostringstream sb;
01747 
01748                         sb << first - absClipBegin + clipBegin - 1 << ends;
01749                         xmlSetProp( srcVideo, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb.str().c_str() );
01750                         // cerr << "case 3 " << endl;
01751                     }
01752 
01753                     // case 4: selection starts somewhere in the file and ends in the file.
01754                     // We must split this node such that end of first file is start of selection - 1
01755                     // and start of second file is end of selection + 1
01756 
01757                     else if ( first > absClipBegin && last < absClipBegin + clipEnd - clipBegin )
01758                     {
01759 
01760                         ostringstream sb1, sb2;
01761                         xmlChar *s;
01762 
01763                         xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01764                         xmlNewProp( video, ( const xmlChar* ) "src", ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "src" ) ) );
01765                         if ( s )
01766                             xmlFree( s );
01767                         sb1 << last - absClipBegin + clipBegin + 1 << ends;
01768                         xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() );
01769                         xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipEnd" ) ) );
01770                         if ( s )
01771                             xmlFree( s );
01772                         xmlAddNextSibling( srcVideo, video );
01773                         sb2 << first - absClipBegin + clipBegin - 1 << ends;
01774                         xmlSetProp( srcVideo, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() );
01775                         // cerr << "case 4 " << endl;
01776                     }
01777 
01778                     absClipBegin += clipEnd - clipBegin + 1;
01779                 }
01780             }
01781 
01782             // if the node is now empty, delete it (can delete - see nextSequence above)
01783 
01784             if ( srcSeq->children == NULL )
01785             {
01786                 xmlUnlinkNode( srcSeq );
01787                 xmlFreeNode( srcSeq );
01788             }
01789         }
01790     }
01791 
01792     RefreshCount( );
01793 
01794     return true;
01795 }
01796 
01797 
01798 bool PlayList::LoadMediaObject( char *filename )
01799 {
01800     // cerr << "bool PlayList::LoadAVI(" << filename << ")" << endl;
01801 
01802     xmlNodePtr  seq;
01803     xmlNodePtr  node;
01804     ostringstream   sb;
01805     FileHandler *mediaFile = NULL;
01806     int existingFrames;
01807     int framesInFile;
01808 
01809     dirty = true;
01810 
01811     // This object should be located in a directory relative to cwd or absolute
01812     string index = directory_utils::get_absolute_path_to_file( "", ( char * ) filename );
01813     if ( GetFileMap() ->GetMap().find( index ) == GetFileMap() ->GetMap().end( ) )
01814     {
01815         if ( strrchr( filename, '.' ) )
01816         {
01817             if ( strncasecmp( strrchr( filename, '.' ), ".avi", 4 ) == 0 )
01818                 mediaFile = new AVIHandler();
01819             else if ( strncasecmp( strrchr( filename, '.' ), ".dv", 3 ) == 0 ||
01820                     strncasecmp( strrchr( filename, '.' ), ".dif", 4 ) == 0 )
01821                 mediaFile = new RawHandler();
01822 #ifdef HAVE_LIBQUICKTIME
01823             else if ( strncasecmp( strrchr( filename, '.' ), ".mov", 4 ) == 0 )
01824                 mediaFile = new QtHandler();
01825 #endif
01826         }
01827 
01828         if ( mediaFile == NULL )
01829             return false;
01830         if ( mediaFile->Open( filename ) == false )
01831             return false;
01832         GetFileMap() ->GetMap() [ index ] = mediaFile;
01833     }
01834     else
01835     {
01836         mediaFile = GetFileMap() ->GetMap() [ index ];
01837     }
01838 
01839     framesInFile = mediaFile->GetTotalFrames();
01840     existingFrames = GetNumFrames();
01841 
01842     seq = xmlNewNode( NULL, ( const xmlChar* ) "seq" );
01843     xmlAddChild( GetBody(), seq );
01844     node = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01845     xmlNewProp( node, ( const xmlChar* ) "src", ( const xmlChar* ) index.c_str() );
01846     xmlNewProp( node, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) "0" );
01847     sb << framesInFile - 1 << ends;
01848     xmlNewProp( node, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb.str().c_str() );
01849     xmlAddChild( seq, node );
01850 
01851     if ( framesInFile > 0 )
01852     {
01853         RefreshCount( );
01854         AutoSplit( existingFrames, existingFrames + framesInFile - 1 );
01855     }
01856     return true;
01857 }
01858 
01859 
01860 bool PlayList::LoadPlayList( char *filename )
01861 {
01862     // cerr << "bool PlayList::LoadPlayList(" << filename << ")" << endl;
01863 
01864     dirty = false;
01865 
01866     xmlNsPtr ns;
01867     xmlNodePtr node;
01868 
01869     xmlFreeDoc( doc );
01870     doc = xmlParseFile( filename );
01871     if ( !doc )
01872     {
01873         cerr << "file does not exist or failed to parse XML" << endl;
01874         return false;
01875     }
01876 
01877     node = xmlDocGetRootElement( doc );
01878     if ( node == NULL )
01879     {
01880         cerr << "empty document" << endl;
01881         xmlFreeDoc( doc );
01882         doc = NULL;
01883         return false;
01884     }
01885     ns = xmlSearchNsByHref( doc, node, SMIL20_NAMESPACE_HREF );
01886     if ( ns == NULL )
01887     {
01888         cerr << "document of the wrong type, Namespace not found" << endl;
01889         xmlFreeDoc( doc );
01890         doc = NULL;
01891         return false;
01892     }
01893     if ( xmlStrcmp( node->name, ( const xmlChar * ) "smil" ) )
01894     {
01895         cerr << "document of the wrong type, root node != smil" << endl;
01896         xmlFreeDoc( doc );
01897         doc = NULL;
01898         return false;
01899     }
01900     CleanPlayList( node );
01901 
01902     // PASS PATH
01903     string path = directory_utils::get_directory_from_file( filename );
01904     parse( GetBody(), fillMap, &path );
01905     dirty = false;
01906 
01907     // Legacy documents have smil2 namespace prefix declaration
01908     if ( xmlSearchNs( doc, node, ( const xmlChar * ) "smil2" ) == NULL )
01909     {
01910         // New age compliant documents use SMIL time values, but we continue
01911         // to use frame numbers internally.
01912         parse( node, convertSmilTimeToFrames, NULL );
01913     }
01914     else
01915     {
01916         // We need to insert a body element into the legacy document
01917         xmlNodePtr root = xmlDocGetRootElement( doc );
01918         xmlNodePtr seq = root->children;
01919         if ( !seq )
01920             return false;
01921         if ( xmlStrcmp( seq->name, ( const xmlChar* ) "seq" ) == 0 )
01922         {
01923             xmlNodePtr body = xmlNewNode( NULL, ( const xmlChar * ) "body" );
01924             while ( seq )
01925             {
01926                 xmlNodePtr next = seq->next;
01927                 xmlUnlinkNode( seq );
01928                 xmlAddChild( body, seq );
01929                 seq = next;
01930             }
01931             xmlAddChild( root, body );
01932         }
01933         dirty = true;
01934     }
01935 
01936     RefreshCount( );
01937 
01938     return true;
01939 }
01940 
01941 static bool relativeMap( xmlNodePtr node, void *p, bool *freed )
01942 {
01943     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
01944     {
01945         // Check whether the required properties exist
01946         xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" );
01947 
01948         if ( src != NULL )
01949         {
01950             // Determine the absolute path to the file indicated by src
01951             string & directory = *( string * ) p;
01952             string index = directory_utils::get_relative_path_to_file( directory, ( char * ) src );
01953 
01954             // Save to relative file now
01955             xmlSetProp( node, ( const xmlChar* ) "src", ( xmlChar * ) index.c_str() );
01956         }
01957 
01958         xmlFree( src );
01959     }
01960     return false;
01961 }
01962 
01963 
01964 bool PlayList::SavePlayList( char *filename, bool isLegacyFormat )
01965 {
01966     // Try to save file
01967     bool ret = false;
01968     // Copy the xml doc
01969     xmlDocPtr copy_doc = xmlNewDoc( ( const xmlChar* ) "1.0" );
01970     xmlNodePtr root = xmlNewNode( NULL, ( const xmlChar* ) "smil" );
01971     xmlNewNs( root, ( const xmlChar* ) SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL );
01972     xmlDocSetRootElement( copy_doc, root );
01973 
01974     if ( isLegacyFormat )
01975     {
01976         // Clone without body
01977         parse( this->GetBody()->children, clone, &root );   
01978 
01979         // Add legacy namespace declaration
01980         xmlNewNs( xmlDocGetRootElement( copy_doc ), SMIL20_NAMESPACE_HREF, ( const xmlChar* ) "smil2" );
01981     }
01982     else
01983     {
01984         // Clone including body
01985         parse( this->GetBody(), clone, &root );
01986 
01987         // Convert frame numbers used internally to SMIL time values.
01988         parse( copy_doc->children, convertFramesToSmilTime, NULL );
01989     }
01990 
01991     if ( Preferences::getInstance().relativeSave )
01992     {
01993         // Obtain path relative to filenames directory
01994         string path = directory_utils::get_directory_from_file( filename );
01995         // Convert the copy to relative
01996         parse( copy_doc->children, relativeMap, &path );
01997         // Save the copy
01998         ret = xmlSaveFormatFile( filename, copy_doc, 1 ) != -1;
01999     }
02000     else
02001     {
02002         // Can save directly
02003         ret = xmlSaveFormatFile( filename, copy_doc, 1 ) != -1;
02004     }
02005     // Delete the copy
02006     xmlFreeDoc( copy_doc );
02007 
02008     // If saved...
02009     if ( !isLegacyFormat && ret )
02010     {
02011         // ... and doc name is unspecified, we have now and we're not dirty any more
02012         // though all undos are dirty
02013         if ( doc_name == "" )
02014         {
02015             doc_name = string( filename );
02016             dirty = false;
02017             GetEditorBackup( ) ->SetAllDirty( );
02018         }
02019         // ... and doc name is the same as file name then we're not dirty any more any more
02020         // though all undos are dirty
02021         else if ( !strcmp( filename, doc_name.c_str() ) )
02022         {
02023             dirty = false;
02024             GetEditorBackup( ) ->SetAllDirty( );
02025         }
02026         // ... otherwise we're still dirty - undos stay the same
02027     }
02028 
02029     return ret;
02030 }
02031 
02042 bool PlayList::SavePlayListEli( char * filename, bool isPAL )
02043 {
02044     EliInfos eli;
02045     if ( doc != NULL )
02046     {
02047         parse( GetBody(), convertEli, &eli );
02048     }
02049 
02050     /* Open a file */
02051     ofstream eli_file( filename );
02052     if ( !eli_file )
02053     {
02054         return false;
02055     }
02056 
02057     eli_file << "LAV Edit List" << endl;
02058     eli_file << ( isPAL ? "PAL" : "NTSC" ) << endl;
02059 
02060     /* The number of clips */
02061     eli_file << eli.size() << endl;
02062 
02063     /* Now, all the clips, without numbers  */
02064     EliInfosIterator End = eli.end();
02065     EliInfosIterator i;
02066     for ( i = eli.begin(); i != End; i++ )
02067     {
02068         eli_file << ( *i ).file << endl;
02069     }
02070 
02071     /* Now, number \s begin \s end\n */
02072     int count = 0;
02073     for ( i = eli.begin(); i != End; i++ )
02074     {
02075         eli_file << count
02076         << " " << ( *i ).clipBegin
02077         << " " << ( *i ).clipEnd << endl;
02078         ++count;
02079     }
02080 
02081     /* Check the final status */
02082     if ( eli_file.bad() )
02083     {
02084         return false;
02085     }
02086 
02087     /* We are done */
02088     eli_file.close();
02089 
02090     return true;
02091 }
02092 
02093 
02094 bool PlayList::SavePlayListSrt( const char* filename )
02095 {
02096     SrtContext srt( filename );
02097 
02098     /* Open a file */
02099     if ( !srt.file )
02100     {
02101         return false;
02102     }
02103     if ( doc )
02104     {
02105         parse( GetBody(), convertSrt, &srt );
02106     }
02107     
02108     return !srt.file.bad();
02109 }
02110 
02111 
02121 void PlayList::CleanPlayList( xmlNodePtr node )
02122 {
02123     while ( node != NULL )
02124     {
02125 
02126         xmlNodePtr nodeToDelete = NULL;
02127 
02128         CleanPlayList( node->children );
02129         if ( xmlStrcmp( node->name, ( const xmlChar* ) "smil" ) == 0 )
02130         {
02131             //do nothing
02132         }
02133         else if ( xmlStrcmp( node->name, ( const xmlChar* ) "body" ) == 0 )
02134         {
02135             //do nothing
02136         }
02137         else if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 )
02138         {
02139             if ( node->children == NULL )
02140             {
02141                 nodeToDelete = node;
02142             }
02143         }
02144         else if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
02145         {
02146             // do nothing
02147         }
02148         else
02149             nodeToDelete = node;
02150         node = node->next;
02151 
02152         if ( nodeToDelete != NULL )
02153         {
02154             xmlUnlinkNode( nodeToDelete );
02155             xmlFreeNode( nodeToDelete );
02156         }
02157     }
02158     RefreshCount( );
02159 }
02160 
02164 void PlayList::CleanPlayList( )
02165 {
02166     if ( GetNumFrames() > 0 )
02167         Delete( 0, GetNumFrames() );
02168     dirty = false;
02169     doc_name = "";
02170     RefreshCount( );
02171 }
02172 
02180 static bool checkIfFileUsed( xmlNodePtr node, void *p, bool *freed )
02181 {
02182     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
02183     {
02184         // Check whether the required properties exist
02185         xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" );
02186         string index( ( char * ) src );
02187         xmlFree( src );
02188         return *( string * ) p == index;
02189     }
02190     return false;
02191 }
02192 
02199 bool PlayList::IsFileUsed( string filename ) const
02200 {
02201     return parse( GetBody(), checkIfFileUsed, &filename );
02202 }
02203 
02209 bool PlayList::IsDirty( ) const
02210 {
02211     return dirty;
02212 }
02213 
02219 void PlayList::SetDirty( bool value )
02220 {
02221     dirty = value;
02222 }
02223 
02232 string PlayList::GetDocName( ) const
02233 {
02234     return doc_name;
02235 }
02236 
02245 void PlayList::SetDocName( string m_doc_name )
02246 {
02247     doc_name = m_doc_name;
02248 }
02249 
02250 void PlayList::GetLastCleanPlayList( PlayList &playlist )
02251 {
02252     if ( playlist.GetNumFrames() > 0 )
02253         playlist.Delete( 0, playlist.GetNumFrames() );
02254 
02255     // If we have a file, load it, otherwise we have valid contents already...
02256     if ( doc_name != "" )
02257         playlist.LoadPlayList( ( char * ) doc_name.c_str() );
02258 }
02259 
02265 string PlayList::GetProjectDirectory( )
02266 {
02267     string output = "";
02268 
02269     if ( GetDocName() != "" )
02270         output = directory_utils::get_directory_from_file( GetDocName( ) );
02271 
02272     if ( output == "" && strcmp( Preferences::getInstance().defaultDirectory, "" ) )
02273         output = directory_utils::expand_directory( Preferences::getInstance().defaultDirectory );
02274 
02275     if ( output == "" )
02276         output = directory_utils::join_file_to_directory( "", "" );
02277 
02278     return output;
02279 }
02280 
02281 string PlayList::GetSeqAttribute( int frameNum, const char* name ) const
02282 {
02283     MovieInfo data;
02284 
02285     memset( &data, 0, sizeof( MovieInfo ) );
02286     data.absBegin = 0;
02287     data.absEnd = 0;
02288     data.absFrame = frameNum;
02289 
02290     if ( parse( GetBody(), findSceneStart, &data ) && data.sequence )
02291     {
02292         const char *value = ( const char* ) xmlGetProp( data.sequence, ( const xmlChar * ) name );
02293         if ( value )
02294             return value;
02295     }
02296     return "";
02297 }
02298 
02299 
02300 bool PlayList::SetSeqAttribute( int frameNum, const char* name, const char* value )
02301 {
02302     MovieInfo data;
02303 
02304     memset( &data, 0, sizeof( MovieInfo ) );
02305     data.absBegin = 0;
02306     data.absEnd = 0;
02307     data.absFrame = frameNum;
02308 
02309     if ( parse( GetBody(), findSceneStart, &data ) && data.sequence )
02310     {
02311         xmlSetProp( data.sequence, ( const xmlChar * ) name, ( const xmlChar * ) value );
02312         dirty = true;
02313         return true;
02314     }
02315     return false;
02316 }
02317 
02318 
02319 bool PlayList::SetDocId( const char* value )
02320 {
02321     if ( GetBody() )
02322     {
02323         xmlSetProp( GetBody(), ( const xmlChar * ) "id", ( const xmlChar * ) value );
02324         dirty = true;
02325         return true;
02326     }
02327     return false;
02328 }
02329 
02330 
02331 string PlayList::GetDocId( ) const
02332 {
02333     if ( GetBody() )
02334     {
02335         const char *value = ( const char* ) xmlGetProp( GetBody(), ( const xmlChar * ) "id" );
02336         if ( value )
02337             return value;
02338     }
02339     return "";
02340 }
02341 
02342 
02343 bool PlayList::SetDocTitle( const char* value )
02344 {
02345     if ( GetBody() )
02346     {
02347         xmlSetProp( GetBody(), ( const xmlChar * ) "title", ( const xmlChar * ) value );
02348         dirty = true;
02349         return true;
02350     }
02351     return false;
02352 }
02353 
02354 
02355 string PlayList::GetDocTitle( ) const
02356 {
02357     if ( GetBody() )
02358     {
02359         const char *value = ( const char* ) xmlGetProp( GetBody(), ( const xmlChar * ) "title" );
02360         if ( value )
02361             return value;
02362     }
02363     return "";
02364 }
02365 
02366 
02371 EditorBackup::EditorBackup() : position( -1 )
02372 {
02373     cerr << ">> Creating undo/redo buffer" << endl;
02374     maxUndos = Preferences::getInstance().maxUndos;
02375 }
02376 
02380 EditorBackup::~EditorBackup()
02381 {
02382     cerr << ">> Destroying undo/redo buffer" << endl;
02383 
02384     while ( backups.size() )
02385     {
02386         delete backups[ backups.size() - 1 ];
02387         backups.pop_back();
02388     }
02389 }
02390 
02391 void EditorBackup::Store( PlayList *playlist, bool isPersisted )
02392 {
02393     cerr << ">>> Received playlist to store at position " << position + 1 << endl;
02394 
02395     // Three conditions to check:
02396     //
02397     // 1. The undo position is 1 less than the current size of the vector and less than the max size
02398     // in which case we dump the new playlist at the top of the vector and increment the position
02399     //
02400 
02401     if ( ( position + 1 ) == ( int ) backups.size() && ( position < maxUndos || maxUndos == 0 ) )
02402     {
02403         cerr << ">>>> Adding to end" << endl;
02404         position ++;
02405         PlayList *temp = new PlayList;
02406         playlist->GetPlayList( 0, playlist->GetNumFrames() - 1, *temp );
02407         temp->SetDirty( playlist->IsDirty( ) );
02408         backups.push_back( temp );
02409     }
02410 
02411     //
02412     // 2. The undo position is not at the end, in which case we need to remove everything from position
02413     // to the end before pushing
02414     //
02415 
02416     else if ( ( position + 1 ) < ( int ) backups.size() )
02417     {
02418         cerr << ">>>> Cleaning from " << position + 1 << " to " << backups.size() << endl;
02419         position ++;
02420         while ( position < ( int ) backups.size() )
02421         {
02422             delete backups[ backups.size() - 1 ];
02423             backups.pop_back();
02424         }
02425         PlayList *temp = new PlayList;
02426         playlist->GetPlayList( 0, playlist->GetNumFrames() - 1, *temp );
02427         temp->SetDirty( playlist->IsDirty( ) );
02428         backups.push_back( temp );
02429     }
02430 
02431     //
02432     // 3. We're at the top of the stack so we need to remove position 0 and push to the end
02433     //
02434 
02435     else if ( position == maxUndos )
02436     {
02437         cerr << ">>>> Removing the earliest playlist to make room" << endl;
02438         delete backups[ 0 ];
02439         backups.erase( backups.begin() );
02440         PlayList *temp = new PlayList;
02441         playlist->GetPlayList( 0, playlist->GetNumFrames() - 1, *temp );
02442         temp->SetDirty( playlist->IsDirty( ) );
02443         backups.push_back( temp );
02444     }
02445 
02446     //
02447     // Just in case we missed something...
02448     //
02449 
02450     else
02451     {
02452         cerr << ">>>> Unknown condition - position = " << position << " size = " << backups.size() << endl;
02453     }
02454 
02455     // Update persistent storage
02456     if ( isPersisted )
02457     {
02458         // First, delete contents of private directory in $HOME
02459         string directory = KINO_AUTOSAVE_DIR;
02460         mkdir( directory.c_str(), 0700 );
02461         DIR* dir = opendir( directory.c_str( ) );
02462         struct dirent* entry;
02463         if ( dir )
02464         {
02465             while ( ( entry = readdir( dir ) ) != NULL )
02466             {
02467                 if ( entry->d_name[0] != '.' )
02468                 {
02469                     ostringstream sb;
02470                     sb << directory << "/" << entry->d_name << ends;
02471                     unlink( sb.str().c_str() );
02472                 }
02473             }
02474             closedir( dir );
02475     
02476             // Second, write each dirty backup
02477             // absolute filenames is faster and more appropriate here
02478             bool isRelative = Preferences::getInstance().relativeSave;
02479             Preferences::getInstance().relativeSave = false;
02480             for ( size_t i = 0; i < backups.size(); ++i )
02481             {
02482                 if ( backups[i]->IsDirty() )
02483                 {
02484                     ostringstream sb;
02485                     sb << directory << "/" << i << ".xml" << ends;
02486                     // save in legacy format for speed
02487                     backups[i]->SavePlayList( const_cast<char*>( sb.str().c_str() ), true );
02488                 }
02489             }
02490             Preferences::getInstance().relativeSave = isRelative;
02491         }
02492     }
02493 }
02494 
02495 void EditorBackup::Undo( PlayList *playlist )
02496 {
02497     cerr << ">>> Received request to undo from position " << position - 1 << endl;
02498     if ( position > 0 )
02499     {
02500         position --;
02501         playlist->Delete( 0, playlist->GetNumFrames() - 1 );
02502         PlayList temp( *( backups[ position ] ) );
02503         playlist->InsertPlayList( temp, 0 );
02504         playlist->SetDirty( temp.IsDirty( ) );
02505     }
02506     else
02507     {
02508         cerr << ">>>> Unable to satisfy request." << endl;
02509     }
02510 }
02511 
02512 void EditorBackup::Redo( PlayList *playlist )
02513 {
02514     cerr << ">>> Received request to recover from position " << position + 1 << endl;
02515     if ( ( position + 1 ) < ( int ) backups.size() )
02516     {
02517         position ++;
02518         playlist->Delete( 0, playlist->GetNumFrames() - 1 );
02519         PlayList temp( *( backups[ position ] ) );
02520         playlist->InsertPlayList( temp, 0 );
02521         playlist->SetDirty( temp.IsDirty( ) );
02522     }
02523     else
02524     {
02525         cerr << ">>>> Unable to satisfy request." << endl;
02526     }
02527 }
02528 
02536 void EditorBackup::SetAllDirty( )
02537 {
02538     vector< PlayList *>::iterator n;
02539     for ( n = backups.begin(); n != backups.end(); ++n )
02540         ( **n ).SetDirty( true );
02541     if ( position >= 0 )
02542         backups[ position ] ->SetDirty( false );
02543 }
02544 
02548 void EditorBackup::Clear( )
02549 {
02550     while ( backups.size() )
02551     {
02552         delete backups[ backups.size() - 1 ];
02553         backups.pop_back();
02554     }
02555     position = -1;
02556 }
02557 
02561 EditorBackup *GetEditorBackup( )
02562 {
02563     static EditorBackup * backup = new EditorBackup( );
02564     return backup;
02565 }
02566 
02567 bool EditorBackup::Restore( PlayList * playlist )
02568 {
02569     string directory = KINO_AUTOSAVE_DIR;
02570     DIR* dir = opendir( directory.c_str( ) );
02571     struct dirent* entry;
02572     bool result = false;
02573 
02574     if ( dir )
02575     {
02576         std::vector<int> names;
02577         while ( ( entry = readdir( dir ) ) != NULL )
02578         {
02579             if ( entry->d_name[0] != '.' )
02580                 names.push_back( atoi( entry->d_name ) );
02581         }
02582         closedir( dir );
02583 
02584         if ( !names.empty() )
02585         {
02586             std::sort( names.begin(), names.end() );
02587             Clear();
02588             PlayList pl;
02589             for ( size_t i = 0; i < names.size(); ++i )
02590             {
02591                 ostringstream sb;
02592                 sb << directory << "/" << names[i] << ".xml" << ends;
02593                 if ( pl.LoadPlayList( const_cast<char*>( sb.str().c_str() ) ) )
02594                     Store( &pl, false );
02595             }
02596             ++position;
02597             Undo( playlist );
02598             result = true;
02599         }
02600     }
02601     return result;
02602 }

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