1/***************************************************************************** 2 * VLCVideoEffectsWindowController.m: MacOS X interface module 3 ***************************************************************************** 4 * Copyright (C) 2011-2015 Felix Paul Kühne 5 * $Id: e5904eb81cc903cc3c800ba3ebd0ce1cece192b3 $ 6 * 7 * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 22 *****************************************************************************/ 23 24#import "CompatibilityFixes.h" 25#import "VLCMain.h" 26#import "VLCVideoEffectsWindowController.h" 27#import "VLCPopupPanelController.h" 28#import "VLCTextfieldPanelController.h" 29#import "VLCCoreInteraction.h" 30#import "VLCHexNumberFormatter.h" 31 32#define getWidgetBoolValue(w) ((vlc_value_t){ .b_bool = [w state] }) 33#define getWidgetIntValue(w) ((vlc_value_t){ .i_int = [w intValue] }) 34#define getWidgetFloatValue(w) ((vlc_value_t){ .f_float = [w floatValue] }) 35#define getWidgetStringValue(w) ((vlc_value_t){ .psz_string = (char *)[[w stringValue] UTF8String] }) 36 37 38#pragma mark - 39#pragma mark Initialization 40 41@implementation VLCVideoEffectsWindowController 42 43+ (void)initialize 44{ 45 /* 46 * Video effects profiles starting with 3.0: 47 * - Index 0 is assumed to be the default profile from previous versions 48 * - Index 0 from settings is never read or written anymore starting with 3.0, as the Default profile 49 * is not persisted anymore. 50 */ 51 52 NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: 53 [NSArray arrayWithObject:[VLCVideoEffectsWindowController defaultProfileString]], @"VideoEffectProfiles", 54 [NSArray arrayWithObject:_NS("Default")], @"VideoEffectProfileNames", 55 nil]; 56 [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; 57} 58 59+ (NSString *)defaultProfileString 60{ 61 return @";;;0;1.000000;1.000000;1.000000;1.000000;0.050000;16;2.000000;OTA=;4;4;16711680;20;15;120;Z3JhZGllbnQ=;1;0;16711680;6;80;VkxD;-1;;-1;255;2;3;3;0;-180.000000"; 62} 63 64- (id)init 65{ 66 self = [super initWithWindowNibName:@"VideoEffects"]; 67 if (self) { 68 self.popupPanel = [[VLCPopupPanelController alloc] init]; 69 self.textfieldPanel = [[VLCTextfieldPanelController alloc] init]; 70 71 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 72 if ([defaults boolForKey:@"VideoEffectApplyProfileOnStartup"]) { 73 // This does not reset the UI (which does not exist yet), but it initalizes needed playlist vars 74 [self resetValues]; 75 76 [self loadProfile]; 77 } else { 78 [self saveCurrentProfileIndex:0]; 79 } 80 81 } 82 83 return self; 84} 85 86/// Loads values from profile into variables 87- (void)loadProfile 88{ 89 intf_thread_t *p_intf = getIntf(); 90 playlist_t *p_playlist = pl_Get(p_intf); 91 VLCCoreInteraction *vci_si = [VLCCoreInteraction sharedInstance]; 92 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 93 NSInteger profileIndex = [self currentProfileIndex]; 94 95 NSString *profileString; 96 if (profileIndex == 0) 97 profileString = [VLCVideoEffectsWindowController defaultProfileString]; 98 else 99 profileString = [[defaults objectForKey:@"VideoEffectProfiles"] objectAtIndex:profileIndex]; 100 101 NSArray *items = [profileString componentsSeparatedByString:@";"]; 102 103 // version 1 of profile string has 32 entries 104 if ([items count] < 32) { 105 msg_Err(p_intf, "Error in parsing profile string"); 106 return; 107 } 108 109 /* filter handling */ 110 NSString *tempString = B64DecNSStr([items firstObject]); 111 NSArray<NSValue *> *vouts = getVouts(); 112 113 /* enable the new filters */ 114 var_SetString(p_playlist, "video-filter", [tempString UTF8String]); 115 if (vouts) 116 for (NSValue *ptr in vouts) { 117 vout_thread_t *p_vout = [ptr pointerValue]; 118 var_SetString(p_vout, "video-filter", [tempString UTF8String]); 119 } 120 121 tempString = B64DecNSStr([items objectAtIndex:1]); 122 /* enable another round of new filters */ 123 var_SetString(p_playlist, "sub-source", [tempString UTF8String]); 124 if (vouts) 125 for (NSValue *ptr in vouts) { 126 vout_thread_t *p_vout = [ptr pointerValue]; 127 var_SetString(p_vout, "sub-source", [tempString UTF8String]); 128 vlc_object_release(p_vout); 129 } 130 131 tempString = B64DecNSStr([items objectAtIndex:2]); 132 /* enable another round of new filters */ 133 char *psz_current_splitter = var_GetString(p_playlist, "video-splitter"); 134 bool b_filter_changed = ![tempString isEqualToString:toNSStr(psz_current_splitter)]; 135 free(psz_current_splitter); 136 137 if (b_filter_changed) 138 var_SetString(p_playlist, "video-splitter", [tempString UTF8String]); 139 140 /* try to set filter values on-the-fly and store them appropriately */ 141 // index 3 is deprecated 142 [vci_si setVideoFilterProperty: "contrast" forFilter: "adjust" withValue: getWidgetFloatValue([items objectAtIndex:4])]; 143 [vci_si setVideoFilterProperty: "brightness" forFilter: "adjust" withValue: getWidgetFloatValue([items objectAtIndex:5])]; 144 [vci_si setVideoFilterProperty: "saturation" forFilter: "adjust" withValue: getWidgetFloatValue([items objectAtIndex:6])]; 145 [vci_si setVideoFilterProperty: "gamma" forFilter: "adjust" withValue: getWidgetFloatValue([items objectAtIndex:7])]; 146 [vci_si setVideoFilterProperty: "sharpen-sigma" forFilter: "sharpen" withValue: getWidgetFloatValue([items objectAtIndex:8])]; 147 [vci_si setVideoFilterProperty: "gradfun-radius" forFilter: "gradfun" withValue: getWidgetIntValue([items objectAtIndex:9])]; 148 [vci_si setVideoFilterProperty: "grain-variance" forFilter: "grain" withValue: getWidgetFloatValue([items objectAtIndex:10])]; 149 [vci_si setVideoFilterProperty: "transform-type" forFilter: "transform" withValue: (vlc_value_t){ .psz_string = (char *)[B64DecNSStr([items objectAtIndex:11]) UTF8String] }]; 150 [vci_si setVideoFilterProperty: "puzzle-rows" forFilter: "puzzle" withValue: getWidgetIntValue([items objectAtIndex:12])]; 151 [vci_si setVideoFilterProperty: "puzzle-cols" forFilter: "puzzle" withValue: getWidgetIntValue([items objectAtIndex:13])]; 152 [vci_si setVideoFilterProperty: "colorthres-color" forFilter: "colorthres" withValue: getWidgetIntValue([items objectAtIndex:14])]; 153 [vci_si setVideoFilterProperty: "colorthres-saturationthres" forFilter: "colorthres" withValue: getWidgetIntValue([items objectAtIndex:15])]; 154 [vci_si setVideoFilterProperty: "colorthres-similaritythres" forFilter: "colorthres" withValue: getWidgetIntValue([items objectAtIndex:16])]; 155 [vci_si setVideoFilterProperty: "sepia-intensity" forFilter: "sepia" withValue: getWidgetIntValue([items objectAtIndex:17])]; 156 [vci_si setVideoFilterProperty: "gradient-mode" forFilter: "gradient" withValue: (vlc_value_t){ .psz_string = (char *)[B64DecNSStr([items objectAtIndex:18]) UTF8String] }]; 157 [vci_si setVideoFilterProperty: "gradient-cartoon" forFilter: "gradient" withValue: (vlc_value_t){ .b_bool = [[items objectAtIndex:19] intValue] }]; 158 [vci_si setVideoFilterProperty: "gradient-type" forFilter: "gradient" withValue: getWidgetIntValue([items objectAtIndex:20])]; 159 [vci_si setVideoFilterProperty: "extract-component" forFilter: "extract" withValue: getWidgetIntValue([items objectAtIndex:21])]; 160 [vci_si setVideoFilterProperty: "posterize-level" forFilter: "posterize" withValue: getWidgetIntValue([items objectAtIndex:22])]; 161 [vci_si setVideoFilterProperty: "blur-factor" forFilter: "motionblur" withValue: getWidgetIntValue([items objectAtIndex:23])]; 162 [vci_si setVideoFilterProperty: "marq-marquee" forFilter: "marq" withValue: (vlc_value_t){ .psz_string = (char *)[B64DecNSStr([items objectAtIndex:24]) UTF8String] }]; 163 [vci_si setVideoFilterProperty: "marq-position" forFilter: "marq" withValue: getWidgetIntValue([items objectAtIndex:25])]; 164 [vci_si setVideoFilterProperty: "logo-file" forFilter: "logo" withValue: (vlc_value_t){ .psz_string = (char *)[B64DecNSStr([items objectAtIndex:26]) UTF8String] }]; 165 [vci_si setVideoFilterProperty: "logo-position" forFilter: "logo" withValue: getWidgetIntValue([items objectAtIndex:27])]; 166 [vci_si setVideoFilterProperty: "logo-opacity" forFilter: "logo" withValue: getWidgetIntValue([items objectAtIndex:28])]; 167 [vci_si setVideoFilterProperty: "clone-count" forFilter: "clone" withValue: getWidgetIntValue([items objectAtIndex:29])]; 168 [vci_si setVideoFilterProperty: "wall-rows" forFilter: "wall" withValue: getWidgetIntValue([items objectAtIndex:30])]; 169 [vci_si setVideoFilterProperty: "wall-cols" forFilter: "wall" withValue: getWidgetIntValue([items objectAtIndex:31])]; 170 171 if ([items count] >= 33) { // version >=2 of profile string 172 [vci_si setVideoFilterProperty: "brightness-threshold" forFilter: "adjust" withValue: (vlc_value_t){ .b_bool = [[items objectAtIndex:32] intValue] }]; 173 } 174 175 vlc_value_t hueValue; 176 if ([items count] >= 34) { // version >=3 of profile string 177 hueValue.f_float = [[items objectAtIndex:33] floatValue]; 178 } else { 179 hueValue.f_float = [[items objectAtIndex:3] intValue]; // deprecated since 3.0.0 180 // convert to new scale ([0,360] --> [-180,180]) 181 hueValue.f_float -= 180; 182 } 183 [vci_si setVideoFilterProperty: "hue" forFilter: "adjust" withValue: hueValue]; 184} 185 186- (void)windowDidLoad 187{ 188 [self.window setTitle: _NS("Video Effects")]; 189 [self.window setExcludedFromWindowsMenu:YES]; 190 [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary]; 191 192 [_segmentView setLabel:_NS("Basic") forSegment:0]; 193 [_segmentView setLabel:_NS("Crop") forSegment:1]; 194 [_segmentView setLabel:_NS("Geometry") forSegment:2]; 195 [_segmentView setLabel:_NS("Color") forSegment:3]; 196 [_segmentView setLabel:_NS("Miscellaneous") forSegment:4]; 197 198 [_applyProfileCheckbox setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"VideoEffectApplyProfileOnStartup"]]; 199 [_applyProfileCheckbox setTitle:_NS("Apply profile at next launch")]; 200 201 [self resetProfileSelector]; 202 203 [_adjustCheckbox setTitle:_NS("Image Adjust")]; 204 [_adjustHueLabel setStringValue:_NS("Hue")]; 205 [_adjustContrastLabel setStringValue:_NS("Contrast")]; 206 [_adjustBrightnessLabel setStringValue:_NS("Brightness")]; 207 [_adjustBrightnessCheckbox setTitle:_NS("Brightness Threshold")]; 208 [_adjustSaturationLabel setStringValue:_NS("Saturation")]; 209 [_adjustGammaLabel setStringValue:_NS("Gamma")]; 210 [_adjustResetButton setTitle: _NS("Reset")]; 211 [_sharpenCheckbox setTitle:_NS("Sharpen")]; 212 [_sharpenLabel setStringValue:_NS("Sigma")]; 213 [_bandingCheckbox setTitle:_NS("Banding removal")]; 214 [_bandingLabel setStringValue:_NS("Radius")]; 215 [_grainCheckbox setTitle:_NS("Film Grain")]; 216 [_grainLabel setStringValue:_NS("Variance")]; 217 [_cropTopLabel setStringValue:_NS("Top")]; 218 [_cropLeftLabel setStringValue:_NS("Left")]; 219 [_cropRightLabel setStringValue:_NS("Right")]; 220 [_cropBottomLabel setStringValue:_NS("Bottom")]; 221 [_cropSyncTopBottomCheckbox setTitle:_NS("Synchronize top and bottom")]; 222 [_cropSyncLeftRightCheckbox setTitle:_NS("Synchronize left and right")]; 223 224 [_transformCheckbox setTitle:_NS("Transform")]; 225 [_transformPopup removeAllItems]; 226 [_transformPopup addItemWithTitle: _NS("Rotate by 90 degrees")]; 227 [[_transformPopup lastItem] setRepresentedObject: @"90"]; 228 [[_transformPopup lastItem] setTag: 90]; 229 [_transformPopup addItemWithTitle: _NS("Rotate by 180 degrees")]; 230 [[_transformPopup lastItem] setRepresentedObject: @"180"]; 231 [[_transformPopup lastItem] setTag: 180]; 232 [_transformPopup addItemWithTitle: _NS("Rotate by 270 degrees")]; 233 [[_transformPopup lastItem] setRepresentedObject: @"270"]; 234 [[_transformPopup lastItem] setTag: 270]; 235 [_transformPopup addItemWithTitle: _NS("Flip horizontally")]; 236 [[_transformPopup lastItem] setRepresentedObject: @"hflip"]; 237 [[_transformPopup lastItem] setTag: 1]; 238 [_transformPopup addItemWithTitle: _NS("Flip vertically")]; 239 [[_transformPopup lastItem] setRepresentedObject: @"vflip"]; 240 [[_transformPopup lastItem] setTag: 2]; 241 [_zoomCheckbox setTitle:_NS("Magnification/Zoom")]; 242 [_puzzleCheckbox setTitle:_NS("Puzzle game")]; 243 [_puzzleRowsLabel setStringValue:_NS("Rows")]; 244 [_puzzleColumnsLabel setStringValue:_NS("Columns")]; 245 [_cloneCheckbox setTitle:_NS("Clone")]; 246 [_cloneNumberLabel setStringValue:_NS("Number of clones")]; 247 [_wallCheckbox setTitle:_NS("Wall")]; 248 [_wallNumbersOfRowsLabel setStringValue:_NS("Rows")]; 249 [_wallNumberOfColumnsLabel setStringValue:_NS("Columns")]; 250 251 [_thresholdCheckbox setTitle:_NS("Color threshold")]; 252 [_thresholdColorLabel setStringValue:_NS("Color")]; 253 [_thresholdColorTextField setFormatter:[[VLCHexNumberFormatter alloc] init]]; 254 [_thresholdSaturationLabel setStringValue:_NS("Saturation")]; 255 [_thresholdSimilarityLabel setStringValue:_NS("Similarity")]; 256 [_sepiaCheckbox setTitle:_NS("Sepia")]; 257 [_sepiaLabel setStringValue:_NS("Intensity")]; 258 [_gradientCheckbox setTitle:_NS("Gradient")]; 259 [_gradientModeLabel setStringValue:_NS("Mode")]; 260 [_gradientModePopup removeAllItems]; 261 [_gradientModePopup addItemWithTitle: _NS("Gradient")]; 262 [[_gradientModePopup lastItem] setRepresentedObject: @"gradient"]; 263 [[_gradientModePopup lastItem] setTag: 1]; 264 [_gradientModePopup addItemWithTitle: _NS("Edge")]; 265 [[_gradientModePopup lastItem] setRepresentedObject: @"edge"]; 266 [[_gradientModePopup lastItem] setTag: 2]; 267 [_gradientModePopup addItemWithTitle: _NS("Hough")]; 268 [[_gradientModePopup lastItem] setRepresentedObject: @"hough"]; 269 [[_gradientModePopup lastItem] setTag: 3]; 270 [_gradientColorCheckbox setTitle:_NS("Color")]; 271 [_gradientCartoonCheckbox setTitle:_NS("Cartoon")]; 272 [_extractCheckbox setTitle:_NS("Color extraction")]; 273 [_extractLabel setStringValue:_NS("Color")]; 274 [_extractTextField setFormatter:[[VLCHexNumberFormatter alloc] init]]; 275 [_invertCheckbox setTitle:_NS("Invert colors")]; 276 [_posterizeCheckbox setTitle:_NS("Posterize")]; 277 [_posterizeLabel setStringValue:_NS("Posterize level")]; 278 [_blurCheckbox setTitle:_NS("Motion blur")]; 279 [_blurLabel setStringValue:_NS("Factor")]; 280 [_motiondetectCheckbox setTitle:_NS("Motion Detect")]; 281 [_watereffectCheckbox setTitle:_NS("Water effect")]; 282 [_wavesCheckbox setTitle:_NS("Waves")]; 283 [_psychedelicCheckbox setTitle:_NS("Psychedelic")]; 284 [_anaglyphCheckbox setTitle:_NS("Anaglyph")]; 285 [_addTextCheckbox setTitle:_NS("Add text")]; 286 [_addTextTextLabel setStringValue:_NS("Text")]; 287 [_addTextPositionLabel setStringValue:_NS("Position")]; 288 [_addTextPositionPopup removeAllItems]; 289 [_addTextPositionPopup addItemWithTitle: _NS("Center")]; 290 [[_addTextPositionPopup lastItem] setTag: 0]; 291 [_addTextPositionPopup addItemWithTitle: _NS("Left")]; 292 [[_addTextPositionPopup lastItem] setTag: 1]; 293 [_addTextPositionPopup addItemWithTitle: _NS("Right")]; 294 [[_addTextPositionPopup lastItem] setTag: 2]; 295 [_addTextPositionPopup addItemWithTitle: _NS("Top")]; 296 [[_addTextPositionPopup lastItem] setTag: 4]; 297 [_addTextPositionPopup addItemWithTitle: _NS("Bottom")]; 298 [[_addTextPositionPopup lastItem] setTag: 8]; 299 [_addTextPositionPopup addItemWithTitle: _NS("Top-Left")]; 300 [[_addTextPositionPopup lastItem] setTag: 5]; 301 [_addTextPositionPopup addItemWithTitle: _NS("Top-Right")]; 302 [[_addTextPositionPopup lastItem] setTag: 6]; 303 [_addTextPositionPopup addItemWithTitle: _NS("Bottom-Left")]; 304 [[_addTextPositionPopup lastItem] setTag: 9]; 305 [_addTextPositionPopup addItemWithTitle: _NS("Bottom-Right")]; 306 [[_addTextPositionPopup lastItem] setTag: 10]; 307 [_addLogoCheckbox setTitle:_NS("Add logo")]; 308 [_addLogoLogoLabel setStringValue:_NS("Logo")]; 309 [_addLogoPositionLabel setStringValue:_NS("Position")]; 310 [_addLogoPositionPopup removeAllItems]; 311 [_addLogoPositionPopup addItemWithTitle: _NS("Center")]; 312 [[_addLogoPositionPopup lastItem] setTag: 0]; 313 [_addLogoPositionPopup addItemWithTitle: _NS("Left")]; 314 [[_addLogoPositionPopup lastItem] setTag: 1]; 315 [_addLogoPositionPopup addItemWithTitle: _NS("Right")]; 316 [[_addLogoPositionPopup lastItem] setTag: 2]; 317 [_addLogoPositionPopup addItemWithTitle: _NS("Top")]; 318 [[_addLogoPositionPopup lastItem] setTag: 4]; 319 [_addLogoPositionPopup addItemWithTitle: _NS("Bottom")]; 320 [[_addLogoPositionPopup lastItem] setTag: 8]; 321 [_addLogoPositionPopup addItemWithTitle: _NS("Top-Left")]; 322 [[_addLogoPositionPopup lastItem] setTag: 5]; 323 [_addLogoPositionPopup addItemWithTitle: _NS("Top-Right")]; 324 [[_addLogoPositionPopup lastItem] setTag: 6]; 325 [_addLogoPositionPopup addItemWithTitle: _NS("Bottom-Left")]; 326 [[_addLogoPositionPopup lastItem] setTag: 9]; 327 [_addLogoPositionPopup addItemWithTitle: _NS("Bottom-Right")]; 328 [[_addLogoPositionPopup lastItem] setTag: 10]; 329 [_addLogoTransparencyLabel setStringValue:_NS("Transparency")]; 330 331 [[NSNotificationCenter defaultCenter] addObserver:self 332 selector:@selector(inputChangedEvent:) 333 name:VLCInputChangedNotification 334 object:nil]; 335 336 [self resetValues]; 337} 338 339- (void)dealloc 340{ 341 [[NSNotificationCenter defaultCenter] removeObserver:self]; 342} 343 344- (void)updateCocoaWindowLevel:(NSInteger)i_level 345{ 346 if (self.isWindowLoaded && [self.window isVisible] && [self.window level] != i_level) 347 [self.window setLevel: i_level]; 348} 349 350#pragma mark - 351#pragma mark internal functions 352 353- (void)saveCurrentProfileIndex:(NSInteger)index 354{ 355 [[NSUserDefaults standardUserDefaults] setInteger:index forKey:@"VideoEffectSelectedProfile"]; 356} 357 358- (NSInteger)currentProfileIndex 359{ 360 return [[NSUserDefaults standardUserDefaults] integerForKey:@"VideoEffectSelectedProfile"]; 361} 362 363/// Returns the list of profile names (omitting the Default entry) 364- (NSArray *)nonDefaultProfileNames 365{ 366 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 367 368 NSMutableArray *names = [[defaults stringArrayForKey:@"VideoEffectProfileNames"] mutableCopy]; 369 [names removeObjectAtIndex:0]; 370 return [names copy]; 371} 372 373-(void)inputChangedEvent:(NSNotification *)o_notification 374{ 375 // reset crop values when input changed 376 [self setCropBottomValue:0]; 377 [self setCropTopValue:0]; 378 [self setCropLeftValue:0]; 379 [self setCropRightValue:0]; 380} 381 382- (void)resetProfileSelector 383{ 384 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 385 [_profilePopup removeAllItems]; 386 387 // Ignore "Default" index 0 from settings 388 [_profilePopup addItemWithTitle:_NS("Default")]; 389 390 [_profilePopup addItemsWithTitles:[self nonDefaultProfileNames]]; 391 392 [[_profilePopup menu] addItem:[NSMenuItem separatorItem]]; 393 [_profilePopup addItemWithTitle:_NS("Duplicate current profile...")]; 394 [[_profilePopup lastItem] setTarget: self]; 395 [[_profilePopup lastItem] setAction: @selector(addProfile:)]; 396 397 if ([[self nonDefaultProfileNames] count] > 0) { 398 [_profilePopup addItemWithTitle:_NS("Organize profiles...")]; 399 [[_profilePopup lastItem] setTarget: self]; 400 [[_profilePopup lastItem] setAction: @selector(removeProfile:)]; 401 } 402 403 [_profilePopup selectItemAtIndex: [self currentProfileIndex]]; 404 // Loading only non-default profiles ensures that vlcrc or command line settings are not overwritten 405 if ([self currentProfileIndex] > 0) 406 [self profileSelectorAction:self]; 407} 408 409- (void)setWidgetValue: (id)widget forOption: (char *)psz_option enabled: (bool)b_state 410{ 411 intf_thread_t *p_intf = getIntf(); 412 playlist_t *p_playlist = pl_Get(p_intf); 413 414 vlc_value_t val; 415 int i_type = config_GetType(psz_option) & VLC_VAR_CLASS; 416 switch (i_type) 417 { 418 case VLC_VAR_BOOL: 419 case VLC_VAR_INTEGER: 420 case VLC_VAR_FLOAT: 421 case VLC_VAR_STRING: 422 break; 423 default: 424 msg_Err(p_intf, "%s variable is of an unsupported type (%d)", psz_option, i_type); 425 return; 426 } 427 428 if (var_Create(p_playlist, psz_option, i_type | VLC_VAR_DOINHERIT) || 429 var_GetChecked(p_playlist, psz_option, i_type, &val)) { 430 return; 431 } 432 433 if (i_type == VLC_VAR_BOOL || i_type == VLC_VAR_INTEGER) 434 { 435 if ([widget isKindOfClass: [NSSlider class]]) 436 { 437 [widget setIntValue: val.i_int]; 438 [widget setToolTip: [NSString stringWithFormat:@"%lli", val.i_int]]; 439 } 440 else if ([widget isKindOfClass: [NSButton class]]) 441 [widget setState: val.i_int ? NSOnState : NSOffState]; 442 else if ([widget isKindOfClass: [NSTextField class]]) 443 [widget setIntValue: val.i_int]; 444 else if ([widget isKindOfClass: [NSStepper class]]) 445 [widget setIntValue: val.i_int]; 446 else if ([widget isKindOfClass: [NSPopUpButton class]]) 447 [widget selectItemWithTag: val.i_int]; 448 } 449 else if (i_type == VLC_VAR_FLOAT) 450 { 451 if ([widget isKindOfClass: [NSSlider class]]) 452 { 453 [widget setFloatValue: val.f_float]; 454 [widget setToolTip: [NSString stringWithFormat:@"%0.3f", val.f_float]]; 455 } 456 } 457 else if (i_type == VLC_VAR_STRING) 458 { 459 if ([widget isKindOfClass: [NSPopUpButton class]]) 460 { 461 for (NSMenuItem *item in [widget itemArray]) 462 if ([item representedObject] && 463 !strcmp([[item representedObject] UTF8String], val.psz_string)) 464 { 465 [widget selectItemWithTitle: [item title]]; 466 break; 467 } 468 } 469 else if ([widget isKindOfClass: [NSTextField class]]) 470 [widget setStringValue: toNSStr(val.psz_string)]; 471 free(val.psz_string); 472 } 473 474 [widget setEnabled: b_state]; 475} 476 477/// Sets widget values based on variables 478- (void)resetValues 479{ 480 intf_thread_t *p_intf = getIntf(); 481 playlist_t *p_playlist = pl_Get(p_intf); 482 NSString *tmpString; 483 char *tmpChar; 484 BOOL b_state; 485 486 /* do we have any filter enabled? if yes, show it. */ 487 char * psz_vfilters; 488 psz_vfilters = var_InheritString(p_playlist, "video-filter"); 489 if (psz_vfilters) { 490 [_adjustCheckbox setState: (NSInteger)strstr(psz_vfilters, "adjust")]; 491 [_sharpenCheckbox setState: (NSInteger)strstr(psz_vfilters, "sharpen")]; 492 [_bandingCheckbox setState: (NSInteger)strstr(psz_vfilters, "gradfun")]; 493 [_grainCheckbox setState: (NSInteger)strstr(psz_vfilters, "grain")]; 494 [_transformCheckbox setState: (NSInteger)strstr(psz_vfilters, "transform")]; 495 [_zoomCheckbox setState: (NSInteger)strstr(psz_vfilters, "magnify")]; 496 [_puzzleCheckbox setState: (NSInteger)strstr(psz_vfilters, "puzzle")]; 497 [_thresholdCheckbox setState: (NSInteger)strstr(psz_vfilters, "colorthres")]; 498 [_sepiaCheckbox setState: (NSInteger)strstr(psz_vfilters, "sepia")]; 499 [_gradientCheckbox setState: (NSInteger)strstr(psz_vfilters, "gradient")]; 500 [_extractCheckbox setState: (NSInteger)strstr(psz_vfilters, "extract")]; 501 [_invertCheckbox setState: (NSInteger)strstr(psz_vfilters, "invert")]; 502 [_posterizeCheckbox setState: (NSInteger)strstr(psz_vfilters, "posterize")]; 503 [_blurCheckbox setState: (NSInteger)strstr(psz_vfilters, "motionblur")]; 504 [_motiondetectCheckbox setState: (NSInteger)strstr(psz_vfilters, "motiondetect")]; 505 [_watereffectCheckbox setState: (NSInteger)strstr(psz_vfilters, "ripple")]; 506 [_wavesCheckbox setState: (NSInteger)strstr(psz_vfilters, "wave")]; 507 [_psychedelicCheckbox setState: (NSInteger)strstr(psz_vfilters, "psychedelic")]; 508 [_anaglyphCheckbox setState: (NSInteger)strstr(psz_vfilters, "anaglyph")]; 509 free(psz_vfilters); 510 } else { 511 [_adjustCheckbox setState: NSOffState]; 512 [_sharpenCheckbox setState: NSOffState]; 513 [_bandingCheckbox setState: NSOffState]; 514 [_grainCheckbox setState: NSOffState]; 515 [_transformCheckbox setState: NSOffState]; 516 [_zoomCheckbox setState: NSOffState]; 517 [_puzzleCheckbox setState: NSOffState]; 518 [_thresholdCheckbox setState: NSOffState]; 519 [_sepiaCheckbox setState: NSOffState]; 520 [_gradientCheckbox setState: NSOffState]; 521 [_extractCheckbox setState: NSOffState]; 522 [_invertCheckbox setState: NSOffState]; 523 [_posterizeCheckbox setState: NSOffState]; 524 [_blurCheckbox setState: NSOffState]; 525 [_motiondetectCheckbox setState: NSOffState]; 526 [_watereffectCheckbox setState: NSOffState]; 527 [_wavesCheckbox setState: NSOffState]; 528 [_psychedelicCheckbox setState: NSOffState]; 529 [_anaglyphCheckbox setState: NSOffState]; 530 } 531 532 psz_vfilters = var_InheritString(p_playlist, "sub-source"); 533 if (psz_vfilters) { 534 [_addTextCheckbox setState: (NSInteger)strstr(psz_vfilters, "marq")]; 535 [_addLogoCheckbox setState: (NSInteger)strstr(psz_vfilters, "logo")]; 536 free(psz_vfilters); 537 } else { 538 [_addTextCheckbox setState: NSOffState]; 539 [_addLogoCheckbox setState: NSOffState]; 540 } 541 542 psz_vfilters = var_InheritString(p_playlist, "video-splitter"); 543 if (psz_vfilters) { 544 [_cloneCheckbox setState: (NSInteger)strstr(psz_vfilters, "clone")]; 545 [_wallCheckbox setState: (NSInteger)strstr(psz_vfilters, "wall")]; 546 free(psz_vfilters); 547 } else { 548 [_cloneCheckbox setState: NSOffState]; 549 [_wallCheckbox setState: NSOffState]; 550 } 551 552 /* fetch and show the various values */ 553 b_state = [_adjustCheckbox state]; 554 [self setWidgetValue: _adjustHueSlider forOption: "hue" enabled: b_state]; 555 [self setWidgetValue: _adjustContrastSlider forOption: "contrast" enabled: b_state]; 556 [self setWidgetValue: _adjustBrightnessSlider forOption: "brightness" enabled: b_state]; 557 [self setWidgetValue: _adjustSaturationSlider forOption: "saturation" enabled: b_state]; 558 [self setWidgetValue: _adjustBrightnessCheckbox forOption: "brightness-threshold" enabled: b_state]; 559 [self setWidgetValue: _adjustGammaSlider forOption: "gamma" enabled: b_state]; 560 [_adjustBrightnessLabel setEnabled: b_state]; 561 [_adjustContrastLabel setEnabled: b_state]; 562 [_adjustGammaLabel setEnabled: b_state]; 563 [_adjustHueLabel setEnabled: b_state]; 564 [_adjustSaturationLabel setEnabled: b_state]; 565 [_adjustResetButton setEnabled: b_state]; 566 567 [self setWidgetValue: _sharpenSlider forOption: "sharpen-sigma" enabled: [_sharpenCheckbox state]]; 568 [_sharpenLabel setEnabled: [_sharpenCheckbox state]]; 569 570 [self setWidgetValue: _bandingSlider forOption: "gradfun-radius" enabled: [_bandingCheckbox state]]; 571 [_bandingLabel setEnabled: [_bandingCheckbox state]]; 572 573 [self setWidgetValue: _grainSlider forOption: "grain-variance" enabled: [_grainCheckbox state]]; 574 [_grainLabel setEnabled: [_grainCheckbox state]]; 575 576 [self setCropLeftValue: 0]; 577 [self setCropTopValue: 0]; 578 [self setCropRightValue: 0]; 579 [self setCropBottomValue: 0]; 580 [_cropSyncTopBottomCheckbox setState: NSOffState]; 581 [_cropSyncLeftRightCheckbox setState: NSOffState]; 582 583 [self setWidgetValue: _transformPopup forOption: "transform-type" enabled: [_transformCheckbox state]]; 584 585 b_state = [_puzzleCheckbox state]; 586 [self setWidgetValue: _puzzleColumnsTextField forOption: "puzzle-cols" enabled: b_state]; 587 [self setWidgetValue: _puzzleColumnsStepper forOption: "puzzle-cols" enabled: b_state]; 588 [self setWidgetValue: _puzzleRowsTextField forOption: "puzzle-rows" enabled: b_state]; 589 [self setWidgetValue: _puzzleRowsStepper forOption: "puzzle-rows" enabled: b_state]; 590 [_puzzleRowsLabel setEnabled: b_state]; 591 [_puzzleColumnsLabel setEnabled: b_state]; 592 593 b_state = [_cloneCheckbox state]; 594 [self setWidgetValue: _cloneNumberTextField forOption: "clone-count" enabled: b_state]; 595 [self setWidgetValue: _cloneNumberStepper forOption: "clone-count" enabled: b_state]; 596 [_cloneNumberLabel setEnabled: b_state]; 597 598 b_state = [_wallCheckbox state]; 599 [self setWidgetValue: _wallNumbersOfRowsTextField forOption: "wall-rows" enabled: b_state]; 600 [self setWidgetValue: _wallNumbersOfRowsStepper forOption: "wall-rows" enabled: b_state]; 601 [self setWidgetValue: _wallNumberOfColumnsTextField forOption: "wall-cols" enabled: b_state]; 602 [self setWidgetValue: _wallNumberOfColumnsStepper forOption: "wall-cols" enabled: b_state]; 603 [_wallNumbersOfRowsLabel setEnabled: b_state]; 604 [_wallNumberOfColumnsLabel setEnabled: b_state]; 605 606 b_state = [_thresholdCheckbox state]; 607 [self setWidgetValue: _thresholdColorTextField forOption: "colorthres-color" enabled: b_state]; 608 [self setWidgetValue: _thresholdSaturationSlider forOption: "colorthres-saturationthres" enabled: b_state]; 609 [self setWidgetValue: _thresholdSimilaritySlider forOption: "colorthres-similaritythres" enabled: b_state]; 610 [_thresholdColorLabel setEnabled: b_state]; 611 [_thresholdSaturationLabel setEnabled: b_state]; 612 [_thresholdSimilarityLabel setEnabled: b_state]; 613 614 b_state = [_sepiaCheckbox state]; 615 [self setWidgetValue: _sepiaTextField forOption: "sepia-intensity" enabled: b_state]; 616 [self setWidgetValue: _sepiaStepper forOption: "sepia-intensity" enabled: b_state]; 617 [_sepiaLabel setEnabled: b_state]; 618 619 b_state = [_gradientCheckbox state]; 620 [self setWidgetValue: _gradientModePopup forOption: "gradient-mode" enabled: b_state]; 621 [self setWidgetValue: _gradientCartoonCheckbox forOption: "gradient-cartoon" enabled: b_state]; 622 [self setWidgetValue: _gradientColorCheckbox forOption: "gradient-type" enabled: b_state]; 623 [_gradientModeLabel setEnabled: b_state]; 624 625 [self setWidgetValue: _extractTextField forOption: "extract-component" enabled: [_extractCheckbox state]]; 626 [_extractLabel setEnabled: [_extractCheckbox state]]; 627 628 b_state = [_posterizeCheckbox state]; 629 [self setWidgetValue: _posterizeTextField forOption: "posterize-level" enabled: b_state]; 630 [self setWidgetValue: _posterizeStepper forOption: "posterize-level" enabled: b_state]; 631 [_posterizeLabel setEnabled: b_state]; 632 633 [self setWidgetValue: _blurSlider forOption: "blur-factor" enabled: [_blurCheckbox state]]; 634 [_blurLabel setEnabled: [_blurCheckbox state]]; 635 636 b_state = [_addTextCheckbox state]; 637 [self setWidgetValue: _addTextTextTextField forOption: "marq-marquee" enabled: b_state]; 638 [self setWidgetValue: _addTextPositionPopup forOption: "marq-position" enabled: b_state]; 639 [_addTextPositionLabel setEnabled: b_state]; 640 [_addTextTextLabel setEnabled: b_state]; 641 642 b_state = [_addLogoCheckbox state]; 643 [self setWidgetValue: _addLogoLogoTextField forOption: "logo-file" enabled: b_state]; 644 [self setWidgetValue: _addLogoPositionPopup forOption: "logo-position" enabled: b_state]; 645 [self setWidgetValue: _addLogoTransparencySlider forOption: "logo-opacity" enabled: b_state]; 646 [_addLogoPositionLabel setEnabled: b_state]; 647 [_addLogoLogoLabel setEnabled: b_state]; 648 [_addLogoTransparencyLabel setEnabled: b_state]; 649} 650 651- (NSString *)generateProfileString 652{ 653 intf_thread_t *p_intf = getIntf(); 654 playlist_t *p_playlist = pl_Get(p_intf); 655 return [NSString stringWithFormat:@"%@;%@;%@;%lli;%f;%f;%f;%f;%f;%lli;%f;%@;%lli;%lli;%lli;%lli;%lli;%lli;%@;%lli;%lli;%lli;%lli;%lli;%@;%lli;%@;%lli;%lli;%lli;%lli;%lli;%lli;%f", 656 B64EncAndFree(var_InheritString(p_playlist, "video-filter")), 657 B64EncAndFree(var_InheritString(p_playlist, "sub-source")), 658 B64EncAndFree(var_InheritString(p_playlist, "video-splitter")), 659 0LL, // former "hue" value, deprecated since 3.0.0 660 var_InheritFloat(p_playlist, "contrast"), 661 var_InheritFloat(p_playlist, "brightness"), 662 var_InheritFloat(p_playlist, "saturation"), 663 var_InheritFloat(p_playlist, "gamma"), 664 var_InheritFloat(p_playlist, "sharpen-sigma"), 665 var_InheritInteger(p_playlist, "gradfun-radius"), 666 var_InheritFloat(p_playlist, "grain-variance"), 667 B64EncAndFree(var_InheritString(p_playlist, "transform-type")), 668 var_InheritInteger(p_playlist, "puzzle-rows"), 669 var_InheritInteger(p_playlist, "puzzle-cols"), 670 var_InheritInteger(p_playlist, "colorthres-color"), 671 var_InheritInteger(p_playlist, "colorthres-saturationthres"), 672 var_InheritInteger(p_playlist, "colorthres-similaritythres"), 673 var_InheritInteger(p_playlist, "sepia-intensity"), 674 B64EncAndFree(var_InheritString(p_playlist, "gradient-mode")), 675 (int64_t)var_InheritBool(p_playlist, "gradient-cartoon"), 676 var_InheritInteger(p_playlist, "gradient-type"), 677 var_InheritInteger(p_playlist, "extract-component"), 678 var_InheritInteger(p_playlist, "posterize-level"), 679 var_InheritInteger(p_playlist, "blur-factor"), 680 B64EncAndFree(var_InheritString(p_playlist, "marq-marquee")), 681 var_InheritInteger(p_playlist, "marq-position"), 682 B64EncAndFree(var_InheritString(p_playlist, "logo-file")), 683 var_InheritInteger(p_playlist, "logo-position"), 684 var_InheritInteger(p_playlist, "logo-opacity"), 685 var_InheritInteger(p_playlist, "clone-count"), 686 var_InheritInteger(p_playlist, "wall-rows"), 687 var_InheritInteger(p_playlist, "wall-cols"), 688 // version 2 of profile string: 689 (int64_t)var_InheritBool(p_playlist, "brightness-threshold"), // index: 32 690 // version 3 of profile string: (vlc-3.0.0) 691 var_InheritFloat(p_playlist, "hue") // index: 33 692 ]; 693} 694 695#pragma mark - 696#pragma mark generic UI code 697 698- (void)saveCurrentProfile 699{ 700 NSInteger currentProfileIndex = [self currentProfileIndex]; 701 702 // Do not save default profile 703 if (currentProfileIndex == 0) { 704 return; 705 } 706 707 /* fetch all the current settings in a uniform string */ 708 NSString *newProfile = [self generateProfileString]; 709 710 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 711 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"VideoEffectProfiles"]]; 712 if (currentProfileIndex >= [workArray count]) 713 return; 714 715 [workArray replaceObjectAtIndex:currentProfileIndex withObject:newProfile]; 716 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfiles"]; 717 [defaults synchronize]; 718} 719 720- (void)saveCurrentProfileAtTerminate 721{ 722 if ([self currentProfileIndex] > 0) { 723 [self saveCurrentProfile]; 724 return; 725 } 726 727 // A new "Custom profile" is only created if the user wants to load this as new profile at startup ... 728 if (_applyProfileCheckbox.state == NSOffState) 729 return; 730 731 // ... and some settings are changed 732 NSString *newProfile = [self generateProfileString]; 733 if ([newProfile compare:[VLCVideoEffectsWindowController defaultProfileString]] == NSOrderedSame) 734 return; 735 736 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 737 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"VideoEffectProfiles"]]; 738 [workArray addObject:newProfile]; 739 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfiles"]; 740 741 NSArray<NSString *> *profileNames = [defaults objectForKey:@"VideoEffectProfileNames"]; 742 NSString *newProfileName; 743 744 unsigned int num_custom = 0; 745 do 746 newProfileName = [@"Custom" stringByAppendingString:[NSString stringWithFormat:@"%03i",num_custom++]]; 747 while ([profileNames containsObject:newProfileName]); 748 749 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"VideoEffectProfileNames"]]; 750 [workArray addObject:newProfileName]; 751 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfileNames"]; 752 753 [self saveCurrentProfileIndex:([workArray count] - 1)]; 754 755 [defaults synchronize]; 756} 757 758- (IBAction)toggleWindow:(id)sender 759{ 760 if ([self.window isKeyWindow]) 761 [self.window orderOut:sender]; 762 else { 763 [self.window setLevel: [[[VLCMain sharedInstance] voutController] currentStatusWindowLevel]]; 764 [self.window makeKeyAndOrderFront:sender]; 765 } 766} 767 768- (IBAction)profileSelectorAction:(id)sender 769{ 770 [self saveCurrentProfile]; 771 772 [self saveCurrentProfileIndex:[_profilePopup indexOfSelectedItem]]; 773 774 [self loadProfile]; 775 [self resetValues]; 776} 777 778- (void)addProfile:(id)sender 779{ 780 /* show panel */ 781#pragma clang diagnostic push 782#pragma clang diagnostic ignored "-Wpartial-availability" 783#ifdef MAC_OS_X_VERSION_10_10 784 if (OSX_YOSEMITE_AND_HIGHER) { 785 [[_textfieldPanel window] setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; 786 } 787#endif 788#pragma clang diagnostic pop 789 [_textfieldPanel setTitleString:_NS("Duplicate current profile for a new profile")]; 790 [_textfieldPanel setSubTitleString:_NS("Enter a name for the new profile:")]; 791 [_textfieldPanel setCancelButtonString:_NS("Cancel")]; 792 [_textfieldPanel setOkButtonString:_NS("Save")]; 793 794 // TODO: Change to weak, when dropping 10.7 support 795 __unsafe_unretained typeof(self) _self = self; 796 [_textfieldPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSString *resultingText) { 797 798 NSInteger currentProfileIndex = [_self currentProfileIndex]; 799 if (returnCode != NSOKButton) { 800 [_profilePopup selectItemAtIndex:currentProfileIndex]; 801 return; 802 } 803 804 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 805 NSArray *profileNames = [defaults objectForKey:@"VideoEffectProfileNames"]; 806 807 // duplicate names are not allowed in the popup control 808 if ([resultingText length] == 0 || [profileNames containsObject:resultingText]) { 809 [_profilePopup selectItemAtIndex:currentProfileIndex]; 810 811 NSAlert *alert = [[NSAlert alloc] init]; 812 [alert setAlertStyle:NSCriticalAlertStyle]; 813 [alert setMessageText:_NS("Please enter a unique name for the new profile.")]; 814 [alert setInformativeText:_NS("Multiple profiles with the same name are not allowed.")]; 815 816 [alert beginSheetModalForWindow:_self.window 817 modalDelegate:nil 818 didEndSelector:nil 819 contextInfo:nil]; 820 return; 821 } 822 823 /* fetch all the current settings in a uniform string */ 824 NSString *newProfile = [_self generateProfileString]; 825 826 /* add string to user defaults as well as a label */ 827 828 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"VideoEffectProfiles"]]; 829 [workArray addObject:newProfile]; 830 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfiles"]; 831 832 [self saveCurrentProfileIndex:([workArray count] - 1)]; 833 834 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"VideoEffectProfileNames"]]; 835 [workArray addObject:resultingText]; 836 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfileNames"]; 837 838 /* save defaults */ 839 [defaults synchronize]; 840 841 /* refresh UI */ 842 [_self resetProfileSelector]; 843 }]; 844} 845 846- (void)removeProfile:(id)sender 847{ 848 /* show panel */ 849#pragma clang diagnostic push 850#pragma clang diagnostic ignored "-Wpartial-availability" 851#ifdef MAC_OS_X_VERSION_10_10 852 if (OSX_YOSEMITE_AND_HIGHER) { 853 [[_popupPanel window] setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; 854 } 855#endif 856#pragma clang diagnostic pop 857 [_popupPanel setTitleString:_NS("Remove a preset")]; 858 [_popupPanel setSubTitleString:_NS("Select the preset you would like to remove:")]; 859 [_popupPanel setOkButtonString:_NS("Remove")]; 860 [_popupPanel setCancelButtonString:_NS("Cancel")]; 861 [_popupPanel setPopupButtonContent:[self nonDefaultProfileNames]]; 862 863 // TODO: Change to weak, when dropping 10.7 support 864 __unsafe_unretained typeof(self) _self = self; 865 [_popupPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSInteger selectedIndex) { 866 867 868 NSInteger activeProfileIndex = [_self currentProfileIndex]; 869 870 if (returnCode != NSOKButton) { 871 [_profilePopup selectItemAtIndex:activeProfileIndex]; 872 return; 873 } 874 875 // Popup panel does not contain the "Default" entry 876 selectedIndex++; 877 878 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 879 /* remove selected profile from settings */ 880 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray: [defaults objectForKey:@"VideoEffectProfiles"]]; 881 [workArray removeObjectAtIndex:selectedIndex]; 882 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfiles"]; 883 884 workArray = [[NSMutableArray alloc] initWithArray: [defaults objectForKey:@"VideoEffectProfileNames"]]; 885 [workArray removeObjectAtIndex:selectedIndex]; 886 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfileNames"]; 887 888 if (activeProfileIndex >= selectedIndex) 889 [self saveCurrentProfileIndex:(activeProfileIndex - 1)]; 890 891 /* save defaults */ 892 [defaults synchronize]; 893 894 /* refresh UI */ 895 [_self resetProfileSelector]; 896 }]; 897} 898 899- (IBAction)applyProfileCheckboxChanged:(id)sender 900{ 901 [[NSUserDefaults standardUserDefaults] setBool:[sender state] forKey:@"VideoEffectApplyProfileOnStartup"]; 902} 903 904#pragma mark - 905#pragma mark basic 906- (IBAction)enableAdjust:(id)sender 907{ 908 if (sender == _adjustResetButton) { 909 [_adjustBrightnessSlider setFloatValue: 1.0]; 910 [_adjustContrastSlider setFloatValue: 1.0]; 911 [_adjustGammaSlider setFloatValue: 1.0]; 912 [_adjustHueSlider setFloatValue: 0]; 913 [_adjustSaturationSlider setFloatValue: 1.0]; 914 [_adjustBrightnessSlider setToolTip: [NSString stringWithFormat:@"%0.3f", 1.0]]; 915 [_adjustContrastSlider setToolTip: [NSString stringWithFormat:@"%0.3f", 1.0]]; 916 [_adjustGammaSlider setToolTip: [NSString stringWithFormat:@"%0.3f", 1.0]]; 917 [_adjustHueSlider setToolTip: [NSString stringWithFormat:@"%.0f", 0.0]]; 918 [_adjustSaturationSlider setToolTip: [NSString stringWithFormat:@"%0.3f", 1.0]]; 919 920 VLCCoreInteraction *vci_si = [VLCCoreInteraction sharedInstance]; 921 [vci_si setVideoFilterProperty: "brightness" forFilter: "adjust" withValue: (vlc_value_t){ .f_float = 1.f }]; 922 [vci_si setVideoFilterProperty: "contrast" forFilter: "adjust" withValue: (vlc_value_t){ .f_float = 1.f }]; 923 [vci_si setVideoFilterProperty: "gamma" forFilter: "adjust" withValue: (vlc_value_t){ .f_float = 1.f }]; 924 [vci_si setVideoFilterProperty: "hue" forFilter: "adjust" withValue: (vlc_value_t){ .f_float = .0f }]; 925 [vci_si setVideoFilterProperty: "saturation" forFilter: "adjust" withValue: (vlc_value_t){ .f_float = 1.f }]; 926 } else { 927 BOOL b_state = [_adjustCheckbox state]; 928 929 [[VLCCoreInteraction sharedInstance] setVideoFilter: "adjust" on: b_state]; 930 [_adjustBrightnessSlider setEnabled: b_state]; 931 [_adjustBrightnessCheckbox setEnabled: b_state]; 932 [_adjustBrightnessLabel setEnabled: b_state]; 933 [_adjustContrastSlider setEnabled: b_state]; 934 [_adjustContrastLabel setEnabled: b_state]; 935 [_adjustGammaSlider setEnabled: b_state]; 936 [_adjustGammaLabel setEnabled: b_state]; 937 [_adjustHueSlider setEnabled: b_state]; 938 [_adjustHueLabel setEnabled: b_state]; 939 [_adjustSaturationSlider setEnabled: b_state]; 940 [_adjustSaturationLabel setEnabled: b_state]; 941 [_adjustResetButton setEnabled: b_state]; 942 } 943} 944 945- (IBAction)adjustSliderChanged:(id)sender 946{ 947 char const *psz_property = nil; 948 if (sender == _adjustBrightnessSlider) 949 psz_property = "brightness"; 950 else if (sender == _adjustContrastSlider) 951 psz_property = "contrast"; 952 else if (sender == _adjustGammaSlider) 953 psz_property = "gamma"; 954 else if (sender == _adjustHueSlider) 955 psz_property = "hue"; 956 else if (sender == _adjustSaturationSlider) 957 psz_property = "saturation"; 958 assert(psz_property); 959 960 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: psz_property forFilter: "adjust" withValue: getWidgetFloatValue(sender)]; 961 962 if (sender == _adjustHueSlider) 963 [_adjustHueSlider setToolTip: [NSString stringWithFormat:@"%.0f", [_adjustHueSlider floatValue]]]; 964 else 965 [sender setToolTip: [NSString stringWithFormat:@"%0.3f", [sender floatValue]]]; 966} 967 968- (IBAction)enableAdjustBrightnessThreshold:(id)sender 969{ 970 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "brightness-threshold" 971 forFilter: "adjust" 972 withValue: getWidgetBoolValue(sender)]; 973} 974 975- (IBAction)enableSharpen:(id)sender 976{ 977 BOOL b_state = [_sharpenCheckbox state]; 978 979 [[VLCCoreInteraction sharedInstance] setVideoFilter: "sharpen" on: b_state]; 980 [_sharpenSlider setEnabled: b_state]; 981 [_sharpenLabel setEnabled: b_state]; 982} 983 984- (IBAction)sharpenSliderChanged:(id)sender 985{ 986 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "sharpen-sigma" forFilter: "sharpen" withValue: getWidgetFloatValue(sender)]; 987 [sender setToolTip: [NSString stringWithFormat:@"%0.3f", [sender floatValue]]]; 988} 989 990- (IBAction)enableBanding:(id)sender 991{ 992 BOOL b_state = [_bandingCheckbox state]; 993 994 [[VLCCoreInteraction sharedInstance] setVideoFilter: "gradfun" on: b_state]; 995 [_bandingSlider setEnabled: b_state]; 996 [_bandingLabel setEnabled: b_state]; 997} 998 999- (IBAction)bandingSliderChanged:(id)sender 1000{ 1001 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "gradfun-radius" forFilter: "gradfun" withValue: getWidgetIntValue(sender)]; 1002 [sender setToolTip: [NSString stringWithFormat:@"%i", [sender intValue]]]; 1003} 1004 1005- (IBAction)enableGrain:(id)sender 1006{ 1007 BOOL b_state = [_grainCheckbox state]; 1008 1009 [[VLCCoreInteraction sharedInstance] setVideoFilter: "grain" on: b_state]; 1010 [_grainSlider setEnabled: b_state]; 1011 [_grainLabel setEnabled: b_state]; 1012} 1013 1014- (IBAction)grainSliderChanged:(id)sender 1015{ 1016 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "grain-variance" forFilter: "grain" withValue: getWidgetFloatValue(sender)]; 1017 [sender setToolTip: [NSString stringWithFormat:@"%0.3f", [sender floatValue]]]; 1018} 1019 1020 1021#pragma mark - 1022#pragma mark crop 1023 1024- (IBAction)cropObjectChanged:(id)sender 1025{ 1026 if ([_cropSyncTopBottomCheckbox state]) { 1027 if (sender == _cropBottomTextField || sender == _cropBottomStepper) 1028 [self setCropTopValue: [self cropBottomValue]]; 1029 else 1030 [self setCropBottomValue: [self cropTopValue]]; 1031 } 1032 if ([_cropSyncLeftRightCheckbox state]) { 1033 if (sender == _cropRightTextField || sender == _cropRightStepper) 1034 [self setCropLeftValue: [self cropRightValue]]; 1035 else 1036 [self setCropRightValue: [self cropLeftValue]]; 1037 } 1038 1039 NSArray<NSValue *> *vouts = getVouts(); 1040 if (vouts) 1041 for (NSValue *ptr in vouts) { 1042 vout_thread_t *p_vout = [ptr pointerValue]; 1043 var_SetInteger(p_vout, "crop-top", [_cropTopTextField intValue]); 1044 var_SetInteger(p_vout, "crop-bottom", [_cropBottomTextField intValue]); 1045 var_SetInteger(p_vout, "crop-left", [_cropLeftTextField intValue]); 1046 var_SetInteger(p_vout, "crop-right", [_cropRightTextField intValue]); 1047 vlc_object_release(p_vout); 1048 } 1049} 1050 1051#pragma mark - 1052#pragma mark geometry 1053- (IBAction)enableTransform:(id)sender 1054{ 1055 [[VLCCoreInteraction sharedInstance] setVideoFilter: "transform" on: [_transformCheckbox state]]; 1056 [_transformPopup setEnabled: [_transformCheckbox state]]; 1057} 1058 1059- (IBAction)transformModifierChanged:(id)sender 1060{ 1061 vlc_value_t value = { .psz_string = (char *)[[[_transformPopup selectedItem] representedObject] UTF8String] }; 1062 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "transform-type" forFilter: "transform" withValue: value]; 1063} 1064 1065- (IBAction)enableZoom:(id)sender 1066{ 1067 [[VLCCoreInteraction sharedInstance] setVideoFilter: "magnify" on: [_zoomCheckbox state]]; 1068} 1069 1070- (IBAction)enablePuzzle:(id)sender 1071{ 1072 BOOL b_state = [_puzzleCheckbox state]; 1073 1074 [[VLCCoreInteraction sharedInstance] setVideoFilter: "puzzle" on: b_state]; 1075 [_puzzleColumnsTextField setEnabled: b_state]; 1076 [_puzzleColumnsStepper setEnabled: b_state]; 1077 [_puzzleColumnsLabel setEnabled: b_state]; 1078 [_puzzleRowsTextField setEnabled: b_state]; 1079 [_puzzleRowsStepper setEnabled: b_state]; 1080 [_puzzleRowsLabel setEnabled: b_state]; 1081} 1082 1083- (IBAction)puzzleModifierChanged:(id)sender 1084{ 1085 if (sender == _puzzleColumnsTextField || sender == _puzzleColumnsStepper) 1086 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "puzzle-cols" forFilter: "puzzle" withValue: getWidgetIntValue(sender)]; 1087 else 1088 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "puzzle-rows" forFilter: "puzzle" withValue: getWidgetIntValue(sender)]; 1089} 1090 1091- (IBAction)enableClone:(id)sender 1092{ 1093 BOOL b_state = [_cloneCheckbox state]; 1094 1095 if (b_state && [_wallCheckbox state]) { 1096 [_wallCheckbox setState: NSOffState]; 1097 [self enableWall:_wallCheckbox]; 1098 } 1099 1100 [[VLCCoreInteraction sharedInstance] setVideoFilter: "clone" on: b_state]; 1101 [_cloneNumberLabel setEnabled: b_state]; 1102 [_cloneNumberTextField setEnabled: b_state]; 1103 [_cloneNumberStepper setEnabled: b_state]; 1104} 1105 1106- (IBAction)cloneModifierChanged:(id)sender 1107{ 1108 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "clone-count" forFilter: "clone" withValue: getWidgetIntValue(_cloneNumberTextField)]; 1109} 1110 1111- (IBAction)enableWall:(id)sender 1112{ 1113 BOOL b_state = [_wallCheckbox state]; 1114 1115 if (b_state && [_cloneCheckbox state]) { 1116 [_cloneCheckbox setState: NSOffState]; 1117 [self enableClone:_cloneCheckbox]; 1118 } 1119 1120 [[VLCCoreInteraction sharedInstance] setVideoFilter: "wall" on: b_state]; 1121 [_wallNumberOfColumnsTextField setEnabled: b_state]; 1122 [_wallNumberOfColumnsStepper setEnabled: b_state]; 1123 [_wallNumberOfColumnsLabel setEnabled: b_state]; 1124 1125 [_wallNumbersOfRowsTextField setEnabled: b_state]; 1126 [_wallNumbersOfRowsStepper setEnabled: b_state]; 1127 [_wallNumbersOfRowsLabel setEnabled: b_state]; 1128} 1129 1130- (IBAction)wallModifierChanged:(id)sender 1131{ 1132 char const *psz_property; 1133 if (sender == _wallNumberOfColumnsTextField || sender == _wallNumberOfColumnsStepper) 1134 psz_property = "wall-cols"; 1135 else 1136 psz_property = "wall-rows"; 1137 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: psz_property forFilter: "wall" withValue: getWidgetIntValue(sender)]; 1138} 1139 1140#pragma mark - 1141#pragma mark color 1142- (IBAction)enableThreshold:(id)sender 1143{ 1144 BOOL b_state = [_thresholdCheckbox state]; 1145 1146 [[VLCCoreInteraction sharedInstance] setVideoFilter: "colorthres" on: b_state]; 1147 [_thresholdColorTextField setEnabled: b_state]; 1148 [_thresholdColorLabel setEnabled: b_state]; 1149 [_thresholdSaturationSlider setEnabled: b_state]; 1150 [_thresholdSaturationLabel setEnabled: b_state]; 1151 [_thresholdSimilaritySlider setEnabled: b_state]; 1152 [_thresholdSimilarityLabel setEnabled: b_state]; 1153} 1154 1155- (IBAction)thresholdModifierChanged:(id)sender 1156{ 1157 if (sender == _thresholdColorTextField) 1158 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "colorthres-color" forFilter: "colorthres" withValue: getWidgetIntValue(sender)]; 1159 else if (sender == _thresholdSaturationSlider) { 1160 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "colorthres-saturationthres" forFilter: "colorthres" withValue: getWidgetIntValue(sender)]; 1161 [_thresholdSaturationSlider setToolTip: [NSString stringWithFormat:@"%i", [_thresholdSaturationSlider intValue]]]; 1162 } else { 1163 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "colorthres-similaritythres" forFilter: "colorthres" withValue: getWidgetIntValue(sender)]; 1164 [_thresholdSimilaritySlider setToolTip: [NSString stringWithFormat:@"%i", [_thresholdSimilaritySlider intValue]]]; 1165 } 1166} 1167 1168- (IBAction)enableSepia:(id)sender 1169{ 1170 BOOL b_state = [_sepiaCheckbox state]; 1171 1172 [[VLCCoreInteraction sharedInstance] setVideoFilter: "sepia" on: b_state]; 1173 [_sepiaTextField setEnabled: b_state]; 1174 [_sepiaStepper setEnabled: b_state]; 1175 [_sepiaLabel setEnabled: b_state]; 1176} 1177 1178- (IBAction)sepiaModifierChanged:(id)sender 1179{ 1180 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "sepia-intensity" forFilter: "sepia" withValue: getWidgetIntValue(sender)]; 1181} 1182 1183- (IBAction)enableGradient:(id)sender 1184{ 1185 BOOL b_state = [_gradientCheckbox state]; 1186 1187 [[VLCCoreInteraction sharedInstance] setVideoFilter: "gradient" on: b_state]; 1188 [_gradientModePopup setEnabled: b_state]; 1189 [_gradientModeLabel setEnabled: b_state]; 1190 [_gradientColorCheckbox setEnabled: b_state]; 1191 [_gradientCartoonCheckbox setEnabled: b_state]; 1192} 1193 1194- (IBAction)gradientModifierChanged:(id)sender 1195{ 1196 if (sender == _gradientModePopup) { 1197 vlc_value_t value = { .psz_string = (char *)[[[sender selectedItem] representedObject] UTF8String] }; 1198 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "gradient-mode" forFilter: "gradient" withValue: value]; 1199 } else if (sender == _gradientColorCheckbox) 1200 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "gradient-type" forFilter: "gradient" withValue: getWidgetBoolValue(sender)]; 1201 else 1202 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "gradient-cartoon" forFilter: "gradient" withValue: getWidgetBoolValue(sender)]; 1203} 1204 1205- (IBAction)enableExtract:(id)sender 1206{ 1207 BOOL b_state = [_extractCheckbox state]; 1208 [[VLCCoreInteraction sharedInstance] setVideoFilter: "extract" on: b_state]; 1209 [_extractTextField setEnabled: b_state]; 1210 [_extractLabel setEnabled: b_state]; 1211} 1212 1213- (IBAction)extractModifierChanged:(id)sender 1214{ 1215 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "extract-component" forFilter: "extract" withValue: getWidgetIntValue(sender)]; 1216} 1217 1218- (IBAction)enableInvert:(id)sender 1219{ 1220 [[VLCCoreInteraction sharedInstance] setVideoFilter: "invert" on: [_invertCheckbox state]]; 1221} 1222 1223- (IBAction)enablePosterize:(id)sender 1224{ 1225 BOOL b_state = [_posterizeCheckbox state]; 1226 1227 [[VLCCoreInteraction sharedInstance] setVideoFilter: "posterize" on: b_state]; 1228 [_posterizeTextField setEnabled: b_state]; 1229 [_posterizeStepper setEnabled: b_state]; 1230 [_posterizeLabel setEnabled: b_state]; 1231} 1232 1233- (IBAction)posterizeModifierChanged:(id)sender 1234{ 1235 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "posterize-level" forFilter: "posterize" withValue: getWidgetIntValue(sender)]; 1236} 1237 1238- (IBAction)enableBlur:(id)sender 1239{ 1240 BOOL b_state = [_blurCheckbox state]; 1241 1242 [[VLCCoreInteraction sharedInstance] setVideoFilter: "motionblur" on: b_state]; 1243 [_blurSlider setEnabled: b_state]; 1244 [_blurLabel setEnabled: b_state]; 1245} 1246 1247- (IBAction)blurModifierChanged:(id)sender 1248{ 1249 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "blur-factor" forFilter: "motionblur" withValue: getWidgetIntValue(sender)]; 1250 [sender setToolTip: [NSString stringWithFormat:@"%i", [sender intValue]]]; 1251} 1252 1253- (IBAction)enableMotionDetect:(id)sender 1254{ 1255 [[VLCCoreInteraction sharedInstance] setVideoFilter: "motiondetect" on: [_motiondetectCheckbox state]]; 1256} 1257 1258- (IBAction)enableWaterEffect:(id)sender 1259{ 1260 [[VLCCoreInteraction sharedInstance] setVideoFilter: "ripple" on: [_watereffectCheckbox state]]; 1261} 1262 1263- (IBAction)enableWaves:(id)sender 1264{ 1265 [[VLCCoreInteraction sharedInstance] setVideoFilter: "wave" on: [_wavesCheckbox state]]; 1266} 1267 1268- (IBAction)enablePsychedelic:(id)sender 1269{ 1270 [[VLCCoreInteraction sharedInstance] setVideoFilter: "psychedelic" on: [_psychedelicCheckbox state]]; 1271} 1272 1273#pragma mark - 1274#pragma mark Miscellaneous 1275- (IBAction)enableAddText:(id)sender 1276{ 1277 BOOL b_state = [_addTextCheckbox state]; 1278 VLCCoreInteraction *vci_si = [VLCCoreInteraction sharedInstance]; 1279 1280 [_addTextPositionPopup setEnabled: b_state]; 1281 [_addTextPositionLabel setEnabled: b_state]; 1282 [_addTextTextLabel setEnabled: b_state]; 1283 [_addTextTextTextField setEnabled: b_state]; 1284 [vci_si setVideoFilter: "marq" on: b_state]; 1285 [vci_si setVideoFilterProperty: "marq-marquee" forFilter: "marq" withValue: getWidgetStringValue(_addTextTextTextField)]; 1286 [vci_si setVideoFilterProperty: "marq-position" forFilter: "marq" withValue: (vlc_value_t){ .i_int = [[_addTextPositionPopup selectedItem] tag] }]; 1287} 1288 1289- (IBAction)addTextModifierChanged:(id)sender 1290{ 1291 if (sender == _addTextTextTextField) 1292 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "marq-marquee" forFilter: "marq" withValue: getWidgetStringValue(sender)]; 1293 else 1294 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "marq-position" forFilter: "marq" withValue: (vlc_value_t){ .i_int = [[sender selectedItem] tag] }]; 1295} 1296 1297- (IBAction)enableAddLogo:(id)sender 1298{ 1299 BOOL b_state = [_addLogoCheckbox state]; 1300 1301 [_addLogoPositionPopup setEnabled: b_state]; 1302 [_addLogoPositionLabel setEnabled: b_state]; 1303 [_addLogoLogoTextField setEnabled: b_state]; 1304 [_addLogoLogoLabel setEnabled: b_state]; 1305 [_addLogoTransparencySlider setEnabled: b_state]; 1306 [_addLogoTransparencyLabel setEnabled: b_state]; 1307 [[VLCCoreInteraction sharedInstance] setVideoFilter: "logo" on: b_state]; 1308} 1309 1310- (IBAction)addLogoModifierChanged:(id)sender 1311{ 1312 if (sender == _addLogoLogoTextField) 1313 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "logo-file" forFilter: "logo" withValue: getWidgetStringValue(sender)]; 1314 else if (sender == _addLogoPositionPopup) 1315 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "logo-position" forFilter: "logo" withValue: (vlc_value_t){ .i_int = [[_addLogoPositionPopup selectedItem] tag] }]; 1316 else { 1317 [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "logo-opacity" forFilter: "logo" withValue: getWidgetIntValue(sender)]; 1318 [_addLogoTransparencySlider setToolTip: [NSString stringWithFormat:@"%i", [_addLogoTransparencySlider intValue]]]; 1319 } 1320} 1321 1322- (IBAction)enableAnaglyph:(id)sender 1323{ 1324 [[VLCCoreInteraction sharedInstance] setVideoFilter: "anaglyph" on: [_anaglyphCheckbox state]]; 1325} 1326 1327@end 1328