VLCVideoEffectsWindowController.m 57.6 KB
Newer Older
1
/*****************************************************************************
2
 * VLCVideoEffectsWindowController.m: MacOS X interface module
3
 *****************************************************************************
4
 * Copyright (C) 2011-2015 Felix Paul Kühne
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * $Id$
 *
 * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

24
#import "CompatibilityFixes.h"
25
#import "VLCMain.h"
26
#import "VLCVideoEffectsWindowController.h"
27 28
#import "VLCPopupPanelController.h"
#import "VLCTextfieldPanelController.h"
29
#import "VLCCoreInteraction.h"
30
#import "VLCHexNumberFormatter.h"
31

32 33 34 35 36
#define getWidgetBoolValue(w)   ((vlc_value_t){ .b_bool = [w state] })
#define getWidgetIntValue(w)    ((vlc_value_t){ .i_int = [w intValue] })
#define getWidgetFloatValue(w)  ((vlc_value_t){ .f_float = [w floatValue] })
#define getWidgetStringValue(w) ((vlc_value_t){ .psz_string = (char *)[[w stringValue] UTF8String] })

37
@interface VLCVideoEffectsWindowController()
38 39 40
{
    NSInteger i_old_profile_index;
}
41 42
@end

43
#pragma mark -
44
#pragma mark Initialization
45

46
@implementation VLCVideoEffectsWindowController
47

48 49
+ (void)initialize
{
50 51
    NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:@";;;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"], @"VideoEffectProfiles",
                                 [NSArray arrayWithObject:_NS("Default")], @"VideoEffectProfileNames", nil];
52 53 54
    [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
}

55 56
- (id)init
{
57 58 59
    self = [super initWithWindowNibName:@"VideoEffects"];
    if (self) {
        i_old_profile_index = -1;
60 61 62

        self.popupPanel = [[VLCPopupPanelController alloc] init];
        self.textfieldPanel = [[VLCTextfieldPanelController alloc] init];
63
    }
64

65 66
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    if ([defaults boolForKey:@"VideoEffectApplyProfileOnStartup"]) {
67 68 69
        [self resetValues];
        [self loadProfile];
    }
70 71
    else
        [defaults setInteger:0 forKey:@"VideoEffectSelectedProfile"];
72

73
    return self;
74 75
}

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
- (void)loadProfile
{
    intf_thread_t *p_intf = getIntf();
    playlist_t *p_playlist = pl_Get(p_intf);
    VLCCoreInteraction *vci_si = [VLCCoreInteraction sharedInstance];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSUInteger profile = [defaults integerForKey:@"VideoEffectSelectedProfile"];

    /* fetch preset */
    NSArray *items = [[[defaults objectForKey:@"VideoEffectProfiles"] objectAtIndex:profile] componentsSeparatedByString:@";"];

    // version 1 of profile string has 32 entries
    if ([items count] < 32) {
        msg_Err(p_intf, "Error in parsing profile string");
        return;
    }

    /* filter handling */
    NSString *tempString = B64DecNSStr([items firstObject]);
    NSArray<NSValue *> *vouts = getVouts();

    /* enable the new filters */
    var_SetString(p_playlist, "video-filter", [tempString UTF8String]);
    if (vouts)
        for (NSValue *ptr in vouts) {
            vout_thread_t *p_vout = [ptr pointerValue];
            var_SetString(p_vout, "video-filter", [tempString UTF8String]);
        }

    tempString = B64DecNSStr([items objectAtIndex:1]);
    /* enable another round of new filters */
    var_SetString(p_playlist, "sub-source", [tempString UTF8String]);
    if (vouts)
        for (NSValue *ptr in vouts) {
            vout_thread_t *p_vout = [ptr pointerValue];
            var_SetString(p_vout, "sub-source", [tempString UTF8String]);
            vlc_object_release(p_vout);
        }

    tempString = B64DecNSStr([items objectAtIndex:2]);
    /* enable another round of new filters */
    char *psz_current_splitter = var_GetString(p_playlist, "video-splitter");
    bool b_filter_changed = ![tempString isEqualToString:toNSStr(psz_current_splitter)];
    free(psz_current_splitter);

    if (b_filter_changed)
        var_SetString(p_playlist, "video-splitter", [tempString UTF8String]);

    /* try to set filter values on-the-fly and store them appropriately */
    // index 3 is deprecated
    [vci_si setVideoFilterProperty: "contrast" forFilter: "adjust" withValue: getWidgetFloatValue([items objectAtIndex:4])];
    [vci_si setVideoFilterProperty: "brightness" forFilter: "adjust" withValue: getWidgetFloatValue([items objectAtIndex:5])];
    [vci_si setVideoFilterProperty: "saturation" forFilter: "adjust" withValue: getWidgetFloatValue([items objectAtIndex:6])];
    [vci_si setVideoFilterProperty: "gamma" forFilter: "adjust" withValue: getWidgetFloatValue([items objectAtIndex:7])];
    [vci_si setVideoFilterProperty: "sharpen-sigma" forFilter: "sharpen" withValue: getWidgetFloatValue([items objectAtIndex:8])];
    [vci_si setVideoFilterProperty: "gradfun-radius" forFilter: "gradfun" withValue: getWidgetIntValue([items objectAtIndex:9])];
    [vci_si setVideoFilterProperty: "grain-variance" forFilter: "grain" withValue: getWidgetFloatValue([items objectAtIndex:10])];
    [vci_si setVideoFilterProperty: "transform-type" forFilter: "transform" withValue: (vlc_value_t){ .psz_string = (char *)[B64DecNSStr([items objectAtIndex:11]) UTF8String] }];
    [vci_si setVideoFilterProperty: "puzzle-rows" forFilter: "puzzle" withValue: getWidgetIntValue([items objectAtIndex:12])];
    [vci_si setVideoFilterProperty: "puzzle-cols" forFilter: "puzzle" withValue: getWidgetIntValue([items objectAtIndex:13])];
    [vci_si setVideoFilterProperty: "colorthres-color" forFilter: "colorthres" withValue: getWidgetIntValue([items objectAtIndex:14])];
    [vci_si setVideoFilterProperty: "colorthres-saturationthres" forFilter: "colorthres" withValue: getWidgetIntValue([items objectAtIndex:15])];
    [vci_si setVideoFilterProperty: "colorthres-similaritythres" forFilter: "colorthres" withValue: getWidgetIntValue([items objectAtIndex:16])];
    [vci_si setVideoFilterProperty: "sepia-intensity" forFilter: "sepia" withValue: getWidgetIntValue([items objectAtIndex:17])];
    [vci_si setVideoFilterProperty: "gradient-mode" forFilter: "gradient" withValue: (vlc_value_t){ .psz_string = (char *)[B64DecNSStr([items objectAtIndex:18]) UTF8String] }];
    [vci_si setVideoFilterProperty: "gradient-cartoon" forFilter: "gradient" withValue: (vlc_value_t){ .b_bool = [[items objectAtIndex:19] intValue] }];
    [vci_si setVideoFilterProperty: "gradient-type" forFilter: "gradient" withValue: getWidgetIntValue([items objectAtIndex:20])];
    [vci_si setVideoFilterProperty: "extract-component" forFilter: "extract" withValue: getWidgetIntValue([items objectAtIndex:21])];
    [vci_si setVideoFilterProperty: "posterize-level" forFilter: "posterize" withValue: getWidgetIntValue([items objectAtIndex:22])];
    [vci_si setVideoFilterProperty: "blur-factor" forFilter: "motionblur" withValue: getWidgetIntValue([items objectAtIndex:23])];
    [vci_si setVideoFilterProperty: "marq-marquee" forFilter: "marq" withValue: (vlc_value_t){ .psz_string = (char *)[B64DecNSStr([items objectAtIndex:24]) UTF8String] }];
    [vci_si setVideoFilterProperty: "marq-position" forFilter: "marq" withValue: getWidgetIntValue([items objectAtIndex:25])];
    [vci_si setVideoFilterProperty: "logo-file" forFilter: "logo" withValue: (vlc_value_t){ .psz_string = (char *)[B64DecNSStr([items objectAtIndex:26]) UTF8String] }];
    [vci_si setVideoFilterProperty: "logo-position" forFilter: "logo" withValue: getWidgetIntValue([items objectAtIndex:27])];
    [vci_si setVideoFilterProperty: "logo-opacity" forFilter: "logo" withValue: getWidgetIntValue([items objectAtIndex:28])];
    [vci_si setVideoFilterProperty: "clone-count" forFilter: "clone" withValue: getWidgetIntValue([items objectAtIndex:29])];
    [vci_si setVideoFilterProperty: "wall-rows" forFilter: "wall" withValue: getWidgetIntValue([items objectAtIndex:30])];
    [vci_si setVideoFilterProperty: "wall-cols" forFilter: "wall" withValue: getWidgetIntValue([items objectAtIndex:31])];

    if ([items count] >= 33) { // version >=2 of profile string
        [vci_si setVideoFilterProperty: "brightness-threshold" forFilter: "adjust" withValue: (vlc_value_t){ .b_bool = [[items objectAtIndex:32] intValue] }];
    }

    vlc_value_t hueValue;
    if ([items count] >= 34) { // version >=3 of profile string
        hueValue.f_float = [[items objectAtIndex:33] floatValue];
    } else {
        hueValue.f_float = [[items objectAtIndex:3] intValue]; // deprecated since 3.0.0
        // convert to new scale ([0,360] --> [-180,180])
        hueValue.f_float -= 180;
    }
    [vci_si setVideoFilterProperty: "hue" forFilter: "adjust" withValue: hueValue];

    [defaults setInteger:profile forKey:@"VideoEffectSelectedProfile"];
    [defaults synchronize];
}

173
- (void)windowDidLoad
174
{
175 176 177
    [self.window setTitle: _NS("Video Effects")];
    [self.window setExcludedFromWindowsMenu:YES];
    [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
178

179 180 181 182 183
    [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"basic"]] setLabel:_NS("Basic")];
    [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"crop"]] setLabel:_NS("Crop")];
    [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"geometry"]] setLabel:_NS("Geometry")];
    [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"color"]] setLabel:_NS("Color")];
    [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"misc"]] setLabel:_NS("Miscellaneous")];
184

185 186
    [_applyProfileCheckbox setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"VideoEffectApplyProfileOnStartup"]];

187 188
    [self resetProfileSelector];

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
    [_adjustCheckbox setTitle:_NS("Image Adjust")];
    [_adjustHueLabel setStringValue:_NS("Hue")];
    [_adjustContrastLabel setStringValue:_NS("Contrast")];
    [_adjustBrightnessLabel setStringValue:_NS("Brightness")];
    [_adjustBrightnessCheckbox setTitle:_NS("Brightness Threshold")];
    [_adjustSaturationLabel setStringValue:_NS("Saturation")];
    [_adjustGammaLabel setStringValue:_NS("Gamma")];
    [_adjustResetButton setTitle: _NS("Reset")];
    [_sharpenCheckbox setTitle:_NS("Sharpen")];
    [_sharpenLabel setStringValue:_NS("Sigma")];
    [_bandingCheckbox setTitle:_NS("Banding removal")];
    [_bandingLabel setStringValue:_NS("Radius")];
    [_grainCheckbox setTitle:_NS("Film Grain")];
    [_grainLabel setStringValue:_NS("Variance")];
    [_cropTopLabel setStringValue:_NS("Top")];
    [_cropLeftLabel setStringValue:_NS("Left")];
    [_cropRightLabel setStringValue:_NS("Right")];
    [_cropBottomLabel setStringValue:_NS("Bottom")];
    [_cropSyncTopBottomCheckbox setTitle:_NS("Synchronize top and bottom")];
    [_cropSyncLeftRightCheckbox setTitle:_NS("Synchronize left and right")];

    [_transformCheckbox setTitle:_NS("Transform")];
    [_transformPopup removeAllItems];
    [_transformPopup addItemWithTitle: _NS("Rotate by 90 degrees")];
213
    [[_transformPopup lastItem] setRepresentedObject: @"90"];
214 215
    [[_transformPopup lastItem] setTag: 90];
    [_transformPopup addItemWithTitle: _NS("Rotate by 180 degrees")];
216
    [[_transformPopup lastItem] setRepresentedObject: @"180"];
217 218
    [[_transformPopup lastItem] setTag: 180];
    [_transformPopup addItemWithTitle: _NS("Rotate by 270 degrees")];
219
    [[_transformPopup lastItem] setRepresentedObject: @"270"];
220 221
    [[_transformPopup lastItem] setTag: 270];
    [_transformPopup addItemWithTitle: _NS("Flip horizontally")];
222
    [[_transformPopup lastItem] setRepresentedObject: @"hflip"];
223 224
    [[_transformPopup lastItem] setTag: 1];
    [_transformPopup addItemWithTitle: _NS("Flip vertically")];
225
    [[_transformPopup lastItem] setRepresentedObject: @"vflip"];
226 227 228 229 230 231 232 233 234 235 236 237 238
    [[_transformPopup lastItem] setTag: 2];
    [_zoomCheckbox setTitle:_NS("Magnification/Zoom")];
    [_puzzleCheckbox setTitle:_NS("Puzzle game")];
    [_puzzleRowsLabel setStringValue:_NS("Rows")];
    [_puzzleColumnsLabel setStringValue:_NS("Columns")];
    [_cloneCheckbox setTitle:_NS("Clone")];
    [_cloneNumberLabel setStringValue:_NS("Number of clones")];
    [_wallCheckbox setTitle:_NS("Wall")];
    [_wallNumbersOfRowsLabel setStringValue:_NS("Rows")];
    [_wallNumberOfColumnsLabel setStringValue:_NS("Columns")];

    [_thresholdCheckbox setTitle:_NS("Color threshold")];
    [_thresholdColorLabel setStringValue:_NS("Color")];
239
    [_thresholdColorTextField setFormatter:[[VLCHexNumberFormatter alloc] init]];
240 241 242 243 244 245 246 247 248
    [_thresholdSaturationLabel setStringValue:_NS("Saturation")];
    [_thresholdSimilarityLabel setStringValue:_NS("Similarity")];
    [_sepiaCheckbox setTitle:_NS("Sepia")];
    [_sepiaLabel setStringValue:_NS("Intensity")];
    [_noiseCheckbox setTitle:_NS("Noise")];
    [_gradientCheckbox setTitle:_NS("Gradient")];
    [_gradientModeLabel setStringValue:_NS("Mode")];
    [_gradientModePopup removeAllItems];
    [_gradientModePopup addItemWithTitle: _NS("Gradient")];
249
    [[_gradientModePopup lastItem] setRepresentedObject: @"gradient"];
250 251
    [[_gradientModePopup lastItem] setTag: 1];
    [_gradientModePopup addItemWithTitle: _NS("Edge")];
252
    [[_gradientModePopup lastItem] setRepresentedObject: @"edge"];
253 254
    [[_gradientModePopup lastItem] setTag: 2];
    [_gradientModePopup addItemWithTitle: _NS("Hough")];
255
    [[_gradientModePopup lastItem] setRepresentedObject: @"hough"];
256 257 258 259 260
    [[_gradientModePopup lastItem] setTag: 3];
    [_gradientColorCheckbox setTitle:_NS("Color")];
    [_gradientCartoonCheckbox setTitle:_NS("Cartoon")];
    [_extractCheckbox setTitle:_NS("Color extraction")];
    [_extractLabel setStringValue:_NS("Color")];
261
    [_extractTextField setFormatter:[[VLCHexNumberFormatter alloc] init]];
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
    [_invertCheckbox setTitle:_NS("Invert colors")];
    [_posterizeCheckbox setTitle:_NS("Posterize")];
    [_posterizeLabel setStringValue:_NS("Posterize level")];
    [_blurCheckbox setTitle:_NS("Motion blur")];
    [_blurLabel setStringValue:_NS("Factor")];
    [_motiondetectCheckbox setTitle:_NS("Motion Detect")];
    [_watereffectCheckbox setTitle:_NS("Water effect")];
    [_wavesCheckbox setTitle:_NS("Waves")];
    [_psychedelicCheckbox setTitle:_NS("Psychedelic")];
    [_anaglyphCheckbox setTitle:_NS("Anaglyph")];
    [_addTextCheckbox setTitle:_NS("Add text")];
    [_addTextTextLabel setStringValue:_NS("Text")];
    [_addTextPositionLabel setStringValue:_NS("Position")];
    [_addTextPositionPopup removeAllItems];
    [_addTextPositionPopup addItemWithTitle: _NS("Center")];
    [[_addTextPositionPopup lastItem] setTag: 0];
    [_addTextPositionPopup addItemWithTitle: _NS("Left")];
    [[_addTextPositionPopup lastItem] setTag: 1];
    [_addTextPositionPopup addItemWithTitle: _NS("Right")];
    [[_addTextPositionPopup lastItem] setTag: 2];
    [_addTextPositionPopup addItemWithTitle: _NS("Top")];
    [[_addTextPositionPopup lastItem] setTag: 4];
    [_addTextPositionPopup addItemWithTitle: _NS("Bottom")];
    [[_addTextPositionPopup lastItem] setTag: 8];
    [_addTextPositionPopup addItemWithTitle: _NS("Top-Left")];
    [[_addTextPositionPopup lastItem] setTag: 5];
    [_addTextPositionPopup addItemWithTitle: _NS("Top-Right")];
    [[_addTextPositionPopup lastItem] setTag: 6];
    [_addTextPositionPopup addItemWithTitle: _NS("Bottom-Left")];
    [[_addTextPositionPopup lastItem] setTag: 9];
    [_addTextPositionPopup addItemWithTitle: _NS("Bottom-Right")];
    [[_addTextPositionPopup lastItem] setTag: 10];
    [_addLogoCheckbox setTitle:_NS("Add logo")];
    [_addLogoLogoLabel setStringValue:_NS("Logo")];
    [_addLogoPositionLabel setStringValue:_NS("Position")];
    [_addLogoPositionPopup removeAllItems];
    [_addLogoPositionPopup addItemWithTitle: _NS("Center")];
    [[_addLogoPositionPopup lastItem] setTag: 0];
    [_addLogoPositionPopup addItemWithTitle: _NS("Left")];
    [[_addLogoPositionPopup lastItem] setTag: 1];
    [_addLogoPositionPopup addItemWithTitle: _NS("Right")];
    [[_addLogoPositionPopup lastItem] setTag: 2];
    [_addLogoPositionPopup addItemWithTitle: _NS("Top")];
    [[_addLogoPositionPopup lastItem] setTag: 4];
    [_addLogoPositionPopup addItemWithTitle: _NS("Bottom")];
    [[_addLogoPositionPopup lastItem] setTag: 8];
    [_addLogoPositionPopup addItemWithTitle: _NS("Top-Left")];
    [[_addLogoPositionPopup lastItem] setTag: 5];
    [_addLogoPositionPopup addItemWithTitle: _NS("Top-Right")];
    [[_addLogoPositionPopup lastItem] setTag: 6];
    [_addLogoPositionPopup addItemWithTitle: _NS("Bottom-Left")];
    [[_addLogoPositionPopup lastItem] setTag: 9];
    [_addLogoPositionPopup addItemWithTitle: _NS("Bottom-Right")];
    [[_addLogoPositionPopup lastItem] setTag: 10];
    [_addLogoTransparencyLabel setStringValue:_NS("Transparency")];

    [_tabView selectFirstTabViewItem:self];
319

320 321 322 323 324
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(inputChangedEvent:)
                                                 name:VLCInputChangedNotification
                                               object:nil];

325 326 327
    [self resetValues];
}

328 329 330 331 332
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

333 334
- (void)updateCocoaWindowLevel:(NSInteger)i_level
{
335
    if (self.isWindowLoaded && [self.window isVisible] && [self.window level] != i_level)
336
        [self.window setLevel: i_level];
337 338
}

339 340
#pragma mark -
#pragma mark internal functions
341 342 343 344 345 346 347 348 349 350

-(void)inputChangedEvent:(NSNotification *)o_notification
{
    // reset crop values when input changed
    [self setCropBottomValue:0];
    [self setCropTopValue:0];
    [self setCropLeftValue:0];
    [self setCropRightValue:0];
}

351 352 353
- (void)resetProfileSelector
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
354
    [_profilePopup removeAllItems];
355

356 357
    NSArray *profileNames = [defaults objectForKey:@"VideoEffectProfileNames"];
    [_profilePopup addItemsWithTitles:profileNames];
358

359 360 361 362
    [[_profilePopup menu] addItem:[NSMenuItem separatorItem]];
    [_profilePopup addItemWithTitle:_NS("Duplicate current profile...")];
    [[_profilePopup lastItem] setTarget: self];
    [[_profilePopup lastItem] setAction: @selector(addProfile:)];
363 364

    if ([profileNames count] > 1) {
365 366 367
        [_profilePopup addItemWithTitle:_NS("Organize profiles...")];
        [[_profilePopup lastItem] setTarget: self];
        [[_profilePopup lastItem] setAction: @selector(removeProfile:)];
368 369
    }

370
    [_profilePopup selectItemAtIndex:[defaults integerForKey:@"VideoEffectSelectedProfile"]];
371 372
    if (i_old_profile_index || [defaults integerForKey:@"VideoEffectSelectedProfile"])
        [self profileSelectorAction:self];
373 374
}

375 376 377
- (void)setWidgetValue: (id)widget forOption: (char *)psz_option enabled: (bool)b_state
{
    intf_thread_t *p_intf = getIntf();
378
    playlist_t *p_playlist = pl_Get(p_intf);
379 380

    vlc_value_t val;
381
    int i_type = config_GetType(psz_option) & VLC_VAR_CLASS;
382 383 384 385 386 387 388 389 390 391 392
    switch (i_type)
    {
    case VLC_VAR_BOOL:
    case VLC_VAR_INTEGER:
    case VLC_VAR_FLOAT:
    case VLC_VAR_STRING:
        break;
    default:
        msg_Err(p_intf, "%s variable is of an unsupported type (%d)", psz_option, i_type);
        return;
    }
393 394 395
    if (var_Create(p_playlist, psz_option, i_type | VLC_VAR_DOINHERIT) ||
        var_GetChecked(p_playlist, psz_option, i_type, &val))
        return;
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440

    if (i_type == VLC_VAR_BOOL || i_type == VLC_VAR_INTEGER)
    {
        if ([widget isKindOfClass: [NSSlider class]])
        {
            [widget setIntValue: val.i_int];
            [widget setToolTip: [NSString stringWithFormat:@"%lli", val.i_int]];
        }
        else if ([widget isKindOfClass: [NSButton class]])
            [widget setState: val.i_int ? NSOnState : NSOffState];
        else if ([widget isKindOfClass: [NSTextField class]])
            [widget setIntValue: val.i_int];
        else if ([widget isKindOfClass: [NSStepper class]])
            [widget setIntValue: val.i_int];
        else if ([widget isKindOfClass: [NSPopUpButton class]])
            [widget selectItemWithTag: val.i_int];
    }
    else if (i_type == VLC_VAR_FLOAT)
    {
        if ([widget isKindOfClass: [NSSlider class]])
        {
            [widget setFloatValue: val.f_float];
            [widget setToolTip: [NSString stringWithFormat:@"%0.3f", val.f_float]];
        }
    }
    else if (i_type == VLC_VAR_STRING)
    {
        if ([widget isKindOfClass: [NSPopUpButton class]])
        {
            for (NSMenuItem *item in [widget itemArray])
                if ([item representedObject] &&
                    !strcmp([[item representedObject] UTF8String], val.psz_string))
                {
                    [widget selectItemWithTitle: [item title]];
                    break;
                }
        }
        else if ([widget isKindOfClass: [NSTextField class]])
            [widget setStringValue: toNSStr(val.psz_string)];
        free(val.psz_string);
    }

    [widget setEnabled: b_state];
}

441 442
- (void)resetValues
{
443
    intf_thread_t *p_intf = getIntf();
444
    playlist_t *p_playlist = pl_Get(p_intf);
445
    NSString *tmpString;
446
    char *tmpChar;
447 448
    BOOL b_state;

449 450
    /* do we have any filter enabled? if yes, show it. */
    char * psz_vfilters;
451
    psz_vfilters = var_InheritString(p_playlist, "video-filter");
452
    if (psz_vfilters) {
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
        [_adjustCheckbox setState: (NSInteger)strstr(psz_vfilters, "adjust")];
        [_sharpenCheckbox setState: (NSInteger)strstr(psz_vfilters, "sharpen")];
        [_bandingCheckbox setState: (NSInteger)strstr(psz_vfilters, "gradfun")];
        [_grainCheckbox setState: (NSInteger)strstr(psz_vfilters, "grain")];
        [_transformCheckbox setState: (NSInteger)strstr(psz_vfilters, "transform")];
        [_zoomCheckbox setState: (NSInteger)strstr(psz_vfilters, "magnify")];
        [_puzzleCheckbox setState: (NSInteger)strstr(psz_vfilters, "puzzle")];
        [_thresholdCheckbox setState: (NSInteger)strstr(psz_vfilters, "colorthres")];
        [_sepiaCheckbox setState: (NSInteger)strstr(psz_vfilters, "sepia")];
        [_noiseCheckbox setState: (NSInteger)strstr(psz_vfilters, "noise")];
        [_gradientCheckbox setState: (NSInteger)strstr(psz_vfilters, "gradient")];
        [_extractCheckbox setState: (NSInteger)strstr(psz_vfilters, "extract")];
        [_invertCheckbox setState: (NSInteger)strstr(psz_vfilters, "invert")];
        [_posterizeCheckbox setState: (NSInteger)strstr(psz_vfilters, "posterize")];
        [_blurCheckbox setState: (NSInteger)strstr(psz_vfilters, "motionblur")];
        [_motiondetectCheckbox setState: (NSInteger)strstr(psz_vfilters, "motiondetect")];
        [_watereffectCheckbox setState: (NSInteger)strstr(psz_vfilters, "ripple")];
        [_wavesCheckbox setState: (NSInteger)strstr(psz_vfilters, "wave")];
        [_psychedelicCheckbox setState: (NSInteger)strstr(psz_vfilters, "psychedelic")];
        [_anaglyphCheckbox setState: (NSInteger)strstr(psz_vfilters, "anaglyph")];
473
        free(psz_vfilters);
474
    } else {
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
        [_adjustCheckbox setState: NSOffState];
        [_sharpenCheckbox setState: NSOffState];
        [_bandingCheckbox setState: NSOffState];
        [_grainCheckbox setState: NSOffState];
        [_transformCheckbox setState: NSOffState];
        [_zoomCheckbox setState: NSOffState];
        [_puzzleCheckbox setState: NSOffState];
        [_thresholdCheckbox setState: NSOffState];
        [_sepiaCheckbox setState: NSOffState];
        [_noiseCheckbox setState: NSOffState];
        [_gradientCheckbox setState: NSOffState];
        [_extractCheckbox setState: NSOffState];
        [_invertCheckbox setState: NSOffState];
        [_posterizeCheckbox setState: NSOffState];
        [_blurCheckbox setState: NSOffState];
        [_motiondetectCheckbox setState: NSOffState];
        [_watereffectCheckbox setState: NSOffState];
        [_wavesCheckbox setState: NSOffState];
        [_psychedelicCheckbox setState: NSOffState];
        [_anaglyphCheckbox setState: NSOffState];
495
    }
496

497
    psz_vfilters = var_InheritString(p_playlist, "sub-source");
498
    if (psz_vfilters) {
499 500
        [_addTextCheckbox setState: (NSInteger)strstr(psz_vfilters, "marq")];
        [_addLogoCheckbox setState: (NSInteger)strstr(psz_vfilters, "logo")];
501
        free(psz_vfilters);
502
    } else {
503 504
        [_addTextCheckbox setState: NSOffState];
        [_addLogoCheckbox setState: NSOffState];
505
    }
506

507
    psz_vfilters = var_InheritString(p_playlist, "video-splitter");
508
    if (psz_vfilters) {
509 510
        [_cloneCheckbox setState: (NSInteger)strstr(psz_vfilters, "clone")];
        [_wallCheckbox setState: (NSInteger)strstr(psz_vfilters, "wall")];
511 512
        free(psz_vfilters);
    } else {
513 514
        [_cloneCheckbox setState: NSOffState];
        [_wallCheckbox setState: NSOffState];
515 516
    }

517
    /* fetch and show the various values */
518
    b_state = [_adjustCheckbox state];
519 520 521 522 523 524
    [self setWidgetValue: _adjustHueSlider forOption: "hue" enabled: b_state];
    [self setWidgetValue: _adjustContrastSlider forOption: "contrast" enabled: b_state];
    [self setWidgetValue: _adjustBrightnessSlider forOption: "brightness" enabled: b_state];
    [self setWidgetValue: _adjustSaturationSlider forOption: "saturation" enabled: b_state];
    [self setWidgetValue: _adjustBrightnessCheckbox forOption: "brightness-threshold" enabled: b_state];
    [self setWidgetValue: _adjustGammaSlider forOption: "gamma" enabled: b_state];
525 526 527 528 529 530 531
    [_adjustBrightnessLabel setEnabled: b_state];
    [_adjustContrastLabel setEnabled: b_state];
    [_adjustGammaLabel setEnabled: b_state];
    [_adjustHueLabel setEnabled: b_state];
    [_adjustSaturationLabel setEnabled: b_state];
    [_adjustResetButton setEnabled: b_state];

532
    [self setWidgetValue: _sharpenSlider forOption: "sharpen-sigma" enabled: [_sharpenCheckbox state]];
533 534
    [_sharpenLabel setEnabled: [_sharpenCheckbox state]];

535
    [self setWidgetValue: _bandingSlider forOption: "gradfun-radius" enabled: [_bandingCheckbox state]];
536 537
    [_bandingLabel setEnabled: [_bandingCheckbox state]];

538
    [self setWidgetValue: _grainSlider forOption: "grain-variance" enabled: [_grainCheckbox state]];
539
    [_grainLabel setEnabled: [_grainCheckbox state]];
540

541 542 543 544
    [self setCropLeftValue: 0];
    [self setCropTopValue: 0];
    [self setCropRightValue: 0];
    [self setCropBottomValue: 0];
545 546
    [_cropSyncTopBottomCheckbox setState: NSOffState];
    [_cropSyncLeftRightCheckbox setState: NSOffState];
547

548 549
    [self setWidgetValue: _transformPopup forOption: "transform-type" enabled: [_transformCheckbox state]];

550
    b_state = [_puzzleCheckbox state];
551 552 553 554
    [self setWidgetValue: _puzzleColumnsTextField forOption: "puzzle-cols" enabled: b_state];
    [self setWidgetValue: _puzzleColumnsStepper forOption: "puzzle-cols" enabled: b_state];
    [self setWidgetValue: _puzzleRowsTextField forOption: "puzzle-rows" enabled: b_state];
    [self setWidgetValue: _puzzleRowsStepper forOption: "puzzle-rows" enabled: b_state];
555 556 557 558
    [_puzzleRowsLabel setEnabled: b_state];
    [_puzzleColumnsLabel setEnabled: b_state];

    b_state = [_cloneCheckbox state];
559 560
    [self setWidgetValue: _cloneNumberTextField forOption: "clone-count" enabled: b_state];
    [self setWidgetValue: _cloneNumberStepper forOption: "clone-count" enabled: b_state];
561
    [_cloneNumberLabel setEnabled: b_state];
562

563
    b_state = [_wallCheckbox state];
564 565 566 567
    [self setWidgetValue: _wallNumbersOfRowsTextField forOption: "wall-rows" enabled: b_state];
    [self setWidgetValue: _wallNumbersOfRowsStepper forOption: "wall-rows" enabled: b_state];
    [self setWidgetValue: _wallNumberOfColumnsTextField forOption: "wall-cols" enabled: b_state];
    [self setWidgetValue: _wallNumberOfColumnsStepper forOption: "wall-cols" enabled: b_state];
568 569 570 571
    [_wallNumbersOfRowsLabel setEnabled: b_state];
    [_wallNumberOfColumnsLabel setEnabled: b_state];

    b_state = [_thresholdCheckbox state];
572 573 574
    [self setWidgetValue: _thresholdColorTextField forOption: "colorthres-color" enabled: b_state];
    [self setWidgetValue: _thresholdSaturationSlider forOption: "colorthres-saturationthres" enabled: b_state];
    [self setWidgetValue: _thresholdSimilaritySlider forOption: "colorthres-similaritythres" enabled: b_state];
575 576 577
    [_thresholdColorLabel setEnabled: b_state];
    [_thresholdSaturationLabel setEnabled: b_state];
    [_thresholdSimilarityLabel setEnabled: b_state];
578

579
    b_state = [_sepiaCheckbox state];
580 581
    [self setWidgetValue: _sepiaTextField forOption: "sepia-intensity" enabled: b_state];
    [self setWidgetValue: _sepiaStepper forOption: "sepia-intensity" enabled: b_state];
582
    [_sepiaLabel setEnabled: b_state];
583

584
    b_state = [_gradientCheckbox state];
585 586 587
    [self setWidgetValue: _gradientModePopup forOption: "gradient-mode" enabled: b_state];
    [self setWidgetValue: _gradientCartoonCheckbox forOption: "gradient-cartoon" enabled: b_state];
    [self setWidgetValue: _gradientColorCheckbox forOption: "gradient-type" enabled: b_state];
588
    [_gradientModeLabel setEnabled: b_state];
589

590
    [self setWidgetValue: _extractTextField forOption: "extract-component" enabled: [_extractCheckbox state]];
591
    [_extractLabel setEnabled: [_extractCheckbox state]];
592

593
    b_state = [_posterizeCheckbox state];
594 595
    [self setWidgetValue: _posterizeTextField forOption: "posterize-level" enabled: b_state];
    [self setWidgetValue: _posterizeStepper forOption: "posterize-level" enabled: b_state];
596
    [_posterizeLabel setEnabled: b_state];
597

598
    [self setWidgetValue: _blurSlider forOption: "blur-factor" enabled: [_blurCheckbox state]];
599
    [_blurLabel setEnabled: [_blurCheckbox state]];
600

601
    b_state = [_addTextCheckbox state];
602 603
    [self setWidgetValue: _addTextTextTextField forOption: "marq-marquee" enabled: b_state];
    [self setWidgetValue: _addTextPositionPopup forOption: "marq-position" enabled: b_state];
604 605
    [_addTextPositionLabel setEnabled: b_state];
    [_addTextTextLabel setEnabled: b_state];
606

607
    b_state = [_addLogoCheckbox state];
608 609 610
    [self setWidgetValue: _addLogoLogoTextField forOption: "logo-file" enabled: b_state];
    [self setWidgetValue: _addLogoPositionPopup forOption: "logo-position" enabled: b_state];
    [self setWidgetValue: _addLogoTransparencySlider forOption: "logo-opacity" enabled: b_state];
611 612 613
    [_addLogoPositionLabel setEnabled: b_state];
    [_addLogoLogoLabel setEnabled: b_state];
    [_addLogoTransparencyLabel setEnabled: b_state];
614 615
}

616 617
- (NSString *)generateProfileString
{
618
    intf_thread_t *p_intf = getIntf();
619
    playlist_t *p_playlist = pl_Get(p_intf);
620
    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",
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
                     B64EncAndFree(var_InheritString(p_playlist, "video-filter")),
                     B64EncAndFree(var_InheritString(p_playlist, "sub-source")),
                     B64EncAndFree(var_InheritString(p_playlist, "video-splitter")),
                     0LL, // former "hue" value, deprecated since 3.0.0
                     var_InheritFloat(p_playlist, "contrast"),
                     var_InheritFloat(p_playlist, "brightness"),
                     var_InheritFloat(p_playlist, "saturation"),
                     var_InheritFloat(p_playlist, "gamma"),
                     var_InheritFloat(p_playlist, "sharpen-sigma"),
                     var_InheritInteger(p_playlist, "gradfun-radius"),
                     var_InheritFloat(p_playlist, "grain-variance"),
                     B64EncAndFree(var_InheritString(p_playlist, "transform-type")),
                     var_InheritInteger(p_playlist, "puzzle-rows"),
                     var_InheritInteger(p_playlist, "puzzle-cols"),
                     var_InheritInteger(p_playlist, "colorthres-color"),
                     var_InheritInteger(p_playlist, "colorthres-saturationthres"),
                     var_InheritInteger(p_playlist, "colorthres-similaritythres"),
                     var_InheritInteger(p_playlist, "sepia-intensity"),
                     B64EncAndFree(var_InheritString(p_playlist, "gradient-mode")),
                     (int64_t)var_InheritBool(p_playlist, "gradient-cartoon"),
                     var_InheritInteger(p_playlist, "gradient-type"),
                     var_InheritInteger(p_playlist, "extract-component"),
                     var_InheritInteger(p_playlist, "posterize-level"),
                     var_InheritInteger(p_playlist, "blur-factor"),
                     B64EncAndFree(var_InheritString(p_playlist, "marq-marquee")),
                     var_InheritInteger(p_playlist, "marq-position"),
                     B64EncAndFree(var_InheritString(p_playlist, "logo-file")),
                     var_InheritInteger(p_playlist, "logo-position"),
                     var_InheritInteger(p_playlist, "logo-opacity"),
                     var_InheritInteger(p_playlist, "clone-count"),
                     var_InheritInteger(p_playlist, "wall-rows"),
                     var_InheritInteger(p_playlist, "wall-cols"),
                     // version 2 of profile string:
                     (int64_t)var_InheritBool(p_playlist, "brightness-threshold"), // index: 32
                     // version 3 of profile string: (vlc-3.0.0)
                     var_InheritFloat(p_playlist, "hue") // index: 33
657 658 659 660 661
            ];
}

#pragma mark -
#pragma mark generic UI code
662 663 664

- (void)saveCurrentProfile
{
665
    if (!i_old_profile_index || i_old_profile_index == -1)
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
        return;

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    /* fetch all the current settings in a uniform string */
    NSString *newProfile = [self generateProfileString];

    NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"VideoEffectProfiles"]];
    if (i_old_profile_index >= [workArray count])
        return;

    [workArray replaceObjectAtIndex:i_old_profile_index withObject:newProfile];
    [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfiles"];
    [defaults synchronize];
}

681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
- (void)saveCurrentProfileAtTerminate
{
    if (i_old_profile_index)
        return [self saveCurrentProfile];

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *newProfile = [self generateProfileString];
    if ([newProfile compare:[[defaults objectForKey:@"VideoEffectProfiles"] firstObject]] == NSOrderedSame)
        return;

    NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"VideoEffectProfiles"]];
    [workArray addObject:newProfile];
    [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfiles"];

    NSArray<NSString *> *profileNames = [defaults objectForKey:@"VideoEffectProfileNames"];
    NSString *newProfileName;

    unsigned int num_custom = 0;
    do
        newProfileName = [@"Custom" stringByAppendingString:[NSString stringWithFormat:@"%03i",num_custom++]];
    while ([profileNames containsObject:newProfileName]);

    workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"VideoEffectProfileNames"]];
    [workArray addObject:newProfileName];
    [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfileNames"];

    [defaults synchronize];
}

710 711
- (IBAction)toggleWindow:(id)sender
{
712 713
    if ([self.window isKeyWindow])
        [self.window orderOut:sender];
714
    else {
715 716
        [self.window setLevel: [[[VLCMain sharedInstance] voutController] currentStatusWindowLevel]];
        [self.window makeKeyAndOrderFront:sender];
717
    }
718 719 720 721
}

- (IBAction)profileSelectorAction:(id)sender
{
722
    [self saveCurrentProfile];
723
    i_old_profile_index = [_profilePopup indexOfSelectedItem];
724 725
    [[NSUserDefaults standardUserDefaults] setInteger:i_old_profile_index forKey:@"VideoEffectSelectedProfile"];
    [self loadProfile];
726
    [self resetValues];
727 728
}

729
- (void)addProfile:(id)sender
730 731
{
    /* show panel */
732 733 734 735 736 737 738 739
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
#ifdef MAC_OS_X_VERSION_10_10
    if (OSX_YOSEMITE_AND_HIGHER) {
        [[_textfieldPanel window] setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
    }
#endif
#pragma clang diagnostic pop
740 741 742 743
    [_textfieldPanel setTitleString:_NS("Duplicate current profile for a new profile")];
    [_textfieldPanel setSubTitleString:_NS("Enter a name for the new profile:")];
    [_textfieldPanel setCancelButtonString:_NS("Cancel")];
    [_textfieldPanel setOkButtonString:_NS("Save")];
744

745 746
    // TODO: Change to weak, when dropping 10.7 support
    __unsafe_unretained typeof(self) _self = self;
747
    [_textfieldPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSString *resultingText) {
748

749
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
750

751 752 753 754
        if (returnCode != NSOKButton) {
            [_profilePopup selectItemAtIndex:[defaults integerForKey:@"VideoEffectSelectedProfile"]];
            return;
        }
755

756
        NSArray *profileNames = [defaults objectForKey:@"VideoEffectProfileNames"];
757

758 759 760
        // duplicate names are not allowed in the popup control
        if ([resultingText length] == 0 || [profileNames containsObject:resultingText]) {
            [_profilePopup selectItemAtIndex:[defaults integerForKey:@"VideoEffectSelectedProfile"]];
761

762 763 764 765
            NSAlert *alert = [[NSAlert alloc] init];
            [alert setAlertStyle:NSCriticalAlertStyle];
            [alert setMessageText:_NS("Please enter a unique name for the new profile.")];
            [alert setInformativeText:_NS("Multiple profiles with the same name are not allowed.")];
766

767 768 769 770 771 772
            [alert beginSheetModalForWindow:_self.window
                              modalDelegate:nil
                             didEndSelector:nil
                                contextInfo:nil];
            return;
        }
773

774 775
        /* fetch all the current settings in a uniform string */
        NSString *newProfile = [_self generateProfileString];
776

777
        /* add string to user defaults as well as a label */
778

779 780 781 782
        NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"VideoEffectProfiles"]];
        [workArray addObject:newProfile];
        [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfiles"];
        [defaults setInteger:[workArray count] - 1 forKey:@"VideoEffectSelectedProfile"];
783

784 785 786
        workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"VideoEffectProfileNames"]];
        [workArray addObject:resultingText];
        [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfileNames"];
787

788 789
        /* save defaults */
        [defaults synchronize];
790

791 792 793
        /* refresh UI */
        [_self resetProfileSelector];
    }];
794 795
}

796
- (void)removeProfile:(id)sender
797 798
{
    /* show panel */
799 800 801 802 803 804 805 806
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpartial-availability"
#ifdef MAC_OS_X_VERSION_10_10
    if (OSX_YOSEMITE_AND_HIGHER) {
        [[_popupPanel window] setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
    }
#endif
#pragma clang diagnostic pop
807 808 809 810
    [_popupPanel setTitleString:_NS("Remove a preset")];
    [_popupPanel setSubTitleString:_NS("Select the preset you would like to remove:")];
    [_popupPanel setOkButtonString:_NS("Remove")];
    [_popupPanel setCancelButtonString:_NS("Cancel")];
811 812
    [_popupPanel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"VideoEffectProfileNames"]];

813 814
    // TODO: Change to weak, when dropping 10.7 support
    __unsafe_unretained typeof(self) _self = self;
815
    [_popupPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSInteger selectedIndex) {
816

817
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
818

819 820 821 822
        if (returnCode != NSOKButton) {
            [_profilePopup selectItemAtIndex:[defaults integerForKey:@"VideoEffectSelectedProfile"]];
            return;
        }
823

824 825 826 827 828
        if (!selectedIndex) { // TODO: add popup to notify user
            [_profilePopup selectItemAtIndex:[defaults integerForKey:@"VideoEffectSelectedProfile"]];
            return;
        }

829 830 831 832
        /* remove selected profile from settings */
        NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray: [defaults objectForKey:@"VideoEffectProfiles"]];
        [workArray removeObjectAtIndex:selectedIndex];
        [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfiles"];
833

834 835 836
        workArray = [[NSMutableArray alloc] initWithArray: [defaults objectForKey:@"VideoEffectProfileNames"]];
        [workArray removeObjectAtIndex:selectedIndex];
        [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"VideoEffectProfileNames"];
837

838 839
        if (i_old_profile_index >= selectedIndex)
            [defaults setInteger:i_old_profile_index - 1 forKey:@"VideoEffectSelectedProfile"];
840

841 842
        /* save defaults */
        [defaults synchronize];
843

844 845 846 847 848
        /* do not save deleted profile */
        i_old_profile_index = -1;
        /* refresh UI */
        [_self resetProfileSelector];
    }];
849 850
}

851 852 853 854 855
- (IBAction)applyProfileCheckboxChanged:(id)sender
{
    [[NSUserDefaults standardUserDefaults] setBool:[sender state] forKey:@"VideoEffectApplyProfileOnStartup"];
}

856 857 858 859
#pragma mark -
#pragma mark basic
- (IBAction)enableAdjust:(id)sender
{
860 861 862 863 864 865 866 867 868 869 870
    if (sender == _adjustResetButton) {
        [_adjustBrightnessSlider setFloatValue: 1.0];
        [_adjustContrastSlider setFloatValue: 1.0];
        [_adjustGammaSlider setFloatValue: 1.0];
        [_adjustHueSlider setFloatValue: 0];
        [_adjustSaturationSlider setFloatValue: 1.0];
        [_adjustBrightnessSlider setToolTip: [NSString stringWithFormat:@"%0.3f", 1.0]];
        [_adjustContrastSlider setToolTip: [NSString stringWithFormat:@"%0.3f", 1.0]];
        [_adjustGammaSlider setToolTip: [NSString stringWithFormat:@"%0.3f", 1.0]];
        [_adjustHueSlider setToolTip: [NSString stringWithFormat:@"%.0f", 0.0]];
        [_adjustSaturationSlider setToolTip: [NSString stringWithFormat:@"%0.3f", 1.0]];
871

872
        VLCCoreInteraction *vci_si = [VLCCoreInteraction sharedInstance];
873 874 875 876 877
        [vci_si setVideoFilterProperty: "brightness" forFilter: "adjust" withValue: (vlc_value_t){ .f_float = 1.f }];
        [vci_si setVideoFilterProperty: "contrast" forFilter: "adjust" withValue: (vlc_value_t){ .f_float = 1.f }];
        [vci_si setVideoFilterProperty: "gamma" forFilter: "adjust" withValue: (vlc_value_t){ .f_float = 1.f }];
        [vci_si setVideoFilterProperty: "hue" forFilter: "adjust" withValue: (vlc_value_t){ .f_float = .0f }];
        [vci_si setVideoFilterProperty: "saturation" forFilter: "adjust" withValue: (vlc_value_t){ .f_float = 1.f }];
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
    } else {
        BOOL b_state = [_adjustCheckbox state];

        [[VLCCoreInteraction sharedInstance] setVideoFilter: "adjust" on: b_state];
        [_adjustBrightnessSlider setEnabled: b_state];
        [_adjustBrightnessCheckbox setEnabled: b_state];
        [_adjustBrightnessLabel setEnabled: b_state];
        [_adjustContrastSlider setEnabled: b_state];
        [_adjustContrastLabel setEnabled: b_state];
        [_adjustGammaSlider setEnabled: b_state];
        [_adjustGammaLabel setEnabled: b_state];
        [_adjustHueSlider setEnabled: b_state];
        [_adjustHueLabel setEnabled: b_state];
        [_adjustSaturationSlider setEnabled: b_state];
        [_adjustSaturationLabel setEnabled: b_state];
        [_adjustResetButton setEnabled: b_state];
    }
895 896 897 898
}

- (IBAction)adjustSliderChanged:(id)sender
{
899 900
    char const *psz_property;

901
    if (sender == _adjustBrightnessSlider)
902
        psz_property = "brightness";
903
    else if (sender == _adjustContrastSlider)
904
        psz_property = "contrast";
905
    else if (sender == _adjustGammaSlider)
906
        psz_property = "gamma";
907
    else if (sender == _adjustHueSlider)
908
        psz_property = "hue";
909
    else if (sender == _adjustSaturationSlider)
910 911
        psz_property = "saturation";
    [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: psz_property forFilter: "adjust" withValue: getWidgetFloatValue(sender)];
912 913 914

    if (sender == _adjustHueSlider)
        [_adjustHueSlider setToolTip: [NSString stringWithFormat:@"%.0f", [_adjustHueSlider floatValue]]];
915 916
    else
        [sender setToolTip: [NSString stringWithFormat:@"%0.3f", [sender floatValue]]];
917 918 919 920
}

- (IBAction)enableAdjustBrightnessThreshold:(id)sender
{
921 922 923
    [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "brightness-threshold"
                                                      forFilter: "adjust"
                                                      withValue: getWidgetBoolValue(sender)];
924 925 926 927
}

- (IBAction)enableSharpen:(id)sender
{
928
    BOOL b_state = [_sharpenCheckbox state];
929

930
    [[VLCCoreInteraction sharedInstance] setVideoFilter: "sharpen" on: b_state];
931 932
    [_sharpenSlider setEnabled: b_state];
    [_sharpenLabel setEnabled: b_state];
933 934 935 936
}

- (IBAction)sharpenSliderChanged:(id)sender
{
937
    [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "sharpen-sigma" forFilter: "sharpen" withValue: getWidgetFloatValue(sender)];
938
    [sender setToolTip: [NSString stringWithFormat:@"%0.3f", [sender floatValue]]];
939 940 941 942
}

- (IBAction)enableBanding:(id)sender
{
943
    BOOL b_state = [_bandingCheckbox state];
944

945
    [[VLCCoreInteraction sharedInstance] setVideoFilter: "gradfun" on: b_state];
946 947
    [_bandingSlider setEnabled: b_state];
    [_bandingLabel setEnabled: b_state];
948 949 950 951
}

- (IBAction)bandingSliderChanged:(id)sender
{
952
    [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "gradfun-radius" forFilter: "gradfun" withValue: getWidgetIntValue(sender)];
953
    [sender setToolTip: [NSString stringWithFormat:@"%i", [sender intValue]]];
954 955 956 957
}

- (IBAction)enableGrain:(id)sender
{
958
    BOOL b_state = [_grainCheckbox state];
959

960
    [[VLCCoreInteraction sharedInstance] setVideoFilter: "grain" on: b_state];
961 962
    [_grainSlider setEnabled: b_state];
    [_grainLabel setEnabled: b_state];
963 964 965 966
}

- (IBAction)grainSliderChanged:(id)sender
{
967
    [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "grain-variance" forFilter: "grain" withValue: getWidgetFloatValue(sender)];
968
    [sender setToolTip: [NSString stringWithFormat:@"%0.3f", [sender floatValue]]];
969 970 971 972 973
}


#pragma mark -
#pragma mark crop
974

975
- (IBAction)cropObjectChanged:(id)sender
976
{
977 978
    if ([_cropSyncTopBottomCheckbox state]) {
        if (sender == _cropBottomTextField || sender == _cropBottomStepper)
979 980 981
            [self setCropTopValue: [self cropBottomValue]];
        else
            [self setCropBottomValue: [self cropTopValue]];
982
    }
983 984
    if ([_cropSyncLeftRightCheckbox state]) {
        if (sender == _cropRightTextField || sender == _cropRightStepper)
985 986 987
            [self setCropLeftValue: [self cropRightValue]];
        else
            [self setCropRightValue: [self cropLeftValue]];
988
    }
989

990 991 992 993 994 995 996 997 998 999
    NSArray<NSValue *> *vouts = getVouts();
    if (vouts)
        for (NSValue *ptr in vouts) {
            vout_thread_t *p_vout = [ptr pointerValue];
            var_SetInteger(p_vout, "crop-top", [_cropTopTextField intValue]);
            var_SetInteger(p_vout, "crop-bottom", [_cropBottomTextField intValue]);
            var_SetInteger(p_vout, "crop-left", [_cropLeftTextField intValue]);
            var_SetInteger(p_vout, "crop-right", [_cropRightTextField intValue]);
            vlc_object_release(p_vout);
        }
1000 1001 1002 1003 1004 1005
}

#pragma mark -
#pragma mark geometry
- (IBAction)enableTransform:(id)sender
{
1006 1007
    [[VLCCoreInteraction sharedInstance] setVideoFilter: "transform" on: [_transformCheckbox state]];
    [_transformPopup setEnabled: [_transformCheckbox state]];
1008 1009 1010 1011
}

- (IBAction)transformModifierChanged:(id)sender
{
1012 1013
    vlc_value_t value = { .psz_string = (char *)[[[_transformPopup selectedItem] representedObject] UTF8String] };
    [[VLCCoreInteraction sharedInstance] setVideoFilterProperty: "transform-type" forFilter: "transform" withValue: value];
1014 1015 1016 1017
}

- (IBAction)enableZoom:(id)sender
{
1018
    [[VLCCoreInteraction sharedInstance] setVideoFilter: "magnify" on: [_zoomCheckbox state]];
1019 1020 1021 1022
}

- (IBAction)enablePuzzle:(id)sender
{
1023
    BOOL b_state = [_puzzleCheckbox state];
1024

1025
    [[VLCCoreInteraction sharedInstance] setVideoFilter: "puzzle" on: b_state];
1026 1027 1028 1029 1030 1031
    [_puzzleColumnsTextField setEnabled: b_state];
    [_puzzleColumnsStepper setEnabled: b_state];
    [_puzzleColumnsLabel setEnabled: b_state];
    [_puzzleRowsTextField setEnabled: b_state];
    [_puzzleRowsStepper setEnabled: b_state];
    [_puzzleRowsLabel setEnabled: b_state];
1032 1033 1034 1035
}

- (IBAction)puzzleModifierChanged:(id)sender
{
1036
    if (sender == _puzzleColumnsTextField || sender == _puzzleColumnsStepper)