Cocoa translation help needed (very simple)

Can someone point me in the right direction on how to use and implement QT Kit’s setSelection in my Applescript Studio project? I have no Cocoa knowledge and all my clumsy attempts to do this have failed so far.

What I want to accomplish is really very simple: to set the selection of the movie in my movie view and play that selection. The range of the selection depends on the current time, so I need to get that as well. In plain language:

Call 1:


tell application "QuickTime Player"
	tell movie 1
		return current time
	end tell
end tell

Call 2:


tell application "QuickTime Player"
	tell movie 1
		set selection start to x
		set selection end to y
		set current time to selection start
		play
	end tell
end tell

Can someone help me, please?

Could someone at least tell an Obj-C newbie if he’s heading in the right direction? Pretty please? This is all greek to me.


#import <Cocoa/Cocoa.h>
#import <QTKit/QTKit.h>

@interface methods : NSObject // QTMovie???
+ (void)setSelection:(QTTimeRange)selection;
+ (QTTime)currentTime;
@end

@implementation methods

+ (void)setSelection: ???
+ (QTTime)currentTime: ???

@end

I took a closer look at a sample project by Jonathan Nathan and was finally able to at least get a result of this call:


call method "getMovieProperties:" of class "methods" with parameters {view "movie"}

With this in my “methods.m” file:


#import <Cocoa/Cocoa.h>
#import <QuickTime/QuickTime.h>

@interface methods : NSObject
+ (NSDictionary *)getMovieProperties:(NSMovieView *)movieView;
@end

@implementation methods

+ (NSDictionary *)getMovieProperties:(NSMovieView *)movieView {
    NSMutableDictionary *returnData = [[[NSMutableDictionary alloc] init] autorelease];
    Movie qtmovie = [[movieView movie] QTMovie];
	[returnData setObject:[NSNumber numberWithInt:(GetMovieTime(qtmovie, nil))] forKey:@"current_time"];
	return returnData;
}

@end

Only problem is that the return value is always 0. Turns out Nathan’s original project always returns 0 as well (or rather 0:0:0, as the result is parsed to a timestring).

Any ideas? Please.

Probably not. However, I’m guessing Studio’s ‘movie’ class is probably bridged to QTMovie, in which case you ought to be able to get the movie object from your window’s movie view, then use ‘call method’ to invoke the relevant QTMovie instance methods; no need to dive into ObjC yourself.

HTH

Thanks you for replying. You mean something like this (doesn’t work)?


tell window 1
		tell movie view 1
			call method "QTMovieCurrentTimeAttribute:" of object "QTMovie"
		end tell
	end tell

On second thoughts, Studio’s ‘movie’ class will be bridged to NSMovie, which is pretty useless in itself. So you’d need to call its ‘QTMovie’ method and feed the result of that to QTMovie class’s ‘movieWithQuickTimeMovie:disposeWhenDone:error:’ method in order to get a QTMovie instance that you can then easily work with. (Much easier than the original C-based QuickTime APIs anyway.) Not sure if you can make that second call with ‘call method’ or if you have to drop into ObjC for that.

Alternatively, you could install the QTKit palette in IB and use a QT movie view in your GUI instead. (I assume Studio is smart enough to let you reference GUI objects even when they’re not bridged directly by AppleScriptKit.)

Sorry I’ve not more time to look into it right now (these days I prefer Python+PyObjC for writing applications so my Studio’s not that great), but hopefully that’ll point you in a useful direction.

HTH

Thank you for the pointers. I still don’t get it, but I’m working on that part :wink:

Sorry, Nathan. The reason I didn’t get a correct return value was that I forgot to address the window with the movie view, eg.


current_time_string of (call method "getMovieProperties:" of class "methods" with parameters {movie view "movie" of window 1}) --> "0:2:9"

So, part 1 completed. Now if only I could figure out how to set the selection of the movie… Hm, wonder if someone can help me?

S L O W L Y making progress… By adding this to my “methods.m” file -


#import <Cocoa/Cocoa.h>
#import <QuickTime/QuickTime.h>

@interface methods : NSObject
+ (NSDictionary *)getCurrentTime:(NSMovieView *)movieView;
+ (NSDictionary *)setCurrentTime:(NSMovieView *)movieView;
@end

@implementation methods

+ (NSDictionary *)getCurrentTime:(NSMovieView *)movieView {
Movie qtmovie = [[movieView movie] QTMovie];
   return [NSNumber numberWithInt:(GetMovieTime(qtmovie, nil))];
}

+ (NSDictionary *)setCurrentTime:(NSMovieView *)movieView {
Movie qtmovie = [[movieView movie] QTMovie];
SetMovieTimeValue(qtmovie, 600);
}
@end

  • I am now able to set the current time using
call method "setCurrentTime:" of class "methods" with parameters {movie view 1 of window 1}

Now, how do I pass a value parameter to this method? Hm, I guess it has something to do with NSValue, right? TIA

Well well well. Look at me. I was finally able to figure it all out. I’ll post a quick how-to here for those interested as soon as I can find the time.

OK, for those who want to add some additional scripting capabilities to their movie view by implementing an Objective-C bridge but don’t know how. Here it is:

  1. Add the QuickTime framework to your project (by dragging /System/Library/Frameworks/QuickTime.framework to Frameworks)
  2. Add a new empty file to your project. Name it “methods.m”
  3. Paste this into that file:

#import <Cocoa/Cocoa.h>
#import <QuickTime/QuickTime.h>

@interface methods : NSObject

+ (NSDictionary *)getCurrentTime:(NSMovieView *)movieView;
+ (NSDictionary *)goToTime:(NSMovieView *)movieView startTime:(int)startTime;
+ (NSDictionary *)setSelection:(NSMovieView *)movieView startTime:(int)startTime endTime:(int)endTime;
+ (NSDictionary *)setRate:(NSMovieView *)movieView value:(double)value;

@end

@implementation methods

+ (NSDictionary *)getCurrentTime:(NSMovieView *)movieView {
	Movie qtmovie = [[movieView movie] QTMovie];
	TimeScale timeScale = GetMovieTimeScale(qtmovie);
	return [NSNumber numberWithInt:( GetMovieTime (qtmovie, nil) / timeScale )]; 
}

+ (NSDictionary *)goToTime:(NSMovieView *)movieView startTime:(int)startTime {
	Movie qtmovie = [[movieView movie] QTMovie];
	TimeScale timeScale = GetMovieTimeScale(qtmovie);
	TimeValue startMovieTime = (TimeValue) startTime  * timeScale;
	SetMovieTimeValue(qtmovie, startMovieTime);
	return nil;
}

+ (NSDictionary *)setSelection:(NSMovieView *)movieView startTime:(int)startTime endTime:(int)endTime {
	Movie qtmovie = [[movieView movie] QTMovie];
	TimeScale timeScale = GetMovieTimeScale(qtmovie);
	TimeValue selectionStart = (TimeValue) startTime * timeScale;
	TimeValue selectionDuration = (TimeValue) (endTime - startTime) * timeScale;
	SetMovieSelection(qtmovie, selectionStart, selectionDuration);
	return nil;
}

+ (NSDictionary *)setRate:(NSMovieView *)movieView value:(double)value {
	Movie qtmovie = [[movieView movie] QTMovie];
	SetMovieRate(qtmovie, X2Fix(value));
	return nil;
}

@end

  1. Now, from within your applescript, you can call these methods anytime you like. To get the current time, use:

set theMovieView to movie view 1 of window 1
set curTime to (call method "getCurrentTime:" of class "methods" with parameters {theMovieView})
--> returns current time in seconds

To set the current selection:


set theMovieView to movie view 1 of window 1
set selectionStart to 0 -- time in seconds
set selectionEnd to 10 -- time in seconds
call method "setSelection:startTime:endTime:" of class "methods" with parameters {theMovieView, selectionStart, selectionEnd}

To go to a specified time:


set theMovieView to movie view 1 of window 1
set selectionStart to 10 -- time in seconds
call method "goToTime:startTime:" of class "methods" with parameters {theMovieView, selectionStart}

To set the playback rate:


set theMovieView to movie view 1 of window 1
set theRate to 2 -- 2=double speed, 1=normal playback, -1=normal speed backwards
call method "setRate:value:" of class "methods" with parameters {theMovieView, theRate}

Additionally, if you would like to add a movie view programatically to an existing window, you can add this line to the interface part of your “methods.m” file:


+ (NSMovieView *)makeMovieView:(NSWindow *)theWindow movie:(NSMovie*)theMovie withFrame:(NSArray *)frameArray;

With the following implementation:


+ (NSMovieView *)makeMovieView:(NSWindow *)theWindow movie:(NSMovie*)theMovie withFrame:(NSArray *)frameArray {
      
   // Create the movie view
   int x, y, w, h;
	x = [[frameArray objectAtIndex:0] intValue];
	y = [[frameArray objectAtIndex:1] intValue];
	w = [[frameArray objectAtIndex:2] intValue];
    h = [[frameArray objectAtIndex:3] intValue];
   NSMovieView* movieView = [[NSMovieView alloc] initWithFrame:NSMakeRect(x, y, w, h)];
   
   // Load the movie
	[movieView setMovie];
	
	// Show the controller?
	[movieView showController:NO adjustingSize:NO];
	
	// Play selection only?
	[movieView setPlaysSelectionOnly:YES];

   // Add the movie view to the window
   [[theWindow contentView] addSubview:movieView]; 
   [movieView release];
   return movieView;
}

To create a new movie view from within your script, all you have to do is to:


set theWindow to window 1
set theMovie to (load movie "MyMovie")
set x to 0 
set y to 0 -- {0, 0} = bottom left corner of window
set w to 800 -- width of movie view in pixels
set h to 600 -- height of movie view in pixels
set newMovieView to (call method "makeMovieView:movie:withFrame:" of class "methods" with parameters {theWindow, theMovie, {x, y, w, h}})

In this example I have only implemented a few methods. To add some more, have a look at “ADC Home > Reference Library > Documentation > Cocoa > Objective-C Language > Application Kit Reference for Objective-C > NSMovieView” in the XCode documentation. By studying the examples provided here you should easily be able to do the neccessary substitutions. Have fun.