OSDN Git Service

6ce8ddcb1ec3809e9ca6a367916e274b15c40365
[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,
68         fX264optNodctdcmtLabel,fX264optNodctdcmtSwitch,fX264optSubmeLabel,fX264optSubmePopUp,
69         fX264optTrellisLabel,fX264optTrellisPopUp, fX264optWeightPLabel, fX264optWeightPSwitch,
70         fX264optMotionEstLabel,fX264optMotionEstPopUp,fX264optMERangeLabel,fX264optMERangePopUp,
71         fX264optBPyramidLabel,fX264optBPyramidPopUp, fX264optAqLabel, fX264optAqSlider,
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     NSString * toolTip = @"";
108     
109     /*B-Frames fX264optBframesPopUp*/
110     int i;
111     [fX264optBframesPopUp removeAllItems];
112     [fX264optBframesPopUp addItemWithTitle:@"Default (3)"];
113     for (i=0; i<17;i++)
114     {
115         [fX264optBframesPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
116     }
117     toolTip =
118         @"Sane values are ~2-5.  This specifies the maximum number of sequential B-frames that the encoder can use.  Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.  Cel-animated source material and B-pyramid also significantly increase the usefulness of larger values. Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off).";
119     [fX264optBframesPopUp setToolTip: toolTip];
120     [fX264optBframesLabel setToolTip: toolTip];
121     
122     /*Reference Frames fX264optRefPopUp*/
123     [fX264optRefPopUp removeAllItems];
124     [fX264optRefPopUp addItemWithTitle:@"Default (3)"];
125     for (i=1; i<17;i++)
126     {
127         [fX264optRefPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
128     }
129     toolTip =
130         @"Sane values are ~1-6.  The more you add, the better the compression, but the slower the encode.  Cel animation tends to benefit from more reference frames a lot more than film content.  Note that many hardware devices have limitations on the number of supported reference frames, so if you're encoding for a handheld or standalone player, don't touch this unless you're absolutely sure you know what you're doing!";
131     [fX264optRefPopUp setToolTip: toolTip];
132     [fX264optRefLabel setToolTip: toolTip];
133
134     /*Weight-P fX264optWeightPSwitch BOOLEAN*/
135     [fX264optWeightPSwitch setState:1];
136     toolTip = 
137         @"Performs extra analysis to decide upon weighting parameters for each frame.  This improves overall compression slightly and improves the quality of fades greatly. Baseline profile, as required for iPods and similar devices, requires weighted P-frame prediction to be disabled.  Note that some devices and players, even those that support Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is completely incompatible with it, for example.";
138     [fX264optWeightPSwitch setToolTip: toolTip];
139     [fX264optWeightPLabel setToolTip: toolTip];
140     
141     /*No Dict Decimate fX264optNodctdcmtSwitch BOOLEAN*/
142     [fX264optNodctdcmtSwitch setState:0];    
143     toolTip =
144         @"x264 normally zeroes out nearly-empty data blocks to save bits to be better used for some other purpose in the video.  However, this can sometimes have slight negative effects on retention of subtle grain and dither.  Don't touch this unless you're having banding issues or other such cases where you are having trouble keeping fine noise.";
145     [fX264optNodctdcmtSwitch setToolTip: toolTip];
146     [fX264optNodctdcmtLabel setToolTip: toolTip];
147     
148     /*Sub Me fX264optSubmePopUp*/
149     [fX264optSubmePopUp removeAllItems];
150     [fX264optSubmePopUp addItemWithTitle:@"Default (7)"];
151     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"0: SAD, no subpel (super fast!)"]];
152     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"1: SAD, qpel"]];
153     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"2: SATD, qpel"]];
154     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"3: SATD, multi-qpel"]];
155     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"4: SATD, qpel on all"]];
156     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"5: SATD, multi-qpel on all"]];
157     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"6: RD in I/P-frames"]];
158     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"7: RD in all frames"]];
159     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"8: RD refine in I/P-frames"]];
160     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"9: RD refine in all frames"]];
161     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"10: QPRD in all frames"]];
162     toolTip =
163         @"This setting controls both subpixel-precision motion estimation and mode decision methods.\n\nSubpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n\nMode decision is the method used to choose how to encode each block of the frame: a very important decision.\n\nSAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n\n6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n\n10, the most powerful and slowest option, requires trellis=2.";
164     [fX264optSubmePopUp setToolTip: toolTip];
165     [fX264optSubmeLabel setToolTip: toolTip];
166     
167     /*Trellis fX264optTrellisPopUp*/
168     [fX264optTrellisPopUp removeAllItems];
169     [fX264optTrellisPopUp addItemWithTitle:@"Default (Encode only)"];
170     [fX264optTrellisPopUp addItemWithTitle:[NSString stringWithFormat:@"Off"]];
171     [fX264optTrellisPopUp addItemWithTitle:[NSString stringWithFormat:@"Encode only"]];
172     [fX264optTrellisPopUp addItemWithTitle:[NSString stringWithFormat:@"Always"]];
173     [fX264optTrellisPopUp setWantsLayer:YES];
174     toolTip =
175         @"Trellis fine-tunes the rounding of transform coefficients to squeeze out 3-5% more compression at the cost of some speed. \"Always\" uses trellis not only during the main encoding process, but also during analysis, which improves compression even more, albeit at great speed cost.  Trellis costs more speed at higher bitrates and requires CABAC.";
176     [fX264optTrellisPopUp setToolTip: toolTip];
177     [fX264optTrellisLabel setToolTip: toolTip];
178     
179     /*Motion Estimation fX264optMotionEstPopUp*/
180     [fX264optMotionEstPopUp removeAllItems];
181     [fX264optMotionEstPopUp addItemWithTitle:@"Default (Hexagon)"];
182     [fX264optMotionEstPopUp addItemWithTitle:@"Diamond"];
183     [fX264optMotionEstPopUp addItemWithTitle:@"Hexagon"];
184     [fX264optMotionEstPopUp addItemWithTitle:@"Uneven Multi-Hexagon"];
185     [fX264optMotionEstPopUp addItemWithTitle:@"Exhaustive"];
186     [fX264optMotionEstPopUp addItemWithTitle:@"Transformed Exhaustive"];
187     toolTip =
188         @"Controls the motion estimation method. Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.  A better motion search method improves compression at the cost of speed.\n\nDiamond: performs an extremely fast and simple search using a diamond pattern.\n\nHexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n\nUneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n\nExhaustive: performs a \"dumb\" search of every pixel in a wide area.  Significantly slower for only a small compression gain.\n\nTransformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement.";
189     [fX264optMotionEstPopUp setToolTip: toolTip];
190     [fX264optMotionEstLabel setToolTip: toolTip];
191     
192     /*Motion Estimation range fX264optMERangePopUp*/
193     [fX264optMERangePopUp removeAllItems];
194     [fX264optMERangePopUp addItemWithTitle:@"Default (16)"];
195     for (i=4; i<65;i++)
196     {
197         [fX264optMERangePopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
198     }
199     toolTip =
200         @"This is the distance x264 searches from its best guess at the motion of a block in order to try to find its actual motion.  Doesn't apply to Diamond or Hexagon search options.  The default is fine for most content, but extremely high motion video, especially at HD resolutions, may benefit from higher ranges, albeit at a high speed cost.";
201     [fX264optMERangePopUp setToolTip: toolTip];
202     [fX264optMERangeLabel setToolTip: toolTip];
203     
204     /*B-frame Pyramids fX264optBPyramidPopUp*/
205     [fX264optBPyramidPopUp removeAllItems];
206     [fX264optBPyramidPopUp addItemWithTitle:@"Default (Normal)"];
207     [fX264optBPyramidPopUp addItemWithTitle:@"Off"];
208     [fX264optBPyramidPopUp addItemWithTitle:@"Strict"];
209     [fX264optBPyramidPopUp setWantsLayer:YES];
210     toolTip =
211         @"B-pyramid improves compression by creating a pyramidal structure (hence the name) of B-frames, allowing B-frames to reference each other to improve compression.  Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit.";
212     [fX264optBPyramidPopUp setToolTip: toolTip];
213     [fX264optBPyramidLabel setToolTip: toolTip];
214     
215     /*Direct B-Frame Prediction Mode fX264optDirectPredPopUp*/
216     [fX264optDirectPredPopUp removeAllItems];
217     [fX264optDirectPredPopUp addItemWithTitle:@"Default (Spatial)"];
218     [fX264optDirectPredPopUp addItemWithTitle:@"None"];
219     [fX264optDirectPredPopUp addItemWithTitle:@"Spatial"];
220     [fX264optDirectPredPopUp addItemWithTitle:@"Temporal"];
221     [fX264optDirectPredPopUp addItemWithTitle:@"Automatic"];
222     [fX264optDirectPredPopUp setWantsLayer:YES];
223     toolTip =
224         @"H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n\nSpatial, the default, is almost always better, but temporal is sometimes useful too.\n\nx264 can, at the cost of a small amount of speed (and accordingly for a small compression gain), adaptively select which is better for each particular frame.";
225     [fX264optDirectPredPopUp setToolTip: toolTip];
226     [fX264optDirectPredLabel setToolTip: toolTip];
227     
228     /* Adaptive B-Frames Mode fX264optBAdaptPopUp */
229     [fX264optBAdaptPopUp removeAllItems];
230     [fX264optBAdaptPopUp addItemWithTitle:@"Default (Fast)"];
231     [fX264optBAdaptPopUp addItemWithTitle:@"Off"];
232     [fX264optBAdaptPopUp addItemWithTitle:@"Fast"];
233     [fX264optBAdaptPopUp addItemWithTitle:@"Optimal"];
234     [fX264optBAdaptPopUp setWantsLayer:YES];
235     toolTip =
236         @"x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n\nFast mode takes roughly the same amount of time no matter how many B-frames you specify.  However, while fast, its decisions are often suboptimal.\n\nOptimal mode gets slower as the maximum number of B-Frames increases, but makes much more accurate decisions, especially when used with B-pyramid.";
237     [fX264optBAdaptPopUp setToolTip: toolTip];
238     [fX264optBAdaptLabel setToolTip: toolTip];
239     
240     /*Alpha Deblock*/
241     [fX264optAlphaDeblockPopUp removeAllItems];
242     [fX264optAlphaDeblockPopUp addItemWithTitle:@"Default (0)"];
243     for (i=-6; i<7;i++)
244     {
245         [fX264optAlphaDeblockPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
246     }
247     toolTip =
248         @"H.264 has a built-in deblocking filter that smooths out blocking artifacts after decoding each frame.  This not only improves visual quality, but also helps compression significantly. The deblocking filter takes a lot of CPU power, so if you're looking to minimize CPU requirements for video playback, disable it.\n\nThe deblocking filter has two adjustable parameters, \"strength\" and \"threshold\". The former controls how strong (or weak) the deblocker is, while the latter controls how many (or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking. The default is 0 (normal strength) for both parameters.";
249     [fX264optAlphaDeblockPopUp setToolTip: toolTip];
250     [fX264optDeblockLabel setToolTip: toolTip];
251
252     /*Beta Deblock*/
253     [fX264optBetaDeblockPopUp removeAllItems];
254     [fX264optBetaDeblockPopUp addItemWithTitle:@"Default (0)"];
255     for (i=-6; i<7;i++)
256     {
257         [fX264optBetaDeblockPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
258     }
259     [fX264optBetaDeblockPopUp setToolTip: toolTip];
260     [fX264optDeblockLabel setToolTip: toolTip];
261
262     /* Analysis fX264optAnalysePopUp */
263     [fX264optAnalysePopUp removeAllItems];
264     [fX264optAnalysePopUp addItemWithTitle:@"Default (Most)"]; /* 0=default */
265     [fX264optAnalysePopUp addItemWithTitle:[NSString stringWithFormat:@"None"]]; /* 1=none */
266     [fX264optAnalysePopUp addItemWithTitle:[NSString stringWithFormat:@"Some"]]; /* 2=some */
267     [fX264optAnalysePopUp addItemWithTitle:[NSString stringWithFormat:@"All"]]; /* 3=all */
268     toolTip =
269         @"Mode decision picks from a variety of options to make its decision: this option chooses what options those are.  Fewer partitions to check means faster encoding, at the cost of worse decisions, since the best option might have been one that was turned off.";
270     [fX264optAnalysePopUp setToolTip: toolTip];
271     [fX264optAnalyseLabel setToolTip: toolTip];
272
273     /* 8x8 DCT fX264op8x8dctSwitch */
274     [fX264opt8x8dctSwitch setState:1];
275     [fX264opt8x8dctSwitch setWantsLayer:YES];
276     toolTip =
277         @"The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.  It improves compression by at least 5% at a very small speed cost and may provide an unusually high visual quality benefit compared to its compression gain.  However, it requires High Profile, which many devices may not support.";
278     [fX264opt8x8dctSwitch setToolTip: toolTip];
279     [fX264opt8x8dctLabel setToolTip: toolTip];
280
281     /* CABAC fX264opCabacSwitch */
282     [fX264optCabacSwitch setState:1];
283     toolTip =
284         @"After the encoder has done its work, it has a bunch of data that needs to be compressed losslessly, similar to ZIP or RAR.  H.264 provides two options for this: CAVLC and CABAC.  CABAC decodes a lot slower but compresses significantly better (10-30%), especially at lower bitrates.  If you're looking to minimize CPU requirements for video playback, disable this option. Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled.";
285     [fX264optCabacSwitch setToolTip: toolTip];
286     [fX264optCabacLabel setToolTip: toolTip];
287
288     /* Adaptive Quantization Strength fX264opAqSlider */
289     [fX264optAqSlider setMinValue:0.0];
290     [fX264optAqSlider setMaxValue:2.0];
291     [fX264optAqSlider setTickMarkPosition:NSTickMarkBelow];
292     [fX264optAqSlider setNumberOfTickMarks:21];
293     [fX264optAqSlider setAllowsTickMarkValuesOnly:YES];
294     [fX264optAqSlider setFloatValue:1.0];
295     toolTip =
296         @"Adaptive quantization controls how the encoder distributes bits across the frame.  Higher values take more bits away from edges and complex areas to improve areas with finer detail.";
297     [fX264optAqSlider setToolTip: toolTip];
298     [fX264optAqLabel setToolTip: toolTip];
299     
300     /* PsyRDO fX264optPsyRDSlider */
301     [fX264optPsyRDSlider setMinValue:0.0];
302     [fX264optPsyRDSlider setMaxValue:2.0];
303     [fX264optPsyRDSlider setTickMarkPosition:NSTickMarkBelow];
304     [fX264optPsyRDSlider setNumberOfTickMarks:21];
305     [fX264optPsyRDSlider setAllowsTickMarkValuesOnly:YES];
306     [fX264optPsyRDSlider setFloatValue:1.0];
307     toolTip =
308         @"Psychovisual rate-distortion optimization takes advantage of the characteristics of human vision to dramatically improve apparent detail and sharpness.  The effect can be made weaker or stronger by adjusting the strength.  Being an RD algorithm, it requires mode decision to be at least \"6\".";
309     [fX264optPsyRDSlider setToolTip: toolTip];
310     [fX264optPsyRDLabel setToolTip: toolTip];
311
312     /* PsyTrellis fX264optPsyRDSlider */
313     [fX264optPsyTrellisSlider setMinValue:0.0];
314     [fX264optPsyTrellisSlider setMaxValue:1.0];
315     [fX264optPsyTrellisSlider setTickMarkPosition:NSTickMarkBelow];
316     [fX264optPsyTrellisSlider setNumberOfTickMarks:21];
317     [fX264optPsyTrellisSlider setAllowsTickMarkValuesOnly:YES];
318     [fX264optPsyTrellisSlider setFloatValue:0.0];
319     toolTip =
320         @"Psychovisual trellis is an experimental algorithm to further improve sharpness and detail retention beyond what Psychovisual RD does.  Recommended values are around 0.2, though higher values may help for very grainy video or lower bitrate encodes.  Not recommended for cel animation and other sharp-edged graphics.";
321     [fX264optPsyTrellisSlider setToolTip: toolTip];
322     [fX264optPsyTrellisLabel setToolTip: toolTip];
323
324     /* Standardize the option string */
325     [self X264AdvancedOptionsStandardizeOptString:nil];
326
327     /* Set Current GUI Settings based on newly standardized string */
328     [self X264AdvancedOptionsSetCurrentSettings:sender];
329
330     /* Fade out options that don't apply */
331     [self X264AdvancedOptionsAnimate: sender];
332 }
333
334 /**
335  * Cleans the option string to use a standard format of option=value
336  */
337 - (IBAction) X264AdvancedOptionsStandardizeOptString: (id) sender
338 {
339     /* Set widgets depending on the opt string in field */
340     NSString * thisOpt; // The separated option such as "bframes=3"
341     NSString * optName = @""; // The option name such as "bframes"
342     NSString * optValue = @"";// The option value such as "3"
343     NSString * changedOptString = @"";
344     NSArray *currentOptsArray;
345     
346     /*First, we get an opt string to process */
347     NSString *currentOptString = [fDisplayX264Options stringValue];
348     
349     /* Verify there is an opt string to process by making sure an
350        option is getting its value set. If so, start to process it. */
351     NSRange currentOptRange = [currentOptString rangeOfString:@"="];
352     if (currentOptRange.location != NSNotFound)
353     {
354         /*Put individual options into an array based on the ":" separator for processing, result is "<opt>=<value>"*/
355         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
356         
357         /*iterate through the array and get <opts> and <values*/
358         int loopcounter;
359         int currentOptsArrayCount = [currentOptsArray count];
360         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
361         {
362             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
363             
364             NSRange splitOptRange = [thisOpt rangeOfString:@"="];
365             if (splitOptRange.location != NSNotFound)
366             {
367                 optName = [thisOpt substringToIndex:splitOptRange.location];
368                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
369                 
370                 /* Standardize the names here depending on whats in the string */
371                 optName = [self X264AdvancedOptionsStandardizeOptNames:optName];
372                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,optValue];    
373             }
374             else // No value given so we use a default of "1"
375             {
376                 optName = thisOpt;
377
378                 /* Standardize the names here depending on whats in the string */
379                 optName = [self X264AdvancedOptionsStandardizeOptNames:optName];
380                 thisOpt = [NSString stringWithFormat:@"%@=%d",optName,1];
381             }
382             
383             /* Construct New String for opts here.*/
384             if ([thisOpt isEqualToString:@""])
385             {
386                 /* Blank option, just add it to the string. (Why?) */
387                 changedOptString = [NSString stringWithFormat:@"%@%@",changedOptString,thisOpt];
388             }
389             else
390             {
391                 if ([changedOptString isEqualToString:@""])
392                 {
393                     /* Blank string, output the current option. */
394                     changedOptString = [NSString stringWithFormat:@"%@",thisOpt];
395                 }
396                 else
397                 {
398                     /* Option exists and string exists, so append the option
399                        to the string with a semi-colon inbetween them.       */
400                     changedOptString = [NSString stringWithFormat:@"%@:%@",changedOptString,thisOpt];
401                 }
402             }
403         }
404     }
405     
406     /* Change the option string to reflect the new standardized option string */
407     [fDisplayX264Options setStringValue:changedOptString];
408 }
409
410 /**
411  * Cleans the option string to use a standard set of option names, by conflating synonyms.
412  */
413 - (NSString *) X264AdvancedOptionsStandardizeOptNames:(NSString *) cleanOptNameString
414 {
415     /* Reference Frames */
416     if ([cleanOptNameString isEqualToString:@"ref"] || [cleanOptNameString isEqualToString:@"frameref"])
417     {
418         cleanOptNameString = @"ref";
419     }
420     
421     /*No Dict Decimate*/
422     if ([cleanOptNameString isEqualToString:@"no-dct-decimate"] || [cleanOptNameString isEqualToString:@"no_dct_decimate"] || [cleanOptNameString isEqualToString:@"nodct_decimate"])
423     {
424         cleanOptNameString = @"no-dct-decimate";
425     }
426     
427     /*Subme*/
428     if ([cleanOptNameString isEqualToString:@"subme"])
429     {
430         cleanOptNameString = @"subq";
431     }
432     
433     /*ME Range*/
434     if ([cleanOptNameString isEqualToString:@"me-range"] || [cleanOptNameString isEqualToString:@"me_range"])
435         cleanOptNameString = @"merange";
436     
437     /*B Pyramid*/
438     if ([cleanOptNameString isEqualToString:@"b_pyramid"])
439     {
440         cleanOptNameString = @"b-pyramid";
441     }
442     
443     /*Direct Prediction*/
444     if ([cleanOptNameString isEqualToString:@"direct-pred"] || [cleanOptNameString isEqualToString:@"direct_pred"])
445     {
446         cleanOptNameString = @"direct";
447     }
448     
449     /*Deblocking*/
450     if ([cleanOptNameString isEqualToString:@"filter"])
451     {
452         cleanOptNameString = @"deblock";
453     }
454     
455     /*Analysis*/
456     if ([cleanOptNameString isEqualToString:@"partitions"])
457     {
458         cleanOptNameString = @"analyse";
459     }
460     
461     return cleanOptNameString;    
462 }
463
464 /**
465  * Fades options in and out depending on whether they're available..
466  */
467 - (IBAction) X264AdvancedOptionsAnimate: (id) sender
468 {
469     /* Lots of situations to cover.
470        - B-frames (when 0 turn of b-frame specific stuff, when < 2 disable b-pyramid)
471        - CABAC (when 0 turn off trellis and psy-trel)
472        - subme (if under 6, turn off psy-rd and psy-trel)
473        - trellis (if 0, turn off psy-trel)
474     */
475     
476     if( sender == fX264optBframesPopUp || sender == nil || sender == fDisplayX264Options )
477     {
478         if ( [fX264optBframesPopUp indexOfSelectedItem ] == 1 )
479         {
480             /* If the b-frame widget is at 1, the user has chosen
481                not to use b-frames at all. So disable the options
482                that can only be used when b-frames are enabled.        */
483             
484             if( [fX264optBPyramidPopUp isHidden] == false )
485             {
486                 [[fX264optBPyramidPopUp animator] setHidden:YES];
487                 [[fX264optBPyramidLabel animator] setHidden:YES];
488                 if ( [fX264optBPyramidPopUp indexOfSelectedItem] > 0 )
489                 {
490                     [fX264optBPyramidPopUp selectItemAtIndex: 0];
491                     [[fX264optBPyramidPopUp cell] performClick:self];
492                 }
493             }
494
495             if( [fX264optDirectPredPopUp isHidden] == false )
496             {
497                 [[fX264optDirectPredPopUp animator] setHidden:YES];
498                 [[fX264optDirectPredLabel animator] setHidden:YES];
499                 if ( [fX264optDirectPredPopUp indexOfSelectedItem] > 0 )
500                 {
501                     [fX264optDirectPredPopUp selectItemAtIndex: 0];
502                     [[fX264optDirectPredPopUp cell] performClick:self];
503                 }
504             }
505
506             if( [fX264optBAdaptPopUp isHidden] == false )
507             {
508                 [[fX264optBAdaptPopUp animator] setHidden:YES];
509                 [[fX264optBAdaptLabel animator] setHidden:YES];
510                 if ( [fX264optBAdaptPopUp indexOfSelectedItem] > 0 )
511                 {
512                     [fX264optBAdaptPopUp selectItemAtIndex: 0];
513                     [[fX264optBAdaptPopUp cell] performClick:self];
514                 }
515             }
516         }
517         else if ( [fX264optBframesPopUp indexOfSelectedItem ] == 2)
518         {
519             /* Only 1 b-frame? Disable b-pyramid. */
520             if( [fX264optBPyramidPopUp isHidden] == false )
521             {
522                 [[fX264optBPyramidPopUp animator] setHidden:YES];
523                 [[fX264optBPyramidLabel animator] setHidden:YES];
524                 if ( [fX264optBPyramidPopUp indexOfSelectedItem] > 0 )
525                 {
526                     [fX264optBPyramidPopUp selectItemAtIndex: 0];
527                     [[fX264optBPyramidPopUp cell] performClick:self];
528                 }
529             }
530
531             if( [fX264optDirectPredPopUp isHidden] == true )
532             {
533                 [[fX264optDirectPredPopUp animator] setHidden:NO];
534                 [[fX264optDirectPredLabel animator] setHidden:NO];
535             }
536             
537             if( [fX264optBAdaptPopUp isHidden] == true )
538             {
539                 [[fX264optBAdaptPopUp animator] setHidden:NO];
540                 [[fX264optBAdaptLabel animator] setHidden:NO];
541             }
542         }
543         else
544         {
545             if( [fX264optBPyramidPopUp isHidden] == true )
546             {
547                 [[fX264optBPyramidPopUp animator] setHidden:NO];
548                 [[fX264optBPyramidLabel animator] setHidden:NO];
549             }
550
551             if( [fX264optDirectPredPopUp isHidden] == true )
552             {
553                 [[fX264optDirectPredPopUp animator] setHidden:NO];
554                 [[fX264optDirectPredLabel animator] setHidden:NO];
555             }
556             
557             if( [fX264optBAdaptPopUp isHidden] == true )
558             {
559                 [[fX264optBAdaptPopUp animator] setHidden:NO];
560                 [[fX264optBAdaptLabel animator] setHidden:NO];
561             }
562         }
563     }
564     
565     if( sender == fX264optCabacSwitch || sender == nil || sender == fDisplayX264Options )
566     {
567         if ( [fX264optCabacSwitch state] == false)
568         {
569             if( [fX264optTrellisPopUp isHidden] == false )
570             {
571                 /* Without CABAC entropy coding, trellis doesn't run. */
572                 [[fX264optTrellisPopUp animator] setHidden:YES];
573                 [[fX264optTrellisLabel animator] setHidden:YES];
574                 [fX264optTrellisPopUp selectItemAtIndex:0];
575                 [[fX264optTrellisPopUp cell] performClick:self];
576                 
577                 if( [fX264optPsyTrellisSlider isHidden] == false)
578                 {
579                     [[fX264optPsyTrellisSlider animator] setHidden:YES];
580                     [[fX264optPsyTrellisLabel animator] setHidden:YES];
581                     if ( [fX264optPsyTrellisSlider floatValue] > 0.0 )
582                     {
583                         [fX264optPsyTrellisSlider setFloatValue:0.0];
584                         [[fX264optPsyTrellisSlider cell] performClick:self];
585                     }
586                 }
587             }
588         }
589         else if( [fX264optTrellisPopUp isHidden] == true)
590         {
591             [[fX264optTrellisPopUp animator] setHidden:NO];
592             [[fX264optTrellisLabel animator] setHidden:NO];
593             
594             if( [fX264optPsyTrellisSlider isHidden] == true)
595             {
596                 [[fX264optPsyTrellisSlider animator] setHidden:NO];
597                 [[fX264optPsyTrellisLabel animator] setHidden:NO];
598             }
599         }
600     }
601     
602     
603     if( sender == fX264optMotionEstPopUp || sender == nil || sender == fDisplayX264Options )
604     {
605         if ( [fX264optMotionEstPopUp indexOfSelectedItem] < 3 )
606         {
607             /* ME-range can only be above 16 if me >= umh
608               and changing it to < 16 is idiotic so hide it . */
609             if( [fX264optMERangePopUp isHidden] == false )
610             {
611                 [[fX264optMERangePopUp animator] setHidden:YES];
612                 [[fX264optMERangeLabel animator] setHidden:YES];
613                 if ( [fX264optMERangePopUp indexOfSelectedItem] > 0 )
614                 {
615                     [fX264optMERangePopUp selectItemAtIndex:0];
616                     [[fX264optMERangePopUp cell] performClick:self];
617                 }
618             }
619         }
620         else
621         {
622             if( [fX264optMERangePopUp isHidden] == true )
623             {
624                 [[fX264optMERangePopUp animator] setHidden:NO];
625                 [[fX264optMERangeLabel animator] setHidden:NO];
626             }
627         }
628     }
629     
630     if( sender == fX264optSubmePopUp || sender == nil || sender == fDisplayX264Options )
631     {
632         if( [fX264optSubmePopUp indexOfSelectedItem] != 0 && [fX264optSubmePopUp indexOfSelectedItem] < 7 )
633         {
634             /* No Psy-RDO or Psy=trel if subme < 6. */
635             if( [fX264optPsyRDSlider isHidden] == false )
636             {
637                 [[fX264optPsyRDSlider animator] setHidden:YES];
638                 [[fX264optPsyRDLabel animator] setHidden:YES];
639                 if ( [fX264optPsyRDSlider floatValue] < 1.0 )
640                 {
641                     [fX264optPsyRDSlider setFloatValue:1.0];
642                     [[fX264optPsyRDSlider cell] performClick:self];            
643                 }
644             }
645
646             if( [fX264optPsyTrellisSlider isHidden] == false)
647             {
648                 [[fX264optPsyTrellisSlider animator] setHidden:YES];
649                 [[fX264optPsyTrellisLabel animator] setHidden:YES];
650                 if ( [fX264optPsyTrellisSlider floatValue] > 0.0 )
651                 {
652                     [fX264optPsyTrellisSlider setFloatValue:0.0];
653                     [[fX264optPsyTrellisSlider cell] performClick:self];
654                 }
655             }
656         }
657         else
658         {
659             if( [fX264optPsyRDSlider isHidden] == true )
660             {
661                 [[fX264optPsyRDSlider animator] setHidden:NO];
662                 [[fX264optPsyRDLabel animator] setHidden:NO];
663             }
664
665             if( ( [fX264optTrellisPopUp indexOfSelectedItem] == 0 || [fX264optTrellisPopUp indexOfSelectedItem] >= 2 ) && [fX264optCabacSwitch state] == true && [fX264optPsyTrellisSlider isHidden] == true )
666             {
667                 [[fX264optPsyTrellisSlider animator] setHidden:NO];
668                 [[fX264optPsyTrellisLabel animator] setHidden:NO];
669             }
670         }
671     }
672     
673     if( sender == fX264optTrellisPopUp || sender == nil || sender == fDisplayX264Options )
674     {
675         if( [fX264optTrellisPopUp indexOfSelectedItem] > 0 && [fX264optTrellisPopUp indexOfSelectedItem] < 2 )
676         {
677             if( [fX264optPsyTrellisSlider isHidden] == false )
678             {
679                 /* No Psy-trellis without trellis. */
680                 [[fX264optPsyTrellisSlider animator] setHidden:YES];
681                 [[fX264optPsyTrellisLabel animator] setHidden:YES];
682                 [[fX264optPsyTrellisSlider animator] setFloatValue:0.0];
683                 [[fX264optPsyTrellisSlider cell] performClick:self];
684             }
685         }
686         else
687         {
688             if( ( [fX264optSubmePopUp indexOfSelectedItem] == 0 || [fX264optSubmePopUp indexOfSelectedItem] >= 7 ) && [fX264optCabacSwitch state] == true  && [fX264optPsyTrellisSlider isHidden] == true )
689             {
690                 [[fX264optPsyTrellisSlider animator] setHidden:NO];
691                 [[fX264optPsyTrellisLabel animator] setHidden:NO];
692             }
693         }
694     }
695 }
696
697 /**
698  * Resets the GUI widgets to the contents of the option string.
699  */
700 - (IBAction) X264AdvancedOptionsSetCurrentSettings: (id) sender
701 {
702     /* Set widgets depending on the opt string in field */
703     NSString * thisOpt; // The separated option such as "bframes=3"
704     NSString * optName = @""; // The option name such as "bframes"
705     NSString * optValue = @"";// The option value such as "3"
706     NSArray *currentOptsArray;
707     
708     /*First, we get an opt string to process */
709     NSString *currentOptString = [fDisplayX264Options stringValue];
710     
711     /* Verify there is an opt string to process by making sure an
712        option is getting its value set. If so, start to process it. */
713     NSRange currentOptRange = [currentOptString rangeOfString:@"="];
714     if (currentOptRange.location != NSNotFound)
715     {
716         /*Put individual options into an array based on the ":" separator for processing, result is "<opt>=<value>"*/
717         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
718         
719         /*iterate through the array and get <opts> and <values*/
720         int loopcounter;
721         int currentOptsArrayCount = [currentOptsArray count];
722         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
723         {
724             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
725             
726             /* Verify the option sets a value */
727             NSRange splitOptRange = [thisOpt rangeOfString:@"="];            
728             if (splitOptRange.location != NSNotFound)
729             {
730                 /* Split thisOpt into an optName setting an optValue. */
731                 optName = [thisOpt substringToIndex:splitOptRange.location];
732                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
733                 
734                 /*Run through the available widgets for x264 opts and set them, as you add widgets, 
735                     they need to be added here. This should be moved to its own method probably*/
736                 
737                 /*bframes NSPopUpButton*/
738                 if ([optName isEqualToString:@"bframes"])
739                 {
740                     [fX264optBframesPopUp selectItemAtIndex:[optValue intValue]+1];
741                 }
742                 /*ref NSPopUpButton*/
743                 if ([optName isEqualToString:@"ref"])
744                 {
745                     // Clamp values to a minimum of 1 and a maximum of 16
746                     if ( [optValue intValue] < 1 )
747                     {
748                         [fX264optRefPopUp selectItemAtIndex:1];
749                         [ self X264AdvancedOptionsChanged: fX264optRefPopUp];
750                     }
751                     else if ( [optValue intValue] > 16 )
752                     {
753                         [fX264optRefPopUp selectItemAtIndex:16];
754                         [ self X264AdvancedOptionsChanged: fX264optRefPopUp];
755                     }
756                     else
757                     {
758                         [fX264optRefPopUp selectItemAtIndex:[optValue intValue]];
759                     }
760                 }
761                 /*WeightP NSButton*/
762                 if ([optName isEqualToString:@"weightp"])
763                 {
764                     if ([optValue intValue] < 1)
765                         [fX264optWeightPSwitch setState:0];
766                     else
767                         [fX264optWeightPSwitch setState:1];
768                 }
769                 /*No Dict Decimate NSButton*/
770                 if ([optName isEqualToString:@"no-dct-decimate"])
771                 {
772                     [fX264optNodctdcmtSwitch setState:[optValue intValue]];
773                 }
774                 /*Sub Me NSPopUpButton*/
775                 if ([optName isEqualToString:@"subq"])
776                 {
777                     [fX264optSubmePopUp selectItemAtIndex:[optValue intValue]+1];
778                 }
779                 /*Trellis NSPopUpButton*/
780                 if ([optName isEqualToString:@"trellis"])
781                 {
782                     [fX264optTrellisPopUp selectItemAtIndex:[optValue intValue]+1];
783                 }
784                 /*Motion Estimation NSPopUpButton*/
785                 if ([optName isEqualToString:@"me"])
786                 {
787                     if ([optValue isEqualToString:@"dia"])
788                         [fX264optMotionEstPopUp selectItemAtIndex:1];
789                     else if ([optValue isEqualToString:@"hex"])
790                         [fX264optMotionEstPopUp selectItemAtIndex:2];
791                     else if ([optValue isEqualToString:@"umh"])
792                         [fX264optMotionEstPopUp selectItemAtIndex:3];
793                     else if ([optValue isEqualToString:@"esa"])
794                         [fX264optMotionEstPopUp selectItemAtIndex:4];
795                     else if ([optValue isEqualToString:@"tesa"])
796                         [fX264optMotionEstPopUp selectItemAtIndex:5];
797                 }
798                 /*ME Range NSPopUpButton*/
799                 if ([optName isEqualToString:@"merange"])
800                 {
801                     [fX264optMERangePopUp selectItemAtIndex:[optValue intValue]-3];
802                 }
803                 /* Adaptive B-Frames NSPopUpButton*/
804                 if ([optName isEqualToString:@"b-adapt"])
805                 {
806                     [fX264optBAdaptPopUp selectItemAtIndex:[optValue intValue]+1];
807                 }
808                 /*B Pyramid NSPButton*/
809                 if ([optName isEqualToString:@"b-pyramid"])
810                 {
811                     
812                     if( [optValue isEqualToString:@"normal"] )
813                     {
814                         [self X264AdvancedOptionsChanged: fX264optBPyramidPopUp];
815                         [fX264optBPyramidPopUp selectItemAtIndex:0];
816                     }
817                     else if( [optValue isEqualToString:@"2"] )
818                     {
819                         [fX264optBPyramidPopUp selectItemAtIndex:0];
820                         [self X264AdvancedOptionsChanged: fX264optBPyramidPopUp];
821                     }
822                     if( [optValue isEqualToString:@"strict"] )
823                     {
824                         [fX264optBPyramidPopUp selectItemAtIndex:2];
825                     }
826                     else if( [optValue isEqualToString:@"1"] )
827                     {
828                         [fX264optBPyramidPopUp selectItemAtIndex:2];
829                         [self X264AdvancedOptionsChanged: fX264optBPyramidPopUp];
830                     }
831                     if( [optValue isEqualToString:@"none"] )
832                     {
833                         [fX264optBPyramidPopUp selectItemAtIndex:1];
834                     }
835                     else if( [optValue isEqualToString:@"0"] )
836                     {
837                         [fX264optBPyramidPopUp selectItemAtIndex:1];
838                         [self X264AdvancedOptionsChanged: fX264optBPyramidPopUp];
839                     }
840                 }                
841                 /*Direct B-frame Prediction NSPopUpButton*/
842                                 if ([optName isEqualToString:@"direct"])
843                 {
844                     if ([optValue isEqualToString:@"none"])
845                         [fX264optDirectPredPopUp selectItemAtIndex:1];
846                     else if ([optValue isEqualToString:@"spatial"])
847                         [fX264optDirectPredPopUp selectItemAtIndex:2];
848                     else if ([optValue isEqualToString:@"temporal"])
849                         [fX264optDirectPredPopUp selectItemAtIndex:3];
850                     else if ([optValue isEqualToString:@"auto"])
851                         [fX264optDirectPredPopUp selectItemAtIndex:4];                        
852                 }
853                 /*Deblocking NSPopUpButtons*/
854                 if ([optName isEqualToString:@"deblock"])
855                 {
856                     NSString * alphaDeblock = @"";
857                     NSString * betaDeblock = @"";
858                     
859                     NSRange splitDeblock = [optValue rangeOfString:@","];
860                     alphaDeblock = [optValue substringToIndex:splitDeblock.location];
861                     betaDeblock = [optValue substringFromIndex:splitDeblock.location + 1];
862                     
863                     if ([alphaDeblock isEqualToString:@"0"] && [betaDeblock isEqualToString:@"0"])
864                     {
865                         /* When both filters are at 0, default */
866                         [fX264optAlphaDeblockPopUp selectItemAtIndex:0];                        
867                         [fX264optBetaDeblockPopUp selectItemAtIndex:0];                               
868                     }
869                     else
870                     {
871                         if (![alphaDeblock isEqualToString:@"0"])
872                         {
873                             /* Alpha isn't 0, so set it. The offset of 7 is
874                                because filters start at -6 instead of at 0. */
875                             [fX264optAlphaDeblockPopUp selectItemAtIndex:[alphaDeblock intValue]+7];
876                         }
877                         else
878                         {
879                             /* Set alpha filter to 0, which is 7 up
880                                because filters start at -6, not 0. */
881                             [fX264optAlphaDeblockPopUp selectItemAtIndex:7];                        
882                         }
883                         
884                         if (![betaDeblock isEqualToString:@"0"])
885                         {
886                             /* Beta isn't 0, so set it. */
887                             [fX264optBetaDeblockPopUp selectItemAtIndex:[betaDeblock intValue]+7];
888                         }
889                         else
890                         {
891                             /* Set beta filter to 0. */
892                             [fX264optBetaDeblockPopUp selectItemAtIndex:7];                        
893                         }
894                     }
895                 }
896                 /* Analysis NSPopUpButton */
897                 if ([optName isEqualToString:@"analyse"])
898                 {
899                     if ([optValue isEqualToString:@"p8x8,b8x8,i8x8,i4x4"])
900                     {
901                         /* Default ("most") */
902                         [fX264optAnalysePopUp selectItemAtIndex:0];
903                     }
904                     else if ([optValue isEqualToString:@"i4x4,i8x8"] ||
905                              [optValue isEqualToString:@"i8x8,i4x4"] )
906                     {
907                         /* Some */
908                         [fX264optAnalysePopUp selectItemAtIndex:2];
909                     }
910                     else if ([optValue isEqualToString:@"none"])
911                     {
912                         [fX264optAnalysePopUp selectItemAtIndex:1];
913                     }
914                     else if ([optValue isEqualToString:@"all"])
915                     {
916                         [fX264optAnalysePopUp selectItemAtIndex:3];
917                     }
918                     
919                 }
920                 /* 8x8 DCT NSButton */
921                 if ([optName isEqualToString:@"8x8dct"])
922                 {
923                     [fX264opt8x8dctSwitch setState:[optValue intValue]];
924                 }
925                 /* CABAC NSButton */
926                 if ([optName isEqualToString:@"cabac"])
927                 {
928                     [fX264optCabacSwitch setState:[optValue intValue]];
929                 }
930                 /* Adaptive Quantization Strength NSSlider */
931                 if ([optName isEqualToString:@"aq-strength"])
932                 {
933                     [fX264optAqSlider setFloatValue:[optValue floatValue]];
934                 }                                                              
935                 /* Psy-RD and Psy-Trellis NSSliders */
936                 if ([optName isEqualToString:@"psy-rd"])
937                 {
938                     NSString * rdOpt = @"";
939                     NSString * trellisOpt = @"";
940                     
941                     NSRange splitRD = [optValue rangeOfString:@","];
942                     rdOpt = [optValue substringToIndex:splitRD.location];
943                     trellisOpt = [optValue substringFromIndex:splitRD.location + 1];
944                     
945                     [fX264optPsyRDSlider setFloatValue:[rdOpt floatValue]];
946                     [fX264optPsyTrellisSlider setFloatValue:[trellisOpt floatValue]];
947                 }                                                              
948             }
949         }
950     }
951 }
952
953 - (NSString *) X264AdvancedOptionsOptIDToString: (id) widget
954 {
955     /*Determine which outlet is being used and set optName to process accordingly */
956     NSString * optNameToChange = @""; // The option name such as "bframes"
957     
958     if (widget == fX264optBframesPopUp)
959     {
960         optNameToChange = @"bframes";
961     }
962     if (widget == fX264optRefPopUp)
963     {
964         optNameToChange = @"ref";
965     }
966     if (widget == fX264optWeightPSwitch)
967     {
968         optNameToChange = @"weightp";
969     }
970     if (widget == fX264optNodctdcmtSwitch)
971     {
972         optNameToChange = @"no-dct-decimate";
973     }
974     if (widget == fX264optSubmePopUp)
975     {
976         optNameToChange = @"subq";
977     }
978     if (widget == fX264optTrellisPopUp)
979     {
980         optNameToChange = @"trellis";
981     }
982     if (widget == fX264optMotionEstPopUp)
983     {
984         optNameToChange = @"me";
985     }
986     if (widget == fX264optMERangePopUp)
987     {
988         optNameToChange = @"merange";
989     }
990     if (widget == fX264optBAdaptPopUp)
991     {
992         optNameToChange = @"b-adapt";
993     }
994     if (widget == fX264optBPyramidPopUp)
995     {
996         optNameToChange = @"b-pyramid";
997     }
998     if (widget == fX264optDirectPredPopUp)
999     {
1000         optNameToChange = @"direct";
1001     }
1002     if (widget == fX264optAlphaDeblockPopUp)
1003     {
1004         optNameToChange = @"deblock";
1005     }
1006     if (widget == fX264optBetaDeblockPopUp)
1007     {
1008         optNameToChange = @"deblock";
1009     }        
1010     if (widget == fX264optAnalysePopUp)
1011     {
1012         optNameToChange = @"analyse";
1013     }
1014     if (widget == fX264opt8x8dctSwitch)
1015     {
1016         optNameToChange = @"8x8dct";
1017     }
1018     if (widget == fX264optCabacSwitch)
1019     {
1020         optNameToChange = @"cabac";
1021     }
1022     if( widget == fX264optAqSlider)
1023     {
1024         optNameToChange = @"aq-strength";
1025     }
1026     if( widget == fX264optPsyRDSlider)
1027     {
1028         optNameToChange = @"psy-rd";
1029     }
1030     if( widget == fX264optPsyTrellisSlider)
1031     {
1032         optNameToChange = @"psy-rd";
1033     }
1034     
1035     return optNameToChange;
1036 }
1037
1038 - (NSString *) X264AdvancedOptionsWidgetToString: (NSString *) optName withID: (id) sender
1039 {
1040     NSString * thisOpt = @""; // The option=value string the method will return
1041     
1042     if ([optName 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     
1058     else if ([optName isEqualToString:@"aq-strength"])
1059     {
1060         if( [fX264optAqSlider floatValue] == 1.0 ) 
1061         {
1062             /* When Aq is 1 it's the default value and can be ignored. */
1063             thisOpt = @"";                                
1064         }
1065         else
1066         {
1067             thisOpt = [NSString stringWithFormat:@"%@=%0.1f", optName, [fX264optAqSlider floatValue] ];
1068         }
1069     }
1070
1071     else if ([optName isEqualToString:@"psy-rd"])
1072     {
1073         if( [fX264optPsyRDSlider floatValue] == 1.0 && [fX264optPsyTrellisSlider floatValue] == 0.0 ) 
1074         {
1075             /* When  PsyRD is 1 and PsyTrel is 0 they're default values and can be ignored. */
1076             thisOpt = @"";                                
1077         }
1078         else
1079         {
1080             /* Otherwise the format is deblock=a,b, where a and b both have an array
1081                offset of 7 because deblocking values start at -6 instead of at zero. */
1082             thisOpt = [NSString stringWithFormat:@"%@=%0.1f,%0.2f", optName, [fX264optPsyRDSlider floatValue], [fX264optPsyTrellisSlider floatValue] ];
1083         }
1084     }
1085     
1086     else if /*Boolean Switches*/ ( [optName isEqualToString:@"no-dct-decimate"] )
1087     {
1088         /* Here is where we take care of the boolean options that work overtly:
1089            no-dct-decimate being on means no-dct-decimate=1, etc. Some options
1090            require the inverse, but those will be handled a couple lines down. */
1091         if ([sender state] == 0)
1092         {
1093             /* When these options are false, don't include them. They all default
1094                to being set off, so they don't need to be mentioned at all.       */
1095             thisOpt = @"";
1096         }
1097         else
1098         {
1099             /* Otherwise, include them as optioname=1 */
1100             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,1];
1101         }
1102     }
1103     
1104     else if ( [optName isEqualToString:@"8x8dct"] || [optName isEqualToString:@"cabac"] || [optName isEqualToString:@"weightp"] )
1105     {
1106         /* These options default to being on. That means they
1107            only need to be included in the string when turned off. */
1108         if ([sender state] == 1)
1109         {
1110             /* It's true so don't include it. */
1111             thisOpt = @"";
1112         }
1113         else
1114         {
1115             /* Otherwise, include cabac=0, etc, in the string. */
1116             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,0];
1117         }
1118     }
1119                                             
1120     else if (([sender indexOfSelectedItem] == 0) && (sender != fX264optAlphaDeblockPopUp) && (sender != fX264optBetaDeblockPopUp) ) // means that "unspecified" is chosen, lets then remove it from the string
1121     {
1122         /* When a widget is at index 0, it's default. Default means don't add to the string.
1123            The exception for deblocking is because for those, *both* need to at index 0
1124            for it to default, so it's handled separately, above this section.                */
1125         thisOpt = @"";
1126     }
1127     
1128     else if ([optName isEqualToString:@"me"])
1129     {
1130         /* Motion estimation uses string values, so this switch
1131            pairs the widget index with the right value string.  */
1132         switch ([sender indexOfSelectedItem])
1133         {   
1134             case 1:
1135                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"dia"];
1136                 break;
1137                 
1138             case 2:
1139                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"hex"];
1140                 break;
1141                 
1142             case 3:
1143                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"umh"];
1144                 break;
1145                 
1146             case 4:
1147                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"esa"];
1148                 break;
1149             
1150             case 5:
1151                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"tesa"];
1152             
1153             default:
1154                 break;
1155         }
1156     }
1157     
1158     else if ([optName isEqualToString:@"direct"])
1159     {
1160         /* Direct prediction uses string values, so this switch
1161            pairs the right string value with the right widget index. */
1162         switch ([sender indexOfSelectedItem])
1163         {   
1164             case 1:
1165                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"none"];
1166                 break;
1167                 
1168             case 2:
1169                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"spatial"];
1170                 break;
1171                 
1172             case 3:
1173                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"temporal"];
1174                 break;
1175                 
1176             case 4:
1177                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"auto"];
1178                 break;
1179                 
1180             default:
1181                 break;
1182         }
1183     }
1184     
1185     else if ([optName isEqualToString:@"analyse"])
1186     {
1187         /* Analysis uses string values as well. */
1188         switch ([sender indexOfSelectedItem])
1189         {   
1190             case 1:
1191                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"none"];
1192                 break;
1193             case 2:
1194                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"i4x4,i8x8"];
1195                 break;
1196             case 3:
1197                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"all"];
1198                 break;
1199                 
1200             default:
1201                 break;
1202         }
1203     }
1204
1205     else if ([optName isEqualToString:@"b-pyramid"])
1206     {
1207         /* B-pyramid uses string values too. */
1208         switch ([sender indexOfSelectedItem])
1209         {   
1210             case 1:
1211                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"none"];
1212                 break;
1213             case 2:
1214                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"strict"];
1215                 break;
1216             case 0:
1217                 thisOpt = @"";
1218                 break;
1219                 
1220             default:
1221                 break;
1222         }
1223     }
1224     
1225     else if ([optName isEqualToString:@"merange"])
1226     {
1227         /* Motion estimation range uses an odd array offset because in addition
1228            to starting with index 0 as default, index 1 starts at 4 instead of 1,
1229            because merange can't go below 4. So it has to be handled separately.  */
1230         thisOpt = [NSString stringWithFormat:@"%@=%d",optName,[sender indexOfSelectedItem]+3];
1231     }
1232     
1233     else if ([optName isEqualToString:@"b-adapt"])
1234     {
1235         /* B-adapt starts at index 0 with default then goes 0, 1, 2)*/
1236         thisOpt = [NSString stringWithFormat:@"%@=%d", optName, [sender indexOfSelectedItem]-1];
1237     }
1238     
1239     else if ([optName isEqualToString:@"ref"])
1240     {
1241         /* Refs use actual index numbers */
1242         thisOpt = [NSString stringWithFormat:@"%@=%d",optName,[sender indexOfSelectedItem]];
1243     }
1244     
1245     else // we have a valid value to change, so change it
1246     {
1247         if ( [sender indexOfSelectedItem] != 0 )
1248         /* Here's our general case, that catches things like b-frames.
1249            Basically, any options that are PopUp menus with index 0 as default and
1250            index 1 as 0, with numerical values, are all handled right here. All of
1251            the above stuff is for the exceptions to the general case.              */
1252             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,[sender indexOfSelectedItem]-1];
1253     }
1254     
1255     return thisOpt;
1256 }
1257
1258 - (BOOL) X264AdvancedOptionsIsOpt: (NSString *) optNameToChange inString: (NSString *) currentOptString
1259 {
1260     /* If the option is in the string but not the beginning of it,
1261        it will be in the form of ":optName=value" so we really want
1262        to be looking for ":optNameToChange=" rather than "optNameToChange". */
1263     NSString *checkOptNameToChange = [NSString stringWithFormat:@":%@=",optNameToChange];
1264     
1265     /* Now we store the part of the string up through the option name in currentOptRange. */
1266     NSRange currentOptRange = [currentOptString rangeOfString:checkOptNameToChange];
1267
1268     /*  We need to know if the option is at the beginning of the string.
1269         If it is at the start, it won't be preceded by a colon.
1270         To figure this out, we'll use the rangeOfString method. First,
1271         store what the option name would be if if it was at the beginning,
1272         in checkOptNameToChangeBeginning. Then, find its range in the string.
1273         If the range is 0, it's the first option listed in the string.       */        
1274     NSString *checkOptNameToChangeBeginning = [NSString stringWithFormat:@"%@=",optNameToChange];
1275     NSRange currentOptRangeBeginning = [currentOptString rangeOfString:checkOptNameToChangeBeginning];
1276
1277     if (currentOptRange.location != NSNotFound || currentOptRangeBeginning.location == 0)
1278         return true;
1279     else
1280         return false;
1281
1282
1283 /**
1284  * Resets the option string to mirror the GUI widgets.
1285  */
1286 - (IBAction) X264AdvancedOptionsChanged: (id) sender
1287 {
1288     /* Look up the equivalent string option name of the calling widget. */
1289     NSString * optNameToChange = [self X264AdvancedOptionsOptIDToString: sender];
1290     
1291     NSString * thisOpt = @"";  // The separated option such as "bframes=3"
1292     NSString * optName = @"";  // The option name such as "bframes"
1293     NSString * optValue = @""; // The option value such as "3"
1294     NSArray *currentOptsArray;
1295     
1296     /* Get the current opt string being displayed. */
1297     NSString *currentOptString = [fDisplayX264Options stringValue];
1298     
1299     /* There are going to be a few possibilities.
1300        - The option might start off the string.
1301        - The option might be in the middle of the string.
1302        - The option might not be in the string at all yet.
1303        - The string itself might not yet exist.             */
1304     
1305     if( [self X264AdvancedOptionsIsOpt: optNameToChange inString: currentOptString] )
1306     {
1307         /* If the option is in the string wth a semicolon, or starts the string, it's time to edit.
1308            This means parsing the whole string into an array of options and values. From there,
1309            iterate through the options, and when you reach the one that's been changed, edit it.   */
1310         
1311         /* Create new empty opt string*/
1312         NSString *changedOptString = @"";
1313         
1314         /* Put individual options into an array based on the ":"
1315            separator for processing, result is "<opt>=<value>"   */
1316         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
1317         
1318         /* Iterate through the array and get <opts> and <values*/
1319         int loopcounter;
1320         int currentOptsArrayCount = [currentOptsArray count];
1321         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
1322         {
1323             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
1324             NSRange splitOptRange = [thisOpt rangeOfString:@"="];
1325             
1326             if (splitOptRange.location != NSNotFound)
1327             {
1328                 /* First off, it's time to handle option strings that
1329                    already have at least one option=value pair in them. */
1330                    
1331                 optName = [thisOpt substringToIndex:splitOptRange.location];
1332                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
1333
1334                 /*If the optNameToChange is found, appropriately change the value or delete it if
1335                     "Unspecified" is set.*/
1336                 if ([optName isEqualToString:optNameToChange])
1337                 {
1338                     thisOpt = [self X264AdvancedOptionsWidgetToString: optName withID: sender];
1339                 }
1340             }
1341             
1342             /* Construct New String for opts here */
1343             if ([thisOpt isEqualToString:@""])
1344             {
1345                 /* Blank option, so just add it to the string. (Why?) */
1346                 changedOptString = [NSString stringWithFormat:@"%@%@",changedOptString,thisOpt];
1347             }
1348             else
1349             {
1350                 if ([changedOptString isEqualToString:@""])
1351                 {
1352                     /* No existing string, make the string this option. */
1353                     changedOptString = [NSString stringWithFormat:@"%@",thisOpt];
1354                 }
1355                 else
1356                 {
1357                     /* Existing string, existing option. Append the
1358                        option to the string, preceding it with a colon. */
1359                     changedOptString = [NSString stringWithFormat:@"%@:%@",changedOptString,thisOpt];
1360                 }
1361             }
1362         }
1363         
1364         /* Change the dislayed option string to reflect the new modified settings */
1365         [fDisplayX264Options setStringValue:changedOptString];    
1366     }
1367     else // if none exists, add it to the string
1368     {
1369         /* This is where options that aren't already in the string are handled. */
1370         if ([[fDisplayX264Options stringValue] isEqualToString: @""])
1371         {
1372             
1373             [fDisplayX264Options setStringValue:
1374                 [self X264AdvancedOptionsWidgetToString: optNameToChange withID: sender]];
1375         }
1376         else
1377         {
1378             /* The string isn't empty, and the option isn't already in it, so
1379                it will need to be appended to the current string with a colon,
1380                as long as the string to be appended isn't just blank (default). */
1381             if( [[self X264AdvancedOptionsWidgetToString: optNameToChange withID: sender] isEqualToString: @""] == false )
1382             {
1383                 [fDisplayX264Options setStringValue:
1384                     [NSString stringWithFormat:@"%@:%@",
1385                         currentOptString,
1386                         [self X264AdvancedOptionsWidgetToString: optNameToChange withID: sender] ]];                
1387             }
1388         }
1389     }
1390     
1391     /* We now need to reset the opt widgets since we changed some stuff */        
1392     [self X264AdvancedOptionsSet:sender];        
1393 }
1394
1395 @end