OSDN Git Service

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