OSDN Git Service

MacGui: Yet another x264 advanced controller change...rewording the b-adapt pop up...
[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 (Fast)"];
188     [fX264optBAdaptPopUp addItemWithTitle:@"Off"];
189     [fX264optBAdaptPopUp addItemWithTitle:@"Fast"];
190     [fX264optBAdaptPopUp addItemWithTitle:@"Optimal"];
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 ( [fX264optBframesPopUp indexOfSelectedItem ] < 2)
407         {
408             /* If the b-frame widget is at 0 or 1, the user has chosen
409                not to use b-frames at all. So disable the options
410                that can only be used when b-frames are enabled.        */
411             
412             if( [fX264optWeightBSwitch isHidden] == false)
413             {
414                 [[fX264optWeightBSwitch animator] setHidden:YES];
415                 [[fX264optWeightBLabel animator] setHidden:YES];
416                 if ( [fX264optWeightBSwitch state] == 1 )
417                     [fX264optWeightBSwitch performClick:self];
418             }
419
420             if( [fX264optBPyramidSwitch isHidden] == false )
421             {
422                 [[fX264optBPyramidSwitch animator] setHidden:YES];
423                 [[fX264optBPyramidLabel animator] setHidden:YES];
424                 if ( [fX264optBPyramidSwitch state] == 1 )
425                     [fX264optBPyramidSwitch performClick:self];
426             }
427
428             if( [fX264optDirectPredPopUp isHidden] == false )
429             {
430                 [[fX264optDirectPredPopUp animator] setHidden:YES];
431                 [[fX264optDirectPredLabel animator] setHidden:YES];
432                 if ( [fX264optDirectPredPopUp indexOfSelectedItem] > 0 )
433                 {
434                     [fX264optDirectPredPopUp selectItemAtIndex: 0];
435                     [[fX264optDirectPredPopUp cell] performClick:self];
436                 }
437             }
438
439             if( [fX264optBAdaptPopUp isHidden] == false )
440             {
441                 [[fX264optBAdaptPopUp animator] setHidden:YES];
442                 [[fX264optBAdaptLabel animator] setHidden:YES];
443                 if ( [fX264optBAdaptPopUp indexOfSelectedItem] > 0 )
444                 {
445                     [fX264optBAdaptPopUp selectItemAtIndex: 0];
446                     [[fX264optBAdaptPopUp cell] performClick:self];
447                 }
448             }
449         }
450         else if ( [fX264optBframesPopUp indexOfSelectedItem ] == 2)
451         {
452             /* Only 1 b-frame? Disable b-pyramid. */
453             if( [fX264optBPyramidSwitch isHidden] == false )
454             {
455                 [[fX264optBPyramidSwitch animator] setHidden:YES];
456                 [[fX264optBPyramidLabel animator] setHidden:YES];
457                 if ( [fX264optBPyramidSwitch state] == 1 )
458                     [fX264optBPyramidSwitch performClick:self];
459             }
460
461             if( [fX264optWeightBSwitch isHidden] == true )
462             {
463                 [[fX264optWeightBSwitch animator] setHidden:NO];
464                 [[fX264optWeightBLabel animator] setHidden:NO];
465             }
466             
467             if( [fX264optDirectPredPopUp isHidden] == true )
468             {
469                 [[fX264optDirectPredPopUp animator] setHidden:NO];
470                 [[fX264optDirectPredLabel animator] setHidden:NO];
471             }
472             
473             if( [fX264optBAdaptPopUp isHidden] == true )
474             {
475                 [[fX264optBAdaptPopUp animator] setHidden:NO];
476                 [[fX264optBAdaptLabel animator] setHidden:NO];
477             }
478         }
479         else
480         {
481             if( [fX264optBPyramidSwitch isHidden] == true )
482             {
483                 [[fX264optBPyramidSwitch animator] setHidden:NO];
484                 [[fX264optBPyramidLabel animator] setHidden:NO];
485             }
486
487             if( [fX264optWeightBSwitch isHidden] == true )
488             {
489                 [[fX264optWeightBSwitch animator] setHidden:NO];
490                 [[fX264optWeightBLabel animator] setHidden:NO];
491             }
492             
493             if( [fX264optDirectPredPopUp isHidden] == true )
494             {
495                 [[fX264optDirectPredPopUp animator] setHidden:NO];
496                 [[fX264optDirectPredLabel animator] setHidden:NO];
497             }
498             
499             if( [fX264optBAdaptPopUp isHidden] == true )
500             {
501                 [[fX264optBAdaptPopUp animator] setHidden:NO];
502                 [[fX264optBAdaptLabel animator] setHidden:NO];
503             }
504         }
505     }
506     
507     if( sender == fX264optCabacSwitch || sender == nil || sender == fDisplayX264Options )
508     {
509         if ( [fX264optCabacSwitch state] == false)
510         {
511             if( [fX264optTrellisPopUp isHidden] == false )
512             {
513                 /* Without CABAC entropy coding, trellis doesn't run. */
514                 [[fX264optTrellisPopUp animator] setHidden:YES];
515                 [[fX264optTrellisLabel animator] setHidden:YES];
516                 [fX264optTrellisPopUp selectItemAtIndex:0];
517                 [[fX264optTrellisPopUp cell] performClick:self];
518             }
519         }
520         else if( [fX264optTrellisPopUp isHidden] == true)
521         {
522             [[fX264optTrellisPopUp animator] setHidden:NO];
523             [[fX264optTrellisLabel animator] setHidden:NO];
524         }
525     }
526     
527     if( sender == fX264optAnalysePopUp || sender == nil || sender == fDisplayX264Options )
528     {
529         if ( [fX264optAnalysePopUp indexOfSelectedItem] == 1)
530         {
531             /* No analysis? Disable 8x8dct */
532             if( [fX264opt8x8dctSwitch isHidden] == false )
533             {
534                 [[fX264opt8x8dctSwitch animator] setHidden:YES];
535                 [[fX264opt8x8dctLabel animator] setHidden:YES];
536                 if ( [fX264opt8x8dctSwitch state] == 1 )
537                     [fX264opt8x8dctSwitch performClick:self];
538             }
539         }
540         else
541         {
542             if( [fX264opt8x8dctSwitch isHidden] == true )
543             {
544                 [[fX264opt8x8dctSwitch animator] setHidden:NO];
545                 [[fX264opt8x8dctLabel animator] setHidden:NO];
546             }
547         }
548     }
549     
550     if( sender == fX264optRefPopUp || sender == nil || sender == fDisplayX264Options )
551     {
552         if ( [fX264optRefPopUp indexOfSelectedItem] < 3)
553         {
554             if( [fX264optMixedRefsSwitch isHidden] == false )
555             {
556                 /* Only do mixed-refs when there are at least 2 refs to mix. */
557                 [[fX264optMixedRefsSwitch animator] setHidden:YES];
558                 [[fX264optMixedRefsLabel animator] setHidden:YES];
559                 if( [fX264optMixedRefsSwitch state] == 1 )
560                     [fX264optMixedRefsSwitch performClick:self];
561             }
562         }
563         else
564         {
565             if( [fX264optMixedRefsSwitch isHidden] == true )
566             {
567                 [[fX264optMixedRefsSwitch animator] setHidden:NO];
568                 [[fX264optMixedRefsLabel animator] setHidden:NO];
569             }
570         }
571     }
572     
573     if( sender == fX264optMotionEstPopUp || sender == nil || sender == fDisplayX264Options )
574     {
575         if ( [fX264optMotionEstPopUp indexOfSelectedItem] < 3 )
576         {
577             /* ME-range can only be above 16 if me >= umh
578               and changing it to < 16 is idiotic so hide it . */
579             if( [fX264optMERangePopUp isHidden] == false )
580             {
581                 [[fX264optMERangePopUp animator] setHidden:YES];
582                 [[fX264optMERangeLabel animator] setHidden:YES];
583                 if ( [fX264optMERangePopUp indexOfSelectedItem] > 0 )
584                 {
585                     [fX264optMERangePopUp selectItemAtIndex:0];
586                     [[fX264optMERangePopUp cell] performClick:self];
587                 }
588             }
589         }
590         else
591         {
592             if( [fX264optMERangePopUp isHidden] == true )
593             {
594                 [[fX264optMERangePopUp animator] setHidden:NO];
595                 [[fX264optMERangeLabel animator] setHidden:NO];
596             }
597         }
598     }
599     
600     if( sender == fX264optSubmePopUp || sender == nil || sender == fDisplayX264Options )
601     {
602         if( [fX264optSubmePopUp indexOfSelectedItem] != 0 && [fX264optSubmePopUp indexOfSelectedItem] < 7 )
603         {
604             /* No Psy-RDO or Psy=trel if subme < 6. */
605             if( [fX264optPsyRDSlider isHidden] == false )
606             {
607                 [[fX264optPsyRDSlider animator] setHidden:YES];
608                 [[fX264optPsyRDLabel animator] setHidden:YES];
609                 [[fX264optPsyRDSlider animator] setFloatValue:1];
610                 if ( [fX264optPsyRDSlider floatValue] < 1.0 )
611                 {
612                     [fX264optPsyRDSlider setFloatValue:1.0];
613                     [[fX264optPsyRDSlider cell] performClick:self];            
614                 }
615             }
616
617             if( [fX264optPsyTrellisSlider isHidden] == false)
618             {
619                 [[fX264optPsyTrellisSlider animator] setHidden:YES];
620                 [[fX264optPsyTrellisLabel animator] setHidden:YES];
621                 [[fX264optPsyTrellisSlider animator] setFloatValue:0];
622                 if ( [fX264optPsyTrellisSlider floatValue] > 0.0 )
623                 {
624                     [fX264optPsyTrellisSlider setFloatValue:0.0];
625                     [[fX264optPsyTrellisSlider cell] performClick:self];
626                 }
627             }
628         }
629         else
630         {
631             if( [fX264optPsyRDSlider isHidden] == true )
632             {
633                 [[fX264optPsyRDSlider animator] setHidden:NO];
634                 [[fX264optPsyRDLabel animator] setHidden:NO];
635             }
636
637             if( [fX264optTrellisPopUp indexOfSelectedItem] >= 2 && [fX264optCabacSwitch state] == true && [fX264optPsyTrellisSlider isHidden] == true )
638             {
639                 [[fX264optPsyTrellisSlider animator] setHidden:NO];
640                 [[fX264optPsyTrellisLabel animator] setHidden:NO];
641             }
642         }
643     }
644     
645     if( sender == fX264optTrellisPopUp || sender == nil || sender == fDisplayX264Options )
646     {
647         if( [fX264optTrellisPopUp indexOfSelectedItem] < 2 )
648         {
649             if( [fX264optPsyTrellisSlider isHidden] == false )
650             {
651                 /* No Psy-trellis without trellis. */
652                 [[fX264optPsyTrellisSlider animator] setHidden:YES];
653                 [[fX264optPsyTrellisLabel animator] setHidden:YES];
654                 [[fX264optPsyTrellisSlider animator] setFloatValue:0.0];
655                 [[fX264optPsyTrellisSlider cell] performClick:self];
656             }
657         }
658         else
659         {
660             if( ( [fX264optSubmePopUp indexOfSelectedItem] == 0 || [fX264optSubmePopUp indexOfSelectedItem] >= 7 ) && [fX264optCabacSwitch state] == true  && [fX264optPsyTrellisSlider isHidden] == true )
661             {
662                 [[fX264optPsyTrellisSlider animator] setHidden:NO];
663                 [[fX264optPsyTrellisLabel animator] setHidden:NO];
664             }
665         }
666     }
667 }
668
669 /**
670  * Resets the GUI widgets to the contents of the option string.
671  */
672 - (IBAction) X264AdvancedOptionsSetCurrentSettings: (id) sender
673 {
674     /* Set widgets depending on the opt string in field */
675     NSString * thisOpt; // The separated option such as "bframes=3"
676     NSString * optName = @""; // The option name such as "bframes"
677     NSString * optValue = @"";// The option value such as "3"
678     NSArray *currentOptsArray;
679     
680     /*First, we get an opt string to process */
681     NSString *currentOptString = [fDisplayX264Options stringValue];
682     
683     /* Verify there is an opt string to process by making sure an
684        option is getting its value set. If so, start to process it. */
685     NSRange currentOptRange = [currentOptString rangeOfString:@"="];
686     if (currentOptRange.location != NSNotFound)
687     {
688         /*Put individual options into an array based on the ":" separator for processing, result is "<opt>=<value>"*/
689         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
690         
691         /*iterate through the array and get <opts> and <values*/
692         int loopcounter;
693         int currentOptsArrayCount = [currentOptsArray count];
694         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
695         {
696             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
697             
698             /* Verify the option sets a value */
699             NSRange splitOptRange = [thisOpt rangeOfString:@"="];            
700             if (splitOptRange.location != NSNotFound)
701             {
702                 /* Split thisOpt into an optName setting an optValue. */
703                 optName = [thisOpt substringToIndex:splitOptRange.location];
704                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
705                 
706                 /*Run through the available widgets for x264 opts and set them, as you add widgets, 
707                     they need to be added here. This should be moved to its own method probably*/
708                 
709                 /*bframes NSPopUpButton*/
710                 if ([optName isEqualToString:@"bframes"])
711                 {
712                     [fX264optBframesPopUp selectItemAtIndex:[optValue intValue]+1];
713                 }
714                 /*ref NSPopUpButton*/
715                 if ([optName isEqualToString:@"ref"])
716                 {
717                     [fX264optRefPopUp selectItemAtIndex:[optValue intValue]+1];
718                 }
719                 /*No Fast PSkip NSButton*/
720                 if ([optName isEqualToString:@"no-fast-pskip"])
721                 {
722                     [fX264optNfpskipSwitch setState:[optValue intValue]];
723                 }
724                 /*No Dict Decimate NSButton*/
725                 if ([optName isEqualToString:@"no-dct-decimate"])
726                 {
727                     [fX264optNodctdcmtSwitch setState:[optValue intValue]];
728                 }
729                 /*Sub Me NSPopUpButton*/
730                 if ([optName isEqualToString:@"subq"])
731                 {
732                     [fX264optSubmePopUp selectItemAtIndex:[optValue intValue]+1];
733                 }
734                 /*Trellis NSPopUpButton*/
735                 if ([optName isEqualToString:@"trellis"])
736                 {
737                     [fX264optTrellisPopUp selectItemAtIndex:[optValue intValue]+1];
738                 }
739                 /*Mixed Refs NSButton*/
740                 if ([optName isEqualToString:@"mixed-refs"])
741                 {
742                     [fX264optMixedRefsSwitch setState:[optValue intValue]];
743                 }
744                 /*Motion Estimation NSPopUpButton*/
745                 if ([optName isEqualToString:@"me"])
746                 {
747                     if ([optValue isEqualToString:@"dia"])
748                         [fX264optMotionEstPopUp selectItemAtIndex:1];
749                     else if ([optValue isEqualToString:@"hex"])
750                         [fX264optMotionEstPopUp selectItemAtIndex:2];
751                     else if ([optValue isEqualToString:@"umh"])
752                         [fX264optMotionEstPopUp selectItemAtIndex:3];
753                     else if ([optValue isEqualToString:@"esa"])
754                         [fX264optMotionEstPopUp selectItemAtIndex:4];
755                     else if ([optValue isEqualToString:@"tesa"])
756                         [fX264optMotionEstPopUp selectItemAtIndex:5];
757                 }
758                 /*ME Range NSPopUpButton*/
759                 if ([optName isEqualToString:@"merange"])
760                 {
761                     [fX264optMERangePopUp selectItemAtIndex:[optValue intValue]-3];
762                 }
763                 /* Adaptive B-Frames NSPopUpButton*/
764                 if ([optName isEqualToString:@"b-adapt"])
765                 {
766                     [fX264optBAdaptPopUp selectItemAtIndex:[optValue intValue]+1];
767                 }
768                 /*Weighted B-Frames NSButton*/
769                 if ([optName isEqualToString:@"weightb"])
770                 {
771                     [fX264optWeightBSwitch setState:[optValue intValue]];
772                 }
773                 /*B Pyramid NSPButton*/
774                 if ([optName isEqualToString:@"b-pyramid"])
775                 {
776                     [fX264optBPyramidSwitch setState:[optValue intValue]];
777                 }
778                 /*Direct B-frame Prediction NSPopUpButton*/
779                 if ([optName isEqualToString:@"direct"])
780                 {
781                     if ([optValue isEqualToString:@"none"])
782                         [fX264optDirectPredPopUp selectItemAtIndex:1];
783                     else if ([optValue isEqualToString:@"spatial"])
784                         [fX264optDirectPredPopUp selectItemAtIndex:2];
785                     else if ([optValue isEqualToString:@"temporal"])
786                         [fX264optDirectPredPopUp selectItemAtIndex:3];
787                     else if ([optValue isEqualToString:@"auto"])
788                         [fX264optDirectPredPopUp selectItemAtIndex:4];                        
789                 }
790                 /*Deblocking NSPopUpButtons*/
791                 if ([optName isEqualToString:@"deblock"])
792                 {
793                     NSString * alphaDeblock = @"";
794                     NSString * betaDeblock = @"";
795                     
796                     NSRange splitDeblock = [optValue rangeOfString:@","];
797                     alphaDeblock = [optValue substringToIndex:splitDeblock.location];
798                     betaDeblock = [optValue substringFromIndex:splitDeblock.location + 1];
799                     
800                     if ([alphaDeblock isEqualToString:@"0"] && [betaDeblock isEqualToString:@"0"])
801                     {
802                         /* When both filters are at 0, default */
803                         [fX264optAlphaDeblockPopUp selectItemAtIndex:0];                        
804                         [fX264optBetaDeblockPopUp selectItemAtIndex:0];                               
805                     }
806                     else
807                     {
808                         if (![alphaDeblock isEqualToString:@"0"])
809                         {
810                             /* Alpha isn't 0, so set it. The offset of 7 is
811                                because filters start at -6 instead of at 0. */
812                             [fX264optAlphaDeblockPopUp selectItemAtIndex:[alphaDeblock intValue]+7];
813                         }
814                         else
815                         {
816                             /* Set alpha filter to 0, which is 7 up
817                                because filters start at -6, not 0. */
818                             [fX264optAlphaDeblockPopUp selectItemAtIndex:7];                        
819                         }
820                         
821                         if (![betaDeblock isEqualToString:@"0"])
822                         {
823                             /* Beta isn't 0, so set it. */
824                             [fX264optBetaDeblockPopUp selectItemAtIndex:[betaDeblock intValue]+7];
825                         }
826                         else
827                         {
828                             /* Set beta filter to 0. */
829                             [fX264optBetaDeblockPopUp selectItemAtIndex:7];                        
830                         }
831                     }
832                 }
833                 /* Analysis NSPopUpButton */
834                 if ([optName isEqualToString:@"analyse"])
835                 {
836                     if ([optValue isEqualToString:@"p8x8,b8x8,i8x8,i4x4"])
837                     {
838                         /* Default ("some") */
839                         [fX264optAnalysePopUp selectItemAtIndex:0];
840                     }
841                     if ([optValue isEqualToString:@"none"])
842                     {
843                         [fX264optAnalysePopUp selectItemAtIndex:1];
844                     }
845                     if ([optValue isEqualToString:@"all"])
846                     {
847                         [fX264optAnalysePopUp selectItemAtIndex:2];
848                     }
849                 }
850                 /* 8x8 DCT NSButton */
851                 if ([optName isEqualToString:@"8x8dct"])
852                 {
853                     [fX264opt8x8dctSwitch setState:[optValue intValue]];
854                 }
855                 /* CABAC NSButton */
856                 if ([optName isEqualToString:@"cabac"])
857                 {
858                     [fX264optCabacSwitch setState:[optValue intValue]];
859                 }
860                 /* Psy-RD and Psy-Trellis NSSliders */
861                 if ([optName isEqualToString:@"psy-rd"])
862                 {
863                     NSString * rdOpt = @"";
864                     NSString * trellisOpt = @"";
865                     
866                     NSRange splitRD = [optValue rangeOfString:@","];
867                     rdOpt = [optValue substringToIndex:splitRD.location];
868                     trellisOpt = [optValue substringFromIndex:splitRD.location + 1];
869                     
870                     [fX264optPsyRDSlider setFloatValue:[rdOpt floatValue]];
871                     [fX264optPsyTrellisSlider setFloatValue:[trellisOpt floatValue]];
872                 }                                                              
873             }
874         }
875     }
876 }
877
878 - (NSString *) X264AdvancedOptionsOptIDToString: (id) widget
879 {
880     /*Determine which outlet is being used and set optName to process accordingly */
881     NSString * optNameToChange = @""; // The option name such as "bframes"
882     
883     if (widget == fX264optBframesPopUp)
884     {
885         optNameToChange = @"bframes";
886     }
887     if (widget == fX264optRefPopUp)
888     {
889         optNameToChange = @"ref";
890     }
891     if (widget == fX264optNfpskipSwitch)
892     {
893         optNameToChange = @"no-fast-pskip";
894     }
895     if (widget == fX264optNodctdcmtSwitch)
896     {
897         optNameToChange = @"no-dct-decimate";
898     }
899     if (widget == fX264optSubmePopUp)
900     {
901         optNameToChange = @"subq";
902     }
903     if (widget == fX264optTrellisPopUp)
904     {
905         optNameToChange = @"trellis";
906     }
907     if (widget == fX264optMixedRefsSwitch)
908     {
909         optNameToChange = @"mixed-refs";
910     }
911     if (widget == fX264optMotionEstPopUp)
912     {
913         optNameToChange = @"me";
914     }
915     if (widget == fX264optMERangePopUp)
916     {
917         optNameToChange = @"merange";
918     }
919     if (widget == fX264optBAdaptPopUp)
920     {
921         optNameToChange = @"b-adapt";
922     }
923     if (widget == fX264optWeightBSwitch)
924     {
925         optNameToChange = @"weightb";
926     }
927     if (widget == fX264optBPyramidSwitch)
928     {
929         optNameToChange = @"b-pyramid";
930     }
931     if (widget == fX264optDirectPredPopUp)
932     {
933         optNameToChange = @"direct";
934     }
935     if (widget == fX264optAlphaDeblockPopUp)
936     {
937         optNameToChange = @"deblock";
938     }
939     if (widget == fX264optBetaDeblockPopUp)
940     {
941         optNameToChange = @"deblock";
942     }        
943     if (widget == fX264optAnalysePopUp)
944     {
945         optNameToChange = @"analyse";
946     }
947     if (widget == fX264opt8x8dctSwitch)
948     {
949         optNameToChange = @"8x8dct";
950     }
951     if (widget == fX264optCabacSwitch)
952     {
953         optNameToChange = @"cabac";
954     }
955     if( widget == fX264optPsyRDSlider)
956     {
957         optNameToChange = @"psy-rd";
958     }
959     if( widget == fX264optPsyTrellisSlider)
960     {
961         optNameToChange = @"psy-rd";
962     }
963     
964     return optNameToChange;
965 }
966
967 - (NSString *) X264AdvancedOptionsWidgetToString: (NSString *) optName withID: (id) sender
968 {
969     NSString * thisOpt = @""; // The option=value string the method will return
970     
971     if ([optName isEqualToString:@"deblock"])
972     {
973         if ((([fX264optAlphaDeblockPopUp indexOfSelectedItem] == 0) || ([fX264optAlphaDeblockPopUp indexOfSelectedItem] == 7)) && (([fX264optBetaDeblockPopUp indexOfSelectedItem] == 0) || ([fX264optBetaDeblockPopUp indexOfSelectedItem] == 7)))
974         {
975             /* When both deblock widgets are 0 or default or a mix of the two,
976                use a blank string, since deblocking defaults to 0,0.           */
977             thisOpt = @"";                                
978         }
979         else
980         {
981             /* Otherwise the format is deblock=a,b, where a and b both have an array
982                offset of 7 because deblocking values start at -6 instead of at zero. */
983             thisOpt = [NSString stringWithFormat:@"%@=%d,%d",optName, ([fX264optAlphaDeblockPopUp indexOfSelectedItem] != 0) ? [fX264optAlphaDeblockPopUp indexOfSelectedItem]-7 : 0,([fX264optBetaDeblockPopUp indexOfSelectedItem] != 0) ? [fX264optBetaDeblockPopUp indexOfSelectedItem]-7 : 0];
984         }
985     }
986     
987     else if ([optName isEqualToString:@"psy-rd"])
988     {
989         if( [fX264optPsyRDSlider floatValue] == 1.0 && [fX264optPsyTrellisSlider floatValue] == 0.0 ) 
990         {
991             /* When  PsyRD is 1 and PsyTrel is 0 they're default values and can be ignored. */
992             thisOpt = @"";                                
993         }
994         else
995         {
996             /* Otherwise the format is deblock=a,b, where a and b both have an array
997                offset of 7 because deblocking values start at -6 instead of at zero. */
998             thisOpt = [NSString stringWithFormat:@"%@=%0.1f,%0.1f", optName, [fX264optPsyRDSlider floatValue], [fX264optPsyTrellisSlider floatValue] ];
999         }
1000     }
1001     
1002     else if /*Boolean Switches*/ ([optName isEqualToString:@"mixed-refs"] || [optName isEqualToString:@"weightb"] ||  [optName isEqualToString:@"b-pyramid"] || [optName isEqualToString:@"no-fast-pskip"] || [optName isEqualToString:@"no-dct-decimate"] || [optName isEqualToString:@"8x8dct"] )
1003     {
1004         /* Here is where we take care of the boolean options that work overtly:
1005            no-dct-decimate being on means no-dct-decimate=1, etc. Some options
1006            require the inverse, but those will be handled a couple lines down. */
1007         if ([sender state] == 0)
1008         {
1009             /* When these options are false, don't include them. They all default
1010                to being set off, so they don't need to be mentioned at all.       */
1011             thisOpt = @"";
1012         }
1013         else
1014         {
1015             /* Otherwise, include them as optioname=1 */
1016             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,1];
1017         }
1018     }
1019     
1020     else if ([optName isEqualToString:@"cabac"])
1021     {
1022         /* CABAC is odd, in that it defaults to being on. That means
1023            it only needs to be included in the string when turned off. */
1024         if ([sender state] == 1)
1025         {
1026             /* It's true so don't include it. */
1027             thisOpt = @"";
1028         }
1029         else
1030         {
1031             /* Otherwise, include cabac=0 in the string to enable CAVLC. */
1032             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,0];
1033         }
1034     }
1035                                             
1036     else if (([sender indexOfSelectedItem] == 0) && (sender != fX264optAlphaDeblockPopUp) && (sender != fX264optBetaDeblockPopUp) ) // means that "unspecified" is chosen, lets then remove it from the string
1037     {
1038         /* When a widget is at index 0, it's default. Default means don't add to the string.
1039            The exception for deblocking is because for those, *both* need to at index 0
1040            for it to default, so it's handled separately, above this section.                */
1041         thisOpt = @"";
1042     }
1043     
1044     else if ([optName isEqualToString:@"me"])
1045     {
1046         /* Motion estimation uses string values, so this switch
1047            pairs the widget index with the right value string.  */
1048         switch ([sender indexOfSelectedItem])
1049         {   
1050             case 1:
1051                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"dia"];
1052                 break;
1053                 
1054             case 2:
1055                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"hex"];
1056                 break;
1057                 
1058             case 3:
1059                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"umh"];
1060                 break;
1061                 
1062             case 4:
1063                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"esa"];
1064                 break;
1065             
1066             case 5:
1067                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"tesa"];
1068             
1069             default:
1070                 break;
1071         }
1072     }
1073     
1074     else if ([optName isEqualToString:@"direct"])
1075     {
1076         /* Direct prediction uses string values, so this switch
1077            pairs the right string value with the right widget index. */
1078         switch ([sender indexOfSelectedItem])
1079         {   
1080             case 1:
1081                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"none"];
1082                 break;
1083                 
1084             case 2:
1085                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"spatial"];
1086                 break;
1087                 
1088             case 3:
1089                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"temporal"];
1090                 break;
1091                 
1092             case 4:
1093                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"auto"];
1094                 break;
1095                 
1096             default:
1097                 break;
1098         }
1099     }
1100     
1101     else if ([optName isEqualToString:@"analyse"])
1102     {
1103         /* Analysis uses string values as well. */
1104         switch ([sender indexOfSelectedItem])
1105         {   
1106             case 1:
1107                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"none"];
1108                 break;
1109                 
1110             case 2:
1111                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"all"];
1112                 break;
1113                 
1114             default:
1115                 break;
1116         }
1117     }
1118     
1119     else if ([optName isEqualToString:@"merange"])
1120     {
1121         /* Motion estimation range uses an odd array offset because in addition
1122            to starting with index 0 as default, index 1 starts at 4 instead of 1,
1123            because merange can't go below 4. So it has to be handled separately.  */
1124         thisOpt = [NSString stringWithFormat:@"%@=%d",optName,[sender indexOfSelectedItem]+3];
1125     }
1126     
1127     else if ([optName isEqualToString:@"b-adapt"])
1128     {
1129         /* B-adapt starts at index 0 with default then goes 0, 1, 2)*/
1130         thisOpt = [NSString stringWithFormat:@"%@=%d", optName, [sender indexOfSelectedItem]-1];
1131     }
1132     
1133     else // we have a valid value to change, so change it
1134     {
1135         if ( [sender indexOfSelectedItem] != 0 )
1136         /* Here's our general case, that catches things like ref frames and b-frames.
1137            Basically, any options that are PopUp menus with index 0 as default and
1138            index 1 as 1, with numerical values, are all handled right here. All of
1139            the above stuff is for the exceptions to the general case.              */
1140             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,[sender indexOfSelectedItem]-1];
1141     }
1142     
1143     return thisOpt;
1144 }
1145
1146 - (BOOL) X264AdvancedOptionsIsOpt: (NSString *) optNameToChange inString: (NSString *) currentOptString
1147 {
1148     /* If the option is in the string but not the beginning of it,
1149        it will be in the form of ":optName=value" so we really want
1150        to be looking for ":optNameToChange=" rather than "optNameToChange". */
1151     NSString *checkOptNameToChange = [NSString stringWithFormat:@":%@=",optNameToChange];
1152     
1153     /* Now we store the part of the string up through the option name in currentOptRange. */
1154     NSRange currentOptRange = [currentOptString rangeOfString:checkOptNameToChange];
1155
1156     /*  We need to know if the option is at the beginning of the string.
1157         If it is at the start, it won't be preceded by a colon.
1158         To figure this out, we'll use the rangeOfString method. First,
1159         store what the option name would be if if it was at the beginning,
1160         in checkOptNameToChangeBeginning. Then, find its range in the string.
1161         If the range is 0, it's the first option listed in the string.       */        
1162     NSString *checkOptNameToChangeBeginning = [NSString stringWithFormat:@"%@=",optNameToChange];
1163     NSRange currentOptRangeBeginning = [currentOptString rangeOfString:checkOptNameToChangeBeginning];
1164
1165     if (currentOptRange.location != NSNotFound || currentOptRangeBeginning.location == 0)
1166         return true;
1167     else
1168         return false;
1169
1170
1171 /**
1172  * Resets the option string to mirror the GUI widgets.
1173  */
1174 - (IBAction) X264AdvancedOptionsChanged: (id) sender
1175 {
1176     /* Look up the equivalent string option name of the calling widget. */
1177     NSString * optNameToChange = [self X264AdvancedOptionsOptIDToString: sender];
1178     
1179     NSString * thisOpt = @"";  // The separated option such as "bframes=3"
1180     NSString * optName = @"";  // The option name such as "bframes"
1181     NSString * optValue = @""; // The option value such as "3"
1182     NSArray *currentOptsArray;
1183     
1184     /* Get the current opt string being displayed. */
1185     NSString *currentOptString = [fDisplayX264Options stringValue];
1186     
1187     /* There are going to be a few possibilities.
1188        - The option might start off the string.
1189        - The option might be in the middle of the string.
1190        - The option might not be in the string at all yet.
1191        - The string itself might not yet exist.             */
1192     
1193     if( [self X264AdvancedOptionsIsOpt: optNameToChange inString: currentOptString] )
1194     {
1195         /* If the option is in the string wth a semicolon, or starts the string, it's time to edit.
1196            This means parsing the whole string into an array of options and values. From there,
1197            iterate through the options, and when you reach the one that's been changed, edit it.   */
1198         
1199         /* Create new empty opt string*/
1200         NSString *changedOptString = @"";
1201         
1202         /* Put individual options into an array based on the ":"
1203            separator for processing, result is "<opt>=<value>"   */
1204         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
1205         
1206         /* Iterate through the array and get <opts> and <values*/
1207         int loopcounter;
1208         int currentOptsArrayCount = [currentOptsArray count];
1209         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
1210         {
1211             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
1212             NSRange splitOptRange = [thisOpt rangeOfString:@"="];
1213             
1214             if (splitOptRange.location != NSNotFound)
1215             {
1216                 /* First off, it's time to handle option strings that
1217                    already have at least one option=value pair in them. */
1218                    
1219                 optName = [thisOpt substringToIndex:splitOptRange.location];
1220                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
1221
1222                 /*If the optNameToChange is found, appropriately change the value or delete it if
1223                     "Unspecified" is set.*/
1224                 if ([optName isEqualToString:optNameToChange])
1225                 {
1226                     thisOpt = [self X264AdvancedOptionsWidgetToString: optName withID: sender];
1227                 }
1228             }
1229             
1230             /* Construct New String for opts here */
1231             if ([thisOpt isEqualToString:@""])
1232             {
1233                 /* Blank option, so just add it to the string. (Why?) */
1234                 changedOptString = [NSString stringWithFormat:@"%@%@",changedOptString,thisOpt];
1235             }
1236             else
1237             {
1238                 if ([changedOptString isEqualToString:@""])
1239                 {
1240                     /* No existing string, make the string this option. */
1241                     changedOptString = [NSString stringWithFormat:@"%@",thisOpt];
1242                 }
1243                 else
1244                 {
1245                     /* Existing string, existing option. Append the
1246                        option to the string, preceding it with a colon. */
1247                     changedOptString = [NSString stringWithFormat:@"%@:%@",changedOptString,thisOpt];
1248                 }
1249             }
1250         }
1251         
1252         /* Change the dislayed option string to reflect the new modified settings */
1253         [fDisplayX264Options setStringValue:[NSString stringWithFormat:changedOptString]];    
1254     }
1255     else // if none exists, add it to the string
1256     {
1257         /* This is where options that aren't already in the string are handled. */
1258         if ([[fDisplayX264Options stringValue] isEqualToString: @""])
1259         {
1260             
1261             [fDisplayX264Options setStringValue:
1262                 [self X264AdvancedOptionsWidgetToString: optNameToChange withID: sender]];
1263         }
1264         else
1265         {
1266             /* The string isn't empty, and the option isn't already in it, so
1267                it will need to be appended to the current string with a colon,
1268                as long as the string to be appended isn't just blank (default). */
1269             if( [[self X264AdvancedOptionsWidgetToString: optNameToChange withID: sender] isEqualToString: @""] == false )
1270             {
1271                 [fDisplayX264Options setStringValue:
1272                     [NSString stringWithFormat:@"%@:%@",
1273                         currentOptString,
1274                         [self X264AdvancedOptionsWidgetToString: optNameToChange withID: sender] ]];                
1275             }
1276         }
1277     }
1278     
1279     /* We now need to reset the opt widgets since we changed some stuff */        
1280     [self X264AdvancedOptionsSet:sender];        
1281 }
1282
1283 @end