summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorritsuka <[email protected]>2015-07-22 07:46:07 +0000
committerritsuka <[email protected]>2015-07-22 07:46:07 +0000
commitbe9ac805c8fbc2c8ba37389ce034db92f15b7e4a (patch)
treef14e4803c1eca0e93c712941f2fed6bfeddd9125
parent99d6dc51d695cce18c27b65fb42196a57a4c3eb7 (diff)
MacGui: various queue improvements, including:
- multiple items drag & drop - "reset job" in contextual menu item to reset the job state - a toolbar item to select the action to perform when the queue is done - useless animations. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@7358 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r--macosx/English.lproj/Preferences.xib20
-rw-r--r--macosx/English.lproj/Queue.xib54
-rw-r--r--macosx/HBController.h2
-rw-r--r--macosx/HBController.m6
-rw-r--r--macosx/HBPreferencesController.h8
-rw-r--r--macosx/HBPreferencesController.m2
-rw-r--r--macosx/HBQueueController.h1
-rw-r--r--macosx/HBQueueController.m441
-rw-r--r--macosx/HBQueueOutlineView.h2
-rw-r--r--macosx/HandBrake.xcodeproj/project.pbxproj6
-rw-r--r--macosx/NSArray+HBAdditions.h23
-rw-r--r--macosx/NSArray+HBAdditions.m58
12 files changed, 392 insertions, 231 deletions
diff --git a/macosx/English.lproj/Preferences.xib b/macosx/English.lproj/Preferences.xib
index 7243893fc..9cde5f346 100644
--- a/macosx/English.lproj/Preferences.xib
+++ b/macosx/English.lproj/Preferences.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="7702" systemVersion="14E17e" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14F6a" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment version="1060" identifier="macosx"/>
<development version="5100" identifier="xcode"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7702"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="HBPreferencesController">
@@ -211,22 +211,22 @@
<popUpButton verticalHuggingPriority="750" id="491">
<rect key="frame" x="124" y="271" width="195" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <popUpButtonCell key="cell" type="push" title="Do Nothing" bezelStyle="rounded" alignment="left" controlSize="small" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="494" id="492">
+ <popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" controlSize="small" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="492">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="smallSystem"/>
<menu key="menu" title="OtherViews" id="493">
<items>
- <menuItem title="Do Nothing" state="on" id="494"/>
- <menuItem title="Alert Window" id="495"/>
- <menuItem title="Growl Notification" id="499"/>
- <menuItem title="Alert Window And Growl" id="496"/>
- <menuItem title="Put Computer To Sleep" id="497"/>
- <menuItem title="Shut Down Computer" id="498"/>
+ <menuItem title="Do Nothing" id="494"/>
+ <menuItem title="Alert" tag="1" id="495"/>
+ <menuItem title="Notification" tag="2" id="499"/>
+ <menuItem title="Alert And Notification" tag="3" id="496"/>
+ <menuItem title="Put Computer To Sleep" tag="4" id="497"/>
+ <menuItem title="Shut Down Computer" tag="5" id="498"/>
</items>
</menu>
</popUpButtonCell>
<connections>
- <binding destination="61" name="selectedValue" keyPath="values.AlertWhenDone" id="501"/>
+ <binding destination="61" name="selectedTag" keyPath="values.HBAlertWhenDone" id="xxK-qX-yth"/>
</connections>
</popUpButton>
<tokenField verticalHuggingPriority="750" id="6aa-GX-TuM">
diff --git a/macosx/English.lproj/Queue.xib b/macosx/English.lproj/Queue.xib
index 82fc7ee3b..96598ec01 100644
--- a/macosx/English.lproj/Queue.xib
+++ b/macosx/English.lproj/Queue.xib
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6724" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14F6a" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
- <deployment version="1060" identifier="macosx"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6724"/>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="HBQueueController">
@@ -17,6 +17,7 @@
<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"/>
+ <windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="893" y="128" width="574" height="423"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1418"/>
@@ -32,11 +33,11 @@
<rect key="frame" x="1" y="1" width="532" height="336"/>
<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="19"/>
+ <outlineView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="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"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
- <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="icon" width="38" minWidth="38" maxWidth="38" id="2624">
@@ -54,7 +55,7 @@
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
</tableHeaderCell>
- <textFieldCell key="dataCell" selectable="YES" editable="YES" alignment="left" id="2609" customClass="HBImageAndTextCell">
+ <textFieldCell key="dataCell" selectable="YES" editable="YES" alignment="left" id="2609">
<font key="font" metaFont="cellTitle"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -125,17 +126,44 @@
</toolbarItem>
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="rHN-a0-oZQ"/>
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="QuV-M8-cet"/>
+ <toolbarItem implicitItemIdentifier="938D3EC6-1547-4AAB-86AF-B3FD3C7AF8BD" label="When Done" paletteLabel="When Done" id="a3c-kV-98E">
+ <nil key="toolTip"/>
+ <size key="minSize" width="100" height="25"/>
+ <size key="maxSize" width="210" height="25"/>
+ <popUpButton key="view" verticalHuggingPriority="750" id="rfS-M1-CnB">
+ <rect key="frame" x="0.0" y="14" width="200" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pch-jl-VXA">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="menu"/>
+ <menu key="menu" id="1Ee-Xt-VES">
+ <items>
+ <menuItem title="Do Nothing" id="sm5-26-sAg"/>
+ <menuItem title="Alert" tag="1" id="fAD-ky-zo6"/>
+ <menuItem title="Notification" tag="2" id="jDL-sB-8e3"/>
+ <menuItem title="Alert and Notification" tag="3" id="aat-1N-Odn"/>
+ <menuItem title="Put Computer to Sleep" tag="4" id="GUQ-xb-HVS"/>
+ <menuItem title="Shut Down Computer" tag="5" id="QmP-SQ-XKK"/>
+ </items>
+ </menu>
+ </popUpButtonCell>
+ <connections>
+ <binding destination="z2J-h1-IDv" name="selectedTag" keyPath="values.HBAlertWhenDone" id="trt-6H-UtD"/>
+ </connections>
+ </popUpButton>
+ </toolbarItem>
</allowedToolbarItems>
<defaultToolbarItems>
<toolbarItem reference="SX6-mq-Hck"/>
<toolbarItem reference="s7o-pK-heI"/>
<toolbarItem reference="QuV-M8-cet"/>
+ <toolbarItem reference="a3c-kV-98E"/>
</defaultToolbarItems>
</toolbar>
<connections>
<outlet property="delegate" destination="-2" id="2579"/>
</connections>
- <point key="canvasLocation" x="666" y="695.5"/>
+ <point key="canvasLocation" x="322" y="355.5"/>
</window>
<menu id="2649" userLabel="ContextMenu">
<items>
@@ -148,10 +176,17 @@
<menuItem title="Show in Finder" id="2655">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
- <action selector="revealSelectedQueueItem:" target="-2" id="2657"/>
+ <action selector="revealSelectedQueueItems:" target="-2" id="qtj-uq-KvZ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="Au5-j1-AAd"/>
+ <menuItem title="Reset job" id="zy6-ab-ush">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="resetJobState:" target="-1" id="fxd-BP-VY6"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="1ZZ-71-d6P"/>
<menuItem title="Clear All" id="2652">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
@@ -174,6 +209,7 @@
</items>
<point key="canvasLocation" x="192" y="450.5"/>
</menu>
+ <userDefaultsController representsSharedInstance="YES" id="z2J-h1-IDv"/>
</objects>
<resources>
<image name="Delete" width="16" height="16"/>
diff --git a/macosx/HBController.h b/macosx/HBController.h
index 625beb492..196f3cae0 100644
--- a/macosx/HBController.h
+++ b/macosx/HBController.h
@@ -100,7 +100,7 @@
- (IBAction)addToQueue:(id)sender;
- (IBAction)addAllTitlesToQueue:(id)sender;
-- (void)rescanJobToMainWindow:(HBJob *)queueItem;
+- (void)openJob:(HBJob *)job;
- (void)setQueueState:(NSString *)info;
- (void)setQueueInfo:(NSString *)info progress:(double)progress hidden:(BOOL)hidden;
diff --git a/macosx/HBController.m b/macosx/HBController.m
index 51b7c503f..1259cc652 100644
--- a/macosx/HBController.m
+++ b/macosx/HBController.m
@@ -298,7 +298,7 @@
[toolbarItem setLabel: @"Stop"];
[toolbarItem setPaletteLabel: @"Stop"];
[toolbarItem setToolTip: @"Stop Encoding"];
- return (queueState != HBStateScanning);
+ return YES;
}
if (action == @selector(pause:))
{
@@ -1054,10 +1054,10 @@
/**
* Rescans the chosen queue item back into the main window
*/
-- (void)rescanJobToMainWindow:(HBJob *)queueItem
+- (void)openJob:(HBJob *)job
{
// Set the browsedSourceDisplayName for showNewScan
- self.jobFromQueue = queueItem;
+ self.jobFromQueue = job;
self.browsedSourceDisplayName = self.jobFromQueue.fileURL.lastPathComponent;
[self performScan:self.jobFromQueue.fileURL scanTitleNum:self.jobFromQueue.titleIdx];
diff --git a/macosx/HBPreferencesController.h b/macosx/HBPreferencesController.h
index 6003aa8bd..9b953998c 100644
--- a/macosx/HBPreferencesController.h
+++ b/macosx/HBPreferencesController.h
@@ -5,6 +5,14 @@
#import <Cocoa/Cocoa.h>
+typedef NS_ENUM(NSUInteger, HBDoneAction) {
+ HBDoneActionAlert = 1,
+ HBDoneActionNotification = 2,
+ HBDoneActionAlertAndNotification = 3,
+ HBDoneActionSleep = 4,
+ HBDoneActionShutDown = 5,
+};
+
@interface HBPreferencesController : NSWindowController <NSToolbarDelegate>
+ (void)registerUserDefaults;
diff --git a/macosx/HBPreferencesController.m b/macosx/HBPreferencesController.m
index 55847acf2..0ec9c5637 100644
--- a/macosx/HBPreferencesController.m
+++ b/macosx/HBPreferencesController.m
@@ -67,7 +67,7 @@
@"HBLastDestinationDirectory": [NSKeyedArchiver archivedDataWithRootObject:desktopURL],
@"HBLastSourceDirectory": [NSKeyedArchiver archivedDataWithRootObject:desktopURL],
@"DefaultAutoNaming": @NO,
- @"AlertWhenDone": @"Alert Window",
+ @"HBAlertWhenDone": @(HBDoneActionNotification),
@"AlertWhenDoneSound": @"YES",
@"LoggingLevel": @"1",
@"HBClearOldLogs": @YES,
diff --git a/macosx/HBQueueController.h b/macosx/HBQueueController.h
index c0efaba17..fc99914f0 100644
--- a/macosx/HBQueueController.h
+++ b/macosx/HBQueueController.h
@@ -25,7 +25,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) NSUInteger count;
@property (nonatomic, readonly) NSUInteger pendingItemsCount;
-@property (nonatomic, readonly) NSUInteger workingItemsCount;
- (void)addJob:(HBJob *)item;
- (void)addJobsFromArray:(NSArray *)items;
diff --git a/macosx/HBQueueController.m b/macosx/HBQueueController.m
index 241dd64f0..e97f7162c 100644
--- a/macosx/HBQueueController.m
+++ b/macosx/HBQueueController.m
@@ -20,11 +20,13 @@
#import "HBStateFormatter.h"
#import "HBDistributedArray.h"
+#import "NSArray+HBAdditions.h"
#import "HBDockTile.h"
#import "HBOutputRedirect.h"
#import "HBJobOutputFileWriter.h"
+#import "HBPreferencesController.h"
// Pasteboard type for or drag operations
#define DragDropSimplePboardType @"HBQueueCustomOutlineViewPboardType"
@@ -34,7 +36,7 @@
#define HB_ROW_HEIGHT_TITLE_ONLY 17.0
-@interface HBQueueController () <HBQueueOutlineViewDelegate>
+@interface HBQueueController () <NSOutlineViewDataSource, HBQueueOutlineViewDelegate>
@property (nonatomic, readonly) HBDockTile *dockTile;
@property (nonatomic, readwrite) double dockIconProgress;
@@ -52,7 +54,7 @@
@property (nonatomic, readwrite) BOOL stop;
@property (nonatomic, readwrite) NSUInteger pendingItemsCount;
-@property (nonatomic, readwrite) NSUInteger workingItemsCount;
+@property (nonatomic, readwrite) NSUInteger completedItemsCount;
@property (nonatomic, strong) NSArray *dragNodesArray;
@@ -96,6 +98,8 @@
// Don't allow autoresizing of main column, else the "delete" column will get
// pushed out of view.
[self.outlineView setAutoresizesOutlineColumn: NO];
+
+ [self getQueueStats];
}
#pragma mark Toolbar
@@ -118,7 +122,7 @@
menuItem.title = NSLocalizedString(@"Stop Encoding", nil);
menuItem.keyEquivalent = @".";
- return self.core.state != HBStateScanning;
+ return YES;
}
}
@@ -138,11 +142,26 @@
if (action == @selector(editSelectedQueueItem:) ||
action == @selector(removeSelectedQueueItem:) ||
- action == @selector(revealSelectedQueueItem:))
+ action == @selector(revealSelectedQueueItems:))
{
return (self.outlineView.selectedRow != -1 || self.outlineView.clickedRow != -1);
}
+ if (action == @selector(resetJobState:))
+ {
+ return self.outlineView.targetedRowIndexes.count > 0;
+ }
+
+ if (action == @selector(clearAll:))
+ {
+ return self.jobs.count > 0;
+ }
+
+ if (action == @selector(clearCompleted:))
+ {
+ return self.completedItemsCount > 0;
+ }
+
return YES;
}
@@ -158,7 +177,7 @@
theItem.image = [NSImage imageNamed:@"stopencode"];
theItem.label = NSLocalizedString(@"Stop", @"");
theItem.toolTip = NSLocalizedString(@"Stop Encoding", @"");
- return s != HBStateScanning;
+ return YES;
}
else
{
@@ -241,14 +260,15 @@
- (void)removeQueueItemAtIndex:(NSUInteger)index
{
- [self.jobs beginTransaction];
- if (self.jobs.count > index)
+ [self removeQueueItemsAtIndexes:[NSIndexSet indexSetWithIndex:index]];
+}
+
+- (void)removeQueueItemsAtIndexes:(NSIndexSet *)indexes
+{
+ if (self.jobs.count > [indexes lastIndex])
{
- [self.jobs removeObjectAtIndex:index];
+ [self.jobs removeObjectsAtIndexes:indexes];
}
- [self.jobs commit];
-
- [self reloadQueue];
}
/**
@@ -258,17 +278,17 @@
{
// lets get the stats on the status of the queue array
NSUInteger pendingCount = 0;
- NSUInteger workingCount = 0;
+ NSUInteger completedCount = 0;
for (HBJob *job in self.jobs)
{
- if (job.state == HBJobStateWorking) // being encoded
+ if (job.state == HBJobStateReady)
{
- workingCount++;
+ pendingCount++;
}
- if (job.state == HBJobStateReady) // pending
+ if (job.state == HBJobStateCompleted)
{
- pendingCount++;
+ completedCount++;
}
}
@@ -290,7 +310,7 @@
[self.controller setQueueState:string];
self.pendingItemsCount = pendingCount;
- self.workingItemsCount = workingCount;
+ self.completedItemsCount = completedCount;
}
- (NSUInteger)count
@@ -340,7 +360,7 @@
- (void)removeCompletedJobs
{
[self.jobs beginTransaction];
- [self removeItemsUsingBlock:^BOOL(HBJob *item) {
+ [self.jobs removeObjectsUsingBlock:^BOOL(HBJob *item) {
return (item.state == HBJobStateCompleted || item.state == HBJobStateCanceled);
}];
[self.jobs commit];
@@ -359,52 +379,6 @@
[self reloadQueue];
}
-- (void)removeItemsUsingBlock:(BOOL (^)(HBJob *item))predicate
-{
- NSMutableArray *itemsToRemove = [NSMutableArray array];
- for (HBJob *item in self.jobs)
- {
- if (predicate(item))
- {
- [itemsToRemove addObject:item];
- }
- }
- [self.jobs removeObjectsInArray:itemsToRemove];
-}
-
-/**
- * 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
-{
- [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];
- [self.jobs removeObjectAtIndex:removeIndex];
- [self.jobs insertObject:object atIndex:insertIndex];
-
- // 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
@@ -455,17 +429,7 @@
// 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];
- }
+ [self jobCompletedAlerts];
// Mark the encode just finished as done
self.currentJob.state = HBJobStateCompleted;
@@ -656,7 +620,7 @@
GROWL_NOTIFICATIONS_DEFAULT: @[SERVICE_NAME]};
}
-- (void)showGrowlDoneNotification:(NSURL *)fileURL
+- (void)showDoneNotification:(NSURL *)fileURL
{
// This end of encode action is called as each encode rolls off of the queue
// Setup the Growl stuff
@@ -692,17 +656,32 @@
}
}
+- (void)jobCompletedAlerts
+{
+ // Both the Notification and Sending to tagger can be done as encodes roll off the queue
+ if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionNotification ||
+ [[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionAlertAndNotification)
+ {
+ // If Play System Alert has been selected in Preferences
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"HBAlertWhenDoneSound"] == YES)
+ {
+ NSBeep();
+ }
+ [self showDoneNotification:self.currentJob.destURL];
+ }
+}
+
- (void)queueCompletedAlerts
{
// If Play System Alert has been selected in Preferences
- if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES)
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"HBAlertWhenDoneSound"] == 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"])
+ // If Alert Window or Window and Notification has been selected
+ if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionAlert ||
+ [[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionAlertAndNotification)
{
// On Screen Notification
NSAlert *alert = [[NSAlert alloc] init];
@@ -713,7 +692,7 @@
}
// If sleep has been selected
- if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
+ if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionSleep)
{
// Sleep
NSDictionary *errorDict;
@@ -722,7 +701,7 @@
[scriptObject executeAndReturnError: &errorDict];
}
// If Shutdown has been selected
- if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"] )
+ if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionShutDown)
{
// Shut Down
NSDictionary *errorDict;
@@ -744,47 +723,56 @@
*/
- (IBAction)removeSelectedQueueItem:(id)sender
{
- NSIndexSet *targetedRow = [self.outlineView targetedRowIndexes];
- NSUInteger row = [targetedRow firstIndex];
- if (row == NSNotFound)
- {
- return;
- }
+ NSMutableIndexSet *targetedRows = [[self.outlineView targetedRowIndexes] mutableCopy];
- // 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)
+ if (targetedRows.count)
{
- // 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:self];
+ [self.jobs beginTransaction];
+ // 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
+ NSIndexSet *workingIndexes = [self.jobs indexesOfObjectsUsingBlock:^BOOL(HBJob *item) {
+ return item.state == HBJobStateWorking;
+ }];
+ NSArray *workingJobs = [self.jobs filteredArrayUsingBlock:^BOOL(HBJob *item) {
+ return item.state == HBJobStateWorking;
+ }];
+
+ [targetedRows removeIndexes:workingIndexes];
+
+ // remove the non working items immediately
+ [self removeQueueItemsAtIndexes:targetedRows];
+ [self getQueueStats];
+
+ [self.outlineView beginUpdates];
+ [self.outlineView removeItemsAtIndexes:targetedRows inParent:nil withAnimation:NSTableViewAnimationEffectFade];
+ [self.outlineView endUpdates];
- NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It?", nil)];
+ [self.jobs commit];
- // Which window to attach the sheet to?
- NSWindow *targetWindow = nil;
- if ([sender respondsToSelector: @selector(window)])
+ if ([workingJobs containsObject:self.currentJob])
{
- targetWindow = [sender window];
- }
+ NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It?", nil)];
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:alertTitle];
- [alert setInformativeText:NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil)];
- [alert addButtonWithTitle:NSLocalizedString(@"Keep Encoding", nil)];
- [alert addButtonWithTitle:NSLocalizedString(@"Stop Encoding and Delete", nil)];
- [alert setAlertStyle:NSCriticalAlertStyle];
-
- [alert beginSheetModalForWindow:targetWindow
- modalDelegate:self
- didEndSelector:@selector(didDimissCancelCurrentJob:returnCode:contextInfo:)
- contextInfo:(__bridge void *)(self.jobs[row])];
- }
- else if ([self.jobs[row] state] != HBJobStateWorking)
- {
- // since we are not a currently encoding item, we can just be removed
- [self removeQueueItemAtIndex:row];
+ // Which window to attach the sheet to?
+ NSWindow *targetWindow = nil;
+ if ([sender respondsToSelector: @selector(window)])
+ {
+ targetWindow = [sender window];
+ }
+
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:alertTitle];
+ [alert setInformativeText:NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil)];
+ [alert addButtonWithTitle:NSLocalizedString(@"Keep Encoding", nil)];
+ [alert addButtonWithTitle:NSLocalizedString(@"Stop Encoding and Delete", nil)];
+ [alert setAlertStyle:NSCriticalAlertStyle];
+
+ [alert beginSheetModalForWindow:targetWindow
+ modalDelegate:self
+ didEndSelector:@selector(didDimissCancelCurrentJob:returnCode:contextInfo:)
+ contextInfo:NULL];
+ }
}
}
@@ -792,43 +780,38 @@
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
- // [self togglePauseResume:nil]; Again will resume encoding
- [self togglePauseResume:self];
-
if (returnCode == NSAlertSecondButtonReturn)
{
NSInteger index = [self.jobs indexOfObject:self.currentJob];
[self cancelCurrentJobAndContinue];
+
+ [self.jobs beginTransaction];
[self removeQueueItemAtIndex:index];
+ [self.jobs commit];
}
}
/**
* Show the finished encode in the finder
*/
-- (IBAction)revealSelectedQueueItem: (id)sender
+- (IBAction)revealSelectedQueueItems: (id)sender
{
- NSIndexSet *targetedRow = [self.outlineView targetedRowIndexes];
- NSInteger row = [targetedRow firstIndex];
- if (row != NSNotFound)
- {
- while (row != NSNotFound)
- {
- HBJob *queueItemToOpen = [self.outlineView itemAtRow:row];
- [[NSWorkspace sharedWorkspace] selectFile:queueItemToOpen.destURL.path inFileViewerRootedAtPath:nil];
+ NSIndexSet *targetedRows = [self.outlineView targetedRowIndexes];
+ NSMutableArray *urls = [[NSMutableArray alloc] init];
- row = [targetedRow indexGreaterThanIndex: row];
- }
+ NSUInteger currentIndex = [targetedRows firstIndex];
+ while (currentIndex != NSNotFound) {
+ NSURL *url = [[self.jobs objectAtIndex:currentIndex] destURL];
+ [urls addObject:url];
+ currentIndex = [targetedRows indexGreaterThanIndex:currentIndex];
}
+
+ [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:urls];
}
- (void)remindUserOfSleepOrShutdown
{
- if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString:@"Put Computer To Sleep"])
+ if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionSleep)
{
// Warn that computer will sleep after encoding
NSBeep();
@@ -846,7 +829,7 @@
[self.delegate showPreferencesWindow:nil];
}
}
- else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString:@"Shut Down Computer"])
+ else if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionShutDown)
{
// Warn that computer will shut down after encoding
NSBeep();
@@ -899,8 +882,6 @@
*/
- (IBAction)cancelRip:(id)sender
{
- [self.core pause];
-
// Which window to attach the sheet to?
NSWindow *window;
if ([sender respondsToSelector:@selector(window)])
@@ -913,7 +894,7 @@
}
NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)];
+ [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)];
@@ -930,8 +911,6 @@
returnCode:(NSInteger)returnCode
contextInfo:(void *)contextInfo
{
- [self.core resume];
-
if (returnCode == NSAlertSecondButtonReturn)
{
[self cancelCurrentJobAndStop];
@@ -974,61 +953,112 @@
}
}
+- (IBAction)resetJobState:(id)sender
+{
+ [self.jobs beginTransaction];
+
+ NSIndexSet *targetedRows = [self.outlineView targetedRowIndexes];
+ NSMutableIndexSet *updatedIndexes = [NSMutableIndexSet indexSet];
+
+ NSUInteger currentIndex = [targetedRows firstIndex];
+ while (currentIndex != NSNotFound) {
+ HBJob *job = self.jobs[currentIndex];
+
+ if (job.state == HBJobStateCanceled || job.state == HBJobStateCompleted)
+ {
+ job.state = HBJobStateReady;
+ [updatedIndexes addIndex:currentIndex];
+ }
+ currentIndex = [targetedRows indexGreaterThanIndex:currentIndex];
+ }
+
+ [self.outlineView reloadDataForRowIndexes:updatedIndexes columnIndexes:[NSIndexSet indexSetWithIndex:0]];
+ [self getQueueStats];
+
+ [self.jobs commit];
+}
+
/**
* Send the selected queue item back to the main window for rescan and possible edit.
*/
- (IBAction)editSelectedQueueItem:(id)sender
{
+ [self.jobs beginTransaction];
+
NSInteger row = [self.outlineView clickedRow];
- if (row == NSNotFound)
+ 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 = self.jobs[row];
+ if (job == self.currentJob)
+ {
+ NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Edit It?", nil)];
+
+ // Which window to attach the sheet to?
+ NSWindow *docWindow = nil;
+ if ([sender respondsToSelector: @selector(window)])
+ {
+ docWindow = [sender window];
+ }
+
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:alertTitle];
+ [alert setInformativeText:NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil)];
+ [alert addButtonWithTitle:NSLocalizedString(@"Keep Encoding", nil)];
+ [alert addButtonWithTitle:NSLocalizedString(@"Stop Encoding and Edit", nil)];
+ [alert setAlertStyle:NSCriticalAlertStyle];
+
+ [alert beginSheetModalForWindow:docWindow
+ modalDelegate:self
+ didEndSelector:@selector(didDimissEditCurrentJob:returnCode:contextInfo:)
+ contextInfo:(__bridge void *)(job)];
+ }
+ else
+ {
+ // since we are not a currently encoding item, we can just be edit it
+ HBJob *item = [[self.jobs[row] representedObject] copy];
+ [self.controller openJob:item];
+
+ // Now that source is loaded and settings applied, delete the queue item from the queue
+ [self.outlineView beginUpdates];
+ [self removeQueueItemAtIndex:row];
+ [self.outlineView removeItemsAtIndexes:[NSIndexSet indexSetWithIndex:row] inParent:nil withAnimation:NSTableViewAnimationEffectFade];
+ [self.outlineView endUpdates];
+
+ [self getQueueStats];
+ }
}
- // 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)
+ [self.jobs commit];
+}
+
+- (void)didDimissEditCurrentJob:(NSAlert *)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void *)contextInfo
+{
+ if (returnCode == NSAlertSecondButtonReturn)
{
- // 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:self];
- NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It ?", nil)];
- // Which window to attach the sheet to?
- NSWindow *docWindow = nil;
- if ([sender respondsToSelector: @selector(window)])
+ HBJob *job = (__bridge HBJob *)contextInfo;
+ NSInteger index = [self.jobs indexOfObject:job];
+
+ if (job == self.currentJob)
{
- docWindow = [sender window];
+ [self cancelCurrentJobAndContinue];
}
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:alertTitle];
- [alert setInformativeText:NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil)];
- [alert addButtonWithTitle:NSLocalizedString(@"Keep Encoding", nil)];
- [alert addButtonWithTitle:NSLocalizedString(@"Stop Encoding and Delete", nil)];
- [alert setAlertStyle:NSCriticalAlertStyle];
-
- [alert beginSheetModalForWindow:docWindow
- modalDelegate:self
- didEndSelector:@selector(didDimissCancelCurrentJob:returnCode:contextInfo:)
- contextInfo:(__bridge void *)(job)];
- }
- else
- {
- // since we are not a currently encoding item, we can just be cancelled
- HBJob *item = [[self.jobs[row] representedObject] copy];
- [self.controller rescanJobToMainWindow:item];
-
- // Now that source is loaded and settings applied, delete the queue item from the queue
- [self removeQueueItemAtIndex:row];
+ [self.jobs beginTransaction];
+ [self.controller openJob:job];
+ [self removeQueueItemAtIndex:index];
+ [self.jobs commit];
}
}
- (IBAction)clearAll:(id)sender
{
[self.jobs beginTransaction];
- [self removeItemsUsingBlock:^BOOL(HBJob *item) {
+ [self.jobs removeObjectsUsingBlock:^BOOL(HBJob *item) {
return (item.state != HBJobStateWorking);
}];
[self.jobs commit];
@@ -1038,7 +1068,7 @@
- (IBAction)clearCompleted:(id)sender
{
[self.jobs beginTransaction];
- [self removeItemsUsingBlock:^BOOL(HBJob *item) {
+ [self.jobs removeObjectsUsingBlock:^BOOL(HBJob *item) {
return (item.state == HBJobStateCompleted);
}];
[self.jobs commit];
@@ -1046,7 +1076,7 @@
}
#pragma mark -
-#pragma mark NSOutlineView delegate
+#pragma mark NSOutlineView data source
- (id)outlineView:(NSOutlineView *)fOutlineView child:(NSInteger)index ofItem:(id)item
{
@@ -1056,7 +1086,7 @@
}
// 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.");
+ NSAssert(NO, @"HBQueueController outlineView:child:ofItem: can't handle nested items.");
return nil;
}
@@ -1088,23 +1118,25 @@
}
}
+#pragma mark NSOutlineView delegate
+
- (void)outlineViewItemDidCollapse:(NSNotification *)notification
{
- id item = [notification userInfo][@"NSObject"];
+ id item = notification.userInfo[@"NSObject"];
NSInteger row = [self.outlineView rowForItem:item];
[self.outlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
}
- (void)outlineViewItemDidExpand:(NSNotification *)notification
{
- id item = [notification userInfo][@"NSObject"];
+ id item = notification.userInfo[@"NSObject"];
NSInteger row = [self.outlineView rowForItem:item];
[self.outlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
}
- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
{
- if ([outlineView isItemExpanded: item])
+ if ([outlineView isItemExpanded:item])
{
// It is important to use a constant value when calculating the height. Querying the tableColumn width will not work, since it dynamically changes as the user resizes -- however, we don't get a notification that the user "did resize" it until after the mouse is let go. We use the latter as a hook for telling the table that the heights changed. We must return the same height from this method every time, until we tell the table the heights have changed. Not doing so will quicly cause drawing problems.
NSTableColumn *tableColumnToWrap = (NSTableColumn *) [outlineView tableColumns][1];
@@ -1177,15 +1209,7 @@
*/
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
- 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.
-
- // 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"])
+ if ([tableColumn.identifier isEqualToString:@"action"])
{
[cell setEnabled: YES];
BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView);
@@ -1193,7 +1217,7 @@
HBJob *job = item;
if (job.state == HBJobStateCompleted)
{
- [cell setAction: @selector(revealSelectedQueueItem:)];
+ [cell setAction: @selector(revealSelectedQueueItems:)];
if (highlighted)
{
[cell setImage:[NSImage imageNamed:@"RevealHighlight"]];
@@ -1230,7 +1254,6 @@
}
}
-#pragma mark -
#pragma mark NSOutlineView delegate (dragging related)
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
@@ -1241,8 +1264,6 @@
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.
self.dragNodesArray = items;
// Provide data for our custom type, and simple NSStrings.
@@ -1285,18 +1306,28 @@
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
{
- NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
+ [self.jobs beginTransaction];
+ [self.outlineView beginUpdates];
- for (id obj in self.dragNodesArray)
+ for (id object in self.dragNodesArray.reverseObjectEnumerator)
{
- [moveItems addIndex:[self.jobs indexOfObject:obj]];
+ NSUInteger sourceIndex = [self.jobs indexOfObject:object];
+ [self.jobs removeObjectAtIndex:sourceIndex];
+
+ if (sourceIndex < index)
+ {
+ index--;
+ }
+
+ [self.jobs insertObject:object atIndex:index];
+
+ NSUInteger destIndex = [self.jobs indexOfObject:object];
+
+ [self.outlineView moveItemAtIndex:sourceIndex inParent:nil toIndex:destIndex inParent:nil];
}
- // 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.
- [self moveObjectsInQueueArray:self.jobs fromIndexes:moveItems toIndex: index];
+ [self.outlineView endUpdates];
+ [self.jobs commit];
return YES;
}
diff --git a/macosx/HBQueueOutlineView.h b/macosx/HBQueueOutlineView.h
index 20401f241..7422973db 100644
--- a/macosx/HBQueueOutlineView.h
+++ b/macosx/HBQueueOutlineView.h
@@ -26,7 +26,7 @@
// By default, NSTableView only drags an image of the first column. Change this to
// drag an image of the queue's icon and desc columns.
-@protocol HBQueueOutlineViewDelegate
+@protocol HBQueueOutlineViewDelegate <NSOutlineViewDelegate>
@optional
- (void)HB_deleteSelectionFromTableView:(NSTableView *)tableView;
diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj
index ab56a9337..e6ae63dc1 100644
--- a/macosx/HandBrake.xcodeproj/project.pbxproj
+++ b/macosx/HandBrake.xcodeproj/project.pbxproj
@@ -143,6 +143,7 @@
A93E0ED31972957000FD67FB /* HBVideoController.m in Sources */ = {isa = PBXBuildFile; fileRef = A93E0ED11972957000FD67FB /* HBVideoController.m */; };
A93E0ED71972958C00FD67FB /* Video.xib in Resources */ = {isa = PBXBuildFile; fileRef = A93E0ED51972958C00FD67FB /* Video.xib */; };
A93FD4751A62ABE800A6AC43 /* HBAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = A93FD4741A62ABE800A6AC43 /* HBAudio.m */; };
+ A95121E61B5F7BE700FD773D /* NSArray+HBAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A95121E51B5F7BE700FD773D /* NSArray+HBAdditions.m */; };
A9523937199A6AAE00588AEF /* HBFilters.m in Sources */ = {isa = PBXBuildFile; fileRef = A9523936199A6AAE00588AEF /* HBFilters.m */; };
A9537BF01A48A85C00141102 /* HBJob+UIAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A9537BEF1A48A85C00141102 /* HBJob+UIAdditions.m */; };
A9537BF31A48A99500141102 /* HBVideo+UIAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A9537BF21A48A99500141102 /* HBVideo+UIAdditions.m */; };
@@ -421,6 +422,8 @@
A93E0ED61972958C00FD67FB /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Video.xib; sourceTree = "<group>"; };
A93FD4731A62ABE800A6AC43 /* HBAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBAudio.h; sourceTree = "<group>"; };
A93FD4741A62ABE800A6AC43 /* HBAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBAudio.m; sourceTree = "<group>"; };
+ A95121E41B5F7BE700FD773D /* NSArray+HBAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+HBAdditions.h"; sourceTree = "<group>"; };
+ A95121E51B5F7BE700FD773D /* NSArray+HBAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+HBAdditions.m"; sourceTree = "<group>"; };
A9523935199A6AAE00588AEF /* HBFilters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBFilters.h; sourceTree = "<group>"; };
A9523936199A6AAE00588AEF /* HBFilters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBFilters.m; sourceTree = "<group>"; };
A9537BEE1A48A85C00141102 /* HBJob+UIAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HBJob+UIAdditions.h"; sourceTree = "<group>"; };
@@ -751,6 +754,8 @@
A9E66D6F1A67A2A8007B641D /* HBDistributedArray.m */,
A98C29C21977B10600AF5DED /* HBLanguagesSelection.h */,
A98C29C31977B10600AF5DED /* HBLanguagesSelection.m */,
+ A95121E41B5F7BE700FD773D /* NSArray+HBAdditions.h */,
+ A95121E51B5F7BE700FD773D /* NSArray+HBAdditions.m */,
A9B34D711976844500871B7D /* UI Views */,
273F20BD14ADC09F0021BE6D /* main.mm */,
);
@@ -1396,6 +1401,7 @@
A91AFD0C1A948827009BECED /* HBOutputFileWriter.m in Sources */,
A9906B2C1A710920001D82D5 /* HBQueueController.m in Sources */,
A9CF25F41990D64E0023F727 /* HBPreset.m in Sources */,
+ A95121E61B5F7BE700FD773D /* NSArray+HBAdditions.m in Sources */,
A9DEC8741A23C87500C79B48 /* HBCore.m in Sources */,
A9F4728D1976BAA70009EC65 /* HBSubtitlesDefaults.m in Sources */,
A93E0ED31972957000FD67FB /* HBVideoController.m in Sources */,
diff --git a/macosx/NSArray+HBAdditions.h b/macosx/NSArray+HBAdditions.h
new file mode 100644
index 000000000..deb2796a4
--- /dev/null
+++ b/macosx/NSArray+HBAdditions.h
@@ -0,0 +1,23 @@
+//
+// NSArray+NSArray_HBArrayAdditions.h
+// HandBrake
+//
+// Created by Damiano Galassi on 22/07/15.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSMutableArray (HBAdditions)
+
+- (void)removeObjectsUsingBlock:(BOOL (^)(id object))block;
+
+@end
+
+
+@interface NSArray (HBAdditions)
+
+- (NSArray *)filteredArrayUsingBlock:(BOOL (^)(id object))block;
+- (NSIndexSet *)indexesOfObjectsUsingBlock:(BOOL (^)(id object))block;
+
+@end \ No newline at end of file
diff --git a/macosx/NSArray+HBAdditions.m b/macosx/NSArray+HBAdditions.m
new file mode 100644
index 000000000..babcc9f15
--- /dev/null
+++ b/macosx/NSArray+HBAdditions.m
@@ -0,0 +1,58 @@
+//
+// NSArray+NSArray_HBArrayAdditions.m
+// HandBrake
+//
+// Created by Damiano Galassi on 22/07/15.
+//
+//
+
+#import "NSArray+HBAdditions.h"
+
+@implementation NSMutableArray (HBAdditions)
+
+- (void)removeObjectsUsingBlock:(BOOL (^)(id object))block
+{
+ NSMutableArray *objectsToRemove = [NSMutableArray array];
+ for (id object in self)
+ {
+ if (block(object))
+ {
+ [objectsToRemove addObject:object];
+ }
+ }
+ [self removeObjectsInArray:objectsToRemove];
+}
+
+@end
+
+@implementation NSArray (HBAdditions)
+
+- (NSArray *)filteredArrayUsingBlock:(BOOL (^)(id object))block
+{
+ NSMutableArray *filteredArray = [NSMutableArray array];
+ for (id object in self)
+ {
+ if (block(object))
+ {
+ [filteredArray addObject:object];
+ }
+ }
+ return [filteredArray copy];
+}
+
+- (NSIndexSet *)indexesOfObjectsUsingBlock:(BOOL (^)(id object))block
+{
+ NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
+ NSUInteger i = 0;
+ for (id object in self)
+ {
+ if (block(object))
+ {
+ [indexes addIndex:i];
+ }
+ i++;
+ }
+ return [indexes copy];
+}
+
+@end