summaryrefslogtreecommitdiffstats
path: root/macosx
diff options
context:
space:
mode:
Diffstat (limited to 'macosx')
-rw-r--r--macosx/Base.lproj/HBQueueDetailsViewController.xib96
-rw-r--r--macosx/Base.lproj/HBQueueTableViewController.xib178
-rw-r--r--macosx/Base.lproj/MainMenu.xib8
-rw-r--r--macosx/Base.lproj/MainWindow.xib22
-rw-r--r--macosx/Base.lproj/Queue.xib319
-rw-r--r--macosx/HBAppDelegate.h10
-rw-r--r--macosx/HBAppDelegate.m43
-rw-r--r--macosx/HBController.h11
-rw-r--r--macosx/HBController.m154
-rw-r--r--macosx/HBJob+UIAdditions.h2
-rw-r--r--macosx/HBJob+UIAdditions.m68
-rw-r--r--macosx/HBQueue.h93
-rw-r--r--macosx/HBQueue.m765
-rw-r--r--macosx/HBQueueController.h27
-rw-r--r--macosx/HBQueueController.m1457
-rw-r--r--macosx/HBQueueDetailsViewController.h27
-rw-r--r--macosx/HBQueueDetailsViewController.m65
-rw-r--r--macosx/HBQueueItem.m18
-rw-r--r--macosx/HBQueueItemView.h3
-rw-r--r--macosx/HBQueueItemView.m25
-rw-r--r--macosx/HBQueueTableViewController.h29
-rw-r--r--macosx/HBQueueTableViewController.m339
-rw-r--r--macosx/HandBrake.xcodeproj/project.pbxproj46
23 files changed, 2181 insertions, 1624 deletions
diff --git a/macosx/Base.lproj/HBQueueDetailsViewController.xib b/macosx/Base.lproj/HBQueueDetailsViewController.xib
new file mode 100644
index 000000000..278805b95
--- /dev/null
+++ b/macosx/Base.lproj/HBQueueDetailsViewController.xib
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="HBQueueDetailsViewController">
+ <connections>
+ <outlet property="detailsLabel" destination="tBk-3B-YhZ" id="UvN-Op-Nf2"/>
+ <outlet property="scrollView" destination="jKW-nH-vE3" id="DWg-bJ-i8M"/>
+ <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customView id="Hz6-mo-xeY">
+ <rect key="frame" x="0.0" y="0.0" width="480" height="98"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <subviews>
+ <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Z14-gh-hWn">
+ <rect key="frame" x="402" y="13" width="64" height="32"/>
+ <buttonCell key="cell" type="push" title="Edit" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="erY-5X-50l">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="editItem:" target="-2" id="ozj-G7-6d5"/>
+ <binding destination="-2" name="enabled" keyPath="self.item" id="Nss-YT-PPX">
+ <dictionary key="options">
+ <string key="NSValueTransformerName">NSIsNotNil</string>
+ </dictionary>
+ </binding>
+ </connections>
+ </button>
+ <scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="jKW-nH-vE3">
+ <rect key="frame" x="20" y="61" width="440" height="17"/>
+ <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="Nz1-nG-orF">
+ <rect key="frame" x="0.0" y="0.0" width="440" height="17"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tBk-3B-YhZ">
+ <rect key="frame" x="0.0" y="0.0" width="440" height="17"/>
+ <textFieldCell key="cell" selectable="YES" allowsUndo="NO" title="Multiline Label" allowsEditingTextAttributes="YES" id="cFx-4D-4X9">
+ <font key="font" usesAppearanceFont="YES"/>
+ <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ </subviews>
+ <constraints>
+ <constraint firstAttribute="trailing" secondItem="tBk-3B-YhZ" secondAttribute="trailing" constant="2" id="Jct-L0-ooa"/>
+ <constraint firstItem="tBk-3B-YhZ" firstAttribute="leading" secondItem="Nz1-nG-orF" secondAttribute="leading" constant="2" id="a3e-oE-U6F"/>
+ <constraint firstItem="tBk-3B-YhZ" firstAttribute="top" secondItem="Nz1-nG-orF" secondAttribute="top" id="gKz-v4-CFb"/>
+ </constraints>
+ </clipView>
+ <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Lon-kY-Grs">
+ <rect key="frame" x="-100" y="-100" width="444" height="16"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="vPy-U2-zcL">
+ <rect key="frame" x="429" y="1" width="16" height="17"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ </scrollView>
+ <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oq4-g0-srt">
+ <rect key="frame" x="330" y="13" width="75" height="32"/>
+ <buttonCell key="cell" type="push" title="Reset" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="hgl-N3-x4S">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="resetItem:" target="-2" id="42R-t4-FxO"/>
+ <binding destination="-2" name="enabled" keyPath="self.item" id="L2a-Rh-3D1">
+ <dictionary key="options">
+ <string key="NSValueTransformerName">NSIsNotNil</string>
+ </dictionary>
+ </binding>
+ </connections>
+ </button>
+ </subviews>
+ <constraints>
+ <constraint firstItem="Z14-gh-hWn" firstAttribute="leading" secondItem="oq4-g0-srt" secondAttribute="trailing" constant="9" id="3oM-tb-KiT"/>
+ <constraint firstItem="oq4-g0-srt" firstAttribute="firstBaseline" secondItem="Z14-gh-hWn" secondAttribute="firstBaseline" id="95H-sb-RO5"/>
+ <constraint firstAttribute="trailing" secondItem="jKW-nH-vE3" secondAttribute="trailing" constant="20" id="Cdo-eU-VL6"/>
+ <constraint firstAttribute="bottom" secondItem="Z14-gh-hWn" secondAttribute="bottom" constant="20" id="CwL-gc-KKJ"/>
+ <constraint firstItem="Z14-gh-hWn" firstAttribute="top" secondItem="jKW-nH-vE3" secondAttribute="bottom" constant="20" id="TC5-6s-DmD"/>
+ <constraint firstItem="jKW-nH-vE3" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" id="TWB-aM-yau"/>
+ <constraint firstAttribute="trailing" secondItem="Z14-gh-hWn" secondAttribute="trailing" constant="20" id="tSm-Qq-U0q"/>
+ <constraint firstItem="jKW-nH-vE3" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="20" id="zXd-jW-bKU"/>
+ </constraints>
+ <point key="canvasLocation" x="140" y="306.5"/>
+ </customView>
+ </objects>
+</document>
diff --git a/macosx/Base.lproj/HBQueueTableViewController.xib b/macosx/Base.lproj/HBQueueTableViewController.xib
new file mode 100644
index 000000000..8eb989790
--- /dev/null
+++ b/macosx/Base.lproj/HBQueueTableViewController.xib
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="HBQueueTableViewController">
+ <connections>
+ <outlet property="tableView" destination="VV7-t7-Ufd" id="VSC-MG-zZW"/>
+ <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customView id="Hz6-mo-xeY">
+ <rect key="frame" x="0.0" y="0.0" width="543" height="150"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <subviews>
+ <scrollView autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="10" verticalLineScroll="24" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aFR-JM-AgT">
+ <rect key="frame" x="-1" y="-1" width="545" height="152"/>
+ <clipView key="contentView" drawsBackground="NO" id="Q5n-II-uln">
+ <rect key="frame" x="1" y="1" width="543" height="150"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" columnSelection="YES" autosaveColumns="NO" rowHeight="22" rowSizeStyle="automatic" viewBased="YES" id="VV7-t7-Ufd" customClass="HBTableView">
+ <rect key="frame" x="0.0" y="0.0" width="543" height="150"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <size key="intercellSpacing" width="3" height="2"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
+ <tableColumns>
+ <tableColumn width="540" minWidth="40" maxWidth="3000" id="QfC-OR-ahq">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="K6c-HW-um8">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <prototypeCellViews>
+ <tableCellView identifier="MainSimpleCell" id="Til-BP-Zag" customClass="HBQueueItemView">
+ <rect key="frame" x="1" y="1" width="540" height="22"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ns0-GA-WCh">
+ <rect key="frame" x="21" y="3" width="494" height="17"/>
+ <constraints>
+ <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="17" id="5Tc-Oy-HWg"/>
+ </constraints>
+ <textFieldCell key="cell" enabled="NO" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Table View Cell" id="xSd-Hl-5Qk">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CSU-Pb-Uaw">
+ <rect key="frame" x="521" y="4" width="16" height="16"/>
+ <constraints>
+ <constraint firstAttribute="height" constant="16" id="NiA-3H-N2G"/>
+ <constraint firstAttribute="width" constant="16" id="cei-uT-jX0"/>
+ </constraints>
+ <buttonCell key="cell" type="bevel" bezelStyle="rounded" image="Delete" imagePosition="only" alignment="center" alternateImage="DeleteHighlightPressed" id="LOA-aO-spo">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ </button>
+ <imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="kNw-fy-iYB">
+ <rect key="frame" x="3" y="5" width="14" height="14"/>
+ <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSActionTemplate" id="ofG-k5-PfV"/>
+ </imageView>
+ </subviews>
+ <constraints>
+ <constraint firstItem="Ns0-GA-WCh" firstAttribute="leading" secondItem="kNw-fy-iYB" secondAttribute="trailing" constant="6" id="KxO-Fj-XQO"/>
+ <constraint firstItem="kNw-fy-iYB" firstAttribute="leading" secondItem="Til-BP-Zag" secondAttribute="leading" constant="3" id="LIY-lT-8dm"/>
+ <constraint firstAttribute="trailing" secondItem="CSU-Pb-Uaw" secondAttribute="trailing" constant="3" id="dIt-QV-ws7"/>
+ <constraint firstItem="Ns0-GA-WCh" firstAttribute="top" secondItem="Til-BP-Zag" secondAttribute="top" constant="2" id="emx-1Y-YDk"/>
+ <constraint firstItem="CSU-Pb-Uaw" firstAttribute="top" secondItem="Til-BP-Zag" secondAttribute="top" constant="2" id="g9F-L6-mfh"/>
+ <constraint firstItem="CSU-Pb-Uaw" firstAttribute="leading" secondItem="Ns0-GA-WCh" secondAttribute="trailing" constant="8" id="nds-NF-PaH"/>
+ <constraint firstItem="kNw-fy-iYB" firstAttribute="top" secondItem="Til-BP-Zag" secondAttribute="top" constant="3" id="r8c-Vs-yih"/>
+ </constraints>
+ <connections>
+ <outlet property="imageView" destination="kNw-fy-iYB" id="PMS-Sv-QjR"/>
+ <outlet property="removeButton" destination="CSU-Pb-Uaw" id="zX0-eM-VbX"/>
+ <outlet property="textField" destination="Ns0-GA-WCh" id="vvk-OU-Psw"/>
+ </connections>
+ </tableCellView>
+ </prototypeCellViews>
+ </tableColumn>
+ </tableColumns>
+ <connections>
+ <outlet property="dataSource" destination="-2" id="Jhm-K7-pBS"/>
+ <outlet property="delegate" destination="-2" id="22i-mE-trX"/>
+ <outlet property="menu" destination="42P-N1-a6j" id="5Zc-oy-bJ0"/>
+ </connections>
+ </tableView>
+ </subviews>
+ <nil key="backgroundColor"/>
+ </clipView>
+ <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="JPN-K8-xyG">
+ <rect key="frame" x="1" y="139" width="541" height="16"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="VMM-tz-dQ6">
+ <rect key="frame" x="224" y="17" width="15" height="102"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ </scrollView>
+ </subviews>
+ <constraints>
+ <constraint firstItem="aFR-JM-AgT" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="-1" id="dbq-rc-NhT"/>
+ <constraint firstAttribute="trailing" secondItem="aFR-JM-AgT" secondAttribute="trailing" constant="-1" id="pkZ-zZ-B9T"/>
+ <constraint firstItem="aFR-JM-AgT" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="-1" id="q6u-sa-SjI"/>
+ <constraint firstAttribute="bottom" secondItem="aFR-JM-AgT" secondAttribute="bottom" constant="-1" id="uKJ-B6-0sE"/>
+ </constraints>
+ <point key="canvasLocation" x="-32.5" y="-53"/>
+ </customView>
+ <menu id="42P-N1-a6j" userLabel="ContextMenu">
+ <items>
+ <menuItem title="Show Source in Finder" id="Pps-jm-zcd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="revealSelectedQueueItemsSources:" target="-2" id="Nbh-Zc-E4X"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show Destination in Finder" id="c1Z-ZC-b8b">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="revealSelectedQueueItems:" target="-2" id="s3C-6U-Ycl"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="cVv-nf-8tx"/>
+ <menuItem title="Edit Job Settings" id="DTF-om-j0l">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="editSelectedQueueItem:" target="-2" id="drH-QA-nh8"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Reset Stopped Job" id="xMs-43-lL0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="resetJobState:" target="-2" id="nsj-r6-Vd4"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="ozr-b8-nig"/>
+ <menuItem title="Remove All Jobs" id="5AR-we-iHC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="removeAll:" target="-2" id="IRO-VC-N0n"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Remove Completed Jobs" id="gNj-D1-wa7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="removeCompleted:" target="-2" id="nUM-Me-Fr2"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Remove Selected Job" id="egD-8R-PTJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="removeSelectedQueueItem:" target="-2" id="EIy-d4-yhJ"/>
+ </connections>
+ </menuItem>
+ </items>
+ <point key="canvasLocation" x="-152" y="642"/>
+ </menu>
+ </objects>
+ <resources>
+ <image name="Delete" width="16" height="16"/>
+ <image name="DeleteHighlightPressed" width="16" height="16"/>
+ <image name="NSActionTemplate" width="14" height="14"/>
+ </resources>
+</document>
diff --git a/macosx/Base.lproj/MainMenu.xib b/macosx/Base.lproj/MainMenu.xib
index 4c6e39b68..26adb990c 100644
--- a/macosx/Base.lproj/MainMenu.xib
+++ b/macosx/Base.lproj/MainMenu.xib
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.68" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment identifier="macosx"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.68"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -123,12 +123,12 @@
</menuItem>
<menuItem title="Start Encoding" keyEquivalent="s" id="2444">
<connections>
- <action selector="rip:" target="-1" id="gca-l6-qac"/>
+ <action selector="toggleStartCancel:" target="-1" id="l0f-rM-50p"/>
</connections>
</menuItem>
<menuItem title="Pause Encoding" keyEquivalent="p" id="2494">
<connections>
- <action selector="pause:" target="-1" id="tq4-oz-OV9"/>
+ <action selector="togglePauseResume:" target="-1" id="Di1-je-YJZ"/>
</connections>
</menuItem>
</items>
diff --git a/macosx/Base.lproj/MainWindow.xib b/macosx/Base.lproj/MainWindow.xib
index 1dcd4f4d0..d691c3cb5 100644
--- a/macosx/Base.lproj/MainWindow.xib
+++ b/macosx/Base.lproj/MainWindow.xib
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.68" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.68"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -40,7 +40,7 @@
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="41" y="572" width="885" height="600"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
<view key="contentView" id="2" customClass="HBFocusRingView">
<rect key="frame" x="0.0" y="0.0" width="885" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -53,7 +53,7 @@
<tabViewItems>
<tabViewItem label="Summary" identifier="" id="BA0-eg-2Ka">
<view key="view" id="BjX-E2-6tb">
- <rect key="frame" x="10" y="25" width="839" height="344"/>
+ <rect key="frame" x="10" y="29" width="839" height="340"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
</tabViewItem>
@@ -428,6 +428,11 @@
</popUpButtonCell>
<connections>
<accessibilityConnection property="title" destination="5180" id="1bt-ea-b2b"/>
+ <binding destination="-2" name="enabled" keyPath="self.job" id="pA6-GK-w84">
+ <dictionary key="options">
+ <string key="NSValueTransformerName">NSIsNotNil</string>
+ </dictionary>
+ </binding>
<binding destination="-2" name="selectedValue" keyPath="self.job.angle" previousBinding="If4-Fp-R0g" id="3Gk-ma-r2w">
<dictionary key="options">
<string key="NSNullPlaceholder" base64-UTF8="YES">
@@ -435,11 +440,6 @@ IA
</string>
</dictionary>
</binding>
- <binding destination="-2" name="enabled" keyPath="self.job" id="pA6-GK-w84">
- <dictionary key="options">
- <string key="NSValueTransformerName">NSIsNotNil</string>
- </dictionary>
- </binding>
<binding destination="-2" name="content" keyPath="self.job.angles" id="If4-Fp-R0g"/>
<binding destination="5676" name="hidden" keyPath="values.UseDvdNav" previousBinding="pA6-GK-w84" id="Ocv-Hr-bje">
<dictionary key="options">
@@ -730,12 +730,12 @@ Blu-ray and DVD sources often have multiple titles, the longest of which is typi
</toolbarItem>
<toolbarItem implicitItemIdentifier="10063EA1-C821-4363-8F59-7840853EB568" label="Start" paletteLabel="Start Encoding" toolTip="Start Encoding" tag="-1" image="encode" id="byg-kj-sEM">
<connections>
- <action selector="rip:" target="-2" id="f6w-0B-Qvn"/>
+ <action selector="toggleStartCancel:" target="-2" id="6gH-Cz-Nuo"/>
</connections>
</toolbarItem>
<toolbarItem implicitItemIdentifier="B4A5CE50-6CD5-4CD0-B639-E1516E1C85C1" label="Pause" paletteLabel="Pause Encoding" toolTip="Pause Encoding" tag="-1" image="pauseencode" id="wTQ-KF-5KW">
<connections>
- <action selector="pause:" target="-2" id="AN1-8V-lc1"/>
+ <action selector="togglePauseResume:" target="-2" id="AN1-8V-lc1"/>
</connections>
</toolbarItem>
<toolbarItem implicitItemIdentifier="3B6A53EE-F973-41A7-A0DF-AEBD73DAC28E" label="Add To Queue" paletteLabel="Add To Queue" toolTip="Add To Queue" tag="-1" image="addqueue" id="DZZ-Fe-wjw">
diff --git a/macosx/Base.lproj/Queue.xib b/macosx/Base.lproj/Queue.xib
index bb379a101..bf105a3fa 100644
--- a/macosx/Base.lproj/Queue.xib
+++ b/macosx/Base.lproj/Queue.xib
@@ -1,18 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.59" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.59"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="HBQueueController">
<connections>
- <outlet property="countTextField" destination="2511" id="7vs-Ty-tNx"/>
<outlet property="pauseToolbarItem" destination="s7o-pK-heI" id="SP7-Fq-gw9"/>
- <outlet property="progressTextField" destination="2646" id="E60-Gv-b2q"/>
<outlet property="ripToolbarItem" destination="SX6-mq-Hck" id="va2-0n-3T0"/>
- <outlet property="tableView" destination="Zhj-ec-Xhl" id="4Ki-XG-eF7"/>
<outlet property="window" destination="2576" id="2645"/>
</connections>
</customObject>
@@ -23,205 +20,11 @@
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="157" y="863" width="583" height="423"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
<value key="minSize" type="size" width="525" height="340"/>
<view key="contentView" id="2577">
<rect key="frame" x="0.0" y="0.0" width="583" height="423"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsCharacterPickerTouchBarItem="YES" preferredMaxLayoutWidth="6000" translatesAutoresizingMaskIntoConstraints="NO" id="2511">
- <rect key="frame" x="18" y="399" width="547" height="14"/>
- <textFieldCell key="cell" controlSize="small" sendsActionOnEndEditing="YES" title="Pending Jobs" id="2637">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsCharacterPickerTouchBarItem="YES" preferredMaxLayoutWidth="6000" translatesAutoresizingMaskIntoConstraints="NO" id="2646">
- <rect key="frame" x="18" y="365" width="547" height="30"/>
- <constraints>
- <constraint firstAttribute="height" constant="30" id="IvQ-56-oOt"/>
- </constraints>
- <textFieldCell key="cell" controlSize="small" sendsActionOnEndEditing="YES" alignment="left" title="There are no jobs currently encoding" id="2647">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <scrollView autohidesScrollers="YES" horizontalLineScroll="22" horizontalPageScroll="10" verticalLineScroll="22" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="V8p-UJ-HY0">
- <rect key="frame" x="20" y="20" width="543" height="337"/>
- <clipView key="contentView" drawsBackground="NO" id="U2w-5W-t6z">
- <rect key="frame" x="1" y="1" width="541" height="335"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" columnSelection="YES" autosaveColumns="NO" rowHeight="20" rowSizeStyle="automatic" viewBased="YES" id="Zhj-ec-Xhl" customClass="HBTableView">
- <rect key="frame" x="0.0" y="0.0" width="541" height="335"/>
- <autoresizingMask key="autoresizingMask"/>
- <size key="intercellSpacing" width="3" height="2"/>
- <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
- <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
- <tableColumns>
- <tableColumn width="538" minWidth="40" maxWidth="3000" id="bXr-Oy-mqu">
- <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
- </tableHeaderCell>
- <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="MWq-ie-HjX">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
- <prototypeCellViews>
- <tableCellView identifier="MainCellForSizing" id="fwz-hO-Ryv" customClass="HBQueueItemView">
- <rect key="frame" x="1" y="1" width="538" height="20"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zFo-Hu-qOb">
- <rect key="frame" x="3" y="4" width="13" height="13"/>
- <buttonCell key="cell" type="disclosureTriangle" bezelStyle="disclosure" imagePosition="above" alignment="left" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="vwa-tA-Eef">
- <behavior key="behavior" pushIn="YES" changeBackground="YES" changeGray="YES" lightByContents="YES"/>
- <font key="font" metaFont="system"/>
- </buttonCell>
- </button>
- <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ytl-nJ-QF2">
- <rect key="frame" x="36" y="2" width="482" height="17"/>
- <constraints>
- <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="17" id="KX1-hQ-cAs"/>
- </constraints>
- <textFieldCell key="cell" enabled="NO" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Table View Cell" id="anR-ZS-eOK">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="iLH-IM-e3U">
- <rect key="frame" x="20" y="4" width="14" height="14"/>
- <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSActionTemplate" id="Fd0-UE-Y9E"/>
- </imageView>
- <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3iF-mi-Ofb">
- <rect key="frame" x="519" y="3" width="16" height="16"/>
- <constraints>
- <constraint firstAttribute="width" constant="16" id="YU8-rD-aOs"/>
- <constraint firstAttribute="height" constant="16" id="zUz-oi-p3D"/>
- </constraints>
- <buttonCell key="cell" type="bevel" bezelStyle="rounded" image="Delete" imagePosition="only" alignment="center" alternateImage="DeleteHighlightPressed" id="6ta-Ak-dc3">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- </buttonCell>
- </button>
- </subviews>
- <constraints>
- <constraint firstItem="3iF-mi-Ofb" firstAttribute="leading" secondItem="ytl-nJ-QF2" secondAttribute="trailing" constant="3" id="B8U-H8-LVU"/>
- <constraint firstItem="3iF-mi-Ofb" firstAttribute="top" secondItem="fwz-hO-Ryv" secondAttribute="top" constant="1" id="Fc7-bU-swQ"/>
- <constraint firstAttribute="bottom" secondItem="ytl-nJ-QF2" secondAttribute="bottom" constant="2" id="PoD-SD-75N"/>
- <constraint firstItem="zFo-Hu-qOb" firstAttribute="leading" secondItem="fwz-hO-Ryv" secondAttribute="leading" constant="3" id="Tlt-5u-MdO"/>
- <constraint firstItem="iLH-IM-e3U" firstAttribute="centerY" secondItem="zFo-Hu-qOb" secondAttribute="centerY" id="XMi-MB-91O"/>
- <constraint firstItem="zFo-Hu-qOb" firstAttribute="top" secondItem="fwz-hO-Ryv" secondAttribute="top" constant="3" id="ita-PD-WDw"/>
- <constraint firstAttribute="trailing" secondItem="3iF-mi-Ofb" secondAttribute="trailing" constant="3" id="pYB-wp-Ngx"/>
- <constraint firstItem="ytl-nJ-QF2" firstAttribute="leading" secondItem="iLH-IM-e3U" secondAttribute="trailing" constant="4" id="uLz-ZO-W6Z"/>
- <constraint firstItem="iLH-IM-e3U" firstAttribute="leading" secondItem="zFo-Hu-qOb" secondAttribute="trailing" constant="4" id="utq-Rq-M1K"/>
- <constraint firstItem="ytl-nJ-QF2" firstAttribute="top" secondItem="fwz-hO-Ryv" secondAttribute="top" constant="1" id="wzO-Uy-p6R"/>
- </constraints>
- <connections>
- <outlet property="expandButton" destination="zFo-Hu-qOb" id="DCW-dQ-Jt2"/>
- <outlet property="imageView" destination="iLH-IM-e3U" id="pnj-VU-8lk"/>
- <outlet property="removeButton" destination="3iF-mi-Ofb" id="okE-58-pJ0"/>
- <outlet property="textField" destination="ytl-nJ-QF2" id="ujr-58-CSG"/>
- </connections>
- </tableCellView>
- <tableCellView identifier="MainCell" id="WdL-72-BXn" customClass="HBQueueItemView">
- <rect key="frame" x="1" y="23" width="538" height="20"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hDo-Zl-9lr">
- <rect key="frame" x="3" y="4" width="13" height="13"/>
- <buttonCell key="cell" type="disclosureTriangle" bezelStyle="disclosure" imagePosition="above" alignment="left" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="gix-c5-O2h">
- <behavior key="behavior" pushIn="YES" changeBackground="YES" changeGray="YES" lightByContents="YES"/>
- <font key="font" metaFont="system"/>
- </buttonCell>
- </button>
- <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="93s-90-w6h">
- <rect key="frame" x="36" y="2" width="482" height="17"/>
- <constraints>
- <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="17" id="fPa-ff-vgB"/>
- </constraints>
- <textFieldCell key="cell" enabled="NO" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Table View Cell" id="F1i-sW-mz6">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="fGK-O0-x2n">
- <rect key="frame" x="20" y="4" width="14" height="14"/>
- <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSActionTemplate" id="sjq-oR-z7j"/>
- </imageView>
- <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="J7T-PN-aVk">
- <rect key="frame" x="519" y="3" width="16" height="16"/>
- <constraints>
- <constraint firstAttribute="width" constant="16" id="V0F-7D-TYq"/>
- <constraint firstAttribute="height" constant="16" id="fVl-JE-S8p"/>
- </constraints>
- <buttonCell key="cell" type="bevel" bezelStyle="rounded" image="Delete" imagePosition="only" alignment="center" alternateImage="DeleteHighlightPressed" id="DHN-sj-IkJ">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- </buttonCell>
- </button>
- </subviews>
- <constraints>
- <constraint firstItem="hDo-Zl-9lr" firstAttribute="leading" secondItem="WdL-72-BXn" secondAttribute="leading" constant="3" id="46G-ka-wQC"/>
- <constraint firstItem="J7T-PN-aVk" firstAttribute="top" secondItem="WdL-72-BXn" secondAttribute="top" constant="1" id="Cnn-dM-JY0"/>
- <constraint firstAttribute="bottom" secondItem="93s-90-w6h" secondAttribute="bottom" constant="2" id="FUz-Ej-rNL"/>
- <constraint firstItem="93s-90-w6h" firstAttribute="leading" secondItem="fGK-O0-x2n" secondAttribute="trailing" constant="4" id="TXB-Wp-bvX"/>
- <constraint firstItem="fGK-O0-x2n" firstAttribute="leading" secondItem="hDo-Zl-9lr" secondAttribute="trailing" constant="4" id="agy-M6-bFJ"/>
- <constraint firstItem="93s-90-w6h" firstAttribute="top" secondItem="WdL-72-BXn" secondAttribute="top" constant="1" id="mkK-5a-3hE"/>
- <constraint firstAttribute="trailing" secondItem="J7T-PN-aVk" secondAttribute="trailing" constant="3" id="n4J-ih-N0C"/>
- <constraint firstItem="fGK-O0-x2n" firstAttribute="centerY" secondItem="hDo-Zl-9lr" secondAttribute="centerY" id="uBZ-4T-gyj"/>
- <constraint firstItem="J7T-PN-aVk" firstAttribute="leading" secondItem="93s-90-w6h" secondAttribute="trailing" constant="3" id="uCp-vf-aad"/>
- <constraint firstItem="hDo-Zl-9lr" firstAttribute="top" secondItem="WdL-72-BXn" secondAttribute="top" constant="3" id="ypH-kh-t70"/>
- </constraints>
- <connections>
- <outlet property="expandButton" destination="hDo-Zl-9lr" id="kd5-U2-oiI"/>
- <outlet property="imageView" destination="fGK-O0-x2n" id="6pO-2g-qSk"/>
- <outlet property="removeButton" destination="J7T-PN-aVk" id="17N-VN-hCz"/>
- <outlet property="textField" destination="93s-90-w6h" id="Bxj-ru-lZr"/>
- </connections>
- </tableCellView>
- </prototypeCellViews>
- </tableColumn>
- </tableColumns>
- <connections>
- <outlet property="dataSource" destination="-2" id="TEa-pj-uBd"/>
- <outlet property="delegate" destination="-2" id="DkB-HG-P9X"/>
- <outlet property="menu" destination="2649" id="3do-4Q-kGX"/>
- </connections>
- </tableView>
- </subviews>
- <nil key="backgroundColor"/>
- </clipView>
- <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="1UD-VE-aty">
- <rect key="frame" x="1" y="320" width="541" height="16"/>
- <autoresizingMask key="autoresizingMask"/>
- </scroller>
- <scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="Ory-ZM-JQ8">
- <rect key="frame" x="224" y="17" width="15" height="102"/>
- <autoresizingMask key="autoresizingMask"/>
- </scroller>
- </scrollView>
- </subviews>
- <constraints>
- <constraint firstItem="2511" firstAttribute="leading" secondItem="2577" secondAttribute="leading" constant="20" id="8Xc-AN-fPc"/>
- <constraint firstAttribute="trailing" secondItem="2511" secondAttribute="trailing" constant="20" id="Eu0-GV-JYK"/>
- <constraint firstAttribute="trailing" secondItem="V8p-UJ-HY0" secondAttribute="trailing" constant="20" id="JIB-NW-QyO"/>
- <constraint firstAttribute="trailing" secondItem="2646" secondAttribute="trailing" constant="20" id="QhH-jG-52I"/>
- <constraint firstItem="2646" firstAttribute="top" secondItem="2511" secondAttribute="bottom" constant="4" id="agE-FW-5eL"/>
- <constraint firstAttribute="bottom" secondItem="V8p-UJ-HY0" secondAttribute="bottom" constant="20" id="nkP-qP-x6z"/>
- <constraint firstItem="V8p-UJ-HY0" firstAttribute="top" secondItem="2646" secondAttribute="bottom" constant="8" id="pLi-au-o2H"/>
- <constraint firstItem="2511" firstAttribute="top" secondItem="2577" secondAttribute="top" constant="10" id="q87-Mh-5mE"/>
- <constraint firstItem="2646" firstAttribute="leading" secondItem="2577" secondAttribute="leading" constant="20" id="r9l-aI-qMG"/>
- <constraint firstItem="V8p-UJ-HY0" firstAttribute="leading" secondItem="2577" secondAttribute="leading" constant="20" id="uaI-oz-plH"/>
- </constraints>
</view>
<toolbar key="toolbar" implicitIdentifier="0FE76B40-49B7-48AE-B44E-D1B8034BC88A" explicitIdentifier="HBQueueToolbar" displayMode="iconAndLabel" sizeMode="regular" id="ZVb-ld-0UP">
<allowedToolbarItems>
@@ -237,9 +40,50 @@
</toolbarItem>
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="rHN-a0-oZQ"/>
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="QuV-M8-cet"/>
+ <toolbarItem implicitItemIdentifier="E914B732-9FA4-42E0-AFA6-353A8ACC406B" label="Actions" paletteLabel="Actions" image="NSActionTemplate" sizingBehavior="auto" id="DCB-WP-NRs">
+ <nil key="toolTip"/>
+ <popUpButton key="view" verticalHuggingPriority="750" id="Wfu-Hw-JBC">
+ <rect key="frame" x="4" y="14" width="40" height="24"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" pullsDown="YES" id="Zxj-rr-UnA">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="menu"/>
+ <menu key="menu" id="BBi-6p-GyW">
+ <items>
+ <menuItem image="NSActionTemplate" hidden="YES" id="l0x-1G-MEr"/>
+ <menuItem title="Reset All Jobs" id="kPX-am-UX1">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="resetAll:" target="-2" id="AYP-Za-Zyi"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Reset Failed Jobs" id="0zn-7P-JbR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="resetFailed:" target="-2" id="gJP-lg-vxx"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="9Kv-XR-d3o"/>
+ <menuItem title="Remove All Jobs" id="Tvo-xt-sag">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="removeAll:" target="-2" id="RYc-Bg-ORR"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Remove Completed Jobs" id="9tg-YJ-DuI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="removeCompleted:" target="-2" id="J6v-wU-JAD"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </popUpButtonCell>
+ </popUpButton>
+ </toolbarItem>
<toolbarItem implicitItemIdentifier="938D3EC6-1547-4AAB-86AF-B3FD3C7AF8BD" label="When Done" paletteLabel="When Done" toolTip="Action to perform when encoding is complete." id="a3c-kV-98E">
<size key="minSize" width="100" height="25"/>
- <size key="maxSize" width="210" height="25"/>
+ <size key="maxSize" width="200" 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"/>
@@ -262,74 +106,41 @@
</connections>
</popUpButton>
</toolbarItem>
+ <toolbarItem implicitItemIdentifier="963C2AD9-6708-4781-9411-3111E9713E3B" label="Details" paletteLabel="Details" image="NSGoLeftTemplate" sizingBehavior="auto" id="DfN-Iw-bzy">
+ <nil key="toolTip"/>
+ <button key="view" verticalHuggingPriority="750" id="KyK-r0-0eJ">
+ <rect key="frame" x="6" y="14" width="32" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="NSGoLeftTemplate" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="BjZ-aR-Hpe">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ </button>
+ <connections>
+ <action selector="toggleDetails:" target="-2" id="EcM-0b-BRV"/>
+ </connections>
+ </toolbarItem>
</allowedToolbarItems>
<defaultToolbarItems>
<toolbarItem reference="SX6-mq-Hck"/>
<toolbarItem reference="s7o-pK-heI"/>
- <toolbarItem reference="QuV-M8-cet"/>
+ <toolbarItem reference="rHN-a0-oZQ"/>
+ <toolbarItem reference="DCB-WP-NRs"/>
<toolbarItem reference="a3c-kV-98E"/>
+ <toolbarItem reference="QuV-M8-cet"/>
+ <toolbarItem reference="DfN-Iw-bzy"/>
</defaultToolbarItems>
</toolbar>
<connections>
<outlet property="delegate" destination="-2" id="2579"/>
</connections>
- <point key="canvasLocation" x="-773" y="15"/>
+ <point key="canvasLocation" x="-948" y="-24"/>
</window>
- <menu id="2649" userLabel="ContextMenu">
- <items>
- <menuItem title="Show Source in Finder" id="gbW-g1-lEc">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="revealSelectedQueueItemsSources:" target="-2" id="NY5-Sp-e08"/>
- </connections>
- </menuItem>
- <menuItem title="Show Destination in Finder" id="2655">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="revealSelectedQueueItems:" target="-2" id="qtj-uq-KvZ"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="Au5-j1-AAd"/>
- <menuItem title="Edit Job Settings" id="2650">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="editSelectedQueueItem:" target="-2" id="2654"/>
- </connections>
- </menuItem>
- <menuItem title="Reset Stopped 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 Jobs" id="2652">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="clearAll:" target="-2" id="Q3d-9G-k0i"/>
- </connections>
- </menuItem>
- <menuItem title="Clear Completed Jobs" id="XdJ-Sl-pwu">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="clearCompleted:" target="-2" id="LMt-sj-JIh"/>
- </connections>
- </menuItem>
- <menuItem title="Clear Selected Job" id="Wfz-Kj-Vtx">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="removeSelectedQueueItem:" target="-2" id="i8t-gS-Bi3"/>
- </connections>
- </menuItem>
- </items>
- <point key="canvasLocation" x="233" y="753.5"/>
- </menu>
<userDefaultsController representsSharedInstance="YES" id="z2J-h1-IDv"/>
</objects>
<resources>
- <image name="Delete" width="16" height="16"/>
- <image name="DeleteHighlightPressed" width="16" height="16"/>
<image name="NSActionTemplate" width="14" height="14"/>
+ <image name="NSGoLeftTemplate" width="9" height="12"/>
<image name="encode" width="32" height="32"/>
<image name="pauseencode" width="32" height="32"/>
</resources>
diff --git a/macosx/HBAppDelegate.h b/macosx/HBAppDelegate.h
index 387437b20..3762c62e9 100644
--- a/macosx/HBAppDelegate.h
+++ b/macosx/HBAppDelegate.h
@@ -6,11 +6,19 @@
#import <Cocoa/Cocoa.h>
+@class HBJob;
+
@interface HBAppDelegate : NSObject <NSApplicationDelegate>
- (IBAction)showPreferencesWindow:(id)sender;
- (IBAction)showOutputPanel:(id)sender;
-- (void)reloadPreset:(id)sender;
+
+- (IBAction)toggleStartCancel:(id)sender;
+- (IBAction)togglePauseResume:(id)sender;
+
+- (IBAction)reloadPreset:(id)sender;
+
+- (void)openJob:(HBJob *)job completionHandler:(void (^)(BOOL result))handler;
@end
diff --git a/macosx/HBAppDelegate.m b/macosx/HBAppDelegate.m
index de227e153..d8790fb7c 100644
--- a/macosx/HBAppDelegate.m
+++ b/macosx/HBAppDelegate.m
@@ -6,6 +6,8 @@
#import "HBAppDelegate.h"
+#import "HBQueue.h"
+
#import "HBUtilities.h"
#import "HBPresetsManager.h"
#import "HBPreset.h"
@@ -28,6 +30,8 @@
@property (unsafe_unretained) IBOutlet NSMenu *presetsMenu;
@property (nonatomic, strong) HBPreferencesController *preferencesController;
+
+@property (nonatomic, strong) HBQueue *queue;
@property (nonatomic, strong) HBQueueController *queueController;
@property (nonatomic, strong) HBOutputPanelController *outputPanel;
@@ -59,9 +63,10 @@
_presetsManager = [[HBPresetsManager alloc] initWithURL:[appSupportURL URLByAppendingPathComponent:PRESET_FILE]];
// Queue
- _queueController = [[HBQueueController alloc] initWithURL:[appSupportURL URLByAppendingPathComponent:QUEUE_FILE]];
+ _queue = [[HBQueue alloc] initWithURL:[appSupportURL URLByAppendingPathComponent:QUEUE_FILE]];
+ _queueController = [[HBQueueController alloc] initWithQueue:_queue];
_queueController.delegate = self;
- _mainController = [[HBController alloc] initWithQueue:_queueController presetsManager:_presetsManager];
+ _mainController = [[HBController alloc] initWithDelegate:self queue:_queue presetsManager:_presetsManager];
}
return self;
}
@@ -116,13 +121,13 @@
}
else
{
- [self.queueController setEncodingJobsAsPending];
- [self.queueController removeCompletedJobs];
+ [self.queue setEncodingJobsAsPending];
+ [self.queue removeCompletedAndCancelledItems];
}
// Now we re-check the queue array to see if there are
// any remaining encodes to be done
- if (self.queueController.count)
+ if (self.queue.items.count)
{
[self showMainWindow:self];
[self showQueueWindow:self];
@@ -158,7 +163,7 @@
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
{
- if (self.queueController.core.state != HBStateIdle)
+ if (self.queue.isEncoding)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"Are you sure you want to quit HandBrake?", @"Quit Alert -> message")];
@@ -192,6 +197,7 @@
_mainController = nil;
_queueController = nil;
+ _queue = nil;
[HBCore closeGlobal];
}
@@ -206,7 +212,7 @@
{
SEL action = menuItem.action;
- if (action == @selector(rip:) || action == @selector(pause:))
+ if (action == @selector(toggleStartCancel:) || action == @selector(togglePauseResume:))
{
// Delegate the validation to the queue controller
return [self.queueController validateMenuItem:menuItem];
@@ -281,21 +287,28 @@
}
}
+#pragma mark - Rescan job
+
+- (void)openJob:(HBJob *)job completionHandler:(void (^)(BOOL result))handler
+{
+ [self.mainController openJob:job completionHandler:handler];
+}
+
#pragma mark - Menu actions
-- (IBAction)rip:(id)sender
+- (IBAction)toggleStartCancel:(id)sender
{
- [self.queueController rip:self];
+ [self.queueController toggleStartCancel:sender];
}
-- (IBAction)pause:(id)sender
+- (IBAction)togglePauseResume:(id)sender
{
- [self.queueController togglePauseResume:self];
+ [self.queueController togglePauseResume:sender];
}
- (IBAction)browseSources:(id)sender
{
- [self.mainController browseSources:self];
+ [self.mainController browseSources:sender];
}
#pragma mark - Presets Menu actions
@@ -310,7 +323,7 @@
- (IBAction)reloadPreset:(id)sender
{
- [self.mainController reloadPreset:self];
+ [self.mainController reloadPreset:sender];
}
#pragma mark - Show Window Menu Items
@@ -325,7 +338,7 @@
_preferencesController = [[HBPreferencesController alloc] init];
}
- [self.preferencesController showWindow:self];
+ [self.preferencesController showWindow:sender];
}
/**
@@ -346,7 +359,7 @@
- (IBAction)showPreviewWindow:(id)sender
{
- [self.mainController showPreviewWindow:self];
+ [self.mainController showPreviewWindow:sender];
}
/**
diff --git a/macosx/HBController.h b/macosx/HBController.h
index 8fb6cfbc7..fa8a9ac42 100644
--- a/macosx/HBController.h
+++ b/macosx/HBController.h
@@ -6,14 +6,15 @@
#import <Cocoa/Cocoa.h>
-@class HBQueueController;
+@class HBAppDelegate;
+@class HBQueue;
@class HBPresetsManager;
@class HBJob;
@interface HBController : NSWindowController
-- (instancetype)initWithQueue:(HBQueueController *)queueController presetsManager:(HBPresetsManager *)manager;
+- (instancetype)initWithDelegate:(HBAppDelegate *)delegate queue:(HBQueue *)queue presetsManager:(HBPresetsManager *)manager;
- (void)launchAction;
@@ -28,10 +29,8 @@
- (IBAction)addToQueue:(id)sender;
- (IBAction)addAllTitlesToQueue:(id)sender;
-- (void)setQueueInfo:(NSAttributedString *)info progress:(double)progress hidden:(BOOL)hidden;
-
-- (IBAction)rip:(id)sender;
-- (IBAction)pause:(id)sender;
+- (IBAction)toggleStartCancel:(id)sender;
+- (IBAction)togglePauseResume:(id)sender;
- (IBAction)selectPresetFromMenu:(id)sender;
diff --git a/macosx/HBController.m b/macosx/HBController.m
index 7dbbf7182..40b42ff9b 100644
--- a/macosx/HBController.m
+++ b/macosx/HBController.m
@@ -33,6 +33,7 @@
#import "HBRenamePresetController.h"
#import "HBAutoNamer.h"
+#import "HBQueue.h"
@import HandBrakeKit;
@@ -122,8 +123,11 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
/// The HBCore used for scanning.
@property (nonatomic, strong) HBCore *core;
-/// The queue controller.
-@property (nonatomic, strong) HBQueueController *queue;
+/// The app delegate.
+@property (nonatomic, strong) HBAppDelegate *delegate;
+
+/// The queue.
+@property (nonatomic, strong) HBQueue *queue;
/// Whether the window is visible or occluded,
/// useful to avoid updating the UI needlessly
@@ -146,7 +150,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
@interface HBController (TouchBar) <NSTouchBarProvider, NSTouchBarDelegate>
- (void)_touchBar_updateButtonsStateForScanCore:(HBState)state;
-- (void)_touchBar_updateButtonsStateForQueueCore:(HBState)state;
+- (void)_touchBar_updateQueueButtonsState;
- (void)_touchBar_validateUserInterfaceItems;
@end
@@ -155,7 +159,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
@implementation HBController
-- (instancetype)initWithQueue:(HBQueueController *)queueController presetsManager:(HBPresetsManager *)manager;
+- (instancetype)initWithDelegate:(HBAppDelegate *)delegate queue:(HBQueue *)queue presetsManager:(HBPresetsManager *)manager;
{
self = [super initWithWindowNibName:@"MainWindow"];
if (self)
@@ -168,8 +172,8 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
fPreviewController = [[HBPreviewController alloc] init];
fPreviewController.documentController = self;
- _queue = queueController;
- _queue.controller = self;
+ _delegate = delegate;
+ _queue = queue;
presetManager = manager;
_currentPreset = manager.defaultPreset;
@@ -271,6 +275,8 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
fFiltersViewController = [[HBFiltersViewController alloc] init];
[fFiltersTab setView:[fFiltersViewController view]];
+ // Add the observeers
+
[self.core addObserver:self forKeyPath:@"state"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
context:HBControllerScanCoreContext];
@@ -283,6 +289,28 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
context:HBControllerQueueCoreContext];
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueDidStartNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ self.bottomConstrain.animator.constant = 0;
+ self->fRipIndicatorShown = YES;
+ self->fRipIndicator.hidden = NO;
+ }];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueDidCompleteNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ self.bottomConstrain.animator.constant = -WINDOW_HEIGHT_OFFSET;
+ self->fRipIndicator.hidden = YES;
+ self->fRipIndicatorShown = NO;
+ }];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueProgressNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ self.progressInfo = note.userInfo[HBQueueProgressNotificationInfoKey];
+ self.progress = [note.userInfo[HBQueueProgressNotificationPercentKey] doubleValue];
+
+ if (self->_visible)
+ {
+ [self updateProgress];
+ }
+ }];
+
self.presetsMenuBuilder = [[HBPresetsMenuBuilder alloc] initWithMenu:self.presetsPopup.menu
action:@selector(selectPresetFromMenu:)
size:[NSFont smallSystemFontSize]
@@ -349,12 +377,11 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
}
else if (context == HBControllerQueueCoreContext)
{
- HBState state = self.queue.core.state;
- [self updateToolbarButtonsStateForQueueCore:state];
+ [self updateToolbarButtonsState];
[self.window.toolbar validateVisibleItems];
if (@available(macOS 10.12.2, *))
{
- [self _touchBar_updateButtonsStateForQueueCore:state];
+ [self _touchBar_updateQueueButtonsState];
[self _touchBar_validateUserInterfaceItems];
}
NSUInteger count = self.queue.pendingItemsCount;
@@ -382,9 +409,9 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
}
}
-- (void)updateToolbarButtonsStateForQueueCore:(HBState)state
+- (void)updateToolbarButtonsState
{
- if (state == HBStatePaused)
+ if (self.queue.canResume)
{
_pauseToolbarItem.image = [NSImage imageNamed: @"encode"];
_pauseToolbarItem.label = NSLocalizedString(@"Resume", @"Toolbar Pause Item");
@@ -397,7 +424,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
_pauseToolbarItem.toolTip = NSLocalizedString(@"Pause Encoding", @"Toolbar Pause Item");
}
- if (state == HBStateScanning || state == HBStateWorking || state == HBStateSearching || state == HBStateMuxing || state == HBStatePaused)
+ if (self.queue.isEncoding)
{
_ripToolbarItem.image = [NSImage imageNamed:@"stopencode"];
_ripToolbarItem.label = NSLocalizedString(@"Stop", @"Toolbar Start/Stop Item");
@@ -443,7 +470,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
{
return YES;
}
- if (action == @selector(rip:) || action == @selector(addToQueue:))
+ if (action == @selector(toggleStartCancel:) || action == @selector(addToQueue:))
{
return NO;
}
@@ -453,30 +480,20 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
return YES;
}
- HBState queueState = _queue.core.state;
-
- if (action == @selector(rip:))
+ if (action == @selector(toggleStartCancel:))
{
- if (queueState == HBStateScanning || queueState == HBStateWorking || queueState == HBStateSearching ||
- queueState == HBStateMuxing || queueState == HBStatePaused)
+ if (self.queue.isEncoding)
{
return YES;
}
else
{
- return (self.job != nil || _queue.pendingItemsCount > 0);
+ return (self.job != nil || self.queue.canEncode);
}
}
- if (action == @selector(pause:)) {
- if (queueState == HBStatePaused)
- {
- return YES;
- }
- else
- {
- return (queueState == HBStateWorking || queueState == HBStateMuxing);
- }
+ if (action == @selector(togglePauseResume:)) {
+ return self.queue.canPause || self.queue.canResume;
}
if (action == @selector(addToQueue:))
@@ -505,11 +522,11 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
{
return self.window.attachedSheet == nil;
}
- if (action == @selector(pause:))
+ if (action == @selector(togglePauseResume:))
{
return [_queue validateMenuItem:menuItem];
}
- if (action == @selector(rip:))
+ if (action == @selector(toggleStartCancel:))
{
BOOL result = [_queue validateMenuItem:menuItem];
@@ -1005,38 +1022,6 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
fRipIndicator.doubleValue = self.progress;
}
-- (void)setQueueInfo:(NSAttributedString *)info progress:(double)progress hidden:(BOOL)hidden
-{
- self.progressInfo = info;
- self.progress = progress;
-
- if (_visible)
- {
- [self updateProgress];
- }
-
- if (hidden)
- {
- if (fRipIndicatorShown)
- {
- self.bottomConstrain.animator.constant = -WINDOW_HEIGHT_OFFSET;
- fRipIndicator.hidden = YES;
- fRipIndicatorShown = NO;
- }
- }
- else
- {
- // If progress bar hasn't been revealed at the bottom of the window, do
- // that now.
- if (!fRipIndicatorShown)
- {
- self.bottomConstrain.animator.constant = 0;
- fRipIndicatorShown = YES;
- fRipIndicator.hidden = NO;
- }
- }
-}
-
#pragma mark - Job Handling
/**
@@ -1073,7 +1058,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
[alert beginSheetModalForWindow:self.window completionHandler:handler];
}
- else if ([_queue jobExistAtURL:job.completeOutputURL])
+ else if ([_queue itemExistAtURL:job.completeOutputURL])
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"There is already a queue item for this destination.", @"File already exists in queue alert -> message")];
@@ -1123,24 +1108,19 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
[self doAddToQueue];
}
- [_queue rip:self];
+ [_delegate toggleStartCancel:self];
}
/**
* Puts up an alert before ultimately calling doRip
*/
-- (IBAction)rip:(id)sender
+- (IBAction)toggleStartCancel:(id)sender
{
// Rip or Cancel ?
- if (_queue.core.state == HBStateWorking || _queue.core.state == HBStatePaused || _queue.core.state == HBStateSearching)
+ if (_queue.isEncoding || _queue.canEncode)
{
// Displays an alert asking user if the want to cancel encoding of current job.
- [_queue cancelRip:self];
- }
- // If there are pending jobs in the queue, then this is a rip the queue
- else if (_queue.pendingItemsCount > 0)
- {
- [_queue rip:self];
+ [_delegate toggleStartCancel:self];
}
else
{
@@ -1156,9 +1136,9 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
}
}
-- (IBAction)pause:(id)sender
+- (IBAction)togglePauseResume:(id)sender
{
- [_queue togglePauseResume:sender];
+ [_delegate togglePauseResume:sender];
}
#pragma mark -
@@ -1213,7 +1193,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
[destinations addObject:job.completeOutputURL];
}
- if ([[NSFileManager defaultManager] fileExistsAtPath:job.completeOutputURL.path] || [_queue jobExistAtURL:job.completeOutputURL])
+ if ([[NSFileManager defaultManager] fileExistsAtPath:job.completeOutputURL.path] || [_queue itemExistAtURL:job.completeOutputURL])
{
fileExists = YES;
break;
@@ -1248,13 +1228,13 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSAlertSecondButtonReturn)
{
- [self->_queue addJobsFromArray:jobs];
+ [self->_queue addJobs:jobs];
}
}];
}
else
{
- [_queue addJobsFromArray:jobs];
+ [_queue addJobs:jobs];
}
}
@@ -1537,7 +1517,7 @@ static NSTouchBarItemIdentifier HBTouchBarActivity = @"fr.handbrake.activity";
NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
item.customizationLabel = NSLocalizedString(@"Start/Stop Encoding", @"Touch bar");
- NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarPlayTemplate] target:self action:@selector(rip:)];
+ NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarPlayTemplate] target:self action:@selector(toggleStartCancel:)];
item.view = button;
return item;
@@ -1547,7 +1527,7 @@ static NSTouchBarItemIdentifier HBTouchBarActivity = @"fr.handbrake.activity";
NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
item.customizationLabel = NSLocalizedString(@"Pause Encoding", @"Touch bar");
- NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarPauseTemplate] target:self action:@selector(pause:)];
+ NSButton *button = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameTouchBarPauseTemplate] target:self action:@selector(togglePauseResume:)];
item.view = button;
return item;
@@ -1592,24 +1572,26 @@ static NSTouchBarItemIdentifier HBTouchBarActivity = @"fr.handbrake.activity";
}
}
-- (void)_touchBar_updateButtonsStateForQueueCore:(HBState)state;
+- (void)_touchBar_updateQueueButtonsState
{
NSButton *ripButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarRip] view];
NSButton *pauseButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarPause] view];
- if (state == HBStateScanning || state == HBStateWorking || state == HBStateSearching || state == HBStateMuxing)
+ if (self.queue.isEncoding)
{
ripButton.image = [NSImage imageNamed:NSImageNameTouchBarRecordStopTemplate];
- pauseButton.image = [NSImage imageNamed:NSImageNameTouchBarPauseTemplate];
}
- else if (state == HBStatePaused)
+ else
+ {
+ ripButton.image = [NSImage imageNamed:NSImageNameTouchBarPlayTemplate];
+ }
+
+ if (self.queue.canResume)
{
- ripButton.image = [NSImage imageNamed:NSImageNameTouchBarRecordStopTemplate];
pauseButton.image = [NSImage imageNamed:NSImageNameTouchBarPlayTemplate];
}
- else if (state == HBStateIdle)
+ else
{
- ripButton.image = [NSImage imageNamed:NSImageNameTouchBarPlayTemplate];
pauseButton.image = [NSImage imageNamed:NSImageNameTouchBarPauseTemplate];
}
}
diff --git a/macosx/HBJob+UIAdditions.h b/macosx/HBJob+UIAdditions.h
index 99dcc10c8..7099f08b0 100644
--- a/macosx/HBJob+UIAdditions.h
+++ b/macosx/HBJob+UIAdditions.h
@@ -16,7 +16,9 @@
@property (nonatomic, readonly) NSArray<NSString *> *containers;
@property (nonatomic, readonly) NSAttributedString *attributedTitleDescription;
+
@property (nonatomic, readonly) NSAttributedString *attributedDescription;
+@property (nonatomic, readonly) NSAttributedString *attributedExpandedDescription;
@property (nonatomic, readonly) NSString *shortDescription;
@property (nonatomic, readonly) NSString *filtersShortDescription;
diff --git a/macosx/HBJob+UIAdditions.m b/macosx/HBJob+UIAdditions.m
index 4f711bacb..28c7edfe5 100644
--- a/macosx/HBJob+UIAdditions.m
+++ b/macosx/HBJob+UIAdditions.m
@@ -119,13 +119,8 @@ static NSDictionary *shortHeightAttr;
}
}
-- (NSAttributedString *)titleAttributedDescription
+- (NSString *)rangeDescription
{
- NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
-
- // Job name
- [attrString appendString:self.description withAttributes:titleAttr];
-
// Range type
NSString *startStopString = @"";
if (self.range.type == HBRangeTypeChapters)
@@ -163,16 +158,37 @@ static NSDictionary *shortHeightAttr;
if (passesString.length)
{
- [attrString appendString:[NSString stringWithFormat:HBKitLocalizedString(@" (Title %d, %@, %@) â–¸ %@\n", @"Title description"),
- self.titleIdx, startStopString, passesString, self.outputFileName]
- withAttributes:detailAttr];
+ return [NSString stringWithFormat:HBKitLocalizedString(@"Title %d, %@, %@", @"Title description"),
+ self.titleIdx, startStopString, passesString];
}
else
{
- [attrString appendString:[NSString stringWithFormat:HBKitLocalizedString(@" (Title %d, %@) â–¸ %@\n", @"Title description"),
- self.titleIdx, startStopString, self.outputFileName]
- withAttributes:detailAttr];
+ return [NSString stringWithFormat:HBKitLocalizedString(@"Title %d, %@", @"Title description"),
+ self.titleIdx, startStopString];
}
+}
+
+- (NSAttributedString *)rangeAttributedDescription
+{
+ NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
+
+ [attrString appendString:@"\t" withAttributes:detailAttr];
+ [attrString appendString:HBKitLocalizedString(@"Range:", @"Range description") withAttributes:detailBoldAttr];
+ [attrString appendString:@" \t" withAttributes:detailAttr];
+ [attrString appendString:self.rangeDescription withAttributes:detailAttr];
+ [attrString appendString:@"\n" withAttributes:detailAttr];
+
+ return attrString;
+}
+- (NSAttributedString *)titleAttributedDescription
+{
+ NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
+
+ // Job name
+ [attrString appendString:self.description withAttributes:titleAttr];
+
+ [attrString appendString:[NSString stringWithFormat:@" (%@) â–¸ %@\n", [self rangeDescription], self.outputFileName]
+ withAttributes:detailAttr];
return attrString;
}
@@ -231,6 +247,19 @@ static NSDictionary *shortHeightAttr;
return attrString;
}
+- (NSAttributedString *)sourceAttributedDescription
+{
+ NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
+
+ [attrString appendString:@"\t" withAttributes:detailAttr];
+ [attrString appendString:HBKitLocalizedString(@"Source:", @"Source description") withAttributes:detailBoldAttr];
+ [attrString appendString:@" \t" withAttributes:detailAttr];
+ [attrString appendString:self.fileURL.path withAttributes:detailAttr];
+ [attrString appendString:@"\n" withAttributes:detailAttr];
+
+ return attrString;
+}
+
- (NSAttributedString *)destinationAttributedDescription
{
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
@@ -629,21 +658,31 @@ static NSDictionary *shortHeightAttr;
@autoreleasepool
{
- [attrString appendAttributedString:[self titleAttributedDescription]];
[attrString appendAttributedString:[self presetAttributedDescription]];
+ [attrString appendString:@"\n" withAttributes: detailAttr];
+ [attrString appendAttributedString:[self sourceAttributedDescription]];
+ [attrString appendString:@"\n" withAttributes: detailAttr];
+ [attrString appendAttributedString:[self destinationAttributedDescription]];
+ [attrString appendString:@"\n" withAttributes: detailAttr];
[attrString appendAttributedString:[self formatAttributedDescription]];
+ [attrString appendString:@"\n" withAttributes: detailAttr];
+ [attrString appendAttributedString:[self rangeAttributedDescription]];
+ [attrString appendString:@"\n" withAttributes: detailAttr];
[attrString appendAttributedString:[self dimensionsAttributedDescription]];
+ [attrString appendString:@"\n" withAttributes: detailAttr];
[attrString appendAttributedString:[self filtersAttributedDescription]];
+ [attrString appendString:@"\n" withAttributes: detailAttr];
[attrString appendAttributedString:[self videoAttributedDescription]];
+ [attrString appendString:@"\n" withAttributes: detailAttr];
if (self.audio.countOfTracks > 1)
{
[attrString appendAttributedString:[self audioAttributedDescription]];
+ [attrString appendString:@"\n" withAttributes: detailAttr];
}
if (self.subtitles.countOfTracks > 1)
{
[attrString appendAttributedString:[self subtitlesAttributedDescription]];
}
- [attrString appendAttributedString:[self destinationAttributedDescription]];
}
[attrString deleteCharactersInRange:NSMakeRange(attrString.length - 1, 1)];
@@ -651,6 +690,7 @@ static NSDictionary *shortHeightAttr;
return attrString;
}
+
#pragma mark - Short descriptions
- (NSString *)videoShortDescription
diff --git a/macosx/HBQueue.h b/macosx/HBQueue.h
new file mode 100644
index 000000000..0e176292b
--- /dev/null
+++ b/macosx/HBQueue.h
@@ -0,0 +1,93 @@
+/* HBQueue.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 <Foundation/Foundation.h>
+
+#import "HBCore.h"
+#import "HBDistributedArray.h"
+#import "HBQueueItem.h"
+#import "HBJobOutputFileWriter.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+extern NSString * const HBQueueDidAddItemNotification;
+extern NSString * const HBQueueDidRemoveItemNotification;
+extern NSString * const HBQueueDidChangeItemNotification;
+extern NSString * const HBQueueItemNotificationIndexesKey; // NSIndexSet
+
+extern NSString * const HBQueueDidMoveItemNotification;
+extern NSString * const HBQueueItemNotificationSourceIndexesKey; // NSArray<NSNumber *>
+extern NSString * const HBQueueItemNotificationTargetIndexesKey; // NSArray<NSNumber *>
+
+extern NSString * const HBQueueReloadItemsNotification;
+
+extern NSString * const HBQueueLowSpaceAlertNotification;
+
+extern NSString * const HBQueueProgressNotification;
+extern NSString * const HBQueueProgressNotificationPercentKey; // NSNumber - double
+extern NSString * const HBQueueProgressNotificationInfoKey; // NSString
+
+extern NSString * const HBQueueDidStartNotification;
+extern NSString * const HBQueueDidCompleteNotification;
+
+extern NSString * const HBQueueDidCompleteItemNotification;
+extern NSString * const HBQueueDidCompleteItemNotificationItemKey; // HBQueueItem
+
+@interface HBQueue : NSObject
+
+- (instancetype)initWithURL:(NSURL *)queueURL;
+
+@property (nonatomic, readonly) NSURL *queueURL;
+
+@property (nonatomic, readonly) HBCore *core;
+@property (nonatomic, readonly) HBDistributedArray<HBQueueItem *> *items;
+
+@property (nonatomic, nullable) HBQueueItem *currentItem;
+@property (nonatomic, nullable) HBJobOutputFileWriter *currentLog;
+
+@property (nonatomic) NSUInteger pendingItemsCount;
+@property (nonatomic) NSUInteger completedItemsCount;
+
+@property (nonatomic) NSUndoManager *undoManager;
+
+- (void)addJob:(HBJob *)job;
+- (void)addJobs:(NSArray<HBJob *> *)jobs;
+
+- (void)addQueueItems:(NSArray<HBQueueItem *> *)items atIndexes:(NSIndexSet *)indexes;
+- (void)removeQueueItemAtIndex:(NSUInteger)index;
+- (void)removeQueueItemsAtIndexes:(NSIndexSet *)indexes;
+- (void)moveQueueItems:(NSArray<HBQueueItem *> *)items toIndex:(NSUInteger)index;
+
+- (BOOL)itemExistAtURL:(NSURL *)url;
+
+- (void)removeAllItems;
+- (void)removeCompletedAndCancelledItems;
+- (void)removeNotWorkingItems;
+- (void)removeCompletedItems;
+
+- (void)resetItemsStateAtIndexes:(NSIndexSet *)indexes;
+- (void)resetAllItems;
+- (void)resetFailedItems;
+
+- (void)setEncodingJobsAsPending;
+
+@property (nonatomic, readonly) BOOL canEncode;
+@property (nonatomic, readonly) BOOL isEncoding;
+
+- (void)start;
+- (void)cancelCurrentItemAndContinue;
+- (void)finishCurrentAndStop;
+- (void)cancelCurrentItemAndStop;
+
+@property (nonatomic, readonly) BOOL canPause;
+- (void)pause;
+
+@property (nonatomic, readonly) BOOL canResume;
+- (void)resume;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/macosx/HBQueue.m b/macosx/HBQueue.m
new file mode 100644
index 000000000..6aee92452
--- /dev/null
+++ b/macosx/HBQueue.m
@@ -0,0 +1,765 @@
+/* HBQueue.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 "HBQueue.h"
+#import "NSArray+HBAdditions.h"
+
+NSString * const HBQueueDidAddItemNotification = @"HBQueueDidAddItemNotification";
+NSString * const HBQueueDidRemoveItemNotification = @"HBQueueDidRemoveItemNotification";
+NSString * const HBQueueDidChangeItemNotification = @"HBQueueDidChangeItemNotification";
+
+NSString * const HBQueueItemNotificationIndexesKey = @"HBQueueReloadItemsNotification";
+
+NSString * const HBQueueDidMoveItemNotification = @"HBQueueDidMoveItemNotification";
+NSString * const HBQueueItemNotificationSourceIndexesKey = @"HBQueueItemNotificationSourceIndexesKey";
+NSString * const HBQueueItemNotificationTargetIndexesKey = @"HBQueueItemNotificationTargetIndexesKey";
+
+NSString * const HBQueueReloadItemsNotification = @"HBQueueReloadItemsNotification";
+
+NSString * const HBQueueLowSpaceAlertNotification = @"HBQueueLowSpaceAlertNotification";
+
+NSString * const HBQueueProgressNotification = @"HBQueueProgressNotification";
+NSString * const HBQueueProgressNotificationPercentKey = @"HBQueueProgressNotificationPercentKey";
+NSString * const HBQueueProgressNotificationInfoKey = @"HBQueueProgressNotificationInfoKey";
+
+NSString * const HBQueueDidStartNotification = @"HBQueueDidStartNotification";
+NSString * const HBQueueDidCompleteNotification = @"HBQueueDidCompleteNotification";
+
+NSString * const HBQueueDidCompleteItemNotification = @"HBQueueDidCompleteItemNotification";
+NSString * const HBQueueDidCompleteItemNotificationItemKey = @"HBQueueDidCompleteItemNotificationItemKey";
+
+@interface HBQueue ()
+
+@property (nonatomic) BOOL stop;
+
+@end
+
+@implementation HBQueue
+
+- (instancetype)initWithURL:(NSURL *)queueURL
+{
+ self = [super init];
+ if (self)
+ {
+ NSInteger loggingLevel = [NSUserDefaults.standardUserDefaults integerForKey:@"LoggingLevel"];
+
+ // Init a separate instance of libhb for the queue
+ _core = [[HBCore alloc] initWithLogLevel:loggingLevel name:@"QueueCore"];
+ _core.automaticallyPreventSleep = NO;
+
+ _items = [[HBDistributedArray alloc] initWithURL:queueURL class:[HBQueueItem class]];
+
+ // Set up the observers
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadQueue) name:HBDistributedArrayChanged object:_items];
+
+ [self updateStats];
+ }
+ return self;
+}
+
+#pragma mark - Public methods
+
+- (void)addJob:(HBJob *)item
+{
+ NSParameterAssert(item);
+ [self addJobs:@[item]];
+}
+
+- (void)addJobs:(NSArray<HBJob *> *)jobs;
+{
+ NSParameterAssert(jobs);
+
+ NSMutableArray<HBQueueItem *> *itemsToAdd = [NSMutableArray array];
+ for (HBJob *job in jobs)
+ {
+ HBQueueItem *item = [[HBQueueItem alloc] initWithJob:job];
+ [itemsToAdd addObject:item];
+ }
+ if (itemsToAdd.count)
+ {
+ NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(self.items.count, itemsToAdd.count)];
+ [self addQueueItems:itemsToAdd atIndexes:indexes];
+ }
+}
+
+- (BOOL)itemExistAtURL:(NSURL *)url
+{
+ NSParameterAssert(url);
+
+ for (HBQueueItem *item in self.items)
+ {
+ if ((item.state == HBQueueItemStateReady || item.state == HBQueueItemStateWorking)
+ && [item.completeOutputURL isEqualTo:url])
+ {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (NSUInteger)count
+{
+ return self.items.count;
+}
+
+- (void)addQueueItems:(NSArray<HBQueueItem *> *)items atIndexes:(NSIndexSet *)indexes
+{
+ NSParameterAssert(items);
+ NSParameterAssert(indexes);
+ [self.items beginTransaction];
+
+ // Forward
+ NSUInteger currentIndex = indexes.firstIndex;
+ NSUInteger currentObjectIndex = 0;
+ while (currentIndex != NSNotFound)
+ {
+ [self.items insertObject:items[currentObjectIndex] atIndex:currentIndex];
+ currentIndex = [indexes indexGreaterThanIndex:currentIndex];
+ currentObjectIndex++;
+ }
+
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidAddItemNotification object:self userInfo:@{HBQueueItemNotificationIndexesKey: indexes}];
+
+ NSUndoManager *undo = self.undoManager;
+ [[undo prepareWithInvocationTarget:self] removeQueueItemsAtIndexes:indexes];
+
+ if (!undo.isUndoing)
+ {
+ if (items.count == 1)
+ {
+ [undo setActionName:NSLocalizedString(@"Add Job To Queue", @"Queue undo action name")];
+ }
+ else
+ {
+ [undo setActionName:NSLocalizedString(@"Add Jobs To Queue", @"Queue undo action name")];
+ }
+ }
+
+ [self updateStats];
+ [self.items commit];
+}
+
+- (void)removeQueueItemAtIndex:(NSUInteger)index
+{
+ [self removeQueueItemsAtIndexes:[NSIndexSet indexSetWithIndex:index]];
+}
+
+- (void)removeQueueItemsAtIndexes:(NSIndexSet *)indexes
+{
+ NSParameterAssert(indexes);
+
+ if (indexes.count == 0)
+ {
+ return;
+ }
+
+ [self.items beginTransaction];
+
+ NSArray<HBQueueItem *> *removeItems = [self.items objectsAtIndexes:indexes];
+
+ if (self.items.count > indexes.lastIndex)
+ {
+ [self.items removeObjectsAtIndexes:indexes];
+ }
+
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidRemoveItemNotification object:self userInfo:@{HBQueueItemNotificationIndexesKey: indexes}];
+
+ NSUndoManager *undo = self.undoManager;
+ [[undo prepareWithInvocationTarget:self] addQueueItems:removeItems atIndexes:indexes];
+
+ if (!undo.isUndoing)
+ {
+ if (indexes.count == 1)
+ {
+ [undo setActionName:NSLocalizedString(@"Remove Job From Queue", @"Queue undo action name")];
+ }
+ else
+ {
+ [undo setActionName:NSLocalizedString(@"Remove Jobs From Queue", @"Queue undo action name")];
+ }
+ }
+
+ [self updateStats];
+ [self.items commit];
+}
+
+- (void)moveQueueItems:(NSArray<HBQueueItem *> *)items toIndex:(NSUInteger)index
+{
+ [self.items beginTransaction];
+
+ NSMutableArray<NSNumber *> *source = [NSMutableArray array];
+ NSMutableArray<NSNumber *> *dest = [NSMutableArray array];
+
+ for (id object in items.reverseObjectEnumerator)
+ {
+ NSUInteger sourceIndex = [self.items indexOfObject:object];
+ [self.items removeObjectAtIndex:sourceIndex];
+
+ if (sourceIndex < index)
+ {
+ index--;
+ }
+
+ [self.items insertObject:object atIndex:index];
+
+ [source addObject:@(index)];
+ [dest addObject:@(sourceIndex)];
+ }
+
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidMoveItemNotification
+ object:self
+ userInfo:@{HBQueueItemNotificationSourceIndexesKey: dest,
+ HBQueueItemNotificationTargetIndexesKey: source}];
+
+ NSUndoManager *undo = self.undoManager;
+ [[undo prepareWithInvocationTarget:self] moveQueueItemsAtIndexes:source toIndexes:dest];
+
+ if (!undo.isUndoing)
+ {
+ if (items.count == 1)
+ {
+ [undo setActionName:NSLocalizedString(@"Move Job in Queue", @"Queue undo action name")];
+ }
+ else
+ {
+ [undo setActionName:NSLocalizedString(@"Move Jobs in Queue", @"Queue undo action name")];
+ }
+ }
+
+ [self.items commit];
+}
+
+- (void)moveQueueItemsAtIndexes:(NSArray *)source toIndexes:(NSArray *)dest
+{
+ [self.items beginTransaction];
+
+ NSMutableArray<NSNumber *> *newSource = [NSMutableArray array];
+ NSMutableArray<NSNumber *> *newDest = [NSMutableArray array];
+
+ for (NSInteger idx = source.count - 1; idx >= 0; idx--)
+ {
+ NSUInteger sourceIndex = [source[idx] integerValue];
+ NSUInteger destIndex = [dest[idx] integerValue];
+
+ [newSource addObject:@(destIndex)];
+ [newDest addObject:@(sourceIndex)];
+
+ id obj = [self.items objectAtIndex:sourceIndex];
+ [self.items removeObjectAtIndex:sourceIndex];
+ [self.items insertObject:obj atIndex:destIndex];
+ }
+
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidMoveItemNotification
+ object:self
+ userInfo:@{HBQueueItemNotificationSourceIndexesKey: newDest,
+ HBQueueItemNotificationTargetIndexesKey: newSource}];
+
+ NSUndoManager *undo = self.undoManager;
+ [[undo prepareWithInvocationTarget:self] moveQueueItemsAtIndexes:newSource toIndexes:newDest];
+
+ if (!undo.isUndoing)
+ {
+ if (source.count == 1)
+ {
+ [undo setActionName:NSLocalizedString(@"Move Job in Queue", @"Queue undo action name")];
+ }
+ else
+ {
+ [undo setActionName:NSLocalizedString(@"Move Jobs in Queue", @"Queue undo action name")];
+ }
+ }
+
+ [self.items commit];
+}
+
+/**
+ * This method will clear the queue of any encodes that are not still pending
+ * this includes both successfully completed encodes as well as canceled encodes
+ */
+- (void)removeCompletedAndCancelledItems
+{
+ [self.items beginTransaction];
+ NSIndexSet *indexes = [self.items indexesOfObjectsUsingBlock:^BOOL(HBQueueItem *item) {
+ return (item.state == HBQueueItemStateCompleted || item.state == HBQueueItemStateCanceled);
+ }];
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidRemoveItemNotification object:self userInfo:@{@"indexes": indexes}];
+ [self.items commit];
+}
+
+/**
+ * This method will clear the queue of all encodes. effectively creating an empty queue
+ */
+- (void)removeAllItems
+{
+ [self.items beginTransaction];
+
+ [self removeQueueItemsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.items.count)]];
+ [self.items commit];
+}
+
+- (void)removeNotWorkingItems
+{
+ [self.items beginTransaction];
+ NSIndexSet *indexes = [self.items indexesOfObjectsUsingBlock:^BOOL(HBQueueItem *item) {
+ return (item.state != HBQueueItemStateWorking);
+ }];
+ [self removeQueueItemsAtIndexes:indexes];
+ [self.items commit];
+}
+
+- (void)removeCompletedItems
+{
+ [self.items beginTransaction];
+ NSIndexSet *indexes = [self.items indexesOfObjectsUsingBlock:^BOOL(HBQueueItem *item) {
+ return (item.state == HBQueueItemStateCompleted);
+ }];
+ [self removeQueueItemsAtIndexes:indexes];
+ [self.items commit];
+}
+
+- (void)resetItemsStateAtIndexes:(NSIndexSet *)indexes
+{
+ if ([self.items beginTransaction] == HBDistributedArrayContentReload)
+ {
+ // Do not execture the action if the array changed.
+ [self.items commit];
+ return;
+ }
+
+ NSMutableIndexSet *updatedIndexes = [NSMutableIndexSet indexSet];
+
+ NSUInteger currentIndex = indexes.firstIndex;
+ while (currentIndex != NSNotFound) {
+ HBQueueItem *item = self.items[currentIndex];
+
+ if (item.state == HBQueueItemStateCanceled || item.state == HBQueueItemStateCompleted || item.state == HBQueueItemStateFailed)
+ {
+ item.state = HBQueueItemStateReady;
+ [updatedIndexes addIndex:currentIndex];
+ }
+ currentIndex = [indexes indexGreaterThanIndex:currentIndex];
+ }
+
+ [self updateStats];
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidChangeItemNotification object:self userInfo:@{HBQueueItemNotificationIndexesKey: indexes}];
+ [self.items commit];
+}
+
+- (void)resetAllItems
+{
+ [self.items beginTransaction];
+ NSIndexSet *indexes = [self.items indexesOfObjectsUsingBlock:^BOOL(HBQueueItem *item) {
+ return (item.state != HBQueueItemStateWorking);
+ }];
+ [self resetItemsStateAtIndexes:indexes];
+ [self.items commit];
+}
+
+- (void)resetFailedItems
+{
+ [self.items beginTransaction];
+ NSIndexSet *indexes = [self.items indexesOfObjectsUsingBlock:^BOOL(HBQueueItem *item) {
+ return (item.state == HBQueueItemStateFailed);
+ }];
+ [self resetItemsStateAtIndexes:indexes];
+ [self.items commit];
+}
+
+/**
+ * This method will set any item marked as encoding back to pending
+ * currently used right after a queue reload
+ */
+- (void)setEncodingJobsAsPending
+{
+ [self.items beginTransaction];
+
+ NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
+ NSUInteger idx = 0;
+ for (HBQueueItem *item in self.items)
+ {
+ // We want to keep any queue item that is pending or was previously being encoded
+ if (item.state == HBQueueItemStateWorking)
+ {
+ item.state = HBQueueItemStateReady;
+ [indexes addIndex:idx];
+ }
+ idx++;
+ }
+
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidChangeItemNotification object:self userInfo:@{HBQueueItemNotificationIndexesKey: indexes}];
+ [self.items commit];
+}
+
+- (BOOL)canEncode
+{
+ return self.pendingItemsCount > 0;
+}
+
+- (BOOL)isEncoding
+{
+ HBState s = self.core.state;
+ return (s == HBStateScanning) || (s == HBStatePaused) || (s == HBStateWorking) || (s == HBStateMuxing) || (s == HBStateSearching);
+}
+
+- (BOOL)canPause
+{
+ HBState s = self.core.state;
+ return (s == HBStateWorking || s == HBStateMuxing);
+}
+
+- (void)pause
+{
+ [self.core pause];
+ [self.core allowSleep];
+}
+
+- (BOOL)canResume
+{
+ return self.core.state == HBStatePaused;
+}
+
+- (void)resume
+{
+ [self.core resume];
+ [self.core preventSleep];
+}
+
+#pragma mark - Private queue editing methods
+
+/**
+ * Reloads the queue, this is called
+ * when another HandBrake instances modifies the queue
+ */
+- (void)reloadQueue
+{
+ [self updateStats];
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueReloadItemsNotification object:self];
+}
+
+- (void)updateStats
+{
+ // lets get the stats on the status of the queue array
+ NSUInteger pendingCount = 0;
+ NSUInteger completedCount = 0;
+
+ for (HBQueueItem *item in self.items)
+ {
+ if (item.state == HBQueueItemStateReady)
+ {
+ pendingCount++;
+ }
+ if (item.state == HBQueueItemStateCompleted)
+ {
+ completedCount++;
+ }
+ }
+
+ self.pendingItemsCount = pendingCount;
+ self.completedItemsCount = completedCount;
+}
+
+- (BOOL)_isDiskSpaceLowAtURL:(NSURL *)url
+{
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"HBQueuePauseIfLowSpace"])
+ {
+ NSURL *volumeURL = nil;
+ NSDictionary<NSURLResourceKey, id> *attrs = [url resourceValuesForKeys:@[NSURLIsVolumeKey, NSURLVolumeURLKey] error:NULL];
+ long long minCapacity = [[[NSUserDefaults standardUserDefaults] stringForKey:@"HBQueueMinFreeSpace"] longLongValue] * 1000000000;
+
+ volumeURL = [attrs[NSURLIsVolumeKey] boolValue] ? url : attrs[NSURLVolumeURLKey];
+
+ if (volumeURL)
+ {
+ [volumeURL removeCachedResourceValueForKey:NSURLVolumeAvailableCapacityKey];
+ attrs = [volumeURL resourceValuesForKeys:@[NSURLVolumeAvailableCapacityKey] error:NULL];
+
+ if (attrs[NSURLVolumeAvailableCapacityKey])
+ {
+ if ([attrs[NSURLVolumeAvailableCapacityKey] longLongValue] < minCapacity)
+ {
+ return YES;
+ }
+ }
+ }
+ }
+
+ return NO;
+}
+
+/**
+ * Used to get the next pending queue item and return it if found
+ */
+- (HBQueueItem *)getNextPendingQueueItem
+{
+ for (HBQueueItem *item in self.items)
+ {
+ if (item.state == HBQueueItemStateReady)
+ {
+ return item;
+ }
+ }
+ return nil;
+}
+
+- (void)start
+{
+ if (self.canEncode && self.core.state == HBStateIdle)
+ {
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidStartNotification object:self];
+ [self.core preventSleep];
+ [self encodeNextQueueItem];
+ }
+}
+
+/**
+ * Starts the queue
+ */
+- (void)encodeNextQueueItem
+{
+ [self.items beginTransaction];
+ self.currentItem = nil;
+
+ // since we have completed an encode, we go to the next
+ if (self.stop)
+ {
+ [HBUtilities writeToActivityLog:"Queue manually stopped"];
+
+ self.stop = NO;
+ [self.core allowSleep];
+
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidCompleteNotification object:self];
+ }
+ else
+ {
+ // Check to see if there are any more pending items in the queue
+ HBQueueItem *nextItem = [self getNextPendingQueueItem];
+
+ if (nextItem && [self _isDiskSpaceLowAtURL:nextItem.outputURL])
+ {
+ // Disk space is low, show an alert
+ [HBUtilities writeToActivityLog:"Queue Stopped, low space on destination disk"];
+ [self.core allowSleep];
+
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidCompleteNotification object:self];
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueLowSpaceAlertNotification object:self];
+ }
+ // If we still have more pending items in our queue, lets go to the next one
+ else if (nextItem)
+ {
+ // now we mark the queue item as working so another instance can not come along and try to scan it while we are scanning
+ nextItem.state = HBQueueItemStateWorking;
+
+ // Tell HB to output a new activity log file for this encode
+ self.currentLog = [[HBJobOutputFileWriter alloc] initWithJob:nextItem.job];
+ if (self.currentLog)
+ {
+ [[HBOutputRedirect stderrRedirect] addListener:self.currentLog];
+ [[HBOutputRedirect stdoutRedirect] addListener:self.currentLog];
+ }
+
+ self.currentItem = nextItem;
+ NSIndexSet *indexes = [NSIndexSet indexSetWithIndex:[self.items indexOfObject:nextItem]];
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidChangeItemNotification object:self userInfo:@{HBQueueItemNotificationIndexesKey: indexes}];
+
+ [self updateStats];
+
+ // now we can go ahead and scan the new pending queue item
+ [self encodeItem:nextItem];
+
+ // erase undo manager history
+ [self.undoManager removeAllActions];
+ }
+ else
+ {
+ [HBUtilities writeToActivityLog:"Queue Done, there are no more pending encodes"];
+ [self.core allowSleep];
+
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidCompleteNotification object:self];
+ }
+ }
+ [self.items commit];
+}
+
+- (void)completedItem:(HBQueueItem *)item result:(HBCoreResult)result;
+{
+ NSParameterAssert(item);
+ [self.items beginTransaction];
+
+ // Since we are done with this encode, tell output to stop writing to the
+ // individual encode log.
+ [[HBOutputRedirect stderrRedirect] removeListener:self.currentLog];
+ [[HBOutputRedirect stdoutRedirect] removeListener:self.currentLog];
+
+ self.currentLog = nil;
+
+ // Mark the encode just finished
+ switch (result) {
+ case HBCoreResultDone:
+ item.state = HBQueueItemStateCompleted;
+ break;
+ case HBCoreResultCanceled:
+ item.state = HBQueueItemStateCanceled;
+ break;
+ default:
+ item.state = HBQueueItemStateFailed;
+ break;
+ }
+
+ // Update UI
+ NSString *info = nil;
+ switch (result) {
+ case HBCoreResultDone:
+ info = NSLocalizedString(@"Encode Finished.", @"Queue status");
+ break;
+ case HBCoreResultCanceled:
+ info = NSLocalizedString(@"Encode Canceled.", @"Queue status");
+ break;
+ default:
+ info = NSLocalizedString(@"Encode Failed.", @"Queue status");
+ break;
+ }
+
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueProgressNotification object:self userInfo:@{HBQueueProgressNotificationPercentKey: @1.0,
+ HBQueueProgressNotificationInfoKey: info}];
+
+ NSInteger index = [self.items indexOfObject:item];
+ NSIndexSet *indexes = index > -1 ? [NSIndexSet indexSetWithIndex:index] : [NSIndexSet indexSet];
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueDidCompleteItemNotification object:self userInfo:@{HBQueueDidCompleteItemNotificationItemKey: item,
+ HBQueueItemNotificationIndexesKey: indexes}];
+
+ [self.items commit];
+}
+
+/**
+ * Here we actually tell hb_scan to perform the source scan, using the path to source and title number
+ */
+- (void)encodeItem:(HBQueueItem *)item
+{
+ NSParameterAssert(item);
+
+ // Progress handler
+ void (^progressHandler)(HBState state, HBProgress progress, NSString *info) = ^(HBState state, HBProgress progress, NSString *info)
+ {
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueProgressNotification object:self userInfo:@{HBQueueProgressNotificationPercentKey: @0,
+ HBQueueProgressNotificationInfoKey: info}];
+ };
+
+ // Completion handler
+ void (^completionHandler)(HBCoreResult result) = ^(HBCoreResult result)
+ {
+ if (result == HBCoreResultDone)
+ {
+ [self realEncodeItem:item];
+ }
+ else
+ {
+ [self completedItem:item result:result];
+ [self encodeNextQueueItem];
+ }
+ };
+
+ // 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:item.fileURL
+ titleIndex:item.job.titleIdx
+ previews:10
+ minDuration:0
+ progressHandler:progressHandler
+ completionHandler:completionHandler];
+}
+
+/**
+ * This assumes that we have re-scanned and loaded up a new queue item to send to libhb
+ */
+- (void)realEncodeItem:(HBQueueItem *)item
+{
+ NSParameterAssert(item);
+
+ HBJob *job = item.job;
+
+ // Reset the title in the job.
+ job.title = self.core.titles.firstObject;
+
+ NSParameterAssert(job);
+
+ HBStateFormatter *formatter = [[HBStateFormatter alloc] init];
+ formatter.title = job.outputFileName;
+ self.core.stateFormatter = formatter;
+
+ // Progress handler
+ void (^progressHandler)(HBState state, HBProgress progress, NSString *info) = ^(HBState state, HBProgress progress, NSString *info)
+ {
+ if (state == HBStateMuxing)
+ {
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueProgressNotification
+ object:self
+ userInfo:@{HBQueueProgressNotificationPercentKey: @1,
+ HBQueueProgressNotificationInfoKey: info}];
+ }
+ else
+ {
+ [NSNotificationCenter.defaultCenter postNotificationName:HBQueueProgressNotification
+ object:self
+ userInfo:@{HBQueueProgressNotificationPercentKey: @(progress.percent),
+ HBQueueProgressNotificationInfoKey: info}];
+ }
+ };
+
+ // Completion handler
+ void (^completionHandler)(HBCoreResult result) = ^(HBCoreResult result)
+ {
+ [self completedItem:item result:result];
+ [self encodeNextQueueItem];
+ };
+
+ // We should be all setup so let 'er rip
+ [self.core encodeJob:job progressHandler:progressHandler completionHandler:completionHandler];
+
+ // We are done using the title, remove it from the job
+ job.title = nil;
+}
+
+/**
+ * Cancels the current job
+ */
+- (void)doCancelCurrentItem
+{
+ if (self.core.state == HBStateScanning)
+ {
+ [self.core cancelScan];
+ }
+ else
+ {
+ [self.core cancelEncode];
+ }
+}
+
+/**
+ * Cancels the current job and starts processing the next in queue.
+ */
+- (void)cancelCurrentItemAndContinue
+{
+ [self doCancelCurrentItem];
+}
+
+/**
+ * Cancels the current job and stops libhb from processing the remaining encodes.
+ */
+- (void)cancelCurrentItemAndStop
+{
+ self.stop = YES;
+ [self doCancelCurrentItem];
+}
+
+/**
+ * Finishes the current job and stops libhb from processing the remaining encodes.
+ */
+- (void)finishCurrentAndStop
+{
+ self.stop = YES;
+}
+
+@end
diff --git a/macosx/HBQueueController.h b/macosx/HBQueueController.h
index 597b68a1d..722416cd1 100644
--- a/macosx/HBQueueController.h
+++ b/macosx/HBQueueController.h
@@ -10,36 +10,17 @@ NS_ASSUME_NONNULL_BEGIN
@class HBAppDelegate;
@class HBController;
-@class HBOutputPanelController;
-@class HBCore;
-@class HBJob;
+@class HBQueue;
@interface HBQueueController : NSWindowController <NSToolbarDelegate, NSWindowDelegate>
-- (instancetype)initWithURL:(NSURL *)queueURL;
+- (instancetype)initWithQueue:(HBQueue *)queue;
-/// The HBCore used for encoding.
-@property (nonatomic, readonly) HBCore *core;
+@property (nonatomic, readonly) HBQueue *queue;
-@property (nonatomic, assign, nullable) HBController *controller;
@property (nonatomic, weak, nullable) HBAppDelegate *delegate;
-@property (nonatomic, readonly) NSUInteger count;
-@property (nonatomic, readonly) NSUInteger pendingItemsCount;
-
-- (void)addJob:(HBJob *)item;
-- (void)addJobsFromArray:(NSArray<HBJob *> *)items;
-
-- (BOOL)jobExistAtURL:(NSURL *)url;
-
-- (void)removeAllJobs;
-- (void)removeCompletedJobs;
-
-- (void)setEncodingJobsAsPending;
-
-- (IBAction)rip:(id)sender;
-- (IBAction)cancelRip:(id)sender;
-
+- (IBAction)toggleStartCancel:(id)sender;
- (IBAction)togglePauseResume:(id)sender;
@end
diff --git a/macosx/HBQueueController.m b/macosx/HBQueueController.m
index 87d0ceefe..40ae17334 100644
--- a/macosx/HBQueueController.m
+++ b/macosx/HBQueueController.m
@@ -6,112 +6,65 @@
#import "HBQueueController.h"
-#import "HBQueueItem.h"
-
-#import "HBController.h"
#import "HBAppDelegate.h"
-#import "HBTableView.h"
-#import "HBQueueItemView.h"
-
-#import "NSArray+HBAdditions.h"
-#import "HBUtilities.h"
+#import "HBQueue.h"
+#import "HBQueueTableViewController.h"
+#import "HBQueueDetailsViewController.h"
#import "HBDockTile.h"
-
-#import "HBOutputRedirect.h"
-#import "HBJobOutputFileWriter.h"
#import "HBPreferencesController.h"
+#import "NSArray+HBAdditions.h"
@import HandBrakeKit;
-// Pasteboard type for or drag operations
-#define DragDropSimplePboardType @"HBQueueCustomTableViewPboardType"
-
-// DockTile update frequency in total percent increment
-#define dockTileUpdateFrequency 0.1f
-
static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
-@interface HBQueueController () <NSTableViewDataSource, HBTableViewDelegate, HBQueueItemViewDelegate, NSUserNotificationCenterDelegate>
+@interface HBQueueController () <NSUserNotificationCenterDelegate, HBQueueTableViewControllerDelegate, HBQueueDetailsViewControllerDelegate>
+
+@property (weak) IBOutlet NSSplitView *splitView;
+@property (nonatomic) NSSplitViewController *splitViewController;
+@property (nonatomic) HBQueueTableViewController *tableViewController;
+@property (nonatomic) HBQueueDetailsViewController *detailsViewController;
/// Whether the window is visible or occluded,
/// useful to avoid updating the UI needlessly
@property (nonatomic) BOOL visible;
-// Progress
-@property (nonatomic, strong) NSAttributedString *progressInfo;
-@property (nonatomic, strong) NSDictionary *monospacedAttr;
-
@property (nonatomic, readonly) HBDockTile *dockTile;
-@property (nonatomic, readwrite) double dockIconProgress;
-
-@property (unsafe_unretained) IBOutlet NSTextField *progressTextField;
-@property (unsafe_unretained) IBOutlet NSTextField *countTextField;
-@property (unsafe_unretained) IBOutlet HBTableView *tableView;
+@property (nonatomic) double dockIconProgress;
@property (nonatomic) IBOutlet NSToolbarItem *ripToolbarItem;
@property (nonatomic) IBOutlet NSToolbarItem *pauseToolbarItem;
-@property (nonatomic) NSTableCellView *dummyCell;
-@property (nonatomic) NSLayoutConstraint *dummyCellWidth;
-
-@property (nonatomic, readonly) HBDistributedArray<HBQueueItem *> *items;
-
-@property (nonatomic) HBQueueItem *currentItem;
-@property (nonatomic) HBJobOutputFileWriter *currentLog;
-
-@property (nonatomic, readwrite) BOOL stop;
-
-@property (nonatomic, readwrite) NSUInteger pendingItemsCount;
-@property (nonatomic, readwrite) NSUInteger completedItemsCount;
-
-@property (nonatomic) NSArray<HBQueueItem *> *dragNodesArray;
-
@end
@interface HBQueueController (TouchBar) <NSTouchBarProvider, NSTouchBarDelegate>
-- (void)_touchBar_updateButtonsStateForQueueCore:(HBState)state;
+- (void)_touchBar_updateButtonsState;
- (void)_touchBar_validateUserInterfaceItems;
@end
@implementation HBQueueController
-- (instancetype)initWithURL:(NSURL *)queueURL;
+- (instancetype)initWithQueue:(HBQueue *)queue
{
- NSParameterAssert(queueURL);
+ NSParameterAssert(queue);
if (self = [super initWithWindowNibName:@"Queue"])
{
// Load the dockTile and instiante initial text fields
- _dockTile = [[HBDockTile alloc] initWithDockTile:[[NSApplication sharedApplication] dockTile]
- image:[[NSApplication sharedApplication] applicationIconImage]];
-
- NSInteger loggingLevel = [[NSUserDefaults standardUserDefaults] integerForKey:@"LoggingLevel"];
-
- // Init a separate instance of libhb for the queue
- _core = [[HBCore alloc] initWithLogLevel:loggingLevel name:@"QueueCore"];
- _core.automaticallyPreventSleep = NO;
+ _dockTile = [[HBDockTile alloc] initWithDockTile:NSApplication.sharedApplication.dockTile
+ image:NSApplication.sharedApplication.applicationIconImage];
- // Progress
- _monospacedAttr = @{NSFontAttributeName: [NSFont monospacedDigitSystemFontOfSize:[NSFont smallSystemFontSize] weight:NSFontWeightRegular]};
- _progressInfo = [[NSAttributedString alloc] initWithString:@""];
+ // Init state
+ _queue = queue;
- // Load the queue from disk.
- _items = [[HBDistributedArray alloc] initWithURL:queueURL class:[HBQueueItem class]];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadQueue) name:HBDistributedArrayChanged object:_items];
-
- [NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
+ NSUserNotificationCenter.defaultUserNotificationCenter.delegate = self;
}
return self;
}
-- (void)dealloc
-{
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-}
-
- (void)windowDidLoad
{
if (@available (macOS 10.12, *))
@@ -119,33 +72,116 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
self.window.tabbingMode = NSWindowTabbingModeDisallowed;
}
- // lets setup our queue list table view for drag and drop here
- [self.tableView registerForDraggedTypes:@[DragDropSimplePboardType]];
- [self.tableView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
- [self.tableView setVerticalMotionCanBeginDrag:YES];
+ _queue.undoManager = self.window.undoManager;
+
+ // Set up the child view controllers
+ _splitViewController = [[NSSplitViewController alloc] init];
+ _splitViewController.splitView = _splitView;
+ _splitViewController.view.wantsLayer = YES;
+ _splitViewController.splitView.vertical = YES;
+ _splitViewController.splitView.autosaveName = @"HBQueueSplitViewAutosave";
+ _splitViewController.splitView.identifier = @"HBQueueSplitViewIdentifier";
+
+ _tableViewController = [[HBQueueTableViewController alloc] initWithQueue:self.queue delegate:self];
+ _detailsViewController = [[HBQueueDetailsViewController alloc] initWithDelegate:self];
+
+ NSSplitViewItem *tableItem = [NSSplitViewItem splitViewItemWithViewController:_tableViewController];
+ tableItem.minimumThickness = 160;
- [self updateQueueStats];
+ [_splitViewController addSplitViewItem:tableItem];
- [self.core addObserver:self forKeyPath:@"state"
+ NSSplitViewItem *detailsItem = [NSSplitViewItem splitViewItemWithViewController:_detailsViewController];
+ detailsItem.canCollapse = YES;
+ detailsItem.minimumThickness = 240;
+
+ [_splitViewController addSplitViewItem:detailsItem];
+
+ self.window.contentViewController = _splitViewController;
+
+ self.window.frameAutosaveName = @"HBQueueWindowFrameAutosave";
+ [self.window setFrameFromString: @"HBQueueWindowFrameAutosave"];
+
+ // Set up observers
+ [self.queue.core addObserver:self forKeyPath:@"state"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
context:HBControllerQueueCoreContext];
- [self addObserver:self forKeyPath:@"pendingItemsCount"
+ [self.queue addObserver:self forKeyPath:@"pendingItemsCount"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
context:HBControllerQueueCoreContext];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueLowSpaceAlertNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ [self queueLowDiskSpaceAlert];
+ }];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueDidCompleteNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ // Since there are no more items to encode, go to queueCompletedAlerts
+ // for user specified alerts after queue completed
+ [self queueCompletedAlerts];
+ }];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueProgressNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ // Update dock icon
+ double progress = [note.userInfo[HBQueueProgressNotificationPercentKey] doubleValue];
+ double hours = 1;
+ double minutes = 1;
+ double seconds = 1;
+
+#define dockTileUpdateFrequency 0.1f
+
+ if (self.dockIconProgress < 100.0 * progress)
+ {
+ [self.dockTile updateDockIcon:progress hours:hours minutes:minutes seconds:seconds];
+ self.dockIconProgress += dockTileUpdateFrequency;
+ }
+ }];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueDidCompleteItemNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ // Restore dock icon
+ [self.dockTile updateDockIcon:-1.0 withETA:@""];
+ self.dockIconProgress = 0;
+
+ // Run the per item notification and actions
+ HBQueueItem *item = note.userInfo[HBQueueDidCompleteItemNotificationItemKey];
+ if (item.state == HBQueueItemStateCompleted)
+ {
+ [self sendToExternalApp:item];
+ }
+
+ if (item.state == HBQueueItemStateCompleted || item.state == HBQueueItemStateFailed)
+ {
+ [self itemCompletedAlerts:item];
+ }
+ }];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == HBControllerQueueCoreContext)
{
- HBState state = self.core.state;
- [self updateToolbarButtonsStateForQueueCore:state];
+ [self updateToolbarButtonsState];
[self.window.toolbar validateVisibleItems];
+
if (@available(macOS 10.12.2, *))
{
- [self _touchBar_updateButtonsStateForQueueCore:state];
+ [self _touchBar_updateButtonsState];
[self _touchBar_validateUserInterfaceItems];
}
+
+ NSString *string;
+ if (self.queue.pendingItemsCount == 0)
+ {
+ string = NSLocalizedString(@"No encode pending", @"Queue status");
+ }
+ else if (self.queue.pendingItemsCount == 1)
+ {
+ string = [NSString stringWithFormat: NSLocalizedString(@"%d encode pending", @"Queue status"), self.queue.pendingItemsCount];
+ }
+ else
+ {
+ string = [NSString stringWithFormat: NSLocalizedString(@"%d encodes pending", @"Queue status"), self.queue.pendingItemsCount];
+ }
+
+ self.window.title = [NSString stringWithFormat: NSLocalizedString(@"Queue (%@)", @"Queue window title"), string];
}
else
{
@@ -155,9 +191,9 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
#pragma mark Toolbar
-- (void)updateToolbarButtonsStateForQueueCore:(HBState)state
+- (void)updateToolbarButtonsState
{
- if (state == HBStatePaused)
+ if (self.queue.canResume)
{
_pauseToolbarItem.image = [NSImage imageNamed: @"encode"];
_pauseToolbarItem.label = NSLocalizedString(@"Resume", @"Toolbar Pause Item");
@@ -170,7 +206,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
_pauseToolbarItem.toolTip = NSLocalizedString(@"Pause Encoding", @"Toolbar Pause Item");
}
- if (state == HBStateScanning || state == HBStateWorking || state == HBStateSearching || state == HBStateMuxing || state == HBStatePaused)
+ if (self.queue.isEncoding)
{
_ripToolbarItem.image = [NSImage imageNamed:@"stopencode"];
_ripToolbarItem.label = NSLocalizedString(@"Stop", @"Toolbar Start/Stop Item");
@@ -188,25 +224,23 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
{
SEL action = menuItem.action;
- if (action == @selector(rip:))
+ if (action == @selector(toggleStartCancel:))
{
- if (self.core.state == HBStateIdle)
- {
- menuItem.title = NSLocalizedString(@"Start Encoding", @"Queue -> start/stop menu");
-
- return (self.pendingItemsCount > 0);
- }
- else if (self.core.state != HBStateIdle)
+ if (self.queue.isEncoding)
{
menuItem.title = NSLocalizedString(@"Stop Encoding", @"Queue -> start/stop menu");
-
return YES;
}
+ else
+ {
+ menuItem.title = NSLocalizedString(@"Start Encoding", @"Queue -> start/stop menu");
+ return self.queue.canEncode;
+ }
}
- if (action == @selector(pause:))
+ if (action == @selector(togglePauseResume:))
{
- if (self.core.state != HBStatePaused)
+ if (self.queue.canPause)
{
menuItem.title = NSLocalizedString(@"Pause Encoding", @"Queue -> pause/resume menu");
}
@@ -215,30 +249,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
menuItem.title = NSLocalizedString(@"Resume Encoding", @"Queue -> pause/resume men");
}
- return (self.core.state == HBStateWorking || self.core.state == HBStatePaused);
- }
-
- if (action == @selector(editSelectedQueueItem:) ||
- action == @selector(removeSelectedQueueItem:) ||
- action == @selector(revealSelectedQueueItems:) ||
- action == @selector(revealSelectedQueueItemsSources:))
- {
- return (self.tableView.selectedRow != -1 || self.tableView.clickedRow != -1);
- }
-
- if (action == @selector(resetJobState:))
- {
- return self.tableView.targetedRowIndexes.count > 0;
- }
-
- if (action == @selector(clearAll:))
- {
- return self.items.count > 0;
- }
-
- if (action == @selector(clearCompleted:))
- {
- return self.completedItemsCount > 0;
+ return self.queue.canPause || self.queue.canResume;
}
return YES;
@@ -246,30 +257,14 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
- (BOOL)validateUserIterfaceItemForAction:(SEL)action
{
- HBState s = self.core.state;
-
if (action == @selector(toggleStartCancel:))
{
- if ((s == HBStateScanning) || (s == HBStatePaused) || (s == HBStateWorking) || (s == HBStateMuxing))
- {
- return YES;
- }
- else
- {
- return (self.pendingItemsCount > 0);
- }
+ return self.queue.isEncoding || self.queue.canEncode;
}
if (action == @selector(togglePauseResume:))
{
- if (s == HBStatePaused)
- {
- return YES;
- }
- else
- {
- return (s == HBStateWorking || s == HBStateMuxing);
- }
+ return self.queue.canPause || self.queue.canResume;
}
return NO;
@@ -281,668 +276,160 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
return [self validateUserIterfaceItemForAction:action];
}
-#pragma mark - Public methods
-- (void)addJob:(HBJob *)item
-{
- NSParameterAssert(item);
- [self addJobsFromArray:@[item]];
-}
-
-- (void)addJobsFromArray:(NSArray<HBJob *> *)jobs;
-{
- NSParameterAssert(jobs);
- NSMutableArray *itemsToAdd = [NSMutableArray array];
- for (HBJob *job in jobs)
- {
- HBQueueItem *item = [[HBQueueItem alloc] initWithJob:job];
- [itemsToAdd addObject:item];
- }
- if (itemsToAdd.count)
- {
- [self addQueueItems:itemsToAdd];
- }
-}
-
-- (BOOL)jobExistAtURL:(NSURL *)url
-{
- NSParameterAssert(url);
-
- for (HBQueueItem *item in self.items)
- {
- if ((item.state == HBQueueItemStateReady || item.state == HBQueueItemStateWorking)
- && [item.completeOutputURL isEqualTo:url])
- {
- return YES;
- }
- }
- return NO;
-}
-
-- (NSUInteger)count
-{
- return self.items.count;
-}
-
-/**
- * This method will clear the queue of any encodes that are not still pending
- * this includes both successfully completed encodes as well as canceled encodes
- */
-- (void)removeCompletedJobs
-{
- [self.items beginTransaction];
- NSIndexSet *indexes = [self.items indexesOfObjectsUsingBlock:^BOOL(HBQueueItem *item) {
- return (item.state == HBQueueItemStateCompleted || item.state == HBQueueItemStateCanceled);
- }];
- [self removeQueueItemsAtIndexes:indexes];
- [self.items commit];
-}
-
-/**
- * This method will clear the queue of all encodes. effectively creating an empty queue
- */
-- (void)removeAllJobs
-{
- [self.items beginTransaction];
- [self removeQueueItemsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.items.count)]];
- [self.items commit];
-}
-
-/**
- * This method will set any item marked as encoding back to pending
- * currently used right after a queue reload
- */
-- (void)setEncodingJobsAsPending
+- (void)windowDidChangeOcclusionState:(NSNotification *)notification
{
- [self.items beginTransaction];
-
- NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
- NSUInteger idx = 0;
- for (HBQueueItem *item in self.items)
- {
- // We want to keep any queue item that is pending or was previously being encoded
- if (item.state == HBQueueItemStateWorking)
- {
- item.state = HBQueueItemStateReady;
- [indexes addIndex:idx];
- }
- idx++;
- }
- [self reloadQueueItemsAtIndexes:indexes];
- [self.items commit];
+ self.visible = self.window.occlusionState & NSWindowOcclusionStateVisible ? YES : NO;
}
#pragma mark - Private queue editing methods
/**
- * Reloads the queue, this is called
- * when another HandBrake instances modifies the queue
+ * Delete encodes from the queue window and accompanying array
+ * Also handling first cancelling the encode if in fact its currently encoding.
*/
-- (void)reloadQueue
-{
- [self updateQueueStats];
- [self.tableView reloadData];
- [self.window.undoManager removeAllActions];
-}
-
-- (void)reloadQueueItemAtIndex:(NSUInteger)idx
-{
- [self reloadQueueItemsAtIndexes:[NSIndexSet indexSetWithIndex:idx]];
-}
-
-- (void)reloadQueueItemsAtIndexes:(NSIndexSet *)indexes
-{
- NSIndexSet *columnIndexes = [NSIndexSet indexSetWithIndex:0];
- [self.tableView reloadDataForRowIndexes:indexes columnIndexes:columnIndexes];
- [self updateQueueStats];
-}
-
-- (void)addQueueItems:(NSArray<HBQueueItem *> *)items
-{
- NSParameterAssert(items);
- NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(self.items.count, items.count)];
- [self addQueueItems:items atIndexes:indexes];
-}
-
-- (void)addQueueItems:(NSArray *)items atIndexes:(NSIndexSet *)indexes
-{
- NSParameterAssert(items);
- NSParameterAssert(indexes);
- [self.items beginTransaction];
- [self.tableView beginUpdates];
-
- // Forward
- NSUInteger currentIndex = indexes.firstIndex;
- NSUInteger currentObjectIndex = 0;
- while (currentIndex != NSNotFound)
- {
- [self.items insertObject:items[currentObjectIndex] atIndex:currentIndex];
- currentIndex = [indexes indexGreaterThanIndex:currentIndex];
- currentObjectIndex++;
- }
-
- [self.tableView insertRowsAtIndexes:indexes
- withAnimation:NSTableViewAnimationSlideDown];
-
- NSUndoManager *undo = self.window.undoManager;
- [[undo prepareWithInvocationTarget:self] removeQueueItemsAtIndexes:indexes];
-
- if (!undo.isUndoing)
- {
- if (items.count == 1)
- {
- [undo setActionName:NSLocalizedString(@"Add Job To Queue", @"Queue undo action name")];
- }
- else
- {
- [undo setActionName:NSLocalizedString(@"Add Jobs To Queue", @"Queue undo action name")];
- }
- }
-
- [self.tableView endUpdates];
- [self updateQueueStats];
- [self.items commit];
-}
-
-- (void)removeQueueItemAtIndex:(NSUInteger)index
-{
- [self removeQueueItemsAtIndexes:[NSIndexSet indexSetWithIndex:index]];
-}
-
- (void)removeQueueItemsAtIndexes:(NSIndexSet *)indexes
{
- NSParameterAssert(indexes);
-
- if (indexes.count == 0)
+ if ([self.queue.items beginTransaction] == HBDistributedArrayContentReload)
{
+ // Do not execture the action if the array changed.
+ [self.queue.items commit];
return;
}
- [self.items beginTransaction];
- [self.tableView beginUpdates];
-
- NSArray<HBQueueItem *> *removeItems = [self.items objectsAtIndexes:indexes];
-
- if (self.items.count > indexes.lastIndex)
- {
- [self.items removeObjectsAtIndexes:indexes];
- }
-
- [self.tableView removeRowsAtIndexes:indexes withAnimation:NSTableViewAnimationSlideUp];
- [self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:indexes.firstIndex] byExtendingSelection:NO];
-
- NSUndoManager *undo = self.window.undoManager;
- [[undo prepareWithInvocationTarget:self] addQueueItems:removeItems atIndexes:indexes];
-
- if (!undo.isUndoing)
- {
- if (indexes.count == 1)
- {
- [undo setActionName:NSLocalizedString(@"Remove Job From Queue", @"Queue undo action name")];
- }
- else
- {
- [undo setActionName:NSLocalizedString(@"Remove Jobs From Queue", @"Queue undo action name")];
- }
- }
-
- [self.tableView endUpdates];
- [self updateQueueStats];
- [self.items commit];
-}
-
-- (void)moveQueueItems:(NSArray *)items toIndex:(NSUInteger)index
-{
- [self.items beginTransaction];
- [self.tableView beginUpdates];
-
- NSMutableArray *source = [NSMutableArray array];
- NSMutableArray *dest = [NSMutableArray array];
-
- for (id object in items.reverseObjectEnumerator)
+ if (indexes.count)
{
- NSUInteger sourceIndex = [self.items indexOfObject:object];
- [self.items removeObjectAtIndex:sourceIndex];
-
-
- if (sourceIndex < index)
- {
- index--;
- }
-
- [self.items insertObject:object atIndex:index];
-
- [source addObject:@(index)];
- [dest addObject:@(sourceIndex)];
-
- [self.tableView moveRowAtIndex:sourceIndex toIndex:index];
- }
-
- NSUndoManager *undo = self.window.undoManager;
- [[undo prepareWithInvocationTarget:self] moveQueueItemsAtIndexes:source toIndexes:dest];
+ NSMutableIndexSet *mutableIndexes = [indexes 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
+ NSIndexSet *workingIndexes = [self.queue.items indexesOfObjectsUsingBlock:^BOOL(HBQueueItem *item) {
+ return item.state == HBQueueItemStateWorking;
+ }];
- if (!undo.isUndoing)
- {
- if (items.count == 1)
+ if ([mutableIndexes containsIndexes:workingIndexes])
{
- [undo setActionName:NSLocalizedString(@"Move Job in Queue", @"Queue undo action name")];
- }
- else
- {
- [undo setActionName:NSLocalizedString(@"Move Jobs in Queue", @"Queue undo action name")];
- }
- }
-
- [self.tableView endUpdates];
- [self.items commit];
-}
-
-- (void)moveQueueItemsAtIndexes:(NSArray *)source toIndexes:(NSArray *)dest
-{
- [self.items beginTransaction];
- [self.tableView beginUpdates];
-
- NSMutableArray *newSource = [NSMutableArray array];
- NSMutableArray *newDest = [NSMutableArray array];
+ [mutableIndexes removeIndexes:workingIndexes];
+ NSArray<HBQueueItem *> *workingItems = [self.queue.items filteredArrayUsingBlock:^BOOL(HBQueueItem *item) {
+ return item.state == HBQueueItemStateWorking;
+ }];
- for (NSInteger idx = source.count - 1; idx >= 0; idx--)
- {
- NSUInteger sourceIndex = [source[idx] integerValue];
- NSUInteger destIndex = [dest[idx] integerValue];
+ if ([workingItems containsObject:self.queue.currentItem])
+ {
+ NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It?", @"Queue Stop Alert -> stop and remove message")];
- [newSource addObject:@(destIndex)];
- [newDest addObject:@(sourceIndex)];
+ // Which window to attach the sheet to?
+ NSWindow *targetWindow = self.window;
- id obj = [self.items objectAtIndex:sourceIndex];
- [self.items removeObjectAtIndex:sourceIndex];
- [self.items insertObject:obj atIndex:destIndex];
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:alertTitle];
+ [alert setInformativeText:NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", @"Queue Stop Alert -> stop and remove informative text")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Keep Encoding", @"Queue Stop Alert -> stop and remove first button")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Stop Encoding and Delete", @"Queue Stop Alert -> stop and remove second button")];
+ [alert setAlertStyle:NSAlertStyleCritical];
- [self.tableView moveRowAtIndex:sourceIndex toIndex:destIndex];
- }
+ [alert beginSheetModalForWindow:targetWindow completionHandler:^(NSModalResponse returnCode) {
+ if (returnCode == NSAlertSecondButtonReturn)
+ {
+ [self.queue.items beginTransaction];
- NSUndoManager *undo = self.window.undoManager;
- [[undo prepareWithInvocationTarget:self] moveQueueItemsAtIndexes:newSource toIndexes:newDest];
+ NSInteger index = [self.queue.items indexOfObject:self.queue.currentItem];
+ [self.queue cancelCurrentItemAndContinue];
- if (!undo.isUndoing)
- {
- if (source.count == 1)
- {
- [undo setActionName:NSLocalizedString(@"Move Job in Queue", @"Queue undo action name")];
- }
- else
- {
- [undo setActionName:NSLocalizedString(@"Move Jobs in Queue", @"Queue undo action name")];
+ [self.queue removeQueueItemAtIndex:index];
+ [self.queue.items commit];
+ }
+ }];
+ }
}
- }
-
- [self.tableView endUpdates];
- [self.items commit];
-}
-
-- (void)windowDidChangeOcclusionState:(NSNotification *)notification
-{
- if ([self.window occlusionState] & NSWindowOcclusionStateVisible)
- {
- self.visible = YES;
- self.progressTextField.attributedStringValue = self.progressInfo;
- }
- else
- {
- self.visible = NO;
- }
-}
-- (void)updateProgress:(NSString *)info progress:(double)progress hidden:(BOOL)hidden
-{
- self.progressInfo = [[NSAttributedString alloc] initWithString:info attributes:_monospacedAttr];
- if (self.visible)
- {
- self.progressTextField.attributedStringValue = _progressInfo;
+ // remove the non working items immediately
+ [self.queue removeQueueItemsAtIndexes:mutableIndexes];
}
- [self.controller setQueueInfo:_progressInfo progress:progress hidden:hidden];
+ [self.queue.items commit];
}
-/**
- * Updates the queue status label.
- */
-- (void)updateQueueStats
+- (void)doEditQueueItem:(HBQueueItem *)item
{
- // lets get the stats on the status of the queue array
- NSUInteger pendingCount = 0;
- NSUInteger completedCount = 0;
-
- for (HBQueueItem *item in self.items)
- {
- if (item.state == HBQueueItemStateReady)
- {
- pendingCount++;
- }
- if (item.state == HBQueueItemStateCompleted)
- {
- completedCount++;
- }
- }
-
- NSString *string;
- if (pendingCount == 0)
- {
- string = NSLocalizedString(@"No encode pending", @"Queue status");
- }
- else if (pendingCount == 1)
- {
- string = [NSString stringWithFormat: NSLocalizedString(@"%d encode pending", @"Queue status"), pendingCount];
- }
- else
- {
- string = [NSString stringWithFormat: NSLocalizedString(@"%d encodes pending", @"Queue status"), pendingCount];
- }
-
- self.countTextField.stringValue = string;
-
- self.pendingItemsCount = pendingCount;
- self.completedItemsCount = completedCount;
-}
-
-#pragma mark - Queue Job Processing
+ NSParameterAssert(item);
+ [self.queue.items beginTransaction];
-- (BOOL)_isDiskSpaceLowAtURL:(NSURL *)url
-{
- if ([[NSUserDefaults standardUserDefaults] boolForKey:@"HBQueuePauseIfLowSpace"])
+ if (item != self.queue.currentItem)
{
- NSURL *volumeURL = nil;
- NSDictionary<NSURLResourceKey, id> *attrs = [url resourceValuesForKeys:@[NSURLIsVolumeKey, NSURLVolumeURLKey] error:NULL];
- long long minCapacity = [[[NSUserDefaults standardUserDefaults] stringForKey:@"HBQueueMinFreeSpace"] longLongValue] * 1000000000;
+ item.state = HBQueueItemStateWorking;
- volumeURL = [attrs[NSURLIsVolumeKey] boolValue] ? url : attrs[NSURLVolumeURLKey];
+ // NSUInteger row = [self.queue.items indexOfObject:item];
+ //FIXME
+ //[self reloadQueueItemAtIndex:row];
- if (volumeURL)
- {
- [volumeURL removeCachedResourceValueForKey:NSURLVolumeAvailableCapacityKey];
- attrs = [volumeURL resourceValuesForKeys:@[NSURLVolumeAvailableCapacityKey] error:NULL];
-
- if (attrs[NSURLVolumeAvailableCapacityKey])
+ [self.delegate openJob:[item.job copy] completionHandler:^(BOOL result) {
+ [self.queue.items beginTransaction];
+ if (result)
{
- if ([attrs[NSURLVolumeAvailableCapacityKey] longLongValue] < minCapacity)
- {
- return YES;
- }
+ // Now that source is loaded and settings applied, delete the queue item from the queue
+ NSInteger index = [self.queue.items indexOfObject:item];
+ item.state = HBQueueItemStateReady;
+ [self.queue removeQueueItemAtIndex:index];
}
- }
- }
-
- return NO;
-}
-
-/**
- * Used to get the next pending queue item and return it if found
- */
-- (HBQueueItem *)getNextPendingQueueItem
-{
- for (HBQueueItem *item in self.items)
- {
- if (item.state == HBQueueItemStateReady)
- {
- return item;
- }
- }
- return nil;
-}
-
-/**
- * Starts the queue
- */
-- (void)encodeNextQueueItem
-{
- [self.items beginTransaction];
- self.currentItem = nil;
-
- // since we have completed an encode, we go to the next
- if (self.stop)
- {
- [HBUtilities writeToActivityLog:"Queue manually stopped"];
-
- self.stop = NO;
- [self.core allowSleep];
- }
- else
- {
- // Check to see if there are any more pending items in the queue
- HBQueueItem *nextItem = [self getNextPendingQueueItem];
-
- if (nextItem && [self _isDiskSpaceLowAtURL:nextItem.outputURL])
- {
- // Disk space is low, show an alert
- [HBUtilities writeToActivityLog:"Queue Stopped, low space on destination disk"];
-
- [self queueLowDiskSpaceAlert];
- }
- // If we still have more pending items in our queue, lets go to the next one
- else if (nextItem)
- {
- // now we mark the queue item as working so another instance can not come along and try to scan it while we are scanning
- nextItem.state = HBQueueItemStateWorking;
-
- // Tell HB to output a new activity log file for this encode
- self.currentLog = [[HBJobOutputFileWriter alloc] initWithJob:nextItem.job];
- if (self.currentLog)
+ else
{
- [[HBOutputRedirect stderrRedirect] addListener:self.currentLog];
- [[HBOutputRedirect stdoutRedirect] addListener:self.currentLog];
+ item.state = HBQueueItemStateFailed;
+ NSBeep();
}
-
- self.currentItem = nextItem;
- [self reloadQueueItemAtIndex:[self.items indexOfObject:nextItem]];
-
- // now we can go ahead and scan the new pending queue item
- [self encodeItem:nextItem];
-
- // erase undo manager history
- [self.window.undoManager removeAllActions];
- }
- else
- {
- [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];
-
- [self.core allowSleep];
- }
- }
- [self.items commit];
-}
-
-- (void)completedItem:(HBQueueItem *)item result:(HBCoreResult)result;
-{
- NSParameterAssert(item);
- [self.items beginTransaction];
-
- // Since we are done with this encode, tell output to stop writing to the
- // individual encode log.
- [[HBOutputRedirect stderrRedirect] removeListener:self.currentLog];
- [[HBOutputRedirect stdoutRedirect] removeListener:self.currentLog];
-
- self.currentLog = nil;
-
- // Check to see if the encode state has not been canceled
- // to determine if we should send it to external app.
- if (result != HBCoreResultCanceled)
- {
- // Send to tagger
- [self sendToExternalApp:item];
- }
-
- // Mark the encode just finished
- switch (result) {
- case HBCoreResultDone:
- item.state = HBQueueItemStateCompleted;
- break;
- case HBCoreResultCanceled:
- item.state = HBQueueItemStateCanceled;
- break;
- default:
- item.state = HBQueueItemStateFailed;
- break;
+ [self.queue.items commit];
+ }];
}
-
- if ([self.items containsObject:item])
+ else
{
- [self reloadQueueItemAtIndex:[self.items indexOfObject:item]];
- }
- [self.window.toolbar validateVisibleItems];
- [self.items commit];
-
- // Update UI
- NSString *info = nil;
- switch (result) {
- case HBCoreResultDone:
- info = NSLocalizedString(@"Encode Finished.", @"Queue status");
- [self itemCompletedAlerts:item result:result];
- break;
- case HBCoreResultCanceled:
- info = NSLocalizedString(@"Encode Canceled.", @"Queue status");
- break;
- default:
- info = NSLocalizedString(@"Encode Failed.", @"Queue status");
- [self itemCompletedAlerts:item result:result];
- break;
+ NSBeep();
}
- [self updateProgress:info progress:1.0 hidden:YES];
- // Restore dock icon
- [self.dockTile updateDockIcon:-1.0 withETA:@""];
- self.dockIconProgress = 0;
+ [self.queue.items commit];
}
/**
- * Here we actually tell hb_scan to perform the source scan, using the path to source and title number
+ * Send the selected queue item back to the main window for rescan and possible edit.
*/
-- (void)encodeItem:(HBQueueItem *)item
+- (void)editQueueItem:(HBQueueItem *)item
{
- NSParameterAssert(item);
-
- // Progress handler
- void (^progressHandler)(HBState state, HBProgress progress, NSString *info) = ^(HBState state, HBProgress progress, NSString *info)
+ if ([self.queue.items beginTransaction] == HBDistributedArrayContentReload)
{
- [self updateProgress:info progress:0 hidden:NO];
- };
+ // Do not execture the action if the array changed.
+ [self.queue.items commit];
+ return;
+ }
- // Completion handler
- void (^completionHandler)(HBCoreResult result) = ^(HBCoreResult result)
+ // if this is a currently encoding item, 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 (item == self.queue.currentItem)
{
- if (result == HBCoreResultDone)
- {
- [self realEncodeItem:item];
- }
- else
- {
- [self completedItem:item result:result];
- [self encodeNextQueueItem];
- }
- };
-
- // 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:item.fileURL
- titleIndex:item.job.titleIdx
- previews:10
- minDuration:0
- progressHandler:progressHandler
- completionHandler:completionHandler];
-}
-
-/**
- * This assumes that we have re-scanned and loaded up a new queue item to send to libhb
- */
-- (void)realEncodeItem:(HBQueueItem *)item
-{
- NSParameterAssert(item);
+ NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Edit It?", @"Queue Edit Alert -> stop and edit message")];
- HBJob *job = item.job;
+ // Which window to attach the sheet to?
+ NSWindow *docWindow = self.window;
- // Reset the title in the job.
- job.title = self.core.titles.firstObject;
-
- NSParameterAssert(job);
-
- HBStateFormatter *formatter = [[HBStateFormatter alloc] init];
- formatter.title = job.outputFileName;
- self.core.stateFormatter = formatter;
-
- // Progress handler
- void (^progressHandler)(HBState state, HBProgress progress, NSString *info) = ^(HBState state, HBProgress progress, NSString *info)
- {
- if (state == HBStateWorking)
- {
- // Update dock icon
- if (self.dockIconProgress < 100.0 * progress.percent)
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:alertTitle];
+ [alert setInformativeText:NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", @"Queue Edit Alert -> stop and edit informative text")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Keep Encoding", @"Queue Edit Alert -> stop and edit first button")];
+ [alert addButtonWithTitle:NSLocalizedString(@"Stop Encoding and Edit", @"Queue Edit Alert -> stop and edit second button")];
+ [alert setAlertStyle:NSAlertStyleCritical];
+
+ [alert beginSheetModalForWindow:docWindow completionHandler:^(NSModalResponse returnCode) {
+ if (returnCode == NSAlertSecondButtonReturn)
{
- [self.dockTile updateDockIcon:progress.percent hours:progress.hours minutes:progress.minutes seconds:progress.seconds];
- self.dockIconProgress += dockTileUpdateFrequency;
+ [self doEditQueueItem:item];
}
- }
- else if (state == HBStateMuxing)
- {
- [self.dockTile updateDockIcon:1.0 withETA:@""];
- }
-
- // Update UI
- [self updateProgress:info progress:progress.percent hidden:NO];
- };
-
- // Completion handler
- void (^completionHandler)(HBCoreResult result) = ^(HBCoreResult result)
- {
- [self completedItem:item result:result];
- [self encodeNextQueueItem];
- };
-
- // We should be all setup so let 'er rip
- [self.core encodeJob:job progressHandler:progressHandler completionHandler:completionHandler];
-
- // We are done using the title, remove it from the job
- job.title = nil;
-}
-
-/**
- * Cancels the current job
- */
-- (void)doCancelCurrentItem
-{
- if (self.core.state == HBStateScanning)
- {
- [self.core cancelScan];
+ }];
}
- else
+ else if (item.state != HBQueueItemStateWorking)
{
- [self.core cancelEncode];
+ [self doEditQueueItem:item];
}
-}
-
-/**
- * Cancels the current job and starts processing the next in queue.
- */
-- (void)cancelCurrentItemAndContinue
-{
- [self doCancelCurrentItem];
-}
-/**
- * Cancels the current job and stops libhb from processing the remaining encodes.
- */
-- (void)cancelCurrentItemAndStop
-{
- self.stop = YES;
- [self doCancelCurrentItem];
-}
-/**
- * Finishes the current job and stops libhb from processing the remaining encodes.
- */
-- (void)finishCurrentAndStop
-{
- self.stop = YES;
+ [self.queue.items commit];
}
#pragma mark - Encode Done Actions
@@ -967,6 +454,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
notification.hasActionButton = YES;
notification.actionButtonTitle = NSLocalizedString(@"Show", @"Notification -> Show in Finder");
notification.userInfo = @{ @"Path": fileURL.path };
+
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
}
@@ -1012,18 +500,20 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
/**
* Runs the alert for a single job
*/
-- (void)itemCompletedAlerts:(HBQueueItem *)item result:(HBCoreResult)result
+- (void)itemCompletedAlerts:(HBQueueItem *)item
{
+ NSUserDefaults *ud = NSUserDefaults.standardUserDefaults;
+
// 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 ([ud integerForKey:@"HBAlertWhenDone"] == HBDoneActionNotification ||
+ [ud integerForKey:@"HBAlertWhenDone"] == HBDoneActionAlertAndNotification)
{
// If Play System Alert has been selected in Preferences
- bool playSound = [[NSUserDefaults standardUserDefaults] boolForKey:@"HBAlertWhenDoneSound"];
+ bool playSound = [ud boolForKey:@"HBAlertWhenDoneSound"];
NSString *title;
NSString *description;
- if (result == HBCoreResultDone)
+ if (item.state == HBQueueItemStateCompleted)
{
title = NSLocalizedString(@"Put down that cocktail…", @"Queue notification alert message");
description = [NSString stringWithFormat:NSLocalizedString(@"Your encode %@ is done!", @"Queue done notification message"),
@@ -1049,15 +539,16 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
*/
- (void)queueCompletedAlerts
{
+ NSUserDefaults *ud = NSUserDefaults.standardUserDefaults;
// If Play System Alert has been selected in Preferences
- if ([[NSUserDefaults standardUserDefaults] boolForKey:@"HBAlertWhenDoneSound"] == YES)
+ if ([ud boolForKey:@"HBAlertWhenDoneSound"] == YES)
{
NSBeep();
}
// If Alert Window or Window and Notification has been selected
- if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionAlert ||
- [[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionAlertAndNotification)
+ if ([ud integerForKey:@"HBAlertWhenDone"] == HBDoneActionAlert ||
+ [ud integerForKey:@"HBAlertWhenDone"] == HBDoneActionAlertAndNotification)
{
// On Screen Notification
NSAlert *alert = [[NSAlert alloc] init];
@@ -1068,7 +559,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
}
// If sleep has been selected
- if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionSleep)
+ if ([ud integerForKey:@"HBAlertWhenDone"] == HBDoneActionSleep)
{
// Sleep
NSDictionary *errorDict;
@@ -1077,7 +568,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
[scriptObject executeAndReturnError: &errorDict];
}
// If Shutdown has been selected
- if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionShutDown)
+ if ([ud integerForKey:@"HBAlertWhenDone"] == HBDoneActionShutDown)
{
// Shut Down
NSDictionary *errorDict;
@@ -1095,114 +586,11 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
[alert runModal];
}
-#pragma mark - Queue Item Controls
-
-/**
- * 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
-{
- if ([self.items beginTransaction] == HBDistributedArrayContentReload)
- {
- // Do not execture the action if the array changed.
- [self.items commit];
- return;
- }
-
- NSMutableIndexSet *targetedRows = [[self.tableView targetedRowIndexes] mutableCopy];
-
- if (targetedRows.count)
- {
- // 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.items indexesOfObjectsUsingBlock:^BOOL(HBQueueItem *item) {
- return item.state == HBQueueItemStateWorking;
- }];
-
- if ([targetedRows containsIndexes:workingIndexes])
- {
- [targetedRows removeIndexes:workingIndexes];
- NSArray<HBQueueItem *> *workingItems = [self.items filteredArrayUsingBlock:^BOOL(HBQueueItem *item) {
- return item.state == HBQueueItemStateWorking;
- }];
-
- if ([workingItems containsObject:self.currentItem])
- {
- NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It?", @"Queue Stop Alert -> stop and remove message")];
-
- // Which window to attach the sheet to?
- NSWindow *targetWindow = self.window;
- 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.", @"Queue Stop Alert -> stop and remove informative text")];
- [alert addButtonWithTitle:NSLocalizedString(@"Keep Encoding", @"Queue Stop Alert -> stop and remove first button")];
- [alert addButtonWithTitle:NSLocalizedString(@"Stop Encoding and Delete", @"Queue Stop Alert -> stop and remove second button")];
- [alert setAlertStyle:NSAlertStyleCritical];
-
- [alert beginSheetModalForWindow:targetWindow completionHandler:^(NSModalResponse returnCode) {
- if (returnCode == NSAlertSecondButtonReturn)
- {
- [self.items beginTransaction];
-
- NSInteger index = [self.items indexOfObject:self.currentItem];
- [self cancelCurrentItemAndContinue];
-
- [self removeQueueItemAtIndex:index];
- [self.items commit];
- }
- }];
- }
- }
-
- // remove the non working items immediately
- [self removeQueueItemsAtIndexes:targetedRows];
- }
- [self.items commit];
-}
-
-/**
- * Show the finished encode in the finder
- */
-- (IBAction)revealSelectedQueueItems:(id)sender
-{
- NSIndexSet *targetedRows = [self.tableView targetedRowIndexes];
- NSMutableArray<NSURL *> *urls = [[NSMutableArray alloc] init];
-
- NSUInteger currentIndex = [targetedRows firstIndex];
- while (currentIndex != NSNotFound) {
- NSURL *url = [[self.items objectAtIndex:currentIndex] completeOutputURL];
- [urls addObject:url];
- currentIndex = [targetedRows indexGreaterThanIndex:currentIndex];
- }
-
- [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:urls];
-}
-
-- (IBAction)revealSelectedQueueItemsSources:(id)sender
-{
- NSIndexSet *targetedRows = [self.tableView targetedRowIndexes];
- NSMutableArray<NSURL *> *urls = [[NSMutableArray alloc] init];
-
- NSUInteger currentIndex = [targetedRows firstIndex];
- while (currentIndex != NSNotFound) {
- NSURL *url = [[self.items objectAtIndex:currentIndex] fileURL];
- [urls addObject:url];
- currentIndex = [targetedRows indexGreaterThanIndex:currentIndex];
- }
-
- [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:urls];
-}
-
- (void)remindUserOfSleepOrShutdown
{
- if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionSleep)
+ NSUserDefaults *ud = NSUserDefaults.standardUserDefaults;
+
+ if ([ud integerForKey:@"HBAlertWhenDone"] == HBDoneActionSleep)
{
// Warn that computer will sleep after encoding
NSBeep();
@@ -1222,7 +610,7 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
[self promptForAppleEventAuthorization];
}
- else if ([[NSUserDefaults standardUserDefaults] integerForKey:@"HBAlertWhenDone"] == HBDoneActionShutDown)
+ else if ([ud integerForKey:@"HBAlertWhenDone"] == HBDoneActionShutDown)
{
// Warn that computer will shut down after encoding
NSBeep();
@@ -1253,32 +641,31 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
}
}
+#pragma mark - UI Actions
+
/**
* Rip: puts up an alert before ultimately calling doRip
*/
-- (IBAction)rip:(id)sender
+- (IBAction)toggleStartCancel:(id)sender
{
// Rip or Cancel ?
- if (self.core.state == HBStateWorking || self.core.state == HBStatePaused || self.core.state == HBStateSearching)
+ if (self.queue.isEncoding)
{
[self cancelRip:sender];
}
// If there are pending items in the queue, then this is a rip the queue
- else if (self.pendingItemsCount > 0)
+ else if (self.queue.canEncode)
{
// 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.core preventSleep];
- [self encodeNextQueueItem];
+ [self.queue start];
}
}
/**
+* Starts or cancels the processing of items depending on the current state
* Displays an alert asking user if the want to cancel encoding of current item.
- * Cancel: returns immediately after posting the alert. Later, when the user
- * acknowledges the alert, doCancelCurrentItem is called.
*/
- (IBAction)cancelRip:(id)sender
{
@@ -1301,358 +688,82 @@ static void *HBControllerQueueCoreContext = &HBControllerQueueCoreContext;
[alert beginSheetModalForWindow:window completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSAlertSecondButtonReturn)
{
- [self cancelCurrentItemAndContinue];
+ [self.queue cancelCurrentItemAndContinue];
}
else if (returnCode == NSAlertThirdButtonReturn)
{
- [self finishCurrentAndStop];
+ [self.queue finishCurrentAndStop];
}
else if (returnCode == NSAlertThirdButtonReturn + 1)
{
- [self cancelCurrentItemAndStop];
+ [self.queue cancelCurrentItemAndStop];
}
}];
}
/**
- * Starts or cancels the processing of items depending on the current state
- */
-- (IBAction)toggleStartCancel:(id)sender
-{
- HBState s = self.core.state;
- if ((s == HBStatePaused) || (s == HBStateWorking) || (s == HBStateMuxing))
- {
- [self cancelRip: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)
+ if (self.queue.canResume)
{
- [self.core resume];
- [self.core preventSleep];
+ [self.queue resume];
}
- else if (s == HBStateWorking || s == HBStateMuxing)
+ else if (self.queue.canPause)
{
- [self.core pause];
- [self.core allowSleep];
+ [self.queue pause];
}
}
-/**
- * Resets the item state to ready.
- */
-- (IBAction)resetJobState:(id)sender
+- (IBAction)toggleDetails:(id)sender
{
- if ([self.items beginTransaction] == HBDistributedArrayContentReload)
- {
- // Do not execture the action if the array changed.
- [self.items commit];
- return;
- }
-
- NSIndexSet *targetedRows = [self.tableView targetedRowIndexes];
- NSMutableIndexSet *updatedIndexes = [NSMutableIndexSet indexSet];
-
- NSUInteger currentIndex = [targetedRows firstIndex];
- while (currentIndex != NSNotFound) {
- HBQueueItem *item = self.items[currentIndex];
-
- if (item.state == HBQueueItemStateCanceled || item.state == HBQueueItemStateCompleted || item.state == HBQueueItemStateFailed)
- {
- item.state = HBQueueItemStateReady;
- [updatedIndexes addIndex:currentIndex];
- }
- currentIndex = [targetedRows indexGreaterThanIndex:currentIndex];
- }
-
- [self reloadQueueItemsAtIndexes:updatedIndexes];
- [self.items commit];
+ NSSplitViewItem *detailsItem = self.splitViewController.splitViewItems[1];
+ detailsItem.animator.collapsed = !detailsItem.isCollapsed;
}
-- (void)editQueueItem:(HBQueueItem *)item
-{
- NSParameterAssert(item);
- [self.items beginTransaction];
+#pragma mark - table view controller delegate
- if (item != self.currentItem)
- {
- item.state = HBQueueItemStateWorking;
-
- NSUInteger row = [self.items indexOfObject:item];
- [self reloadQueueItemAtIndex:row];
-
- [self.controller openJob:[item.job copy] completionHandler:^(BOOL result) {
- [self.items beginTransaction];
- if (result)
- {
- // Now that source is loaded and settings applied, delete the queue item from the queue
- NSInteger index = [self.items indexOfObject:item];
- item.state = HBQueueItemStateReady;
- [self removeQueueItemAtIndex:index];
- }
- else
- {
- item.state = HBQueueItemStateFailed;
- NSBeep();
- }
- [self.items commit];
- }];
- }
- else
- {
- NSBeep();
- }
-
- [self.items commit];
-}
-
-/**
- * Send the selected queue item back to the main window for rescan and possible edit.
- */
-- (IBAction)editSelectedQueueItem:(id)sender
+- (void)tableViewDidSelectItem:(HBQueueItem *)item
{
- if ([self.items beginTransaction] == HBDistributedArrayContentReload)
- {
- // Do not execture the action if the array changed.
- [self.items commit];
- return;
- }
-
- NSInteger row = self.tableView.clickedRow;
- if (row != NSNotFound)
- {
- // if this is a currently encoding item, 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
- HBQueueItem *item = self.items[row];
- if (item == self.currentItem)
- {
- NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Edit It?", @"Queue Edit Alert -> stop and edit message")];
-
- // Which window to attach the sheet to?
- NSWindow *docWindow = self.window;
- 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.", @"Queue Edit Alert -> stop and edit informative text")];
- [alert addButtonWithTitle:NSLocalizedString(@"Keep Encoding", @"Queue Edit Alert -> stop and edit first button")];
- [alert addButtonWithTitle:NSLocalizedString(@"Stop Encoding and Edit", @"Queue Edit Alert -> stop and edit second button")];
- [alert setAlertStyle:NSAlertStyleCritical];
-
- [alert beginSheetModalForWindow:docWindow completionHandler:^(NSModalResponse returnCode) {
- if (returnCode == NSAlertSecondButtonReturn)
- {
- [self editQueueItem:item];
- }
- }];
- }
- else if (item.state != HBQueueItemStateWorking)
- {
- [self editQueueItem:item];
- }
- }
-
- [self.items commit];
+ self.detailsViewController.item = item;
}
-- (IBAction)clearAll:(id)sender
+- (void)tableViewEditItem:(HBQueueItem *)item
{
- [self.items beginTransaction];
- NSIndexSet *indexes = [self.items indexesOfObjectsUsingBlock:^BOOL(HBQueueItem *item) {
- return (item.state != HBQueueItemStateWorking);
- }];
- [self removeQueueItemsAtIndexes:indexes];
- [self.items commit];
+ [self editQueueItem:item];
}
-- (IBAction)clearCompleted:(id)sender
-{
- [self.items beginTransaction];
- NSIndexSet *indexes = [self.items indexesOfObjectsUsingBlock:^BOOL(HBQueueItem *item) {
- return (item.state == HBQueueItemStateCompleted);
- }];
+- (void)tableViewRemoveItemsAtIndexes:(nonnull NSIndexSet *)indexes {
[self removeQueueItemsAtIndexes:indexes];
- [self.items commit];
-}
-
-#pragma mark - NSTableView data source
-
-- (NSView *)tableView:(NSTableView *)tableView
- viewForTableColumn:(NSTableColumn *)tableColumn
- row:(NSInteger)row {
-
- HBQueueItemView *view = [tableView makeViewWithIdentifier:@"MainCell" owner:self];
- HBQueueItem *item = self.items[row];
-
- view.delegate = self;
- view.item = item;
-
- return view;
-}
-
-- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
-{
- return self.items.count;
-}
-
-- (NSTableCellView *)dummyCell
-{
- if (!_dummyCell) {
- _dummyCell = [self.tableView makeViewWithIdentifier:@"MainCellForSizing" owner: self];
- _dummyCellWidth = [NSLayoutConstraint constraintWithItem:_dummyCell
- attribute:NSLayoutAttributeWidth
- relatedBy:NSLayoutRelationEqual
- toItem:nil
- attribute:NSLayoutAttributeNotAnAttribute
- multiplier:1.0f
- constant:500];
- [_dummyCell addConstraint:_dummyCellWidth];
- }
- return _dummyCell;
-}
-
-- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
-{
- HBQueueItem *item = self.items[row];
-
- if (item.expanded)
- {
- CGFloat width = tableView.frame.size.width;
- self.dummyCellWidth.constant = width;
- self.dummyCell.textField.preferredMaxLayoutWidth = width - 60;
- self.dummyCell.textField.attributedStringValue = item.attributedDescription;
-
- CGFloat height = self.dummyCell.fittingSize.height;
- return height;
- }
- else
- {
- return 20;
- }
-}
-
-- (void)toggleRowsAtIndexes:(NSIndexSet *)rowIndexes expand:(BOOL)expand
-{
- NSMutableIndexSet *rowsToExpand = [NSMutableIndexSet indexSet];
- [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) {
- HBQueueItem *item = self.items[index];
- BOOL expanded = item.expanded;
- if (expanded != expand)
- {
- item.expanded = !expanded;
- [rowsToExpand addIndex:index];
- }
-
- HBQueueItemView *itemView = (HBQueueItemView *)[self.tableView viewAtColumn:0 row:index makeIfNecessary:NO];
- if (expand)
- {
- [itemView expand];
- }
- else
- {
- [itemView collapse];
- }
- }];
- [self.tableView noteHeightOfRowsWithIndexesChanged:rowsToExpand];
-}
-
-#pragma mark NSQueueItemView delegate
-
-- (void)removeQueueItem:(nonnull HBQueueItem *)item
-{
- NSUInteger index = [self.items indexOfObject:item];
- [self removeQueueItemAtIndex:index];
}
-- (void)revealQueueItem:(nonnull HBQueueItem *)item
-{
- [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[item.completeOutputURL]];
+- (void)detailsViewEditItem:(nonnull HBQueueItem *)item {
+ [self editQueueItem:item];
}
-- (void)toggleQueueItemHeight:(nonnull HBQueueItem *)item
-{
- NSInteger row = [self.items indexOfObject:item];
- [self toggleRowsAtIndexes:[NSIndexSet indexSetWithIndex:row] expand:!item.expanded];
+- (void)detailsViewResetItem:(nonnull HBQueueItem *)item {
+ [self editQueueItem:item];
}
-#pragma mark NSTableView delegate
-
-- (void)HB_deleteSelectionFromTableView:(NSTableView *)tableView
+- (IBAction)resetAll:(id)sender
{
- [self removeSelectedQueueItem:tableView];
+ [self.queue resetAllItems];
}
-- (void)HB_expandSelectionFromTableView:(NSTableView *)tableView
+- (IBAction)resetFailed:(id)sender
{
- NSIndexSet *rowIndexes = [self.tableView selectedRowIndexes];
- [self toggleRowsAtIndexes:rowIndexes expand:YES];
-}
-
-- (void)HB_collapseSelectionFromTableView:(NSTableView *)tableView;
-{
- NSIndexSet *rowIndexes = [self.tableView selectedRowIndexes];
- [self toggleRowsAtIndexes:rowIndexes expand:NO];
-}
-
-- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard;
-{
- NSArray<HBQueueItem *> *items = [self.items objectsAtIndexes:rowIndexes];
- // Dragging is only allowed of the pending items.
- if (items[0].state != HBQueueItemStateReady)
- {
- return NO;
- }
-
- 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;
+ [self.queue resetFailedItems];
}
-- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id<NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation
+- (IBAction)removeAll:(id)sender
{
- // Don't allow dropping ONTO an item since they can't really contain any children.
- BOOL isOnDropTypeProposal = dropOperation == NSTableViewDropOn;
- if (isOnDropTypeProposal)
- {
- return NSDragOperationNone;
- }
-
- // We do not let the user drop a pending item before or *above*
- // already finished or currently encoding items.
- NSInteger encodingRow = [self.items indexOfObject:self.currentItem];
- if (encodingRow != NSNotFound && row <= encodingRow)
- {
- return NSDragOperationNone;
- row = MAX(row, encodingRow);
- }
-
- return NSDragOperationMove;
+ [self.queue removeNotWorkingItems];
}
-- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id<NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation
+- (IBAction)removeCompleted:(id)sender
{
- [self moveQueueItems:self.dragNodesArray toIndex:row];
- return YES;
+ [self.queue removeCompletedItems];
}
@end
@@ -1705,24 +816,26 @@ static NSTouchBarItemIdentifier HBTouchBarPause = @"fr.handbrake.pause";
return nil;
}
-- (void)_touchBar_updateButtonsStateForQueueCore:(HBState)state;
+- (void)_touchBar_updateButtonsState;
{
NSButton *ripButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarRip] view];
NSButton *pauseButton = (NSButton *)[[self.touchBar itemForIdentifier:HBTouchBarPause] view];
- if (state == HBStateScanning || state == HBStateWorking || state == HBStateSearching || state == HBStateMuxing)
+ if (self.queue.isEncoding)
{
ripButton.image = [NSImage imageNamed:NSImageNameTouchBarRecordStopTemplate];
- pauseButton.image = [NSImage imageNamed:NSImageNameTouchBarPauseTemplate];
}
- else if (state == HBStatePaused)
+ else
+ {
+ ripButton.image = [NSImage imageNamed:NSImageNameTouchBarPlayTemplate];
+ }
+
+ if (self.queue.canResume)
{
- ripButton.image = [NSImage imageNamed:NSImageNameTouchBarRecordStopTemplate];
pauseButton.image = [NSImage imageNamed:NSImageNameTouchBarPlayTemplate];
}
- else if (state == HBStateIdle)
+ else
{
- ripButton.image = [NSImage imageNamed:NSImageNameTouchBarPlayTemplate];
pauseButton.image = [NSImage imageNamed:NSImageNameTouchBarPauseTemplate];
}
}
diff --git a/macosx/HBQueueDetailsViewController.h b/macosx/HBQueueDetailsViewController.h
new file mode 100644
index 000000000..8fca10a1a
--- /dev/null
+++ b/macosx/HBQueueDetailsViewController.h
@@ -0,0 +1,27 @@
+/* HBQueueDetailsViewController.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>
+#import "HBQueueItem.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol HBQueueDetailsViewControllerDelegate
+
+- (void)detailsViewEditItem:(HBQueueItem *)item;
+- (void)detailsViewResetItem:(HBQueueItem *)item;
+
+@end
+
+@interface HBQueueDetailsViewController : NSViewController
+
+- (instancetype)initWithDelegate:(id<HBQueueDetailsViewControllerDelegate>)delegate;
+
+@property (nonatomic, nullable) HBQueueItem *item;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/macosx/HBQueueDetailsViewController.m b/macosx/HBQueueDetailsViewController.m
new file mode 100644
index 000000000..a779346e8
--- /dev/null
+++ b/macosx/HBQueueDetailsViewController.m
@@ -0,0 +1,65 @@
+/* HBQueueDetailsViewController.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 "HBQueueDetailsViewController.h"
+
+@interface HBQueueDetailsViewController ()
+
+@property (weak) IBOutlet NSTextField *detailsLabel;
+@property (weak) IBOutlet NSScrollView *scrollView;
+
+@property (weak) id<HBQueueDetailsViewControllerDelegate> delegate;
+
+@end
+
+@implementation HBQueueDetailsViewController
+
+- (NSString *)nibName
+{
+ return @"HBQueueDetailsViewController";
+}
+
+- (instancetype)initWithDelegate:(id<HBQueueDetailsViewControllerDelegate>)delegate
+{
+ self = [super init];
+ if (self)
+ {
+ _delegate = delegate;
+ }
+ return self;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ self.item = nil;
+}
+
+- (void)setItem:(HBQueueItem *)item
+{
+ _item = item;
+ if (item)
+ {
+ self.detailsLabel.attributedStringValue = item.attributedDescription;
+ [self.scrollView flashScrollers];
+ }
+ else
+ {
+ self.detailsLabel.stringValue = NSLocalizedString(@"No job selected", @"");
+ }
+}
+
+- (IBAction)editItem:(id)sender
+{
+ [self.delegate detailsViewEditItem:self.item];
+}
+
+- (IBAction)resetItem:(id)sender
+{
+ [self.delegate detailsViewResetItem:self.item];
+}
+
+
+@end
diff --git a/macosx/HBQueueItem.m b/macosx/HBQueueItem.m
index e0806d7e7..959167731 100644
--- a/macosx/HBQueueItem.m
+++ b/macosx/HBQueueItem.m
@@ -11,8 +11,8 @@
@implementation HBQueueItem
@synthesize job = _job;
-@synthesize attributedDescription = _attributedDescription;
@synthesize attributedTitleDescription = _attributedTitleDescription;
+@synthesize attributedDescription = _attributedDescription;
@synthesize uuid = _uuid;
@@ -49,14 +49,6 @@
return _job.completeOutputURL;
}
-- (NSAttributedString *)attributedDescription
-{
- if (_attributedDescription == nil) {
- _attributedDescription = _job.attributedDescription;
- }
- return _attributedDescription;
-}
-
- (NSAttributedString *)attributedTitleDescription
{
if (_attributedTitleDescription == nil) {
@@ -65,6 +57,14 @@
return _attributedTitleDescription;
}
+- (NSAttributedString *)attributedDescription
+{
+ if (_attributedDescription == nil) {
+ _attributedDescription = _job.attributedDescription;
+ }
+ return _attributedDescription;
+}
+
#pragma mark - NSSecureCoding
+ (BOOL)supportsSecureCoding
diff --git a/macosx/HBQueueItemView.h b/macosx/HBQueueItemView.h
index 4dedf3766..e79ee51c8 100644
--- a/macosx/HBQueueItemView.h
+++ b/macosx/HBQueueItemView.h
@@ -23,9 +23,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, weak, nullable) HBQueueItem *item;
@property (nonatomic, weak, nullable) id <HBQueueItemViewDelegate> delegate;
-- (void)expand;
-- (void)collapse;
-
@end
NS_ASSUME_NONNULL_END
diff --git a/macosx/HBQueueItemView.m b/macosx/HBQueueItemView.m
index d736fe59f..39fbdf4ce 100644
--- a/macosx/HBQueueItemView.m
+++ b/macosx/HBQueueItemView.m
@@ -29,22 +29,11 @@
[self HB_updateRightButton];
self.removeButton.target = self;
- self.expandButton.target = self;
- self.expandButton.action = @selector(toggleHeight:);
}
- (void)HB_updateLabel
{
- if (_item.expanded)
- {
- self.textField.attributedStringValue = _item.attributedDescription;
- self.expandButton.state = NSOnState;
- }
- else
- {
- self.textField.attributedStringValue = _item.attributedTitleDescription;
- self.expandButton.state = NSOffState;
- }
+ self.textField.stringValue = _item.outputFileName;
}
- (void)HB_updateState
@@ -114,18 +103,6 @@
[self HB_updateRightButton];
}
-- (void)expand
-{
- self.expandButton.state = NSOnState;
- self.textField.attributedStringValue = _item.attributedDescription;
-}
-
-- (void)collapse
-{
- self.expandButton.state = NSOffState;
- self.textField.attributedStringValue = _item.attributedTitleDescription;
-}
-
- (BOOL)isFlipped
{
return YES;
diff --git a/macosx/HBQueueTableViewController.h b/macosx/HBQueueTableViewController.h
new file mode 100644
index 000000000..95ab53ac7
--- /dev/null
+++ b/macosx/HBQueueTableViewController.h
@@ -0,0 +1,29 @@
+/* HBQueueTableViewController.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>
+
+#import "HBQueue.h"
+#import "HBDistributedArray.h"
+#import "HBQueueItem.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol HBQueueTableViewControllerDelegate
+
+- (void)tableViewDidSelectItem:(nullable HBQueueItem *)item;
+- (void)tableViewEditItem:(HBQueueItem *)item;
+- (void)tableViewRemoveItemsAtIndexes:(NSIndexSet *)indexes;
+
+@end
+
+@interface HBQueueTableViewController : NSViewController
+
+- (instancetype)initWithQueue:(HBQueue *)queue delegate:(id<HBQueueTableViewControllerDelegate>)delegate;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/macosx/HBQueueTableViewController.m b/macosx/HBQueueTableViewController.m
new file mode 100644
index 000000000..d0b4753cb
--- /dev/null
+++ b/macosx/HBQueueTableViewController.m
@@ -0,0 +1,339 @@
+/* HBQueueTableViewController.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 "HBQueueTableViewController.h"
+
+#import "HBTableView.h"
+#import "HBQueueItemView.h"
+
+// Pasteboard type for or drag operations
+#define HBQueueDragDropPboardType @"HBQueueCustomTableViewPboardType"
+
+@interface HBQueueTableViewController () <NSTableViewDataSource, NSTableViewDelegate, HBQueueItemViewDelegate>
+
+@property (nonatomic, readonly) HBQueue *queue;
+@property (nonatomic) NSArray<HBQueueItem *> *dragNodesArray;
+
+@property (strong) id<HBQueueTableViewControllerDelegate> delegate;
+
+@property (weak) IBOutlet HBTableView *tableView;
+
+@end
+
+@implementation HBQueueTableViewController
+
+- (instancetype)initWithQueue:(HBQueue *)state delegate:(id<HBQueueTableViewControllerDelegate>)delegate
+{
+ self = [super init];
+ if (self)
+ {
+ _queue = state;
+ _delegate = delegate;
+ }
+ return self;
+}
+
+- (NSString *)nibName
+{
+ return @"HBQueueTableViewController";
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ // lets setup our queue list table view for drag and drop here
+ [self.tableView registerForDraggedTypes:@[HBQueueDragDropPboardType]];
+ [self.tableView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
+ [self.tableView setVerticalMotionCanBeginDrag:YES];
+
+ // Reloads the queue, this is called
+ // when another HandBrake instances modifies the queue
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueReloadItemsNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ [self.tableView reloadData];
+ }];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueDidAddItemNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ NSIndexSet *indexes = note.userInfo[HBQueueItemNotificationIndexesKey];
+ [self.tableView insertRowsAtIndexes:indexes withAnimation:NSTableViewAnimationSlideDown];
+ }];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueDidRemoveItemNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ NSIndexSet *indexes = note.userInfo[HBQueueItemNotificationIndexesKey];
+ [self.tableView removeRowsAtIndexes:indexes withAnimation:NSTableViewAnimationSlideUp];
+ [self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:indexes.firstIndex] byExtendingSelection:NO];
+ }];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueDidMoveItemNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ NSArray<NSNumber *> *source = note.userInfo[HBQueueItemNotificationSourceIndexesKey];
+ NSArray<NSNumber *> *target = note.userInfo[HBQueueItemNotificationTargetIndexesKey];
+
+ [self.tableView beginUpdates];
+ for (NSInteger idx = 0; idx < source.count; idx++)
+ {
+ [self.tableView moveRowAtIndex:source[idx].integerValue toIndex:target[idx].integerValue];
+ }
+ [self.tableView endUpdates];
+ }];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueDidChangeItemNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ NSIndexSet *indexes = note.userInfo[HBQueueItemNotificationIndexesKey];
+ NSIndexSet *columnIndexes = [NSIndexSet indexSetWithIndex:0];
+ [self.tableView reloadDataForRowIndexes:indexes columnIndexes:columnIndexes];
+ }];
+
+ [NSNotificationCenter.defaultCenter addObserverForName:HBQueueDidCompleteItemNotification object:_queue queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
+ NSIndexSet *indexes = note.userInfo[HBQueueItemNotificationIndexesKey];
+ NSIndexSet *columnIndexes = [NSIndexSet indexSetWithIndex:0];
+ if (indexes.count)
+ {
+ [self.tableView reloadDataForRowIndexes:indexes columnIndexes:columnIndexes];
+ }
+ }];
+}
+
+#pragma mark - UI Actions
+
+/**
+ * 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
+{
+ NSMutableIndexSet *targetedRows = [[self.tableView targetedRowIndexes] mutableCopy];
+ [self.delegate tableViewRemoveItemsAtIndexes:targetedRows];
+}
+
+/**
+ * Show the finished encode in the finder
+ */
+- (IBAction)revealSelectedQueueItems:(id)sender
+{
+ NSIndexSet *targetedRows = [self.tableView targetedRowIndexes];
+ NSMutableArray<NSURL *> *urls = [[NSMutableArray alloc] init];
+
+ NSUInteger currentIndex = [targetedRows firstIndex];
+ while (currentIndex != NSNotFound) {
+ NSURL *url = [[self.queue.items objectAtIndex:currentIndex] completeOutputURL];
+ [urls addObject:url];
+ currentIndex = [targetedRows indexGreaterThanIndex:currentIndex];
+ }
+
+ [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:urls];
+}
+
+- (IBAction)revealSelectedQueueItemsSources:(id)sender
+{
+ NSIndexSet *targetedRows = [self.tableView targetedRowIndexes];
+ NSMutableArray<NSURL *> *urls = [[NSMutableArray alloc] init];
+
+ NSUInteger currentIndex = [targetedRows firstIndex];
+ while (currentIndex != NSNotFound) {
+ NSURL *url = [[self.queue.items objectAtIndex:currentIndex] fileURL];
+ [urls addObject:url];
+ currentIndex = [targetedRows indexGreaterThanIndex:currentIndex];
+ }
+
+ [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:urls];
+}
+
+/**
+ * Resets the item state to ready.
+ */
+- (IBAction)resetJobState:(id)sender
+{
+ NSIndexSet *targetedRows = [self.tableView targetedRowIndexes];
+ if (targetedRows.count)
+ {
+ [self.queue resetItemsStateAtIndexes:targetedRows];
+ }
+}
+
+/**
+ * Send the selected queue item back to the main window for rescan and possible edit.
+ */
+- (IBAction)editSelectedQueueItem:(id)sender
+{
+ NSInteger row = self.tableView.clickedRow;
+ HBQueueItem *item = [self.queue.items objectAtIndex:row];
+ if (item)
+ {
+ [self.delegate tableViewEditItem:item];
+ }
+}
+
+- (IBAction)removeAll:(id)sender
+{
+ [self.queue removeNotWorkingItems];
+}
+
+- (IBAction)removeCompleted:(id)sender
+{
+ [self.queue removeCompletedItems];
+}
+
+#pragma mark - UI Validation
+
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+ SEL action = menuItem.action;
+
+ if (action == @selector(editSelectedQueueItem:) ||
+ action == @selector(removeSelectedQueueItem:) ||
+ action == @selector(revealSelectedQueueItems:) ||
+ action == @selector(revealSelectedQueueItemsSources:))
+ {
+ return (self.tableView.selectedRow != -1 || self.tableView.clickedRow != -1);
+ }
+
+ if (action == @selector(resetJobState:))
+ {
+ return self.tableView.targetedRowIndexes.count > 0;
+ }
+
+ if (action == @selector(removeAll:))
+ {
+ return self.queue.items.count > 0;
+ }
+
+ if (action == @selector(removeCompleted:))
+ {
+ return self.queue.completedItemsCount > 0;
+ }
+
+ return YES;
+}
+
+#pragma mark - NSTableView data source
+
+- (NSView *)tableView:(NSTableView *)tableView
+ viewForTableColumn:(NSTableColumn *)tableColumn
+ row:(NSInteger)row {
+
+ HBQueueItemView *view = [tableView makeViewWithIdentifier:@"MainSimpleCell" owner:self];
+ HBQueueItem *item = self.queue.items[row];
+
+ view.delegate = self;
+ view.item = item;
+
+ return view;
+}
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
+{
+ return self.queue.items.count;
+}
+
+- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
+{
+ return 22;
+}
+
+- (void)toggleRowsAtIndexes:(NSIndexSet *)rowIndexes expand:(BOOL)expand
+{
+ NSMutableIndexSet *rowsToExpand = [NSMutableIndexSet indexSet];
+ [rowIndexes enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) {
+ HBQueueItem *item = self.queue.items[index];
+ BOOL expanded = item.expanded;
+ if (expanded != expand)
+ {
+ item.expanded = !expanded;
+ [rowsToExpand addIndex:index];
+ }
+
+ //HBQueueItemView *itemView = (HBQueueItemView *)[self.tableView viewAtColumn:0 row:index makeIfNecessary:NO];
+ //if (expand)
+ //{
+ //[itemView expand];
+ //}
+ //else
+ //{
+ //[itemView collapse];
+ //}
+ }];
+ [self.tableView noteHeightOfRowsWithIndexesChanged:rowsToExpand];
+}
+
+#pragma mark NSQueueItemView delegate
+
+- (void)removeQueueItem:(nonnull HBQueueItem *)item
+{
+ NSUInteger index = [self.queue.items indexOfObject:item];
+ [self.queue removeQueueItemAtIndex:index];
+}
+
+- (void)revealQueueItem:(nonnull HBQueueItem *)item
+{
+ [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[item.completeOutputURL]];
+}
+
+- (void)toggleQueueItemHeight:(nonnull HBQueueItem *)item
+{
+ NSInteger row = [self.queue.items indexOfObject:item];
+ [self toggleRowsAtIndexes:[NSIndexSet indexSetWithIndex:row] expand:!item.expanded];
+}
+
+#pragma mark NSTableView delegate
+
+- (void)tableViewSelectionDidChange:(NSNotification *)notification
+{
+ NSInteger selectedRow = self.tableView.selectedRow;
+ HBQueueItem *selectedItem = selectedRow > -1 ? self.queue.items[selectedRow] : nil;
+ [self.delegate tableViewDidSelectItem:selectedItem];
+}
+
+- (void)HB_deleteSelectionFromTableView:(NSTableView *)tableView
+{
+ [self removeSelectedQueueItem:tableView];
+}
+
+- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard;
+{
+ NSArray<HBQueueItem *> *items = [self.queue.items objectsAtIndexes:rowIndexes];
+ // Dragging is only allowed of the pending items.
+ if (items[0].state != HBQueueItemStateReady)
+ {
+ return NO;
+ }
+
+ self.dragNodesArray = items;
+
+ // Provide data for our custom type, and simple NSStrings.
+ [pboard declareTypes:@[HBQueueDragDropPboardType] owner:self];
+
+ // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
+ [pboard setData:[NSData data] forType:HBQueueDragDropPboardType];
+
+ return YES;
+}
+
+- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id<NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation
+{
+ // Don't allow dropping ONTO an item since they can't really contain any children.
+ BOOL isOnDropTypeProposal = dropOperation == NSTableViewDropOn;
+ if (isOnDropTypeProposal)
+ {
+ return NSDragOperationNone;
+ }
+
+ // We do not let the user drop a pending item before or *above*
+ // already finished or currently encoding items.
+ NSInteger encodingRow = [self.queue.items indexOfObject:self.queue.currentItem];
+ if (encodingRow != NSNotFound && row <= encodingRow)
+ {
+ return NSDragOperationNone;
+ row = MAX(row, encodingRow);
+ }
+
+ return NSDragOperationMove;
+}
+
+- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id<NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation
+{
+ [self.queue moveQueueItems:self.dragNodesArray toIndex:row];
+ return YES;
+}
+
+@end
diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj
index bd8668d12..f63eec1bf 100644
--- a/macosx/HandBrake.xcodeproj/project.pbxproj
+++ b/macosx/HandBrake.xcodeproj/project.pbxproj
@@ -201,10 +201,14 @@
A95121E61B5F7BE700FD773D /* NSArray+HBAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A95121E51B5F7BE700FD773D /* NSArray+HBAdditions.m */; };
A955128B1A320B02001BFC6F /* libjansson.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A95512881A320A12001BFC6F /* libjansson.a */; };
A957EBCD218DBE5900007988 /* HBAutoNamer.m in Sources */ = {isa = PBXBuildFile; fileRef = A957EBCC218DBE5900007988 /* HBAutoNamer.m */; };
+ A958EAC222E24D6400D83AF4 /* HBQueueTableViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A958EAC422E24D6400D83AF4 /* HBQueueTableViewController.xib */; };
+ A958EAC522E24D6800D83AF4 /* HBQueueDetailsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A958EAC722E24D6800D83AF4 /* HBQueueDetailsViewController.xib */; };
A95BA15D220C968500A2F9F9 /* HBQueueItem.m in Sources */ = {isa = PBXBuildFile; fileRef = A95BA15C220C968500A2F9F9 /* HBQueueItem.m */; };
A95BA161220CA5DB00A2F9F9 /* HBDistributedArray.m in Sources */ = {isa = PBXBuildFile; fileRef = A95BA160220CA5DB00A2F9F9 /* HBDistributedArray.m */; };
A95BC1E71CD2548A008D6A33 /* volHighTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = A95BC1E51CD2548A008D6A33 /* volHighTemplate.pdf */; };
A95BC1E81CD2548A008D6A33 /* volLowTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = A95BC1E61CD2548A008D6A33 /* volLowTemplate.pdf */; };
+ A96127DF22E0994E0086E6DC /* HBQueueDetailsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A96127DD22E0994E0086E6DC /* HBQueueDetailsViewController.m */; };
+ A96127E422E09ADD0086E6DC /* HBQueueTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A96127E222E09ADD0086E6DC /* HBQueueTableViewController.m */; };
A96664B01CCE45BF00DA4A57 /* HBPlayerHUDController.m in Sources */ = {isa = PBXBuildFile; fileRef = A96664AE1CCE45BF00DA4A57 /* HBPlayerHUDController.m */; };
A96664B51CCE48F700DA4A57 /* HBPictureHUDController.m in Sources */ = {isa = PBXBuildFile; fileRef = A96664B31CCE48F700DA4A57 /* HBPictureHUDController.m */; };
A96664BA1CCE493D00DA4A57 /* HBEncodingProgressHUDController.m in Sources */ = {isa = PBXBuildFile; fileRef = A96664B81CCE493D00DA4A57 /* HBEncodingProgressHUDController.m */; };
@@ -218,6 +222,7 @@
A973E109216E74AC00D498EC /* HBImageUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = A9CE0A931F57EC4600724577 /* HBImageUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; };
A973E10C216E74E900D498EC /* HBThumbnailItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = A973E10B216E74E900D498EC /* HBThumbnailItemView.m */; };
A975B02220F7AF29004675CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A975B02020F7AF29004675CC /* Localizable.strings */; };
+ A97ECB8222E1D85500570935 /* HBQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = A97ECB8122E1D85500570935 /* HBQueue.m */; };
A98036CD1CCA91DD007661AA /* HBAVPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A98036CC1CCA91DD007661AA /* HBAVPlayer.m */; };
A98B8E241C7DD2A200B810C9 /* HBPresetCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = A997D8EB1A4ABB0900E19B6F /* HBPresetCoding.h */; settings = {ATTRIBUTES = (Public, ); }; };
A98C29C41977B10600AF5DED /* HBLanguagesSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = A98C29C31977B10600AF5DED /* HBLanguagesSelection.m */; };
@@ -542,6 +547,8 @@
A957EBCB218DBE5900007988 /* HBAutoNamer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBAutoNamer.h; sourceTree = "<group>"; };
A957EBCC218DBE5900007988 /* HBAutoNamer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HBAutoNamer.m; sourceTree = "<group>"; };
A958605B2216A5E5002092B1 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
+ A958EAC322E24D6400D83AF4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/HBQueueTableViewController.xib; sourceTree = "<group>"; };
+ A958EAC622E24D6800D83AF4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/HBQueueDetailsViewController.xib; sourceTree = "<group>"; };
A9597A281A49749D00007771 /* HBRange+UIAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HBRange+UIAdditions.h"; sourceTree = "<group>"; };
A9597A291A49749D00007771 /* HBRange+UIAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "HBRange+UIAdditions.m"; sourceTree = "<group>"; };
A95BA15B220C968500A2F9F9 /* HBQueueItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBQueueItem.h; sourceTree = "<group>"; };
@@ -551,6 +558,10 @@
A95BC1E51CD2548A008D6A33 /* volHighTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = volHighTemplate.pdf; sourceTree = "<group>"; };
A95BC1E61CD2548A008D6A33 /* volLowTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = volLowTemplate.pdf; sourceTree = "<group>"; };
A95CB2FB217B6D07001E9F51 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ A96127DC22E0994E0086E6DC /* HBQueueDetailsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBQueueDetailsViewController.h; sourceTree = "<group>"; };
+ A96127DD22E0994E0086E6DC /* HBQueueDetailsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HBQueueDetailsViewController.m; sourceTree = "<group>"; };
+ A96127E122E09ADD0086E6DC /* HBQueueTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBQueueTableViewController.h; sourceTree = "<group>"; };
+ A96127E222E09ADD0086E6DC /* HBQueueTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HBQueueTableViewController.m; sourceTree = "<group>"; };
A9637D9120F7A252001EAE7C /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/HBEncodingProgressHUDController.strings; sourceTree = "<group>"; };
A9637D9220F7A252001EAE7C /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/HBTitleSelection.strings; sourceTree = "<group>"; };
A9637D9320F7A252001EAE7C /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Presets.strings; sourceTree = "<group>"; };
@@ -600,6 +611,8 @@
A975B02120F7AF29004675CC /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
A975C08C1AE8C5270061870D /* HBStateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBStateFormatter.h; sourceTree = "<group>"; };
A975C08D1AE8C5270061870D /* HBStateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBStateFormatter.m; sourceTree = "<group>"; };
+ A97ECB8022E1D85500570935 /* HBQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBQueue.h; sourceTree = "<group>"; };
+ A97ECB8122E1D85500570935 /* HBQueue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HBQueue.m; sourceTree = "<group>"; };
A98036CB1CCA91DD007661AA /* HBAVPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBAVPlayer.h; sourceTree = "<group>"; };
A98036CC1CCA91DD007661AA /* HBAVPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBAVPlayer.m; sourceTree = "<group>"; };
A988AF9B1BC7C35F00932543 /* HBChapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBChapter.h; sourceTree = "<group>"; };
@@ -1076,13 +1089,21 @@
A901C2401BC7CFDC00D77735 /* Queue */ = {
isa = PBXGroup;
children = (
+ A97ECB8022E1D85500570935 /* HBQueue.h */,
+ A97ECB8122E1D85500570935 /* HBQueue.m */,
A95BA15B220C968500A2F9F9 /* HBQueueItem.h */,
A95BA15C220C968500A2F9F9 /* HBQueueItem.m */,
- A9D3634F2209C08500D8EFEA /* HBQueueItemView.h */,
- A9D363502209C08500D8EFEA /* HBQueueItemView.m */,
A9AA447C1970726500D7DEFC /* HBQueueController.h */,
A9906B2B1A710920001D82D5 /* HBQueueController.m */,
A9A96BD220CAD63000A39AFB /* Queue.xib */,
+ A9D3634F2209C08500D8EFEA /* HBQueueItemView.h */,
+ A9D363502209C08500D8EFEA /* HBQueueItemView.m */,
+ A96127E122E09ADD0086E6DC /* HBQueueTableViewController.h */,
+ A96127E222E09ADD0086E6DC /* HBQueueTableViewController.m */,
+ A958EAC422E24D6400D83AF4 /* HBQueueTableViewController.xib */,
+ A96127DC22E0994E0086E6DC /* HBQueueDetailsViewController.h */,
+ A96127DD22E0994E0086E6DC /* HBQueueDetailsViewController.m */,
+ A958EAC722E24D6800D83AF4 /* HBQueueDetailsViewController.xib */,
);
name = Queue;
sourceTree = "<group>";
@@ -1650,6 +1671,7 @@
D86C9DD51C6D372500F06F1B /* Assets.xcassets in Resources */,
A9A96BCE20CAD61F00A39AFB /* SubtitlesDefaults.xib in Resources */,
A9A96BDA20CAD64B00A39AFB /* PicturePreview.xib in Resources */,
+ A958EAC222E24D6400D83AF4 /* HBQueueTableViewController.xib in Resources */,
A95BC1E71CD2548A008D6A33 /* volHighTemplate.pdf in Resources */,
A9A96BB620CAD5D600A39AFB /* HBTitleSelection.xib in Resources */,
A9E52CD8218DD52B00E17B86 /* ExceptionAlert.xib in Resources */,
@@ -1670,6 +1692,7 @@
A9A96BC820CAD61000A39AFB /* AudioDefaults.xib in Resources */,
A9A96BB920CAD5EE00A39AFB /* HBPictureViewController.xib in Resources */,
A91943111FB5E39E001E9BB0 /* HBSummaryViewController.xib in Resources */,
+ A958EAC522E24D6800D83AF4 /* HBQueueDetailsViewController.xib in Resources */,
A9A96BBF20CAD5F800A39AFB /* Video.xib in Resources */,
A9E1468316BC2AD800C307BC /* PrevTemplate.pdf in Resources */,
A937EECB1C6C7C0300EEAE6D /* dsa_pub.pem in Resources */,
@@ -1745,6 +1768,7 @@
A916C9991C8449E200C7B560 /* main.mm in Sources */,
A973E10C216E74E900D498EC /* HBThumbnailItemView.m in Sources */,
A916C9981C8449DB00C7B560 /* HBTitleSelectionController.m in Sources */,
+ A96127E422E09ADD0086E6DC /* HBQueueTableViewController.m in Sources */,
A903C5601CCE78060026B0ED /* NSWindow+HBAdditions.m in Sources */,
A9A0CBE81CCEA3670045B3DF /* HBPlayerTrack.m in Sources */,
A916C9971C8449CA00C7B560 /* HBAudioDefaultsController.m in Sources */,
@@ -1785,6 +1809,8 @@
A95121E61B5F7BE700FD773D /* NSArray+HBAdditions.m in Sources */,
A96664B51CCE48F700DA4A57 /* HBPictureHUDController.m in Sources */,
A957EBCD218DBE5900007988 /* HBAutoNamer.m in Sources */,
+ A96127DF22E0994E0086E6DC /* HBQueueDetailsViewController.m in Sources */,
+ A97ECB8222E1D85500570935 /* HBQueue.m in Sources */,
A92B148220CA9F7700146FD8 /* HBHUDView.m in Sources */,
A914BCB31BC441C700157917 /* HBPreviewView.m in Sources */,
273F20B714ADBE670021BE6D /* HBPreviewController.m in Sources */,
@@ -1944,6 +1970,22 @@
name = MainWindow.xib;
sourceTree = "<group>";
};
+ A958EAC422E24D6400D83AF4 /* HBQueueTableViewController.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ A958EAC322E24D6400D83AF4 /* Base */,
+ );
+ name = HBQueueTableViewController.xib;
+ sourceTree = "<group>";
+ };
+ A958EAC722E24D6800D83AF4 /* HBQueueDetailsViewController.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ A958EAC622E24D6800D83AF4 /* Base */,
+ );
+ name = HBQueueDetailsViewController.xib;
+ sourceTree = "<group>";
+ };
A975B02020F7AF29004675CC /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (