OSDN Git Service

MacGui: Hopefully this time the option hiding code for the advanced x264 tab actually...
[handbrake-jp/handbrake-jp-git.git] / macosx / HBAdvancedController.m
1 /* HBAdvancedController
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 "HBAdvancedController.h"
8
9 @implementation HBAdvancedController
10
11 - (id)init
12 {
13     [super init];
14     [self loadMyNibFile];
15     
16     return self;
17 }
18
19 - (void) setView: (NSBox *) box
20 {
21     fOptionsBox = box;
22     [fOptionsBox setContentView:fX264optView];
23 }
24
25 - (BOOL) loadMyNibFile
26 {
27     if(![NSBundle loadNibNamed:@"AdvancedView" owner:self])
28     {
29         NSLog(@"Warning! Could not load myNib file.\n");
30         return NO;
31     }
32     
33     return YES;
34 }
35
36 - (NSString *) optionsString
37 {
38     return [fDisplayX264Options stringValue];
39 }
40
41 - (void) setOptions: (NSString *)string
42 {
43     [fDisplayX264Options setStringValue:string];
44     [self X264AdvancedOptionsSet:nil];
45 }
46
47 - (void) setHidden: (BOOL) hide
48 {
49     if(hide)
50     {
51         [fOptionsBox setContentView:fEmptyView];
52         [fX264optViewTitleLabel setStringValue: @"Only Used With The x264 (H.264) Codec"];
53     }
54     else
55     {
56         [fOptionsBox setContentView:fX264optView];
57         [fX264optViewTitleLabel setStringValue: @""];
58     }
59     return;
60 }
61
62  - (void) enableUI: (bool) b
63 {
64     unsigned i;
65     NSControl * controls[] =
66       { fX264optViewTitleLabel,fDisplayX264Options,fDisplayX264OptionsLabel,fX264optBframesLabel,
67         fX264optBframesPopUp,fX264optRefLabel,fX264optRefPopUp,fX264optNfpskipLabel,fX264optNfpskipSwitch,
68         fX264optNodctdcmtLabel,fX264optNodctdcmtSwitch,fX264optSubmeLabel,fX264optSubmePopUp,
69         fX264optTrellisLabel,fX264optTrellisPopUp,fX264optMixedRefsLabel,fX264optMixedRefsSwitch,
70         fX264optMotionEstLabel,fX264optMotionEstPopUp,fX264optMERangeLabel,fX264optMERangePopUp,
71         fX264optWeightBLabel,fX264optWeightBSwitch, fX264optBPyramidLabel,fX264optBPyramidSwitch,
72         fX264optDirectPredLabel,fX264optDirectPredPopUp,fX264optDeblockLabel,fX264optAnalyseLabel,
73         fX264optAnalysePopUp,fX264opt8x8dctLabel,fX264opt8x8dctSwitch,fX264optCabacLabel,fX264optCabacSwitch,
74         fX264optAlphaDeblockPopUp,fX264optBetaDeblockPopUp, fX264optPsyRDSlider, fX264optPsyRDLabel, fX264optPsyTrellisSlider, fX264optPsyTrellisLabel, fX264optBAdaptPopUp, fX264optBAdaptLabel };
75
76     for( i = 0; i < sizeof( controls ) / sizeof( NSControl * ); i++ )
77     {
78         if( [[controls[i] className] isEqualToString: @"NSTextField"] )
79         {
80             NSTextField * tf = (NSTextField *) controls[i];
81             if( ![tf isBezeled] )
82             {
83                 [tf setTextColor: b ? [NSColor controlTextColor] :
84                     [NSColor disabledControlTextColor]];
85                 continue;
86             }
87         }
88         [controls[i] setEnabled: b];
89
90     }
91     
92     [fX264optView setWantsLayer:YES];
93 }
94
95 - (void)dealloc
96 {
97     [super dealloc];
98 }
99
100 /**
101  * Populates the option widgets
102  */
103 - (IBAction) X264AdvancedOptionsSet: (id) sender
104 {
105     /*Set opt widget values here*/
106     
107     /*B-Frames fX264optBframesPopUp*/
108     int i;
109     [fX264optBframesPopUp removeAllItems];
110     [fX264optBframesPopUp addItemWithTitle:@"Default (0)"];
111     for (i=0; i<17;i++)
112     {
113         [fX264optBframesPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
114     }
115     
116     /*Reference Frames fX264optRefPopUp*/
117     [fX264optRefPopUp removeAllItems];
118     [fX264optRefPopUp addItemWithTitle:@"Default (1)"];
119     for (i=0; i<17;i++)
120     {
121         [fX264optRefPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
122     }
123     
124     /*No Fast P-Skip fX264optNfpskipSwitch BOOLEAN*/
125     [fX264optNfpskipSwitch setState:0];
126     
127     /*No Dict Decimate fX264optNodctdcmtSwitch BOOLEAN*/
128     [fX264optNodctdcmtSwitch setState:0];    
129     
130     /*Sub Me fX264optSubmePopUp*/
131     [fX264optSubmePopUp removeAllItems];
132     [fX264optSubmePopUp addItemWithTitle:@"Default (6)"];
133     for (i=0; i<10;i++)
134     {
135         [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
136     }
137     
138     /*Trellis fX264optTrellisPopUp*/
139     [fX264optTrellisPopUp removeAllItems];
140     [fX264optTrellisPopUp addItemWithTitle:@"Default (0)"];
141     for (i=0; i<3;i++)
142     {
143         [fX264optTrellisPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
144     }
145     [fX264optTrellisPopUp setWantsLayer:YES];
146     
147     /*Mixed-references fX264optMixedRefsSwitch BOOLEAN*/
148     [fX264optMixedRefsSwitch setState:0];
149     [fX264optMixedRefsSwitch setWantsLayer:YES];
150     
151     /*Motion Estimation fX264optMotionEstPopUp*/
152     [fX264optMotionEstPopUp removeAllItems];
153     [fX264optMotionEstPopUp addItemWithTitle:@"Default (Hexagon)"];
154     [fX264optMotionEstPopUp addItemWithTitle:@"Diamond"];
155     [fX264optMotionEstPopUp addItemWithTitle:@"Hexagon"];
156     [fX264optMotionEstPopUp addItemWithTitle:@"Uneven Multi-Hexagon"];
157     [fX264optMotionEstPopUp addItemWithTitle:@"Exhaustive"];
158     [fX264optMotionEstPopUp addItemWithTitle:@"Transformed Exhaustive"];
159     
160     /*Motion Estimation range fX264optMERangePopUp*/
161     [fX264optMERangePopUp removeAllItems];
162     [fX264optMERangePopUp addItemWithTitle:@"Default (16)"];
163     for (i=4; i<65;i++)
164     {
165         [fX264optMERangePopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
166     }
167     
168     /*Weighted B-Frame Prediction fX264optWeightBSwitch BOOLEAN*/
169     [fX264optWeightBSwitch setState:0];
170     [fX264optWeightBSwitch setWantsLayer:YES];
171     
172     /*B-frame Pyramids fX264optBPyramidSwitch BOOLEAN*/
173     [fX264optBPyramidSwitch setState:0];
174     [fX264optBPyramidSwitch setWantsLayer:YES];
175     
176     /*Direct B-Frame Prediction Mode fX264optDirectPredPopUp*/
177     [fX264optDirectPredPopUp removeAllItems];
178     [fX264optDirectPredPopUp addItemWithTitle:@"Default (Spatial)"];
179     [fX264optDirectPredPopUp addItemWithTitle:@"None"];
180     [fX264optDirectPredPopUp addItemWithTitle:@"Spatial"];
181     [fX264optDirectPredPopUp addItemWithTitle:@"Temporal"];
182     [fX264optDirectPredPopUp addItemWithTitle:@"Automatic"];
183     [fX264optDirectPredPopUp setWantsLayer:YES];
184     
185     /* Adaptive B-Frames Mode fX264optBAdaptPopUp */
186     [fX264optBAdaptPopUp removeAllItems];
187     [fX264optBAdaptPopUp addItemWithTitle:@"Default (1)"];
188     [fX264optBAdaptPopUp addItemWithTitle:@"0"];
189     [fX264optBAdaptPopUp addItemWithTitle:@"1"];
190     [fX264optBAdaptPopUp addItemWithTitle:@"2"];
191     [fX264optBAdaptPopUp setWantsLayer:YES];
192     
193     /*Alpha Deblock*/
194     [fX264optAlphaDeblockPopUp removeAllItems];
195     [fX264optAlphaDeblockPopUp addItemWithTitle:@"Default (0)"];
196     for (i=-6; i<7;i++)
197     {
198         [fX264optAlphaDeblockPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
199     }
200
201     /*Beta Deblock*/
202     [fX264optBetaDeblockPopUp removeAllItems];
203     [fX264optBetaDeblockPopUp addItemWithTitle:@"Default (0)"];
204     for (i=-6; i<7;i++)
205     {
206         [fX264optBetaDeblockPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
207     }
208
209     /* Analysis fX264optAnalysePopUp */
210     [fX264optAnalysePopUp removeAllItems];
211     [fX264optAnalysePopUp addItemWithTitle:@"Default (some)"]; /* 0=default */
212     [fX264optAnalysePopUp addItemWithTitle:[NSString stringWithFormat:@"None"]]; /* 1=none */
213     [fX264optAnalysePopUp addItemWithTitle:[NSString stringWithFormat:@"All"]]; /* 2=all */
214
215     /* 8x8 DCT fX264op8x8dctSwitch */
216     [fX264opt8x8dctSwitch setState:0];
217     [fX264opt8x8dctSwitch setWantsLayer:YES];
218
219     /* CABAC fX264opCabacSwitch */
220     [fX264optCabacSwitch setState:1];
221     
222     /* PsyRDO fX264optPsyRDSlider */
223     [fX264optPsyRDSlider setMinValue:0.0];
224     [fX264optPsyRDSlider setMaxValue:1.0];
225     [fX264optPsyRDSlider setTickMarkPosition:NSTickMarkBelow];
226     [fX264optPsyRDSlider setNumberOfTickMarks:10];
227     [fX264optPsyRDSlider setAllowsTickMarkValuesOnly:YES];
228     [fX264optPsyRDSlider setFloatValue:1.0];
229
230     /* PsyTrellis fX264optPsyRDSlider */
231     [fX264optPsyTrellisSlider setMinValue:0.0];
232     [fX264optPsyTrellisSlider setMaxValue:1.0];
233     [fX264optPsyTrellisSlider setTickMarkPosition:NSTickMarkBelow];
234     [fX264optPsyTrellisSlider setNumberOfTickMarks:10];
235     [fX264optPsyTrellisSlider setAllowsTickMarkValuesOnly:YES];
236     [fX264optPsyTrellisSlider setFloatValue:0.0];
237
238     /* Standardize the option string */
239     [self X264AdvancedOptionsStandardizeOptString:nil];
240
241     /* Set Current GUI Settings based on newly standardized string */
242     [self X264AdvancedOptionsSetCurrentSettings:sender];
243
244     /* Fade out options that don't apply */
245     [self X264AdvancedOptionsAnimate: sender];
246 }
247
248 /**
249  * Cleans the option string to use a standard format of option=value
250  */
251 - (IBAction) X264AdvancedOptionsStandardizeOptString: (id) sender
252 {
253     /* Set widgets depending on the opt string in field */
254     NSString * thisOpt; // The separated option such as "bframes=3"
255     NSString * optName = @""; // The option name such as "bframes"
256     NSString * optValue = @"";// The option value such as "3"
257     NSString * changedOptString = @"";
258     NSArray *currentOptsArray;
259     
260     /*First, we get an opt string to process */
261     NSString *currentOptString = [fDisplayX264Options stringValue];
262     
263     /* Verify there is an opt string to process by making sure an
264        option is getting its value set. If so, start to process it. */
265     NSRange currentOptRange = [currentOptString rangeOfString:@"="];
266     if (currentOptRange.location != NSNotFound)
267     {
268         /*Put individual options into an array based on the ":" separator for processing, result is "<opt>=<value>"*/
269         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
270         
271         /*iterate through the array and get <opts> and <values*/
272         int loopcounter;
273         int currentOptsArrayCount = [currentOptsArray count];
274         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
275         {
276             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
277             
278             NSRange splitOptRange = [thisOpt rangeOfString:@"="];
279             if (splitOptRange.location != NSNotFound)
280             {
281                 optName = [thisOpt substringToIndex:splitOptRange.location];
282                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
283                 
284                 /* Standardize the names here depending on whats in the string */
285                 optName = [self X264AdvancedOptionsStandardizeOptNames:optName];
286                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,optValue];    
287             }
288             else // No value given so we use a default of "1"
289             {
290                 optName = thisOpt;
291
292                 /* Standardize the names here depending on whats in the string */
293                 optName = [self X264AdvancedOptionsStandardizeOptNames:optName];
294                 thisOpt = [NSString stringWithFormat:@"%@=%d",optName,1];
295             }
296             
297             /* Construct New String for opts here.*/
298             if ([thisOpt isEqualToString:@""])
299             {
300                 /* Blank option, just add it to the string. (Why?) */
301                 changedOptString = [NSString stringWithFormat:@"%@%@",changedOptString,thisOpt];
302             }
303             else
304             {
305                 if ([changedOptString isEqualToString:@""])
306                 {
307                     /* Blank string, output the current option. */
308                     changedOptString = [NSString stringWithFormat:@"%@",thisOpt];
309                 }
310                 else
311                 {
312                     /* Option exists and string exists, so append the option
313                        to the string with a semi-colon inbetween them.       */
314                     changedOptString = [NSString stringWithFormat:@"%@:%@",changedOptString,thisOpt];
315                 }
316             }
317         }
318     }
319     
320     /* Change the option string to reflect the new standardized option string */
321     [fDisplayX264Options setStringValue:[NSString stringWithFormat:changedOptString]];
322 }
323
324 /**
325  * Cleans the option string to use a standard set of option names, by conflating synonyms.
326  */
327 - (NSString *) X264AdvancedOptionsStandardizeOptNames:(NSString *) cleanOptNameString
328 {
329     /* Reference Frames */
330     if ([cleanOptNameString isEqualToString:@"ref"] || [cleanOptNameString isEqualToString:@"frameref"])
331     {
332         cleanOptNameString = @"ref";
333     }
334     
335     /*No Fast PSkip nofast_pskip*/
336     if ([cleanOptNameString isEqualToString:@"no-fast-pskip"] || [cleanOptNameString isEqualToString:@"no_fast_pskip"] || [cleanOptNameString isEqualToString:@"nofast_pskip"])
337     {
338         cleanOptNameString = @"no-fast-pskip";
339     }
340     
341     /*No Dict Decimate*/
342     if ([cleanOptNameString isEqualToString:@"no-dct-decimate"] || [cleanOptNameString isEqualToString:@"no_dct_decimate"] || [cleanOptNameString isEqualToString:@"nodct_decimate"])
343     {
344         cleanOptNameString = @"no-dct-decimate";
345     }
346     
347     /*Subme*/
348     if ([cleanOptNameString isEqualToString:@"subme"])
349     {
350         cleanOptNameString = @"subq";
351     }
352     
353     /*ME Range*/
354     if ([cleanOptNameString isEqualToString:@"me-range"] || [cleanOptNameString isEqualToString:@"me_range"])
355         cleanOptNameString = @"merange";
356     
357     /*WeightB*/
358     if ([cleanOptNameString isEqualToString:@"weight-b"] || [cleanOptNameString isEqualToString:@"weight_b"])
359     {
360         cleanOptNameString = @"weightb";
361     }
362     
363     /*B Pyramid*/
364     if ([cleanOptNameString isEqualToString:@"b_pyramid"])
365     {
366         cleanOptNameString = @"b-pyramid";
367     }
368     
369     /*Direct Prediction*/
370     if ([cleanOptNameString isEqualToString:@"direct-pred"] || [cleanOptNameString isEqualToString:@"direct_pred"])
371     {
372         cleanOptNameString = @"direct";
373     }
374     
375     /*Deblocking*/
376     if ([cleanOptNameString isEqualToString:@"filter"])
377     {
378         cleanOptNameString = @"deblock";
379     }
380     
381     /*Analysis*/
382     if ([cleanOptNameString isEqualToString:@"partitions"])
383     {
384         cleanOptNameString = @"analyse";
385     }
386     
387     return cleanOptNameString;    
388 }
389
390 /**
391  * Fades options in and out depending on whether they're available..
392  */
393 - (IBAction) X264AdvancedOptionsAnimate: (id) sender
394 {
395     /* Lots of situations to cover.
396        - B-frames (when 0 turn of b-frame specific stuff, when < 2 disable b-pyramid)
397        - CABAC (when 0 turn off trellis)
398        - analysis (if none, turn off 8x8dct)
399        - refs (under 2, disable mixed-refs)
400        - subme (if under 6, turn off psy-rd and psy-trel)
401        - trellis (if 0, turn off psy-trel)
402     */
403     
404     if( sender == fX264optBframesPopUp || sender == nil || sender == fDisplayX264Options )
405     {
406         if( sender == nil )
407             fprintf(stderr, "Bframes sender is nil!\n");
408         else if( sender == fDisplayX264Options )
409             fprintf(stderr, "Bframes sender is text box!\n");
410             
411         if ( [fX264optBframesPopUp indexOfSelectedItem ] < 2)
412         {
413             /* If the b-frame widget is at 0 or 1, the user has chosen
414                not to use b-frames at all. So disable the options
415                that can only be used when b-frames are enabled.        */
416             
417             if( [fX264optWeightBSwitch isHidden] == false)
418             {
419                 [[fX264optWeightBSwitch animator] setHidden:YES];
420                 [[fX264optWeightBLabel animator] setHidden:YES];
421                 if ( [fX264optWeightBSwitch state] == 1 )
422                     [fX264optWeightBSwitch performClick:self];
423             }
424
425             if( [fX264optBPyramidSwitch isHidden] == false )
426             {
427                 [[fX264optBPyramidSwitch animator] setHidden:YES];
428                 [[fX264optBPyramidLabel animator] setHidden:YES];
429                 if ( [fX264optBPyramidSwitch state] == 1 )
430                     [fX264optBPyramidSwitch performClick:self];
431             }
432
433             if( [fX264optDirectPredPopUp isHidden] == false )
434             {
435                 [[fX264optDirectPredPopUp animator] setHidden:YES];
436                 [[fX264optDirectPredLabel animator] setHidden:YES];
437                 if ( [fX264optDirectPredPopUp indexOfSelectedItem] > 0 )
438                 {
439                     [fX264optDirectPredPopUp selectItemAtIndex: 0];
440                     [[fX264optDirectPredPopUp cell] performClick:self];
441                 }
442             }
443
444             if( [fX264optBAdaptPopUp isHidden] == false )
445             {
446                 [[fX264optBAdaptPopUp animator] setHidden:YES];
447                 [[fX264optBAdaptLabel animator] setHidden:YES];
448                 if ( [fX264optBAdaptPopUp indexOfSelectedItem] > 0 )
449                 {
450                     [fX264optBAdaptPopUp selectItemAtIndex: 0];
451                     [[fX264optBAdaptPopUp cell] performClick:self];
452                 }
453             }
454         }
455         else if ( [fX264optBframesPopUp indexOfSelectedItem ] == 2)
456         {
457             /* Only 1 b-frame? Disable b-pyramid. */
458             if( [fX264optBPyramidSwitch isHidden] == false )
459             {
460                 [[fX264optBPyramidSwitch animator] setHidden:YES];
461                 [[fX264optBPyramidLabel animator] setHidden:YES];
462                 if ( [fX264optBPyramidSwitch state] == 1 )
463                     [fX264optBPyramidSwitch performClick:self];
464             }
465
466             if( [fX264optWeightBSwitch isHidden] == true )
467             {
468                 [[fX264optWeightBSwitch animator] setHidden:NO];
469                 [[fX264optWeightBLabel animator] setHidden:NO];
470             }
471             
472             if( [fX264optDirectPredPopUp isHidden] == true )
473             {
474                 [[fX264optDirectPredPopUp animator] setHidden:NO];
475                 [[fX264optDirectPredLabel animator] setHidden:NO];
476             }
477             
478             if( [fX264optBAdaptPopUp isHidden] == true )
479             {
480                 [[fX264optBAdaptPopUp animator] setHidden:NO];
481                 [[fX264optBAdaptLabel animator] setHidden:NO];
482             }
483         }
484         else
485         {
486             if( [fX264optBPyramidSwitch isHidden] == true )
487             {
488                 [[fX264optBPyramidSwitch animator] setHidden:NO];
489                 [[fX264optBPyramidLabel animator] setHidden:NO];
490             }
491
492             if( [fX264optWeightBSwitch isHidden] == true )
493             {
494                 [[fX264optWeightBSwitch animator] setHidden:NO];
495                 [[fX264optWeightBLabel animator] setHidden:NO];
496             }
497             
498             if( [fX264optDirectPredPopUp isHidden] == true )
499             {
500                 [[fX264optDirectPredPopUp animator] setHidden:NO];
501                 [[fX264optDirectPredLabel animator] setHidden:NO];
502             }
503             
504             if( [fX264optBAdaptPopUp isHidden] == true )
505             {
506                 [[fX264optBAdaptPopUp animator] setHidden:NO];
507                 [[fX264optBAdaptLabel animator] setHidden:NO];
508             }
509         }
510     }
511     
512     if( sender == fX264optCabacSwitch || sender == nil || sender == fDisplayX264Options )
513     {
514         if ( [fX264optCabacSwitch state] == false)
515         {
516             if( [fX264optTrellisPopUp isHidden] == false )
517             {
518                 /* Without CABAC entropy coding, trellis doesn't run. */
519                 [[fX264optTrellisPopUp animator] setHidden:YES];
520                 [[fX264optTrellisLabel animator] setHidden:YES];
521                 [fX264optTrellisPopUp selectItemAtIndex:0];
522                 [[fX264optTrellisPopUp cell] performClick:self];
523             }
524         }
525         else if( [fX264optTrellisPopUp isHidden] == true)
526         {
527             [[fX264optTrellisPopUp animator] setHidden:NO];
528             [[fX264optTrellisLabel animator] setHidden:NO];
529         }
530     }
531     
532     if( sender == fX264optAnalysePopUp || sender == nil || sender == fDisplayX264Options )
533     {
534         if ( [fX264optAnalysePopUp indexOfSelectedItem] == 1)
535         {
536             /* No analysis? Disable 8x8dct */
537             if( [fX264opt8x8dctSwitch isHidden] == false )
538             {
539                 [[fX264opt8x8dctSwitch animator] setHidden:YES];
540                 [[fX264opt8x8dctLabel animator] setHidden:YES];
541                 if ( [fX264opt8x8dctSwitch state] == 1 )
542                     [fX264opt8x8dctSwitch performClick:self];
543             }
544         }
545         else
546         {
547             if( [fX264opt8x8dctSwitch isHidden] == true )
548             {
549                 [[fX264opt8x8dctSwitch animator] setHidden:NO];
550                 [[fX264opt8x8dctLabel animator] setHidden:NO];
551             }
552         }
553     }
554     
555     if( sender == fX264optRefPopUp || sender == nil || sender == fDisplayX264Options )
556     {
557         if ( [fX264optRefPopUp indexOfSelectedItem] < 3)
558         {
559             if( [fX264optMixedRefsSwitch isHidden] == false )
560             {
561                 /* Only do mixed-refs when there are at least 2 refs to mix. */
562                 [[fX264optMixedRefsSwitch animator] setHidden:YES];
563                 [[fX264optMixedRefsLabel animator] setHidden:YES];
564                 if( [fX264optMixedRefsSwitch state] == 1 )
565                     [fX264optMixedRefsSwitch performClick:self];
566             }
567         }
568         else
569         {
570             if( [fX264optMixedRefsSwitch isHidden] == true )
571             {
572                 [[fX264optMixedRefsSwitch animator] setHidden:NO];
573                 [[fX264optMixedRefsLabel animator] setHidden:NO];
574             }
575         }
576     }
577     
578     if( sender == fX264optMotionEstPopUp || sender == nil || sender == fDisplayX264Options )
579     {
580         if ( [fX264optMotionEstPopUp indexOfSelectedItem] < 3 )
581         {
582             /* ME-range can only be above 16 if me >= umh
583               and changing it to < 16 is idiotic so hide it . */
584             if( [fX264optMERangePopUp isHidden] == false )
585             {
586                 [[fX264optMERangePopUp animator] setHidden:YES];
587                 [[fX264optMERangeLabel animator] setHidden:YES];
588                 if ( [fX264optMERangePopUp indexOfSelectedItem] > 0 )
589                 {
590                     [fX264optMERangePopUp selectItemAtIndex:0];
591                     [[fX264optMERangePopUp cell] performClick:self];
592                 }
593             }
594         }
595         else
596         {
597             if( [fX264optMERangePopUp isHidden] == true )
598             {
599                 [[fX264optMERangePopUp animator] setHidden:NO];
600                 [[fX264optMERangeLabel animator] setHidden:NO];
601             }
602         }
603     }
604     
605     if( sender == fX264optSubmePopUp || sender == nil || sender == fDisplayX264Options )
606     {
607         if( [fX264optSubmePopUp indexOfSelectedItem] != 0 && [fX264optSubmePopUp indexOfSelectedItem] < 7 )
608         {
609             /* No Psy-RDO or Psy=trel if subme < 6. */
610             if( [fX264optPsyRDSlider isHidden] == false )
611             {
612                 [[fX264optPsyRDSlider animator] setHidden:YES];
613                 [[fX264optPsyRDLabel animator] setHidden:YES];
614                 [[fX264optPsyRDSlider animator] setFloatValue:1];
615                 if ( [fX264optPsyRDSlider floatValue] < 1.0 )
616                 {
617                     [fX264optPsyRDSlider setFloatValue:1.0];
618                     [[fX264optPsyRDSlider cell] performClick:self];            
619                 }
620             }
621
622             if( [fX264optPsyTrellisSlider isHidden] == false)
623             {
624                 [[fX264optPsyTrellisSlider animator] setHidden:YES];
625                 [[fX264optPsyTrellisLabel animator] setHidden:YES];
626                 [[fX264optPsyTrellisSlider animator] setFloatValue:0];
627                 if ( [fX264optPsyTrellisSlider floatValue] > 0.0 )
628                 {
629                     [fX264optPsyTrellisSlider setFloatValue:0.0];
630                     [[fX264optPsyTrellisSlider cell] performClick:self];
631                 }
632             }
633         }
634         else
635         {
636             if( [fX264optPsyRDSlider isHidden] == true )
637             {
638                 [[fX264optPsyRDSlider animator] setHidden:NO];
639                 [[fX264optPsyRDLabel animator] setHidden:NO];
640             }
641
642             if( [fX264optTrellisPopUp indexOfSelectedItem] >= 2 && [fX264optCabacSwitch state] == true && [fX264optPsyTrellisSlider isHidden] == true )
643             {
644                 [[fX264optPsyTrellisSlider animator] setHidden:NO];
645                 [[fX264optPsyTrellisLabel animator] setHidden:NO];
646             }
647         }
648     }
649     
650     if( sender == fX264optTrellisPopUp || sender == nil || sender == fDisplayX264Options )
651     {
652         if( [fX264optTrellisPopUp indexOfSelectedItem] < 2 )
653         {
654             if( [fX264optPsyTrellisSlider isHidden] == false )
655             {
656                 /* No Psy-trellis without trellis. */
657                 [[fX264optPsyTrellisSlider animator] setHidden:YES];
658                 [[fX264optPsyTrellisLabel animator] setHidden:YES];
659                 [[fX264optPsyTrellisSlider animator] setFloatValue:0.0];
660                 [[fX264optPsyTrellisSlider cell] performClick:self];
661             }
662         }
663         else
664         {
665             if( ( [fX264optSubmePopUp indexOfSelectedItem] == 0 || [fX264optSubmePopUp indexOfSelectedItem] >= 7 ) && [fX264optCabacSwitch state] == true  && [fX264optPsyTrellisSlider isHidden] == true )
666             {
667                 [[fX264optPsyTrellisSlider animator] setHidden:NO];
668                 [[fX264optPsyTrellisLabel animator] setHidden:NO];
669             }
670         }
671     }
672 }
673
674 /**
675  * Resets the GUI widgets to the contents of the option string.
676  */
677 - (IBAction) X264AdvancedOptionsSetCurrentSettings: (id) sender
678 {
679     /* Set widgets depending on the opt string in field */
680     NSString * thisOpt; // The separated option such as "bframes=3"
681     NSString * optName = @""; // The option name such as "bframes"
682     NSString * optValue = @"";// The option value such as "3"
683     NSArray *currentOptsArray;
684     
685     /*First, we get an opt string to process */
686     NSString *currentOptString = [fDisplayX264Options stringValue];
687     
688     /* Verify there is an opt string to process by making sure an
689        option is getting its value set. If so, start to process it. */
690     NSRange currentOptRange = [currentOptString rangeOfString:@"="];
691     if (currentOptRange.location != NSNotFound)
692     {
693         /*Put individual options into an array based on the ":" separator for processing, result is "<opt>=<value>"*/
694         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
695         
696         /*iterate through the array and get <opts> and <values*/
697         int loopcounter;
698         int currentOptsArrayCount = [currentOptsArray count];
699         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
700         {
701             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
702             
703             /* Verify the option sets a value */
704             NSRange splitOptRange = [thisOpt rangeOfString:@"="];            
705             if (splitOptRange.location != NSNotFound)
706             {
707                 /* Split thisOpt into an optName setting an optValue. */
708                 optName = [thisOpt substringToIndex:splitOptRange.location];
709                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
710                 
711                 /*Run through the available widgets for x264 opts and set them, as you add widgets, 
712                     they need to be added here. This should be moved to its own method probably*/
713                 
714                 /*bframes NSPopUpButton*/
715                 if ([optName isEqualToString:@"bframes"])
716                 {
717                     [fX264optBframesPopUp selectItemAtIndex:[optValue intValue]+1];
718                 }
719                 /*ref NSPopUpButton*/
720                 if ([optName isEqualToString:@"ref"])
721                 {
722                     [fX264optRefPopUp selectItemAtIndex:[optValue intValue]+1];
723                 }
724                 /*No Fast PSkip NSButton*/
725                 if ([optName isEqualToString:@"no-fast-pskip"])
726                 {
727                     [fX264optNfpskipSwitch setState:[optValue intValue]];
728                 }
729                 /*No Dict Decimate NSButton*/
730                 if ([optName isEqualToString:@"no-dct-decimate"])
731                 {
732                     [fX264optNodctdcmtSwitch setState:[optValue intValue]];
733                 }
734                 /*Sub Me NSPopUpButton*/
735                 if ([optName isEqualToString:@"subq"])
736                 {
737                     [fX264optSubmePopUp selectItemAtIndex:[optValue intValue]+1];
738                 }
739                 /*Trellis NSPopUpButton*/
740                 if ([optName isEqualToString:@"trellis"])
741                 {
742                     [fX264optTrellisPopUp selectItemAtIndex:[optValue intValue]+1];
743                 }
744                 /*Mixed Refs NSButton*/
745                 if ([optName isEqualToString:@"mixed-refs"])
746                 {
747                     [fX264optMixedRefsSwitch setState:[optValue intValue]];
748                 }
749                 /*Motion Estimation NSPopUpButton*/
750                 if ([optName isEqualToString:@"me"])
751                 {
752                     if ([optValue isEqualToString:@"dia"])
753                         [fX264optMotionEstPopUp selectItemAtIndex:1];
754                     else if ([optValue isEqualToString:@"hex"])
755                         [fX264optMotionEstPopUp selectItemAtIndex:2];
756                     else if ([optValue isEqualToString:@"umh"])
757                         [fX264optMotionEstPopUp selectItemAtIndex:3];
758                     else if ([optValue isEqualToString:@"esa"])
759                         [fX264optMotionEstPopUp selectItemAtIndex:4];
760                     else if ([optValue isEqualToString:@"tesa"])
761                         [fX264optMotionEstPopUp selectItemAtIndex:5];
762                 }
763                 /*ME Range NSPopUpButton*/
764                 if ([optName isEqualToString:@"merange"])
765                 {
766                     [fX264optMERangePopUp selectItemAtIndex:[optValue intValue]-3];
767                 }
768                 /* Adaptive B-Frames NSPopUpButton*/
769                 if ([optName isEqualToString:@"b-adapt"])
770                 {
771                     [fX264optBAdaptPopUp selectItemAtIndex:[optValue intValue]+1];
772                 }
773                 /*Weighted B-Frames NSButton*/
774                 if ([optName isEqualToString:@"weightb"])
775                 {
776                     [fX264optWeightBSwitch setState:[optValue intValue]];
777                 }
778                 /*B Pyramid NSPButton*/
779                 if ([optName isEqualToString:@"b-pyramid"])
780                 {
781                     [fX264optBPyramidSwitch setState:[optValue intValue]];
782                 }
783                 /*Direct B-frame Prediction NSPopUpButton*/
784                 if ([optName isEqualToString:@"direct"])
785                 {
786                     if ([optValue isEqualToString:@"none"])
787                         [fX264optDirectPredPopUp selectItemAtIndex:1];
788                     else if ([optValue isEqualToString:@"spatial"])
789                         [fX264optDirectPredPopUp selectItemAtIndex:2];
790                     else if ([optValue isEqualToString:@"temporal"])
791                         [fX264optDirectPredPopUp selectItemAtIndex:3];
792                     else if ([optValue isEqualToString:@"auto"])
793                         [fX264optDirectPredPopUp selectItemAtIndex:4];                        
794                 }
795                 /*Deblocking NSPopUpButtons*/
796                 if ([optName isEqualToString:@"deblock"])
797                 {
798                     NSString * alphaDeblock = @"";
799                     NSString * betaDeblock = @"";
800                     
801                     NSRange splitDeblock = [optValue rangeOfString:@","];
802                     alphaDeblock = [optValue substringToIndex:splitDeblock.location];
803                     betaDeblock = [optValue substringFromIndex:splitDeblock.location + 1];
804                     
805                     if ([alphaDeblock isEqualToString:@"0"] && [betaDeblock isEqualToString:@"0"])
806                     {
807                         /* When both filters are at 0, default */
808                         [fX264optAlphaDeblockPopUp selectItemAtIndex:0];                        
809                         [fX264optBetaDeblockPopUp selectItemAtIndex:0];                               
810                     }
811                     else
812                     {
813                         if (![alphaDeblock isEqualToString:@"0"])
814                         {
815                             /* Alpha isn't 0, so set it. The offset of 7 is
816                                because filters start at -6 instead of at 0. */
817                             [fX264optAlphaDeblockPopUp selectItemAtIndex:[alphaDeblock intValue]+7];
818                         }
819                         else
820                         {
821                             /* Set alpha filter to 0, which is 7 up
822                                because filters start at -6, not 0. */
823                             [fX264optAlphaDeblockPopUp selectItemAtIndex:7];                        
824                         }
825                         
826                         if (![betaDeblock isEqualToString:@"0"])
827                         {
828                             /* Beta isn't 0, so set it. */
829                             [fX264optBetaDeblockPopUp selectItemAtIndex:[betaDeblock intValue]+7];
830                         }
831                         else
832                         {
833                             /* Set beta filter to 0. */
834                             [fX264optBetaDeblockPopUp selectItemAtIndex:7];                        
835                         }
836                     }
837                 }
838                 /* Analysis NSPopUpButton */
839                 if ([optName isEqualToString:@"analyse"])
840                 {
841                     if ([optValue isEqualToString:@"p8x8,b8x8,i8x8,i4x4"])
842                     {
843                         /* Default ("some") */
844                         [fX264optAnalysePopUp selectItemAtIndex:0];
845                     }
846                     if ([optValue isEqualToString:@"none"])
847                     {
848                         [fX264optAnalysePopUp selectItemAtIndex:1];
849                     }
850                     if ([optValue isEqualToString:@"all"])
851                     {
852                         [fX264optAnalysePopUp selectItemAtIndex:2];
853                     }
854                 }
855                 /* 8x8 DCT NSButton */
856                 if ([optName isEqualToString:@"8x8dct"])
857                 {
858                     [fX264opt8x8dctSwitch setState:[optValue intValue]];
859                 }
860                 /* CABAC NSButton */
861                 if ([optName isEqualToString:@"cabac"])
862                 {
863                     [fX264optCabacSwitch setState:[optValue intValue]];
864                 }
865                 /* Psy-RD and Psy-Trellis NSSliders */
866                 if ([optName isEqualToString:@"psy-rd"])
867                 {
868                     NSString * rdOpt = @"";
869                     NSString * trellisOpt = @"";
870                     
871                     NSRange splitRD = [optValue rangeOfString:@","];
872                     rdOpt = [optValue substringToIndex:splitRD.location];
873                     trellisOpt = [optValue substringFromIndex:splitRD.location + 1];
874                     
875                     [fX264optPsyRDSlider setFloatValue:[rdOpt floatValue]];
876                     [fX264optPsyTrellisSlider setFloatValue:[trellisOpt floatValue]];
877                 }                                                              
878             }
879         }
880     }
881 }
882
883 /**
884  * Resets the option string to mirror the GUI widgets.
885  */
886 - (IBAction) X264AdvancedOptionsChanged: (id) sender
887 {
888     /*Determine which outlet is being used and set optName to process accordingly */
889     NSString * optNameToChange = @""; // The option name such as "bframes"
890     
891     if (sender == fX264optBframesPopUp)
892     {
893         optNameToChange = @"bframes";
894     }
895     if (sender == fX264optRefPopUp)
896     {
897         optNameToChange = @"ref";
898     }
899     if (sender == fX264optNfpskipSwitch)
900     {
901         optNameToChange = @"no-fast-pskip";
902     }
903     if (sender == fX264optNodctdcmtSwitch)
904     {
905         optNameToChange = @"no-dct-decimate";
906     }
907     if (sender == fX264optSubmePopUp)
908     {
909         optNameToChange = @"subq";
910     }
911     if (sender == fX264optTrellisPopUp)
912     {
913         optNameToChange = @"trellis";
914     }
915     if (sender == fX264optMixedRefsSwitch)
916     {
917         optNameToChange = @"mixed-refs";
918     }
919     if (sender == fX264optMotionEstPopUp)
920     {
921         optNameToChange = @"me";
922     }
923     if (sender == fX264optMERangePopUp)
924     {
925         optNameToChange = @"merange";
926     }
927     if (sender == fX264optBAdaptPopUp)
928     {
929         optNameToChange = @"b-adapt";
930     }
931     if (sender == fX264optWeightBSwitch)
932     {
933         optNameToChange = @"weightb";
934     }
935     if (sender == fX264optBPyramidSwitch)
936     {
937         optNameToChange = @"b-pyramid";
938     }
939     if (sender == fX264optDirectPredPopUp)
940     {
941         optNameToChange = @"direct";
942     }
943     if (sender == fX264optAlphaDeblockPopUp)
944     {
945         optNameToChange = @"deblock";
946     }
947     if (sender == fX264optBetaDeblockPopUp)
948     {
949         optNameToChange = @"deblock";
950     }        
951     if (sender == fX264optAnalysePopUp)
952     {
953         optNameToChange = @"analyse";
954     }
955     if (sender == fX264opt8x8dctSwitch)
956     {
957         optNameToChange = @"8x8dct";
958     }
959     if (sender == fX264optCabacSwitch)
960     {
961         optNameToChange = @"cabac";
962     }
963     if( sender == fX264optPsyRDSlider)
964     {
965         optNameToChange = @"psy-rd";
966     }
967     if( sender == fX264optPsyTrellisSlider)
968     {
969         optNameToChange = @"psy-rd";
970     }
971     
972     
973     /* Set widgets depending on the opt string in field */
974     NSString * thisOpt; // The separated option such as "bframes=3"
975     NSString * optName = @""; // The option name such as "bframes"
976     NSString * optValue = @"";// The option value such as "3"
977     NSArray *currentOptsArray;
978     
979     /*First, we get an opt string to process */
980     NSString *currentOptString = [fDisplayX264Options stringValue];
981     
982     /* There are going to be a few possibilities.
983        - The option might start off the string.
984        - The option might be in the middle of the string.
985        - The option might not be in the string at all yet.
986        - The string itself might not yet exist.
987        
988        Because each of these possibilities means constructing a different kind of string,
989        they're all handled separately in a sea of messy, somewhat redundant code. =(     */
990        
991     /* If the option is in the string but not the beginning of it, it will be in the form of ":optName=value"
992        so we really want to be looking for ":optNameToChange=" rather than "optNameToChange".                 */
993     NSString *checkOptNameToChange = [NSString stringWithFormat:@":%@=",optNameToChange];
994     
995     /* Now we store the part of the string up through the option name in currentOptRange. */
996     NSRange currentOptRange = [currentOptString rangeOfString:checkOptNameToChange];
997
998     /*  We need to know if the option is at the beginning of the string.
999         If it is at the start, it won't be preceded by a colon.
1000         To figure this out, we'll use the rangeOfString method. First,
1001         store what the option name would be if if it was at the beginning,
1002         in checkOptNameToChangeBeginning. Then, find its range in the string.
1003         If the range is 0, it's the first option listed in the string.       */        
1004     NSString *checkOptNameToChangeBeginning = [NSString stringWithFormat:@"%@=",optNameToChange];
1005     NSRange currentOptRangeBeginning = [currentOptString rangeOfString:checkOptNameToChangeBeginning];
1006     
1007     if (currentOptRange.location != NSNotFound || currentOptRangeBeginning.location == 0)
1008     {
1009         /* If the option is in the string wth a semicolon, or starts the string, it's time to edit.
1010            This means parsing the whole string into an array of options and values. From there,
1011            iterate through the options, and when you reach the one that's been changed, edit it.   */
1012         
1013         /* Create new empty opt string*/
1014         NSString *changedOptString = @"";
1015         
1016         /*Put individual options into an array based on the ":" separator for processing, result is "<opt>=<value>"*/
1017         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
1018         
1019         /*iterate through the array and get <opts> and <values*/
1020         int loopcounter;
1021         int currentOptsArrayCount = [currentOptsArray count];
1022         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
1023         {
1024             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
1025             NSRange splitOptRange = [thisOpt rangeOfString:@"="];
1026             
1027             if (splitOptRange.location != NSNotFound)
1028             {
1029                 /* First off, it's time to handle option strings that
1030                    already have at least one option=value pair in them. */
1031                    
1032                 optName = [thisOpt substringToIndex:splitOptRange.location];
1033                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
1034                 
1035                 /*Run through the available widgets for x264 opts and set them, as you add widgets, 
1036                     they need to be added here. This should be moved to its own method probably*/
1037                 
1038                 /*If the optNameToChange is found, appropriately change the value or delete it if
1039                     "Unspecified" is set.*/
1040                 if ([optName isEqualToString:optNameToChange])
1041                 {
1042                     if ([optNameToChange isEqualToString:@"deblock"])
1043                     {
1044                         if ((([fX264optAlphaDeblockPopUp indexOfSelectedItem] == 0) || ([fX264optAlphaDeblockPopUp indexOfSelectedItem] == 7)) && (([fX264optBetaDeblockPopUp indexOfSelectedItem] == 0) || ([fX264optBetaDeblockPopUp indexOfSelectedItem] == 7)))
1045                         {
1046                             /* When both deblock widgets are 0 or default or a mix of the two,
1047                                use a blank string, since deblocking defaults to 0,0.           */
1048                             thisOpt = @"";                                
1049                         }
1050                         else
1051                         {
1052                             /* Otherwise the format is deblock=a,b, where a and b both have an array
1053                                offset of 7 because deblocking values start at -6 instead of at zero. */
1054                             thisOpt = [NSString stringWithFormat:@"%@=%d,%d",optName, ([fX264optAlphaDeblockPopUp indexOfSelectedItem] != 0) ? [fX264optAlphaDeblockPopUp indexOfSelectedItem]-7 : 0,([fX264optBetaDeblockPopUp indexOfSelectedItem] != 0) ? [fX264optBetaDeblockPopUp indexOfSelectedItem]-7 : 0];
1055                         }
1056                     }
1057                     if ([optNameToChange isEqualToString:@"psy-rd"])
1058                     {
1059                         if( [fX264optPsyRDSlider floatValue] == 1.0 && [fX264optPsyTrellisSlider floatValue] == 0.0 ) 
1060                         {
1061                             /* When  PsyRD is 1 and PsyTrel is 0 they're default values and can be ignored. */
1062                             thisOpt = @"";                                
1063                         }
1064                         else
1065                         {
1066                             /* Otherwise the format is deblock=a,b, where a and b both have an array
1067                                offset of 7 because deblocking values start at -6 instead of at zero. */
1068                             thisOpt = [NSString stringWithFormat:@"%@=%0.1f,%0.1f", optName, [fX264optPsyRDSlider floatValue], [fX264optPsyTrellisSlider floatValue] ];
1069                         }
1070                     }
1071                     else if /*Boolean Switches*/ ([optNameToChange isEqualToString:@"mixed-refs"] || [optNameToChange isEqualToString:@"weightb"] ||  [optNameToChange isEqualToString:@"b-pyramid"] || [optNameToChange isEqualToString:@"no-fast-pskip"] || [optNameToChange isEqualToString:@"no-dct-decimate"] || [optNameToChange isEqualToString:@"8x8dct"] )
1072                     {
1073                         /* Here is where we take care of the boolean options that work overtly:
1074                            no-dct-decimate being on means no-dct-decimate=1, etc. Some options
1075                            require the inverse, but those will be handled a couple lines down. */
1076                         if ([sender state] == 0)
1077                         {
1078                             /* When these options are false, don't include them. They all default
1079                                to being set off, so they don't need to be mentioned at all.       */
1080                             thisOpt = @"";
1081                         }
1082                         else
1083                         {
1084                             /* Otherwise, include them as optioname=1 */
1085                             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,1];
1086                         }
1087                     }
1088                     else if ([optNameToChange isEqualToString:@"cabac"])
1089                     {
1090                         /* CABAC is odd, in that it defaults to being on. That means
1091                            it only needs to be included in the string when turned off. */
1092                         if ([sender state] == 1)
1093                         {
1094                             /* It's true so don't include it. */
1095                             thisOpt = @"";
1096                         }
1097                         else
1098                         {
1099                             /* Otherwise, include cabac=0 in the string to enable CAVLC. */
1100                             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,0];
1101                         }
1102                     }                                        
1103                     else if (([sender indexOfSelectedItem] == 0) && (sender != fX264optAlphaDeblockPopUp) && (sender != fX264optBetaDeblockPopUp) ) // means that "unspecified" is chosen, lets then remove it from the string
1104                     {
1105                         /* When a widget is at index 0, it's default. Default means don't add to the string.
1106                            The exception for deblocking is because for those, *both* need to at index 0
1107                            for it to default, so it's handled separately, above this section.                */
1108                         thisOpt = @"";
1109                     }
1110                     else if ([optNameToChange isEqualToString:@"me"])
1111                     {
1112                         /* Motion estimation uses string values, so this switch
1113                            pairs the widget index with the right value string.  */
1114                         switch ([sender indexOfSelectedItem])
1115                         {   
1116                             case 1:
1117                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"dia"];
1118                                 break;
1119                                 
1120                             case 2:
1121                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"hex"];
1122                                 break;
1123                                 
1124                             case 3:
1125                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"umh"];
1126                                 break;
1127                                 
1128                             case 4:
1129                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"esa"];
1130                                 break;
1131                             
1132                             case 5:
1133                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"tesa"];
1134                             
1135                             default:
1136                                 break;
1137                         }
1138                     }
1139                     else if ([optNameToChange isEqualToString:@"direct"])
1140                     {
1141                         /* Direct prediction uses string values, so this switch
1142                            pairs the right string value with the right widget index. */
1143                         switch ([sender indexOfSelectedItem])
1144                         {   
1145                             case 1:
1146                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"none"];
1147                                 break;
1148                                 
1149                             case 2:
1150                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"spatial"];
1151                                 break;
1152                                 
1153                             case 3:
1154                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"temporal"];
1155                                 break;
1156                                 
1157                             case 4:
1158                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"auto"];
1159                                 break;
1160                                 
1161                             default:
1162                                 break;
1163                         }
1164                     }
1165                     else if ([optNameToChange isEqualToString:@"analyse"])
1166                     {
1167                         /* Analysis uses string values as well. */
1168                         switch ([sender indexOfSelectedItem])
1169                         {   
1170                             case 1:
1171                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"none"];
1172                                 break;
1173                                 
1174                             case 2:
1175                                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"all"];
1176                                 break;
1177                                 
1178                             default:
1179                                 break;
1180                         }
1181                     }
1182                     else if ([optNameToChange isEqualToString:@"merange"])
1183                     {
1184                         /* Motion estimation range uses an odd array offset because in addition
1185                            to starting with index 0 as default, index 1 starts at 4 instead of 1,
1186                            because merange can't go below 4. So it has to be handled separately.  */
1187                         thisOpt = [NSString stringWithFormat:@"%@=%d",optName,[sender indexOfSelectedItem]+3];
1188                     }
1189                     else if ([optNameToChange isEqualToString:@"b-adapt"])
1190                     {
1191                         /* B-adapt starts at index 0 with default then goes 0, 1, 2)*/
1192                         thisOpt = [NSString stringWithFormat:@"%@=%d", optName, [sender indexOfSelectedItem]-1];
1193                     }
1194                     else // we have a valid value to change, so change it
1195                     {
1196                         if ( [sender indexOfSelectedItem] != 0 )
1197                         /* Here's our general case, that catches things like ref frames and b-frames.
1198                            Basically, any options that are PopUp menus with index 0 as default and
1199                            index 1 as 1, with numerical values, are all handled right here. All of
1200                            the above stuff is for the exceptions to the general case.              */
1201                             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,[sender indexOfSelectedItem]-1];
1202                     }
1203                 }
1204             }
1205             
1206             /* Construct New String for opts here */
1207             if ([thisOpt isEqualToString:@""])
1208             {
1209                 /* Blank option, so just add it to the string. (Why?) */
1210                 changedOptString = [NSString stringWithFormat:@"%@%@",changedOptString,thisOpt];
1211             }
1212             else
1213             {
1214                 if ([changedOptString isEqualToString:@""])
1215                 {
1216                     /* No existing string, make the string this option. */
1217                     changedOptString = [NSString stringWithFormat:@"%@",thisOpt];
1218                 }
1219                 else
1220                 {
1221                     /* Existing string, existing option. Append the
1222                        option to the string, preceding it with a colon. */
1223                     changedOptString = [NSString stringWithFormat:@"%@:%@",changedOptString,thisOpt];
1224                 }
1225             }
1226         }
1227         
1228         /* Change the dislayed option string to reflect the new modified settings */
1229         [fDisplayX264Options setStringValue:[NSString stringWithFormat:changedOptString]];    
1230     }
1231     else // if none exists, add it to the string
1232     {
1233         /* This is where options that aren't already in the string are handled. */
1234         if ([[fDisplayX264Options stringValue] isEqualToString: @""])
1235         {
1236             /* The option might not be in the string because the
1237                string is empty. Handle this possibility first.   */
1238             if ([optNameToChange isEqualToString:@"me"])
1239             {
1240                 /* Special case for motion estimation, which uses string values
1241                    that need to be paired up with the equivalent widget index.  */
1242                 switch ([sender indexOfSelectedItem])
1243                 {   
1244                     case 1:
1245                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1246                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"dia"]]];
1247                         break;
1248                         
1249                     case 2:
1250                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1251                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"hex"]]];
1252                         break;
1253                         
1254                     case 3:
1255                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1256                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"umh"]]];
1257                         break;
1258                         
1259                     case 4:
1260                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1261                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"esa"]]];
1262                         break;
1263                     
1264                     case 5:
1265                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1266                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"tesa"]]];
1267                         break;
1268                     
1269                     default:
1270                         break;
1271                 }
1272             }
1273             else if ([optNameToChange isEqualToString:@"direct"])
1274             {
1275                 /* Special case for direct prediction, which uses string values
1276                    that need to be paired up with the equivalent widget index.  */
1277                 switch ([sender indexOfSelectedItem])
1278                 {   
1279                     case 1:
1280                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1281                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"none"]]];
1282                         break;
1283                         
1284                     case 2:
1285                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1286                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"spatial"]]];
1287                         break;
1288                         
1289                     case 3:
1290                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1291                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"temporal"]]];
1292                         break;
1293                         
1294                     case 4:
1295                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1296                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"auto"]]];
1297                         break;
1298                         
1299                     default:
1300                         break;
1301                 }
1302             }
1303             else if ([optNameToChange isEqualToString:@"analyse"])
1304             {
1305                 /* Special case for partition analysis, which uses string values
1306                    that need to be paired up with the equivalent widget index.  */
1307                 switch ([sender indexOfSelectedItem])
1308                 {   
1309                     case 1:
1310                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1311                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"none"]]];
1312                         break;
1313                         
1314                     case 2:
1315                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1316                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"all"]]];
1317                         break;
1318                         
1319                     default:
1320                         break;
1321                 }
1322             }
1323             else if ([optNameToChange isEqualToString:@"merange"])
1324             {
1325                 /* Special case for motion estimation range, which uses
1326                    a widget index offset of 3. This is because the
1327                    first valid value after default is four, not zero.   */
1328                 [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1329                     [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d",[sender indexOfSelectedItem]+3]]];
1330             }
1331             else if ([optNameToChange isEqualToString:@"b-adapt"])
1332             {
1333                 /* 0 is default, index 1 is 0*/
1334                 [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1335                     [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d",[sender indexOfSelectedItem]-1]]];                
1336             }
1337             else if ([optNameToChange isEqualToString:@"deblock"])
1338             {
1339                 /* Very special case for deblock. Uses a weird widget index offset
1340                    of 7, because the first value after default is -6, rather than 0.
1341                    As well, deblock only goes to default when *both* alpha and beta
1342                    are zero. If only one is zero, you can't mark it down as default.
1343                    Instead, mark that one down as literally 0. This is because when
1344                    widgets are at default values, they aren't included in the string.
1345                    If only one filter is at 0, both need to be overtly specified.    */
1346                 [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d,%d", ([fX264optAlphaDeblockPopUp indexOfSelectedItem] != 0) ? [fX264optAlphaDeblockPopUp indexOfSelectedItem]-7 : 0, ([fX264optBetaDeblockPopUp indexOfSelectedItem] != 0) ? [fX264optBetaDeblockPopUp indexOfSelectedItem]-7 : 0]]];                
1347             }
1348             else if ([optNameToChange isEqualToString:@"psy-rd"])
1349             {
1350                 /* Special case for psy-rd and psy-trellis. */
1351                 if( [fX264optPsyRDSlider floatValue] == 1 && [fX264optPsyTrellisSlider floatValue] == 0 )
1352                 {
1353                     /* Defaults, use null string. */
1354                     [fDisplayX264Options setStringValue:@""];
1355                 }
1356                 else
1357                 {
1358                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%0.1f,%0.1f", [NSString stringWithFormat:optNameToChange], [fX264optPsyRDSlider floatValue],  [fX264optPsyTrellisSlider floatValue]]];
1359                 }
1360             }
1361             else if /*Boolean Switches*/ ([optNameToChange isEqualToString:@"mixed-refs"] || [optNameToChange isEqualToString:@"weightb"] || [optNameToChange isEqualToString:@"b-pyramid"] || [optNameToChange isEqualToString:@"no-fast-pskip"] || [optNameToChange isEqualToString:@"no-dct-decimate"] || [optNameToChange isEqualToString:@"8x8dct"] )
1362             {
1363                 /* This covers all the boolean options that need to be specified only when true. */
1364                 if ([sender state] == 0)
1365                 {
1366                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@""]];                    
1367                 }
1368                 else
1369                 {
1370                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1371                         [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d",[sender state]]]];
1372                 }
1373             }
1374             else if ([optNameToChange isEqualToString:@"cabac"])
1375             {
1376                 /* CABAC is weird in that you need the inverse. Only include in the string
1377                    when cabac=0, because cabac=1 is the default. Turning it off means CAVLC. */
1378                 if ([sender state] == 1)
1379                 {
1380                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@""]];                                        
1381                 }
1382                 else
1383                 {
1384                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1385                         [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d",[sender state]]]];                    
1386                 }
1387             }            
1388             else
1389             {
1390                 if ( [sender indexOfSelectedItem] != 0 )
1391                 /* General case to cover all the normal PopUp widgets, like ref and b-frames. */
1392                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@=%@", 
1393                     [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d",[sender indexOfSelectedItem]-1]]];
1394             }
1395         }
1396         else
1397         {
1398             /* The string isn't empty, and the option isn't already in it,
1399                so it will need to be appended to the string with a colon.  */
1400             if ([optNameToChange isEqualToString:@"me"])
1401             {
1402                 /* Special case for motion estimation, which uses string values
1403                    that need to be paired up with the equivalent widget index.  */
1404                 switch ([sender indexOfSelectedItem])
1405                 {   
1406                     case 1:
1407                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1408                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1409                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"dia"]]];
1410                         break;
1411                         
1412                     case 2:
1413                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1414                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1415                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"hex"]]];
1416                         break;
1417                         
1418                     case 3:
1419                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1420                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1421                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"umh"]]];
1422                         break;
1423                         
1424                     case 4:
1425                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1426                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1427                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"esa"]]];
1428                         break;
1429
1430                     case 5:
1431                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1432                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1433                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"tesa"]]];
1434                         break;
1435                         
1436                     default:
1437                         break;
1438                 }
1439             }
1440             else if ([optNameToChange isEqualToString:@"direct"])
1441             {
1442                 /* Special case for direct prediction, which uses string values
1443                    that need to be paired up with the equivalent widget index.  */
1444                 switch ([sender indexOfSelectedItem])
1445                 {   
1446                     case 1:
1447                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1448                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1449                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"none"]]];
1450                         break;
1451                         
1452                     case 2:
1453                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1454                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1455                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"spatial"]]];
1456                         break;
1457                         
1458                     case 3:
1459                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1460                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1461                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"temporal"]]];
1462                         break;
1463                         
1464                     case 4:
1465                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1466                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1467                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"auto"]]];
1468                         break;
1469                         
1470                     default:
1471                         break;
1472                 }
1473             }
1474             else if ([optNameToChange isEqualToString:@"analyse"])
1475             {
1476                 /* Special case for partition analysis, which uses string values
1477                    that need to be paired up with the equivalent widget index.  */
1478                 switch ([sender indexOfSelectedItem])
1479                 {   
1480                     case 1:
1481                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1482                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1483                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"none"]]];
1484                         break;
1485                         
1486                     case 2:
1487                         [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", 
1488                             [NSString stringWithFormat:[fDisplayX264Options stringValue]],
1489                             [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"all"]]];
1490                         break;
1491                         
1492                     default:
1493                         break;
1494                 }
1495             }
1496             
1497             else if ([optNameToChange isEqualToString:@"merange"])
1498             {
1499                 /* Motion estimation range uses a weird offset since its index goes
1500                    0: default, 1: 4, because the first valid value is 4, not 1.     */
1501                 [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@",[NSString stringWithFormat:[fDisplayX264Options stringValue]], 
1502                     [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d",[sender indexOfSelectedItem]+3]]];
1503             }
1504             else if ([optNameToChange isEqualToString:@"b-adapt"])
1505             {
1506                 /* 0 is default, index 1 is 0*/
1507                 [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@",[NSString stringWithFormat:[fDisplayX264Options stringValue]], 
1508                     [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d",[sender indexOfSelectedItem]-1]]];                
1509             }
1510             else if ([optNameToChange isEqualToString:@"deblock"])
1511             {
1512                 /* Deblock is really weird because it has two values, and if only one is default, both
1513                    still need to be specified directly. with the default one at zero. To make deblock
1514                    just a little more fun, values start at -6 instead of at zero.                       */
1515                 [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@", [NSString stringWithFormat:[fDisplayX264Options stringValue]], [NSString stringWithFormat:optNameToChange], [NSString stringWithFormat:@"%d,%d", ([fX264optAlphaDeblockPopUp indexOfSelectedItem] != 0) ? [fX264optAlphaDeblockPopUp indexOfSelectedItem]-7 : 0, ([fX264optBetaDeblockPopUp indexOfSelectedItem] != 0) ? [fX264optBetaDeblockPopUp indexOfSelectedItem]-7 : 0]]];                
1516             }
1517             else if ([optNameToChange isEqualToString:@"psy-rd"])
1518             {
1519                 /* Special case for psy-rd and psy-trel */
1520                 if( [fX264optPsyRDSlider floatValue] == 1 && [fX264optPsyTrellisSlider floatValue] == 0 )
1521                 {
1522                     /* Defaults, don't change string. */
1523                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:[fDisplayX264Options stringValue]]];
1524                 }
1525                 else
1526                 {
1527                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%0.1f,%0.1f", [NSString stringWithFormat:[fDisplayX264Options stringValue]], [NSString stringWithFormat:optNameToChange], [fX264optPsyRDSlider floatValue],  [fX264optPsyTrellisSlider floatValue]]];
1528                 }
1529                 
1530             }
1531             else if /*Boolean Switches*/ ([optNameToChange isEqualToString:@"mixed-refs"] || [optNameToChange isEqualToString:@"weightb"] || [optNameToChange isEqualToString:@"b-pyramid"] || [optNameToChange isEqualToString:@"no-fast-pskip"] || [optNameToChange isEqualToString:@"no-dct-decimate"] || [optNameToChange isEqualToString:@"8x8dct"] )
1532             {
1533                 /* Covers all the normal booleans, that only need to be included in the string when they're true. */
1534                 if ([sender state] == 0)
1535                 {
1536                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@",[NSString stringWithFormat:[fDisplayX264Options stringValue]]]];                    
1537                 }
1538                 else
1539                 {
1540                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@",[NSString stringWithFormat:[fDisplayX264Options stringValue]], 
1541                         [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d",[sender state]]]];                
1542                 }
1543             }
1544             else if ([optNameToChange isEqualToString:@"cabac"])
1545             {
1546                 /* CABAC is weird, in that it's an inverse. Only include it in the string when it's false. */
1547                 if ([sender state] == 1)
1548                 {
1549                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@",[NSString stringWithFormat:[fDisplayX264Options stringValue]]]];                    
1550                 }
1551                 else
1552                 {
1553                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@",[NSString stringWithFormat:[fDisplayX264Options stringValue]], 
1554                         [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d",[sender state]]]];
1555                 }
1556             }
1557             else
1558             {
1559                 /* General case to handle the normal PopUp widgets like ref and b-frames. */
1560                 if ( [sender indexOfSelectedItem] != 0 )
1561                     [fDisplayX264Options setStringValue:[NSString stringWithFormat:@"%@:%@=%@",[NSString stringWithFormat:[fDisplayX264Options stringValue]], 
1562                     [NSString stringWithFormat:optNameToChange],[NSString stringWithFormat:@"%d",[sender indexOfSelectedItem]-1]]];
1563             }
1564         }
1565     }
1566     
1567     /* We now need to reset the opt widgets since we changed some stuff */        
1568     [self X264AdvancedOptionsSet:sender];        
1569 }
1570
1571 @end