]> granicus.if.org Git - handbrake/commitdiff
MacGui: refactor the queue and implement part of the new queue ui.
authorDamiano Galassi <damiog@gmail.com>
Fri, 19 Jul 2019 19:17:27 +0000 (21:17 +0200)
committerDamiano Galassi <damiog@gmail.com>
Fri, 19 Jul 2019 19:17:27 +0000 (21:17 +0200)
23 files changed:
macosx/Base.lproj/HBQueueDetailsViewController.xib [new file with mode: 0644]
macosx/Base.lproj/HBQueueTableViewController.xib [new file with mode: 0644]
macosx/Base.lproj/MainMenu.xib
macosx/Base.lproj/MainWindow.xib
macosx/Base.lproj/Queue.xib
macosx/HBAppDelegate.h
macosx/HBAppDelegate.m
macosx/HBController.h
macosx/HBController.m
macosx/HBJob+UIAdditions.h
macosx/HBJob+UIAdditions.m
macosx/HBQueue.h [new file with mode: 0644]
macosx/HBQueue.m [new file with mode: 0644]
macosx/HBQueueController.h
macosx/HBQueueController.m
macosx/HBQueueDetailsViewController.h [new file with mode: 0644]
macosx/HBQueueDetailsViewController.m [new file with mode: 0644]
macosx/HBQueueItem.m
macosx/HBQueueItemView.h
macosx/HBQueueItemView.m
macosx/HBQueueTableViewController.h [new file with mode: 0644]
macosx/HBQueueTableViewController.m [new file with mode: 0644]
macosx/HandBrake.xcodeproj/project.pbxproj

diff --git a/macosx/Base.lproj/HBQueueDetailsViewController.xib b/macosx/Base.lproj/HBQueueDetailsViewController.xib
new file mode 100644 (file)
index 0000000..278805b
--- /dev/null
@@ -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 (file)
index 0000000..8eb9897
--- /dev/null
@@ -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>
index 4c6e39b68089612f040c6744e570937b8451a634..26adb990c84992249d5e31def54a3217030e01bc 100644 (file)
@@ -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">
                             </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>
index 1dcd4f4d0c2aead0c2df14cc744aadf26dd4dc25..d691c3cb5deaa32cc3cbec585bba880e6348a3c5 100644 (file)
@@ -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>
                         </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">
index bb379a101ebea3fe5206882fb62e77e3063838a1..bf105a3fac4d9d47f70d177821a342785151ee16 100644 (file)
@@ -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>
             <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>
                     </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"/>
                             </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>
index 387437b2078c2397d4b89efa987e055b29ef52d1..3762c62e97061ae29bc3a129366430573f57164e 100644 (file)
@@ -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
index de227e1538e12a270a38564cab64bbde06cbedde..d8790fb7c6a01d9c3d799fd78a7c72c8a87d033a 100644 (file)
@@ -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;
         _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;
 }
     }
     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];
 
 - (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")];
 
     _mainController = nil;
     _queueController = nil;
+    _queue = nil;
 
     [HBCore closeGlobal];
 }
 {
     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];
     }
 }
 
+#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
 
 - (IBAction)reloadPreset:(id)sender
 {
-    [self.mainController reloadPreset:self];
+    [self.mainController reloadPreset:sender];
 }
 
 #pragma mark - Show Window Menu Items
         _preferencesController = [[HBPreferencesController alloc] init];
     }
 
-    [self.preferencesController showWindow:self];
+    [self.preferencesController showWindow:sender];
 }
 
 /**
 
 - (IBAction)showPreviewWindow:(id)sender
 {
-    [self.mainController showPreviewWindow:self];
+    [self.mainController showPreviewWindow:sender];
 }
 
 /**
index 8fb6cfbc7069e18b1294b382e9a5f1a6d9221d11..fa8a9ac425b30c6564b52bb4fe5f8e626753ec9a 100644 (file)
@@ -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;
 
 - (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;
 
index 7dbbf71822f1a26dc9ac2cd451ec37a1e07f01b0..40b42ff9bda9851ba5f85e17036c05984bcd3ec0 100644 (file)
@@ -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];
     }
 }
index 99dcc10c8fcfc6d2d5fcfaf08ff602be0d83a307..7099f08b0168b2ff0a73e43c4ab741bb0fb7f815 100644 (file)
@@ -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;
index 4f711bacb06222f7f5fe545cead8253fb8deea2e..28c7edfe5ad8bbb7edfc44d82f543018e355f455 100644 (file)
@@ -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 (file)
index 0000000..0e17629
--- /dev/null
@@ -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 (file)
index 0000000..6aee924
--- /dev/null
@@ -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
index 597b68a1d0fe40565e79a4da90c0f6f058ae4878..722416cd1a383cf828521c514056c2c2af2a5055 100644 (file)
@@ -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
index 87d0ceefe9cf644614d618fe5051e4a83ff1656c..40ae17334d6f9e12f235d35a25c7e050a83bccef 100644 (file)
 
 #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];
 
-    [self updateQueueStats];
+    NSSplitViewItem *tableItem = [NSSplitViewItem splitViewItemWithViewController:_tableViewController];
+    tableItem.minimumThickness = 160;
 
-    [self.core addObserver:self forKeyPath:@"state"
+    [_splitViewController addSplitViewItem:tableItem];
+
+    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)
-    {
-        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];
-
-    if (!undo.isUndoing)
+    if (indexes.count)
     {
-        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.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];
-
-    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];
-
-        [self.tableView moveRowAtIndex:sourceIndex toIndex:destIndex];
-    }
-
-    NSUndoManager *undo = self.window.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.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;
-    }
-    [self.controller setQueueInfo:_progressInfo progress:progress hidden:hidden];
-}
-
-/**
- *  Updates the queue status label.
- */
-- (void)updateQueueStats
-{
-    // 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
-
-- (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];
+        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 (volumeURL)
+        if ([mutableIndexes containsIndexes:workingIndexes])
         {
-            [volumeURL removeCachedResourceValueForKey:NSURLVolumeAvailableCapacityKey];
-            attrs = [volumeURL resourceValuesForKeys:@[NSURLVolumeAvailableCapacityKey] error:NULL];
+            [mutableIndexes removeIndexes:workingIndexes];
+            NSArray<HBQueueItem *> *workingItems = [self.queue.items filteredArrayUsingBlock:^BOOL(HBQueueItem *item) {
+                return item.state == HBQueueItemStateWorking;
+            }];
 
-            if (attrs[NSURLVolumeAvailableCapacityKey])
+            if ([workingItems containsObject:self.queue.currentItem])
             {
-                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;
-}
-
-/**
- *  Starts the queue
- */
-- (void)encodeNextQueueItem
-{
-    [self.items beginTransaction];
-    self.currentItem = nil;
+                NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It?", @"Queue Stop Alert -> stop and remove message")];
 
-    // since we have completed an encode, we go to the next
-    if (self.stop)
-    {
-        [HBUtilities writeToActivityLog:"Queue manually stopped"];
+                // Which window to attach the sheet to?
+                NSWindow *targetWindow = self.window;
 
-        self.stop = NO;
-        [self.core allowSleep];
-    }
-    else
-    {
-        // Check to see if there are any more pending items in the queue
-        HBQueueItem *nextItem = [self getNextPendingQueueItem];
+                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];
 
-        if (nextItem && [self _isDiskSpaceLowAtURL:nextItem.outputURL])
-        {
-            // Disk space is low, show an alert
-            [HBUtilities writeToActivityLog:"Queue Stopped, low space on destination disk"];
+                [alert beginSheetModalForWindow:targetWindow completionHandler:^(NSModalResponse returnCode) {
+                    if (returnCode == NSAlertSecondButtonReturn)
+                    {
+                        [self.queue.items beginTransaction];
 
-            [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;
+                        NSInteger index = [self.queue.items indexOfObject:self.queue.currentItem];
+                        [self.queue cancelCurrentItemAndContinue];
 
-            // 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.queue removeQueueItemAtIndex:index];
+                        [self.queue.items commit];
+                    }
+                }];
             }
-
-            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;
-    }
-
-    if ([self.items containsObject:item])
-    {
-        [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;
+        // remove the non working items immediately
+        [self.queue removeQueueItemsAtIndexes:mutableIndexes];
     }
-    [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
- */
-- (void)encodeItem:(HBQueueItem *)item
+- (void)doEditQueueItem:(HBQueueItem *)item
 {
     NSParameterAssert(item);
+    [self.queue.items beginTransaction];
 
-    // Progress handler
-    void (^progressHandler)(HBState state, HBProgress progress, NSString *info) = ^(HBState state, HBProgress progress, NSString *info)
+    if (item != self.queue.currentItem)
     {
-        [self updateProgress:info progress:0 hidden:NO];
-    };
-
-    // 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);
+        item.state = HBQueueItemStateWorking;
 
-    HBStateFormatter *formatter = [[HBStateFormatter alloc] init];
-    formatter.title = job.outputFileName;
-    self.core.stateFormatter = formatter;
+        //        NSUInteger row = [self.queue.items indexOfObject:item];
+        //FIXME
+        //[self reloadQueueItemAtIndex:row];
 
-    // 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)
+        [self.delegate openJob:[item.job copy] completionHandler:^(BOOL result) {
+            [self.queue.items beginTransaction];
+            if (result)
+            {
+                // 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];
+            }
+            else
             {
-                [self.dockTile updateDockIcon:progress.percent hours:progress.hours minutes:progress.minutes seconds:progress.seconds];
-                self.dockIconProgress += dockTileUpdateFrequency;
+                item.state = HBQueueItemStateFailed;
+                NSBeep();
             }
-        }
-        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.queue.items commit];
+        }];
+    }
+    else
     {
-        [self completedItem:item result:result];
-        [self encodeNextQueueItem];
-    };
-
-    // We should be all setup so let 'er rip
-    [self.core encodeJob:job progressHandler:progressHandler completionHandler:completionHandler];
+        NSBeep();
+    }
 
-    // We are done using the title, remove it from the job
-    job.title = nil;
+    [self.queue.items commit];
 }
 
 /**
- * Cancels the current job
+ * Send the selected queue item back to the main window for rescan and possible edit.
  */
-- (void)doCancelCurrentItem
+- (void)editQueueItem:(HBQueueItem *)item
 {
-    if (self.core.state == HBStateScanning)
+    if ([self.queue.items beginTransaction] == HBDistributedArrayContentReload)
     {
-        [self.core cancelScan];
+        // Do not execture the action if the array changed.
+        [self.queue.items commit];
+        return;
     }
-    else
+
+    // 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)
     {
-        [self.core cancelEncode];
-    }
-}
+        NSString *alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Edit It?", @"Queue Edit Alert -> stop and edit message")];
 
-/**
- * Cancels the current job and starts processing the next in queue.
- */
-- (void)cancelCurrentItemAndContinue
-{
-    [self doCancelCurrentItem];
-}
+        // Which window to attach the sheet to?
+        NSWindow *docWindow = self.window;
 
-/**
- * Cancels the current job and stops libhb from processing the remaining encodes.
- */
-- (void)cancelCurrentItemAndStop
-{
-    self.stop = YES;
-    [self doCancelCurrentItem];
-}
+        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 doEditQueueItem:item];
+            }
+        }];
+    }
+    else if (item.state != HBQueueItemStateWorking)
+    {
+        [self doEditQueueItem:item];
+    }
 
-/**
- * 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];
-
-    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];
-}
+#pragma mark - table view controller delegate
 
-/**
- * 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)toggleQueueItemHeight:(nonnull HBQueueItem *)item
-{
-    NSInteger row = [self.items indexOfObject:item];
-    [self toggleRowsAtIndexes:[NSIndexSet indexSetWithIndex:row] expand:!item.expanded];
-}
-
-#pragma mark NSTableView delegate
-
-- (void)HB_deleteSelectionFromTableView:(NSTableView *)tableView
-{
-    [self removeSelectedQueueItem:tableView];
+- (void)detailsViewEditItem:(nonnull HBQueueItem *)item {
+    [self editQueueItem:item];
 }
 
-- (void)HB_expandSelectionFromTableView:(NSTableView *)tableView
-{
-    NSIndexSet *rowIndexes = [self.tableView selectedRowIndexes];
-    [self toggleRowsAtIndexes:rowIndexes expand:YES];
+- (void)detailsViewResetItem:(nonnull HBQueueItem *)item {
+    [self editQueueItem:item];
 }
 
-- (void)HB_collapseSelectionFromTableView:(NSTableView *)tableView;
+- (IBAction)resetAll:(id)sender
 {
-    NSIndexSet *rowIndexes = [self.tableView selectedRowIndexes];
-    [self toggleRowsAtIndexes:rowIndexes expand:NO];
+    [self.queue resetAllItems];
 }
 
-- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard;
+- (IBAction)resetFailed:(id)sender
 {
-    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 (file)
index 0000000..8fca10a
--- /dev/null
@@ -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 (file)
index 0000000..a779346
--- /dev/null
@@ -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
index e0806d7e7dbac050ce12a820074bf1fccdc66b90..959167731fbaf54819a808450e1d7bd545cc0fef 100644 (file)
@@ -11,8 +11,8 @@
 @implementation HBQueueItem
 
 @synthesize job = _job;
-@synthesize attributedDescription = _attributedDescription;
 @synthesize attributedTitleDescription = _attributedTitleDescription;
+@synthesize attributedDescription = _attributedDescription;
 
 @synthesize uuid = _uuid;
 
     return _job.completeOutputURL;
 }
 
-- (NSAttributedString *)attributedDescription
-{
-    if (_attributedDescription == nil) {
-        _attributedDescription = _job.attributedDescription;
-    }
-    return _attributedDescription;
-}
-
 - (NSAttributedString *)attributedTitleDescription
 {
     if (_attributedTitleDescription == nil) {
     return _attributedTitleDescription;
 }
 
+- (NSAttributedString *)attributedDescription
+{
+    if (_attributedDescription == nil) {
+        _attributedDescription = _job.attributedDescription;
+    }
+    return _attributedDescription;
+}
+
 #pragma mark - NSSecureCoding
 
 + (BOOL)supportsSecureCoding
index 4dedf3766452a81ae8952f078d2b513290006832..e79ee51c877e37b4a023c9c13a0f567e7b31f43c 100644 (file)
@@ -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
index d736fe59f71766b1dba6ea8ea4ad09bd0433101f..39fbdf4ce054f97544caea8d58a615ba93d8fa87 100644 (file)
     [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
     [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 (file)
index 0000000..95ab53a
--- /dev/null
@@ -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 (file)
index 0000000..d0b4753
--- /dev/null
@@ -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
index bd8668d12653f13f9fc98ccc6579fac1ab2f952f..f63eec1bfcd74faa97b9a85c9d79e3678f38cbf2 100644 (file)
                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 */; };
                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 */; };
                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>"; };
                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>"; };
                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>"; };
                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>";
                                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 */,
                                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 */,
                                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 */,
                                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 */,
                        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 = (