OSDN Git Service

Fix PFR issue where there are different number of frames in 1st and 2nd pass.
[handbrake-jp/handbrake-jp-git.git] / macosx / HBQueueController.mm
1 /* HBQueueController
2
3     This file is part of the HandBrake source code.
4     Homepage: <http://handbrake.fr/>.
5     It may be used under the terms of the GNU General Public License. */
6
7 #import "HBQueueController.h"
8 #import "Controller.h"
9 #import "HBImageAndTextCell.h"
10
11 #define HB_ROW_HEIGHT_TITLE_ONLY           17.0
12 #define HB_ROW_HEIGHT_FULL_DESCRIPTION           200.0
13 // Pasteboard type for or drag operations
14 #define DragDropSimplePboardType        @"MyCustomOutlineViewPboardType"
15
16 //------------------------------------------------------------------------------------
17 #pragma mark -
18 //------------------------------------------------------------------------------------
19
20 //------------------------------------------------------------------------------------
21 // NSMutableAttributedString (HBAdditions)
22 //------------------------------------------------------------------------------------
23
24 @interface NSMutableAttributedString (HBAdditions)
25 - (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary;
26 @end
27
28 @implementation NSMutableAttributedString (HBAdditions)
29 - (void) appendString: (NSString*)aString withAttributes: (NSDictionary *)aDictionary
30 {
31     NSAttributedString * s = [[[NSAttributedString alloc]
32         initWithString: aString
33         attributes: aDictionary] autorelease];
34     [self appendAttributedString: s];
35 }
36 @end
37
38
39 @implementation HBQueueOutlineView
40
41 - (void)viewDidEndLiveResize
42 {
43     // Since we disabled calculating row heights during a live resize, force them to
44     // recalculate now.
45     [self noteHeightOfRowsWithIndexesChanged:
46             [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [self numberOfRows])]];
47     [super viewDidEndLiveResize];
48 }
49
50
51
52 /* This should be for dragging, we take this info from the presets right now */
53 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
54 {
55     fIsDragging = YES;
56
57     // By default, NSTableView only drags an image of the first column. Change this to
58     // drag an image of the queue's icon and desc and action columns.
59     NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"desc"], [self tableColumnWithIdentifier:@"icon"],[self tableColumnWithIdentifier:@"action"], nil];
60     return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
61 }
62
63
64
65 - (void) mouseDown:(NSEvent *)theEvent
66 {
67     [super mouseDown:theEvent];
68         fIsDragging = NO;
69 }
70
71
72
73 - (BOOL) isDragging;
74 {
75     return fIsDragging;
76 }
77
78 @end
79
80 #pragma mark Toolbar Identifiers
81 // Toolbar identifiers
82 static NSString*    HBQueueToolbar                            = @"HBQueueToolbar1";
83 static NSString*    HBQueueStartCancelToolbarIdentifier       = @"HBQueueStartCancelToolbarIdentifier";
84 static NSString*    HBQueuePauseResumeToolbarIdentifier       = @"HBQueuePauseResumeToolbarIdentifier";
85
86 #pragma mark -
87
88 @implementation HBQueueController
89
90 //------------------------------------------------------------------------------------
91 // init
92 //------------------------------------------------------------------------------------
93 - (id)init
94 {
95     if (self = [super initWithWindowNibName:@"Queue"])
96     {
97         // NSWindowController likes to lazily load its window nib. Since this
98         // controller tries to touch the outlets before accessing the window, we
99         // need to force it to load immadiately by invoking its accessor.
100         //
101         // If/when we switch to using bindings, this can probably go away.
102         [self window];
103
104         // Our defaults
105         [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
106             @"NO",      @"QueueWindowIsOpen",
107             @"NO",      @"QueueShowsDetail",
108             @"YES",     @"QueueShowsJobsAsGroups",
109             nil]];
110
111         fJobGroups = [[NSMutableArray arrayWithCapacity:0] retain];
112        }
113
114         return self;
115 }
116
117 - (void)setQueueArray: (NSMutableArray *)QueueFileArray
118 {
119     [fJobGroups setArray:QueueFileArray];
120     fIsDragging = NO; 
121     /* First stop any timer working now */
122     //[self stopAnimatingCurrentJobGroupInQueue];
123     [fOutlineView reloadData];
124     
125     
126     
127     /* lets get the stats on the status of the queue array */
128     
129     fPendingCount = 0;
130     fCompletedCount = 0;
131     fCanceledCount = 0;
132     fWorkingCount = 0;
133     
134     /* We use a number system to set the encode status of the queue item
135      * in controller.mm
136      * 0 == already encoded
137      * 1 == is being encoded
138      * 2 == is yet to be encoded
139      * 3 == cancelled
140      */
141         int i = 0;
142     NSDictionary *thisQueueDict = nil;
143         for(id tempObject in fJobGroups)
144         {
145                 thisQueueDict = tempObject;
146                 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed
147                 {
148                         fCompletedCount++;      
149                 }
150                 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded
151                 {
152                         fWorkingCount++;
153             /* we have an encoding job so, lets start the animation timer */
154             if ([thisQueueDict objectForKey:@"EncodingPID"] && [[thisQueueDict objectForKey:@"EncodingPID"] intValue] == pidNum)
155             {
156                 fEncodingQueueItem = i;
157             }
158                 }
159         if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending          
160         {
161                         fPendingCount++;
162                 }
163         if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled                
164         {
165                         fCanceledCount++;
166                 }
167                 i++;
168         }
169     
170     /* Set the queue status field in the queue window */
171     NSMutableString * string;
172     if (fPendingCount == 1)
173     {
174         string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending", @"" ), fPendingCount];
175     }
176     else
177     {
178         string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending", @"" ), fPendingCount];
179     }
180     [fQueueCountField setStringValue:string];
181     
182 }
183
184 /* This method sets the status string in the queue window
185  * and is called from Controller.mm (fHBController)
186  * instead of running another timer here polling libhb
187  * for encoding status
188  */
189 - (void)setQueueStatusString: (NSString *)statusString
190 {
191     
192     [fProgressTextField setStringValue:statusString];
193     
194 }
195
196 //------------------------------------------------------------------------------------
197 // dealloc
198 //------------------------------------------------------------------------------------
199 - (void)dealloc
200 {
201     // clear the delegate so that windowWillClose is not attempted
202     if( [[self window] delegate] == self )
203         [[self window] setDelegate:nil];
204
205     [fJobGroups release];
206
207     [fSavedExpandedItems release];
208     [fSavedSelectedItems release];
209
210     [[NSNotificationCenter defaultCenter] removeObserver:self];
211
212     [super dealloc];
213 }
214
215 //------------------------------------------------------------------------------------
216 // Receive HB handle
217 //------------------------------------------------------------------------------------
218 - (void)setHandle: (hb_handle_t *)handle
219 {
220     fQueueEncodeLibhb = handle;
221 }
222
223 //------------------------------------------------------------------------------------
224 // Receive HBController
225 //------------------------------------------------------------------------------------
226 - (void)setHBController: (HBController *)controller
227 {
228     fHBController = controller;
229 }
230
231 - (void)setPidNum: (int)myPidnum
232 {
233     pidNum = myPidnum;
234     [fHBController writeToActivityLog: "HBQueueController : My Pidnum is %d", pidNum];
235 }
236
237 #pragma mark -
238
239 //------------------------------------------------------------------------------------
240 // Displays and brings the queue window to the front
241 //------------------------------------------------------------------------------------
242 - (IBAction) showQueueWindow: (id)sender
243 {
244     [self showWindow:sender];
245     [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"];
246     [self startAnimatingCurrentWorkingEncodeInQueue];
247 }
248
249
250
251 //------------------------------------------------------------------------------------
252 // awakeFromNib
253 //------------------------------------------------------------------------------------
254 - (void)awakeFromNib
255 {
256     [self setupToolbar];
257
258     if( ![[self window] setFrameUsingName:@"Queue"] )
259         [[self window] center];
260     [self setWindowFrameAutosaveName:@"Queue"];
261
262     /* lets setup our queue list outline view for drag and drop here */
263     [fOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ];
264     [fOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
265     [fOutlineView setVerticalMotionCanBeginDrag: YES];
266
267
268     // Don't allow autoresizing of main column, else the "delete" column will get
269     // pushed out of view.
270     [fOutlineView setAutoresizesOutlineColumn: NO];
271
272 #if HB_OUTLINE_METRIC_CONTROLS
273     [fIndentation setHidden: NO];
274     [fSpacing setHidden: NO];
275     [fIndentation setIntegerValue:[fOutlineView indentationPerLevel]];  // debug
276     [fSpacing setIntegerValue:3];       // debug
277 #endif
278
279     // Show/hide UI elements
280     fCurrentJobPaneShown = NO;     // it's shown in the nib
281
282 }
283
284
285 //------------------------------------------------------------------------------------
286 // windowWillClose
287 //------------------------------------------------------------------------------------
288 - (void)windowWillClose:(NSNotification *)aNotification
289 {
290     [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"QueueWindowIsOpen"];
291     [self stopAnimatingCurrentJobGroupInQueue];
292 }
293
294 #pragma mark Toolbar
295
296 //------------------------------------------------------------------------------------
297 // setupToolbar
298 //------------------------------------------------------------------------------------
299 - (void)setupToolbar
300 {
301     // Create a new toolbar instance, and attach it to our window
302     NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: HBQueueToolbar] autorelease];
303
304     // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults
305     [toolbar setAllowsUserCustomization: YES];
306     [toolbar setAutosavesConfiguration: YES];
307     [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
308
309     // We are the delegate
310     [toolbar setDelegate: self];
311
312     // Attach the toolbar to our window
313     [[self window] setToolbar:toolbar];
314 }
315
316 //------------------------------------------------------------------------------------
317 // toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:
318 //------------------------------------------------------------------------------------
319 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
320         itemForItemIdentifier:(NSString *)itemIdentifier
321         willBeInsertedIntoToolbar:(BOOL)flag
322 {
323     // Required delegate method: Given an item identifier, this method returns an item.
324     // The toolbar will use this method to obtain toolbar items that can be displayed
325     // in the customization sheet, or in the toolbar itself.
326
327     NSToolbarItem *toolbarItem = nil;
328
329     if ([itemIdentifier isEqual: HBQueueStartCancelToolbarIdentifier])
330     {
331         toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
332
333         // Set the text label to be displayed in the toolbar and customization palette
334         [toolbarItem setLabel: @"Start"];
335         [toolbarItem setPaletteLabel: @"Start/Cancel"];
336
337         // Set up a reasonable tooltip, and image
338         [toolbarItem setToolTip: @"Start Encoding"];
339         [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
340
341         // Tell the item what message to send when it is clicked
342         [toolbarItem setTarget: self];
343         [toolbarItem setAction: @selector(toggleStartCancel:)];
344     }
345
346     if ([itemIdentifier isEqual: HBQueuePauseResumeToolbarIdentifier])
347     {
348         toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
349
350         // Set the text label to be displayed in the toolbar and customization palette
351         [toolbarItem setLabel: @"Pause"];
352         [toolbarItem setPaletteLabel: @"Pause/Resume"];
353
354         // Set up a reasonable tooltip, and image
355         [toolbarItem setToolTip: @"Pause Encoding"];
356         [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
357
358         // Tell the item what message to send when it is clicked
359         [toolbarItem setTarget: self];
360         [toolbarItem setAction: @selector(togglePauseResume:)];
361     }
362
363     return toolbarItem;
364 }
365
366 //------------------------------------------------------------------------------------
367 // toolbarDefaultItemIdentifiers:
368 //------------------------------------------------------------------------------------
369 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
370 {
371     // Required delegate method: Returns the ordered list of items to be shown in the
372     // toolbar by default.
373
374     return [NSArray arrayWithObjects:
375         HBQueueStartCancelToolbarIdentifier,
376         HBQueuePauseResumeToolbarIdentifier,
377         nil];
378 }
379
380 //------------------------------------------------------------------------------------
381 // toolbarAllowedItemIdentifiers:
382 //------------------------------------------------------------------------------------
383 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
384 {
385     // Required delegate method: Returns the list of all allowed items by identifier.
386     // By default, the toolbar does not assume any items are allowed, even the
387     // separator. So, every allowed item must be explicitly listed.
388
389     return [NSArray arrayWithObjects:
390         HBQueueStartCancelToolbarIdentifier,
391         HBQueuePauseResumeToolbarIdentifier,
392         NSToolbarCustomizeToolbarItemIdentifier,
393         NSToolbarFlexibleSpaceItemIdentifier,
394         NSToolbarSpaceItemIdentifier,
395         NSToolbarSeparatorItemIdentifier,
396         nil];
397 }
398
399 //------------------------------------------------------------------------------------
400 // validateToolbarItem:
401 //------------------------------------------------------------------------------------
402 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
403 {
404     // Optional method: This message is sent to us since we are the target of some
405     // toolbar item actions.
406
407     if (!fQueueEncodeLibhb) return NO;
408
409     BOOL enable = NO;
410
411     hb_state_t s;
412     hb_get_state2 (fQueueEncodeLibhb, &s);
413
414     if ([[toolbarItem itemIdentifier] isEqual: HBQueueStartCancelToolbarIdentifier])
415     {
416         if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
417         {
418             enable = YES;
419             [toolbarItem setImage:[NSImage imageNamed: @"Stop"]];
420             [toolbarItem setLabel: @"Stop"];
421             [toolbarItem setToolTip: @"Stop Encoding"];
422         }
423
424         else if (fPendingCount > 0)
425         {
426             enable = YES;
427             [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
428             [toolbarItem setLabel: @"Start"];
429             [toolbarItem setToolTip: @"Start Encoding"];
430         }
431
432         else
433         {
434             enable = NO;
435             [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
436             [toolbarItem setLabel: @"Start"];
437             [toolbarItem setToolTip: @"Start Encoding"];
438         }
439     }
440
441     if ([[toolbarItem itemIdentifier] isEqual: HBQueuePauseResumeToolbarIdentifier])
442     {
443         if (s.state == HB_STATE_PAUSED)
444         {
445             enable = YES;
446             [toolbarItem setImage:[NSImage imageNamed: @"Play"]];
447             [toolbarItem setLabel: @"Resume"];
448             [toolbarItem setToolTip: @"Resume Encoding"];
449        }
450
451         else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
452         {
453             enable = YES;
454             [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
455             [toolbarItem setLabel: @"Pause"];
456             [toolbarItem setToolTip: @"Pause Encoding"];
457         }
458         else
459         {
460             enable = NO;
461             [toolbarItem setImage:[NSImage imageNamed: @"Pause"]];
462             [toolbarItem setLabel: @"Pause"];
463             [toolbarItem setToolTip: @"Pause Encoding"];
464         }
465     }
466
467     return enable;
468 }
469
470 #pragma mark -
471
472
473 #pragma mark Queue Item Controls
474 //------------------------------------------------------------------------------------
475 // Delete encodes from the queue window and accompanying array
476 // Also handling first cancelling the encode if in fact its currently encoding.
477 //------------------------------------------------------------------------------------
478 - (IBAction)removeSelectedQueueItem: (id)sender
479 {
480     NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];
481     NSUInteger row = [selectedRows firstIndex];
482     if( row == NSNotFound )
483         return;
484     /* if this is a currently encoding job, we need to be sure to alert the user,
485      * to let them decide to cancel it first, then if they do, we can come back and
486      * remove it */
487     
488     if ([[[fJobGroups objectAtIndex:row] objectForKey:@"Status"] integerValue] == 1)
489     {
490        /* We pause the encode here so that it doesn't finish right after and then
491         * screw up the sync while the window is open
492         */
493        [fHBController Pause:NULL];
494          NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It ?", nil)];
495         // Which window to attach the sheet to?
496         NSWindow * docWindow = nil;
497         if ([sender respondsToSelector: @selector(window)])
498             docWindow = [sender window];
499         
500         
501         NSBeginCriticalAlertSheet(
502                                   alertTitle,
503                                   NSLocalizedString(@"Keep Encoding", nil),
504                                   nil,
505                                   NSLocalizedString(@"Stop Encoding and Delete", nil),
506                                   docWindow, self,
507                                   nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,
508                                   NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil));
509         
510         // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
511     }
512     else
513     { 
514     /* since we are not a currently encoding item, we can just be removed */
515             [fHBController removeQueueFileItem:row];
516     }
517 }
518
519 - (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
520 {
521     /* We resume encoding and perform the appropriate actions 
522      * Note: Pause: is a toggle type method based on hb's current
523      * state, if it paused, it will resume encoding and vice versa.
524      * In this case, we are paused from the calling window, so calling
525      * [fHBController Pause:NULL]; Again will resume encoding
526      */
527     [fHBController Pause:NULL];
528     if (returnCode == NSAlertOtherReturn)
529     {
530         /* We need to save the currently encoding item number first */
531         int encodingItemToRemove = fEncodingQueueItem;
532         /* Since we are encoding, we need to let fHBController Cancel this job
533          * upon which it will move to the next one if there is one
534          */
535         [fHBController doCancelCurrentJob];
536         /* Now, we can go ahead and remove the job we just cancelled since
537          * we have its item number from above
538          */
539         [fHBController removeQueueFileItem:encodingItemToRemove];
540     }
541     
542 }
543
544 //------------------------------------------------------------------------------------
545 // Show the finished encode in the finder
546 //------------------------------------------------------------------------------------
547 - (IBAction)revealSelectedQueueItem: (id)sender
548 {
549     NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];
550     NSInteger row = [selectedRows firstIndex];
551     if (row != NSNotFound)
552     {
553         while (row != NSNotFound)
554         {
555            NSMutableDictionary *queueItemToOpen = [fOutlineView itemAtRow: row];
556          [[NSWorkspace sharedWorkspace] selectFile:[queueItemToOpen objectForKey:@"DestinationPath"] inFileViewerRootedAtPath:nil];
557
558             row = [selectedRows indexGreaterThanIndex: row];
559         }
560     }
561 }
562
563
564 //------------------------------------------------------------------------------------
565 // Starts or cancels the processing of jobs depending on the current state
566 //------------------------------------------------------------------------------------
567 - (IBAction)toggleStartCancel: (id)sender
568 {
569     if (!fQueueEncodeLibhb) return;
570
571     hb_state_t s;
572     hb_get_state2 (fQueueEncodeLibhb, &s);
573
574     if ((s.state == HB_STATE_PAUSED) || (s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
575         [fHBController Cancel: fQueuePane]; // sender == fQueuePane so that warning alert shows up on queue window
576
577     else if (fPendingCount > 0)
578         [fHBController Rip: NULL];
579 }
580
581 //------------------------------------------------------------------------------------
582 // Toggles the pause/resume state of libhb
583 //------------------------------------------------------------------------------------
584 - (IBAction)togglePauseResume: (id)sender
585 {
586     if (!fQueueEncodeLibhb) return;
587     
588     hb_state_t s;
589     hb_get_state2 (fQueueEncodeLibhb, &s);
590     
591     if (s.state == HB_STATE_PAUSED)
592     {
593         hb_resume (fQueueEncodeLibhb);
594         [self startAnimatingCurrentWorkingEncodeInQueue];
595     }
596     else if ((s.state == HB_STATE_WORKING) || (s.state == HB_STATE_MUXING))
597     {
598         hb_pause (fQueueEncodeLibhb);
599         [self stopAnimatingCurrentJobGroupInQueue];
600     }
601 }
602
603
604 //------------------------------------------------------------------------------------
605 // Send the selected queue item back to the main window for rescan and possible edit.
606 //------------------------------------------------------------------------------------
607 - (IBAction)editSelectedQueueItem: (id)sender
608 {
609     NSIndexSet * selectedRows = [fOutlineView selectedRowIndexes];
610     NSUInteger row = [selectedRows firstIndex];
611     if( row == NSNotFound )
612         return;
613     /* if this is a currently encoding job, we need to be sure to alert the user,
614      * to let them decide to cancel it first, then if they do, we can come back and
615      * remove it */
616     
617     if ([[[fJobGroups objectAtIndex:row] objectForKey:@"Status"] integerValue] == 1)
618     {
619        /* We pause the encode here so that it doesn't finish right after and then
620         * screw up the sync while the window is open
621         */
622        [fHBController Pause:NULL];
623          NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop This Encode and Remove It ?", nil)];
624         // Which window to attach the sheet to?
625         NSWindow * docWindow = nil;
626         if ([sender respondsToSelector: @selector(window)])
627             docWindow = [sender window];
628         
629         
630         NSBeginCriticalAlertSheet(
631                                   alertTitle,
632                                   NSLocalizedString(@"Keep Encoding", nil),
633                                   nil,
634                                   NSLocalizedString(@"Stop Encoding and Delete", nil),
635                                   docWindow, self,
636                                   nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,
637                                   NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil));
638         
639     }
640     else
641     { 
642     /* since we are not a currently encoding item, we can just be cancelled */
643     [fHBController rescanQueueItemToMainWindow:[[fJobGroups objectAtIndex:row] objectForKey:@"SourcePath"] scanTitleNum:[[[fJobGroups objectAtIndex:row] objectForKey:@"TitleNumber"] integerValue] selectedQueueItem:row];
644     
645     }
646 }
647
648
649 #pragma mark -
650 #pragma mark Animate Endcoding Item
651
652
653
654
655 //------------------------------------------------------------------------------------
656 // Starts animating the job icon of the currently processing job in the queue outline
657 // view.
658 //------------------------------------------------------------------------------------
659 - (void) startAnimatingCurrentWorkingEncodeInQueue
660 {
661     if (!fAnimationTimer)
662         fAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0/12.0     // 1/12 because there are 6 images in the animation cycle
663                 target:self
664                 selector:@selector(animateWorkingEncodeInQueue:)
665                 userInfo:nil
666                 repeats:YES] retain];
667 }
668
669 //------------------------------------------------------------------------------------
670 // If a job is currently processing, its job icon in the queue outline view is
671 // animated to its next state.
672 //------------------------------------------------------------------------------------
673 - (void) animateWorkingEncodeInQueue:(NSTimer*)theTimer
674 {
675     if (fWorkingCount > 0)
676     {
677         fAnimationIndex++;
678         fAnimationIndex %= 6;   // there are 6 animation images; see outlineView:objectValueForTableColumn:byItem: below.
679         [self animateWorkingEncodeIconInQueue];
680     }
681 }
682
683 /* We need to make sure we denote only working encodes even for multiple instances */
684 - (void) animateWorkingEncodeIconInQueue
685 {
686     NSInteger row = fEncodingQueueItem; /// need to set to fEncodingQueueItem
687     NSInteger col = [fOutlineView columnWithIdentifier: @"icon"];
688     if (row != -1 && col != -1)
689     {
690         NSRect frame = [fOutlineView frameOfCellAtColumn:col row:row];
691         [fOutlineView setNeedsDisplayInRect: frame];
692     }
693 }
694
695 //------------------------------------------------------------------------------------
696 // Stops animating the job icon of the currently processing job in the queue outline
697 // view.
698 //------------------------------------------------------------------------------------
699 - (void) stopAnimatingCurrentJobGroupInQueue
700 {
701     if (fAnimationTimer && [fAnimationTimer isValid])
702     {
703         [fAnimationTimer invalidate];
704         [fAnimationTimer release];
705         fAnimationTimer = nil;
706     }
707 }
708
709
710 #pragma mark -
711
712 - (void)moveObjectsInArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
713 {
714     NSUInteger index = [indexSet lastIndex];
715     NSUInteger aboveInsertIndexCount = 0;
716
717     while (index != NSNotFound)
718     {
719         NSUInteger removeIndex;
720
721         if (index >= insertIndex)
722         {
723             removeIndex = index + aboveInsertIndexCount;
724             aboveInsertIndexCount++;
725         }
726         else
727         {
728             removeIndex = index;
729             insertIndex--;
730         }
731
732         id object = [[array objectAtIndex:removeIndex] retain];
733         [array removeObjectAtIndex:removeIndex];
734         [array insertObject:object atIndex:insertIndex];
735         [object release];
736
737         index = [indexSet indexLessThanIndex:index];
738     }
739 }
740
741
742 #pragma mark -
743 #pragma mark NSOutlineView delegate
744
745
746 - (id)outlineView:(NSOutlineView *)fOutlineView child:(NSInteger)index ofItem:(id)item
747 {
748     if (item == nil)
749         return [fJobGroups objectAtIndex:index];
750
751     // We are only one level deep, so we can't be asked about children
752     NSAssert (NO, @"HBQueueController outlineView:child:ofItem: can't handle nested items.");
753     return nil;
754 }
755
756 - (BOOL)outlineView:(NSOutlineView *)fOutlineView isItemExpandable:(id)item
757 {
758     // Our outline view has no levels, but we can still expand every item. Doing so
759     // just makes the row taller. See heightOfRowByItem below.
760     return YES;
761 }
762
763 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
764 {
765     // Our outline view has no levels, but we can still expand every item. Doing so
766     // just makes the row taller. See heightOfRowByItem below.
767 return ![(HBQueueOutlineView*)outlineView isDragging];
768 }
769
770 - (NSInteger)outlineView:(NSOutlineView *)fOutlineView numberOfChildrenOfItem:(id)item
771 {
772     // Our outline view has no levels, so number of children will be zero for all
773     // top-level items.
774     if (item == nil)
775         return [fJobGroups count];
776     else
777         return 0;
778 }
779
780 - (void)outlineViewItemDidCollapse:(NSNotification *)notification
781 {
782     id item = [[notification userInfo] objectForKey:@"NSObject"];
783     NSInteger row = [fOutlineView rowForItem:item];
784     [fOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
785 }
786
787 - (void)outlineViewItemDidExpand:(NSNotification *)notification
788 {
789     id item = [[notification userInfo] objectForKey:@"NSObject"];
790     NSInteger row = [fOutlineView rowForItem:item];
791     [fOutlineView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row,1)]];
792 }
793
794 - (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
795 {
796     if ([outlineView isItemExpanded: item])
797     {
798         /* Below is the original code to accommodate a live resize,
799          * however as stated in travistex's comments it's very buggy.
800          * For now I will leave it here ... commented out and use
801          * the code below to determine the row height based on each
802          * encodes optional parameters and how they are displayed. */
803         
804         // Short-circuit here if in a live resize primarily to fix a bug but also to
805         // increase resposivness during a resize. There's a bug in NSTableView that
806         // causes row heights to get messed up if you try to change them during a live
807         // resize. So if in a live resize, simply return the previously calculated
808         // height. The row heights will get fixed up after the resize because we have
809         // implemented viewDidEndLiveResize to force all of them to be recalculated.
810         // if ([outlineView inLiveResize] && [item lastDescriptionHeight] > 0)
811         //   return [item lastDescriptionHeight];
812         
813         // CGFloat width = [[outlineView tableColumnWithIdentifier: @"desc"] width];
814         // Column width is NOT what is ultimately used. I can't quite figure out what
815         // width to use for calculating text metrics. No matter how I tweak this value,
816         // there are a few conditions in which the drawn text extends below the bounds
817         // of the row cell. In previous versions, which ran under Tiger, I was
818         // reducing width by 47 pixles.
819         // width -= 2;     // (?) for intercell spacing
820         
821         // CGFloat height = [item heightOfDescriptionForWidth: width];
822         // return height;
823         
824         /* So, we know several rows of text that are in all queue items for display.
825          * These are the title line, Preset, Format, Destination, Picture, and Video Lines
826          */
827         CGFloat rowHeightNonTitle = 15.0;
828         /* Add the title line height, then the non title line height for Preset, Format, Destination
829          * Picture and Video
830          */
831         CGFloat itemHeightForDisplay = HB_ROW_HEIGHT_TITLE_ONLY + (rowHeightNonTitle * 5);
832         
833         /* get our item row number so we an use it to calc how many lines we have to display based
834          * on MP4 Options, Filter Options, X264 Options, Audio Tracks and Subtitles from our queue array */
835         int itemRowNum = [outlineView rowForItem: item];
836         NSMutableDictionary *queueItemToCheck = [outlineView itemAtRow: itemRowNum];
837         
838         /* Check to see if we need to allow for mp4 opts */
839         BOOL mp4OptsPresent = NO;
840         if ([[queueItemToCheck objectForKey:@"FileFormat"] isEqualToString: @"MP4 file"])
841         {
842             
843             if( [[queueItemToCheck objectForKey:@"Mp4LargeFile"] intValue] == 1)
844             {
845                 mp4OptsPresent = YES;
846             }
847             if( [[queueItemToCheck objectForKey:@"Mp4HttpOptimize"] intValue] == 1)
848             {
849                 mp4OptsPresent = YES;
850             }
851             if( [[queueItemToCheck objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
852             {
853                 mp4OptsPresent = YES;
854             }
855         }
856         
857         if (mp4OptsPresent == YES)
858         {
859             itemHeightForDisplay +=  rowHeightNonTitle;   
860         }
861         
862         /* check to see if we need to allow for the Picture Filters row */
863         BOOL pictureFiltersPresent = NO;
864         if( [[queueItemToCheck objectForKey:@"PictureDetelecine"] intValue] > 0)
865         {
866             pictureFiltersPresent = YES;
867         }
868         if( [[queueItemToCheck objectForKey:@"PictureDecomb"] intValue] > 0)
869         {
870             pictureFiltersPresent = YES;
871         }
872         if( [[queueItemToCheck objectForKey:@"PictureDeinterlace"] intValue] > 0)
873         {
874             pictureFiltersPresent = YES;
875         }
876         if( [[queueItemToCheck objectForKey:@"PictureDenoise"] intValue] > 0)
877         {
878             pictureFiltersPresent = YES;
879         }
880         if( [[queueItemToCheck objectForKey:@"PictureDeblock"] intValue] > 0)
881         {
882             pictureFiltersPresent = YES;
883         }
884         if( [[queueItemToCheck objectForKey:@"VideoGrayScale"] intValue] > 0)
885         {
886             pictureFiltersPresent = YES;
887         }
888         
889         if (pictureFiltersPresent == YES)
890         {
891             itemHeightForDisplay +=  rowHeightNonTitle;
892         }
893         
894         /* check to see if we need a line to display x264 options */
895         if ([[queueItemToCheck objectForKey:@"VideoEncoder"] isEqualToString: @"H.264 (x264)"])
896         {
897             itemHeightForDisplay +=  rowHeightNonTitle;
898         }
899         
900         /* check to see how many audio track lines to allow for */
901                 unsigned int ourMaximumNumberOfAudioTracks = [HBController maximumNumberOfAllowedAudioTracks];
902                 int actualCountOfAudioTracks = 0;
903                 for (unsigned int i = 1; i <= ourMaximumNumberOfAudioTracks; i++) {
904                         if (0 < [[queueItemToCheck objectForKey: [NSString stringWithFormat: @"Audio%dTrack", i]] intValue]) {
905                                 actualCountOfAudioTracks++;
906                         }
907                 }
908                 itemHeightForDisplay += (actualCountOfAudioTracks * rowHeightNonTitle);
909         
910         /* add in subtitle lines for each subtitle in the SubtitleList array */
911         itemHeightForDisplay +=  rowHeightNonTitle * [[queueItemToCheck objectForKey:@"SubtitleList"] count];
912         
913         return itemHeightForDisplay;
914         
915     }
916     else
917     {
918         return HB_ROW_HEIGHT_TITLE_ONLY;
919     }
920 }
921
922 - (CGFloat) heightOfDescriptionForWidth:(CGFloat)width
923 {
924     // Try to return the cached value if no changes have happened since the last time
925     //if ((width == fLastDescriptionWidth) && (fLastDescriptionHeight != 0) && !fNeedsDescription)
926        // return fLastDescriptionHeight;
927
928     //if (fNeedsDescription)
929     //    [self updateDescription];
930
931     // Calculate the height
932     //NSRect bounds = [fDescription boundingRectWithSize:NSMakeSize(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin];
933     //fLastDescriptionHeight = bounds.size.height + 6.0; // add some border to bottom
934     //fLastDescriptionWidth = width;
935     return HB_ROW_HEIGHT_FULL_DESCRIPTION;
936
937 /* supposedly another way to do this, in case boundingRectWithSize isn't working
938     NSTextView* tmpView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, width, 1)];
939     [[tmpView textStorage] setAttributedString:aString];
940     [tmpView setHorizontallyResizable:NO];
941     [tmpView setVerticallyResizable:YES];
942 //    [[tmpView textContainer] setHeightTracksTextView: YES];
943 //    [[tmpView textContainer] setContainerSize: NSMakeSize(width, 10000)];
944     [tmpView sizeToFit];
945     float height = [tmpView frame].size.height;
946     [tmpView release];
947     return height;
948 */
949 }
950
951 - (CGFloat) lastDescriptionHeight
952 {
953     return HB_ROW_HEIGHT_FULL_DESCRIPTION;
954 }
955
956 - (id)outlineView:(NSOutlineView *)fOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
957 {
958     if ([[tableColumn identifier] isEqualToString:@"desc"])
959     {
960         
961         
962         /* Below should be put into a separate method but I am way too f'ing lazy right now */
963         NSMutableAttributedString * finalString = [[[NSMutableAttributedString alloc] initWithString: @""] autorelease];
964         // Attributes
965         NSMutableParagraphStyle * ps = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain];
966         [ps setHeadIndent: 40.0];
967         [ps setParagraphSpacing: 1.0];
968         [ps setTabStops:[NSArray array]];    // clear all tabs
969         [ps addTabStop: [[[NSTextTab alloc] initWithType: NSLeftTabStopType location: 20.0] autorelease]];
970         
971         
972         NSDictionary* detailAttr = [NSDictionary dictionaryWithObjectsAndKeys:
973                                     [NSFont systemFontOfSize:10.0], NSFontAttributeName,
974                                     ps, NSParagraphStyleAttributeName,
975                                     nil];
976         
977         NSDictionary* detailBoldAttr = [NSDictionary dictionaryWithObjectsAndKeys:
978                                         [NSFont boldSystemFontOfSize:10.0], NSFontAttributeName,
979                                         ps, NSParagraphStyleAttributeName,
980                                         nil];
981         
982         NSDictionary* titleAttr = [NSDictionary dictionaryWithObjectsAndKeys:
983                                    [NSFont systemFontOfSize:[NSFont systemFontSize]], NSFontAttributeName,
984                                    ps, NSParagraphStyleAttributeName,
985                                    nil];
986         
987         NSDictionary* shortHeightAttr = [NSDictionary dictionaryWithObjectsAndKeys:
988                                          [NSFont systemFontOfSize:2.0], NSFontAttributeName,
989                                          nil];
990         
991         /* First line, we should strip the destination path and just show the file name and add the title num and chapters (if any) */
992         NSString * summaryInfo;
993         
994         NSString * titleString = [NSString stringWithFormat:@"Title %d", [[item objectForKey:@"TitleNumber"] intValue]];
995         
996         NSString * startStopString = @"";
997         if ([[item objectForKey:@"fEncodeStartStop"] intValue] == 0)
998         {
999             /* Start Stop is chapters */
1000             startStopString = ([[item objectForKey:@"ChapterStart"] intValue] == [[item objectForKey:@"ChapterEnd"] intValue]) ?
1001             [NSString stringWithFormat:@"Chapter %d", [[item objectForKey:@"ChapterStart"] intValue]] :
1002             [NSString stringWithFormat:@"Chapters %d through %d", [[item objectForKey:@"ChapterStart"] intValue], [[item objectForKey:@"ChapterEnd"] intValue]];
1003         }
1004         else if ([[item objectForKey:@"fEncodeStartStop"] intValue] == 1)
1005         {
1006             /* Start Stop is seconds */
1007             startStopString = [NSString stringWithFormat:@"Seconds %d through %d", [[item objectForKey:@"StartSeconds"] intValue], [[item objectForKey:@"StartSeconds"] intValue] + [[item objectForKey:@"StopSeconds"] intValue]];
1008         }
1009         else if ([[item objectForKey:@"fEncodeStartStop"] intValue] == 2)
1010         {
1011             /* Start Stop is Frames */
1012             startStopString = [NSString stringWithFormat:@"Frames %d through %d", [[item objectForKey:@"StartFrame"] intValue], [[item objectForKey:@"StartFrame"] intValue] + [[item objectForKey:@"StopFrame"] intValue]];
1013         }
1014         
1015         NSString * passesString = @"";
1016         /* check to see if our first subtitle track is Foreign Language Search, in which case there is an in depth scan */
1017         if ([item objectForKey:@"SubtitleList"] && [[[[item objectForKey:@"SubtitleList"] objectAtIndex:0] objectForKey:@"subtitleSourceTrackNum"] intValue] == 1)
1018         {
1019           passesString = [passesString stringByAppendingString:@"1 Foreign Language Search Pass - "];
1020         }
1021         if ([[item objectForKey:@"VideoTwoPass"] intValue] == 0)
1022         {
1023             passesString = [passesString stringByAppendingString:@"1 Video Pass"];
1024         }
1025         else
1026         {
1027             if ([[item objectForKey:@"VideoTurboTwoPass"] intValue] == 1)
1028             {
1029                 passesString = [passesString stringByAppendingString:@"2 Video Passes First Turbo"];
1030             }
1031             else
1032             {
1033                 passesString = [passesString stringByAppendingString:@"2 Video Passes"];
1034             }
1035         }
1036         
1037         [finalString appendString:[NSString stringWithFormat:@"%@", [item objectForKey:@"SourceName"]] withAttributes:titleAttr];
1038         
1039         /* lets add the output file name to the title string here */
1040         NSString * outputFilenameString = [[item objectForKey:@"DestinationPath"] lastPathComponent];
1041         
1042         summaryInfo = [NSString stringWithFormat: @" (%@, %@, %@) -> %@", titleString, startStopString, passesString, outputFilenameString];
1043         
1044         [finalString appendString:[NSString stringWithFormat:@"%@\n", summaryInfo] withAttributes:detailAttr];  
1045         
1046         // Insert a short-in-height line to put some white space after the title
1047         [finalString appendString:@"\n" withAttributes:shortHeightAttr];
1048         // End of Title Stuff
1049         
1050         /* Second Line  (Preset Name)*/
1051         [finalString appendString: @"Preset: " withAttributes:detailBoldAttr];
1052         [finalString appendString:[NSString stringWithFormat:@"%@\n", [item objectForKey:@"PresetName"]] withAttributes:detailAttr];
1053         
1054         /* Third Line  (Format Summary) */
1055         NSString * audioCodecSummary = @"";     //      This seems to be set by the last track we have available...
1056         /* Lets also get our audio track detail since we are going through the logic for use later */
1057                 unsigned int ourMaximumNumberOfAudioTracks = [HBController maximumNumberOfAllowedAudioTracks];
1058                 NSMutableArray *audioDetails = [NSMutableArray arrayWithCapacity: ourMaximumNumberOfAudioTracks];
1059                 NSString *base;
1060                 NSString *detailString;
1061                 NSNumber *drc;
1062                 for (unsigned int i = 1; i <= ourMaximumNumberOfAudioTracks; i++) {
1063                         base = [NSString stringWithFormat: @"Audio%d", i];
1064                         if (0 < [[item objectForKey: [base stringByAppendingString: @"Track"]] intValue]) {
1065                                 audioCodecSummary = [NSString stringWithFormat: @"%@", [item objectForKey: [base stringByAppendingString: @"Encoder"]]];
1066                                 drc = [item objectForKey: [base stringByAppendingString: @"TrackDRCSlider"]];
1067                                 detailString = [NSString stringWithFormat: @"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps), DRC: %@",
1068                                                                 [item objectForKey: [base stringByAppendingString: @"TrackDescription"]],
1069                                                                 [item objectForKey: [base stringByAppendingString: @"Encoder"]],
1070                                                                 [item objectForKey: [base stringByAppendingString: @"Mixdown"]],
1071                                                                 [item objectForKey: [base stringByAppendingString: @"Samplerate"]],
1072                                                                 [item objectForKey: [base stringByAppendingString: @"Bitrate"]],
1073                                                                 (0.0 < [drc floatValue]) ? drc : @"Off"
1074                                                                 ];
1075                                 [audioDetails addObject: detailString];
1076                         }
1077                 }
1078         
1079         
1080         NSString * jobFormatInfo;
1081         if ([[item objectForKey:@"ChapterMarkers"] intValue] == 1)
1082             jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video  %@ Audio, Chapter Markers\n", [item objectForKey:@"FileFormat"], [item objectForKey:@"VideoEncoder"], audioCodecSummary];
1083         else
1084             jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video  %@ Audio\n", [item objectForKey:@"FileFormat"], [item objectForKey:@"VideoEncoder"], audioCodecSummary];
1085         
1086         
1087         [finalString appendString: @"Format: " withAttributes:detailBoldAttr];
1088         [finalString appendString: jobFormatInfo withAttributes:detailAttr];
1089         
1090         /* Optional String for mp4 options */
1091         if ([[item objectForKey:@"FileFormat"] isEqualToString: @"MP4 file"])
1092         {
1093             NSString * MP4Opts = @"";
1094             BOOL mp4OptsPresent = NO;
1095             if( [[item objectForKey:@"Mp4LargeFile"] intValue] == 1)
1096             {
1097                 mp4OptsPresent = YES;
1098                 MP4Opts = [MP4Opts stringByAppendingString:@" - Large file size"];
1099             }
1100             if( [[item objectForKey:@"Mp4HttpOptimize"] intValue] == 1)
1101             {
1102                 mp4OptsPresent = YES;
1103                 MP4Opts = [MP4Opts stringByAppendingString:@" - Web optimized"];
1104             }
1105             
1106             if( [[item objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
1107             {
1108                 mp4OptsPresent = YES;
1109                 MP4Opts = [MP4Opts stringByAppendingString:@" - iPod 5G support "];
1110             }
1111             if (mp4OptsPresent == YES)
1112             {
1113                 [finalString appendString: @"MP4 Options: " withAttributes:detailBoldAttr];
1114                 [finalString appendString: MP4Opts withAttributes:detailAttr];
1115                 [finalString appendString:@"\n" withAttributes:detailAttr];
1116             }
1117         }
1118         
1119         /* Fourth Line (Destination Path)*/
1120         [finalString appendString: @"Destination: " withAttributes:detailBoldAttr];
1121         [finalString appendString: [item objectForKey:@"DestinationPath"] withAttributes:detailAttr];
1122         [finalString appendString:@"\n" withAttributes:detailAttr];
1123         
1124         /* Fifth Line Picture Details*/
1125         NSString * pictureInfo;
1126         pictureInfo = [NSString stringWithFormat:@"%@", [item objectForKey:@"PictureSizingSummary"]];
1127         if ([[item objectForKey:@"PictureKeepRatio"] intValue] == 1)
1128         {
1129             pictureInfo = [pictureInfo stringByAppendingString:@" Keep Aspect Ratio"];
1130         }
1131         
1132         if ([[item objectForKey:@"VideoGrayScale"] intValue] == 1)
1133         {
1134             pictureInfo = [pictureInfo stringByAppendingString:@", Grayscale"];
1135         }
1136         
1137         [finalString appendString: @"Picture: " withAttributes:detailBoldAttr];
1138         [finalString appendString: pictureInfo withAttributes:detailAttr];
1139         [finalString appendString:@"\n" withAttributes:detailAttr];
1140         
1141         /* Optional String for Picture Filters */
1142         
1143         NSString * pictureFilters = @"";
1144         BOOL pictureFiltersPresent = NO;
1145         
1146         if( [[item objectForKey:@"PictureDetelecine"] intValue] == 1)
1147         {
1148             pictureFiltersPresent = YES;
1149             pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[item objectForKey:@"PictureDetelecineCustom"]]];
1150         }
1151         else if( [[item objectForKey:@"PictureDetelecine"] intValue] == 2)
1152         {
1153             pictureFiltersPresent = YES;
1154             pictureFilters = [pictureFilters stringByAppendingString:@" - Detelecine (Default)"];
1155         }
1156         
1157         if( [[item objectForKey:@"PictureDecombDeinterlace"] intValue] == 1)
1158         {
1159             if ([[item objectForKey:@"PictureDecomb"] intValue] != 0)
1160             {
1161                 pictureFiltersPresent = YES;
1162                 if( [[item objectForKey:@"PictureDecomb"] intValue] == 1)
1163                 {
1164                     pictureFiltersPresent = YES;
1165                     pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[item objectForKey:@"PictureDecombCustom"]]];
1166                 }
1167                 else if( [[item objectForKey:@"PictureDecomb"] intValue] == 2)
1168                 {
1169                     pictureFiltersPresent = YES;
1170                     pictureFilters = [pictureFilters stringByAppendingString:@" - Decomb (Default)"];
1171                 }
1172             }
1173         }
1174         else
1175         {
1176             if ([[item objectForKey:@"PictureDeinterlace"] intValue] != 0)
1177             {
1178                 pictureFiltersPresent = YES;
1179                 if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 1)
1180                 {
1181                     pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[item objectForKey:@"PictureDeinterlaceCustom"]]];            
1182                 }
1183                 else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 2)
1184                 {
1185                     pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Fast)"];
1186                 }
1187                 else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 3)
1188                 {
1189                     pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Slow)"];           
1190                 }
1191                 else if ([[item objectForKey:@"PictureDeinterlace"] intValue] == 4)
1192                 {
1193                     pictureFilters = [pictureFilters stringByAppendingString:@" - Deinterlace (Slower)"];            
1194                 }
1195                 
1196             }
1197         }
1198         if ([[item objectForKey:@"PictureDenoise"] intValue] != 0)
1199         {
1200             pictureFiltersPresent = YES;
1201             if ([[item objectForKey:@"PictureDenoise"] intValue] == 1)
1202             {
1203                 pictureFilters = [pictureFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[item objectForKey:@"PictureDenoiseCustom"]]];            
1204             }
1205             else if ([[item objectForKey:@"PictureDenoise"] intValue] == 2)
1206             {
1207                 pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Weak)"];
1208             }
1209             else if ([[item objectForKey:@"PictureDenoise"] intValue] == 3)
1210             {
1211                 pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Medium)"];           
1212             }
1213             else if ([[item objectForKey:@"PictureDenoise"] intValue] == 4)
1214             {
1215                 pictureFilters = [pictureFilters stringByAppendingString:@" - Denoise (Strong)"];            
1216             }
1217             
1218         }
1219         if ([[item objectForKey:@"PictureDeblock"] intValue] != 0)
1220         {
1221             pictureFiltersPresent = YES;
1222             pictureFilters = [pictureFilters stringByAppendingString: [NSString stringWithFormat:@" - Deblock (pp7) (%d)",[[item objectForKey:@"PictureDeblock"] intValue]]];
1223         }
1224         
1225         if ([[item objectForKey:@"VideoGrayScale"] intValue] == 1)
1226         {
1227             pictureFiltersPresent = YES;
1228             pictureFilters = [pictureFilters stringByAppendingString:@" - Grayscale"];
1229         }
1230         
1231         if (pictureFiltersPresent == YES)
1232         {
1233             [finalString appendString: @"Filters: " withAttributes:detailBoldAttr];
1234             [finalString appendString: pictureFilters withAttributes:detailAttr];
1235             [finalString appendString:@"\n" withAttributes:detailAttr];
1236         }
1237         
1238         /* Sixth Line Video Details*/
1239         NSString * videoInfo;
1240         videoInfo = [NSString stringWithFormat:@"Encoder: %@", [item objectForKey:@"VideoEncoder"]];
1241         
1242         /* for framerate look to see if we are using vfr detelecine */
1243         if ([[item objectForKey:@"JobIndexVideoFramerate"] intValue] == 0)
1244         {
1245             if ([[item objectForKey:@"PictureDetelecine"] intValue] == 1)
1246             {
1247                 /* we are using same as source with vfr detelecine */
1248                 videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (vfr detelecine)", videoInfo];
1249             }
1250             else
1251             {
1252                 /* we are using a variable framerate without dropping frames */
1253                 videoInfo = [NSString stringWithFormat:@"%@ Framerate: Same as source (variable)", videoInfo];
1254             }
1255         }
1256         else
1257         {
1258             /* we have a specified, constant framerate */
1259             if ([[item objectForKey:@"VideoFrameratePFR"] intValue] == 1)
1260             {
1261             videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@ (peak framerate)", videoInfo ,[item objectForKey:@"VideoFramerate"]];
1262             }
1263             else
1264             {
1265             videoInfo = [NSString stringWithFormat:@"%@ Framerate: %@ (constant framerate)", videoInfo ,[item objectForKey:@"VideoFramerate"]];
1266             }
1267         }
1268         
1269         if ([[item objectForKey:@"VideoQualityType"] intValue] == 0)// Target Size MB
1270         {
1271             videoInfo = [NSString stringWithFormat:@"%@ Target Size: %@(MB) (%d(kbps) abr)", videoInfo ,[item objectForKey:@"VideoTargetSize"],[[item objectForKey:@"VideoAvgBitrate"] intValue]];
1272         }
1273         else if ([[item objectForKey:@"VideoQualityType"] intValue] == 1) // ABR
1274         {
1275             videoInfo = [NSString stringWithFormat:@"%@ Bitrate: %d(kbps)", videoInfo ,[[item objectForKey:@"VideoAvgBitrate"] intValue]];
1276         }
1277         else // CRF
1278         {
1279             videoInfo = [NSString stringWithFormat:@"%@ Constant Quality: %.2f", videoInfo ,[[item objectForKey:@"VideoQualitySlider"] floatValue]];
1280         }
1281         
1282         [finalString appendString: @"Video: " withAttributes:detailBoldAttr];
1283         [finalString appendString: videoInfo withAttributes:detailAttr];
1284         [finalString appendString:@"\n" withAttributes:detailAttr];
1285         
1286         if ([[item objectForKey:@"VideoEncoder"] isEqualToString: @"H.264 (x264)"])
1287         {
1288             [finalString appendString: @"x264 Options: " withAttributes:detailBoldAttr];
1289             [finalString appendString: [item objectForKey:@"x264Option"] withAttributes:detailAttr];
1290             [finalString appendString:@"\n" withAttributes:detailAttr];
1291         }
1292         
1293         
1294         
1295         /* Seventh Line Audio Details*/
1296         NSEnumerator *audioDetailEnumerator = [audioDetails objectEnumerator];
1297                 NSString *anAudioDetail;
1298                 int audioDetailCount = 0;
1299                 while (nil != (anAudioDetail = [audioDetailEnumerator nextObject])) {
1300                         audioDetailCount++;
1301                         if (0 < [anAudioDetail length]) {
1302                                 [finalString appendString: [NSString stringWithFormat: @"Audio Track %d ", audioDetailCount] withAttributes: detailBoldAttr];
1303                                 [finalString appendString: anAudioDetail withAttributes: detailAttr];
1304                                 [finalString appendString: @"\n" withAttributes: detailAttr];
1305                         }
1306                 }
1307
1308         /* Eighth Line Subtitle Details */
1309         
1310         int i = 0;
1311         NSEnumerator *enumerator = [[item objectForKey:@"SubtitleList"] objectEnumerator];
1312         id tempObject;
1313         while (tempObject = [enumerator nextObject])
1314         {
1315             /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
1316              * we want to ignore it for display as well as encoding.
1317              */
1318             if ([[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue] > 0)
1319             { 
1320                 /* remember that index 0 of Subtitles can contain "Foreign Audio Search*/
1321                 [finalString appendString: @"Subtitle: " withAttributes:detailBoldAttr];
1322                 [finalString appendString: [tempObject objectForKey:@"subtitleSourceTrackName"] withAttributes:detailAttr];
1323                 if ([[tempObject objectForKey:@"subtitleTrackForced"] intValue] == 1)
1324                 {
1325                     [finalString appendString: @" - Forced Only" withAttributes:detailAttr];
1326                 }
1327                 if ([[tempObject objectForKey:@"subtitleTrackBurned"] intValue] == 1)
1328                 {
1329                     [finalString appendString: @" - Burned In" withAttributes:detailAttr];
1330                 }
1331                 if ([[tempObject objectForKey:@"subtitleTrackDefault"] intValue] == 1)
1332                 {
1333                     [finalString appendString: @" - Default" withAttributes:detailAttr];
1334                 }
1335                 [finalString appendString:@"\n" withAttributes:detailAttr];
1336             }
1337             i++;
1338         }      
1339         
1340         return finalString;
1341     }
1342     else if ([[tableColumn identifier] isEqualToString:@"icon"])
1343     {
1344         if ([[item objectForKey:@"Status"] intValue] == 0)
1345         {
1346             return [NSImage imageNamed:@"EncodeComplete"];
1347         }
1348         else if ([[item objectForKey:@"Status"] intValue] == 1)
1349         {
1350             return [NSImage imageNamed: [NSString stringWithFormat: @"EncodeWorking%d", fAnimationIndex]];
1351         }
1352         else if ([[item objectForKey:@"Status"] intValue] == 3)
1353         {
1354             return [NSImage imageNamed:@"EncodeCanceled"];
1355         }
1356         else
1357         {
1358             return [NSImage imageNamed:@"JobSmall"];
1359         }
1360         
1361     }
1362     else
1363     {
1364         return @"";
1365     }
1366 }
1367 /* This method inserts the proper action icons into the far right of the queue window */
1368 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
1369 {
1370     if ([[tableColumn identifier] isEqualToString:@"desc"])
1371     {
1372
1373
1374         // nb: The "desc" column is currently an HBImageAndTextCell. However, we are longer
1375         // using the image portion of the cell so we could switch back to a regular NSTextFieldCell.
1376
1377         // Set the image here since the value returned from outlineView:objectValueForTableColumn: didn't specify the image part
1378         [cell setImage:nil];
1379     }
1380     else if ([[tableColumn identifier] isEqualToString:@"action"])
1381     {
1382         [cell setEnabled: YES];
1383         BOOL highlighted = [outlineView isRowSelected:[outlineView rowForItem: item]] && [[outlineView window] isKeyWindow] && ([[outlineView window] firstResponder] == outlineView);
1384         
1385         if ([[item objectForKey:@"Status"] intValue] == 0 || ([[item objectForKey:@"Status"] intValue] == 1 && [[item objectForKey:@"EncodingPID"] intValue] != pidNum))
1386         {
1387             [cell setAction: @selector(revealSelectedQueueItem:)];
1388             if (highlighted)
1389             {
1390                 [cell setImage:[NSImage imageNamed:@"RevealHighlight"]];
1391                 [cell setAlternateImage:[NSImage imageNamed:@"RevealHighlightPressed"]];
1392             }
1393             else
1394                 [cell setImage:[NSImage imageNamed:@"Reveal"]];
1395         }
1396         else
1397         {
1398             
1399                 [cell setAction: @selector(removeSelectedQueueItem:)];
1400                 if (highlighted)
1401                 {
1402                     [cell setImage:[NSImage imageNamed:@"DeleteHighlight"]];
1403                     [cell setAlternateImage:[NSImage imageNamed:@"DeleteHighlightPressed"]];
1404                 }
1405                 else
1406                     [cell setImage:[NSImage imageNamed:@"Delete"]];
1407    
1408         }
1409     }
1410 }
1411
1412 - (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
1413 {
1414     // By default, the disclosure image gets centered vertically in the cell. We want
1415     // always at the top.
1416     if ([outlineView isItemExpanded: item])
1417         [cell setImagePosition: NSImageAbove];
1418     else
1419         [cell setImagePosition: NSImageOnly];
1420 }
1421
1422 #pragma mark -
1423 #pragma mark NSOutlineView delegate (dragging related)
1424
1425 //------------------------------------------------------------------------------------
1426 // NSTableView delegate
1427 //------------------------------------------------------------------------------------
1428
1429
1430 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1431 {
1432     // Dragging is only allowed of the pending items.
1433     if ([[[items objectAtIndex:0] objectForKey:@"Status"] integerValue] != 2) // 2 is pending
1434     {
1435         return NO;
1436     }
1437     
1438     // Don't retain since this is just holding temporaral drag information, and it is
1439     //only used during a drag!  We could put this in the pboard actually.
1440     fDraggedNodes = items;
1441     
1442     // Provide data for our custom type, and simple NSStrings.
1443     [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
1444     
1445     // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
1446     [pboard setData:[NSData data] forType:DragDropSimplePboardType];
1447     
1448     return YES;
1449 }
1450
1451
1452 /* This method is used to validate the drops. */
1453 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
1454 {
1455     // Don't allow dropping ONTO an item since they can't really contain any children.
1456     BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
1457     if (isOnDropTypeProposal)
1458     {
1459         return NSDragOperationNone;
1460     }
1461     
1462     // Don't allow dropping INTO an item since they can't really contain any children.
1463     if (item != nil)
1464     {
1465         index = [fOutlineView rowForItem: item] + 1;
1466         item = nil;
1467     }
1468     
1469     // NOTE: Should we allow dropping a pending job *above* the
1470     // finished or already encoded jobs ?
1471     // We do not let the user drop a pending job before or *above*
1472     // already finished or currently encoding jobs.
1473     if (index <= fEncodingQueueItem)
1474     {
1475         return NSDragOperationNone;
1476         index = MAX (index, fEncodingQueueItem);
1477         }
1478     
1479     [outlineView setDropItem:item dropChildIndex:index];
1480     return NSDragOperationGeneric;
1481 }
1482
1483 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
1484 {
1485     NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
1486
1487     for( id obj in fDraggedNodes )
1488         [moveItems addIndex:[fJobGroups indexOfObject:obj]];
1489
1490     // Successful drop, we use moveObjectsInQueueArray:... in fHBController
1491     // to properly rearrange the queue array, save it to plist and then send it back here.
1492     // since Controller.mm is handling all queue array manipulation.
1493     // We *could do this here, but I think we are better served keeping that code together.
1494     [fHBController moveObjectsInQueueArray:fJobGroups fromIndexes:moveItems toIndex: index];
1495     return YES;
1496 }
1497
1498
1499 @end