/*****************************************************************************
* VLCSimplePrefsController.m: Simple Preferences for Mac OS X
*****************************************************************************
* Copyright (C) 2008-2018 VLC authors and VideoLAN
* $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.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#import "CompatibilityFixes.h"
#import "VLCSimplePrefsController.h"
#import "prefs.h"
#import <vlc_actions.h>
#import <vlc_interface.h>
#import <vlc_dialog.h>
#import <vlc_modules.h>
#import <vlc_plugin.h>
#import <vlc_config_cat.h>
#import "misc.h"
#import "VLCMain.h"
#import "VLCMain+OldPrefs.h"
#import "AppleRemote.h"
#import "VLCCoreInteraction.h"
#import "NSScreen+VLCAdditions.h"

#ifdef HAVE_SPARKLE
#import <Sparkle/Sparkle.h>                        //for o_intf_last_updateLabel
#endif


static struct {
    const char iso[6];
    const char name[34];
    BOOL isRightToLeft;

} const language_map[] = {
    { "auto",  N_("Auto"),              NO },
    { "en",    "American English",      NO },
    { "ar",    "عربي",                  YES },
    { "an",    "Aragonés",              NO },
    { "as_IN", "অসমীয়া",                 NO },
    { "ast",   "Asturianu",             NO },
    { "be",    "беларуская мова",       NO },
    { "brx",   "बर'/बड़",                 NO },
    { "bn",    "বাংলা",                   NO },
    { "pt_BR", "Português Brasileiro",  NO },
    { "en_GB", "British English",       NO },
    { "my",    "မြန်မာစာ",                NO },
    { "el",    "Νέα Ελληνικά",          NO },
    { "bg",    "български език",        NO },
    { "ca",    "Català",                NO },
    { "zh_TW", "正體中文",                NO },
    { "co",    "Corsu",                 NO },
    { "cs",    "Čeština",               NO },
    { "cy",    "Cymraeg",               NO },
    { "da",    "Dansk",                 NO },
    { "nl",    "Nederlands",            NO },
    { "fi",    "Suomi",                 NO },
    { "eo",    "Esperanto",             NO },
    { "et",    "eesti keel",            NO },
    { "eu",    "Euskara",               NO },
    { "fr",    "Français",              NO },
    { "ga",    "Gaeilge",               NO },
    { "gd",    "Gàidhlig",              NO },
    { "gl",    "Galego",                NO },
    { "gu",    "ગુજરાતી",                 NO },
    { "de",    "Deutsch",               NO },
    { "he",    "עברית",                 YES },
    { "hr",    "hrvatski",              NO },
    { "kn",    "ಕನ್ನಡ",                   NO },
    { "lv",    "Latviešu valoda",       NO },
    { "hu",    "Magyar",                NO },
    { "mr",    "मराठी",                   NO },
    { "is",    "íslenska",              NO },
    { "id",    "Bahasa Indonesia",      NO },
    { "it",    "Italiano",              NO },
    { "ie",    "Interlingue",           NO },
    { "ja",    "日本語",                 NO },
    { "ko",    "한국어",                  NO },
    { "lt",    "lietuvių",              NO },
    { "lo",    "ລາວ",                   NO },
    { "mk",    "македонски",            NO },
    { "ms",    "Melayu",                NO },
    { "nb",    "Bokmål",                NO },
    { "nn",    "Nynorsk",               NO },
    { "kk",    "Қазақ тілі",            NO },
    { "km",    "ភាសាខ្មែរ",                NO },
    { "ne",    "नेपाली",                  NO },
    { "oc",    "Occitan",               NO },
    { "or_IN", "ଓଡ଼ିଆ",                  NO },
    { "pl",    "Polski",                NO },
    { "pt_PT", "Português",             NO },
    { "pa",    "ਪੰਜਾਬੀ",                  NO },
    { "ro",    "Română",                NO },
    { "ru",    "Русский",               NO },
    { "zh_CN", "简体中文",                NO },
    { "sm",    "Gagana Sāmoa",          NO },
    { "si",    "සිංහල",                   NO },
    { "sr",    "српски",                NO },
    { "sk",    "Slovensky",             NO },
    { "sl",    "slovenščina",           NO },
    { "es",    "Español",               NO },
    { "es_MX", "Español Mexicano",      NO },
    { "sv",    "Svenska",               NO },
    { "sw",    "كِسوَحِيلِ",                YES },
    { "th",    "ภาษาไทย",               NO },
    { "tr",    "Türkçe",                NO },
    { "uk",    "украї́нська мо́ва",       NO },
    { "vi",    "tiếng Việt",            NO },
    { "wa",    "Walon",                 NO }
};

static NSString* VLCSPrefsToolbarIdentifier = @"Our Simple Preferences Toolbar Identifier";
static NSString* VLCIntfSettingToolbarIdentifier = @"Intf Settings Item Identifier";
static NSString* VLCAudioSettingToolbarIdentifier = @"Audio Settings Item Identifier";
static NSString* VLCVideoSettingToolbarIdentifier = @"Video Settings Item Identifier";
static NSString* VLCOSDSettingToolbarIdentifier = @"Subtitles Settings Item Identifier";
static NSString* VLCInputSettingToolbarIdentifier = @"Input Settings Item Identifier";
static NSString* VLCHotkeysSettingToolbarIdentifier = @"Hotkeys Settings Item Identifier";

@interface VLCSimplePrefsController() <NSToolbarDelegate, NSWindowDelegate>
{
    BOOL _audioSettingChanged;
    BOOL _intfSettingChanged;
    BOOL _videoSettingChanged;
    BOOL _osdSettingChanged;
    BOOL _inputSettingChanged;
    BOOL _hotkeyChanged;

    NSOpenPanel *_selectFolderPanel;
    NSArray *_hotkeyDescriptions;
    NSArray *_hotkeyNames;
    NSArray *_hotkeysNonUseableKeys;
    NSMutableArray *_hotkeySettings;
    NSString *_keyInTransition;

    intf_thread_t *p_intf;
}
@end

@implementation VLCSimplePrefsController

#pragma mark Initialisation

- (id)init
{
    self = [super initWithWindowNibName:@"SimplePreferences"];
    if (self) {
        p_intf = getIntf();
    }

    return self;
}

- (void)windowDidLoad
{
    [self initStrings];

#ifdef HAVE_SPARKLE
    [_intf_updateCheckbox bind:@"value"
                   toObject:[SUUpdater sharedUpdater]
                withKeyPath:@"automaticallyChecksForUpdates"
                    options:nil];
#else
    [_intf_updateCheckbox setState:NSOffState];
    [_intf_updateCheckbox setEnabled:NO];
#endif

    /* setup the toolbar */
    NSToolbar * toolbar = [[NSToolbar alloc] initWithIdentifier: VLCSPrefsToolbarIdentifier];
    [toolbar setAllowsUserCustomization: NO];
    [toolbar setAutosavesConfiguration: NO];
    [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
    [toolbar setSizeMode: NSToolbarSizeModeRegular];
    [toolbar setDelegate: self];
    [self.window setToolbar:toolbar];

    [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
    [self.window setHidesOnDeactivate:YES];

    [_hotkeys_listbox setTarget:self];
    [_hotkeys_listbox setDoubleAction:@selector(hotkeyTableDoubleClick:)];

    /* setup useful stuff */
    _hotkeysNonUseableKeys = [NSArray arrayWithObjects:@"Command-c", @"Command-x", @"Command-v", @"Command-a", @"Command-," , @"Command-h", @"Command-Alt-h", @"Command-Shift-o", @"Command-o", @"Command-d", @"Command-n", @"Command-s", @"Command-l", @"Command-r", @"Command-3", @"Command-m", @"Command-w", @"Command-Shift-w", @"Command-Shift-c", @"Command-Shift-p", @"Command-i", @"Command-e", @"Command-Shift-e", @"Command-b", @"Command-Shift-m", @"Command-Ctrl-m", @"Command-?", @"Command-Alt-?", @"Command-Shift-f", nil];

    // Workaround for Mac OS X Lion, which does not apply the same constraints when set in IB
    NSView *clipView = _contentView.superview;

    NSDictionary *views = @{ @"view": _contentView };
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[view]|" options:0 metrics:nil views:views];
    [clipView addConstraints:constraints];
}

#define CreateToolbarItem(name, desc, img, sel) \
    toolbarItem = create_toolbar_item(itemIdent, name, desc, img, self, @selector(sel));
static inline NSToolbarItem *
create_toolbar_item(NSString *itemIdent, NSString *name, NSString *desc, NSString *img, id target, SEL selector)
{
    NSToolbarItem *toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier: itemIdent];

    [toolbarItem setLabel:name];
    [toolbarItem setPaletteLabel:desc];

    [toolbarItem setToolTip:desc];
    [toolbarItem setImage:[NSImage imageNamed:img]];

    [toolbarItem setTarget:target];
    [toolbarItem setAction:selector];

    [toolbarItem setEnabled:YES];
    [toolbarItem setAutovalidates:YES];

    return toolbarItem;
}

- (NSToolbarItem *) toolbar:(NSToolbar *)toolbar
      itemForItemIdentifier:(NSString *)itemIdent
  willBeInsertedIntoToolbar:(BOOL)willBeInserted
{
    NSToolbarItem *toolbarItem = nil;

    if ([itemIdent isEqual: VLCIntfSettingToolbarIdentifier]) {
        CreateToolbarItem(_NS("Interface"), _NS("Interface Settings"), @"VLCInterfaceCone", showInterfaceSettings);
    } else if ([itemIdent isEqual: VLCAudioSettingToolbarIdentifier]) {
        CreateToolbarItem(_NS("Audio"), _NS("Audio Settings"), @"VLCAudioCone", showAudioSettings);
    } else if ([itemIdent isEqual: VLCVideoSettingToolbarIdentifier]) {
        CreateToolbarItem(_NS("Video"), _NS("Video Settings"), @"VLCVideoCone", showVideoSettings);
    } else if ([itemIdent isEqual: VLCOSDSettingToolbarIdentifier]) {
        CreateToolbarItem(_NS(SUBPIC_TITLE), _NS("Subtitle & On Screen Display Settings"), @"VLCSubtitleCone", showOSDSettings);
    } else if ([itemIdent isEqual: VLCInputSettingToolbarIdentifier]) {
        CreateToolbarItem(_NS(INPUT_TITLE), _NS("Input & Codec Settings"), @"VLCInputCone", showInputSettings);
    } else if ([itemIdent isEqual: VLCHotkeysSettingToolbarIdentifier]) {
        CreateToolbarItem(_NS("Hotkeys"), _NS("Hotkeys settings"), @"VLCHotkeysCone", showHotkeySettings);
    }

    return toolbarItem;
}

- (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
{
    return [NSArray arrayWithObjects:VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier,
             VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier,
             NSToolbarFlexibleSpaceItemIdentifier, nil];
}

- (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
{
    return [NSArray arrayWithObjects:VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier,
             VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier,
             NSToolbarFlexibleSpaceItemIdentifier, nil];
}

- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
{
    return [NSArray arrayWithObjects:VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier,
             VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, nil];
}

- (void)initStrings
{
    /* audio */
    [_audio_effectsBox setTitle: _NS("Audio Effects")];
    [_audio_enableCheckbox setTitle: _NS("Enable audio")];
    [_audio_generalBox setTitle: _NS("General Audio")];
    [_audio_langLabel setStringValue: _NS("Preferred Audio language")];
    [_audio_lastCheckbox setTitle: _NS("Enable Last.fm submissions")];
    [_audio_lastpwdLabel setStringValue: _NS("Password")];
    [_audio_lastuserLabel setStringValue: _NS("Username")];
    [_audio_visualLabel setStringValue: _NS("Visualization")];
    [_audio_autosavevol_yesButtonCell setTitle: _NS("Keep audio level between sessions")];
    [_audio_autosavevol_noButtonCell setTitle: _NS("Always reset audio start level to:")];

    /* hotkeys */
    [_hotkeys_changeButton setTitle: _NS("Change")];
    [_hotkeys_change_win setTitle: _NS("Change Hotkey")];
    [_hotkeys_change_cancelButton setTitle: _NS("Cancel")];
    [_hotkeys_change_okButton setTitle: _NS("OK")];
    [_hotkeys_clearButton setTitle: _NS("Clear")];
    [_hotkeysLabel setStringValue: _NS("Select an action to change the associated hotkey:")];
    [[[_hotkeys_listbox tableColumnWithIdentifier: @"action"] headerCell] setStringValue: _NS("Action")];
    [[[_hotkeys_listbox tableColumnWithIdentifier: @"shortcut"] headerCell] setStringValue: _NS("Shortcut")];

    /* input */
    [_input_recordBox setTitle: _NS("Record directory or filename")];
    [_input_recordButton setTitle: _NS("Browse...")];
    [_input_recordButton setToolTip: _NS("Directory or filename where the records will be stored")];
    [_input_aviLabel setStringValue: _NS("Repair AVI Files")];
    [_input_cachelevelLabel setStringValue: _NS("Default Caching Level")];
    [_input_cachingBox setTitle: _NS("Caching")];
    [_input_cachelevel_customLabel setStringValue: _NS("Use the complete preferences to configure custom caching values for each access module.")];
    [_input_muxBox setTitle: _NS("Codecs / Muxers")];
    [_input_netBox setTitle: _NS("Network")];
    [_input_hardwareAccelerationCheckbox setTitle: _NS("Hardware decoding")];
    [_input_postprocLabel setStringValue: _NS("Post-Processing Quality")];
    [_input_skipLoopLabel setStringValue: _NS("Skip the loop filter for H.264 decoding")];
    [_input_urlhandlerButton setTitle: _NS("Edit default application settings for network protocols")];
    [_input_skipFramesCheckbox setTitle: _NS("Skip frames")];

    /* url handler */
    [_urlhandler_titleLabel setStringValue: _NS("Open network streams using the following protocols")];
    [_urlhandler_subtitleLabel setStringValue: _NS("Note that these are system-wide settings.")];
    [_urlhandler_saveButton setTitle: _NS("Save")];
    [_urlhandler_cancelButton setTitle: _NS("Cancel")];

    /* interface */
    [_intf_generalSettingsBox setTitle:_NS("General settings")];
    [_intf_languageLabel setStringValue: _NS("Language")];
    [_intf_styleLabel setStringValue: _NS("Interface style")];
    [_intf_style_darkButtonCell setTitle: _NS("Dark")];
    [_intf_style_brightButtonCell setTitle: _NS("Bright")];

    [_intf_playbackControlBox setTitle:_NS("Playback control")];
    [_intf_continueplaybackLabel setStringValue:_NS("Continue playback")];
    [_intf_appleremoteCheckbox setTitle: _NS("Control playback with the Apple Remote")];
    [_intf_mediakeysCheckbox setTitle: _NS("Control playback with media keys")];
    [_intf_appleremote_sysvolCheckbox setTitle: _NS("Control system volume with the Apple Remote")];
    [_intf_statusIconCheckbox setTitle: _NS("Display VLC status menu icon")];

    [_intf_playbackBehaviourBox setTitle:_NS("Playback behaviour")];
    [_intf_enableNotificationsCheckbox setTitle: _NS("Enable notifications on playlist item change")];
    [_intf_pauseitunesLabel setStringValue:_NS("Control external music players")];

    [_intf_networkBox setTitle: _NS("Privacy / Network Interaction")];
    [_intf_artCheckbox setTitle: _NS("Allow metadata network access")];
    [_intf_updateCheckbox setTitle: _NS("Automatically check for updates")];
    [_intf_last_updateLabel setStringValue: @""];

    [_intf_luahttpBox setTitle:_NS("HTTP web interface")];
    [_intf_enableluahttpCheckbox setTitle: _NS("Enable HTTP web interface")];
    [_intf_luahttppwdLabel setStringValue:_NS("Password")];

    /* Subtitles and OSD */
    [_osd_encodingLabel setStringValue: _NS("Default Encoding")];
    [_osd_fontBox setTitle: _NS("Display Settings")];
    [_osd_fontButton setTitle: _NS("Choose...")];
    [_osd_font_colorLabel setStringValue: _NS("Font color")];
    [_osd_font_sizeLabel setStringValue: _NS("Font size")];
    [_osd_fontLabel setStringValue: _NS("Font")];
    [_osd_langBox setTitle: _NS("Subtitle languages")];
    [_osd_langLabel setStringValue: _NS("Preferred subtitle language")];
    [_osd_osdBox setTitle: _NS("On Screen Display")];
    [_osd_osdCheckbox setTitle: _NS("Enable OSD")];
    [_osd_opacityLabel setStringValue: _NS("Opacity")];
    [_osd_forceboldCheckbox setTitle: _NS("Force bold")];
    [_osd_outline_colorLabel setStringValue: _NS("Outline color")];
    [_osd_outline_thicknessLabel setStringValue: _NS("Outline thickness")];

    /* video */
    [_video_enableCheckbox setTitle: _NS("Enable video")];
    [_video_displayBox setTitle: _NS("Display")];
    [_video_embeddedCheckbox setTitle: _NS("Show video within the main window")];
    [_video_pauseWhenMinimizedCheckbox setTitle:_NS("Pause the video playback when minimized")];
    [_video_onTopCheckbox setTitle: _NS("Float on Top")];
    [_video_videodecoCheckbox setTitle: _NS("Window decorations")];

    [_video_fullscreenBox setTitle:_NS("Fullscreen settings")];
    [_video_startInFullscreenCheckbox setTitle:_NS("Start in fullscreen")];
    [_video_blackScreenCheckbox setTitle: _NS("Black screens in Fullscreen mode")];
    [_video_nativeFullscreenCheckbox setTitle: _NS("Use the native fullscreen mode")];
    [_video_deviceLabel setStringValue: _NS("Fullscreen Video Device")];

    [_video_snapBox setTitle: _NS("Video snapshots")];
    [_video_snap_folderButton setTitle: _NS("Browse...")];
    [_video_snap_folderLabel setStringValue: _NS("Folder")];
    [_video_snap_formatLabel setStringValue: _NS("Format")];
    [_video_snap_prefixLabel setStringValue: _NS("Prefix")];
    [_video_snap_seqnumCheckbox setTitle: _NS("Sequential numbering")];
    [_video_deinterlaceLabel setStringValue: _NS("Deinterlace")];
    [_video_deinterlace_modeLabel setStringValue: _NS("Deinterlace mode")];
    [_video_videoBox setTitle: _NS("Video")];

    /* generic stuff */
    [_showAllButton setTitle: _NS("Show All")];
    [_cancelButton setTitle: _NS("Cancel")];
    [_resetButton setTitle: _NS("Reset All")];
    [_saveButton setTitle: _NS("Save")];
    [self.window setTitle: _NS("Preferences")];
}

/* TODO: move this part to core */
#define config_GetLabel(a,b) __config_GetLabel(VLC_OBJECT(a),b)
static inline const char * __config_GetLabel(vlc_object_t *p_this, const char *psz_name)
{
    module_config_t *p_config = config_FindConfig(psz_name);

    /* sanity checks */
    if (!p_config) {
        msg_Err(p_this, "option %s does not exist", psz_name);
        return NULL;
    }

    if (p_config->psz_longtext)
        return p_config->psz_longtext;
    else if (p_config->psz_text)
        return p_config->psz_text;
    else
        msg_Warn(p_this, "option %s does not include any help", psz_name);

    return NULL;
}

#pragma mark -
#pragma mark Setup controls

- (void)setupButton: (NSPopUpButton *)object forStringList: (const char *)name
{
    module_config_t *p_item;

    [object removeAllItems];
    p_item = config_FindConfig(name);
    /* serious problem, if no item found */
    assert(p_item);

    char **values, **texts;
    ssize_t count = config_GetPszChoices(VLC_OBJECT(getIntf()), name,
                                         &values, &texts);
    if (count < 0) {
        msg_Err(p_intf, "Cannot get choices for %s", name);
        return;
    }
    for (ssize_t i = 0; i < count && texts; i++) {
        if (texts[i] == NULL || values[i] == NULL)
            continue;

        if (strcmp(texts[i], "") != 0) {
            NSMenuItem *mi = [[NSMenuItem alloc] initWithTitle: toNSStr(texts[i]) action: NULL keyEquivalent: @""];
            [mi setRepresentedObject: toNSStr(values[i])];
            [[object menu] addItem:mi];

            if (p_item->value.psz && !strcmp(p_item->value.psz, values[i]))
                [object selectItem: [object lastItem]];
        } else {
            [[object menu] addItem: [NSMenuItem separatorItem]];
        }

        free(texts[i]);
        free(values[i]);
    }

    free(texts);
    free(values);

    if (p_item->psz_longtext)
        [object setToolTip: _NS(p_item->psz_longtext)];
}

// just for clarification that this is a module list
- (void)setupButton: (NSPopUpButton *)object forModuleList: (const char *)name
{
    [self setupButton: object forStringList: name];
}

- (void)setupButton: (NSPopUpButton *)object forIntList: (const char *)name
{
    module_config_t *p_item;

    [object removeAllItems];
    p_item = config_FindConfig(name);

    /* serious problem, if no item found */
    assert(p_item);

    int64_t *values;
    char **texts;
    ssize_t count = config_GetIntChoices(VLC_OBJECT(getIntf()), name, &values, &texts);
    for (ssize_t i = 0; i < count; i++) {
        NSMenuItem *mi = [[NSMenuItem alloc] initWithTitle: toNSStr(texts[i]) action: NULL keyEquivalent: @""];
        [mi setRepresentedObject:[NSNumber numberWithInt:values[i]]];
        [[object menu] addItem:mi];

        if (p_item->value.i == values[i])
            [object selectItem:[object lastItem]];

        free(texts[i]);
    }
    free(texts);

    if (p_item->psz_longtext)
        [object setToolTip: _NS(p_item->psz_longtext)];
}

- (void)setupButton: (NSButton *)object forBoolValue: (const char *)name
{
    [object setState: config_GetInt(p_intf, name)];
    [object setToolTip: _NS(config_GetLabel(p_intf, name))];
}

- (void)setupField:(NSTextField *)object forOption:(const char *)psz_option
{
    char *psz_tmp = config_GetPsz(p_intf, psz_option);
    [object setStringValue: toNSStr(psz_tmp)];
    [object setToolTip: _NS(config_GetLabel(p_intf, psz_option))];
    free(psz_tmp);
}

- (BOOL)hasModule:(NSString *)moduleName inConfig:(NSString *)config
{
    char *value = config_GetPsz(p_intf, [config UTF8String]);
    NSString *modules = toNSStr(value);
    free(value);

    return [[modules componentsSeparatedByString:@":"] containsObject:moduleName];
}

- (void)changeModule:(NSString *)moduleName inConfig:(NSString *)config enable:(BOOL)enable
{
    char *value = config_GetPsz(p_intf, [config UTF8String]);
    NSString *modules = toNSStr(value);
    free(value);

    NSMutableArray *components = [[modules componentsSeparatedByString:@":"] mutableCopy];
    if (enable) {
        if (![components containsObject:moduleName]) {
            [components addObject:moduleName];
        }
    } else {
        [components removeObject:moduleName];
    }

    // trim empty entries
    [components removeObject:@""];

    config_PutPsz(p_intf, [config UTF8String], [[components componentsJoinedByString:@":"] UTF8String]);
}

- (void)resetControls
{
    module_config_t *p_item;
    int i, y = 0;
    char *psz_tmp;

    /**********************
     * interface settings *
     **********************/
    NSUInteger sel = 0;
    const char *pref = NULL;
    pref = [[[NSUserDefaults standardUserDefaults] objectForKey:@"language"] UTF8String];
    for (int x = 0; x < ARRAY_SIZE(language_map); x++) {
        [_intf_languagePopup addItemWithTitle:toNSStr(language_map[x].name)];
        if (pref) {
            if (!strcmp(language_map[x].iso, pref))
                sel = x;
        }
    }
    [_intf_languagePopup selectItemAtIndex:sel];

    [self setupButton:_intf_continueplaybackPopup forIntList: "macosx-continue-playback"];
    if (!var_InheritBool(p_intf, "macosx-recentitems")) {
        [_intf_continueplaybackPopup setEnabled: NO];
        [_intf_continueplaybackPopup setToolTip: _NS("Media files cannot be resumed because keeping recent media items is disabled.")];
    } else {
        [_intf_continueplaybackPopup setEnabled: YES];
    }

    [self setupButton:_intf_appleremoteCheckbox forBoolValue: "macosx-appleremote"];
    [self setupButton:_intf_appleremote_sysvolCheckbox forBoolValue: "macosx-appleremote-sysvol"];
    [self setupButton:_intf_statusIconCheckbox forBoolValue: "macosx-statusicon"];
    [self setupButton:_intf_mediakeysCheckbox forBoolValue: "macosx-mediakeys"];

    [self setupButton:_video_nativeFullscreenCheckbox forBoolValue: "macosx-nativefullscreenmode"];
    [self setupButton:_video_embeddedCheckbox forBoolValue: "embedded-video"];

    [self setupButton:_intf_pauseitunesPopup forIntList: "macosx-control-itunes"];

    [self setupButton:_intf_artCheckbox forBoolValue: "metadata-network-access"];


#ifdef HAVE_SPARKLE
    if ([[SUUpdater sharedUpdater] lastUpdateCheckDate] != NULL)
        [_intf_last_updateLabel setStringValue: [NSString stringWithFormat: _NS("Last check on: %@"), [[[SUUpdater sharedUpdater] lastUpdateCheckDate] descriptionWithLocale: [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]]]];
    else
        [_intf_last_updateLabel setStringValue: _NS("No check was performed yet.")];
#endif

    BOOL growlEnabled = [self hasModule:@"growl" inConfig:@"control"];
    [_intf_enableNotificationsCheckbox setState: growlEnabled ? NSOnState : NSOffState];

    if (config_GetInt(p_intf, "macosx-interfacestyle")) {
        [_intf_style_darkButtonCell setState: YES];
        [_intf_style_brightButtonCell setState: NO];
    } else {
        [_intf_style_darkButtonCell setState: NO];
        [_intf_style_brightButtonCell setState: YES];
    }

    BOOL httpEnabled = [self hasModule:@"http" inConfig:@"extraintf"];
    [_intf_enableluahttpCheckbox setState: httpEnabled ? NSOnState : NSOffState];
    _intf_luahttppwdTextField.enabled = httpEnabled;

    [self setupField:_intf_luahttppwdTextField forOption: "http-password"];

    /******************
     * audio settings *
     ******************/
    [self setupButton:_audio_enableCheckbox forBoolValue: "audio"];

    if (config_GetInt(p_intf, "volume-save")) {
        [_audio_autosavevol_yesButtonCell setState: NSOnState];
        [_audio_autosavevol_noButtonCell setState: NSOffState];
        [_audio_volTextField setEnabled: NO];
        [_audio_volSlider setEnabled: NO];

        [_audio_volSlider setIntValue: 100];
        [_audio_volTextField setIntValue: 100];
    } else {
        [_audio_autosavevol_yesButtonCell setState: NSOffState];
        [_audio_autosavevol_noButtonCell setState: NSOnState];
        [_audio_volTextField setEnabled: YES];
        [_audio_volSlider setEnabled: YES];

        i = var_InheritInteger(p_intf, "auhal-volume");
        i = i * 200. / AOUT_VOLUME_MAX;
        [_audio_volSlider setIntValue: i];
        [_audio_volTextField setIntValue: i];
    }

    [self setupField:_audio_langTextField forOption: "audio-language"];

    [self setupButton:_audio_visualPopup forModuleList: "audio-visual"];

    /* Last.FM is optional */
    if (module_exists("audioscrobbler")) {
        [self setupField:_audio_lastuserTextField forOption:"lastfm-username"];
        [self setupField:_audio_lastpwdSecureTextField forOption:"lastfm-password"];

        if (config_ExistIntf(VLC_OBJECT(p_intf), "audioscrobbler")) {
            [_audio_lastCheckbox setState: NSOnState];
            [_audio_lastuserTextField setEnabled: YES];
            [_audio_lastpwdSecureTextField setEnabled: YES];
        } else {
            [_audio_lastCheckbox setState: NSOffState];
            [_audio_lastuserTextField setEnabled: NO];
            [_audio_lastpwdSecureTextField setEnabled: NO];
        }
    } else
        [_audio_lastCheckbox setEnabled: NO];

    /******************
     * video settings *
     ******************/
    [self setupButton:_video_enableCheckbox forBoolValue: "video"];
    [self setupButton:_video_startInFullscreenCheckbox forBoolValue: "fullscreen"];
    [self setupButton:_video_onTopCheckbox forBoolValue: "video-on-top"];
    [self setupButton:_video_blackScreenCheckbox forBoolValue: "macosx-black"];
    [self setupButton:_video_videodecoCheckbox forBoolValue: "video-deco"];
    [self setupButton:_video_pauseWhenMinimizedCheckbox forBoolValue: "macosx-pause-minimized"];

    [_video_devicePopup removeAllItems];
    i = 0;
    y = [[NSScreen screens] count];
    [_video_devicePopup addItemWithTitle: _NS("Default")];
    [[_video_devicePopup lastItem] setTag: 0];
    while (i < y) {
        NSRect s_rect = [[[NSScreen screens] objectAtIndex:i] frame];
        [_video_devicePopup addItemWithTitle:
         [NSString stringWithFormat: @"%@ %i (%ix%i)", _NS("Screen"), i+1,
                   (int)s_rect.size.width, (int)s_rect.size.height]];
        [[_video_devicePopup lastItem] setTag: (int)[[[NSScreen screens] objectAtIndex:i] displayID]];
        i++;
    }
    [_video_devicePopup selectItemAtIndex: 0];
    [_video_devicePopup selectItemWithTag: config_GetInt(p_intf, "macosx-vdev")];

    [self setupField:_video_snap_folderTextField forOption:"snapshot-path"];
    [self setupField:_video_snap_prefixTextField forOption:"snapshot-prefix"];
    [self setupButton:_video_snap_seqnumCheckbox forBoolValue: "snapshot-sequential"];
    [self setupButton:_video_snap_formatPopup forStringList: "snapshot-format"];
    [self setupButton:_video_deinterlacePopup forIntList: "deinterlace"];
    [self setupButton:_video_deinterlace_modePopup forStringList: "deinterlace-mode"];

    // set lion fullscreen mode restrictions
    [self enableLionFullscreenMode: [_video_nativeFullscreenCheckbox state]];

    /***************************
     * input & codecs settings *
     ***************************/
    [self setupField:_input_recordTextField forOption:"input-record-path"];

    [self setupButton:_input_hardwareAccelerationCheckbox forBoolValue: "videotoolbox"];
    [_input_postprocTextField setIntValue: config_GetInt(p_intf, "postproc-q")];
    [_input_postprocTextField setToolTip: _NS(config_GetLabel(p_intf, "postproc-q"))];
    [self setupButton:_input_skipFramesCheckbox forBoolValue: "skip-frames"];
    [self setupButton:_input_aviPopup forIntList: "avi-index"];
    [self setupButton:_input_skipLoopPopup forIntList: "avcodec-skiploopfilter"];

    [_input_cachelevelPopup removeAllItems];
    NSMenuItem *item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Custom") action:nil keyEquivalent:@""];
    [item setTag: 0];
    item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Lowest Latency") action:nil keyEquivalent:@""];
    [item setTag: 100];
    item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Low Latency") action:nil keyEquivalent:@""];
    [item setTag: 200];
    item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Normal") action:nil keyEquivalent:@""];
    [item setTag: 300];
    item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Higher Latency") action:nil keyEquivalent:@""];
    [item setTag: 500];
    item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Highest Latency") action:nil keyEquivalent:@""];
    [item setTag: 1000];

    #define TestCaC(name, factor) \
    cache_equal = cache_equal && \
    (i_cache * factor == config_GetInt(p_intf, name));

    /* Select the accurate value of the PopupButton */
    bool cache_equal = true;
    int i_cache = config_GetInt(p_intf, "file-caching");

    TestCaC("network-caching", 10/3);
    TestCaC("disc-caching", 1);
    TestCaC("live-caching", 1);
    if (cache_equal) {
        [_input_cachelevelPopup selectItemWithTag: i_cache];
        [_input_cachelevel_customLabel setHidden: YES];
    } else {
        [_input_cachelevelPopup selectItemWithTitle: _NS("Custom")];
        [_input_cachelevel_customLabel setHidden: NO];
    }
    #undef TestCaC

    /*********************
     * subtitle settings *
     *********************/
    [self setupButton:_osd_osdCheckbox forBoolValue: "osd"];

    [self setupButton:_osd_encodingPopup forStringList: "subsdec-encoding"];
    [self setupField:_osd_langTextField forOption: "sub-language" ];

    [self setupField:_osd_fontTextField forOption: "freetype-font"];
    [self setupButton:_osd_font_colorPopup forIntList: "freetype-color"];
    [self setupButton:_osd_font_sizePopup forIntList: "freetype-rel-fontsize"];
    i = config_GetInt(p_intf, "freetype-opacity") * 100.0 / 255.0 + 0.5;
    [_osd_opacityTextField setIntValue: i];
    [_osd_opacitySlider setIntValue: i];
    [_osd_opacitySlider setToolTip: _NS(config_GetLabel(p_intf, "freetype-opacity"))];
    [_osd_opacityTextField setToolTip: [_osd_opacitySlider toolTip]];
    [self setupButton:_osd_forceboldCheckbox forBoolValue: "freetype-bold"];
    [self setupButton:_osd_outline_colorPopup forIntList: "freetype-outline-color"];
    [self setupButton:_osd_outline_thicknessPopup forIntList: "freetype-outline-thickness"];

    /********************
     * hotkeys settings *
     ********************/
    _hotkeySettings = [[NSMutableArray alloc] init];
    NSMutableArray *tempArray_desc = [[NSMutableArray alloc] init];
    NSMutableArray *tempArray_names = [[NSMutableArray alloc] init];

    /* Get the main Module */
    module_t *p_main = module_get_main();
    assert(p_main);
    unsigned confsize;
    module_config_t *p_config;

    p_config = module_config_get (p_main, &confsize);

    for (size_t i = 0; i < confsize; i++) {
        module_config_t *p_item = p_config + i;

        if (CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL
           && !strncmp(p_item->psz_name , "key-", 4)
           && !EMPTY_STR(p_item->psz_text)) {
            [tempArray_desc addObject: _NS(p_item->psz_text)];
            [tempArray_names addObject: toNSStr(p_item->psz_name)];
            if (p_item->value.psz)
                [_hotkeySettings addObject: toNSStr(p_item->value.psz)];
            else
                [_hotkeySettings addObject: [NSString string]];
        }
    }
    module_config_free (p_config);

    _hotkeyDescriptions = [[NSArray alloc] initWithArray:tempArray_desc copyItems: YES];
    _hotkeyNames = [[NSArray alloc] initWithArray:tempArray_names copyItems: YES];

    [_hotkeys_listbox reloadData];
}

#pragma mark -
#pragma mark General actions

- (void)showSimplePrefs
{
    /* we want to show the interface settings, if no category was chosen */
    if ([[self.window toolbar] selectedItemIdentifier] == nil) {
        [[self.window toolbar] setSelectedItemIdentifier: VLCIntfSettingToolbarIdentifier];
        [self showInterfaceSettings];
    }

    [self resetControls];

    [self.window makeKeyAndOrderFront: self];
}

- (void)showSimplePrefsWithLevel:(NSInteger)i_window_level
{
    [self.window setLevel: i_window_level];
    [self showSimplePrefs];
}

- (IBAction)buttonAction:(id)sender
{
    if (sender == _cancelButton) {
        [[NSFontPanel sharedFontPanel] close];
        [self.window orderOut: sender];
    } else if (sender == _saveButton) {
        [self saveChangedSettings];
        [[NSFontPanel sharedFontPanel] close];
        [self.window orderOut: sender];
    } else if (sender == _showAllButton) {
        [self.window orderOut: self];
        [[[VLCMain sharedInstance] preferences] showPrefsWithLevel:[self.window level]];
    } else
        msg_Warn(p_intf, "unknown buttonAction sender");
}

- (IBAction)resetPreferences:(NSControl *)sender
{
    NSBeginInformationalAlertSheet(_NS("Reset Preferences"), _NS("Cancel"),
                                   _NS("Continue"), nil, [sender window], self,
                                   @selector(sheetDidEnd: returnCode: contextInfo:), NULL, nil, @"%@",
                                   _NS("This will reset VLC media player's preferences.\n\n"
                                       "Note that VLC will restart during the process, so your current "
                                       "playlist will be emptied and eventual playback, streaming or "
                                       "transcoding activities will stop immediately.\n\n"
                                       "The Media Library will not be affected.\n\n"
                                       "Are you sure you want to continue?"));
}

- (void)sheetDidEnd:(NSWindow *)o_sheet
         returnCode:(int)i_return
        contextInfo:(void *)o_context
{
    if (i_return == NSAlertAlternateReturn) {
        /* reset VLC's config */
        config_ResetAll(p_intf);
        [self resetControls];

        /* force config file creation, since libvlc won't exit normally */
        config_SaveConfigFile(p_intf);

        /* reset OS X defaults */
        [[VLCMain sharedInstance] resetAndReinitializeUserDefaults];

        /* Relaunch now */
        const char * path = [[[NSBundle mainBundle] executablePath] UTF8String];

        /* For some reason we need to fork(), not just execl(), which reports a ENOTSUP then. */
        if (fork() != 0) {
            exit(0);
            return;
        }
        execl(path, path, NULL);
    }
}

static inline void save_int_list(intf_thread_t * p_intf, id object, const char * name)
{
    NSNumber *p_valueobject = (NSNumber *)[[object selectedItem] representedObject];
    if (p_valueobject) {
        assert([p_valueobject isKindOfClass:[NSNumber class]]);
        config_PutInt(p_intf, name, [p_valueobject intValue]);
    }
}

static inline void save_string_list(intf_thread_t * p_intf, id object, const char * name)
{
    NSString *p_stringobject = (NSString *)[[object selectedItem] representedObject];
    if (p_stringobject) {
        assert([p_stringobject isKindOfClass:[NSString class]]);
        config_PutPsz(p_intf, name, [p_stringobject UTF8String]);
    }
}

+ (BOOL)updateRightToLeftSettings
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *isoCode = [defaults stringForKey:@"language"];

    if (!isoCode || [isoCode isEqualToString:@"auto"]) {
        // Automatic handling of right to left
        [defaults removeObjectForKey:@"NSForceRightToLeftWritingDirection"];
        [defaults removeObjectForKey:@"AppleTextDirection"];
    } else {
        for(int i = 0; i < ARRAY_SIZE(language_map); i++) {
            if (!strcmp(language_map[i].iso, [isoCode UTF8String])) {
                [defaults setBool:language_map[i].isRightToLeft forKey:@"NSForceRightToLeftWritingDirection"];
                [defaults setBool:language_map[i].isRightToLeft forKey:@"AppleTextDirection"];
                return YES;
            }
        }
    }

    return NO;
}

- (void)saveChangedSettings
{
#define SaveIntList(object, name) save_int_list(p_intf, object, name)

#define SaveStringList(object, name) save_string_list(p_intf, object, name)
#define SaveModuleList(object, name) SaveStringList(object, name)

    /**********************
     * interface settings *
     **********************/
    if (_intfSettingChanged) {
        NSUInteger index = [_intf_languagePopup indexOfSelectedItem];
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        [defaults setObject:toNSStr(language_map[index].iso) forKey:@"language"];
        [VLCSimplePrefsController updateRightToLeftSettings];
        [defaults synchronize];

        config_PutInt(p_intf, "metadata-network-access", [_intf_artCheckbox state]);

        config_PutInt(p_intf, "macosx-appleremote", [_intf_appleremoteCheckbox state]);
        config_PutInt(p_intf, "macosx-appleremote-sysvol", [_intf_appleremote_sysvolCheckbox state]);
        config_PutInt(p_intf, "macosx-statusicon", [_intf_statusIconCheckbox state]);
        config_PutInt(p_intf, "macosx-mediakeys", [_intf_mediakeysCheckbox state]);
        config_PutInt(p_intf, "macosx-interfacestyle", [_intf_style_darkButtonCell state]);

        [self changeModule:@"growl" inConfig:@"control" enable:[_intf_enableNotificationsCheckbox state] == NSOnState];

        [self changeModule:@"http" inConfig:@"extraintf" enable:[_intf_enableluahttpCheckbox state] == NSOnState];
        config_PutPsz(p_intf, "http-password", [[_intf_luahttppwdTextField stringValue] UTF8String]);

        SaveIntList(_intf_pauseitunesPopup, "macosx-control-itunes");
        SaveIntList(_intf_continueplaybackPopup, "macosx-continue-playback");

        /* activate stuff without restart */
        if ([_intf_appleremoteCheckbox state] == YES)
            [[VLCCoreInteraction sharedInstance] startListeningWithAppleRemote];
        else
            [[VLCCoreInteraction sharedInstance] stopListeningWithAppleRemote];
        _intfSettingChanged = NO;
    }

    /******************
     * audio settings *
     ******************/
    if (_audioSettingChanged) {
        config_PutInt(p_intf, "audio", [_audio_enableCheckbox state]);
        config_PutInt(p_intf, "volume-save", [_audio_autosavevol_yesButtonCell state]);
        var_SetBool(p_intf, "volume-save", [_audio_autosavevol_yesButtonCell state]);
        if ([_audio_volTextField isEnabled])
            config_PutInt(p_intf, "auhal-volume", ([_audio_volTextField intValue] * AOUT_VOLUME_MAX) / 200);

        config_PutPsz(p_intf, "audio-language", [[_audio_langTextField stringValue] UTF8String]);

        SaveModuleList(_audio_visualPopup, "audio-visual");

        /* Last.FM is optional */
        if (module_exists("audioscrobbler")) {
            [_audio_lastCheckbox setEnabled: YES];
            if ([_audio_lastCheckbox state] == NSOnState)
                config_AddIntf(p_intf, "audioscrobbler");
            else
                config_RemoveIntf(p_intf, "audioscrobbler");

            config_PutPsz(p_intf, "lastfm-username", [[_audio_lastuserTextField stringValue] UTF8String]);
            config_PutPsz(p_intf, "lastfm-password", [[_audio_lastpwdSecureTextField stringValue] UTF8String]);
        }
        else
            [_audio_lastCheckbox setEnabled: NO];
        _audioSettingChanged = NO;
    }

    /******************
     * video settings *
     ******************/
    if (_videoSettingChanged) {
        config_PutInt(p_intf, "video", [_video_enableCheckbox state]);
        config_PutInt(p_intf, "fullscreen", [_video_startInFullscreenCheckbox state]);
        config_PutInt(p_intf, "video-deco", [_video_videodecoCheckbox state]);
        config_PutInt(p_intf, "video-on-top", [_video_onTopCheckbox state]);
        config_PutInt(p_intf, "macosx-black", [_video_blackScreenCheckbox state]);

        config_PutInt(p_intf, "macosx-pause-minimized", [_video_pauseWhenMinimizedCheckbox state]);

        config_PutInt(p_intf, "embedded-video", [_video_embeddedCheckbox state]);
        config_PutInt(p_intf, "macosx-nativefullscreenmode", [_video_nativeFullscreenCheckbox state]);
        config_PutInt(p_intf, "macosx-vdev", [[_video_devicePopup selectedItem] tag]);

        config_PutPsz(p_intf, "snapshot-path", [[_video_snap_folderTextField stringValue] UTF8String]);
        config_PutPsz(p_intf, "snapshot-prefix", [[_video_snap_prefixTextField stringValue] UTF8String]);
        config_PutInt(p_intf, "snapshot-sequential", [_video_snap_seqnumCheckbox state]);
        SaveStringList(_video_snap_formatPopup, "snapshot-format");
        SaveIntList(_video_deinterlacePopup, "deinterlace");
        SaveStringList(_video_deinterlace_modePopup, "deinterlace-mode");
        _videoSettingChanged = NO;
    }

    /***************************
     * input & codecs settings *
     ***************************/
    if (_inputSettingChanged) {
        config_PutPsz(p_intf, "input-record-path", [[_input_recordTextField stringValue] UTF8String]);

        config_PutInt(p_intf, "videotoolbox", [_input_hardwareAccelerationCheckbox state]);
        config_PutInt(p_intf, "postproc-q", [_input_postprocTextField intValue]);
        config_PutInt(p_intf, "skip-frames", [_input_skipFramesCheckbox state]);

        SaveIntList(_input_aviPopup, "avi-index");

        SaveIntList(_input_skipLoopPopup, "avcodec-skiploopfilter");

        #define CaC(name, factor) config_PutInt(p_intf, name, [[_input_cachelevelPopup selectedItem] tag] * factor)
        if ([[_input_cachelevelPopup selectedItem] tag] == 0) {
            msg_Dbg(p_intf, "Custom chosen, not adjusting cache values");
        } else {
            msg_Dbg(p_intf, "Adjusting all cache values to: %i", (int)[[_input_cachelevelPopup selectedItem] tag]);
            CaC("file-caching", 1);
            CaC("network-caching", 10/3);
            CaC("disc-caching", 1);
            CaC("live-caching", 1);
        }
        #undef CaC
        _inputSettingChanged = NO;
    }

    /**********************
     * subtitles settings *
     **********************/
    if (_osdSettingChanged) {
        config_PutInt(p_intf, "osd", [_osd_osdCheckbox state]);

        if ([_osd_encodingPopup indexOfSelectedItem] >= 0)
            SaveStringList(_osd_encodingPopup, "subsdec-encoding");
        else
            config_PutPsz(p_intf, "subsdec-encoding", "");

        config_PutPsz(p_intf, "sub-language", [[_osd_langTextField stringValue] UTF8String]);

        config_PutPsz(p_intf, "freetype-font", [[_osd_fontTextField stringValue] UTF8String]);
        SaveIntList(_osd_font_colorPopup, "freetype-color");
        SaveIntList(_osd_font_sizePopup, "freetype-rel-fontsize");
        config_PutInt(p_intf, "freetype-opacity", [_osd_opacityTextField intValue] * 255.0 / 100.0 + 0.5);
        config_PutInt(p_intf, "freetype-bold", [_osd_forceboldCheckbox state]);
        SaveIntList(_osd_outline_colorPopup, "freetype-outline-color");
        SaveIntList(_osd_outline_thicknessPopup, "freetype-outline-thickness");
        _osdSettingChanged = NO;
    }

    /********************
     * hotkeys settings *
     ********************/
    if (_hotkeyChanged) {
        NSUInteger hotKeyCount = [_hotkeySettings count];
        for (NSUInteger i = 0; i < hotKeyCount; i++)
            config_PutPsz(p_intf, [[_hotkeyNames objectAtIndex:i] UTF8String], [[_hotkeySettings objectAtIndex:i]UTF8String]);
        _hotkeyChanged = NO;
    }

    [[VLCCoreInteraction sharedInstance] fixIntfSettings];

    /* okay, let's save our changes to vlcrc */
    config_SaveConfigFile(p_intf);

    [[NSNotificationCenter defaultCenter] postNotificationName:VLCMediaKeySupportSettingChangedNotification object:nil];
    [[NSNotificationCenter defaultCenter] postNotificationName:VLCConfigurationChangedNotification object:nil];
}

- (void)showSettingsForCategory:(NSView *)categoryView
{
    [_contentView setSubviews:[NSArray array]];
    [_contentView addSubview:categoryView];

    NSDictionary *views = @{ @"view": categoryView };
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[view]|" options:0 metrics:nil views:views];
    [_contentView addConstraints:constraints];
    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:views];
    [_contentView addConstraints:constraints];

    [_scrollView layoutSubtreeIfNeeded];
    [_scrollView flashScrollers];
}

#pragma mark -
#pragma mark Specific actions

// disables some video settings which do not work in lion mode
- (void)enableLionFullscreenMode: (BOOL)_value
{
    [_video_videodecoCheckbox setEnabled: !_value];
    [_video_blackScreenCheckbox setEnabled: !_value];
    [_video_devicePopup setEnabled: !_value];

    if (_value) {
        [_video_videodecoCheckbox setState: NSOnState];
        [_video_blackScreenCheckbox setState: NSOffState];

        NSString *tooltipText = _NS("This setting cannot be changed because the native fullscreen mode is enabled.");
        [_video_videodecoCheckbox setToolTip:tooltipText];
        [_video_blackScreenCheckbox setToolTip:tooltipText];
        [_video_devicePopup setToolTip:tooltipText];

    } else {
        [self setupButton:_video_videodecoCheckbox forBoolValue: "video-deco"];
        [self setupButton:_video_blackScreenCheckbox forBoolValue: "macosx-black"];

        [_video_devicePopup setToolTip:@""];
    }
}

- (IBAction)interfaceSettingChanged:(id)sender
{
    if (sender == _intf_enableluahttpCheckbox) {
        _intf_luahttppwdTextField.enabled = [sender state] == NSOnState;
    }

    _intfSettingChanged = YES;
}

- (void)showInterfaceSettings
{
    [self showSettingsForCategory:_intfView];
}

- (IBAction)audioSettingChanged:(id)sender
{
    if (sender == _audio_volSlider)
        [_audio_volTextField setIntValue: [_audio_volSlider intValue]];

    if (sender == _audio_volTextField)
        [_audio_volSlider setIntValue: [_audio_volTextField intValue]];

    if (sender == _audio_lastCheckbox) {
        if ([_audio_lastCheckbox state] == NSOnState) {
            [_audio_lastpwdSecureTextField setEnabled: YES];
            [_audio_lastuserTextField setEnabled: YES];
        } else {
            [_audio_lastpwdSecureTextField setEnabled: NO];
            [_audio_lastuserTextField setEnabled: NO];
        }
    }

    if (sender == _audio_autosavevolMatrix) {
        BOOL enableVolumeSlider = [_audio_autosavevolMatrix selectedTag] == 1;
        [_audio_volTextField setEnabled: enableVolumeSlider];
        [_audio_volSlider setEnabled: enableVolumeSlider];
    }

    _audioSettingChanged = YES;
}

- (void)showAudioSettings
{
    [self showSettingsForCategory:_audioView];
}

- (IBAction)videoSettingChanged:(id)sender
{
    if (sender == _video_nativeFullscreenCheckbox)
        [self enableLionFullscreenMode:[sender state]];
    else if (sender == _video_snap_folderButton) {
        _selectFolderPanel = [[NSOpenPanel alloc] init];
        [_selectFolderPanel setCanChooseDirectories: YES];
        [_selectFolderPanel setCanChooseFiles: NO];
        [_selectFolderPanel setResolvesAliases: YES];
        [_selectFolderPanel setAllowsMultipleSelection: NO];
        [_selectFolderPanel setMessage: _NS("Choose the folder to save your video snapshots to.")];
        [_selectFolderPanel setCanCreateDirectories: YES];
        [_selectFolderPanel setPrompt: _NS("Choose")];
        [_selectFolderPanel beginSheetModalForWindow:self.window completionHandler: ^(NSInteger returnCode) {
            if (returnCode == NSOKButton) {
                [_video_snap_folderTextField setStringValue: [[_selectFolderPanel URL] path]];
            }
        }];
    }

    _videoSettingChanged = YES;
}

- (void)showVideoSettings
{
    [self showSettingsForCategory:_videoView];
}

- (IBAction)osdSettingChanged:(id)sender
{
    if (sender == _osd_opacityTextField)
        [_osd_opacitySlider setIntValue: [_osd_opacityTextField intValue]];

    if (sender == _osd_opacitySlider)
        [_osd_opacityTextField setIntValue: [_osd_opacitySlider intValue]];

    _osdSettingChanged = YES;
}

- (void)showOSDSettings
{
    [self showSettingsForCategory:_osdView];
}

- (void)controlTextDidChange:(NSNotification *)notification
{
    id notificationObject = [notification object];
    if (notificationObject == _audio_langTextField ||
       notificationObject ==  _audio_lastpwdSecureTextField ||
       notificationObject ==  _audio_lastuserTextField ||
       notificationObject == _audio_volTextField)
        _audioSettingChanged = YES;
    else if (notificationObject == _input_recordTextField ||
            notificationObject == _input_postprocTextField)
        _inputSettingChanged = YES;
    else if (notificationObject == _osd_fontTextField ||
            notificationObject == _osd_langTextField ||
            notificationObject == _osd_opacityTextField)
        _osdSettingChanged = YES;
    else if (notificationObject == _video_snap_folderTextField ||
            notificationObject == _video_snap_prefixTextField)
        _videoSettingChanged = YES;
    else if (notificationObject == _intf_luahttppwdTextField)
        _intfSettingChanged = YES;
}

- (IBAction)showFontPicker:(id)sender
{
    char * font = config_GetPsz(p_intf, "freetype-font");
    NSString * fontName = font ? toNSStr(font) : nil;
    free(font);
    if (fontName) {
        NSFont * font = [NSFont fontWithName:fontName size:0.0];
        [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:NO];
    }
    [[NSFontManager sharedFontManager] setTarget: self];
    [[NSFontPanel sharedFontPanel] setDelegate:self];
    [[NSFontPanel sharedFontPanel] makeKeyAndOrderFront:self];
}

- (void)changeFont:(id)sender
{
    NSFont *someFont = [NSFont systemFontOfSize:12];

    // converts given font to changes in font panel. Original font is irrelevant
    NSFont *selectedFont = [sender convertFont:someFont];

    [_osd_fontTextField setStringValue:[selectedFont fontName]];
    [self osdSettingChanged:self];
}

- (NSUInteger)validModesForFontPanel:(NSFontPanel *)fontPanel
{
    return NSFontPanelFaceModeMask | NSFontPanelCollectionModeMask;
}

- (IBAction)inputSettingChanged:(id)sender
{
    if (sender == _input_cachelevelPopup) {
        if ([[[_input_cachelevelPopup selectedItem] title] isEqualToString: _NS("Custom")])
            [_input_cachelevel_customLabel setHidden: NO];
        else
            [_input_cachelevel_customLabel setHidden: YES];
    } else if (sender == _input_recordButton) {
        _selectFolderPanel = [[NSOpenPanel alloc] init];
        [_selectFolderPanel setCanChooseDirectories: YES];
        [_selectFolderPanel setCanChooseFiles: YES];
        [_selectFolderPanel setResolvesAliases: YES];
        [_selectFolderPanel setAllowsMultipleSelection: NO];
        [_selectFolderPanel setMessage: _NS("Choose the directory or filename where the records will be stored.")];
        [_selectFolderPanel setCanCreateDirectories: YES];
        [_selectFolderPanel setPrompt: _NS("Choose")];
        [_selectFolderPanel beginSheetModalForWindow:self.window completionHandler: ^(NSInteger returnCode) {
            if (returnCode == NSOKButton)
            {
                [_input_recordTextField setStringValue: [[_selectFolderPanel URL] path]];
                _inputSettingChanged = YES;
            }
        }];

        return;
    }

    _inputSettingChanged = YES;
}

- (void)showInputSettings
{
    [self showSettingsForCategory:_inputView];
}

- (NSString *)bundleIdentifierForApplicationName:(NSString *)appName
{
    NSWorkspace * workspace = [NSWorkspace sharedWorkspace];
    NSString * appPath = [workspace fullPathForApplication:appName];
    if (appPath) {
        NSBundle * appBundle = [NSBundle bundleWithPath:appPath];
        return [appBundle bundleIdentifier];
    }
    return nil;
}

- (NSString *)applicationNameForBundleIdentifier:(NSString *)bundleIdentifier
{
    return [[[NSFileManager defaultManager] displayNameAtPath:[[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:bundleIdentifier]] stringByDeletingPathExtension];
}

- (NSImage *)iconForBundleIdentifier:(NSString *)bundleIdentifier
{
    NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
    NSSize iconSize = NSMakeSize(16., 16.);
    NSImage *icon = [workspace iconForFile:[workspace absolutePathForAppBundleWithIdentifier:bundleIdentifier]];
    [icon setSize:iconSize];
    return icon;
}

- (IBAction)urlHandlerAction:(id)sender
{
    NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];

    if (sender == _input_urlhandlerButton) {

        void (^fillUrlHandlerPopup)(NSString*, NSPopUpButton*) = ^void(NSString *protocol, NSPopUpButton *object) {

            NSArray *handlers = (__bridge_transfer NSArray *)LSCopyAllHandlersForURLScheme((__bridge CFStringRef)protocol);
            NSMutableArray *rawHandlers = [[NSMutableArray alloc] init];
            [object removeAllItems];
            NSUInteger count = [handlers count];
            for (NSUInteger x = 0; x < count; x++) {
                NSString *rawhandler = [handlers objectAtIndex:x];
                NSString *handler = [self applicationNameForBundleIdentifier:rawhandler];
                if (handler && ![handler isEqualToString:@""]) {
                    [object addItemWithTitle:handler];
                    [[object lastItem] setImage: [self iconForBundleIdentifier:[handlers objectAtIndex:x]]];
                    [rawHandlers addObject: rawhandler];
                }
            }
            [object selectItemAtIndex: [rawHandlers indexOfObject:(__bridge_transfer id)LSCopyDefaultHandlerForURLScheme((__bridge CFStringRef)protocol)]];
        };

        fillUrlHandlerPopup(@"ftp", _urlhandler_ftpPopup);
        fillUrlHandlerPopup(@"mms", _urlhandler_mmsPopup);
        fillUrlHandlerPopup(@"rtmp", _urlhandler_rtmpPopup);
        fillUrlHandlerPopup(@"rtp", _urlhandler_rtpPopup);
        fillUrlHandlerPopup(@"rtsp", _urlhandler_rtspPopup);
        fillUrlHandlerPopup(@"sftp", _urlhandler_sftpPopup);
        fillUrlHandlerPopup(@"smb", _urlhandler_smbPopup);
        fillUrlHandlerPopup(@"udp", _urlhandler_udpPopup);


        [NSApp beginSheet:_urlhandler_win modalForWindow:self.window modalDelegate:self didEndSelector:NULL contextInfo:nil];
    } else {
        [_urlhandler_win orderOut:sender];
        [NSApp endSheet:_urlhandler_win];

        if (sender == _urlhandler_saveButton) {
            LSSetDefaultHandlerForURLScheme(CFSTR("ftp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_ftpPopup selectedItem] title]]);
            LSSetDefaultHandlerForURLScheme(CFSTR("mms"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_mmsPopup selectedItem] title]]);
            LSSetDefaultHandlerForURLScheme(CFSTR("mmsh"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_mmsPopup selectedItem] title]]);
            LSSetDefaultHandlerForURLScheme(CFSTR("rtmp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_rtmpPopup selectedItem] title]]);
            LSSetDefaultHandlerForURLScheme(CFSTR("rtp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_rtpPopup selectedItem] title]]);
            LSSetDefaultHandlerForURLScheme(CFSTR("rtsp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_rtspPopup selectedItem] title]]);
            LSSetDefaultHandlerForURLScheme(CFSTR("sftp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_sftpPopup selectedItem] title]]);
            LSSetDefaultHandlerForURLScheme(CFSTR("smb"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_smbPopup selectedItem] title]]);
            LSSetDefaultHandlerForURLScheme(CFSTR("udp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_udpPopup selectedItem] title]]);
        }
    }
}

#pragma mark -
#pragma mark Hotkey actions

- (void)hotkeyTableDoubleClick:(id)object
{
    // -1 is header
    if ([_hotkeys_listbox clickedRow] >= 0)
        [self hotkeySettingChanged:_hotkeys_listbox];
}

- (IBAction)hotkeySettingChanged:(id)sender
{
    if (sender == _hotkeys_changeButton || sender == _hotkeys_listbox) {
        [_hotkeys_changeLabel setStringValue: [NSString stringWithFormat: _NS("Press new keys for\n\"%@\""),
                                               [_hotkeyDescriptions objectAtIndex:[_hotkeys_listbox selectedRow]]]];
        [_hotkeys_change_keysLabel setStringValue: [[VLCStringUtility sharedInstance] OSXStringKeyToString:[_hotkeySettings objectAtIndex:[_hotkeys_listbox selectedRow]]]];
        [_hotkeys_change_takenLabel setStringValue: @""];
        [_hotkeys_change_win setInitialFirstResponder: [_hotkeys_change_win contentView]];
        [_hotkeys_change_win makeFirstResponder: [_hotkeys_change_win contentView]];
        [NSApp runModalForWindow:_hotkeys_change_win];
    } else if (sender == _hotkeys_change_cancelButton) {
        [NSApp stopModal];
        [_hotkeys_change_win close];
    } else if (sender == _hotkeys_change_okButton) {
        NSInteger i_returnValue;
        if (! _keyInTransition) {
            [NSApp stopModal];
            [_hotkeys_change_win close];
            msg_Err(p_intf, "internal error prevented the hotkey switch");
            return;
        }

        _hotkeyChanged = YES;

        i_returnValue = [_hotkeySettings indexOfObject: _keyInTransition];
        if (i_returnValue != NSNotFound)
            [_hotkeySettings replaceObjectAtIndex: i_returnValue withObject: [NSString string]];
        NSString *tempString;
        tempString = [_keyInTransition stringByReplacingOccurrencesOfString:@"-" withString:@"+"];
        i_returnValue = [_hotkeySettings indexOfObject: tempString];
        if (i_returnValue != NSNotFound)
            [_hotkeySettings replaceObjectAtIndex: i_returnValue withObject: [NSString string]];

        [_hotkeySettings replaceObjectAtIndex: [_hotkeys_listbox selectedRow] withObject:_keyInTransition];

        [NSApp stopModal];
        [_hotkeys_change_win close];

        [_hotkeys_listbox reloadData];
    } else if (sender == _hotkeys_clearButton) {
        [_hotkeySettings replaceObjectAtIndex: [_hotkeys_listbox selectedRow] withObject: [NSString string]];
        [_hotkeys_listbox reloadData];
        _hotkeyChanged = YES;
    }
}

- (void)showHotkeySettings
{
    [self showSettingsForCategory:_hotkeysView];
}

- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
    return [_hotkeySettings count];
}

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
    NSString * identifier = [aTableColumn identifier];

    if ([identifier isEqualToString: @"action"])
        return [_hotkeyDescriptions objectAtIndex:rowIndex];
    else if ([identifier isEqualToString: @"shortcut"])
        return [[VLCStringUtility sharedInstance] OSXStringKeyToString:[_hotkeySettings objectAtIndex:rowIndex]];
    else {
        msg_Err(p_intf, "unknown TableColumn identifier (%s)!", [identifier UTF8String]);
        return NULL;
    }
}

- (BOOL)changeHotkeyTo: (NSString *)theKey
{
    NSInteger i_returnValue, i_returnValue2;
    i_returnValue = [_hotkeysNonUseableKeys indexOfObject: theKey];

    if (i_returnValue != NSNotFound || [theKey isEqualToString:@""]) {
        [_hotkeys_change_keysLabel setStringValue: _NS("Invalid combination")];
        [_hotkeys_change_takenLabel setStringValue: _NS("Regrettably, these keys cannot be assigned as hotkey shortcuts.")];
        [_hotkeys_change_okButton setEnabled: NO];
        return NO;
    } else {
        [_hotkeys_change_keysLabel setStringValue: [[VLCStringUtility sharedInstance] OSXStringKeyToString:theKey]];

        i_returnValue = [_hotkeySettings indexOfObject: theKey];
        i_returnValue2 = [_hotkeySettings indexOfObject: [theKey stringByReplacingOccurrencesOfString:@"-" withString:@"+"]];
        if (i_returnValue != NSNotFound)
            [_hotkeys_change_takenLabel setStringValue: [NSString stringWithFormat:
                                                         _NS("This combination is already taken by \"%@\"."),
                                                         [_hotkeyDescriptions objectAtIndex:i_returnValue]]];
        else if (i_returnValue2 != NSNotFound)
            [_hotkeys_change_takenLabel setStringValue: [NSString stringWithFormat:
                                                         _NS("This combination is already taken by \"%@\"."),
                                                         [_hotkeyDescriptions objectAtIndex:i_returnValue2]]];
        else
            [_hotkeys_change_takenLabel setStringValue: @""];

        [_hotkeys_change_okButton setEnabled: YES];
        _keyInTransition = theKey;
        return YES;
    }
}

@end
