summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--macosx/Controller.h85
-rw-r--r--macosx/Controller.m1668
-rw-r--r--macosx/English.lproj/MainMenu.xib14
-rw-r--r--macosx/English.lproj/Queue.xib17
-rw-r--r--macosx/HBAttributedStringAdditions.h16
-rw-r--r--macosx/HBAttributedStringAdditions.m20
-rw-r--r--macosx/HBCore.h41
-rw-r--r--macosx/HBCore.m155
-rw-r--r--macosx/HBDistributedArray.h36
-rw-r--r--macosx/HBDistributedArray.m328
-rw-r--r--macosx/HBJob+UIAdditions.h2
-rw-r--r--macosx/HBJob+UIAdditions.m406
-rw-r--r--macosx/HBJob.h2
-rw-r--r--macosx/HBJob.m10
-rw-r--r--macosx/HBPreviewGenerator.m94
-rw-r--r--macosx/HBQueueController.h29
-rw-r--r--macosx/HBQueueController.mm1716
-rw-r--r--macosx/HandBrake.xcodeproj/project.pbxproj18
-rw-r--r--macosx/HandBrake.xcodeproj/xcshareddata/xcschemes/HandBrake [DEBUG].xcscheme8
-rw-r--r--macosx/HandBrake.xcodeproj/xcshareddata/xcschemes/HandBrake [RELEASE].xcscheme8
20 files changed, 2278 insertions, 2395 deletions
diff --git a/macosx/Controller.h b/macosx/Controller.h
index 87d305bb3..499051225 100644
--- a/macosx/Controller.h
+++ b/macosx/Controller.h
@@ -5,7 +5,6 @@
It may be used under the terms of the GNU General Public License. */
#import <Cocoa/Cocoa.h>
-#import <Growl/Growl.h>
@class HBQueueController;
@@ -24,9 +23,11 @@
@class HBPresetsManager;
@class HBDockTile;
-@interface HBController : NSObject <NSApplicationDelegate, NSDrawerDelegate, GrowlApplicationBridgeDelegate>
+@class HBJob;
+
+@interface HBController : NSObject <NSApplicationDelegate, NSDrawerDelegate>
{
- IBOutlet NSWindow * fWindow;
+ IBOutlet NSWindow *fWindow;
IBOutlet NSTabView *fMainTabView;
@@ -124,37 +125,16 @@
IBOutlet NSProgressIndicator * fRipIndicator;
BOOL fRipIndicatorShown;
- /* Queue File variables */
- FSEventStreamRef QueueStream;
- NSString * QueueFile;
- NSMutableArray * QueueFileArray;
- NSInteger currentQueueEncodeIndex; // Used to track the currently encoding queueu item
-
/* User Preset variables here */
HBPresetsManager * presetManager;
HBPresetsViewController * fPresetsView;
IBOutlet NSMenu * presetsMenu;
IBOutlet NSDrawer * fPresetDrawer;
-
- /* Queue variables */
- int hbInstanceNum; //stores the number of HandBrake instances currently running
- int fPendingCount; // Number of various kinds of job groups in fJobGroups.
- int fWorkingCount;
-
- pid_t pidNum; // The pid number for this instance
- NSString * currentQueueEncodeNameString;
-
- /* integer to set to determine the previous state
- of encode 0==idle, 1==encoding, 2==cancelled*/
- int fEncodeState;
-
- /* Dock progress variables */
- double dockIconProgress;
-
- HBDockTile *dockTile;
}
+@property (nonatomic, readonly) NSWindow *window;
+
- (IBAction) browseSources: (id) sender;
- (IBAction) showSourceTitleScanPanel: (id) sender;
- (IBAction) closeSourceTitleScanPanel: (id) sender;
@@ -174,47 +154,22 @@
- (void)pictureSettingsDidChange;
- (IBAction) openMainWindow: (id) sender;
-/* Add All titles to the queue */
-- (IBAction) addAllTitlesToQueue: (id) sender;
-- (void) addAllTitlesToQueueAlertDone: (NSWindow *) sheet
- returnCode: (int) returnCode contextInfo: (void *) contextInfo;
-- (void) doAddAllTitlesToQueue;
-
-/* Queue File Stuff */
-- (void) initQueueFSEvent;
-- (void) closeQueueFSEvent;
-- (void) loadQueueFile;
-- (void) reloadQueue;
-- (void)saveQueueFileItem;
-- (void) incrementQueueItemDone:(NSInteger) queueItemDoneIndexNum;
-- (void) performNewQueueScan:(NSString *) scanPath scanTitleNum: (NSInteger) scanTitleNum;
-- (void) processNewQueueEncode;
-- (void) clearQueueEncodedItems;
-/* Queue Editing */
-- (void)rescanQueueItemToMainWindow:(NSUInteger) selectedQueueItem;
-
-- (void) removeQueueFileItem:(NSUInteger) queueItemToRemove;
-- (void) clearQueueAllItems;
-- (void)moveObjectsInQueueArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex;
-- (void)getQueueStats;
-- (void)setQueueEncodingItemsAsPending;
-- (IBAction) addToQueue: (id) sender;
-- (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
- returnCode: (int) returnCode contextInfo: (void *) contextInfo;
-- (void) doAddToQueue;
-
-- (IBAction) showQueueWindow:(id)sender;
+// Queue
+- (IBAction)addToQueue:(id)sender;
+- (IBAction)addAllTitlesToQueue:(id)sender;
+
+- (void)rescanJobToMainWindow:(HBJob *)queueItem;
+- (void)setQueueState:(NSString *)info;
+- (void)setQueueInfo:(NSString *)info progress:(double)progress hidden:(BOOL)hidden;
+
+- (IBAction)showQueueWindow:(id)sender;
- (IBAction)showPreferencesWindow:(id)sender;
-- (IBAction) Rip: (id) sender;
-- (void) overWriteAlertDone: (NSWindow *) sheet
- returnCode: (int) returnCode contextInfo: (void *) contextInfo;
+- (IBAction)rip:(id)sender;
-- (IBAction) Cancel: (id) sender;
-- (void) doCancelCurrentJob;
-- (void) doCancelCurrentJobAndStop;
-- (IBAction) Pause: (id) sender;
+- (IBAction)cancel:(id)sender;
+- (IBAction)pause:(id)sender;
- (IBAction) openHomepage: (id) sender;
- (IBAction) openForums: (id) sender;
@@ -231,8 +186,4 @@
- (IBAction)addFactoryPresets:(id)sender;
- (IBAction)showDebugOutputPanel:(id)sender;
-- (void) remindUserOfSleepOrShutdown;
-
-- (int) hbInstances;
-
@end
diff --git a/macosx/Controller.m b/macosx/Controller.m
index bc57b3518..4023d1300 100644
--- a/macosx/Controller.m
+++ b/macosx/Controller.m
@@ -27,39 +27,26 @@
#import "HBPresetsViewController.h"
#import "HBAddPresetController.h"
-#import "HBPicture+UIAdditions.h"
-#import "HBFilters+UIAdditions.h"
-
#import "HBCore.h"
#import "HBJob.h"
-// DockTile update freqency in total percent increment
-#define dockTileUpdateFrequency 0.1f
-
@interface HBController () <HBPresetsViewControllerDelegate, HBPreviewControllerDelegate, HBPictureControllerDelegate>
@property (nonatomic, copy) NSString *browsedSourceDisplayName;
-// The current job.
+/// The current job.
@property (nonatomic, retain) HBJob *job;
-// The job to be applied from the queue.
+/// The job to be applied from the queue.
@property (nonatomic, retain) HBJob *jobFromQueue;
-// The current selected preset.
+/// The current selected preset.
@property (nonatomic, retain) HBPreset *selectedPreset;
@property (nonatomic) BOOL customPreset;
-/**
- * The HBCore used for scanning.
- */
+/// The HBCore used for scanning.
@property (nonatomic, retain) HBCore *core;
-/**
- * The HBCore used for encoding.
- */
-@property (nonatomic, retain) HBCore *queueCore;
-
@end
@implementation HBController
@@ -77,22 +64,14 @@
fPictureController = [[HBPictureController alloc] init];
fPreviewController = [[HBPreviewController alloc] initWithDelegate:self];
fQueueController = [[HBQueueController alloc] init];
+ fQueueController.controller = self;
+ fQueueController.outputPanel = outputPanel;
// we init the HBPresetsManager
NSURL *presetsURL = [NSURL fileURLWithPath:[[HBUtilities appSupportPath] stringByAppendingPathComponent:@"UserPresets.plist"]];
presetManager = [[HBPresetsManager alloc] initWithURL:presetsURL];
_selectedPreset = [presetManager.defaultPreset retain];
- // Workaround to avoid a bug in Snow Leopard
- // we can switch back to [[NSApplication sharedApplication] applicationIconImage]
- // when we won't support it anymore.
- NSImage *appIcon = [NSImage imageNamed:@"HandBrake"];
- [appIcon setSize:NSMakeSize(1024, 1024)];
-
- // Load the dockTile and instiante initial text fields
- dockTile = [[HBDockTile alloc] initWithDockTile:[[NSApplication sharedApplication] dockTile]
- image:appIcon];
-
// Lets report the HandBrake version number here to the activity log and text log file
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
NSString *versionStringFull = [NSString stringWithFormat:@"Handbrake Version: %@ (%@)", infoDict[@"CFBundleShortVersionString"], infoDict[@"CFBundleVersion"]];
@@ -106,23 +85,11 @@
_core = [[HBCore alloc] initWithLoggingLevel:loggingLevel];
_core.name = @"ScanCore";
- // Init a separate instance of libhb for user scanning and setting up jobs
- _queueCore = [[HBCore alloc] initWithLoggingLevel:loggingLevel];
- _queueCore.name = @"QueueCore";
-
- // Registers the observers to the cores notifications.
- [self registerScanCoreNotifications];
- [self registerQueueCoreNotifications];
-
- // Set the Growl Delegate
- [GrowlApplicationBridge setGrowlDelegate: self];
-
[fPictureController setDelegate:self];
-
[fPreviewController setCore:self.core];
- [fQueueController setCore:self.queueCore];
- [fQueueController setHBController:self];
+ // Set the Growl Delegate
+ [GrowlApplicationBridge setGrowlDelegate:fQueueController];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(autoSetM4vExtension:) name:HBMixdownChangedNotification object:nil];
}
@@ -132,6 +99,8 @@
- (void) applicationDidFinishLaunching: (NSNotification *) notification
{
+ [self enableUI:NO];
+
// Checks for presets updates
[self checkBuiltInsForUpdates];
@@ -142,40 +111,33 @@
[fPresetDrawer open:self];
}
- /* Init QueueFile .plist */
- [self loadQueueFile];
- [self initQueueFSEvent];
- /* Run hbInstances to get any info on other instances as well as set the
- * pid number for this instance in the case of multi-instance encoding. */
- hbInstanceNum = [self hbInstances];
-
- /* If we are a single instance it is safe to clean up the previews if there are any
- * left over. This is a bit of a kludge but will prevent a build up of old instance
- * live preview cruft. No danger of removing an active preview directory since they
- * are created later in HBPreviewController if they don't exist at the moment a live
- * preview encode is initiated. */
+ // Get the number of HandBrake instances currently running
+ NSUInteger hbInstanceNum = [NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]].count;
+
+ // If we are a single instance it is safe to clean up the previews if there are any
+ // left over. This is a bit of a kludge but will prevent a build up of old instance
+ // live preview cruft. No danger of removing an active preview directory since they
+ // are created later in HBPreviewController if they don't exist at the moment a live
+ // preview encode is initiated.
if (hbInstanceNum == 1)
{
- NSString *PreviewDirectory = [[HBUtilities appSupportPath] stringByAppendingPathComponent:@"Previews"];
- NSError *error;
- NSArray *files = [ [NSFileManager defaultManager] contentsOfDirectoryAtPath: PreviewDirectory error: &error ];
- for( NSString *file in files )
+ NSString *previewDirectory = [[HBUtilities appSupportPath] stringByAppendingPathComponent:@"Previews"];
+ NSError *error = nil;
+ NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:previewDirectory error:&error];
+ for (NSString *file in files)
{
- if( ![file isEqual: @"."] && ![file isEqual: @".."] )
+ if (![file isEqual:@"."] && ![file isEqual:@".."])
{
- [ [NSFileManager defaultManager] removeItemAtPath: [ PreviewDirectory stringByAppendingPathComponent: file ] error: &error ];
- if( error )
+ BOOL result = [[NSFileManager defaultManager] removeItemAtPath:[previewDirectory stringByAppendingPathComponent:file] error:&error];
+ if (result == NO && error)
{
//an error occurred
- [HBUtilities writeToActivityLog: "Could not remove existing preview at : %s",[file UTF8String] ];
+ [HBUtilities writeToActivityLog: "Could not remove existing preview at : %s", file.UTF8String];
}
}
}
-
}
- [self enableUI: NO];
-
// Open debug output window now if it was visible when HB was closed
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"])
[self showDebugOutputPanel:nil];
@@ -186,70 +148,53 @@
[self openMainWindow:nil];
- /* Now we re-check the queue array to see if there are
- * any remaining encodes to be done in it and ask the
- * user if they want to reload the queue */
- if ([QueueFileArray count] > 0)
+ // Now we re-check the queue array to see if there are
+ // any remaining encodes to be done in it and ask the
+ // user if they want to reload the queue */
+ if (fQueueController.count)
{
- /* run getQueueStats to see whats in the queue file */
- [self getQueueStats];
- /* this results in these values
- * fPendingCount = 0;
- * fWorkingCount = 0;
- */
-
- /* On Screen Notification
- * We check to see if there is already another instance of hb running.
- * Note: hbInstances == 1 means we are the only instance of HandBrake.app
- */
+ // On Screen Notification
+ // We check to see if there is already another instance of hb running.
+ // Note: hbInstances == 1 means we are the only instance of HandBrake.app
+ NSAlert *alert = nil;
if (hbInstanceNum > 1)
{
- NSAlert *alert = [[NSAlert alloc] init];
+ alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"There is already an instance of HandBrake running.", @"")];
[alert setInformativeText:NSLocalizedString(@"HandBrake will now load up the existing queue.", nil)];
[alert addButtonWithTitle:NSLocalizedString(@"Reload Queue", nil)];
- [alert beginSheetModalForWindow:fWindow
- modalDelegate:self
- didEndSelector:@selector(didDimissReloadQueue:returnCode:contextInfo:)
- contextInfo:nil];
- [alert release];
}
else
{
- if (fWorkingCount > 0 || fPendingCount > 0)
+ if (fQueueController.workingItemsCount > 0 || fQueueController.pendingItemsCount > 0)
{
NSString *alertTitle;
- if (fWorkingCount > 0)
+ if (fQueueController.workingItemsCount > 0)
{
alertTitle = [NSString stringWithFormat:
NSLocalizedString(@"HandBrake Has Detected %d Previously Encoding Item(s) and %d Pending Item(s) In Your Queue.", @""),
- fWorkingCount,fPendingCount];
+ fQueueController.workingItemsCount, fQueueController.pendingItemsCount];
}
else
{
alertTitle = [NSString stringWithFormat:
NSLocalizedString(@"HandBrake Has Detected %d Pending Item(s) In Your Queue.", @""),
- fPendingCount];
+ fQueueController.pendingItemsCount];
}
- NSAlert *alert = [[NSAlert alloc] init];
+ alert = [[NSAlert alloc] init];
[alert setMessageText:alertTitle];
[alert setInformativeText:NSLocalizedString(@"Do you want to reload them ?", nil)];
[alert addButtonWithTitle:NSLocalizedString(@"Reload Queue", nil)];
[alert addButtonWithTitle:NSLocalizedString(@"Empty Queue", nil)];
[alert setAlertStyle:NSCriticalAlertStyle];
- [alert beginSheetModalForWindow:fWindow
- modalDelegate:self
- didEndSelector:@selector(didDimissReloadQueue:returnCode:contextInfo:)
- contextInfo:nil];
- [alert release];
}
else
{
- /* Since we addressed any pending or previously encoding items above, we go ahead and make sure
- * the queue is empty of any finished items or cancelled items */
- [self clearQueueAllItems];
+ // Since we addressed any pending or previously encoding items above, we go ahead and make sure
+ // the queue is empty of any finished items or cancelled items.
+ [fQueueController removeAllJobs];
if (self.core.state != HBStateScanning && !self.job)
{
@@ -265,7 +210,40 @@
}
}
}
-
+ }
+
+ if (alert)
+ {
+ NSModalResponse response = [alert runModal];
+
+ if (response == NSAlertSecondButtonReturn)
+ {
+ [HBUtilities writeToActivityLog: "didDimissReloadQueue NSAlertSecondButtonReturn Chosen"];
+ [fQueueController removeAllJobs];
+
+ // We show whichever open source window specified in LaunchSourceBehavior preference key
+ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
+ {
+ [self browseSources:nil];
+ }
+
+ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
+ {
+ [self browseSources:(id)fOpenSourceTitleMMenu];
+ }
+ }
+ else
+ {
+ [HBUtilities writeToActivityLog: "didDimissReloadQueue NSAlertFirstButtonReturn Chosen"];
+ if (hbInstanceNum == 1)
+ {
+ [fQueueController setEncodingJobsAsPending];
+ }
+
+ [self showQueueWindow:nil];
+ }
+
+ [alert release];
}
}
else
@@ -284,7 +262,6 @@
}
}
}
- currentQueueEncodeNameString = @"";
}
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
@@ -333,49 +310,6 @@
}
#pragma mark -
-#pragma mark Multiple Instances
-
-/* hbInstances checks to see if other instances of HB are running and also sets the pid for this instance for multi-instance queue encoding */
-
- /* Note for now since we are in early phases of multi-instance I have put in quite a bit of logging. Can be removed as we see fit. */
-- (int) hbInstances
-{
- /* check to see if another instance of HandBrake.app is running */
- NSArray *runningInstances = [NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]];
- NSRunningApplication *runningInstance;
-
- NSRunningApplication *thisInstance = [NSRunningApplication currentApplication];
- NSString *thisInstanceAppPath = [[NSBundle mainBundle] bundlePath];
- [HBUtilities writeToActivityLog: "hbInstances path to this instance: %s", [thisInstanceAppPath UTF8String]];
-
- int hbInstances = 0;
- NSString *runningInstanceAppPath;
- pid_t runningInstancePidNum;
-
- for (runningInstance in runningInstances)
- {
- /*Report the path to each active instances app path */
- runningInstancePidNum = [runningInstance processIdentifier];
- runningInstanceAppPath = [[runningInstance bundleURL] path];
- [HBUtilities writeToActivityLog: "hbInstance found instance pidnum: %d at path: %s", runningInstancePidNum, [runningInstanceAppPath UTF8String]];
- /* see if this is us*/
- if ([runningInstance isEqual: thisInstance])
- {
- /* If so this is our pidnum */
- [HBUtilities writeToActivityLog: "hbInstance MATCH FOUND, our pidnum is: %d", runningInstancePidNum];
- /* Get the PID number for this hb instance, used in multi instance encoding */
- pidNum = runningInstancePidNum;
- /* Report this pid to the activity log */
- [HBUtilities writeToActivityLog: "Pid for this instance: %d", pidNum];
- /* Tell fQueueController what our pidNum is */
- [fQueueController setPidNum:pidNum];
- }
- hbInstances++;
- }
- return hbInstances;
-}
-
-#pragma mark -
#pragma mark Drag & drop handling
// This method is used by OSX to know what kind of files can be drag & drop on the NSWindow
@@ -409,42 +343,9 @@
#pragma mark -
-- (void) didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
-{
-
- [HBUtilities writeToActivityLog: "didDimissReloadQueue number of hb instances:%d", hbInstanceNum];
- if (returnCode == NSAlertSecondButtonReturn)
- {
- [HBUtilities writeToActivityLog: "didDimissReloadQueue NSAlertSecondButtonReturn Chosen"];
- [self clearQueueAllItems];
-
- /* We show whichever open source window specified in LaunchSourceBehavior preference key */
- if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
- {
- [self browseSources:nil];
- }
-
- if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
- {
- [self browseSources:(id)fOpenSourceTitleMMenu];
- }
- }
- else
- {
- [HBUtilities writeToActivityLog: "didDimissReloadQueue NSAlertFirstButtonReturn Chosen"];
- if (hbInstanceNum == 1)
- {
-
- [self setQueueEncodingItemsAsPending];
- }
- [self reloadQueue];
- [self showQueueWindow:NULL];
- }
-}
-
- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app
{
- if (self.queueCore.state != HBStateIdle)
+ if (fQueueController.core.state != HBStateIdle)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil)];
@@ -467,7 +368,7 @@
}
// Warn if items still in the queue
- else if (fPendingCount > 0)
+ else if (fQueueController.pendingItemsCount > 0)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil)];
@@ -495,23 +396,20 @@
[presetManager savePresets];
[presetManager release];
- [self closeQueueFSEvent];
- [currentQueueEncodeNameString release];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
[outputPanel release];
[fQueueController release];
+ [fQueueController release];
[fPreviewController release];
[fPictureController release];
- [dockTile release];
-
- [[NSNotificationCenter defaultCenter] removeObserver:self];
self.core = nil;
- self.queueCore = nil;
self.browsedSourceDisplayName = nil;
+
[HBCore closeGlobal];
}
-
- (void) awakeFromNib
{
/* For 64 bit builds, the threaded animation in the progress
@@ -525,6 +423,8 @@
* should require their own thread.
*/
+ _window = fWindow;
+
[fScanIndicator setUsesThreadedAnimation:NO];
[fRipIndicator setUsesThreadedAnimation:NO];
@@ -655,294 +555,6 @@
fPresetsView.enabled = b;
}
-/**
- * Registers the observers to the scan core notifications
- */
-- (void)registerScanCoreNotifications
-{
- NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreScanningNotification object:self.core queue:mainQueue usingBlock:^(NSNotification *note) {
- hb_state_t s = *(self.core.hb_state);
- #define p s.param.scanning
- if (p.preview_cur)
- {
- fSrcDVD2Field.stringValue = [NSString stringWithFormat:
- NSLocalizedString( @"Scanning title %d of %d, preview %d…", @"" ),
- p.title_cur, p.title_count,
- p.preview_cur];
- }
- else
- {
- fSrcDVD2Field.stringValue = [NSString stringWithFormat:
- NSLocalizedString( @"Scanning title %d of %d…", @"" ),
- p.title_cur, p.title_count];
- }
- fScanIndicator.hidden = NO;
- fScanHorizontalLine.hidden = YES;
- fScanIndicator.doubleValue = 100.0 * p.progress;
- #undef p
- }];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreScanDoneNotification object:self.core queue:mainQueue usingBlock:^(NSNotification *note) {
- fScanHorizontalLine.hidden = NO;
- fScanIndicator.hidden = YES;
- fScanIndicator.indeterminate = NO;
- fScanIndicator.doubleValue = 0.0;
-
- [HBUtilities writeToActivityLog:"ScanDone state received from fHandle"];
- [self showNewScan];
- [[fWindow toolbar] validateVisibleItems];
- }];
-}
-
-- (void)registerQueueCoreNotifications
-{
- NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreScanningNotification object:self.queueCore queue:mainQueue usingBlock:^(NSNotification *note) {
- hb_state_t s = *(self.queueCore.hb_state);
- #define p s.param.scanning
- NSString *scan_status;
- if (p.preview_cur)
- {
- scan_status = [NSString stringWithFormat:
- NSLocalizedString( @"Queue Scanning title %d of %d, preview %d…", @"" ),
- p.title_cur, p.title_count, p.preview_cur];
- }
- else
- {
- scan_status = [NSString stringWithFormat:
- NSLocalizedString( @"Queue Scanning title %d of %d…", @"" ),
- p.title_cur, p.title_count];
- }
- fStatusField.stringValue = scan_status;
-
- // Set the status string in fQueueController as well
- [fQueueController setQueueStatusString: scan_status];
- #undef p
- }];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreScanDoneNotification object:self.queueCore queue:mainQueue usingBlock:^(NSNotification *note) {
- [HBUtilities writeToActivityLog:"ScanDone state received from fQueueEncodeLibhb"];
- [self processNewQueueEncode];
- [[fWindow toolbar] validateVisibleItems];
- }];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreSearchingNotification object:self.queueCore queue:mainQueue usingBlock:^(NSNotification *note) {
- hb_state_t s = *(self.queueCore.hb_state);
- #define p s.param.working
- NSMutableString *string = [NSMutableString stringWithFormat:
- NSLocalizedString(@"Searching for start point… : %.2f %%", @""),
- 100.0 * p.progress];
-
- if (p.seconds > -1)
- {
- [string appendFormat:NSLocalizedString(@" (ETA %02dh%02dm%02ds)", @"" ), p.hours, p.minutes, p.seconds];
- }
-
- fStatusField.stringValue = string;
- // Set the status string in fQueueController as well
- [fQueueController setQueueStatusString: string];
-
- // Update slider
- CGFloat progress_total = (p.progress + p.job_cur - 1) / p.job_count;
- fRipIndicator.indeterminate = NO;
- fRipIndicator.doubleValue = 100.0 * progress_total;
-
- // If progress bar hasn't been revealed at the bottom of the window, do
- // that now. This code used to be in doRip. I moved it to here to handle
- // the case where hb_start is called by HBQueueController and not from
- // HBController.
- if (!fRipIndicatorShown)
- {
- NSRect frame = [fWindow frame];
- if (frame.size.width <= 591)
- frame.size.width = 591;
- frame.size.height += 36;
- frame.origin.y -= 36;
- [fWindow setFrame:frame display:YES animate:YES];
- fRipIndicatorShown = YES;
- }
- #undef p
- }];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreWorkingNotification object:self.queueCore queue:mainQueue usingBlock:^(NSNotification *note) {
- hb_state_t s = *(self.queueCore.hb_state);
- #define p s.param.working
- // Update text field
- NSString *pass_desc;
- if (p.job_cur == 1 && p.job_count > 1)
- {
- HBJob *queueJob = QueueFileArray[currentQueueEncodeIndex];
- if (queueJob.subtitles.tracks.count &&
- [queueJob.subtitles.tracks.firstObject[keySubTrackIndex] intValue] == -1)
- {
- pass_desc = NSLocalizedString(@"(subtitle scan)", @"");
- }
- else
- {
- pass_desc = @"";
- }
- }
- else
- {
- pass_desc = @"";
- }
-
- NSMutableString *string;
- if (pass_desc.length)
- {
- string = [NSMutableString stringWithFormat:
- NSLocalizedString(@"Encoding: %@ \nPass %d %@ of %d, %.2f %%", @""),
- currentQueueEncodeNameString,
- p.job_cur, pass_desc, p.job_count, 100.0 * p.progress];
- }
- else
- {
- string = [NSMutableString stringWithFormat:
- NSLocalizedString(@"Encoding: %@ \nPass %d of %d, %.2f %%", @""),
- currentQueueEncodeNameString,
- p.job_cur, p.job_count, 100.0 * p.progress];
- }
-
- if (p.seconds > -1)
- {
- if (p.rate_cur > 0.0)
- {
- [string appendFormat:
- NSLocalizedString(@" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @""),
- p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
- }
- else
- {
- [string appendFormat:
- NSLocalizedString(@" (ETA %02dh%02dm%02ds)", @""),
- p.hours, p.minutes, p.seconds];
- }
- }
-
- fStatusField.stringValue = string;
- [fQueueController setQueueStatusString:string];
-
- // Update slider
- CGFloat progress_total = (p.progress + p.job_cur - 1) / p.job_count;
- fRipIndicator.indeterminate = NO;
- fRipIndicator.doubleValue = 100.0 * progress_total;
-
- // If progress bar hasn't been revealed at the bottom of the window, do
- // that now. This code used to be in doRip. I moved it to here to handle
- // the case where hb_start is called by HBQueueController and not from
- // HBController.
- if (!fRipIndicatorShown)
- {
- NSRect frame = [fWindow frame];
- if (frame.size.width <= 591)
- frame.size.width = 591;
- frame.size.height += 36;
- frame.origin.y -= 36;
- [fWindow setFrame:frame display:YES animate:YES];
- fRipIndicatorShown = YES;
- }
-
- /* Update dock icon */
- if (dockIconProgress < 100.0 * progress_total)
- {
- // ETA format is [XX]X:XX:XX when ETA is greater than one hour
- // [X]X:XX when ETA is greater than 0 (minutes or seconds)
- // When these conditions doesn't applied (eg. when ETA is undefined)
- // we show just a tilde (~)
-
- NSString *etaStr = @"";
- if (p.hours > 0)
- etaStr = [NSString stringWithFormat:@"%d:%02d:%02d", p.hours, p.minutes, p.seconds];
- else if (p.minutes > 0 || p.seconds > 0)
- etaStr = [NSString stringWithFormat:@"%d:%02d", p.minutes, p.seconds];
- else
- etaStr = @"~";
-
- [dockTile updateDockIcon:progress_total withETA:etaStr];
-
- dockIconProgress += dockTileUpdateFrequency;
- }
- #undef p
- }];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreMuxingNotification object:self.queueCore queue:mainQueue usingBlock:^(NSNotification *note) {
- // Update text field
- fStatusField.stringValue = NSLocalizedString(@"Muxing…", @"");
- // Set the status string in fQueueController as well
- [fQueueController setQueueStatusString:NSLocalizedString(@"Muxing…", @"")];
- // Update slider
- fRipIndicator.indeterminate = YES;
- [fRipIndicator startAnimation: nil];
-
- // Update dock icon
- [dockTile updateDockIcon:1.0 withETA:@""];
- }];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCorePausedNotification object:self.queueCore queue:mainQueue usingBlock:^(NSNotification *note) {
- NSString *paused = NSLocalizedString(@"Paused", @"");
- fStatusField.stringValue = paused;
- [fQueueController setQueueStatusString:paused];
- }];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreWorkDoneNotification object:self.queueCore queue:mainQueue usingBlock:^(NSNotification *note) {
- fStatusField.stringValue = NSLocalizedString(@"Encode Finished.", @"");
- // Set the status string in fQueueController as well
- [fQueueController setQueueStatusString:NSLocalizedString(@"Encode Finished.", @"")];
- fRipIndicator.indeterminate = NO;
- fRipIndicator.doubleValue = 0.0;
- [fRipIndicator setIndeterminate: NO];
-
- [[fWindow toolbar] validateVisibleItems];
-
- // Restore dock icon
- [dockTile updateDockIcon:-1.0 withETA:@""];
- dockIconProgress = 0;
-
- if (fRipIndicatorShown)
- {
- NSRect frame = [fWindow frame];
- if( frame.size.width <= 591 )
- frame.size.width = 591;
- frame.size.height += -36;
- frame.origin.y -= -36;
- [fWindow setFrame:frame display:YES animate:YES];
- fRipIndicatorShown = NO;
- }
- // Since we are done with this encode, tell output to stop writing to the
- // individual encode log.
- [outputPanel endEncodeLog];
-
- // Check to see if the encode state has not been cancelled
- // to determine if we should check for encode done notifications.
- if (fEncodeState != 2)
- {
- // Get the output file name for the finished encode
- HBJob *finishedJob = QueueFileArray[currentQueueEncodeIndex];
-
- // Both the Growl Alert and Sending to tagger can be done as encodes roll off the queue
- if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] ||
- [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
- {
- // If Play System Alert has been selected in Preferences
- if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES)
- {
- NSBeep();
- }
- [self showGrowlDoneNotification:finishedJob.destURL];
- }
-
- // Send to tagger
- [self sendToExternalApp:finishedJob.destURL];
-
- // since we have successfully completed an encode, we increment the queue counter
- [self incrementQueueItemDone:currentQueueEncodeIndex];
- }
- }];
-}
-
#pragma mark -
#pragma mark Toolbar
@@ -961,7 +573,7 @@
return YES;
}
- if (action == @selector(Rip:) || action == @selector(addToQueue:))
+ if (action == @selector(rip:) || action == @selector(addToQueue:))
return NO;
}
else
@@ -976,11 +588,11 @@
}
}
- HBState queueState = self.queueCore.state;
+ HBState queueState = fQueueController.core.state;
if (queueState == HBStateScanning || queueState == HBStateWorking || queueState == HBStateSearching || queueState == HBStateMuxing)
{
- if (action == @selector(Rip:))
+ if (action == @selector(rip:))
{
[toolbarItem setImage: [NSImage imageNamed: @"stopencode"]];
[toolbarItem setLabel: @"Stop"];
@@ -988,7 +600,7 @@
[toolbarItem setToolTip: @"Stop Encoding"];
return YES;
}
- if (action == @selector(Pause:))
+ if (action == @selector(pause:))
{
[toolbarItem setImage: [NSImage imageNamed: @"pauseencode"]];
[toolbarItem setLabel: @"Pause"];
@@ -999,7 +611,7 @@
}
else if (queueState == HBStatePaused)
{
- if (action == @selector(Pause:))
+ if (action == @selector(pause:))
{
[toolbarItem setImage: [NSImage imageNamed: @"encode"]];
[toolbarItem setLabel: @"Resume"];
@@ -1007,15 +619,15 @@
[toolbarItem setToolTip: @"Resume Encoding"];
return YES;
}
- if (action == @selector(Rip:))
+ if (action == @selector(rip:))
return YES;
}
else
{
- if (action == @selector(Rip:))
+ if (action == @selector(rip:))
{
[toolbarItem setImage: [NSImage imageNamed: @"encode"]];
- if (fPendingCount > 0)
+ if (fQueueController.pendingItemsCount > 0)
[toolbarItem setLabel: @"Start Queue"];
else
[toolbarItem setLabel: @"Start"];
@@ -1044,7 +656,7 @@
}
// If there are any pending queue items, make sure the start/stop button is active.
- if (action == @selector(Rip:) && (fPendingCount > 0 || self.job))
+ if (action == @selector(rip:) && (fQueueController.pendingItemsCount > 0 || self.job))
return YES;
if (action == @selector(showQueueWindow:))
return YES;
@@ -1061,7 +673,7 @@
- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
{
SEL action = [menuItem action];
- HBState queueState = self.queueCore.state;
+ HBState queueState = fQueueController.core.state;
if (action == @selector(addToQueue:) || action == @selector(addAllTitlesToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
return self.job && [fWindow attachedSheet] == nil;
@@ -1069,7 +681,7 @@
if (action == @selector(selectDefaultPreset:))
return [fWindow attachedSheet] == nil;
- if (action == @selector(Pause:))
+ if (action == @selector(pause:))
{
if (queueState == HBStateWorking)
{
@@ -1086,7 +698,7 @@
else
return NO;
}
- if (action == @selector(Rip:))
+ if (action == @selector(rip:))
{
if (queueState == HBStateWorking || queueState == HBStateMuxing || queueState == HBStatePaused)
{
@@ -1126,94 +738,6 @@
}
#pragma mark -
-#pragma mark Encode Done Actions
-
-#define SERVICE_NAME @"Encode Done"
-
-/**
- * Register a test notification and make
- * it enabled by default
- */
-- (NSDictionary *)registrationDictionaryForGrowl
-{
- return @{GROWL_NOTIFICATIONS_ALL: @[SERVICE_NAME],
- GROWL_NOTIFICATIONS_DEFAULT: @[SERVICE_NAME]};
-}
-
-- (void)showGrowlDoneNotification:(NSURL *)fileURL
-{
- // This end of encode action is called as each encode rolls off of the queue
- // Setup the Growl stuff
- NSString *growlMssg = [NSString stringWithFormat:@"your HandBrake encode %@ is done!", fileURL.lastPathComponent];
- [GrowlApplicationBridge notifyWithTitle:@"Put down that cocktail…"
- description:growlMssg
- notificationName:SERVICE_NAME
- iconData:nil
- priority:0
- isSticky:1
- clickContext:nil];
-}
-
-- (void)sendToExternalApp:(NSURL *)fileURL
-{
- // This end of encode action is called as each encode rolls off of the queue
- if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)
- {
- NSString *sendToApp = [[NSUserDefaults standardUserDefaults] objectForKey:@"SendCompletedEncodeToApp"];
- if (![sendToApp isEqualToString:@"None"])
- {
- [HBUtilities writeToActivityLog: "trying to send encode to: %s", [sendToApp UTF8String]];
- NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@%@%@", @"tell application \"",sendToApp,@"\" to open (POSIX file \"", fileURL.path, @"\")"]];
- [myScript executeAndReturnError: nil];
- [myScript release];
- }
- }
-}
-
-- (void) queueCompletedAlerts
-{
- /* If Play System Alert has been selected in Preferences */
- if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES )
- {
- NSBeep();
- }
-
- /* If Alert Window or Window and Growl has been selected */
- if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] ||
- [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"] )
- {
- /*On Screen Notification*/
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:@"Put down that cocktail…"];
- [alert setInformativeText:@"Your HandBrake queue is done!"];
- [NSApp requestUserAttention:NSCriticalRequest];
- [alert runModal];
- [alert release];
- }
-
- /* If sleep has been selected */
- if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"] )
- {
- /* Sleep */
- NSDictionary *errorDict;
- NSAppleScript *scriptObject = [[NSAppleScript alloc] initWithSource:
- @"tell application \"Finder\" to sleep"];
- [scriptObject executeAndReturnError: &errorDict];
- [scriptObject release];
- }
- /* If Shutdown has been selected */
- if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"] )
- {
- /* Shut Down */
- NSDictionary* errorDict;
- NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
- @"tell application \"Finder\" to shut down"];
- [scriptObject executeAndReturnError: &errorDict];
- [scriptObject release];
- }
-}
-
-#pragma mark -
#pragma mark Get New Source
/**
@@ -1450,75 +974,102 @@
int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
int min_title_duration_seconds = [[[NSUserDefaults standardUserDefaults] objectForKey:@"MinTitleScanSeconds"] intValue];
- [self.core scan:scanURL
- titleNum:scanTitleNum
- previewsNum:hb_num_previews minTitleDuration:min_title_duration_seconds];
+ [self.core scanURL:scanURL
+ titleIndex:scanTitleNum
+ previews:hb_num_previews minDuration:min_title_duration_seconds
+ progressHandler:^(HBState state, hb_state_t hb_state)
+ {
+ #define p hb_state.param.scanning
+ if (p.preview_cur)
+ {
+ fSrcDVD2Field.stringValue = [NSString stringWithFormat:
+ NSLocalizedString( @"Scanning title %d of %d, preview %d…", @"" ),
+ p.title_cur, p.title_count,
+ p.preview_cur];
+ }
+ else
+ {
+ fSrcDVD2Field.stringValue = [NSString stringWithFormat:
+ NSLocalizedString( @"Scanning title %d of %d…", @"" ),
+ p.title_cur, p.title_count];
+ }
+ fScanIndicator.hidden = NO;
+ fScanHorizontalLine.hidden = YES;
+ fScanIndicator.doubleValue = 100.0 * p.progress;
+ #undef p
+ }
+ completationHandler:^(BOOL success)
+ {
+ fScanHorizontalLine.hidden = NO;
+ fScanIndicator.hidden = YES;
+ fScanIndicator.indeterminate = NO;
+ fScanIndicator.doubleValue = 0.0;
+
+ if (success)
+ {
+ [self showNewScan];
+ }
+ else
+ {
+ // We display a message if a valid source was not chosen
+ fSrcDVD2Field.stringValue = NSLocalizedString(@"No Valid Source Found", @"");
+ }
+ [fWindow.toolbar validateVisibleItems];
+ }];
}
}
- (void)showNewScan
{
- if (!self.core.titles.count)
+ if (self.jobFromQueue)
{
- // We display a message if a valid source was not chosen
- fSrcDVD2Field.stringValue = @"No Valid Source Found";
+ // we are a rescan of an existing queue item and need to apply the queued settings to the scan
+ [HBUtilities writeToActivityLog: "showNewScan: This is a queued item rescan"];
}
else
{
- if (self.jobFromQueue)
- {
- // we are a rescan of an existing queue item and need to apply the queued settings to the scan
- [HBUtilities writeToActivityLog: "showNewScan: This is a queued item rescan"];
- }
- else
- {
- [HBUtilities writeToActivityLog: "showNewScan: This is a new source item scan"];
- }
-
- [fSrcTitlePopUp removeAllItems];
+ [HBUtilities writeToActivityLog: "showNewScan: This is a new source item scan"];
+ }
- for (HBTitle *title in self.core.titles)
- {
- // Set Source Name at top of window with the browsedSourceDisplayName grokked right before -performScan
- if (!self.browsedSourceDisplayName)
- {
- self.browsedSourceDisplayName = @"NoNameDetected";
- }
- fSrcDVD2Field.stringValue = self.browsedSourceDisplayName;
+ [fSrcTitlePopUp removeAllItems];
- [fSrcTitlePopUp addItemWithTitle:title.description];
+ for (HBTitle *title in self.core.titles)
+ {
+ // Set Source Name at top of window with the browsedSourceDisplayName grokked right before -performScan
+ fSrcDVD2Field.stringValue = self.browsedSourceDisplayName;
- // See if this is the main feature according
- if (title.isFeatured)
- {
- [fSrcTitlePopUp selectItemWithTitle:title.description];
- }
- }
+ [fSrcTitlePopUp addItemWithTitle:title.description];
- // Select the first item is nothing is selected
- if (!fSrcTitlePopUp.selectedItem)
+ // See if this is the main feature according
+ if (title.isFeatured)
{
- [fSrcTitlePopUp selectItemAtIndex:0];
+ [fSrcTitlePopUp selectItemWithTitle:title.description];
}
+ }
- // Updates the main window ui
- [self enableUI:YES];
- [self titlePopUpChanged:nil];
+ // Select the first item is nothing is selected
+ if (!fSrcTitlePopUp.selectedItem)
+ {
+ [fSrcTitlePopUp selectItemAtIndex:0];
+ }
- // Open preview window now if it was visible when HB was closed
- if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PreviewWindowIsOpen"])
- [self showPreviewWindow:nil];
+ // Updates the main window ui
+ [self enableUI:YES];
+ [self titlePopUpChanged:nil];
- // Open picture sizing window now if it was visible when HB was closed
- if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PictureSizeWindowIsOpen"])
- [self showPicturePanel:nil];
+ // Open preview window now if it was visible when HB was closed
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PreviewWindowIsOpen"])
+ [self showPreviewWindow:nil];
- if (self.jobFromQueue)
- {
- [fPresetsView deselect];
+ // Open picture sizing window now if it was visible when HB was closed
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PictureSizeWindowIsOpen"])
+ [self showPicturePanel:nil];
- self.jobFromQueue = nil;
- }
+ if (self.jobFromQueue)
+ {
+ [fPresetsView deselect];
+
+ self.jobFromQueue = nil;
}
}
@@ -1565,438 +1116,83 @@
return contentSize;
}
-#pragma mark -
-#pragma mark Queue File
-
-static void queueFSEventStreamCallback(
- ConstFSEventStreamRef streamRef,
- void *clientCallBackInfo,
- size_t numEvents,
- void *eventPaths,
- const FSEventStreamEventFlags eventFlags[],
- const FSEventStreamEventId eventIds[])
-{
- if (numEvents >= 1)
- {
- // Reload the queue only if one of the following events happened.
- FSEventStreamEventFlags flags = eventFlags[0];
- if (flags & (kFSEventStreamEventFlagItemCreated | kFSEventStreamEventFlagItemRemoved | kFSEventStreamEventFlagItemRenamed | kFSEventStreamEventFlagItemModified))
- {
- HBController *hb = (HBController *)clientCallBackInfo;
- [hb reloadQueue];
- }
- }
-}
+#pragma mark - Queue Item Editing
-- (void)initQueueFSEvent
+/**
+ * Rescans the chosen queue item back into the main window
+ */
+- (void)rescanJobToMainWindow:(HBJob *)queueItem
{
- /* Define variables and create a CFArray object containing
- CFString objects containing paths to watch.
- */
- CFStringRef mypath = (CFStringRef) [[HBUtilities appSupportPath] stringByAppendingPathComponent:@"Queue"];
- CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, 1, NULL);
-
- FSEventStreamContext callbackCtx;
- callbackCtx.version = 0;
- callbackCtx.info = self;
- callbackCtx.retain = NULL;
- callbackCtx.release = NULL;
- callbackCtx.copyDescription = NULL;
-
- CFAbsoluteTime latency = 0.5; /* Latency in seconds */
-
- /* Create the stream, passing in a callback */
- QueueStream = FSEventStreamCreate(NULL,
- &queueFSEventStreamCallback,
- &callbackCtx,
- pathsToWatch,
- kFSEventStreamEventIdSinceNow,
- latency,
- kFSEventStreamCreateFlagIgnoreSelf | kFSEventStreamCreateFlagMarkSelf
- );
-
- CFRelease(pathsToWatch);
+ // Set the browsedSourceDisplayName for showNewScan
+ self.jobFromQueue = queueItem;
+ self.browsedSourceDisplayName = self.jobFromQueue.fileURL.lastPathComponent;
- /* Create the stream before calling this. */
- FSEventStreamScheduleWithRunLoop(QueueStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
- FSEventStreamStart(QueueStream);
+ [self performScan:self.jobFromQueue.fileURL scanTitleNum:self.jobFromQueue.titleIdx];
}
-- (void)closeQueueFSEvent
-{
- FSEventStreamStop(QueueStream);
- FSEventStreamInvalidate(QueueStream);
- FSEventStreamRelease(QueueStream);
-}
+#pragma mark - Queue
-- (void)loadQueueFile
+- (void)setQueueState:(NSString *)info
{
- // We declare the default NSFileManager into fileManager
- NSFileManager *fileManager = [NSFileManager defaultManager];
- NSString *appSupportPath = [HBUtilities appSupportPath];
-
- // We define the location of the user presets file
- QueueFile = [[appSupportPath stringByAppendingPathComponent:@"Queue/Queue.hbqueue"] retain];
-
- // We check for the Queue.plist
- if (![fileManager fileExistsAtPath:QueueFile])
- {
- if (![fileManager fileExistsAtPath:[appSupportPath stringByAppendingPathComponent:@"Queue"]])
- {
- [fileManager createDirectoryAtPath:[appSupportPath stringByAppendingPathComponent:@"Queue"] withIntermediateDirectories:YES attributes:nil error:NULL];
- }
- [fileManager createFileAtPath:QueueFile contents:nil attributes:nil];
- }
-
- @try
- {
- QueueFileArray = [[NSKeyedUnarchiver unarchiveObjectWithFile:QueueFile] retain];
- }
- @catch (NSException *exception)
- {
- [HBUtilities writeToActivityLog:"failed to read the queue to disk"];
- }
-
- // lets check to see if there is anything in the queue file .plist
- if (QueueFileArray == nil)
- {
- // if not, then lets initialize an empty array
- QueueFileArray = [[NSMutableArray alloc] init];
- }
- else
- {
- // ONLY clear out encoded items if we are single instance
- if (hbInstanceNum == 1)
- {
- [self clearQueueEncodedItems];
- }
- }
+ fQueueStatus.stringValue = info;
}
-- (void)reloadQueue
-{
- [HBUtilities writeToActivityLog:"Queue reloaded"];
+#define WINDOW_HEIGHT 591
+#define WINDOW_HEIGHT_OFFSET 36
- NSMutableArray *tempQueueArray = nil;;
- @try
- {
- tempQueueArray = [NSKeyedUnarchiver unarchiveObjectWithFile:QueueFile];
- }
- @catch (NSException *exception)
- {
- tempQueueArray = nil;
- [HBUtilities writeToActivityLog:"failed to read the queue to disk"];
- }
-
- [QueueFileArray setArray:tempQueueArray];
- // Send Fresh QueueFileArray to fQueueController to update queue window
- [fQueueController setQueueArray:QueueFileArray];
- [self getQueueStats];
-}
-
-- (void)addQueueFileItem
+- (void)setQueueInfo:(NSString *)info progress:(double)progress hidden:(BOOL)hidden
{
- [QueueFileArray addObject:[[self.job copy] autorelease]];
- [self saveQueueFileItem];
-}
-
-- (void)removeQueueFileItem:(NSUInteger)queueItemToRemove
-{
- [QueueFileArray removeObjectAtIndex:queueItemToRemove];
- [self saveQueueFileItem];
-}
+ fStatusField.stringValue = info;
+ fRipIndicator.doubleValue = progress;
-- (void)saveQueueFileItem
-{
- if (![NSKeyedArchiver archiveRootObject:QueueFileArray toFile:QueueFile])
+ if (hidden)
{
- [HBUtilities writeToActivityLog:"failed to write the queue to disk"];
- }
- [fQueueController setQueueArray: QueueFileArray];
- [self getQueueStats];
-}
-
-/**
- * Updates the queue status label on the main window.
- */
-- (void)getQueueStats
-{
- // lets get the stats on the status of the queue array
- fPendingCount = 0;
- fWorkingCount = 0;
-
- int i = 0;
- for (HBJob *job in QueueFileArray)
- {
- if (job.state == HBJobStateWorking) // being encoded
- {
- fWorkingCount++;
- // check to see if we are the instance doing this encoding
- if (job.pidId == pidNum)
- {
- currentQueueEncodeIndex = i;
- }
- }
- if (job.state == HBJobStateReady) // pending
+ if (fRipIndicatorShown)
{
- fPendingCount++;
- }
- i++;
- }
+ NSRect frame = fWindow.frame;
+ if (frame.size.width <= WINDOW_HEIGHT)
+ frame.size.width = WINDOW_HEIGHT;
+ frame.size.height += -WINDOW_HEIGHT_OFFSET;
+ frame.origin.y -= -WINDOW_HEIGHT_OFFSET;
+ [fWindow setFrame:frame display:YES animate:YES];
+ fRipIndicatorShown = NO;
- // Set the queue status field in the main window
- NSString *string;
- if (fPendingCount == 0)
- {
- string = NSLocalizedString( @"No encode pending", @"");
- }
- else if (fPendingCount == 1)
- {
- string = [NSString stringWithFormat: NSLocalizedString( @"%d encode pending", @"" ), fPendingCount];
+ // Refresh the toolbar buttons
+ [fWindow.toolbar validateVisibleItems];
+ }
}
else
{
- string = [NSString stringWithFormat: NSLocalizedString( @"%d encodes pending", @"" ), fPendingCount];
- }
-
- [fQueueStatus setStringValue:string];
-}
-
-/**
- * Used to get the next pending queue item index and return it if found
- */
-- (NSInteger)getNextPendingQueueIndex
-{
- // initialize nextPendingIndex to -1, this value tells incrementQueueItemDone that there are no pending items in the queue
- NSInteger nextPendingIndex = -1;
- BOOL nextPendingFound = NO;
- for (HBJob *job in QueueFileArray)
- {
- if (job.state == HBJobStateReady && nextPendingFound == NO) // pending
- {
- nextPendingFound = YES;
- nextPendingIndex = [QueueFileArray indexOfObject:job];
- [HBUtilities writeToActivityLog: "getNextPendingQueueIndex next pending encode index is:%d", nextPendingIndex];
- }
- }
- return nextPendingIndex;
-}
-
-/**
- * This method will set any item marked as encoding back to pending
- * currently used right after a queue reload
- */
-- (void) setQueueEncodingItemsAsPending
-{
- for (HBJob *job in QueueFileArray)
- {
- // We want to keep any queue item that is pending or was previously being encoded
- if (job.state == HBJobStateWorking || job.state == HBJobStateReady)
+ // If progress bar hasn't been revealed at the bottom of the window, do
+ // that now.
+ if (!fRipIndicatorShown)
{
- // If the queue item is marked as "working"
- // then change its status back to ready which effectively
- // puts it back into the queue to be encoded
- if (job.state == HBJobStateWorking)
- {
- job.state = HBJobStateReady;
- }
- }
- }
-
- [self saveQueueFileItem];
-}
+ NSRect frame = fWindow.frame;
+ if (frame.size.width <= WINDOW_HEIGHT)
+ frame.size.width = WINDOW_HEIGHT;
+ frame.size.height += WINDOW_HEIGHT_OFFSET;
+ frame.origin.y -= WINDOW_HEIGHT_OFFSET;
+ [fWindow setFrame:frame display:YES animate:YES];
+ fRipIndicatorShown = YES;
-/**
- * This method will clear the queue of any encodes that are not still pending
- * this includes both successfully completed encodes as well as cancelled encodes
- */
-- (void) clearQueueEncodedItems
-{
- NSMutableArray *tempArray = [NSMutableArray array];
- for (HBJob *job in QueueFileArray)
- {
- /* If the queue item is either completed (0) or cancelled (3) from the
- * last session, then we put it in tempArray to be deleted from QueueFileArray.
- * NOTE: this means we retain pending (2) and also an item that is marked as
- * still encoding (1). If the queue has an item that is still marked as encoding
- * from a previous session, we can conlude that HB was either shutdown, or crashed
- * during the encodes so we keep it and tell the user in the "Load Queue Alert"
- */
- if (job.state == HBJobStateCompleted || job.state == HBJobStateCanceled)
- {
- [tempArray addObject:job];
+ // Refresh the toolbar buttons
+ [fWindow.toolbar validateVisibleItems];
}
}
-
- [QueueFileArray removeObjectsInArray:tempArray];
- [self saveQueueFileItem];
-}
-
-/**
- * This method will clear the queue of all encodes. effectively creating an empty queue
- */
-- (void) clearQueueAllItems
-{
- NSMutableArray *tempArray = [NSMutableArray array];
- // we look here to see if the preset is we move on to the next one
- for (HBJob *job in QueueFileArray)
- {
- [tempArray addObject:job];
- }
-
- [QueueFileArray removeObjectsInArray:tempArray];
- [self saveQueueFileItem];
-}
-
-/**
- * this is actually called from the queue controller to modify the queue array and return it back to the queue controller
- */
-- (void)moveObjectsInQueueArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
-{
- NSUInteger index = [indexSet lastIndex];
- NSUInteger aboveInsertIndexCount = 0;
-
- NSUInteger removeIndex;
-
- if (index >= insertIndex)
- {
- removeIndex = index + aboveInsertIndexCount;
- aboveInsertIndexCount++;
- }
- else
- {
- removeIndex = index;
- insertIndex--;
- }
-
- id object = [[QueueFileArray objectAtIndex:removeIndex] retain];
- [QueueFileArray removeObjectAtIndex:removeIndex];
- [QueueFileArray insertObject:object atIndex:insertIndex];
- [object release];
-
- // We save all of the Queue data here
- // and it also gets sent back to the queue controller
- [self saveQueueFileItem];
-
}
-#pragma mark -
-#pragma mark Queue Job Processing
-
-- (void)incrementQueueItemDone:(NSInteger)queueItemDoneIndexNum
-{
- // Mark the encode just finished as done (status 0)
- HBJob *queueJob = QueueFileArray[currentQueueEncodeIndex];
- queueJob.state = HBJobStateCompleted;
-
- // We save all of the Queue data here
- [self saveQueueFileItem];
-
- /* Since we have now marked a queue item as done
- * we can go ahead and increment currentQueueEncodeIndex
- * so that if there is anything left in the queue we can
- * go ahead and move to the next item if we want to */
- NSInteger queueItems = [QueueFileArray count];
- /* Check to see if there are any more pending items in the queue */
- NSInteger newQueueItemIndex = [self getNextPendingQueueIndex];
- /* If we still have more pending items in our queue, lets go to the next one */
- if (newQueueItemIndex >= 0 && newQueueItemIndex < queueItems)
- {
- // Set our currentQueueEncodeIndex now to the newly found Pending encode as we own it
- currentQueueEncodeIndex = newQueueItemIndex;
- // now we mark the queue item as Status = 1 ( being encoded ) so another instance can not come along and try to scan it while we are scanning
- queueJob = QueueFileArray[currentQueueEncodeIndex];
- queueJob.state = HBJobStateWorking;
- [HBUtilities writeToActivityLog: "incrementQueueItemDone new pending items found: %d", currentQueueEncodeIndex];
- [self saveQueueFileItem];
- queueJob = QueueFileArray[currentQueueEncodeIndex];
- // now we can go ahead and scan the new pending queue item
- [self performNewQueueScan:queueJob.fileURL.path scanTitleNum:queueJob.titleIdx];
- }
- else
- {
- [HBUtilities writeToActivityLog: "incrementQueueItemDone there are no more pending encodes"];
- // Since there are no more items to encode, go to queueCompletedAlerts
- // for user specified alerts after queue completed
- [self queueCompletedAlerts];
- }
-}
+#pragma mark - Job Handling
/**
- * Here we actually tell hb_scan to perform the source scan, using the path to source and title number
+ * Actually adds a job to the queue
*/
-- (void) performNewQueueScan:(NSString *) scanPath scanTitleNum: (NSInteger) scanTitleNum
+- (void)doAddToQueue
{
- HBJob *queueJob = QueueFileArray[currentQueueEncodeIndex];
- // Tell HB to output a new activity log file for this encode
- [outputPanel startEncodeLog:queueJob.destURL];
-
- // We now flag the queue item as being owned by this instance of HB using the PID
- queueJob.pidId = pidNum;
- // Get the currentQueueEncodeNameString from the queue item to display in the status field */
- [currentQueueEncodeNameString autorelease];
- currentQueueEncodeNameString = [[queueJob.destURL.path lastPathComponent] retain];
- // We save all of the Queue data here
- [self saveQueueFileItem];
-
- // Only scan 10 previews before an encode - additional previews are
- // only useful for autocrop and static previews, which are already taken care of at this point
- NSURL *fileURL = [NSURL fileURLWithPath:scanPath];
- [self.queueCore scan:fileURL titleNum:scanTitleNum previewsNum:10 minTitleDuration:0];
+ [fQueueController addJob:[[self.job copy] autorelease]];
}
/**
- * This assumes that we have re-scanned and loaded up a new queue item to send to libhb as fQueueEncodeLibhb
- */
-- (void)processNewQueueEncode
-{
- HBJob *queueJob = QueueFileArray[currentQueueEncodeIndex];
-
- if (self.queueCore.titles.count)
- {
- // Reset the title in the job.
- queueJob.title = self.queueCore.titles[0];
-
- // We should be all setup so let 'er rip
- [self.queueCore encodeJob:queueJob];
- fEncodeState = 1;
-
- // Lets mark our new encode as 1 or "Encoding"
- queueJob.state = HBJobStateWorking;
-
- // We are done using the title, remove it from the job
- queueJob.title = nil;
- [self saveQueueFileItem];
- }
- else
- {
- [HBUtilities writeToActivityLog: "processNewQueueEncode WARNING nothing found in the title list"];
- }
-}
-
-#pragma mark -
-#pragma mark Queue Item Editing
-
-/* Rescans the chosen queue item back into the main window */
-- (void)rescanQueueItemToMainWindow:(NSUInteger)selectedQueueItem
-{
- [HBUtilities writeToActivityLog:"rescanQueueItemToMainWindow: Re-scanning queue item at index:%d", selectedQueueItem];
-
- // Set the browsedSourceDisplayName for showNewScan
- self.jobFromQueue = QueueFileArray[selectedQueueItem];
- self.browsedSourceDisplayName = self.jobFromQueue.fileURL.lastPathComponent;
-
- [self performScan:self.jobFromQueue.fileURL scanTitleNum:self.jobFromQueue.titleIdx];
-
- // Now that source is loaded and settings applied, delete the queue item from the queue
- [HBUtilities writeToActivityLog: "applyQueueSettingsToMainWindow: deleting queue item:%d", selectedQueueItem];
- [self removeQueueFileItem:selectedQueueItem];
-}
-
-#pragma mark -
-#pragma mark Job Handling
-
-/* addToQueue: puts up an alert before ultimately calling doAddToQueue
+ * Puts up an alert before ultimately calling doAddToQueue
*/
- (IBAction)addToQueue:(id)sender
{
@@ -2006,56 +1202,38 @@ static void queueFSEventStreamCallback(
if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
{
NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:@"Warning!"];
- [alert setInformativeText:@"This is not a valid destination directory!"];
+ [alert setMessageText:NSLocalizedString(@"Warning!", @"")];
+ [alert setInformativeText:NSLocalizedString(@"This is not a valid destination directory!", @"")];
[alert runModal];
[alert release];
return;
}
-
- BOOL fileExists;
- fileExists = NO;
-
- BOOL fileExistsInQueue;
- fileExistsInQueue = NO;
-
- // We check for and existing file here
- if([[NSFileManager defaultManager] fileExistsAtPath:self.job.destURL.path])
- {
- fileExists = YES;
- }
-
- // We now run through the queue and make sure we are not overwriting an exisiting queue item
- for (HBJob *job in QueueFileArray)
- {
- if ([job.destURL isEqualTo:self.job.destURL])
- {
- fileExistsInQueue = YES;
- }
- }
- if (fileExists == YES)
+ if ([[NSFileManager defaultManager] fileExistsAtPath:self.job.destURL.path])
{
- NSAlert *alert = [NSAlert alertWithMessageText:@"File already exists."
- defaultButton:@"Cancel"
- alternateButton:@"Overwrite"
- otherButton:nil
- informativeTextWithFormat:@"Do you want to overwrite %@?", self.job.destURL.path];
+ // File exist, warn user
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:NSLocalizedString(@"File already exists.", @"")];
+ [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Do you want to overwrite %@?", @""), self.job.destURL.path]];
+ [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Overwrite", @"")];
[alert setAlertStyle:NSCriticalAlertStyle];
- [alert beginSheetModalForWindow:fWindow modalDelegate:self didEndSelector:@selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ) contextInfo:NULL];
-
+ [alert beginSheetModalForWindow:fWindow modalDelegate:self didEndSelector:@selector(overwriteAddToQueueAlertDone:returnCode:contextInfo:) contextInfo:NULL];
+ [alert release];
}
- else if (fileExistsInQueue == YES)
+ else if ([fQueueController jobExistAtURL:self.job.destURL])
{
- NSAlert *alert = [NSAlert alertWithMessageText:@"There is already a queue item for this destination."
- defaultButton:@"Cancel"
- alternateButton:@"Overwrite"
- otherButton:nil
- informativeTextWithFormat:@"Do you want to overwrite %@?", self.job.destURL.path];
+ // File exist in queue, warn user
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:NSLocalizedString(@"There is already a queue item for this destination.", @"")];
+ [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Do you want to overwrite %@?", @""), self.job.destURL.path]];
+ [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Overwrite", @"")];
[alert setAlertStyle:NSCriticalAlertStyle];
- [alert beginSheetModalForWindow:fWindow modalDelegate:self didEndSelector:@selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ) contextInfo:NULL];
+ [alert beginSheetModalForWindow:fWindow modalDelegate:self didEndSelector:@selector(overwriteAddToQueueAlertDone:returnCode:contextInfo:) contextInfo:NULL];
+ [alert release];
}
else
{
@@ -2063,312 +1241,159 @@ static void queueFSEventStreamCallback(
}
}
-/* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
- the user if they want to overwrite an exiting movie file.
-*/
-- (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
- returnCode: (int) returnCode contextInfo: (void *) contextInfo
+/**
+ * Called from the alert posted by addToQueue
+ * that asks the user if they want to overwrite an exiting movie file.
+ */
+- (void)overwriteAddToQueueAlertDone:(NSAlert *)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void *)contextInfo
{
- if( returnCode == NSAlertAlternateReturn )
+ if (returnCode == NSAlertSecondButtonReturn)
+ {
[self doAddToQueue];
+ }
}
-- (void) doAddToQueue
+- (void)doRip
{
- [self addQueueFileItem];
+ // if there are no jobs in the queue, then add this one to the queue and rip
+ // otherwise, just rip the queue
+ if (fQueueController.pendingItemsCount == 0)
+ {
+ [self doAddToQueue];
+ }
+
+ [fQueueController rip:self];
}
-/* Rip: puts up an alert before ultimately calling doRip
-*/
-- (IBAction) Rip: (id) sender
+/**
+ * Puts up an alert before ultimately calling doRip
+ */
+- (IBAction)rip:(id)sender
{
- [HBUtilities writeToActivityLog: "Rip: Pending queue count is %d", fPendingCount];
- /* Rip or Cancel ? */
- if (self.queueCore.state == HBStateWorking || self.queueCore.state == HBStatePaused)
+ // Rip or Cancel ?
+ if (fQueueController.core.state == HBStateWorking || fQueueController.core.state == HBStatePaused)
{
- [self Cancel: sender];
+ [self cancel:sender];
return;
}
-
- // We check to see if we need to warn the user that the computer will go to sleep
- // or shut down when encoding is finished
- [self remindUserOfSleepOrShutdown];
-
+
// If there are pending jobs in the queue, then this is a rip the queue
- if (fPendingCount > 0)
+ if (fQueueController.pendingItemsCount > 0)
{
- currentQueueEncodeIndex = [self getNextPendingQueueIndex];
- // here lets start the queue with the first pending item
- HBJob *queueJob = QueueFileArray[currentQueueEncodeIndex];
- [self performNewQueueScan:queueJob.fileURL.path scanTitleNum:queueJob.titleIdx];
-
+ [fQueueController rip:self];
return;
}
-
+
// Before adding jobs to the queue, check for a valid destination.
NSString *destinationDirectory = self.job.destURL.path.stringByDeletingLastPathComponent;
if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
{
NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:@"Warning!"];
- [alert setInformativeText:@"This is not a valid destination directory!"];
+ [alert setMessageText:NSLocalizedString(@"Warning!", @"")];
+ [alert setInformativeText:NSLocalizedString(@"This is not a valid destination directory!", @"")];
[alert runModal];
[alert release];
return;
}
-
+
// We check for duplicate name here
- if( [[NSFileManager defaultManager] fileExistsAtPath:self.job.destURL.path] )
+ if ([[NSFileManager defaultManager] fileExistsAtPath:self.job.destURL.path])
{
- NSAlert *alert = [NSAlert alertWithMessageText:@"File already exists."
- defaultButton:@"Cancel"
- alternateButton:@"Overwrite"
- otherButton:nil
- informativeTextWithFormat:@"Do you want to overwrite %@?", self.job.destURL.path];
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:NSLocalizedString(@"Warning!", @"")];
+ [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Do you want to overwrite %@?", @""), self.job.destURL.path]];
+ [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Overwrite", @"")];
[alert setAlertStyle:NSCriticalAlertStyle];
- [alert beginSheetModalForWindow:fWindow modalDelegate:self didEndSelector:@selector( overWriteAlertDone:returnCode:contextInfo: ) contextInfo:NULL];
+ [alert beginSheetModalForWindow:fWindow modalDelegate:self didEndSelector:@selector(overWriteAlertDone:returnCode:contextInfo:) contextInfo:NULL];
// overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
- }
- else
- {
- /* if there are no pending jobs in the queue, then add this one to the queue and rip
- otherwise, just rip the queue */
- if(fPendingCount == 0)
- {
- [self doAddToQueue];
- }
-
- // go right to processing the new queue encode
- currentQueueEncodeIndex = [self getNextPendingQueueIndex];
- HBJob *queueJob = QueueFileArray[currentQueueEncodeIndex];
- [self performNewQueueScan:queueJob.fileURL.path scanTitleNum:queueJob.titleIdx];
- }
-}
-
-/* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
- want to overwrite an exiting movie file.
-*/
-- (void) overWriteAlertDone: (NSWindow *) sheet
- returnCode: (int) returnCode contextInfo: (void *) contextInfo
-{
- if( returnCode == NSAlertAlternateReturn )
- {
- // if there are no jobs in the queue, then add this one to the queue and rip
- // otherwise, just rip the queue
- if (fPendingCount == 0)
- {
- [self doAddToQueue];
- }
-
- [[NSUserDefaults standardUserDefaults] setURL:self.job.destURL.URLByDeletingLastPathComponent
- forKey:@"HBLastDestinationDirectory"];
- currentQueueEncodeIndex = [self getNextPendingQueueIndex];
- HBJob *queueJob = QueueFileArray[currentQueueEncodeIndex];
- [self performNewQueueScan:queueJob.fileURL.path scanTitleNum:queueJob.titleIdx];
- }
-}
-
-- (void) remindUserOfSleepOrShutdown
-{
- if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
- {
- /*Warn that computer will sleep after encoding*/
- NSBeep();
- [NSApp requestUserAttention:NSCriticalRequest];
-
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:@"The computer will sleep after encoding is done."];
- [alert setInformativeText:@"You have selected to sleep the computer after encoding. To turn off sleeping, go to the HandBrake preferences."];
- [alert addButtonWithTitle:@"OK"];
- [alert addButtonWithTitle:@"Preferences…"];
-
- NSInteger reminduser = [alert runModal];
[alert release];
- if (reminduser == NSAlertSecondButtonReturn)
- {
- [self showPreferencesWindow:nil];
- }
- }
- else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
- {
- /*Warn that computer will shut down after encoding*/
- NSBeep();
- [NSApp requestUserAttention:NSCriticalRequest];
-
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:@"The computer will shut down after encoding is done."];
- [alert setInformativeText:@"You have selected to shut down the computer after encoding. To turn off shut down, go to the HandBrake preferences."];
- [alert addButtonWithTitle:@"OK"];
- [alert addButtonWithTitle:@"Preferences…"];
-
- NSInteger reminduser = [alert runModal];
- if (reminduser == NSAlertSecondButtonReturn)
- {
- [self showPreferencesWindow:nil];
- }
- [alert release];
- }
-
-}
-
-//------------------------------------------------------------------------------------
-// Displays an alert asking user if the want to cancel encoding of current job.
-// Cancel: returns immediately after posting the alert. Later, when the user
-// acknowledges the alert, doCancelCurrentJob is called.
-//------------------------------------------------------------------------------------
-- (IBAction)Cancel: (id)sender
-{
- [self.queueCore pause];
-
- // Which window to attach the sheet to?
- NSWindow * docWindow;
- if ([sender respondsToSelector: @selector(window)])
- {
- docWindow = [sender window];
}
else
{
- docWindow = fWindow;
+ [self doRip];
}
-
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)];
- [alert setInformativeText:NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil)];
- [alert addButtonWithTitle:NSLocalizedString(@"Continue Encoding", nil)];
- [alert addButtonWithTitle:NSLocalizedString(@"Cancel Current and Stop", nil)];
- [alert addButtonWithTitle:NSLocalizedString(@"Cancel Current and Continue", nil)];
- [alert setAlertStyle:NSCriticalAlertStyle];
- [alert beginSheetModalForWindow:docWindow
- modalDelegate:self
- didEndSelector:@selector(didDimissCancel:returnCode:contextInfo:)
- contextInfo:nil];
- [alert release];
}
-- (void) didDimissCancel: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
+/**
+ * overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
+ * want to overwrite an exiting movie file.
+ */
+- (void)overWriteAlertDone:(NSAlert *)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void *)contextInfo
{
- /* No need to prevent system sleep here as we didn't allow it in Cancel: */
- [self.queueCore resume];
-
if (returnCode == NSAlertSecondButtonReturn)
{
- [self doCancelCurrentJobAndStop];
- }
- else if (returnCode == NSAlertThirdButtonReturn)
- {
- [self doCancelCurrentJob]; // <- this also stops libhb
+ [self doRip];
}
}
-//------------------------------------------------------------------------------------
-// Cancels and deletes the current job and stops libhb from processing the remaining
-// encodes.
-//------------------------------------------------------------------------------------
-- (void) doCancelCurrentJob
+/**
+ * Displays an alert asking user if the want to cancel encoding of current job.
+ * Cancel: returns immediately after posting the alert. Later, when the user
+ * acknowledges the alert, doCancelCurrentJob is called.
+ */
+- (IBAction)cancel:(id)sender
{
- // Stop the current job.
- [self.queueCore stop];
-
- fEncodeState = 2; // don't alert at end of processing since this was a cancel
-
- // now that we've stopped the currently encoding job, lets mark it as cancelled
- [QueueFileArray[currentQueueEncodeIndex] setState:HBJobStateCanceled];
- // and as always, save it in Queue.plist
- /* We save all of the Queue data here */
- [self saveQueueFileItem];
-
- // and see if there are more items left in our queue
- NSInteger queueItems = [QueueFileArray count];
- /* If we still have more items in our queue, lets go to the next one */
- /* Check to see if there are any more pending items in the queue */
- NSInteger newQueueItemIndex = [self getNextPendingQueueIndex];
- /* If we still have more pending items in our queue, lets go to the next one */
- if (newQueueItemIndex >= 0 && newQueueItemIndex < queueItems)
- {
- /*Set our currentQueueEncodeIndex now to the newly found Pending encode as we own it */
- currentQueueEncodeIndex = newQueueItemIndex;
- /* now we mark the queue item as worling so another instance can not come along and try to scan it while we are scanning */
- [QueueFileArray[currentQueueEncodeIndex] setState:HBJobStateWorking];
- [HBUtilities writeToActivityLog: "incrementQueueItemDone new pending items found: %d", currentQueueEncodeIndex];
- [self saveQueueFileItem];
-
- HBJob *queueJob = QueueFileArray[currentQueueEncodeIndex];
- // now we can go ahead and scan the new pending queue item
- [self performNewQueueScan:queueJob.fileURL.path scanTitleNum:queueJob.titleIdx];
- }
- else
- {
- [HBUtilities writeToActivityLog: "incrementQueueItemDone there are no more pending encodes"];
- }
+ [fQueueController cancel:self];
}
-- (void) doCancelCurrentJobAndStop
+- (IBAction)pause:(id)sender
{
- [self.queueCore stop];
-
- fEncodeState = 2; // don't alert at end of processing since this was a cancel
-
- // now that we've stopped the currently encoding job, lets mark it as cancelled
- [QueueFileArray[currentQueueEncodeIndex] setState:HBJobStateCanceled];
- // and as always, save it in Queue.plist
- /* We save all of the Queue data here */
- [self saveQueueFileItem];
- // so now lets move to
- currentQueueEncodeIndex++ ;
- [HBUtilities writeToActivityLog: "cancelling current job and stopping the queue"];
-}
-- (IBAction) Pause: (id) sender
-{
- if (self.queueCore.state == HBStatePaused)
+ if (fQueueController.core.state == HBStatePaused)
{
- [self.queueCore resume];
+ [fQueueController.core resume];
}
else
{
- [self.queueCore pause];
+ [fQueueController.core pause];
}
}
#pragma mark -
#pragma mark Batch Queue Titles Methods
-- (IBAction) addAllTitlesToQueue: (id) sender
+
+- (IBAction)addAllTitlesToQueue:(id)sender
{
- NSAlert *alert = [NSAlert alertWithMessageText:@"You are about to add ALL titles to the queue!"
- defaultButton:@"Cancel"
- alternateButton:@"Yes, I want to add all titles to the queue"
- otherButton:nil
- informativeTextWithFormat:@"Current preset will be applied to all %ld titles. Are you sure you want to do this?", (long)[fSrcTitlePopUp numberOfItems]];
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:NSLocalizedString(@"You are about to add ALL titles to the queue!", @"")];
+ [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"Current preset will be applied to all %ld titles. Are you sure you want to do this?", @""), self.core.titles.count]];
+ [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Yes, I want to add all titles to the queue", @"")];
[alert setAlertStyle:NSCriticalAlertStyle];
- [alert beginSheetModalForWindow:fWindow modalDelegate:self didEndSelector:@selector( addAllTitlesToQueueAlertDone:returnCode:contextInfo: ) contextInfo:NULL];
+ [alert beginSheetModalForWindow:fWindow modalDelegate:self didEndSelector:@selector(addAllTitlesToQueueAlertDone:returnCode:contextInfo:) contextInfo:NULL];
+ [alert release];
}
-- (void) addAllTitlesToQueueAlertDone: (NSWindow *) sheet
- returnCode: (int) returnCode contextInfo: (void *) contextInfo
+- (void)addAllTitlesToQueueAlertDone:(NSAlert *)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void *)contextInfo
{
- if( returnCode == NSAlertAlternateReturn )
+ if (returnCode == NSAlertSecondButtonReturn)
+ {
[self doAddAllTitlesToQueue];
+ }
}
-- (void) doAddAllTitlesToQueue
+- (void)doAddAllTitlesToQueue
{
- // first get the currently selected index so we can choose it again after cycling through the available titles.
- NSInteger currentlySelectedTitle = [fSrcTitlePopUp indexOfSelectedItem];
-
- /* For each title in the fSrcTitlePopUp, select it */
- for (int i = 0; i < [fSrcTitlePopUp numberOfItems]; i++)
+ NSMutableArray *jobs = [[NSMutableArray alloc] init];
+
+ for (HBTitle *title in self.core.titles)
{
- [fSrcTitlePopUp selectItemAtIndex:i];
- // Now call titlePopUpChanged to load it up
- [self titlePopUpChanged:nil];
- // now add the title to the queue
- [self addToQueue:nil];
+ HBJob *job = [[HBJob alloc] initWithTitle:title andPreset:self.selectedPreset];
+ job.destURL = [self destURLForJob:job];
+ [jobs addObject:job];
+ [job release];
}
- // Now that we are done, reselect the previously selected title.
- [fSrcTitlePopUp selectItemAtIndex: currentlySelectedTitle];
- // Now call titlePopUpChanged to load it up
- [self titlePopUpChanged:nil];
+
+ [fQueueController addJobsFromArray:jobs];
+ [jobs release];
}
#pragma mark -
@@ -2376,11 +1401,6 @@ static void queueFSEventStreamCallback(
- (void)updateFileName
{
- if (!self.job)
- {
- return;
- }
-
HBTitle *title = self.job.title;
// Generate a new file name
@@ -2396,6 +1416,24 @@ static void queueFSEventStreamCallback(
[NSString stringWithFormat:@"%@.%@", fileName, self.job.destURL.pathExtension]];
}
+- (NSURL *)destURLForJob:(HBJob *)job
+{
+ // Check to see if the last destination has been set,use if so, if not, use Desktop
+ NSURL *destURL = [[NSUserDefaults standardUserDefaults] URLForKey:@"HBLastDestinationDirectory"];
+ if (!destURL || ![[NSFileManager defaultManager] fileExistsAtPath:destURL.path])
+ {
+ destURL = [NSURL fileURLWithPath:[NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES) firstObject]
+ isDirectory:YES];
+ }
+
+ destURL = [destURL URLByAppendingPathComponent:job.title.name];
+ // use the correct extension based on the container
+ const char *ext = hb_container_get_default_extension(self.job.container);
+ destURL = [destURL URLByAppendingPathExtension:@(ext)];
+
+ return destURL;
+}
+
- (IBAction) titlePopUpChanged: (id) sender
{
// If there is already a title load, save the current settings to a preset
@@ -2415,21 +1453,7 @@ static void queueFSEventStreamCallback(
else
{
self.job = [[[HBJob alloc] initWithTitle:title andPreset:self.selectedPreset] autorelease];
-
- // Check to see if the last destination has been set,use if so, if not, use Desktop
- NSURL *destURL = [[NSUserDefaults standardUserDefaults] URLForKey:@"HBLastDestinationDirectory"];
- if (!destURL || ![[NSFileManager defaultManager] fileExistsAtPath:destURL.path])
- {
- destURL = [NSURL fileURLWithPath:[NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES) firstObject]
- isDirectory:YES];
- }
-
- destURL = [destURL URLByAppendingPathComponent:title.name];
- // use the correct extension based on the container
- const char *ext = hb_container_get_default_extension(self.job.container);
- destURL = [destURL URLByAppendingPathExtension:@(ext)];
-
- self.job.destURL = destURL;
+ self.job.destURL = [self destURLForJob:self.job];
// set m4v extension if necessary - do not override user-specified .mp4 extension
if (self.job.container & HB_MUX_MASK_MP4)
@@ -2595,10 +1619,9 @@ static void queueFSEventStreamCallback(
*/
- (IBAction) showQueueWindow:(id)sender
{
- [fQueueController showQueueWindow:sender];
+ [fQueueController showWindow:sender];
}
-
- (IBAction) toggleDrawer:(id)sender
{
if ([fPresetDrawer state] == NSDrawerClosedState)
@@ -2616,7 +1639,6 @@ static void queueFSEventStreamCallback(
/**
* Shows Picture Settings Window.
*/
-
- (IBAction) showPicturePanel: (id) sender
{
[fPictureController showPictureWindow];
diff --git a/macosx/English.lproj/MainMenu.xib b/macosx/English.lproj/MainMenu.xib
index 13e98f79d..2aa8f63da 100644
--- a/macosx/English.lproj/MainMenu.xib
+++ b/macosx/English.lproj/MainMenu.xib
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C81f" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6724" systemVersion="14C99d" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment version="1060" identifier="macosx"/>
<development version="5100" identifier="xcode"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6724"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -415,12 +415,12 @@
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<connections>
+ <binding destination="240" name="value" keyPath="self.job.mp4iPodCompatible" id="GWA-QG-Bfu"/>
<binding destination="240" name="hidden" keyPath="self.job.mp4iPodCompatibleEnabled" id="eGS-R2-zSV">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
- <binding destination="240" name="value" keyPath="self.job.mp4iPodCompatible" id="GWA-QG-Bfu"/>
</connections>
</button>
<textField verticalHuggingPriority="750" id="5505">
@@ -491,12 +491,12 @@
</toolbarItem>
<toolbarItem implicitItemIdentifier="4CAB4280-DF01-4BDC-B23B-98BD6E6190F4" label="Start" paletteLabel="Start Encoding" toolTip="Start Encoding" tag="-1" image="encode" id="byg-kj-sEM">
<connections>
- <action selector="Rip:" target="240" id="0B6-IB-70f"/>
+ <action selector="rip:" target="240" id="0Db-56-2aY"/>
</connections>
</toolbarItem>
<toolbarItem implicitItemIdentifier="E4C5E251-DD8D-4288-AC80-AF8E654B7A32" label="Pause" paletteLabel="Pause Encoding" toolTip="Pause Encoding" tag="-1" image="pauseencode" id="wTQ-KF-5KW">
<connections>
- <action selector="Pause:" target="240" id="mIG-m6-Stn"/>
+ <action selector="pause:" target="240" id="OjT-ck-YX1"/>
</connections>
</toolbarItem>
<toolbarItem implicitItemIdentifier="69E83092-D0D1-48A5-BF46-99EFB3EC630A" label="Add To Queue" paletteLabel="Add To Queue" tag="-1" image="addqueue" id="DZZ-Fe-wjw">
@@ -647,12 +647,12 @@
</menuItem>
<menuItem title="Start Encoding" keyEquivalent="s" id="2444">
<connections>
- <action selector="Rip:" target="240" id="2448"/>
+ <action selector="rip:" target="240" id="kGM-Ym-khx"/>
</connections>
</menuItem>
<menuItem title="Pause Encoding" keyEquivalent="p" id="2494">
<connections>
- <action selector="Pause:" target="240" id="2496"/>
+ <action selector="pause:" target="240" id="RAM-7s-91U"/>
</connections>
</menuItem>
</items>
diff --git a/macosx/English.lproj/Queue.xib b/macosx/English.lproj/Queue.xib
index 116eebee8..297e3c964 100644
--- a/macosx/English.lproj/Queue.xib
+++ b/macosx/English.lproj/Queue.xib
@@ -1,21 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13F14" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6724" systemVersion="14C99d" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment version="1060" identifier="macosx"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6724"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="HBQueueController">
<connections>
- <outlet property="fOutlineView" destination="2597" id="2601"/>
- <outlet property="fProgressTextField" destination="2646" id="2648"/>
- <outlet property="fQueueCountField" destination="2511" id="2564"/>
+ <outlet property="countTextField" destination="2511" id="7vs-Ty-tNx"/>
+ <outlet property="outlineView" destination="2597" id="dPQ-wg-8cy"/>
+ <outlet property="progressTextField" destination="2646" id="E60-Gv-b2q"/>
<outlet property="window" destination="2576" id="2645"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
- <customObject id="-3" userLabel="Application"/>
- <window title="Queue - HandBrake" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="QueueWindow" animationBehavior="default" id="2576" userLabel="Window">
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <window title="Queue" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="QueueWindow" animationBehavior="default" id="2576" userLabel="Window">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" unifiedTitleAndToolbar="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="893" y="128" width="574" height="423"/>
@@ -33,7 +33,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<outlineView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" indentationPerLevel="16" autoresizesOutlineColumn="YES" outlineTableColumn="2624" id="2597" customClass="HBQueueOutlineView">
- <rect key="frame" x="0.0" y="0.0" width="532" height="336"/>
+ <rect key="frame" x="0.0" y="0.0" width="532" height="19"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@@ -137,6 +137,7 @@
<connections>
<outlet property="delegate" destination="-2" id="2579"/>
</connections>
+ <point key="canvasLocation" x="383" y="524.5"/>
</window>
<menu id="2649" userLabel="ContextMenu">
<items>
diff --git a/macosx/HBAttributedStringAdditions.h b/macosx/HBAttributedStringAdditions.h
new file mode 100644
index 000000000..bcc26e839
--- /dev/null
+++ b/macosx/HBAttributedStringAdditions.h
@@ -0,0 +1,16 @@
+//
+// HBAttributedStringAdditions.h
+// HandBrake
+//
+// Created by Damiano Galassi on 16/01/15.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSMutableAttributedString (HBAttributedStringAdditions)
+
+- (void)appendString:(NSString *)aString withAttributes:(NSDictionary *)aDictionary;
+
+@end
+
diff --git a/macosx/HBAttributedStringAdditions.m b/macosx/HBAttributedStringAdditions.m
new file mode 100644
index 000000000..fc685482d
--- /dev/null
+++ b/macosx/HBAttributedStringAdditions.m
@@ -0,0 +1,20 @@
+//
+// HBAttributedStringAdditions.m
+// HandBrake
+//
+// Created by Damiano Galassi on 16/01/15.
+//
+//
+
+#import "HBAttributedStringAdditions.h"
+
+@implementation NSMutableAttributedString (HBAttributedStringAdditions)
+
+- (void)appendString:(NSString *)aString withAttributes:(NSDictionary *)aDictionary
+{
+ NSAttributedString *s = [[[NSAttributedString alloc] initWithString:aString
+ attributes:aDictionary] autorelease];
+ [self appendAttributedString:s];
+}
+
+@end \ No newline at end of file
diff --git a/macosx/HBCore.h b/macosx/HBCore.h
index f78c26c55..66fd40d72 100644
--- a/macosx/HBCore.h
+++ b/macosx/HBCore.h
@@ -22,19 +22,12 @@ typedef NS_ENUM(NSUInteger, HBState) {
HBStateSearching = HB_STATE_SEARCHING ///< HB is searching
};
-// These constants specify various status notifications sent by HBCore
-extern NSString *HBCoreScanningNotification;
-extern NSString *HBCoreScanDoneNotification;
-extern NSString *HBCoreSearchingNotification;
-extern NSString *HBCoreWorkingNotification;
-extern NSString *HBCorePausedNotification;
-extern NSString *HBCoreWorkDoneNotification;
-extern NSString *HBCoreMuxingNotification;
+typedef void (^HBCoreProgressHandler)(HBState state, hb_state_t hb_state);
+typedef void (^HBCoreCompletationHandler)(BOOL success);
/**
* HBCore is an Objective-C interface to the low-level HandBrake library.
- * HBCore monitors state changes of libhb and provides notifications via
- * NSNotificationCenter to any object who needs them. It can also be used
+ * HBCore monitors state changes of libhb. It can also be used
* to implement properties that can be directly bound to elements of the gui.
*/
@interface HBCore : NSObject
@@ -68,7 +61,7 @@ extern NSString *HBCoreMuxingNotification;
@property (nonatomic, readonly) HBState state;
/**
- * Pointer to a hb_state_s struct containing state information of libhb.
+ * Pointer to a hb_state_s struct containing the detailed state information of libhb.
*/
@property (nonatomic, readonly) hb_state_t *hb_state;
@@ -78,12 +71,12 @@ extern NSString *HBCoreMuxingNotification;
@property (nonatomic, readonly) hb_handle_t *hb_handle;
/**
- * The name of the core, used in for debugging purpose.
+ * The name of the core, used for debugging purpose.
*/
@property (nonatomic, copy) NSString *name;
/**
- * Determines whether the scan operation can scan a particural URL or whether an additional decription lib is needed..
+ * Determines whether the scan operation can scan a particural URL or whether an additional decryption lib is needed.
*
* @param url the URL of the input file.
* @param error an error containing additional info.
@@ -100,7 +93,12 @@ extern NSString *HBCoreMuxingNotification;
* @param previewsNum the number of previews image to generate.
* @param minTitleDuration the minimum duration of the wanted titles in seconds.
*/
-- (void)scan:(NSURL *)url titleNum:(NSUInteger)titleNum previewsNum:(NSUInteger)previewsNum minTitleDuration:(NSUInteger)minTitleDuration;
+- (void)scanURL:(NSURL *)url
+ titleIndex:(NSUInteger)titleNum
+ previews:(NSUInteger)previewsNum
+ minDuration:(NSUInteger)minTitleDuration
+progressHandler:(HBCoreProgressHandler)progressHandler
+completationHandler:(HBCoreCompletationHandler)completationHandler;
/**
* Cancels the scan execution.
@@ -117,19 +115,20 @@ extern NSString *HBCoreMuxingNotification;
*
* @param job the job to encode.
*/
-- (void)encodeJob:(HBJob *)job;
+- (void)encodeJob:(HBJob *)job progressHandler:(HBCoreProgressHandler)progressHandler completationHandler:(HBCoreCompletationHandler)completationHandler;
/**
- * Starts the libhb encoding session.
- *
- * This method must be called after all jobs have been added.
+ * Stops encoding session and releases resources.
*/
-- (void)start;
+- (void)cancelEncode;
/**
- * Stops encoding session and releases resources.
+ * Starts the libhb encoding session.
+ *
+ * This method must be called after all jobs have been added.
+ * deprecated, will be removed soon.
*/
-- (void)stop;
+- (void)startProgressHandler:(HBCoreProgressHandler)progressHandler completationHandler:(HBCoreCompletationHandler)completationHandler;
/**
* Pauses the encoding session.
diff --git a/macosx/HBCore.m b/macosx/HBCore.m
index 111f5dc1e..05871bbbc 100644
--- a/macosx/HBCore.m
+++ b/macosx/HBCore.m
@@ -12,29 +12,6 @@
#include <dlfcn.h>
-// These constants specify various status notifications sent by HBCore
-
-/// Notification sent to update status while scanning. Matches HB_STATE_SCANNING constant in libhb.
-NSString *HBCoreScanningNotification = @"HBCoreScanningNotification";
-
-/// Notification sent after scanning is complete. Matches HB_STATE_SCANDONE constant in libhb.
-NSString *HBCoreScanDoneNotification = @"HBCoreScanDoneNotification";
-
-/// Notification sent to update status while searching. Matches HB_STATE_SEARCHING constant in libhb.
-NSString *HBCoreSearchingNotification = @"HBCoreSearchingNotification";
-
-/// Notification sent to update status while encoding. Matches HB_STATE_WORKING constant in libhb.
-NSString *HBCoreWorkingNotification = @"HBCoreWorkingNotification";
-
-/// Notification sent when encoding is paused. Matches HB_STATE_PAUSED constant in libhb.
-NSString *HBCorePausedNotification = @"HBCorePausedNotification";
-
-/// Notification sent after encoding is complete. Matches HB_STATE_WORKDONE constant in libhb.
-NSString *HBCoreWorkDoneNotification = @"HBCoreWorkDoneNotification";
-
-/// Notification sent to update status while muxing. Matches HB_STATE_MUXING constant in libhb.
-NSString *HBCoreMuxingNotification = @"HBCoreMuxingNotification";
-
/**
* Private methods of HBCore.
*/
@@ -49,6 +26,15 @@ NSString *HBCoreMuxingNotification = @"HBCoreMuxingNotification";
/// Current scanned titles.
@property (nonatomic, readwrite, retain) NSArray *titles;
+/// Progress handler.
+@property (nonatomic, readwrite, copy) HBCoreProgressHandler progressHandler;
+
+/// Completation handler.
+@property (nonatomic, readwrite, copy) HBCoreCompletationHandler completationHandler;
+
+/// User cancelled.
+@property (nonatomic, readwrite, getter=isCancelled) BOOL cancelled;
+
- (void)stateUpdateTimer:(NSTimer *)timer;
@end
@@ -161,8 +147,12 @@ NSString *HBCoreMuxingNotification = @"HBCoreMuxingNotification";
return YES;
}
-- (void)scan:(NSURL *)url titleNum:(NSUInteger)titleNum previewsNum:(NSUInteger)previewsNum minTitleDuration:(NSUInteger)minTitleDuration;
+- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)titleNum previews:(NSUInteger)previewsNum minDuration:(NSUInteger)minTitleDuration progressHandler:(HBCoreProgressHandler)progressHandler completationHandler:(HBCoreCompletationHandler)completationHandler
{
+ // Copy the progress/completation blocks
+ self.progressHandler = progressHandler;
+ self.completationHandler = completationHandler;
+
// Start the timer to handle libhb state changes
[self startUpdateTimerWithInterval:0.2];
@@ -207,7 +197,7 @@ NSString *HBCoreMuxingNotification = @"HBCoreMuxingNotification";
/**
* Creates an array of lightweight HBTitles instances.
*/
-- (void)scanDone
+- (BOOL)scanDone
{
hb_title_set_t *title_set = hb_get_title_set(_hb_handle);
NSMutableArray *titles = [NSMutableArray array];
@@ -221,6 +211,8 @@ NSString *HBCoreMuxingNotification = @"HBCoreMuxingNotification";
self.titles = [[titles copy] autorelease];
[HBUtilities writeToActivityLog:"%s scan done", self.name.UTF8String];
+
+ return self.titles.count;
}
- (void)cancelScan
@@ -232,23 +224,25 @@ NSString *HBCoreMuxingNotification = @"HBCoreMuxingNotification";
#pragma mark - Encodes
-- (void)encodeJob:(HBJob *)job
+- (void)encodeJob:(HBJob *)job progressHandler:(HBCoreProgressHandler)progressHandler completationHandler:(HBCoreCompletationHandler)completationHandler;
{
+ // Add the job to libhb
hb_job_t *hb_job = job.hb_job;
-
- [HBUtilities writeToActivityLog: "processNewQueueEncode number of passes expected is: %d", (job.video.twoPass + 1)];
hb_job_set_file(hb_job, job.destURL.path.fileSystemRepresentation);
-
hb_add(self.hb_handle, hb_job);
// Free the job
hb_job_close(&hb_job);
- [self start];
+ [self startProgressHandler:progressHandler completationHandler:completationHandler];
}
-- (void)start
+- (void)startProgressHandler:(HBCoreProgressHandler)progressHandler completationHandler:(HBCoreCompletationHandler)completationHandler;
{
+ // Copy the progress/completation blocks
+ self.progressHandler = progressHandler;
+ self.completationHandler = completationHandler;
+
// Start the timer to handle libhb state changes
[self startUpdateTimerWithInterval:0.5];
@@ -263,20 +257,34 @@ NSString *HBCoreMuxingNotification = @"HBCoreMuxingNotification";
[HBUtilities writeToActivityLog:"%s work started", self.name.UTF8String];
}
-- (void)workDone
+- (BOOL)workDone
{
// HB_STATE_WORKDONE happpens as a result of libhb finishing all its jobs
// or someone calling hb_stop. In the latter case, hb_stop does not clear
// out the remaining passes/jobs in the queue. We'll do that here.
hb_job_t *job;
while ((job = hb_job(_hb_handle, 0)))
+ {
hb_rem(_hb_handle, job);
+ }
[HBUtilities writeToActivityLog:"%s work done", self.name.UTF8String];
+
+ if (self.isCancelled)
+ {
+ self.cancelled = NO;
+ return NO;
+ }
+ else
+ {
+ return YES;
+ }
}
-- (void)stop
+- (void)cancelEncode
{
+ self.cancelled = YES;
+
hb_stop(_hb_handle);
hb_system_sleep_allow(_hb_handle);
@@ -334,19 +342,15 @@ NSString *HBCoreMuxingNotification = @"HBCoreMuxingNotification";
switch (stateValue)
{
case HB_STATE_WORKING:
- return @selector(handleHBStateWorking);
case HB_STATE_SCANNING:
- return @selector(handleHBStateScanning);
case HB_STATE_MUXING:
- return @selector(handleHBStateMuxing);
case HB_STATE_PAUSED:
- return @selector(handleHBStatePaused);
case HB_STATE_SEARCHING:
- return @selector(handleHBStateSearching);
+ return @selector(handleProgress);
case HB_STATE_SCANDONE:
- return @selector(handleHBStateScanDone);
+ return @selector(handleScanCompletation);
case HB_STATE_WORKDONE:
- return @selector(handleHBStateWorkDone);
+ return @selector(handleWorkCompletation);
default:
NSAssert1(NO, @"[HBCore selectorForState:] unknown state %lu", stateValue);
return NULL;
@@ -397,68 +401,51 @@ NSString *HBCoreMuxingNotification = @"HBCoreMuxingNotification";
#pragma mark - Notifications
/**
- * Processes HBStateScanning state information. Current implementation just
- * sends HBCoreScanningNotification.
+ * Processes HBStateSearching state information. Current implementation just
+ * sends HBCoreSearchingNotification.
*/
-- (void)handleHBStateScanning
+- (void)handleProgress
{
- [[NSNotificationCenter defaultCenter] postNotificationName:HBCoreScanningNotification object:self];
+ if (self.progressHandler)
+ {
+ self.progressHandler(self.state, *(self.hb_state));
+ }
}
/**
* Processes HBStateScanDone state information. Current implementation just
* sends HBCoreScanDoneNotification.
*/
-- (void)handleHBStateScanDone
-{
- [self scanDone];
- [[NSNotificationCenter defaultCenter] postNotificationName:HBCoreScanDoneNotification object:self];
-}
-
-/**
- * Processes HBStateWorking state information. Current implementation just
- * sends HBCoreWorkingNotification.
- */
-- (void)handleHBStateWorking
+- (void)handleScanCompletation
{
- [[NSNotificationCenter defaultCenter] postNotificationName:HBCoreWorkingNotification object:self];
-}
+ BOOL success = [self scanDone];
-/**
- * Processes HBStatePaused state information. Current implementation just
- * sends HBCorePausedNotification.
- */
-- (void)handleHBStatePaused
-{
- [[NSNotificationCenter defaultCenter] postNotificationName:HBCorePausedNotification object:self];
+ if (self.completationHandler)
+ {
+ HBCoreCompletationHandler completationHandler = [self.completationHandler retain];
+ self.progressHandler = nil;
+ self.completationHandler = nil;
+ completationHandler(success);
+ [completationHandler release];
+ }
}
/**
* Processes HBStateWorkDone state information. Current implementation just
* sends HBCoreWorkDoneNotification.
*/
-- (void)handleHBStateWorkDone
+- (void)handleWorkCompletation
{
- [self workDone];
- [[NSNotificationCenter defaultCenter] postNotificationName:HBCoreWorkDoneNotification object:self];
-}
+ BOOL success = [self workDone];
-/**
- * Processes HBStateMuxing state information. Current implementation just
- * sends HBCoreMuxingNotification.
- */
-- (void)handleHBStateMuxing
-{
- [[NSNotificationCenter defaultCenter] postNotificationName:HBCoreMuxingNotification object:self];
-}
-
-/**
- * Processes HBStateSearching state information. Current implementation just
- * sends HBCoreSearchingNotification.
- */
-- (void)handleHBStateSearching
-{
- [[NSNotificationCenter defaultCenter] postNotificationName:HBCoreSearchingNotification object:self];
+ if (self.completationHandler)
+ {
+ HBCoreCompletationHandler completationHandler = [self.completationHandler retain];
+ self.progressHandler = nil;
+ self.completationHandler = nil;
+ completationHandler(success);
+ [completationHandler release];
+ }
}
@end
diff --git a/macosx/HBDistributedArray.h b/macosx/HBDistributedArray.h
new file mode 100644
index 000000000..4163fca17
--- /dev/null
+++ b/macosx/HBDistributedArray.h
@@ -0,0 +1,36 @@
+/* HBDistributedArray.h $
+
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
+
+#import <Cocoa/Cocoa.h>
+
+extern NSString *HBDistributedArrayChanged;
+
+/**
+ * HBDistributedArray
+ * a mutable array that share its content between processes.
+ * post a HBDistributedArrayChanged when the content is changed
+ * by another process.
+ *
+ * Use beginTransaction and commit to wrap atomic changes to the array.
+ *
+ * It is safe to keep a reference to an array object.
+ */
+@interface HBDistributedArray : NSMutableArray
+
+- (instancetype)initWithURL:(NSURL *)fileURL;
+
+/**
+ * Begin a transaction on the array
+ */
+- (void)beginTransaction;
+
+/**
+ * Commit the changes and notify
+ * the observers about the changes.
+ */
+- (void)commit;
+
+@end
diff --git a/macosx/HBDistributedArray.m b/macosx/HBDistributedArray.m
new file mode 100644
index 000000000..c8b07ddcf
--- /dev/null
+++ b/macosx/HBDistributedArray.m
@@ -0,0 +1,328 @@
+/* HBDistributedArray.m $
+
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
+
+#import "HBDistributedArray.h"
+
+#include <semaphore.h>
+
+/**
+ * HBProxyArrayObject wraps an object inside a proxy
+ * to make it possible to keep a reference to an array
+ * object even if the underlying has been swapped
+ */
+
+@interface HBProxyArrayObject : NSProxy
+
+- (instancetype)initWithObject:(id)object;
+
+@property (nonatomic, retain) id representedObject;
+@property (nonatomic, readonly) NSString *uuid;
+
+@end
+
+@implementation HBProxyArrayObject
+
+- (instancetype)initWithObject:(id)object
+{
+ _representedObject = [object retain];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_representedObject release];
+ [super dealloc];
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
+{
+ return [self.representedObject methodSignatureForSelector:selector];
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation
+{
+ [invocation invokeWithTarget:self.representedObject];
+}
+
+- (NSString *)uuid
+{
+ return [self.representedObject uuid];
+}
+
+@end
+
+NSString *HBDistributedArrayChanged = @"HBDistributedArrayChanged";
+NSString *HBDistributedArraWrittenToDisk = @"HBDistributedArraWrittenToDisk";
+
+@interface HBDistributedArray ()
+
+@property (nonatomic, readonly) NSMutableArray *array;
+@property (nonatomic, readonly) NSURL *fileURL;
+@property (nonatomic, readwrite) NSTimeInterval modifiedTime;
+
+@property (nonatomic, readonly) sem_t *mutex;
+
+@end
+
+@implementation HBDistributedArray
+
+- (instancetype)initWithURL:(NSURL *)fileURL
+{
+ self = [super init];
+ if (self)
+ {
+ _fileURL = [fileURL copy];
+ _array = [[NSMutableArray alloc] init];
+
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ if (![fileManager fileExistsAtPath:_fileURL.path])
+ {
+ if (![fileManager fileExistsAtPath:_fileURL.URLByDeletingLastPathComponent.path])
+ {
+ [fileManager createDirectoryAtPath:_fileURL.URLByDeletingLastPathComponent.path withIntermediateDirectories:YES attributes:nil error:NULL];
+ }
+ [fileManager createFileAtPath:_fileURL.path contents:nil attributes:nil];
+ }
+
+ NSArray *runningInstances = [NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]];
+ const char *name = [NSString stringWithFormat:@"/%@.hblock", _fileURL.lastPathComponent].UTF8String;
+
+ // Unlink the semaphore if we are the only
+ // instance running, this fixes the case where
+ // HB crashed while the sem is locked.
+ if (runningInstances.count == 1)
+ {
+ sem_unlink(name);
+ }
+
+ // Use a named semaphore as a mutex for now
+ // it can cause a deadlock if an instance
+ // crashed while it has the lock on the semaphore.
+ _mutex = sem_open(name, O_CREAT, 0777, 1);
+ if (_mutex == SEM_FAILED) {
+ NSLog(@"%s: %d\n", "Error in creating semaphore: ", errno);
+ }
+
+ [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:HBDistributedArraWrittenToDisk object:nil];
+
+ // Load the array from disk
+ [self lock];
+ [self reload];
+ [self unlock];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [self lock];
+ [self synchronize];
+ [self unlock];
+
+ [_fileURL release];
+ _fileURL = nil;
+ [_array release];
+ _array = nil;
+
+ [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
+
+ sem_close(_mutex);
+
+ [super dealloc];
+}
+
+- (void)lock
+{
+ sem_wait(self.mutex);
+}
+
+- (void)unlock
+{
+ sem_post(self.mutex);
+}
+
+- (void)beginTransaction
+{
+ [self lock];
+ // We got the lock, need to check if
+ // someone else modified the file
+ // while we were locked, because we
+ // could have not received the notification yet
+ NSDate *date = nil;
+ [self.fileURL getResourceValue:&date forKey:NSURLAttributeModificationDateKey error:nil];
+ if (date.timeIntervalSinceReferenceDate > self.modifiedTime)
+ {
+ // File was modified while we waited on the lock
+ // reload it
+ [self reload];
+ }
+}
+
+- (void)commit
+{
+ [self synchronize];
+ [self unlock];
+}
+
+- (void)postNotification
+{
+ [[NSNotificationCenter defaultCenter] postNotificationName:HBDistributedArrayChanged object:self];
+}
+
+/**
+ * Handle the distributed notification
+ */
+- (void)handleNotification:(NSNotification *)notification
+{
+ if (!([notification.object integerValue] == getpid()))
+ {
+ if ([notification.userInfo[@"path"] isEqualToString:self.fileURL.path])
+ {
+ [self lock];
+ [self reload];
+ [self unlock];
+ }
+ }
+}
+
+/**
+ * Reload the array from disk
+ */
+- (void)reload
+{
+ NSMutableArray *temp = nil;;
+ @try
+ {
+ temp = [NSKeyedUnarchiver unarchiveObjectWithFile:self.fileURL.path];
+ }
+ @catch (NSException *exception)
+ {
+ temp = nil;
+ }
+
+ // Swap the proxy objects representation with the new
+ // one read from disk
+ NSMutableArray *proxyArray = [NSMutableArray array];
+ for (id anObject in temp)
+ {
+ NSString *uuid = [anObject uuid];
+
+ HBProxyArrayObject *proxy = nil;
+ for (HBProxyArrayObject *temp in self.array)
+ {
+ if ([[temp uuid] isEqualToString:uuid])
+ {
+ temp.representedObject = anObject;
+ proxy = temp;
+ break;
+ }
+ }
+
+ if (proxy)
+ {
+ [proxyArray addObject:proxy];
+ }
+ else
+ {
+ [proxyArray addObject:[self wrapObjectIfNeeded:anObject]];
+ }
+ }
+
+ [self setArray:proxyArray];
+ [self postNotification];
+
+ // Update the time, so we can avoid reloaded the file from disk later.
+ self.modifiedTime = [NSDate timeIntervalSinceReferenceDate];
+}
+
+/**
+ * Writes the changes to disk
+ */
+- (void)synchronize
+{
+ NSMutableArray *temp = [NSMutableArray array];
+
+ // Unwrap the array objects and save them to disk
+ for (HBProxyArrayObject *proxy in self)
+ {
+ [temp addObject:proxy.representedObject];
+ }
+
+ if (![NSKeyedArchiver archiveRootObject:temp toFile:self.fileURL.path])
+ {
+ NSLog(@"failed to write the queue to disk");
+ }
+
+ // Send a distributed notification.
+ [[NSDistributedNotificationCenter defaultCenter] postNotificationName:HBDistributedArraWrittenToDisk
+ object:[NSString stringWithFormat:@"%d", getpid()]
+ userInfo:@{@"path": self.fileURL.path}
+ deliverImmediately:YES];
+
+ // Update the time, so we can avoid reloaded the file from disk later.
+ self.modifiedTime = [NSDate timeIntervalSinceReferenceDate];
+}
+
+/**
+ * Wraps an object inside a HBObjectProxy instance
+ * if it's not already wrapped.
+ *
+ * @param anObject the object to wrap
+ *
+ * @return a wrapped object
+ */
+- (id)wrapObjectIfNeeded:(id)anObject
+{
+ if ([[anObject class] isEqual:[HBProxyArrayObject class]])
+ {
+ return anObject;
+ }
+ else
+ {
+ return [[[HBProxyArrayObject alloc] initWithObject:anObject] autorelease];
+ }
+}
+
+#pragma mark - Methods needed to subclass NSMutableArray
+
+- (void)insertObject:(id)anObject atIndex:(NSUInteger)index
+{
+ [self.array insertObject:[self wrapObjectIfNeeded:anObject] atIndex:index];
+}
+
+- (void)removeObjectAtIndex:(NSUInteger)index
+{
+ [self.array removeObjectAtIndex:index];
+}
+
+- (void)addObject:(id)anObject
+{
+ [self.array addObject:[self wrapObjectIfNeeded:anObject]];
+}
+
+- (void)removeLastObject
+{
+ [self.array removeLastObject];
+}
+
+- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject
+{
+ [self.array replaceObjectAtIndex:index withObject:[self wrapObjectIfNeeded:anObject]];
+}
+
+- (NSUInteger)count
+{
+ return [self.array count];
+}
+
+- (id)objectAtIndex:(NSUInteger)index
+{
+ return [self.array objectAtIndex:index];
+}
+
+@end
diff --git a/macosx/HBJob+UIAdditions.h b/macosx/HBJob+UIAdditions.h
index a8adc4f25..0ef1d05be 100644
--- a/macosx/HBJob+UIAdditions.h
+++ b/macosx/HBJob+UIAdditions.h
@@ -14,6 +14,8 @@
@property (nonatomic, readonly) NSArray *angles;
+@property (nonatomic, readonly) NSAttributedString *attributedDescription;
+
@end
@interface HBContainerTransformer : NSValueTransformer
diff --git a/macosx/HBJob+UIAdditions.m b/macosx/HBJob+UIAdditions.m
index 432478a42..1f941e5df 100644
--- a/macosx/HBJob+UIAdditions.m
+++ b/macosx/HBJob+UIAdditions.m
@@ -5,8 +5,24 @@
It may be used under the terms of the GNU General Public License. */
#import "HBJob+UIAdditions.h"
+
+#import "HBAttributedStringAdditions.h"
+#import "HBJob.h"
+#import "HBAudioTrack.h"
+#import "HBAudioDefaults.h"
+
+#import "HBPicture+UIAdditions.h"
+#import "HBFilters+UIAdditions.h"
+
#include "hb.h"
+// Text Styles
+static NSMutableParagraphStyle *ps;
+static NSDictionary *detailAttr;
+static NSDictionary *detailBoldAttr;
+static NSDictionary *titleAttr;
+static NSDictionary *shortHeightAttr;
+
@implementation HBJob (UIAdditions)
- (BOOL)mp4OptionsEnabled
@@ -56,6 +72,396 @@
return [[containers copy] autorelease];
}
+- (void)initStyles
+{
+ if (!ps)
+ {
+ // Attributes
+ ps = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
+ [ps setHeadIndent: 40.0];
+ [ps setParagraphSpacing: 1.0];
+ [ps setTabStops:@[]]; // clear all tabs
+ [ps addTabStop: [[[NSTextTab alloc] initWithType: NSLeftTabStopType location: 20.0] autorelease]];
+
+ detailAttr = [@{NSFontAttributeName: [NSFont systemFontOfSize:10.0],
+ NSParagraphStyleAttributeName: ps} retain];
+
+ detailBoldAttr = [@{NSFontAttributeName: [NSFont boldSystemFontOfSize:10.0],
+ NSParagraphStyleAttributeName: ps} retain];
+
+ titleAttr = [@{NSFontAttributeName: [NSFont systemFontOfSize:[NSFont systemFontSize]],
+ NSParagraphStyleAttributeName: ps} retain];
+
+ shortHeightAttr = [@{NSFontAttributeName: [NSFont systemFontOfSize:2.0]} retain];
+ }
+}
+
+- (NSAttributedString *)attributedDescription
+{
+ // Below should be put into a separate method but I am way too f'ing lazy right now
+ NSMutableAttributedString *finalString = [[NSMutableAttributedString alloc] initWithString: @""];
+
+ [self initStyles];
+
+ @autoreleasepool
+ {
+ // First line, we should strip the destination path and just show the file name and add the title num and chapters (if any)
+ NSString *summaryInfo;
+
+ NSString *titleString = [NSString stringWithFormat:@"Title %d", self.titleIdx];
+
+ NSString *startStopString = @"";
+ if (self.range.type == HBRangeTypeChapters)
+ {
+ // Start Stop is chapters
+ startStopString = (self.range.chapterStart == self.range.chapterStop) ?
+ [NSString stringWithFormat:@"Chapter %d", self.range.chapterStart] :
+ [NSString stringWithFormat:@"Chapters %d through %d", self.range.chapterStart, self.range.chapterStop];
+ }
+ else if (self.range.type == HBRangeTypeSeconds)
+ {
+ // Start Stop is seconds
+ startStopString = [NSString stringWithFormat:@"Seconds %d through %d", self.range.secondsStart, self.range.secondsStop];
+ }
+ else if (self.range.type == HBRangeTypeFrames)
+ {
+ // Start Stop is Frames
+ startStopString = [NSString stringWithFormat:@"Frames %d through %d", self.range.frameStart, self.range.frameStop];
+ }
+ NSString *passesString = @"";
+ // check to see if our first subtitle track is Foreign Language Search, in which case there is an in depth scan
+ if (self.subtitles.tracks.count && [self.subtitles.tracks[0][@"keySubTrackIndex"] intValue] == -1)
+ {
+ passesString = [passesString stringByAppendingString:@"1 Foreign Language Search Pass - "];
+ }
+ if (self.video.qualityType == 1 || self.video.twoPass == NO)
+ {
+ passesString = [passesString stringByAppendingString:@"1 Video Pass"];
+ }
+ else
+ {
+ if (self.video.turboTwoPass == YES)
+ {
+ passesString = [passesString stringByAppendingString:@"2 Video Passes First Turbo"];
+ }
+ else
+ {
+ passesString = [passesString stringByAppendingString:@"2 Video Passes"];
+ }
+ }
+
+ [finalString appendString:[NSString stringWithFormat:@"%@", self.fileURL.path.lastPathComponent] withAttributes:titleAttr];
+
+ // lets add the output file name to the title string here
+ NSString *outputFilenameString = self.destURL.lastPathComponent;
+
+ summaryInfo = [NSString stringWithFormat: @" (%@, %@, %@) -> %@", titleString, startStopString, passesString, outputFilenameString];
+
+ [finalString appendString:[NSString stringWithFormat:@"%@\n", summaryInfo] withAttributes:detailAttr];
+
+ // Insert a short-in-height line to put some white space after the title
+ [finalString appendString:@"\n" withAttributes:shortHeightAttr];
+ // End of Title Stuff
+
+ // Second Line (Preset Name)
+ [finalString appendString: @"Preset: " withAttributes:detailBoldAttr];
+ [finalString appendString:[NSString stringWithFormat:@"%@\n", self.presetName] withAttributes:detailAttr];
+
+ // Third Line (Format Summary)
+ NSString *audioCodecSummary = @""; // This seems to be set by the last track we have available...
+ // Lets also get our audio track detail since we are going through the logic for use later
+
+ NSMutableArray *audioDetails = [NSMutableArray array];
+ BOOL autoPassthruPresent = NO;
+
+ for (HBAudioTrack *audioTrack in self.audio.tracks)
+ {
+ if (audioTrack.enabled)
+ {
+ audioCodecSummary = [NSString stringWithFormat: @"%@", audioTrack.codec[keyAudioCodecName]];
+ NSNumber *drc = audioTrack.drc;
+ NSNumber *gain = audioTrack.gain;
+ NSString *detailString = [NSString stringWithFormat: @"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps), DRC: %@, Gain: %@",
+ audioTrack.track[keyAudioTrackName],
+ audioTrack.codec[keyAudioCodecName],
+ audioTrack.mixdown[keyAudioMixdownName],
+ audioTrack.sampleRate[keyAudioSampleRateName],
+ audioTrack.bitRate[keyAudioBitrateName],
+ (0.0 < [drc floatValue]) ? (NSObject *)drc : (NSObject *)@"Off",
+ (0.0 != [gain floatValue]) ? (NSObject *)gain : (NSObject *)@"Off"
+ ];
+ [audioDetails addObject: detailString];
+ // check if we have an Auto Passthru output track
+ if ([audioTrack.codec[keyAudioCodecName] isEqualToString: @"Auto Passthru"])
+ {
+ autoPassthruPresent = YES;
+ }
+ }
+ }
+
+ NSString *jobFormatInfo;
+ if (self.chaptersEnabled)
+ jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video %@ Audio, Chapter Markers\n",
+ @(hb_container_get_name(self.container)), @(hb_video_encoder_get_name(self.video.encoder)), audioCodecSummary];
+ else
+ jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video %@ Audio\n",
+ @(hb_container_get_name(self.container)), @(hb_video_encoder_get_name(self.video.encoder)), audioCodecSummary];
+
+ [finalString appendString: @"Format: " withAttributes:detailBoldAttr];
+ [finalString appendString: jobFormatInfo withAttributes:detailAttr];
+
+ // Optional String for muxer options
+ NSMutableString *containerOptions = [NSMutableString stringWithString:@""];
+ if ((self.container & HB_MUX_MASK_MP4) && self.mp4HttpOptimize)
+ {
+ [containerOptions appendString:@" - Web optimized"];
+ }
+ if ((self.container & HB_MUX_MASK_MP4) && self.mp4iPodCompatible)
+ {
+ [containerOptions appendString:@" - iPod 5G support"];
+ }
+ if ([containerOptions hasPrefix:@" - "])
+ {
+ [containerOptions deleteCharactersInRange:NSMakeRange(0, 3)];
+ }
+ if (containerOptions.length)
+ {
+ [finalString appendString:@"Container Options: " withAttributes:detailBoldAttr];
+ [finalString appendString:containerOptions withAttributes:detailAttr];
+ [finalString appendString:@"\n" withAttributes:detailAttr];
+ }
+
+ // Fourth Line (Destination Path)
+ [finalString appendString: @"Destination: " withAttributes:detailBoldAttr];
+ [finalString appendString: self.destURL.path withAttributes:detailAttr];
+ [finalString appendString:@"\n" withAttributes:detailAttr];
+
+
+ // Fifth Line Picture Details
+ NSString *pictureInfo = [NSString stringWithFormat:@"%@", self.picture.summary];
+ if (self.picture.keepDisplayAspect)
+ {
+ pictureInfo = [pictureInfo stringByAppendingString:@" Keep Aspect Ratio"];
+ }
+ [finalString appendString:@"Picture: " withAttributes:detailBoldAttr];
+ [finalString appendString:pictureInfo withAttributes:detailAttr];
+ [finalString appendString:@"\n" withAttributes:detailAttr];
+
+ /* Optional String for Picture Filters */
+ if (self.filters.summary.length)
+ {
+ NSString *pictureFilters = [NSString stringWithFormat:@"%@", self.filters.summary];
+ [finalString appendString:@"Filters: " withAttributes:detailBoldAttr];
+ [finalString appendString:pictureFilters withAttributes:detailAttr];
+ [finalString appendString:@"\n" withAttributes:detailAttr];
+ }
+
+ // Sixth Line Video Details
+ NSString * videoInfo = [NSString stringWithFormat:@"Encoder: %@", @(hb_video_encoder_get_name(self.video.encoder))];
+
+ // for framerate look to see if we are using vfr detelecine
+ if (self.video.frameRate == 0)
+ {
+ if (self.video.frameRateMode == 0)
+ {
+ // we are using same as source with vfr detelecine
+ videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (Variable Frame Rate)", videoInfo];
+ }
+ else
+ {
+ // we are using a variable framerate without dropping frames
+ videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (Constant Frame Rate)", videoInfo];
+ }
+ }
+ else
+ {
+ // we have a specified, constant framerate
+ if (self.video.frameRateMode == 0)
+ {
+ videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@ (Peak Frame Rate)", videoInfo, @(hb_video_framerate_get_name(self.video.frameRate))];
+ }
+ else
+ {
+ videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@ (Constant Frame Rate)", videoInfo, @(hb_video_framerate_get_name(self.video.frameRate))];
+ }
+ }
+
+
+ if (self.video.qualityType == 0) // ABR
+ {
+ videoInfo = [NSString stringWithFormat:@"%@ Bitrate: %d(kbps)", videoInfo, self.video.avgBitrate];
+ }
+ else // CRF
+ {
+ videoInfo = [NSString stringWithFormat:@"%@ Constant Quality: %.2f", videoInfo ,self.video.quality];
+ }
+
+ [finalString appendString: @"Video: " withAttributes:detailBoldAttr];
+ [finalString appendString: videoInfo withAttributes:detailAttr];
+ [finalString appendString:@"\n" withAttributes:detailAttr];
+
+
+ if (self.video.encoder == HB_VCODEC_X264 || self.video.encoder == HB_VCODEC_X265)
+ {
+ // we are using x264/x265
+ NSString *encoderPresetInfo = @"";
+ if (self.video.advancedOptions)
+ {
+ // we are using the old advanced panel
+ if (self.video.videoOptionExtra.length)
+ {
+ encoderPresetInfo = [encoderPresetInfo stringByAppendingString:self.video.videoOptionExtra];
+ }
+ else
+ {
+ encoderPresetInfo = [encoderPresetInfo stringByAppendingString:@"default settings"];
+ }
+ }
+ else
+ {
+ // we are using the x264 system
+ encoderPresetInfo = [encoderPresetInfo stringByAppendingString: [NSString stringWithFormat:@"Preset: %@", self.video.preset]];
+ if (self.video.tune.length)
+ {
+ encoderPresetInfo = [encoderPresetInfo stringByAppendingString: [NSString stringWithFormat:@" - Tune: %@", self.video.tune]];
+ }
+ if (self.video.videoOptionExtra.length)
+ {
+ encoderPresetInfo = [encoderPresetInfo stringByAppendingString: [NSString stringWithFormat:@" - Options: %@", self.video.videoOptionExtra]];
+ }
+ if (self.video.profile.length)
+ {
+ encoderPresetInfo = [encoderPresetInfo stringByAppendingString: [NSString stringWithFormat:@" - Profile: %@", self.video.profile]];
+ }
+ if (self.video.level.length)
+ {
+ encoderPresetInfo = [encoderPresetInfo stringByAppendingString: [NSString stringWithFormat:@" - Level: %@", self.video.level]];
+ }
+ }
+ [finalString appendString: @"Encoder Options: " withAttributes:detailBoldAttr];
+ [finalString appendString: encoderPresetInfo withAttributes:detailAttr];
+ [finalString appendString:@"\n" withAttributes:detailAttr];
+ }
+ else
+ {
+ // we are using libavcodec
+ NSString *lavcInfo = @"";
+ if (self.video.videoOptionExtra.length)
+ {
+ lavcInfo = [lavcInfo stringByAppendingString:self.video.videoOptionExtra];
+ }
+ else
+ {
+ lavcInfo = [lavcInfo stringByAppendingString: @"default settings"];
+ }
+ [finalString appendString: @"Encoder Options: " withAttributes:detailBoldAttr];
+ [finalString appendString: lavcInfo withAttributes:detailAttr];
+ [finalString appendString:@"\n" withAttributes:detailAttr];
+ }
+
+
+ // Seventh Line Audio Details
+ int audioDetailCount = 0;
+ for (NSString *anAudioDetail in audioDetails) {
+ audioDetailCount++;
+ if (anAudioDetail.length) {
+ [finalString appendString: [NSString stringWithFormat: @"Audio Track %d ", audioDetailCount] withAttributes: detailBoldAttr];
+ [finalString appendString: anAudioDetail withAttributes: detailAttr];
+ [finalString appendString: @"\n" withAttributes: detailAttr];
+ }
+ }
+
+ // Eigth Line Auto Passthru Details
+ // only print Auto Passthru settings if we have an Auro Passthru output track
+ if (autoPassthruPresent == YES)
+ {
+ NSString *autoPassthruFallback = @"", *autoPassthruCodecs = @"";
+ HBAudioDefaults *audioDefaults = self.audio.defaults;
+ autoPassthruFallback = [autoPassthruFallback stringByAppendingString:@(hb_audio_encoder_get_name(audioDefaults.encoderFallback))];
+ if (audioDefaults.allowAACPassthru)
+ {
+ autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@"AAC"];
+ }
+ if (audioDefaults.allowAC3Passthru)
+ {
+ if (autoPassthruCodecs.length)
+ {
+ autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@", "];
+ }
+ autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@"AC3"];
+ }
+ if (audioDefaults.allowDTSHDPassthru)
+ {
+ if (autoPassthruCodecs.length)
+ {
+ autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@", "];
+ }
+ autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@"DTS-HD"];
+ }
+ if (audioDefaults.allowDTSPassthru)
+ {
+ if (autoPassthruCodecs.length)
+ {
+ autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@", "];
+ }
+ autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@"DTS"];
+ }
+ if (audioDefaults.allowMP3Passthru)
+ {
+ if (autoPassthruCodecs.length)
+ {
+ autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@", "];
+ }
+ autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@"MP3"];
+ }
+ [finalString appendString: @"Auto Passthru Codecs: " withAttributes: detailBoldAttr];
+ if (autoPassthruCodecs.length)
+ {
+ [finalString appendString: autoPassthruCodecs withAttributes: detailAttr];
+ }
+ else
+ {
+ [finalString appendString: @"None" withAttributes: detailAttr];
+ }
+ [finalString appendString: @"\n" withAttributes: detailAttr];
+ [finalString appendString: @"Auto Passthru Fallback: " withAttributes: detailBoldAttr];
+ [finalString appendString: autoPassthruFallback withAttributes: detailAttr];
+ [finalString appendString: @"\n" withAttributes: detailAttr];
+ }
+
+ // Ninth Line Subtitle Details
+ int i = 0;
+ for (NSDictionary *track in self.subtitles.tracks)
+ {
+ // Ignore the none track.
+ if (i == self.subtitles.tracks.count - 1)
+ {
+ continue;
+ }
+
+ /* remember that index 0 of Subtitles can contain "Foreign Audio Search*/
+ [finalString appendString: @"Subtitle: " withAttributes:detailBoldAttr];
+ [finalString appendString: track[@"keySubTrackName"] withAttributes:detailAttr];
+ if ([track[@"keySubTrackForced"] intValue] == 1)
+ {
+ [finalString appendString: @" - Forced Only" withAttributes:detailAttr];
+ }
+ if ([track[@"keySubTrackBurned"] intValue] == 1)
+ {
+ [finalString appendString: @" - Burned In" withAttributes:detailAttr];
+ }
+ if ([track[@"keySubTrackDefault"] intValue] == 1)
+ {
+ [finalString appendString: @" - Default" withAttributes:detailAttr];
+ }
+ [finalString appendString:@"\n" withAttributes:detailAttr];
+ i++;
+ }
+ }
+
+ return [finalString autorelease];
+}
+
@end
@implementation HBContainerTransformer
diff --git a/macosx/HBJob.h b/macosx/HBJob.h
index d7e4f14c7..26a004f95 100644
--- a/macosx/HBJob.h
+++ b/macosx/HBJob.h
@@ -49,7 +49,7 @@ typedef NS_ENUM(NSUInteger, HBJobState){
@property (nonatomic, readwrite, assign) HBTitle *title;
@property (nonatomic, readonly) int titleIdx;
-@property (nonatomic, readwrite) int pidId;
+@property (nonatomic, readonly) NSString *uuid;
/**
* The file URL of the source.
diff --git a/macosx/HBJob.m b/macosx/HBJob.m
index d23bb6039..c55e99fa8 100644
--- a/macosx/HBJob.m
+++ b/macosx/HBJob.m
@@ -44,6 +44,8 @@ NSString *HBContainerChangedNotification = @"HBContainerChangedNotificatio
_chapterTitles = [title.chapters mutableCopy];
+ _uuid = [[[NSUUID UUID] UUIDString] retain];
+
[self applyPreset:preset];
}
@@ -142,6 +144,8 @@ NSString *HBContainerChangedNotification = @"HBContainerChangedNotificatio
[_chapterTitles release];
+ [_uuid release];
+
[super dealloc];
}
@@ -156,7 +160,7 @@ NSString *HBContainerChangedNotification = @"HBContainerChangedNotificatio
copy->_state = HBJobStateReady;
copy->_presetName = [_presetName copy];
copy->_titleIdx = _titleIdx;
- copy->_pidId = _pidId;
+ copy->_uuid = [[[NSUUID UUID] UUIDString] retain];
copy->_fileURL = [_fileURL copy];
copy->_destURL = [_destURL copy];
@@ -192,7 +196,7 @@ NSString *HBContainerChangedNotification = @"HBContainerChangedNotificatio
encodeInt(_state);
encodeObject(_presetName);
encodeInt(_titleIdx);
- encodeInt(_pidId);
+ encodeObject(_uuid);
encodeObject(_fileURL);
encodeObject(_destURL);
@@ -221,7 +225,7 @@ NSString *HBContainerChangedNotification = @"HBContainerChangedNotificatio
decodeInt(_state);
decodeObject(_presetName);
decodeInt(_titleIdx);
- decodeInt(_pidId);
+ decodeObject(_uuid);
decodeObject(_fileURL);
decodeObject(_destURL);
diff --git a/macosx/HBPreviewGenerator.m b/macosx/HBPreviewGenerator.m
index 51c0fd125..88ad2c320 100644
--- a/macosx/HBPreviewGenerator.m
+++ b/macosx/HBPreviewGenerator.m
@@ -27,8 +27,6 @@ typedef enum EncodeState : NSUInteger {
@property (nonatomic, readonly) HBJob *job;
@property (nonatomic) HBCore *core;
-@property (nonatomic, getter=isCancelled) BOOL cancelled;
-
@property (nonatomic, retain) NSURL *fileURL;
@@ -243,64 +241,43 @@ typedef enum EncodeState : NSUInteger {
int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue];
self.core = [[[HBCore alloc] initWithLoggingLevel:loggingLevel] autorelease];
self.core.name = @"PreviewCore";
- [self registerCoreNotifications];
// lets go ahead and send it off to libhb
hb_add(self.core.hb_handle, job);
hb_job_close(&job);
// start the actual encode
- [self.core start];
-
- return YES;
-}
-
-/**
- * Cancels the encoding process
- */
-- (void) cancel
-{
- if (self.core.state == HBStateWorking || self.core.state == HBStatePaused)
- {
- [self.core stop];
- self.cancelled = YES;
- }
-}
-
-/**
- * Registers for notifications from HBCore.
- */
-- (void) registerCoreNotifications
-{
- NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreWorkingNotification object:self.core queue:mainQueue usingBlock:^(NSNotification *note) {
- hb_state_t s = *(self.core.hb_state);
-
- NSMutableString *info = [NSMutableString stringWithFormat: @"Encoding preview: %.2f %%", 100.0 * s.param.working.progress];
+ [self.core startProgressHandler:^(HBState state, hb_state_t hb_state) {
+ switch (state) {
+ case HBStateWorking:
+ {
+ NSMutableString *info = [NSMutableString stringWithFormat: @"Encoding preview: %.2f %%", 100.0 * hb_state.param.working.progress];
+
+ if (hb_state.param.working.seconds > -1)
+ {
+ [info appendFormat:@" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)",
+ hb_state.param.working.rate_cur, hb_state.param.working.rate_avg, hb_state.param.working.hours,
+ hb_state.param.working.minutes, hb_state.param.working.seconds];
+ }
+
+ double progress = 100.0 * hb_state.param.working.progress;
+
+ [self.delegate updateProgress:progress info:info];
+ break;
+ }
+ case HBStateMuxing:
+ [self.delegate updateProgress:100.0 info:@"Muxing Preview…"];
+ break;
- if (s.param.working.seconds > -1)
- {
- [info appendFormat:@" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)",
- s.param.working.rate_cur, s.param.working.rate_avg, s.param.working.hours,
- s.param.working.minutes, s.param.working.seconds];
+ default:
+ break;
}
-
- double progress = 100.0 * s.param.working.progress;
-
- [self.delegate updateProgress:progress info:info];
- }];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreMuxingNotification object:self.core queue:mainQueue usingBlock:^(NSNotification *note) {
- [self.delegate updateProgress:100.0 info:@"Muxing Preview…"];
- }];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:HBCoreWorkDoneNotification object:self.core queue:mainQueue usingBlock:^(NSNotification *note) {
- [self.core stop];
+ }
+ completationHandler:^(BOOL success) {
self.core = nil;
- /* Encode done, call the delegate and close libhb handle */
- if (!self.isCancelled)
+ // Encode done, call the delegate and close libhb handle
+ if (success)
{
[self.delegate didCreateMovieAtURL:self.fileURL];
}
@@ -308,18 +285,27 @@ typedef enum EncodeState : NSUInteger {
{
[self.delegate didCancelMovieCreation];
}
+ }];
- self.cancelled = NO;
+ return YES;
+}
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }];
+/**
+ * Cancels the encoding process
+ */
+- (void) cancel
+{
+ if (self.core.state == HBStateWorking || self.core.state == HBStatePaused)
+ {
+ [self.core cancelEncode];
+ }
}
#pragma mark -
- (void) dealloc
{
- [self.core stop];
+ [self.core cancelEncode];
[_core release];
_core = nil;
diff --git a/macosx/HBQueueController.h b/macosx/HBQueueController.h
index 41f76698b..87d114df5 100644
--- a/macosx/HBQueueController.h
+++ b/macosx/HBQueueController.h
@@ -5,19 +5,34 @@
It may be used under the terms of the GNU General Public License. */
#import <Cocoa/Cocoa.h>
+#import <Growl/Growl.h>
@class HBController;
+@class HBOutputPanelController;
@class HBCore;
+@class HBJob;
-@interface HBQueueController : NSWindowController <NSToolbarDelegate, NSWindowDelegate>
+@interface HBQueueController : NSWindowController <NSToolbarDelegate, NSWindowDelegate, GrowlApplicationBridgeDelegate>
-- (void)setPidNum: (int)myPidnum;
-- (void)setCore: (HBCore *)core;
-- (void)setHBController: (HBController *)controller;
+/// The HBCore used for encoding.
+@property (nonatomic, readonly) HBCore *core;
-- (void)setQueueArray: (NSMutableArray *)QueueFileArray;
-- (void)setQueueStatusString: (NSString *)statusString;
+@property (nonatomic, assign) HBController *controller;
+@property (nonatomic, assign) HBOutputPanelController *outputPanel;
-- (IBAction)showQueueWindow: (id)sender;
+@property (nonatomic, readonly) NSUInteger count;
+@property (nonatomic, readonly) NSUInteger pendingItemsCount;
+@property (nonatomic, readonly) NSUInteger workingItemsCount;
+
+- (void)addJob:(HBJob *)item;
+- (void)addJobsFromArray:(NSArray *)items;
+
+- (BOOL)jobExistAtURL:(NSURL *)url;
+
+- (void)removeAllJobs;
+- (void)setEncodingJobsAsPending;
+
+- (IBAction)rip:(id)sender;
+- (IBAction)cancel:(id)sender;
@end
diff --git a/macosx/HBQueueController.mm b/macosx/HBQueueController.mm
index 542b2ba33..9ca4137bf 100644
--- a/macosx/HBQueueController.mm
+++ b/macosx/HBQueueController.mm
@@ -1,3 +1,4 @@
+
/* HBQueueController
This file is part of the HandBrake source code.
@@ -5,92 +6,53 @@
It may be used under the terms of the GNU General Public License. */
#import "HBQueueController.h"
+
#import "HBCore.h"
#import "Controller.h"
+#import "HBOutputPanelController.h"
#import "HBQueueOutlineView.h"
-#import "HBImageAndTextCell.h"
#import "HBUtilities.h"
#import "HBJob.h"
-#import "HBAudioDefaults.h"
-#import "HBAudioTrack.h"
+#import "HBJob+UIAdditions.h"
-#import "HBPicture+UIAdditions.h"
-#import "HBFilters+UIAdditions.h"
+#import "HBDistributedArray.h"
-#define HB_ROW_HEIGHT_TITLE_ONLY 17.0
+#import "HBDockTile.h"
// Pasteboard type for or drag operations
-#define DragDropSimplePboardType @"HBQueueCustomOutlineViewPboardType"
+#define DragDropSimplePboardType @"HBQueueCustomOutlineViewPboardType"
-#pragma mark -
+// DockTile update freqency in total percent increment
+#define dockTileUpdateFrequency 0.1f
-//------------------------------------------------------------------------------------
-// NSMutableAttributedString (HBAdditions)
-//------------------------------------------------------------------------------------
-
-@interface NSMutableAttributedString (HBAdditions)
-- (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary;
-@end
-
-@implementation NSMutableAttributedString (HBAdditions)
-- (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary
-{
- NSAttributedString *s = [[[NSAttributedString alloc] initWithString:aString
- attributes:aDictionary] autorelease];
- [self appendAttributedString:s];
-}
-@end
-
-#pragma mark -
+#define HB_ROW_HEIGHT_TITLE_ONLY 17.0
@interface HBQueueController () <HBQueueOutlineViewDelegate>
-{
- HBController *fHBController; // reference to HBController
- NSMutableArray *fJobGroups; // mirror image of the queue array from controller.mm
-
- int pidNum; // Records the PID number from HBController for this instance
- int fEncodingQueueItem; // corresponds to the index of fJobGroups encoding item
- int fPendingCount; // Number of various kinds of job groups in fJobGroups.
- int fWorkingCount;
- NSMutableIndexSet *fSavedExpandedItems; // used by save/restoreOutlineViewState to preserve which items are expanded
- NSMutableIndexSet *fSavedSelectedItems; // used by save/restoreOutlineViewState to preserve which items are selected
- NSMutableDictionary *descriptions;
+@property (nonatomic, readonly) HBDockTile *dockTile;
+@property (nonatomic, readwrite) double dockIconProgress;
- NSTimer *fAnimationTimer; // animates the icon of the current job in the queue outline view
- int fAnimationIndex; // used to generate name of image used to animate the current job in the queue outline view
+@property (assign) IBOutlet NSTextField *progressTextField;
+@property (assign) IBOutlet NSTextField *countTextField;
+@property (assign) IBOutlet HBQueueOutlineView *outlineView;
- IBOutlet NSTextField *fProgressTextField;
+@property (nonatomic, readonly) NSMutableDictionary *descriptions;
- IBOutlet HBQueueOutlineView *fOutlineView;
- IBOutlet NSTextField *fQueueCountField;
- NSArray *fDraggedNodes;
+@property (nonatomic, readonly) HBDistributedArray *jobs;
+@property (nonatomic, retain) HBJob *currentJob;
- // Text Styles
- NSMutableParagraphStyle *ps;
- NSDictionary *detailAttr;
- NSDictionary *detailBoldAttr;
- NSDictionary *titleAttr;
- NSDictionary *shortHeightAttr;
-}
-
-@property (nonatomic, readonly) HBCore *queueCore;
+@property (nonatomic, readwrite) NSUInteger pendingItemsCount;
+@property (nonatomic, readwrite) NSUInteger workingItemsCount;
-/* control encodes in the window */
-- (IBAction)removeSelectedQueueItem: (id)sender;
-- (IBAction)revealSelectedQueueItem: (id)sender;
-- (IBAction)editSelectedQueueItem: (id)sender;
+@property (nonatomic, retain) NSArray *dragNodesArray;
@end
@implementation HBQueueController
-//------------------------------------------------------------------------------------
-// init
-//------------------------------------------------------------------------------------
-- (id)init
+- (instancetype)init
{
if (self = [super initWithWindowNibName:@"Queue"])
{
@@ -101,267 +63,743 @@
// If/when we switch to using bindings, this can probably go away.
[self window];
- // Our defaults
- [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"QueueWindowIsOpen": @"NO"}];
+ _descriptions = [[NSMutableDictionary alloc] init];
+
+ // Workaround to avoid a bug in Snow Leopard
+ // we can switch back to [[NSApplication sharedApplication] applicationIconImage]
+ // when we won't support it anymore.
+ NSImage *appIcon = [NSImage imageNamed:@"HandBrake"];
+ [appIcon setSize:NSMakeSize(1024, 1024)];
+
+ // Load the dockTile and instiante initial text fields
+ _dockTile = [[HBDockTile alloc] initWithDockTile:[[NSApplication sharedApplication] dockTile]
+ image:appIcon];
- fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain];
- descriptions = [[NSMutableDictionary alloc] init];
+ int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue];
- [self initStyles];
+ // Init a separate instance of libhb for the queue
+ _core = [[HBCore alloc] initWithLoggingLevel:loggingLevel];
+ _core.name = @"QueueCore";
+
+ [self loadQueueFile];
}
return self;
}
-- (void)setQueueArray:(NSMutableArray *)QueueFileArray
+- (void)dealloc
+{
+ // clear the delegate so that windowWillClose is not attempted
+ if ([[self window] delegate] == self)
+ [[self window] setDelegate:nil];
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [_core release];
+ [_jobs release];
+ [_currentJob release];
+
+ [_dockTile release];
+ [_descriptions release];
+ [_dragNodesArray release];
+
+ [super dealloc];
+}
+
+- (void)windowDidLoad
+{
+ // lets setup our queue list outline view for drag and drop here
+ [self.outlineView registerForDraggedTypes:@[DragDropSimplePboardType]];
+ [self.outlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
+ [self.outlineView setVerticalMotionCanBeginDrag:YES];
+
+ // Don't allow autoresizing of main column, else the "delete" column will get
+ // pushed out of view.
+ [self.outlineView setAutoresizesOutlineColumn: NO];
+}
+
+- (void)windowWillClose:(NSNotification *)aNotification
+{
+ [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"];
+}
+
+/**
+ * Displays and brings the queue window to the front
+ */
+- (IBAction)showWindow:(id)sender
+{
+ [super showWindow:sender];
+ [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"];
+}
+
+#pragma mark Toolbar
+
+- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
+{
+ SEL action = theItem.action;
+ HBState s = self.core.state;
+
+ if (action == @selector(toggleStartCancel:))
+ {
+ if ((s == HBStatePaused) || (s == HBStateWorking) || (s == HBStateMuxing))
+ {
+ theItem.image = [NSImage imageNamed:@"stopencode"];
+ theItem.label = NSLocalizedString(@"Stop", @"");
+ theItem.toolTip = NSLocalizedString(@"Stop Encoding", @"");
+ return YES;
+ }
+ else
+ {
+ theItem.image = [NSImage imageNamed:@"encode"];
+ theItem.label = NSLocalizedString(@"Start", @"");
+ theItem.toolTip = NSLocalizedString(@"Start Encoding", @"");
+ return (self.pendingItemsCount > 0);
+ }
+ }
+
+ if (action == @selector(togglePauseResume:))
+ {
+ if (s == HBStatePaused)
+ {
+ theItem.image = [NSImage imageNamed:@"encode"];
+ theItem.label = NSLocalizedString(@"Resume", @"");
+ theItem.toolTip = NSLocalizedString(@"Resume Encoding", @"");
+ return YES;
+ }
+ else
+ {
+ theItem.image = [NSImage imageNamed:@"pauseencode"];
+ theItem.label = NSLocalizedString(@"Pause", @"");
+ theItem.toolTip = NSLocalizedString(@"Pause Encoding", @"");
+ return (s == HBStateWorking || s == HBStateMuxing);
+ }
+ }
+
+ return NO;
+}
+
+#pragma mark -
+#pragma mark Queue File
+
+- (void)reloadQueue
{
- [fJobGroups setArray:QueueFileArray];
- [descriptions removeAllObjects];
+ [self getQueueStats];
+ [self.outlineView reloadData];
+}
- [fOutlineView reloadData];
+- (void)loadQueueFile
+{
+ NSURL *queueURL = [NSURL fileURLWithPath:[[HBUtilities appSupportPath] stringByAppendingPathComponent:@"Queue/Queue.hbqueue"]];
+ _jobs = [[HBDistributedArray alloc] initWithURL:queueURL];
+
+ [self reloadQueue];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadQueue) name:HBDistributedArrayChanged object:_jobs];
+}
+
+- (void)addJob:(HBJob *)item
+{
+ [self.jobs beginTransaction];
+ [self.jobs addObject:item];
+ [self.jobs commit];
+
+ [self reloadQueue];
+}
+- (void)addJobsFromArray:(NSArray *)items;
+{
+ [self.jobs beginTransaction];
+ [self.jobs addObjectsFromArray:items];
+ [self.jobs commit];
+
+ [self reloadQueue];
+}
+
+- (BOOL)jobExistAtURL:(NSURL *)url
+{
+ for (HBJob *item in self.jobs)
+ {
+ if ([item.destURL isEqualTo:url])
+ {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (void)removeQueueItemAtIndex:(NSUInteger)index
+{
+ [self.jobs beginTransaction];
+ if (self.jobs.count > index)
+ {
+ [self.jobs removeObjectAtIndex:index];
+ }
+ [self.jobs commit];
+
+ [self reloadQueue];
+}
+
+/**
+ * Updates the queue status label.
+ */
+- (void)getQueueStats
+{
// lets get the stats on the status of the queue array
-
- fPendingCount = 0;
- fWorkingCount = 0;
-
- int i = 0;
- for (HBJob *job in fJobGroups)
- {
- if (job.state == HBJobStateWorking) // being encoded
- {
- fWorkingCount++;
- // we have an encoding job so, lets start the animation timer
- if (job.pidId == pidNum)
- {
- fEncodingQueueItem = i;
- }
- }
+ NSUInteger pendingCount = 0;
+ NSUInteger workingCount = 0;
+
+ for (HBJob *job in self.jobs)
+ {
+ if (job.state == HBJobStateWorking) // being encoded
+ {
+ workingCount++;
+ }
if (job.state == HBJobStateReady) // pending
{
- fPendingCount++;
- }
- i++;
- }
+ pendingCount++;
+ }
+ }
- // Set the queue status field in the queue window
- NSMutableString *string;
- if (fPendingCount == 0)
+ NSString *string;
+ if (pendingCount == 0)
{
- string = [NSMutableString stringWithFormat: NSLocalizedString( @"No encode pending", @"" )];
+ string = NSLocalizedString(@"No encode pending", @"");
}
- else if (fPendingCount == 1)
+ else if (pendingCount == 1)
{
- string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending", @"" ), fPendingCount];
+ string = [NSString stringWithFormat: NSLocalizedString(@"%d encode pending", @""), pendingCount];
}
else
{
- string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encodes pending", @"" ), fPendingCount];
+ string = [NSString stringWithFormat: NSLocalizedString(@"%d encodes pending", @""), pendingCount];
}
- [fQueueCountField setStringValue:string];
-
+
+ self.countTextField.stringValue = string;
+ [self.controller setQueueState:string];
+
+ self.pendingItemsCount = pendingCount;
+ self.workingItemsCount = workingCount;
}
-/* This method sets the status string in the queue window
- * and is called from Controller.mm (fHBController)
- * instead of running another timer here polling libhb
- * for encoding status
- */
-- (void)setQueueStatusString:(NSString *)statusString
+- (NSUInteger)count
{
- [fProgressTextField setStringValue:statusString];
+ return self.jobs.count;
}
-//------------------------------------------------------------------------------------
-// dealloc
-//------------------------------------------------------------------------------------
-- (void)dealloc
+/**
+ * Used to get the next pending queue item and return it if found
+ */
+- (HBJob *)getNextPendingQueueItem
{
- // clear the delegate so that windowWillClose is not attempted
- if( [[self window] delegate] == self )
- [[self window] setDelegate:nil];
+ for (HBJob *job in self.jobs)
+ {
+ if (job.state == HBJobStateReady)
+ {
+ return job;
+ }
+ }
+ return nil;
+}
- [fJobGroups release];
+/**
+ * This method will set any item marked as encoding back to pending
+ * currently used right after a queue reload
+ */
+- (void)setEncodingJobsAsPending
+{
+ [self.jobs beginTransaction];
+ for (HBJob *job in self.jobs)
+ {
+ // We want to keep any queue item that is pending or was previously being encoded
+ if (job.state == HBJobStateWorking)
+ {
+ job.state = HBJobStateReady;
+ }
+ }
+ [self.jobs commit];
- [fSavedExpandedItems release];
- [fSavedSelectedItems release];
+ [self reloadQueue];
+}
- [ps release];
- [detailAttr release];
- [detailBoldAttr release];
- [titleAttr release];
- [shortHeightAttr release];
+/**
+ * This method will clear the queue of any encodes that are not still pending
+ * this includes both successfully completed encodes as well as cancelled encodes
+ */
+- (void)clearEncodedJobs
+{
+ [self.jobs beginTransaction];
+ NSMutableArray *encodedJobs = [NSMutableArray array];
+ for (HBJob *job in self.jobs)
+ {
+ if (job.state == HBJobStateCompleted || job.state == HBJobStateCanceled)
+ {
+ [encodedJobs addObject:job];
+ }
+ }
- [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [self.jobs removeObjectsInArray:encodedJobs];
+ [self.jobs commit];
- [super dealloc];
+ [self reloadQueue];
}
-//------------------------------------------------------------------------------------
-// Receive HB handle
-//------------------------------------------------------------------------------------
-- (void)setCore: (HBCore *)core
+/**
+ * This method will clear the queue of all encodes. effectively creating an empty queue
+ */
+- (void)removeAllJobs
{
- _queueCore = core;
-}
+ [self.jobs beginTransaction];
+ [self.jobs removeAllObjects];
+ [self.jobs commit];
-//------------------------------------------------------------------------------------
-// Receive HBController
-//------------------------------------------------------------------------------------
-- (void)setHBController: (HBController *)controller
-{
- fHBController = controller;
+ [self reloadQueue];
}
-- (void)setPidNum: (int)myPidnum
+/**
+ * this is actually called from the queue controller to modify the queue array and return it back to the queue controller
+ */
+- (void)moveObjectsInQueueArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
{
- pidNum = myPidnum;
- [HBUtilities writeToActivityLog: "HBQueueController : My Pidnum is %d", pidNum];
+ [self.jobs beginTransaction];
+
+ NSUInteger index = [indexSet lastIndex];
+ NSUInteger aboveInsertIndexCount = 0;
+
+ NSUInteger removeIndex;
+
+ if (index >= insertIndex)
+ {
+ removeIndex = index + aboveInsertIndexCount;
+ aboveInsertIndexCount++;
+ }
+ else
+ {
+ removeIndex = index;
+ insertIndex--;
+ }
+
+ id object = [self.jobs[removeIndex] retain];
+ [self.jobs removeObjectAtIndex:removeIndex];
+ [self.jobs insertObject:object atIndex:insertIndex];
+ [object release];
+
+ // We save all of the Queue data here
+ // and it also gets sent back to the queue controller
+ [self.jobs commit];
+ [self reloadQueue];
}
#pragma mark -
+#pragma mark Queue Job Processing
-//------------------------------------------------------------------------------------
-// Displays and brings the queue window to the front
-//------------------------------------------------------------------------------------
-- (IBAction) showQueueWindow: (id)sender
+/**
+ * Starts the queue
+ */
+- (void)encodeNextQueueItem
{
- [self showWindow:sender];
- [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"];
- [self startAnimatingCurrentWorkingEncodeInQueue];
+ // Check to see if there are any more pending items in the queue
+ HBJob *nextJob = [self getNextPendingQueueItem];
+
+ // If we still have more pending items in our queue, lets go to the next one
+ if (nextJob)
+ {
+ self.currentJob = nextJob;
+ // now we mark the queue item as working so another instance can not come along and try to scan it while we are scanning
+ self.currentJob.state = HBJobStateWorking;
+
+ // now we can go ahead and scan the new pending queue item
+ [self performScan:self.currentJob.fileURL titleIdx:self.currentJob.titleIdx];
+ }
+ else
+ {
+ self.currentJob = nil;
+
+ [HBUtilities writeToActivityLog:"Queue Done, there are no more pending encodes"];
+
+ // Since there are no more items to encode, go to queueCompletedAlerts
+ // for user specified alerts after queue completed
+ [self queueCompletedAlerts];
+ }
}
-//------------------------------------------------------------------------------------
-// windowDidLoad
-//------------------------------------------------------------------------------------
-- (void)windowDidLoad
+- (void)encodeCompleted
{
- /* lets setup our queue list outline view for drag and drop here */
- [fOutlineView registerForDraggedTypes: @[DragDropSimplePboardType] ];
- [fOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
- [fOutlineView setVerticalMotionCanBeginDrag: YES];
+ // Since we are done with this encode, tell output to stop writing to the
+ // individual encode log.
+ [self.outputPanel endEncodeLog];
- // Don't allow autoresizing of main column, else the "delete" column will get
- // pushed out of view.
- [fOutlineView setAutoresizesOutlineColumn: NO];
+ // Check to see if the encode state has not been cancelled
+ // to determine if we should check for encode done notifications.
+ if (self.currentJob.state != HBJobStateCanceled)
+ {
+ // Both the Growl Alert and Sending to tagger can be done as encodes roll off the queue
+ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] ||
+ [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
+ {
+ // If Play System Alert has been selected in Preferences
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES)
+ {
+ NSBeep();
+ }
+ [self showGrowlDoneNotification:self.currentJob.destURL];
+ }
+
+ // Mark the encode just finished as done
+ self.currentJob.state = HBJobStateCompleted;
+
+ // Send to tagger
+ [self sendToExternalApp:self.currentJob.destURL];
+ }
+
+ self.currentJob = nil;
+
+ // since we have successfully completed an encode, we go to the next
+ [self encodeNextQueueItem];
+
+ [self.window.toolbar validateVisibleItems];
+ [self reloadQueue];
}
-//------------------------------------------------------------------------------------
-// windowWillClose
-//------------------------------------------------------------------------------------
-- (void)windowWillClose:(NSNotification *)aNotification
+/**
+ * Here we actually tell hb_scan to perform the source scan, using the path to source and title number
+ */
+- (void)performScan:(NSURL *)scanURL titleIdx:(NSInteger)index
{
- [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"];
- [self stopAnimatingCurrentJobGroupInQueue];
+ // Tell HB to output a new activity log file for this encode
+ [self.outputPanel startEncodeLog:self.currentJob.destURL];
+
+ // Only scan 10 previews before an encode - additional previews are
+ // only useful for autocrop and static previews, which are already taken care of at this point
+ [self.core scanURL:scanURL
+ titleIndex:index
+ previews:10
+ minDuration:0
+ progressHandler:^(HBState state, hb_state_t hb_state) {
+ NSMutableString *status = [NSMutableString stringWithFormat:
+ NSLocalizedString( @"Queue Scanning title %d of %d…", @"" ),
+ hb_state.param.scanning.title_cur, hb_state.param.scanning.title_count];
+ if (hb_state.param.scanning.preview_cur)
+ {
+ [status appendFormat:@", preview %d…", hb_state.param.scanning.preview_cur];
+ }
+
+ self.progressTextField.stringValue = status;
+ [self.controller setQueueInfo:status progress:0 hidden:NO];
+ }
+ completationHandler:^(BOOL success) {
+ if (success)
+ {
+ [self doEncodeQueueItem];
+ }
+ else
+ {
+ [self.jobs beginTransaction];
+
+ self.currentJob.state = HBJobStateCanceled;
+ [self encodeCompleted];
+
+ [self.jobs commit];
+ [self reloadQueue];
+ }
+
+ [self.window.toolbar validateVisibleItems];
+ }];
}
-#pragma mark Toolbar
+/**
+ * This assumes that we have re-scanned and loaded up a new queue item to send to libhb
+ */
+- (void)doEncodeQueueItem
+{
+ // Reset the title in the job.
+ self.currentJob.title = self.core.titles[0];
+
+ // We should be all setup so let 'er rip
+ [self.core encodeJob:self.currentJob
+ progressHandler:^(HBState state, hb_state_t hb_state) {
+ NSMutableString *string = nil;
+ CGFloat progress = 0;
+ switch (state)
+ {
+ case HBStateSearching:
+ {
+ #define p hb_state.param.working
+ string = [NSMutableString stringWithFormat:
+ NSLocalizedString(@"Searching for start point… : %.2f %%", @""),
+ 100.0 * p.progress];
+
+ if (p.seconds > -1)
+ {
+ [string appendFormat:NSLocalizedString(@" (ETA %02dh%02dm%02ds)", @"" ), p.hours, p.minutes, p.seconds];
+ }
+ #undef p
+
+ break;
+ }
+ case HBStateWorking:
+ {
+ #define p hb_state.param.working
+ NSString *pass_desc = @"";
+ if (p.job_cur == 1 && p.job_count > 1)
+ {
+ if ([self.currentJob.subtitles.tracks.firstObject[keySubTrackIndex] intValue] == -1)
+ {
+ pass_desc = NSLocalizedString(@"(subtitle scan)", @"");
+ }
+ }
+
+ if (pass_desc.length)
+ {
+ string = [NSMutableString stringWithFormat:
+ NSLocalizedString(@"Encoding: %@ \nPass %d %@ of %d, %.2f %%", @""),
+ self.currentJob.destURL.lastPathComponent,
+ p.job_cur, pass_desc, p.job_count, 100.0 * p.progress];
+ }
+ else
+ {
+ string = [NSMutableString stringWithFormat:
+ NSLocalizedString(@"Encoding: %@ \nPass %d of %d, %.2f %%", @""),
+ self.currentJob.destURL.lastPathComponent,
+ p.job_cur, p.job_count, 100.0 * p.progress];
+ }
+
+ if (p.seconds > -1)
+ {
+ if (p.rate_cur > 0.0)
+ {
+ [string appendFormat:
+ NSLocalizedString(@" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @""),
+ p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
+ }
+ else
+ {
+ [string appendFormat:
+ NSLocalizedString(@" (ETA %02dh%02dm%02ds)", @""),
+ p.hours, p.minutes, p.seconds];
+ }
+ }
+
+ progress = (p.progress + p.job_cur - 1) / p.job_count;
+
+ // Update dock icon
+ if (self.dockIconProgress < 100.0 * progress)
+ {
+ // ETA format is [XX]X:XX:XX when ETA is greater than one hour
+ // [X]X:XX when ETA is greater than 0 (minutes or seconds)
+ // When these conditions doesn't applied (eg. when ETA is undefined)
+ // we show just a tilde (~)
+
+ NSString *etaStr = @"";
+ if (p.hours > 0)
+ etaStr = [NSString stringWithFormat:@"%d:%02d:%02d", p.hours, p.minutes, p.seconds];
+ else if (p.minutes > 0 || p.seconds > 0)
+ etaStr = [NSString stringWithFormat:@"%d:%02d", p.minutes, p.seconds];
+ else
+ etaStr = @"~";
+
+ [self.dockTile updateDockIcon:progress withETA:etaStr];
+
+ self.dockIconProgress += dockTileUpdateFrequency;
+ }
+ #undef p
+
+ break;
+ }
+ case HBStateMuxing:
+ {
+ string = [NSMutableString stringWithString:NSLocalizedString(@"Muxing…", @"")];
+
+ // Update dock icon
+ [self.dockTile updateDockIcon:1.0 withETA:@""];
+
+ break;
+ }
+ case HBStatePaused:
+ {
+ string = [NSMutableString stringWithString:NSLocalizedString(@"Paused", @"")];
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Update text field
+ self.progressTextField.stringValue = string;
+ [self.controller setQueueInfo:string progress:progress * 100.0 hidden:NO];
+ }
+ completationHandler:^(BOOL success) {
+ NSString *info = NSLocalizedString(@"Encode Finished.", @"");
+
+ self.progressTextField.stringValue = info;
+ [self.controller setQueueInfo:info progress:100.0 hidden:YES];
+
+ // Restore dock icon
+ [self.dockTile updateDockIcon:-1.0 withETA:@""];
+ self.dockIconProgress = 0;
+
+ [self.jobs beginTransaction];
+
+ [self encodeCompleted];
+
+ [self.jobs commit];
+ [self reloadQueue];
+ }];
+
+ // We are done using the title, remove it from the job
+ self.currentJob.title = nil;
+}
+
+/**
+ * Cancels and deletes the current job and starts processing the next in queue.
+ */
+- (void)doCancelCurrentJob
+{
+ [self.jobs beginTransaction];
+
+ self.currentJob.state = HBJobStateCanceled;
+ [self.core cancelEncode];
+
+ [self.jobs commit];
+ [self reloadQueue];
+}
-//------------------------------------------------------------------------------------
-// validateToolbarItem:
-//------------------------------------------------------------------------------------
-- (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
+/**
+ * Cancels and deletes the current job and stops libhb from processing the remaining encodes.
+ */
+- (void)doCancelCurrentJobAndStop
{
- // Optional method: This message is sent to us since we are the target of some
- // toolbar item actions.
+ [self.jobs beginTransaction];
- if (!self.queueCore) return NO;
+ self.currentJob.state = HBJobStateCanceled;
- BOOL enable = NO;
- HBState s = self.queueCore.state;
+ [self.core cancelEncode];
- if ([[toolbarItem itemIdentifier] isEqualToString:@"HBQueueStartCancelToolbarIdentifier"])
- {
- if ((s == HBStatePaused) || (s == HBStateWorking) || (s == HBStateMuxing))
- {
- enable = YES;
- [toolbarItem setImage:[NSImage imageNamed: @"stopencode"]];
- [toolbarItem setLabel: @"Stop"];
- [toolbarItem setToolTip: @"Stop Encoding"];
- }
+ [self.jobs commit];
+ [self reloadQueue];
+}
- else if (fPendingCount > 0)
- {
- enable = YES;
- [toolbarItem setImage:[NSImage imageNamed: @"encode"]];
- [toolbarItem setLabel: @"Start"];
- [toolbarItem setToolTip: @"Start Encoding"];
- }
+#pragma mark - Encode Done Actions
- else
+#define SERVICE_NAME @"Encode Done"
+
+/**
+ * Register a test notification and make
+ * it enabled by default
+ */
+- (NSDictionary *)registrationDictionaryForGrowl
+{
+ return @{GROWL_NOTIFICATIONS_ALL: @[SERVICE_NAME],
+ GROWL_NOTIFICATIONS_DEFAULT: @[SERVICE_NAME]};
+}
+
+- (void)showGrowlDoneNotification:(NSURL *)fileURL
+{
+ // This end of encode action is called as each encode rolls off of the queue
+ // Setup the Growl stuff
+ NSString *growlMssg = [NSString stringWithFormat:@"your HandBrake encode %@ is done!", fileURL.lastPathComponent];
+ [GrowlApplicationBridge notifyWithTitle:@"Put down that cocktail…"
+ description:growlMssg
+ notificationName:SERVICE_NAME
+ iconData:nil
+ priority:0
+ isSticky:1
+ clickContext:nil];
+}
+
+- (void)sendToExternalApp:(NSURL *)fileURL
+{
+ // This end of encode action is called as each encode rolls off of the queue
+ if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)
+ {
+ NSString *sendToApp = [[NSUserDefaults standardUserDefaults] objectForKey:@"SendCompletedEncodeToApp"];
+ if (![sendToApp isEqualToString:@"None"])
{
- enable = NO;
- [toolbarItem setImage:[NSImage imageNamed: @"encode"]];
- [toolbarItem setLabel: @"Start"];
- [toolbarItem setToolTip: @"Start Encoding"];
+ [HBUtilities writeToActivityLog: "trying to send encode to: %s", [sendToApp UTF8String]];
+ NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@%@%@", @"tell application \"",sendToApp,@"\" to open (POSIX file \"", fileURL.path, @"\")"]];
+ [myScript executeAndReturnError: nil];
+ [myScript release];
}
}
+}
- if ([[toolbarItem itemIdentifier] isEqualToString:@"HBQueuePauseResumeToolbarIdentifier"])
+- (void)queueCompletedAlerts
+{
+ // If Play System Alert has been selected in Preferences
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES)
{
- if (s == HBStatePaused)
- {
- enable = YES;
- [toolbarItem setImage:[NSImage imageNamed: @"encode"]];
- [toolbarItem setLabel: @"Resume"];
- [toolbarItem setToolTip: @"Resume Encoding"];
- }
+ NSBeep();
+ }
- else if ((s == HBStateWorking) || (s == HBStateMuxing))
- {
- enable = YES;
- [toolbarItem setImage:[NSImage imageNamed: @"pauseencode"]];
- [toolbarItem setLabel: @"Pause"];
- [toolbarItem setToolTip: @"Pause Encoding"];
- }
- else
- {
- enable = NO;
- [toolbarItem setImage:[NSImage imageNamed: @"pauseencode"]];
- [toolbarItem setLabel: @"Pause"];
- [toolbarItem setToolTip: @"Pause Encoding"];
- }
+ // If Alert Window or Window and Growl has been selected
+ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] ||
+ [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
+ {
+ // On Screen Notification
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:NSLocalizedString(@"Put down that cocktail…", @"")];
+ [alert setInformativeText:NSLocalizedString(@"Your HandBrake queue is done!", @"")];
+ [NSApp requestUserAttention:NSCriticalRequest];
+ [alert runModal];
+ [alert release];
}
- return enable;
+ // If sleep has been selected
+ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
+ {
+ // Sleep
+ NSDictionary *errorDict;
+ NSAppleScript *scriptObject = [[NSAppleScript alloc] initWithSource:
+ @"tell application \"Finder\" to sleep"];
+ [scriptObject executeAndReturnError: &errorDict];
+ [scriptObject release];
+ }
+ // If Shutdown has been selected
+ if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"] )
+ {
+ // Shut Down
+ NSDictionary *errorDict;
+ NSAppleScript *scriptObject = [[NSAppleScript alloc] initWithSource:@"tell application \"Finder\" to shut down"];
+ [scriptObject executeAndReturnError: &errorDict];
+ [scriptObject release];
+ }
}
-#pragma mark -
-
-
-#pragma mark Queue Item Controls
+#pragma mark - Queue Item Controls
- (void)HB_deleteSelectionFromTableView:(NSTableView *)tableView
{
[self removeSelectedQueueItem:tableView];
}
-//------------------------------------------------------------------------------------
-// Delete encodes from the queue window and accompanying array
-// Also handling first cancelling the encode if in fact its currently encoding.
-//------------------------------------------------------------------------------------
-- (IBAction)removeSelectedQueueItem: (id)sender
+/**
+ * Delete encodes from the queue window and accompanying array
+ * Also handling first cancelling the encode if in fact its currently encoding.
+ */
+- (IBAction)removeSelectedQueueItem:(id)sender
{
- NSIndexSet *targetedRow = [fOutlineView targetedRowIndexes];
+ NSIndexSet *targetedRow = [self.outlineView targetedRowIndexes];
NSUInteger row = [targetedRow firstIndex];
if (row == NSNotFound)
+ {
return;
- /* if this is a currently encoding job, we need to be sure to alert the user,
- * to let them decide to cancel it first, then if they do, we can come back and
- * remove it */
-
- if ([fJobGroups[row] state] == HBJobStateWorking)
+ }
+
+ // if this is a currently encoding job, we need to be sure to alert the user,
+ // to let them decide to cancel it first, then if they do, we can come back and
+ // remove it
+ if (self.jobs[row] == self.currentJob)
{
- /* We pause the encode here so that it doesn't finish right after and then
- * screw up the sync while the window is open
- */
- [fHBController Pause:NULL];
- NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It ?", nil)];
+ // We pause the encode here so that it doesn't finish right after and then
+ // screw up the sync while the window is open
+ [self togglePauseResume:nil];
+
+ NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It?", nil)];
+
// Which window to attach the sheet to?
- NSWindow *docWindow = nil;
+ NSWindow *targetWindow = nil;
if ([sender respondsToSelector: @selector(window)])
{
- docWindow = [sender window];
+ targetWindow = [sender window];
}
NSAlert *alert = [[NSAlert alloc] init];
@@ -371,56 +809,55 @@
[alert addButtonWithTitle:NSLocalizedString(@"Stop Encoding and Delete", nil)];
[alert setAlertStyle:NSCriticalAlertStyle];
- [alert beginSheetModalForWindow:docWindow
+ [alert beginSheetModalForWindow:targetWindow
modalDelegate:self
didEndSelector:@selector(didDimissCancelCurrentJob:returnCode:contextInfo:)
- contextInfo:nil];
+ contextInfo:self.jobs[row]];
[alert release];
}
- else
- {
+ else if ([self.jobs[row] state] != HBJobStateWorking)
+ {
// since we are not a currently encoding item, we can just be removed
- [fHBController removeQueueFileItem:row];
+ [self removeQueueItemAtIndex:row];
}
}
-- (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
+- (void)didDimissCancelCurrentJob:(NSAlert *)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void *)contextInfo
{
- /* We resume encoding and perform the appropriate actions
- * Note: Pause: is a toggle type method based on hb's current
- * state, if it paused, it will resume encoding and vice versa.
- * In this case, we are paused from the calling window, so calling
- * [fHBController Pause:NULL]; Again will resume encoding
- */
- [fHBController Pause:NULL];
+ // We resume encoding and perform the appropriate actions
+ // Note: Pause: is a toggle type method based on hb's current
+ // state, if it paused, it will resume encoding and vice versa.
+ // In this case, we are paused from the calling window, so calling
+ // [self togglePauseResume:nil]; Again will resume encoding
+ [self togglePauseResume:nil];
+
if (returnCode == NSAlertSecondButtonReturn)
{
- /* We need to save the currently encoding item number first */
- int encodingItemToRemove = fEncodingQueueItem;
- /* Since we are encoding, we need to let fHBController Cancel this job
- * upon which it will move to the next one if there is one
- */
- [fHBController doCancelCurrentJob];
- /* Now, we can go ahead and remove the job we just cancelled since
- * we have its item number from above
- */
- [fHBController removeQueueFileItem:encodingItemToRemove];
+ // We need to save the currently encoding item number first
+ NSInteger index = [self.jobs indexOfObject:self.currentJob];
+ // Since we are encoding, we need to let fHBController Cancel this job
+ // upon which it will move to the next one if there is one
+ [self doCancelCurrentJob];
+ // Now, we can go ahead and remove the job we just cancelled since
+ // we have its item number from above
+ [self removeQueueItemAtIndex:index];
}
-
}
-//------------------------------------------------------------------------------------
-// Show the finished encode in the finder
-//------------------------------------------------------------------------------------
+/**
+ * Show the finished encode in the finder
+ */
- (IBAction)revealSelectedQueueItem: (id)sender
{
- NSIndexSet *targetedRow = [fOutlineView targetedRowIndexes];
+ NSIndexSet *targetedRow = [self.outlineView targetedRowIndexes];
NSInteger row = [targetedRow firstIndex];
if (row != NSNotFound)
{
while (row != NSNotFound)
{
- HBJob *queueItemToOpen = [fOutlineView itemAtRow:row];
+ HBJob *queueItemToOpen = [self.outlineView itemAtRow:row];
[[NSWorkspace sharedWorkspace] selectFile:queueItemToOpen.destURL.path inFileViewerRootedAtPath:nil];
row = [targetedRow indexGreaterThanIndex: row];
@@ -428,65 +865,177 @@
}
}
-//------------------------------------------------------------------------------------
-// Starts or cancels the processing of jobs depending on the current state
-//------------------------------------------------------------------------------------
-- (IBAction)toggleStartCancel: (id)sender
+- (void)remindUserOfSleepOrShutdown
{
- if (!self.queueCore) return;
+ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString:@"Put Computer To Sleep"])
+ {
+ // Warn that computer will sleep after encoding
+ NSBeep();
+ [NSApp requestUserAttention:NSCriticalRequest];
- HBState s = self.queueCore.state;
- if ((s == HBStatePaused) || (s == HBStateWorking) || (s == HBStateMuxing))
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:NSLocalizedString(@"The computer will sleep after encoding is done.", @"")];
+ [alert setInformativeText:NSLocalizedString(@"You have selected to sleep the computer after encoding. To turn off sleeping, go to the HandBrake preferences.", @"")];
+ [alert addButtonWithTitle:NSLocalizedString(@"OK", @"")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Preferences…",@"")];
+
+ NSInteger response = [alert runModal];
+ if (response == NSAlertSecondButtonReturn)
+ {
+ [self.controller showPreferencesWindow:nil];
+ }
+ [alert release];
+ }
+ else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString:@"Shut Down Computer"])
{
- [fHBController Cancel: self];
+ // Warn that computer will shut down after encoding
+ NSBeep();
+ [NSApp requestUserAttention:NSCriticalRequest];
+
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:NSLocalizedString(@"The computer will shut down after encoding is done.", @"")];
+ [alert setInformativeText:NSLocalizedString(@"You have selected to shut down the computer after encoding. To turn off shut down, go to the HandBrake preferences.", @"")];
+ [alert addButtonWithTitle:NSLocalizedString(@"OK", @"")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Preferences…", @"")];
+
+ NSInteger response = [alert runModal];
+ if (response == NSAlertSecondButtonReturn)
+ {
+ [self.controller showPreferencesWindow:nil];
+ }
+ [alert release];
}
- else if (fPendingCount > 0)
+}
+
+/**
+ * Rip: puts up an alert before ultimately calling doRip
+ */
+- (IBAction)rip:(id)sender
+{
+ // Rip or Cancel ?
+ if (self.core.state == HBStateWorking || self.core.state == HBStatePaused)
{
- [fHBController Rip: NULL];
+ [self cancel:sender];
}
+ // If there are pending jobs in the queue, then this is a rip the queue
+ else if (self.pendingItemsCount > 0)
+ {
+ // We check to see if we need to warn the user that the computer will go to sleep
+ // or shut down when encoding is finished
+ [self remindUserOfSleepOrShutdown];
+
+ [self.jobs beginTransaction];
+
+ [self encodeNextQueueItem];
+
+ [self.jobs commit];
+ [self reloadQueue];
+ }
+}
+
+/**
+ * Displays an alert asking user if the want to cancel encoding of current job.
+ * Cancel: returns immediately after posting the alert. Later, when the user
+ * acknowledges the alert, doCancelCurrentJob is called.
+ */
+- (IBAction)cancel:(id)sender
+{
+ [self.core pause];
+
+ // Which window to attach the sheet to?
+ NSWindow *window;
+ if ([sender respondsToSelector:@selector(window)])
+ {
+ window = [sender window];
+ }
+ else
+ {
+ window = self.window;
+ }
+
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)];
+ [alert setInformativeText:NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil)];
+ [alert addButtonWithTitle:NSLocalizedString(@"Continue Encoding", nil)];
+ [alert addButtonWithTitle:NSLocalizedString(@"Cancel Current and Stop", nil)];
+ [alert addButtonWithTitle:NSLocalizedString(@"Cancel Current and Continue", nil)];
+ [alert setAlertStyle:NSCriticalAlertStyle];
+
+ [alert beginSheetModalForWindow:window
+ modalDelegate:self
+ didEndSelector:@selector(didDimissCancel:returnCode:contextInfo:)
+ contextInfo:nil];
+ [alert release];
}
-//------------------------------------------------------------------------------------
-// Toggles the pause/resume state of libhb
-//------------------------------------------------------------------------------------
-- (IBAction)togglePauseResume: (id)sender
+- (void)didDimissCancel:(NSAlert *)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void *)contextInfo
{
- if (!self.queueCore) return;
+ [self.core resume];
- HBState s = self.queueCore.state;
+ if (returnCode == NSAlertSecondButtonReturn)
+ {
+ [self doCancelCurrentJobAndStop]; // <- this also stops libhb
+ }
+ else if (returnCode == NSAlertThirdButtonReturn)
+ {
+ [self doCancelCurrentJob];
+ }
+}
+
+/**
+ * Starts or cancels the processing of jobs depending on the current state
+ */
+- (IBAction)toggleStartCancel:(id)sender
+{
+ HBState s = self.core.state;
+ if ((s == HBStatePaused) || (s == HBStateWorking) || (s == HBStateMuxing))
+ {
+ [self cancel:self];
+ }
+ else if (self.pendingItemsCount > 0)
+ {
+ [self rip:self];
+ }
+}
+
+/**
+ * Toggles the pause/resume state of libhb
+ */
+- (IBAction)togglePauseResume:(id)sender
+{
+ HBState s = self.core.state;
if (s == HBStatePaused)
{
- [self.queueCore resume];
- [self startAnimatingCurrentWorkingEncodeInQueue];
+ [self.core resume];
}
- else if ((s == HBStateWorking) || (s == HBStateMuxing))
+ else if (s == HBStateWorking || s == HBStateMuxing)
{
- [self.queueCore pause];
- [self stopAnimatingCurrentJobGroupInQueue];
+ [self.core pause];
}
}
-//------------------------------------------------------------------------------------
-// Send the selected queue item back to the main window for rescan and possible edit.
-//------------------------------------------------------------------------------------
-- (IBAction)editSelectedQueueItem: (id)sender
+/**
+ * Send the selected queue item back to the main window for rescan and possible edit.
+ */
+- (IBAction)editSelectedQueueItem:(id)sender
{
- NSInteger row = [fOutlineView clickedRow];
+ NSInteger row = [self.outlineView clickedRow];
if (row == NSNotFound)
{
return;
}
- /* if this is a currently encoding job, we need to be sure to alert the user,
- * to let them decide to cancel it first, then if they do, we can come back and
- * remove it */
-
- HBJob *job = fJobGroups[row];
+ // if this is a currently encoding job, we need to be sure to alert the user,
+ // to let them decide to cancel it first, then if they do, we can come back and
+ // remove it
+ HBJob *job = self.jobs[row];
if (job.state == HBJobStateWorking)
{
// We pause the encode here so that it doesn't finish right after and then
// screw up the sync while the window is open
- [fHBController Pause:NULL];
+ [self togglePauseResume:nil];
NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It ?", nil)];
// Which window to attach the sheet to?
NSWindow *docWindow = nil;
@@ -505,115 +1054,29 @@
[alert beginSheetModalForWindow:docWindow
modalDelegate:self
didEndSelector:@selector(didDimissCancelCurrentJob:returnCode:contextInfo:)
- contextInfo:nil];
+ contextInfo:job];
[alert release];
}
else
{
- /* since we are not a currently encoding item, we can just be cancelled */
- [fHBController rescanQueueItemToMainWindow:row];
- }
-}
-
-
-#pragma mark -
-#pragma mark Animate Encoding Item
-
-//------------------------------------------------------------------------------------
-// Starts animating the job icon of the currently processing job in the queue outline
-// view.
-//------------------------------------------------------------------------------------
-- (void) startAnimatingCurrentWorkingEncodeInQueue
-{
- if (!fAnimationTimer)
- fAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0/12.0 // 1/12 because there are 6 images in the animation cycle
- target:self
- selector:@selector(animateWorkingEncodeInQueue:)
- userInfo:nil
- repeats:YES] retain];
-}
-
-//------------------------------------------------------------------------------------
-// If a job is currently processing, its job icon in the queue outline view is
-// animated to its next state.
-//------------------------------------------------------------------------------------
-- (void) animateWorkingEncodeInQueue:(NSTimer*)theTimer
-{
- if (fWorkingCount > 0)
- {
- fAnimationIndex++;
- fAnimationIndex %= 6; // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below.
- [self animateWorkingEncodeIconInQueue];
- }
-}
+ // since we are not a currently encoding item, we can just be cancelled
+ HBJob *item = [[[self.jobs[row] representedObject] copy] autorelease];
+ [self.controller rescanJobToMainWindow:item];
-/* We need to make sure we denote only working encodes even for multiple instances */
-- (void) animateWorkingEncodeIconInQueue
-{
- NSInteger row = fEncodingQueueItem; /// need to set to fEncodingQueueItem
- NSInteger col = [fOutlineView columnWithIdentifier: @"icon"];
- if (row != -1 && col != -1)
- {
- NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row];
- [fOutlineView setNeedsDisplayInRect: frame];
+ // Now that source is loaded and settings applied, delete the queue item from the queue
+ [self removeQueueItemAtIndex:row];
}
}
-//------------------------------------------------------------------------------------
-// Stops animating the job icon of the currently processing job in the queue outline
-// view.
-//------------------------------------------------------------------------------------
-- (void) stopAnimatingCurrentJobGroupInQueue
-{
- if (fAnimationTimer && [fAnimationTimer isValid])
- {
- [fAnimationTimer invalidate];
- [fAnimationTimer release];
- fAnimationTimer = nil;
- }
-}
-
-
-#pragma mark -
-
-- (void)moveObjectsInArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
-{
- NSUInteger index = [indexSet lastIndex];
- NSUInteger aboveInsertIndexCount = 0;
-
- while (index != NSNotFound)
- {
- NSUInteger removeIndex;
-
- if (index >= insertIndex)
- {
- removeIndex = index + aboveInsertIndexCount;
- aboveInsertIndexCount++;
- }
- else
- {
- removeIndex = index;
- insertIndex--;
- }
-
- id object = [array[removeIndex] retain];
- [array removeObjectAtIndex:removeIndex];
- [array insertObject:object atIndex:insertIndex];
- [object release];
-
- index = [indexSet indexLessThanIndex:index];
- }
-}
-
-
#pragma mark -
#pragma mark NSOutlineView delegate
-
- (id)outlineView:(NSOutlineView *)fOutlineView child:(NSInteger)index ofItem:(id)item
{
if (item == nil)
- return fJobGroups[index];
+ {
+ return self.jobs[index];
+ }
// We are only one level deep, so we can't be asked about children
NSAssert (NO, @"HBQueueController outlineView:child:ofItem: can't handle nested items.");
@@ -639,23 +1102,27 @@
// Our outline view has no levels, so number of children will be zero for all
// top-level items.
if (item == nil)
- return [fJobGroups count];
+ {
+ return [self.jobs count];
+ }
else
+ {
return 0;
+ }
}
- (void)outlineViewItemDidCollapse:(NSNotification *)notification
{
id item = [notification userInfo][@"NSObject"];
- NSInteger row = [fOutlineView rowForItem:item];
- [fOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
+ NSInteger row = [self.outlineView rowForItem:item];
+ [self.outlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
}
- (void)outlineViewItemDidExpand:(NSNotification *)notification
{
id item = [notification userInfo][@"NSObject"];
- NSInteger row = [fOutlineView rowForItem:item];
- [fOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
+ NSInteger row = [self.outlineView rowForItem:item];
+ [self.outlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
}
- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
@@ -685,403 +1152,24 @@
}
}
-- (void)initStyles
-{
- // Attributes
- ps = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
- [ps setHeadIndent: 40.0];
- [ps setParagraphSpacing: 1.0];
- [ps setTabStops:@[]]; // clear all tabs
- [ps addTabStop: [[[NSTextTab alloc] initWithType: NSLeftTabStopType location: 20.0] autorelease]];
-
- detailAttr = [@{NSFontAttributeName: [NSFont systemFontOfSize:10.0],
- NSParagraphStyleAttributeName: ps} retain];
-
- detailBoldAttr = [@{NSFontAttributeName: [NSFont boldSystemFontOfSize:10.0],
- NSParagraphStyleAttributeName: ps} retain];
-
- titleAttr = [@{NSFontAttributeName: [NSFont systemFontOfSize:[NSFont systemFontSize]],
- NSParagraphStyleAttributeName: ps} retain];
-
- shortHeightAttr = [@{NSFontAttributeName: [NSFont systemFontOfSize:2.0]} retain];
-}
-
- (id)outlineView:(NSOutlineView *)fOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
- if ([[tableColumn identifier] isEqualToString:@"desc"])
+ if ([tableColumn.identifier isEqualToString:@"desc"])
{
HBJob *job = item;
- if ([descriptions objectForKey:@(job.hash)])
- {
- return [descriptions objectForKey:@(job.hash)];
- }
-
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- /* Below should be put into a separate method but I am way too f'ing lazy right now */
- NSMutableAttributedString *finalString = [[NSMutableAttributedString alloc] initWithString: @""];
-
- /* First line, we should strip the destination path and just show the file name and add the title num and chapters (if any) */
- NSString *summaryInfo;
-
- NSString *titleString = [NSString stringWithFormat:@"Title %d", job.titleIdx];
-
- NSString *startStopString = @"";
- if (job.range.type == HBRangeTypeChapters)
- {
- // Start Stop is chapters
- startStopString = (job.range.chapterStart == job.range.chapterStop) ?
- [NSString stringWithFormat:@"Chapter %d", job.range.chapterStart] :
- [NSString stringWithFormat:@"Chapters %d through %d", job.range.chapterStart, job.range.chapterStop];
- }
- else if (job.range.type == HBRangeTypeSeconds)
+ if (self.descriptions[@(job.hash)])
{
- // Start Stop is seconds
- startStopString = [NSString stringWithFormat:@"Seconds %d through %d", job.range.secondsStart, job.range.secondsStop];
- }
- else if (job.range.type == HBRangeTypeFrames)
- {
- // Start Stop is Frames
- startStopString = [NSString stringWithFormat:@"Frames %d through %d", job.range.frameStart, job.range.frameStop];
- }
- NSString *passesString = @"";
- // check to see if our first subtitle track is Foreign Language Search, in which case there is an in depth scan
- if (job.subtitles.tracks.count && [job.subtitles.tracks[0][@"keySubTrackIndex"] intValue] == -1)
- {
- passesString = [passesString stringByAppendingString:@"1 Foreign Language Search Pass - "];
- }
- if (job.video.qualityType == 1 || job.video.twoPass == NO)
- {
- passesString = [passesString stringByAppendingString:@"1 Video Pass"];
- }
- else
- {
- if (job.video.turboTwoPass == YES)
- {
- passesString = [passesString stringByAppendingString:@"2 Video Passes First Turbo"];
- }
- else
- {
- passesString = [passesString stringByAppendingString:@"2 Video Passes"];
- }
+ return self.descriptions[@(job.hash)];
}
- [finalString appendString:[NSString stringWithFormat:@"%@", job.fileURL.path.lastPathComponent] withAttributes:titleAttr];
+ NSAttributedString *finalString = job.attributedDescription;
+ self.descriptions[@(job.hash)] = finalString;;
- /* lets add the output file name to the title string here */
- NSString *outputFilenameString = job.destURL.lastPathComponent;
-
- summaryInfo = [NSString stringWithFormat: @" (%@, %@, %@) -> %@", titleString, startStopString, passesString, outputFilenameString];
-
- [finalString appendString:[NSString stringWithFormat:@"%@\n", summaryInfo] withAttributes:detailAttr];
-
- // Insert a short-in-height line to put some white space after the title
- [finalString appendString:@"\n" withAttributes:shortHeightAttr];
- // End of Title Stuff
-
- // Second Line (Preset Name)
- [finalString appendString: @"Preset: " withAttributes:detailBoldAttr];
- [finalString appendString:[NSString stringWithFormat:@"%@\n", job.presetName] withAttributes:detailAttr];
-
- // Third Line (Format Summary)
- NSString *audioCodecSummary = @""; // This seems to be set by the last track we have available...
- // Lets also get our audio track detail since we are going through the logic for use later
-
- NSMutableArray *audioDetails = [NSMutableArray array];
- BOOL autoPassthruPresent = NO;
-
- for (HBAudioTrack *audioTrack in job.audio.tracks)
- {
- if (audioTrack.enabled)
- {
- audioCodecSummary = [NSString stringWithFormat: @"%@", audioTrack.codec[keyAudioCodecName]];
- NSNumber *drc = audioTrack.drc;
- NSNumber *gain = audioTrack.gain;
- NSString *detailString = [NSString stringWithFormat: @"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps), DRC: %@, Gain: %@",
- audioTrack.track[keyAudioTrackName],
- audioTrack.codec[keyAudioCodecName],
- audioTrack.mixdown[keyAudioMixdownName],
- audioTrack.sampleRate[keyAudioSampleRateName],
- audioTrack.bitRate[keyAudioBitrateName],
- (0.0 < [drc floatValue]) ? (NSObject *)drc : (NSObject *)@"Off",
- (0.0 != [gain floatValue]) ? (NSObject *)gain : (NSObject *)@"Off"
- ];
- [audioDetails addObject: detailString];
- // check if we have an Auto Passthru output track
- if ([audioTrack.codec[keyAudioCodecName] isEqualToString: @"Auto Passthru"])
- {
- autoPassthruPresent = YES;
- }
- }
- }
-
- NSString *jobFormatInfo;
- if (job.chaptersEnabled)
- jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video %@ Audio, Chapter Markers\n",
- @(hb_container_get_name(job.container)), @(hb_video_encoder_get_name(job.video.encoder)), audioCodecSummary];
- else
- jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video %@ Audio\n",
- @(hb_container_get_name(job.container)), @(hb_video_encoder_get_name(job.video.encoder)), audioCodecSummary];
-
- [finalString appendString: @"Format: " withAttributes:detailBoldAttr];
- [finalString appendString: jobFormatInfo withAttributes:detailAttr];
-
- // Optional String for muxer options
- NSMutableString *containerOptions = [NSMutableString stringWithString:@""];
- if ((job.container & HB_MUX_MASK_MP4) && job.mp4HttpOptimize)
- {
- [containerOptions appendString:@" - Web optimized"];
- }
- if ((job.container & HB_MUX_MASK_MP4) && job.mp4iPodCompatible)
- {
- [containerOptions appendString:@" - iPod 5G support"];
- }
- if ([containerOptions hasPrefix:@" - "])
- {
- [containerOptions deleteCharactersInRange:NSMakeRange(0, 3)];
- }
- if (containerOptions.length)
- {
- [finalString appendString:@"Container Options: " withAttributes:detailBoldAttr];
- [finalString appendString:containerOptions withAttributes:detailAttr];
- [finalString appendString:@"\n" withAttributes:detailAttr];
- }
-
- // Fourth Line (Destination Path)
- [finalString appendString: @"Destination: " withAttributes:detailBoldAttr];
- [finalString appendString: job.destURL.path withAttributes:detailAttr];
- [finalString appendString:@"\n" withAttributes:detailAttr];
-
-
- // Fifth Line Picture Details
- NSString *pictureInfo = [NSString stringWithFormat:@"%@", job.picture.summary];
- if (job.picture.keepDisplayAspect)
- {
- pictureInfo = [pictureInfo stringByAppendingString:@" Keep Aspect Ratio"];
- }
- [finalString appendString:@"Picture: " withAttributes:detailBoldAttr];
- [finalString appendString:pictureInfo withAttributes:detailAttr];
- [finalString appendString:@"\n" withAttributes:detailAttr];
-
- /* Optional String for Picture Filters */
- if (job.filters.summary.length)
- {
- NSString *pictureFilters = [NSString stringWithFormat:@"%@", job.filters.summary];
- [finalString appendString:@"Filters: " withAttributes:detailBoldAttr];
- [finalString appendString:pictureFilters withAttributes:detailAttr];
- [finalString appendString:@"\n" withAttributes:detailAttr];
- }
-
- // Sixth Line Video Details
- NSString * videoInfo = [NSString stringWithFormat:@"Encoder: %@", @(hb_video_encoder_get_name(job.video.encoder))];
-
- // for framerate look to see if we are using vfr detelecine
- if (job.video.frameRate == 0)
- {
- if (job.video.frameRateMode == 0)
- {
- // we are using same as source with vfr detelecine
- videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (Variable Frame Rate)", videoInfo];
- }
- else
- {
- // we are using a variable framerate without dropping frames
- videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (Constant Frame Rate)", videoInfo];
- }
- }
- else
- {
- // we have a specified, constant framerate
- if (job.video.frameRateMode == 0)
- {
- videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@ (Peak Frame Rate)", videoInfo, @(hb_video_framerate_get_name(job.video.frameRate))];
- }
- else
- {
- videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@ (Constant Frame Rate)", videoInfo, @(hb_video_framerate_get_name(job.video.frameRate))];
- }
- }
-
-
- if (job.video.qualityType == 0) // ABR
- {
- videoInfo = [NSString stringWithFormat:@"%@ Bitrate: %d(kbps)", videoInfo, job.video.avgBitrate];
- }
- else // CRF
- {
- videoInfo = [NSString stringWithFormat:@"%@ Constant Quality: %.2f", videoInfo ,job.video.quality];
- }
-
- [finalString appendString: @"Video: " withAttributes:detailBoldAttr];
- [finalString appendString: videoInfo withAttributes:detailAttr];
- [finalString appendString:@"\n" withAttributes:detailAttr];
-
-
- if (job.video.encoder == HB_VCODEC_X264 || job.video.encoder == HB_VCODEC_X265)
- {
- // we are using x264/x265
- NSString *encoderPresetInfo = @"";
- if (job.video.advancedOptions)
- {
- // we are using the old advanced panel
- if (job.video.videoOptionExtra.length)
- {
- encoderPresetInfo = [encoderPresetInfo stringByAppendingString:job.video.videoOptionExtra];
- }
- else
- {
- encoderPresetInfo = [encoderPresetInfo stringByAppendingString:@"default settings"];
- }
- }
- else
- {
- // we are using the x264 system
- encoderPresetInfo = [encoderPresetInfo stringByAppendingString: [NSString stringWithFormat:@"Preset: %@", job.video.preset]];
- if (job.video.tune.length)
- {
- encoderPresetInfo = [encoderPresetInfo stringByAppendingString: [NSString stringWithFormat:@" - Tune: %@", job.video.tune]];
- }
- if (job.video.videoOptionExtra.length)
- {
- encoderPresetInfo = [encoderPresetInfo stringByAppendingString: [NSString stringWithFormat:@" - Options: %@", job.video.videoOptionExtra]];
- }
- if (job.video.profile.length)
- {
- encoderPresetInfo = [encoderPresetInfo stringByAppendingString: [NSString stringWithFormat:@" - Profile: %@", job.video.profile]];
- }
- if (job.video.level.length)
- {
- encoderPresetInfo = [encoderPresetInfo stringByAppendingString: [NSString stringWithFormat:@" - Level: %@", job.video.level]];
- }
- }
- [finalString appendString: @"Encoder Options: " withAttributes:detailBoldAttr];
- [finalString appendString: encoderPresetInfo withAttributes:detailAttr];
- [finalString appendString:@"\n" withAttributes:detailAttr];
- }
- else
- {
- // we are using libavcodec
- NSString *lavcInfo = @"";
- if (job.video.videoOptionExtra.length)
- {
- lavcInfo = [lavcInfo stringByAppendingString:job.video.videoOptionExtra];
- }
- else
- {
- lavcInfo = [lavcInfo stringByAppendingString: @"default settings"];
- }
- [finalString appendString: @"Encoder Options: " withAttributes:detailBoldAttr];
- [finalString appendString: lavcInfo withAttributes:detailAttr];
- [finalString appendString:@"\n" withAttributes:detailAttr];
- }
-
-
- // Seventh Line Audio Details
- int audioDetailCount = 0;
- for (NSString *anAudioDetail in audioDetails) {
- audioDetailCount++;
- if (anAudioDetail.length) {
- [finalString appendString: [NSString stringWithFormat: @"Audio Track %d ", audioDetailCount] withAttributes: detailBoldAttr];
- [finalString appendString: anAudioDetail withAttributes: detailAttr];
- [finalString appendString: @"\n" withAttributes: detailAttr];
- }
- }
-
- // Eigth Line Auto Passthru Details
- // only print Auto Passthru settings if we have an Auro Passthru output track
- if (autoPassthruPresent == YES)
- {
- NSString *autoPassthruFallback = @"", *autoPassthruCodecs = @"";
- HBAudioDefaults *audioDefaults = job.audio.defaults;
- autoPassthruFallback = [autoPassthruFallback stringByAppendingString:@(hb_audio_encoder_get_name(audioDefaults.encoderFallback))];
- if (audioDefaults.allowAACPassthru)
- {
- autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@"AAC"];
- }
- if (audioDefaults.allowAC3Passthru)
- {
- if (autoPassthruCodecs.length)
- {
- autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@", "];
- }
- autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@"AC3"];
- }
- if (audioDefaults.allowDTSHDPassthru)
- {
- if (autoPassthruCodecs.length)
- {
- autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@", "];
- }
- autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@"DTS-HD"];
- }
- if (audioDefaults.allowDTSPassthru)
- {
- if (autoPassthruCodecs.length)
- {
- autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@", "];
- }
- autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@"DTS"];
- }
- if (audioDefaults.allowMP3Passthru)
- {
- if (autoPassthruCodecs.length)
- {
- autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@", "];
- }
- autoPassthruCodecs = [autoPassthruCodecs stringByAppendingString:@"MP3"];
- }
- [finalString appendString: @"Auto Passthru Codecs: " withAttributes: detailBoldAttr];
- if (autoPassthruCodecs.length)
- {
- [finalString appendString: autoPassthruCodecs withAttributes: detailAttr];
- }
- else
- {
- [finalString appendString: @"None" withAttributes: detailAttr];
- }
- [finalString appendString: @"\n" withAttributes: detailAttr];
- [finalString appendString: @"Auto Passthru Fallback: " withAttributes: detailBoldAttr];
- [finalString appendString: autoPassthruFallback withAttributes: detailAttr];
- [finalString appendString: @"\n" withAttributes: detailAttr];
- }
-
- // Ninth Line Subtitle Details
- int i = 0;
- for (NSDictionary *track in job.subtitles.tracks)
- {
- // Ignore the none track.
- if (i == job.subtitles.tracks.count - 1)
- {
- continue;
- }
-
- /* remember that index 0 of Subtitles can contain "Foreign Audio Search*/
- [finalString appendString: @"Subtitle: " withAttributes:detailBoldAttr];
- [finalString appendString: track[@"keySubTrackName"] withAttributes:detailAttr];
- if ([track[@"keySubTrackForced"] intValue] == 1)
- {
- [finalString appendString: @" - Forced Only" withAttributes:detailAttr];
- }
- if ([track[@"keySubTrackBurned"] intValue] == 1)
- {
- [finalString appendString: @" - Burned In" withAttributes:detailAttr];
- }
- if ([track[@"keySubTrackDefault"] intValue] == 1)
- {
- [finalString appendString: @" - Default" withAttributes:detailAttr];
- }
- [finalString appendString:@"\n" withAttributes:detailAttr];
- i++;
- }
-
- [pool release];
-
- [descriptions setObject:finalString forKey:@(job.hash)];
-
- return [finalString autorelease];
+ return finalString;
}
- else if ([[tableColumn identifier] isEqualToString:@"icon"])
+ else if ([tableColumn.identifier isEqualToString:@"icon"])
{
HBJob *job = item;
if (job.state == HBJobStateCompleted)
@@ -1090,7 +1178,7 @@
}
else if (job.state == HBJobStateWorking)
{
- return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]];
+ return [NSImage imageNamed:@"EncodeWorking0"];
}
else if (job.state == HBJobStateCanceled)
{
@@ -1106,10 +1194,13 @@
return @"";
}
}
-/* This method inserts the proper action icons into the far right of the queue window */
+
+/**
+ * This method inserts the proper action icons into the far right of the queue window
+ */
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
- if ([[tableColumn identifier] isEqualToString:@"desc"])
+ if ([tableColumn.identifier isEqualToString:@"desc"])
{
// nb: The "desc" column is currently an HBImageAndTextCell. However, we are longer
// using the image portion of the cell so we could switch back to a regular NSTextFieldCell.
@@ -1117,7 +1208,7 @@
// Set the image here since the value returned from outlineView:objectValueForTableColumn: didn't specify the image part
[cell setImage:nil];
}
- else if ([[tableColumn identifier] isEqualToString:@"action"])
+ else if ([tableColumn.identifier isEqualToString:@"action"])
{
[cell setEnabled: YES];
BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView);
@@ -1152,43 +1243,40 @@
{
// By default, the disclosure image gets centered vertically in the cell. We want
// always at the top.
- if ([outlineView isItemExpanded: item])
+ if ([outlineView isItemExpanded:item])
+ {
[cell setImagePosition: NSImageAbove];
+ }
else
+ {
[cell setImagePosition: NSImageOnly];
+ }
}
#pragma mark -
#pragma mark NSOutlineView delegate (dragging related)
-//------------------------------------------------------------------------------------
-// NSTableView delegate
-//------------------------------------------------------------------------------------
-
-
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
{
// Dragging is only allowed of the pending items.
- if ([items[0] state] != HBJobStateReady) // 2 is pending
+ if ([items[0] state] != HBJobStateReady)
{
return NO;
}
-
+
// Don't retain since this is just holding temporaral drag information, and it is
//only used during a drag! We could put this in the pboard actually.
- fDraggedNodes = items;
-
+ self.dragNodesArray = items;
+
// Provide data for our custom type, and simple NSStrings.
[pboard declareTypes:@[DragDropSimplePboardType] owner:self];
-
+
// the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
[pboard setData:[NSData data] forType:DragDropSimplePboardType];
-
+
return YES;
}
-
-/* This method is used to validate the drops. */
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
{
// Don't allow dropping ONTO an item since they can't really contain any children.
@@ -1201,20 +1289,19 @@
// Don't allow dropping INTO an item since they can't really contain any children.
if (item != nil)
{
- index = [fOutlineView rowForItem: item] + 1;
+ index = [self.outlineView rowForItem: item] + 1;
item = nil;
}
-
- // NOTE: Should we allow dropping a pending job *above* the
- // finished or already encoded jobs ?
+
// We do not let the user drop a pending job before or *above*
// already finished or currently encoding jobs.
- if (index <= fEncodingQueueItem)
+ NSInteger encodingIndex = [self.jobs indexOfObject:self.currentJob];
+ if (index <= encodingIndex)
{
return NSDragOperationNone;
- index = MAX (index, fEncodingQueueItem);
+ index = MAX (index, encodingIndex);
}
-
+
[outlineView setDropItem:item dropChildIndex:index];
return NSDragOperationGeneric;
}
@@ -1223,14 +1310,17 @@
{
NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
- for (id obj in fDraggedNodes)
- [moveItems addIndex:[fJobGroups indexOfObject:obj]];
+ for (id obj in self.dragNodesArray)
+ {
+ [moveItems addIndex:[self.jobs indexOfObject:obj]];
+ }
// Successful drop, we use moveObjectsInQueueArray:... in fHBController
// to properly rearrange the queue array, save it to plist and then send it back here.
// since Controller.mm is handling all queue array manipulation.
- // We *could do this here, but I think we are better served keeping that code together.
- [fHBController moveObjectsInQueueArray:fJobGroups fromIndexes:moveItems toIndex: index];
+ // We could do this here, but I think we are better served keeping that code together.
+ [self moveObjectsInQueueArray:self.jobs fromIndexes:moveItems toIndex: index];
+
return YES;
}
diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj
index e840a408a..30476e490 100644
--- a/macosx/HandBrake.xcodeproj/project.pbxproj
+++ b/macosx/HandBrake.xcodeproj/project.pbxproj
@@ -153,6 +153,7 @@
A9935213196F38A70069C6B7 /* ChaptersTitles.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9935211196F38A70069C6B7 /* ChaptersTitles.xib */; };
A9AA447A1970664A00D7DEFC /* HBUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = A9AA44791970664A00D7DEFC /* HBUtilities.m */; };
A9BB0F2719A0ECE40079F1C1 /* HBHUDButtonCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A9BB0F2619A0ECE40079F1C1 /* HBHUDButtonCell.m */; };
+ A9BC24C91A69293E007DC41A /* HBAttributedStringAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A9BC24C81A69293E007DC41A /* HBAttributedStringAdditions.m */; };
A9C0DB85197E7B0000DF55B3 /* SubtitlesDefaults.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9C0DB83197E7B0000DF55B3 /* SubtitlesDefaults.xib */; };
A9C9F88919A733FE00DC8923 /* HBHUDView.m in Sources */ = {isa = PBXBuildFile; fileRef = A9C9F88819A733FE00DC8923 /* HBHUDView.m */; };
A9CF25F11990D62C0023F727 /* Presets.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9CF25EF1990D62C0023F727 /* Presets.xib */; };
@@ -173,6 +174,7 @@
A9E1468316BC2AD800C307BC /* prev-p.pdf in Resources */ = {isa = PBXBuildFile; fileRef = A9E1467F16BC2AD800C307BC /* prev-p.pdf */; };
A9E2FD271A21BC4A000E8D3F /* HBAddPresetController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E2FD251A21BC4A000E8D3F /* HBAddPresetController.m */; };
A9E2FD2B1A21BC6F000E8D3F /* AddPreset.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9E2FD291A21BC6F000E8D3F /* AddPreset.xib */; };
+ A9E66D701A67A2A8007B641D /* HBDistributedArray.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E66D6F1A67A2A8007B641D /* HBDistributedArray.m */; };
A9EA43681A2210C400785E95 /* HBQueueOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = A9EA43671A2210C400785E95 /* HBQueueOutlineView.m */; };
A9F2EB6F196F12C800066546 /* Audio.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9F2EB6D196F12C800066546 /* Audio.xib */; };
A9F472891976B7F30009EC65 /* HBSubtitlesDefaultsController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9F472871976B7F30009EC65 /* HBSubtitlesDefaultsController.m */; };
@@ -419,6 +421,8 @@
A9B34D74197696FE00871B7D /* DiskArbitration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DiskArbitration.framework; path = System/Library/Frameworks/DiskArbitration.framework; sourceTree = SDKROOT; };
A9BB0F2519A0ECE40079F1C1 /* HBHUDButtonCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBHUDButtonCell.h; sourceTree = "<group>"; };
A9BB0F2619A0ECE40079F1C1 /* HBHUDButtonCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBHUDButtonCell.m; sourceTree = "<group>"; };
+ A9BC24C71A69293E007DC41A /* HBAttributedStringAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBAttributedStringAdditions.h; sourceTree = "<group>"; };
+ A9BC24C81A69293E007DC41A /* HBAttributedStringAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBAttributedStringAdditions.m; sourceTree = "<group>"; };
A9C0DB84197E7B0000DF55B3 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = SubtitlesDefaults.xib; sourceTree = "<group>"; };
A9C9F88719A733FE00DC8923 /* HBHUDView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBHUDView.h; sourceTree = "<group>"; };
A9C9F88819A733FE00DC8923 /* HBHUDView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBHUDView.m; sourceTree = "<group>"; };
@@ -449,6 +453,8 @@
A9E2FD241A21BC4A000E8D3F /* HBAddPresetController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBAddPresetController.h; sourceTree = "<group>"; };
A9E2FD251A21BC4A000E8D3F /* HBAddPresetController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBAddPresetController.m; sourceTree = "<group>"; };
A9E2FD2A1A21BC6F000E8D3F /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = AddPreset.xib; sourceTree = "<group>"; };
+ A9E66D6E1A67A2A8007B641D /* HBDistributedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBDistributedArray.h; sourceTree = "<group>"; };
+ A9E66D6F1A67A2A8007B641D /* HBDistributedArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBDistributedArray.m; sourceTree = "<group>"; };
A9EA43661A2210C400785E95 /* HBQueueOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBQueueOutlineView.h; sourceTree = "<group>"; };
A9EA43671A2210C400785E95 /* HBQueueOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBQueueOutlineView.m; sourceTree = "<group>"; };
A9F2EB6E196F12C800066546 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Audio.xib; sourceTree = "<group>"; };
@@ -678,6 +684,8 @@
A9AA44791970664A00D7DEFC /* HBUtilities.m */,
273F209D14ADBE670021BE6D /* HBOutputRedirect.h */,
273F209E14ADBE670021BE6D /* HBOutputRedirect.m */,
+ A9E66D6E1A67A2A8007B641D /* HBDistributedArray.h */,
+ A9E66D6F1A67A2A8007B641D /* HBDistributedArray.m */,
A98C29C21977B10600AF5DED /* HBLanguagesSelection.h */,
A98C29C31977B10600AF5DED /* HBLanguagesSelection.m */,
A9B34D711976844500871B7D /* UI Views */,
@@ -993,6 +1001,8 @@
A9B34D711976844500871B7D /* UI Views */ = {
isa = PBXGroup;
children = (
+ A9BC24C71A69293E007DC41A /* HBAttributedStringAdditions.h */,
+ A9BC24C81A69293E007DC41A /* HBAttributedStringAdditions.m */,
A9EA43661A2210C400785E95 /* HBQueueOutlineView.h */,
A9EA43671A2210C400785E95 /* HBQueueOutlineView.m */,
46AB433315F98A2B009C0961 /* DockTextField.h */,
@@ -1081,7 +1091,7 @@
273F1FE014AD9DA40021BE6D /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0610;
+ LastUpgradeCheck = 0620;
};
buildConfigurationList = 273F1FE314AD9DA40021BE6D /* Build configuration list for PBXProject "HandBrake" */;
compatibilityVersion = "Xcode 3.2";
@@ -1217,6 +1227,7 @@
A9DEC8771A23C88D00C79B48 /* HBVideo.m in Sources */,
A9523937199A6AAE00588AEF /* HBFilters.m in Sources */,
A9AA447A1970664A00D7DEFC /* HBUtilities.m in Sources */,
+ A9BC24C91A69293E007DC41A /* HBAttributedStringAdditions.m in Sources */,
273F20AC14ADBE670021BE6D /* Controller.m in Sources */,
273F20AD14ADBE670021BE6D /* HBAdvancedController.m in Sources */,
273F20AE14ADBE670021BE6D /* HBAudioTrack.m in Sources */,
@@ -1231,6 +1242,7 @@
A93FD4751A62ABE800A6AC43 /* HBAudio.m in Sources */,
A971281F1A2C75180088C076 /* HBTitle.m in Sources */,
273F20B514ADBE670021BE6D /* HBPreferencesController.m in Sources */,
+ A9E66D701A67A2A8007B641D /* HBDistributedArray.m in Sources */,
A9DC6C52196F04F6002AE6B4 /* HBSubtitlesController.m in Sources */,
A9F472891976B7F30009EC65 /* HBSubtitlesDefaultsController.m in Sources */,
A9CF25F41990D64E0023F727 /* HBPreset.m in Sources */,
@@ -1437,6 +1449,8 @@
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
+ GCC_WARN_SHADOW = NO;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
@@ -1477,6 +1491,8 @@
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
+ GCC_WARN_SHADOW = NO;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
diff --git a/macosx/HandBrake.xcodeproj/xcshareddata/xcschemes/HandBrake [DEBUG].xcscheme b/macosx/HandBrake.xcodeproj/xcshareddata/xcschemes/HandBrake [DEBUG].xcscheme
index 3ec110ffa..a4579d8bc 100644
--- a/macosx/HandBrake.xcodeproj/xcshareddata/xcschemes/HandBrake [DEBUG].xcscheme
+++ b/macosx/HandBrake.xcodeproj/xcshareddata/xcschemes/HandBrake [DEBUG].xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0610"
+ LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -62,7 +62,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
- <BuildableProductRunnable>
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "273F203814ADBC200021BE6D"
@@ -80,7 +81,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "debug"
debugDocumentVersioning = "YES">
- <BuildableProductRunnable>
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "273F203814ADBC200021BE6D"
diff --git a/macosx/HandBrake.xcodeproj/xcshareddata/xcschemes/HandBrake [RELEASE].xcscheme b/macosx/HandBrake.xcodeproj/xcshareddata/xcschemes/HandBrake [RELEASE].xcscheme
index 59c647285..d51e11b37 100644
--- a/macosx/HandBrake.xcodeproj/xcshareddata/xcschemes/HandBrake [RELEASE].xcscheme
+++ b/macosx/HandBrake.xcodeproj/xcshareddata/xcschemes/HandBrake [RELEASE].xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0610"
+ LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -62,7 +62,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
- <BuildableProductRunnable>
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "273F203814ADBC200021BE6D"
@@ -80,7 +81,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "release"
debugDocumentVersioning = "YES">
- <BuildableProductRunnable>
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "273F203814ADBC200021BE6D"