OSDN Git Service

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