From de6e8f85686ad3f63be7fba7b4bf10aa1912a09a Mon Sep 17 00:00:00 2001
From: ritsuka <damiog@gmail.com>
Date: Mon, 26 Jan 2015 08:30:23 +0000
Subject: [PATCH] MacGui: add a method to return a CGImageRef for a preview in
 HBCore, and skip the alpha to use less memory. Use a dispatch_source as a
 timer in HBCore so we will be able to run the update loop on its own thread.
 Remove the pointer to hb_handle_t, no class outside HBCore uses it.

git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@6816 b64f7644-9d1e-0410-96f1-a4d463321fa5
---
 macosx/HBController.m                      |   2 +-
 macosx/HBCore.h                            |  35 +++-
 macosx/HBCore.m                            | 188 +++++++++++++++------
 macosx/HBPreviewController.m               |   4 +-
 macosx/HBPreviewGenerator.h                |   4 +-
 macosx/HBPreviewGenerator.m                |  97 ++---------
 macosx/HandBrake.xcodeproj/project.pbxproj |   4 +-
 7 files changed, 179 insertions(+), 155 deletions(-)

diff --git a/macosx/HBController.m b/macosx/HBController.m
index 9de07ef23..75c3c5ef1 100644
--- a/macosx/HBController.m
+++ b/macosx/HBController.m
@@ -791,7 +791,7 @@
 
     // Generate a new file name
     NSString *fileName = [HBUtilities automaticNameForSource:title.name
-                                                       title:title.hb_title->index
+                                                       title:title.index
                                                     chapters:NSMakeRange(self.job.range.chapterStart + 1, self.job.range.chapterStop + 1)
                                                      quality:self.job.video.qualityType ? self.job.video.quality : 0
                                                      bitrate:!self.job.video.qualityType ? self.job.video.avgBitrate : 0
diff --git a/macosx/HBCore.h b/macosx/HBCore.h
index 6eee94065..03d05ba9e 100644
--- a/macosx/HBCore.h
+++ b/macosx/HBCore.h
@@ -8,6 +8,8 @@
 #include "hb.h"
 
 @class HBJob;
+@class HBPicture;
+@class HBTitle;
 
 // These constants specify the current state of HBCore.
 typedef NS_ENUM(NSUInteger, HBState) {
@@ -69,11 +71,6 @@ typedef void (^HBCoreCompletionHandler)(BOOL success);
  */
 @property (nonatomic, readonly) HBState state;
 
-/**
- * Pointer to a libhb handle used by this HBCore instance.
- */
-@property (nonatomic, readonly) hb_handle_t *hb_handle;
-
 /**
  *  The name of the core, used for debugging purpose.
  */
@@ -90,7 +87,7 @@ typedef void (^HBCoreCompletionHandler)(BOOL success);
 - (BOOL)canScan:(NSURL *)url error:(NSError **)error;
 
 /**
- *  Starts the asynchronous execution of a scan.
+ *  Initiates an asynchronous scan operation and returns immediately.
  *
  *  @param url                 the URL of the input file.
  *  @param index            the index of the desired title. Use 0 to scan every title.
@@ -103,6 +100,7 @@ typedef void (^HBCoreCompletionHandler)(BOOL success);
 
 /**
  *  Cancels the scan execution.
+ *  Cancel can be invoked when the scan is running.
  */
 - (void)cancelScan;
 
@@ -112,7 +110,23 @@ typedef void (^HBCoreCompletionHandler)(BOOL success);
 @property (nonatomic, readonly) NSArray *titles;
 
 /**
- *  Starts an asynchronous encoding session with the passed job.
+ *  This function converts an image created by libhb (specified via index)
+ *  into an CGImage.
+ *
+ *  @param index       the index of the desired image.
+ *  @param title       Handle to hb_title_t of desired title
+ *  @param frame       a HBPicture instance that describe the image's frame.
+ *  @param deinterlace whether the preview image must be deinterlaced or not.
+ *
+ *  @return a CGImageRef of the wanted image, NULL if the index is out of bounds.
+ */
+- (CGImageRef)copyImageAtIndex:(NSUInteger)index
+                      forTitle:(HBTitle *)title
+                  pictureFrame:(HBPicture *)frame
+                   deinterlace:(BOOL)deinterlace;
+
+/**
+ *  Initiates an asynchronous encode operation and returns immediately.
  *
  *  @param job                 the job to encode
  *  @param progressHandler     a block called periodically with the progress information.
@@ -121,17 +135,20 @@ typedef void (^HBCoreCompletionHandler)(BOOL success);
 - (void)encodeJob:(HBJob *)job progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler;
 
 /**
- * Stops encoding session and releases resources.
+ *  Stops encode operation and releases resources.
+ *  Cancel can be invoked when the encode is running.
  */
 - (void)cancelEncode;
 
 /**
- *  Pauses the encoding session.
+ *  Pauses the encode operation.
+ *  Pause can be invoked when the encode is running.
  */
 - (void)pause;
 
 /**
  *  Resumes a paused encoding session.
+ *  Resume can be invoked when the encode is running.
  */
 - (void)resume;
 
diff --git a/macosx/HBCore.m b/macosx/HBCore.m
index 61afbcdc4..8446a6a33 100644
--- a/macosx/HBCore.m
+++ b/macosx/HBCore.m
@@ -32,11 +32,15 @@ static void hb_error_handler(const char *errmsg)
 /// Pointer to a hb_state_s struct containing the detailed state information of libhb.
 @property (nonatomic, readonly) hb_state_t *hb_state;
 
+/// Pointer to a libhb handle used by this HBCore instance.
+@property (nonatomic, readonly) hb_handle_t *hb_handle;
+
 /// Current state of HBCore.
 @property (nonatomic, readwrite) HBState state;
 
 /// Timer used to poll libhb for state changes.
-@property (nonatomic, readwrite, retain) NSTimer *updateTimer;
+@property (nonatomic, readwrite) dispatch_source_t updateTimer;
+@property (nonatomic, readonly) dispatch_queue_t updateTimerQueue;
 
 /// Current scanned titles.
 @property (nonatomic, readwrite, retain) NSArray *titles;
@@ -91,8 +95,6 @@ static void hb_error_handler(const char *errmsg)
  * functions HBCore are used.
  *
  * @param debugMode         If set to YES, libhb will print verbose debug output.
- *
- * @return YES if libhb was opened, NO if there was an error.
  */
 - (instancetype)initWithLoggingLevel:(int)loggingLevel
 {
@@ -101,6 +103,7 @@ static void hb_error_handler(const char *errmsg)
     {
         _name = @"HBCore";
         _state = HBStateIdle;
+        _updateTimerQueue = dispatch_queue_create("fr.handbrake.coreQueue", DISPATCH_QUEUE_SERIAL);
         _hb_state = malloc(sizeof(struct hb_state_s));
 
         _hb_handle = hb_init(loggingLevel, 0);
@@ -120,10 +123,19 @@ static void hb_error_handler(const char *errmsg)
 - (void)dealloc
 {
     [self stopUpdateTimer];
+
+    dispatch_release(_updateTimerQueue);
+
     hb_close(&_hb_handle);
     _hb_handle = NULL;
-
     free(_hb_state);
+
+    [_name release];
+    _name = nil;
+
+    [_titles release];
+    _titles = nil;
+
     [super dealloc];
 }
 
@@ -177,11 +189,12 @@ static void hb_error_handler(const char *errmsg)
 - (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler
 {
     NSAssert(self.state == HBStateIdle, @"[HBCore scanURL:] called while another scan or encode already in progress");
+    NSAssert(url, @"[HBCore scanURL:] called with nil url.");
 
     // Reset the titles array
     self.titles = nil;
 
-    // Copy the progress/completation blocks
+    // Copy the progress/completion blocks
     self.progressHandler = progressHandler;
     self.completionHandler = completionHandler;
 
@@ -260,13 +273,80 @@ static void hb_error_handler(const char *errmsg)
     [HBUtilities writeToActivityLog:"%s scan cancelled", self.name.UTF8String];
 }
 
+#pragma mark - Preview images
+
+- (CGImageRef)copyImageAtIndex:(NSUInteger)index
+                      forTitle:(HBTitle *)title
+                  pictureFrame:(HBPicture *)frame
+                   deinterlace:(BOOL)deinterlace
+{
+    CGImageRef img = NULL;
+
+    hb_geometry_settings_t geo;
+    memset(&geo, 0, sizeof(geo));
+    geo.geometry.width = frame.width;
+    geo.geometry.height = frame.height;
+    // ignore the par.
+    geo.geometry.par.num = 1;
+    geo.geometry.par.den = 1;
+    int crop[4] = {frame.cropTop, frame.cropBottom, frame.cropLeft, frame.cropRight};
+    memcpy(geo.crop, crop, sizeof(int[4]));
+
+    hb_image_t *image = hb_get_preview2(_hb_handle, title.index, (int)index, &geo, deinterlace);
+
+    if (image)
+    {
+        // Create an CGImageRef and copy the libhb image into it.
+        // The image data returned by hb_get_preview2 is 4 bytes per pixel, BGRA format.
+        // Alpha is ignored.
+        CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNone;
+        CFMutableDataRef imgData = CFDataCreateMutable(kCFAllocatorDefault, 3 * image->width * image->height);
+        CGDataProviderRef provider = CGDataProviderCreateWithCFData(imgData);
+        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+        img = CGImageCreate(image->width,
+                            image->height,
+                            8,
+                            24,
+                            image->width * 3,
+                            colorSpace,
+                            bitmapInfo,
+                            provider,
+                            NULL,
+                            NO,
+                            kCGRenderingIntentDefault);
+        CGColorSpaceRelease(colorSpace);
+        CGDataProviderRelease(provider);
+        CFRelease(imgData);
+
+        UInt8 *src_line = image->data;
+        UInt8 *dst = CFDataGetMutableBytePtr(imgData);
+        for (int r = 0; r < image->height; r++)
+        {
+            UInt8 *src = src_line;
+            for (int c = 0; c < image->width; c++)
+            {
+                *dst++ = src[2];
+                *dst++ = src[1];
+                *dst++ = src[0];
+                src += 4;
+            }
+            src_line += image->plane[0].stride;
+        }
+
+        hb_image_close(&image);
+    }
+    
+    return img;
+}
+
 #pragma mark - Encodes
 
 - (void)encodeJob:(HBJob *)job progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler;
 {
     NSAssert(self.state == HBStateIdle, @"[HBCore encodeJob:] called while another scan or encode already in progress");
+    NSAssert(job, @"[HBCore encodeJob:] called with nil job");
 
-    // Copy the progress/completation blocks
+    // Copy the progress/completion blocks
     self.progressHandler = progressHandler;
     self.completionHandler = completionHandler;
 
@@ -347,13 +427,16 @@ static void hb_error_handler(const char *errmsg)
 {
     if (!self.updateTimer)
     {
-        self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:seconds
-                                                            target:self
-                                                          selector:@selector(stateUpdateTimer:)
-                                                          userInfo:NULL
-                                                           repeats:YES];
-
-        [[NSRunLoop currentRunLoop] addTimer:self.updateTimer forMode:NSEventTrackingRunLoopMode];
+        dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
+        if (timer)
+        {
+            dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), (uint64_t)(seconds * NSEC_PER_SEC), (uint64_t)(seconds * NSEC_PER_SEC / 10));
+            dispatch_source_set_event_handler(timer, ^{
+                [self updateState];
+            });
+            dispatch_resume(timer);
+        }
+        self.updateTimer = timer;
     }
 }
 
@@ -362,30 +445,11 @@ static void hb_error_handler(const char *errmsg)
  */
 - (void)stopUpdateTimer
 {
-    [self.updateTimer invalidate];
-    self.updateTimer = nil;
-}
-
-/**
- * Transforms a libhb state constant to a matching HBCore selector.
- */
-- (const SEL)selectorForState:(HBState)stateValue
-{
-    switch (stateValue)
+    if (self.updateTimer)
     {
-        case HB_STATE_WORKING:
-        case HB_STATE_SCANNING:
-        case HB_STATE_MUXING:
-        case HB_STATE_PAUSED:
-        case HB_STATE_SEARCHING:
-            return @selector(handleProgress);
-        case HB_STATE_SCANDONE:
-            return @selector(handleScanCompletion);
-        case HB_STATE_WORKDONE:
-            return @selector(handleWorkCompletion);
-        default:
-            NSAssert1(NO, @"[HBCore selectorForState:] unknown state %lu", stateValue);
-            return NULL;
+        dispatch_source_cancel(self.updateTimer);
+        dispatch_release(self.updateTimer);
+        self.updateTimer = NULL;
     }
 }
 
@@ -394,7 +458,7 @@ static void hb_error_handler(const char *errmsg)
  * Additional processing for each state is performed in methods that start
  * with 'handle'.
  */
-- (void)stateUpdateTimer:(NSTimer *)timer
+- (void)updateState
 {
     hb_get_state(_hb_handle, _hb_state);
 
@@ -407,25 +471,18 @@ static void hb_error_handler(const char *errmsg)
     // Update HBCore state to reflect the current state of libhb
     self.state = _hb_state->state;
 
-    // Determine name of the method that does further processing for this state.
-    SEL sel = [self selectorForState:self.state];
-
+    // Call the handler for the current state
     if (_hb_state->state == HB_STATE_WORKDONE || _hb_state->state == HB_STATE_SCANDONE)
     {
-        // Libhb reported HB_STATE_WORKDONE or HB_STATE_SCANDONE,
-        // so nothing interesting will happen after this point, stop the timer.
-        [self stopUpdateTimer];
-
-        // Set the state to idle, because the update timer won't fire again.
-        self.state = HBStateIdle;
-        hb_system_sleep_allow(_hb_handle);
+        [self handleCompletion];
+    }
+    else
+    {
+        [self handleProgress];
     }
-
-    // Call the determined selector.
-    [self performSelector:sel];
 }
 
-#pragma mark - Notifications
+#pragma mark - Blocks callbacks
 
 /**
  * Processes progress state information.
@@ -438,6 +495,32 @@ static void hb_error_handler(const char *errmsg)
     }
 }
 
+/**
+ * Processes completion state information.
+ */
+- (void)handleCompletion
+{
+    // Libhb reported HB_STATE_WORKDONE or HB_STATE_SCANDONE,
+    // so nothing interesting will happen after this point, stop the timer.
+    [self stopUpdateTimer];
+
+    // Set the state to idle, because the update timer won't fire again.
+    self.state = HBStateIdle;
+    // Reallow system sleep.
+    hb_system_sleep_allow(_hb_handle);
+
+    // Call the completion block and clean ups the handlers
+    self.progressHandler = nil;
+    if (_hb_state->state == HB_STATE_WORKDONE)
+    {
+        [self handleWorkCompletion];
+    }
+    else
+    {
+        [self handleScanCompletion];
+    }
+}
+
 /**
  *  Runs the completion block and clean ups the internal blocks.
  *
@@ -447,8 +530,9 @@ static void hb_error_handler(const char *errmsg)
 {
     if (self.completionHandler)
     {
+        // Retain the completion block, because it could be replaced
+        // inside the same block.
         HBCoreCompletionHandler completionHandler = [self.completionHandler retain];
-        self.progressHandler = nil;
         self.completionHandler = nil;
         completionHandler(result);
         [completionHandler release];
diff --git a/macosx/HBPreviewController.m b/macosx/HBPreviewController.m
index b139fa25d..31ac1c3f6 100644
--- a/macosx/HBPreviewController.m
+++ b/macosx/HBPreviewController.m
@@ -676,8 +676,8 @@ typedef enum ViewMode : NSUInteger {
 {
     if (self.window.isVisible)
     {
-        NSImage *fPreviewImage = [self.generator imageAtIndex:self.pictureIndex shouldCache:YES];
-        [self.pictureLayer setContents:fPreviewImage];
+        CGImageRef fPreviewImage = [self.generator imageAtIndex:self.pictureIndex shouldCache:YES];
+        [self.pictureLayer setContents:(id)fPreviewImage];
     }
 
     HBPicture *pict = self.job.picture;
diff --git a/macosx/HBPreviewGenerator.h b/macosx/HBPreviewGenerator.h
index 577fe2d6e..42cf013a0 100644
--- a/macosx/HBPreviewGenerator.h
+++ b/macosx/HBPreviewGenerator.h
@@ -4,7 +4,7 @@
  Homepage: <http://handbrake.fr/>.
  It may be used under the terms of the GNU General Public License. */
 
-#import <Cocoa/Cocoa.h>
+#import <Foundation/Foundation.h>
 
 @class HBCore;
 @class HBJob;
@@ -25,7 +25,7 @@
 - (instancetype)initWithCore:(HBCore *)core job:(HBJob *)job;
 
 /* Still image generator */
-- (NSImage *) imageAtIndex: (NSUInteger) index shouldCache: (BOOL) cache;
+- (CGImageRef) imageAtIndex: (NSUInteger) index shouldCache: (BOOL) cache;
 - (NSUInteger) imagesCount;
 - (void) purgeImageCache;
 
diff --git a/macosx/HBPreviewGenerator.m b/macosx/HBPreviewGenerator.m
index c4a0d2aa7..61ed448bb 100644
--- a/macosx/HBPreviewGenerator.m
+++ b/macosx/HBPreviewGenerator.m
@@ -10,11 +10,10 @@
 
 #import "HBCore.h"
 #import "HBJob.h"
-#import "HBJob+HBJobConversion.h"
 
 @interface HBPreviewGenerator ()
 
-@property (nonatomic, readonly, retain) NSMutableDictionary *picturePreviews;
+@property (nonatomic, readonly) NSMutableDictionary *picturePreviews;
 @property (nonatomic, readonly) NSUInteger imagesCount;
 @property (nonatomic, readonly) HBCore *scanCore;
 @property (nonatomic, readonly) HBJob *job;
@@ -46,26 +45,28 @@
  *
  * @param index picture index in title.
  */
-- (NSImage *) imageAtIndex: (NSUInteger) index shouldCache: (BOOL) cache
+- (CGImageRef) imageAtIndex: (NSUInteger) index shouldCache: (BOOL) cache
 {
     if (index >= self.imagesCount)
         return nil;
 
     // The preview for the specified index may not currently exist, so this method
     // generates it if necessary.
-    NSImage *theImage = [self.picturePreviews objectForKey:@(index)];
+    CGImageRef theImage = (CGImageRef)[self.picturePreviews objectForKey:@(index)];
 
     if (!theImage)
     {
         HBFilters *filters = self.job.filters;
         BOOL deinterlace = (filters.deinterlace && !filters.useDecomb) || (filters.decomb && filters.useDecomb);
 
-        theImage = [HBPreviewGenerator makeImageForPicture:index
-                                                     libhb:self.scanCore.hb_handle
-                                                   picture:self.job.picture
-                                               deinterlace:deinterlace];
+        theImage = (CGImageRef)[(id)[self.scanCore copyImageAtIndex:index
+                                                           forTitle:self.job.title
+                                                       pictureFrame:self.job.picture
+                                                        deinterlace:deinterlace] autorelease];
         if (cache && theImage)
-            [self.picturePreviews setObject:theImage forKey:@(index)];
+        {
+            [self.picturePreviews setObject:(id)theImage forKey:@(index)];
+        }
     }
 
     return theImage;
@@ -80,84 +81,6 @@
     [self.picturePreviews removeAllObjects];
 }
 
-/** 
- * This function converts an image created by libhb (specified via pictureIndex) into
- * an NSImage suitable for the GUI code to use. If removeBorders is YES,
- * makeImageForPicture crops the image generated by libhb stripping off the gray
- * border around the content. This is the low-level method that generates the image.
- * -imageForPicture calls this function whenever it can't find an image in its cache.
- *
- * @param pictureIndex Index in title.
- * @param handle Handle to hb_handle_t.
- * @param title Handle to hb_title_t of desired title.
- * @param deinterlace Whether the preview image must be deinterlaced or not.
- */
-+ (NSImage *) makeImageForPicture: (NSUInteger) pictureIndex
-                            libhb: (hb_handle_t *) handle
-                          picture: (HBPicture *) picture
-                      deinterlace: (BOOL) deinterlace
-{
-    NSImage *img = nil;
-
-    hb_geometry_settings_t geo;
-    memset(&geo, 0, sizeof(geo));
-    geo.geometry.width = picture.width;
-    geo.geometry.height = picture.height;
-    // HBPreviewController will scale the image later,
-    // ignore the par.
-    geo.geometry.par.num = 1;
-    geo.geometry.par.den = 1;
-    int crop[4] = {picture.cropTop, picture.cropBottom, picture.cropLeft, picture.cropRight};
-    memcpy(geo.crop, crop, sizeof(int[4]));
-
-    hb_image_t *image;
-    image = hb_get_preview2(handle, picture.title.hb_title->index, (int)pictureIndex, &geo, deinterlace);
-
-    if (image)
-    {
-        // Create an NSBitmapImageRep and copy the libhb image into it, converting it from
-        // libhb's format to one suitable for NSImage.
-
-        // The image data returned by hb_get_preview2 is 4 bytes per pixel, BGRA format.
-        // Alpha is ignored.
-        NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc]
-                                     initWithBitmapDataPlanes:nil
-                                     pixelsWide:image->width
-                                     pixelsHigh:image->height
-                                     bitsPerSample:8
-                                     samplesPerPixel:3   // ignore alpha
-                                     hasAlpha:NO
-                                     isPlanar:NO
-                                     colorSpaceName:NSCalibratedRGBColorSpace
-                                     bitmapFormat:NSAlphaFirstBitmapFormat
-                                     bytesPerRow:image->width * 4
-                                     bitsPerPixel:32] autorelease];
-
-        UInt8 *src_line = image->data;
-        UInt32 *dst = (UInt32 *)[imgrep bitmapData];
-        for (int r = 0; r < image->height; r++)
-        {
-            UInt32 *src = (UInt32 *)src_line;
-            for (int c = 0; c < image->width; c++)
-            {
-#if TARGET_RT_LITTLE_ENDIAN
-                *dst++ = Endian32_Swap(*src++);
-#else
-                *dst++ = *src++;
-#endif
-            }
-            src_line += image->plane[0].stride;
-        }
-
-        img = [[[NSImage alloc] initWithSize: NSMakeSize(image->width, image->height)] autorelease];
-        [img addRepresentation:imgrep];
-    }
-
-    hb_image_close(&image);
-
-    return img;
-}
-
 #pragma mark -
 #pragma mark Preview movie
 
diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj
index f052c27ae..bed13cd57 100644
--- a/macosx/HandBrake.xcodeproj/project.pbxproj
+++ b/macosx/HandBrake.xcodeproj/project.pbxproj
@@ -942,8 +942,6 @@
 				A996B0F81A62C51C00B64179 /* Audio */,
 				A91017B51A64441700039BFB /* Subtitles */,
 				A9537BED1A48A7F900141102 /* UI Bindings Additions */,
-				A9AA447D1970729300D7DEFC /* HBPreviewGenerator.h */,
-				A9D1E41618262364002F6424 /* HBPreviewGenerator.m */,
 				273F209714ADBE670021BE6D /* HBDVDDetector.h */,
 				273F209814ADBE670021BE6D /* HBDVDDetector.m */,
 				A9A2A77F1A4737DD006C219C /* NSCodingMacro.h */,
@@ -985,6 +983,8 @@
 				273F20AA14ADBE670021BE6D /* HBPictureController.m */,
 				273F20A314ADBE670021BE6D /* HBPreviewController.h */,
 				273F20A414ADBE670021BE6D /* HBPreviewController.m */,
+				A9AA447D1970729300D7DEFC /* HBPreviewGenerator.h */,
+				A9D1E41618262364002F6424 /* HBPreviewGenerator.m */,
 				273F209B14ADBE670021BE6D /* HBOutputPanelController.h */,
 				273F209C14ADBE670021BE6D /* HBOutputPanelController.m */,
 				273F209F14ADBE670021BE6D /* HBPreferencesController.h */,
-- 
2.40.0