From d24a3e783ddcdd09bab3e2ab1b041a6b139652a0 Mon Sep 17 00:00:00 2001 From: Matthias Neeracher <neeri@users.sourceforge.net> Date: Sun, 14 Aug 2011 21:25:48 +0200 Subject: [PATCH] Crude export capabilities work --- Medianno/MAAnno.h | 3 ++ Medianno/MAAnno.mm | 10 +++++ Medianno/MADocWindow.h | 1 + Medianno/MADocWindow.mm | 78 +++++++++++++++++++++++++++++++--- Medianno/en.lproj/MainMenu.xib | 34 ++++++++++++++- 5 files changed, 118 insertions(+), 8 deletions(-) diff --git a/Medianno/MAAnno.h b/Medianno/MAAnno.h index ec9493a..59430d3 100644 --- a/Medianno/MAAnno.h +++ b/Medianno/MAAnno.h @@ -8,6 +8,7 @@ #import <Foundation/Foundation.h> #import <CoreData/CoreData.h> +#import <QTKit/QTKit.h> @class MATag; @@ -21,6 +22,8 @@ - (NSString *)shortLocation; + (NSSet *)keyPathsForValuesAffectingShortLocation; +- (QTTime)qtLocation; ++ (NSSet *)keyPathsForValuesAffectingQtLocation; /* * Tags are never manipulated through the tag objects, but always through diff --git a/Medianno/MAAnno.mm b/Medianno/MAAnno.mm index 81c2d90..3ed957c 100644 --- a/Medianno/MAAnno.mm +++ b/Medianno/MAAnno.mm @@ -51,6 +51,16 @@ return [NSSet setWithObject:@"location"]; } +- (QTTime)qtLocation +{ + return QTTimeFromString(self.location); +} + ++ (NSSet *)keyPathsForValuesAffectingQtLocation +{ + return [NSSet setWithObject:@"location"]; +} + - (NSArray *)tagDescriptions { NSSet * tags = self.tags; diff --git a/Medianno/MADocWindow.h b/Medianno/MADocWindow.h index f849332..739dbff 100644 --- a/Medianno/MADocWindow.h +++ b/Medianno/MADocWindow.h @@ -19,6 +19,7 @@ } - (IBAction)addMediaFiles:(id)sender; +- (IBAction)exportMedia:(id)sender; - (void)addMedia:(NSArray *)urls; - (IBAction)addAnnotation:(id)sender; - (IBAction)mediaSkipBackward:(id)sender; diff --git a/Medianno/MADocWindow.mm b/Medianno/MADocWindow.mm index 75856f5..ce166e8 100644 --- a/Medianno/MADocWindow.mm +++ b/Medianno/MADocWindow.mm @@ -37,6 +37,11 @@ #pragma mark Media management +- (QTMovie *)currentMovie +{ + return [movieView movie]; +} + - (IBAction)addMediaFiles:(id)sender { NSOpenPanel * openPanel = [NSOpenPanel openPanel]; @@ -87,10 +92,68 @@ [[[MAAddMediaSheet alloc] init] runWithParentWindow:self media:expandedURLs]; } +- (void)exportMediaToURL:(NSURL *)url +{ + QTMovie * currentMovie = [self currentMovie]; + NSIndexSet * selection = [annotationController selectionIndexes]; + NSArray * annotations = [annotationController arrangedObjects]; + // + // Determine overall range of interest + // + QTTimeRange exportRange = QTMakeTimeRange(QTMakeTimeWithTimeInterval(0.0), [[[currentMovie movieAttributes] valueForKey:QTMovieDurationAttribute] QTTimeValue]); + if ([selection count]) { + if ([selection lastIndex] < [annotations count]-1) + exportRange.duration = [[annotations objectAtIndex:[selection lastIndex]+1] qtLocation]; + if ([selection firstIndex] > 0) { + exportRange.time = [[annotations objectAtIndex:[selection firstIndex]] qtLocation]; + exportRange.duration = QTTimeDecrement(exportRange.duration, exportRange.time); + } + } + NSError * error; + QTMovie * exportMovie = [[QTMovie alloc] initWithMovie:currentMovie timeRange:exportRange error:&error]; + if (!exportMovie) + [[self document] presentError:error]; + + if ([selection count] < [selection lastIndex]-[selection firstIndex]+1) { + [exportMovie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute]; + // + // Cut out discontiguous portions (in reverse order to simplify time accounting) + // + __block NSUInteger lastUsed = [selection lastIndex]; + [selection enumerateRangesWithOptions:NSEnumerationReverse usingBlock:^(NSRange range, BOOL *stop) { + if (range.location+range.length < lastUsed+1) { + QTTimeRange unusedRange = QTMakeTimeRange([[annotations objectAtIndex:range.location+range.length] qtLocation], + [[annotations objectAtIndex:lastUsed] qtLocation]); + unusedRange.duration = QTTimeDecrement(unusedRange.duration, unusedRange.time); + unusedRange.time = QTTimeDecrement(unusedRange.time, exportRange.time); + [exportMovie deleteSegment:unusedRange]; + } + lastUsed = range.location; + }]; + } + // + // Now write out the movie + // + NSDictionary * attributes = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] + forKey:QTMovieFlatten]; + [exportMovie writeToFile:[url path] withAttributes:attributes]; +} + +- (IBAction)exportMedia:(id)sender +{ + NSSavePanel * savePanel = [NSSavePanel savePanel]; + [savePanel setAllowedFileTypes:[QTMovie movieFileTypes:QTIncludeCommonTypes]]; + [savePanel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) { + [savePanel orderOut:self]; + if (result == NSFileHandlingPanelOKButton) + [self exportMediaToURL:[savePanel URL]]; + }]; +} + - (IBAction)addAnnotation:(id)sender { [movieView pause:sender]; - QTTime location = [[movieView movie] currentTime]; + QTTime location = [[self currentMovie] currentTime]; MAAnno * anno = [[self document] addAnnotationForMedia:[[mediaController selectedObjects] objectAtIndex:0] location:location]; [annotationController setSelectedObjects:[NSArray arrayWithObject:anno]]; [annotationTable editColumn:[annotationTable columnWithIdentifier:@"tags"] @@ -115,7 +178,7 @@ - (IBAction)toggleMediaPlay:(id)sender { - if ([[movieView movie] rate] > 0.0f) + if ([[self currentMovie] rate] > 0.0f) [movieView pause:sender]; else [movieView play:sender]; @@ -125,7 +188,7 @@ { if ([item action] == @selector(toggleMediaPlay:)) { NSMenuItem * menuItem = (NSMenuItem *)item; - if ([[movieView movie] rate] > 0.0f) + if ([[self currentMovie] rate] > 0.0f) [menuItem setTitle:NSLocalizedString(@"Pause", @"Pause playback")]; else [menuItem setTitle:NSLocalizedString(@"Play", @"Start playback")]; @@ -144,13 +207,14 @@ static NSTimeInterval sLastSkip = 0.0; - (void)skipTimeInterval { - QTTime interval= QTMakeTimeWithTimeInterval(abs(sLastSkip)); - QTTime current = [[movieView movie] currentTime]; + QTMovie * currentMovie = [self currentMovie]; + QTTime interval = QTMakeTimeWithTimeInterval(abs(sLastSkip)); + QTTime current = [currentMovie currentTime]; if (sLastSkip > 0) current = QTTimeIncrement(current, interval); else current = QTTimeDecrement(current, interval); - [[movieView movie] setCurrentTime:current]; + [currentMovie setCurrentTime:current]; sLastSkip *= 1.1; [NSRunLoop cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetSkipFactor:) object:self]; [self performSelector:@selector(resetSkipFactor:) withObject:self afterDelay:1.0]; @@ -214,7 +278,7 @@ static NSTimeInterval sLastSkip = 0.0; NSArray * selection = [annotationController selectedObjects]; if ([selection count]) if (MAAnno * firstSelectedAnno = [selection objectAtIndex:0]) - [[movieView movie] setCurrentTime:QTTimeFromString([firstSelectedAnno location])]; + [[self currentMovie] setCurrentTime:[firstSelectedAnno qtLocation]]; } @end diff --git a/Medianno/en.lproj/MainMenu.xib b/Medianno/en.lproj/MainMenu.xib index 4293527..6f32b69 100644 --- a/Medianno/en.lproj/MainMenu.xib +++ b/Medianno/en.lproj/MainMenu.xib @@ -293,6 +293,15 @@ <reference key="NSOnImage" ref="1033313550"/> <reference key="NSMixedImage" ref="310636482"/> </object> + <object class="NSMenuItem" id="678484874"> + <reference key="NSMenu" ref="720053764"/> + <string key="NSTitle">Export…</string> + <string key="NSKeyEquiv">S</string> + <int key="NSKeyEquivModMask">1048576</int> + <int key="NSMnemonicLoc">2147483647</int> + <reference key="NSOnImage" ref="1033313550"/> + <reference key="NSMixedImage" ref="310636482"/> + </object> <object class="NSMenuItem" id="1010469920"> <reference key="NSMenu" ref="720053764"/> <bool key="NSIsDisabled">YES</bool> @@ -1344,6 +1353,14 @@ </object> <int key="connectionID">584</int> </object> + <object class="IBConnectionRecord"> + <object class="IBActionConnection" key="connection"> + <string key="label">exportMedia:</string> + <reference key="source" ref="1014"/> + <reference key="destination" ref="678484874"/> + </object> + <int key="connectionID">592</int> + </object> </object> <object class="IBMutableOrderedSet" key="objectRecords"> <object class="NSArray" key="orderedObjects"> @@ -1440,6 +1457,7 @@ <reference ref="1010469920"/> <reference ref="906148750"/> <reference ref="7029338"/> + <reference ref="678484874"/> </object> <reference key="parent" ref="379814623"/> </object> @@ -2042,6 +2060,11 @@ <reference key="parent" ref="0"/> <string key="objectName">AppController</string> </object> + <object class="IBObjectRecord"> + <int key="objectID">590</int> + <reference key="object" ref="678484874"/> + <reference key="parent" ref="720053764"/> + </object> </object> </object> <object class="NSMutableDictionary" key="flattenedProperties"> @@ -2138,6 +2161,7 @@ <string>58.IBPluginDependency</string> <string>580.IBPluginDependency</string> <string>583.IBPluginDependency</string> + <string>590.IBPluginDependency</string> <string>72.IBPluginDependency</string> <string>73.IBPluginDependency</string> <string>74.IBPluginDependency</string> @@ -2253,6 +2277,7 @@ <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> </object> </object> <object class="NSMutableDictionary" key="unlocalizedProperties"> @@ -2267,7 +2292,7 @@ <reference key="dict.values" ref="0"/> </object> <nil key="sourceID"/> - <int key="maxID">589</int> + <int key="maxID">592</int> </object> <object class="IBClassDescriber" key="IBDocument.Classes"> <object class="NSMutableArray" key="referencedPartialClassDescriptions"> @@ -2307,6 +2332,7 @@ <bool key="EncodedWithXMLCoder">YES</bool> <string>addAnnotation:</string> <string>addMediaFiles:</string> + <string>exportMedia:</string> <string>mediaSkipBackward:</string> <string>mediaSkipForward:</string> <string>toggleMediaPlay:</string> @@ -2318,6 +2344,7 @@ <string>id</string> <string>id</string> <string>id</string> + <string>id</string> </object> </object> <object class="NSMutableDictionary" key="actionInfosByName"> @@ -2326,6 +2353,7 @@ <bool key="EncodedWithXMLCoder">YES</bool> <string>addAnnotation:</string> <string>addMediaFiles:</string> + <string>exportMedia:</string> <string>mediaSkipBackward:</string> <string>mediaSkipForward:</string> <string>toggleMediaPlay:</string> @@ -2340,6 +2368,10 @@ <string key="name">addMediaFiles:</string> <string key="candidateClassName">id</string> </object> + <object class="IBActionInfo"> + <string key="name">exportMedia:</string> + <string key="candidateClassName">id</string> + </object> <object class="IBActionInfo"> <string key="name">mediaSkipBackward:</string> <string key="candidateClassName">id</string>