X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fmacosx%2Fsimple_prefs.m;h=ecd955a2aacd3a41adf1e606940f1b5bb58bb5d9;hb=36eff3f63a815dc59a4401ddece17cf2fc6dc7b6;hp=42ebc42c248c5bbaa15522ff6b8e0076dc0f8697;hpb=4b531b445fa95a23c25942042e1c7402b96764d6;p=vlc diff --git a/modules/gui/macosx/simple_prefs.m b/modules/gui/macosx/simple_prefs.m index 42ebc42c24..ecd955a2aa 100644 --- a/modules/gui/macosx/simple_prefs.m +++ b/modules/gui/macosx/simple_prefs.m @@ -1,7 +1,7 @@ /***************************************************************************** * simple_prefs.m: Simple Preferences for Mac OS X ***************************************************************************** -* Copyright (C) 2008 the VideoLAN team +* Copyright (C) 2008-2009 the VideoLAN team * $Id$ * * Authors: Felix Paul Kühne @@ -25,7 +25,12 @@ #import "prefs.h" #import #import +#import +#import #import "misc.h" +#import "intf.h" +#import "AppleRemote.h" +#import //for o_intf_last_update_lbl static NSString* VLCSPrefsToolbarIdentifier = @"Our Simple Preferences Toolbar Identifier"; static NSString* VLCIntfSettingToolbarIdentifier = @"Intf Settings Item Identifier"; @@ -82,9 +87,12 @@ static VLCSimplePrefs *_o_sharedInstance = nil; if( val & KEY_MODIFIER_COMMAND ) [o_temp_str appendString: [NSString stringWithUTF8String: "\xE2\x8C\x98"]]; - const char *base = KeyToString( val & ~KEY_MODIFIER ); + char *base = KeyToString( val & ~KEY_MODIFIER ); if( base ) + { [o_temp_str appendString: [NSString stringWithUTF8String: base]]; + free( base ); + } else o_temp_str = [NSMutableString stringWithString:_NS("Not Set")]; return o_temp_str; @@ -93,7 +101,7 @@ static VLCSimplePrefs *_o_sharedInstance = nil; - (void)awakeFromNib { [self initStrings]; - + /* setup the toolbar */ NSToolbar * o_sprefs_toolbar = [[[NSToolbar alloc] initWithIdentifier: VLCSPrefsToolbarIdentifier] autorelease]; [o_sprefs_toolbar setAllowsUserCustomization: NO]; @@ -102,7 +110,7 @@ static VLCSimplePrefs *_o_sharedInstance = nil; [o_sprefs_toolbar setSizeMode: NSToolbarSizeModeRegular]; [o_sprefs_toolbar setDelegate: self]; [o_sprefs_win setToolbar: o_sprefs_toolbar]; - + /* setup useful stuff */ o_hotkeysNonUseableKeys = [[NSArray arrayWithObjects: [NSNumber numberWithInt: KEY_MODIFIER_COMMAND|'c'], @@ -162,8 +170,8 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des return o_toolbarItem; } -- (NSToolbarItem *) toolbar: (NSToolbar *)o_sprefs_toolbar - itemForItemIdentifier: (NSString *)o_itemIdent +- (NSToolbarItem *) toolbar: (NSToolbar *)o_sprefs_toolbar + itemForItemIdentifier: (NSString *)o_itemIdent willBeInsertedIntoToolbar: (BOOL)b_willBeInserted { NSToolbarItem *o_toolbarItem = nil; @@ -198,19 +206,19 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar { - return [NSArray arrayWithObjects: VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier, + return [NSArray arrayWithObjects: VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier, VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, NSToolbarFlexibleSpaceItemIdentifier, nil]; } - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar { - return [NSArray arrayWithObjects: VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier, + return [NSArray arrayWithObjects: VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier, VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, NSToolbarFlexibleSpaceItemIdentifier, nil]; } - (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar { - return [NSArray arrayWithObjects: VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier, + return [NSArray arrayWithObjects: VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier, VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, nil]; } @@ -242,22 +250,18 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des [[[o_hotkeys_listbox tableColumnWithIdentifier: @"shortcut"] headerCell] setStringValue: _NS("Shortcut")]; /* input */ - [o_input_access_box setTitle: _NS("Access Filter")]; [o_input_avi_txt setStringValue: _NS("Repair AVI Files")]; - [o_input_bandwidth_ckb setTitle: _NS("Bandwidth limiter")]; [o_input_cachelevel_txt setStringValue: _NS("Default Caching Level")]; [o_input_caching_box setTitle: _NS("Caching")]; [o_input_cachelevel_custom_txt setStringValue: _NS("Use the complete preferences to configure custom caching values for each access module.")]; - [o_input_dump_ckb setTitle: _NS("Dump")]; [o_input_httpproxy_txt setStringValue: _NS("HTTP Proxy")]; [o_input_httpproxypwd_txt setStringValue: _NS("Password for HTTP Proxy")]; [o_input_mux_box setTitle: _NS("Codecs / Muxers")]; [o_input_net_box setTitle: _NS("Network")]; [o_input_postproc_txt setStringValue: _NS("Post-Processing Quality")]; - [o_input_record_ckb setTitle: _NS("Record")]; [o_input_rtsp_ckb setTitle: _NS("Use RTP over RTSP (TCP)")]; + [o_input_skipLoop_txt setStringValue: _NS("Skip the loop filter for H.264 decoding")]; [o_input_serverport_txt setStringValue: _NS("Default Server Port")]; - [o_input_timeshift_ckb setTitle: _NS("Timeshift")]; /* interface */ [o_intf_art_txt setStringValue: _NS("Album art download policy")]; @@ -265,7 +269,12 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des [o_intf_fspanel_ckb setTitle: _NS("Show Fullscreen Controller")]; [o_intf_lang_txt setStringValue: _NS("Language")]; [o_intf_network_box setTitle: _NS("Privacy / Network Interaction")]; - + [o_intf_appleremote_ckb setTitle: _NS("Control playback with the Apple Remote")]; + [o_intf_mediakeys_ckb setTitle: _NS("Control playback with media keys")]; + [o_intf_mediakeys_bg_ckb setTitle: _NS("...when VLC is in background")]; + [o_intf_update_ckb setTitle: _NS("Automatically check for updates")]; + [o_intf_last_update_lbl setStringValue: @""]; + /* Subtitles and OSD */ [o_osd_encoding_txt setStringValue: _NS("Default Encoding")]; [o_osd_font_box setTitle: _NS("Display Settings")]; @@ -293,7 +302,7 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des [o_video_snap_format_txt setStringValue: _NS("Format")]; [o_video_snap_prefix_txt setStringValue: _NS("Prefix")]; [o_video_snap_seqnum_ckb setTitle: _NS("Sequential numbering")]; - + /* generic stuff */ [[o_sprefs_basicFull_matrix cellAtRow: 0 column: 0] setStringValue: _NS("Basic")]; [[o_sprefs_basicFull_matrix cellAtRow: 0 column: 1] setStringValue: _NS("All")]; @@ -303,6 +312,31 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des [o_sprefs_win setTitle: _NS("Preferences")]; } +/* TODO: move this part to core */ +#define config_GetLabel(a,b) __config_GetLabel(VLC_OBJECT(a),b) +static inline char * __config_GetLabel( vlc_object_t *p_this, const char *psz_name ) +{ + module_config_t *p_config; + + p_config = config_FindConfig( p_this, 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; +} + - (void)setupButton: (NSPopUpButton *)object forStringList: (const char *)name { module_config_t *p_item; @@ -318,14 +352,15 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des NSMenuItem *mi; if( p_item->ppsz_list_text != NULL ) mi = [[NSMenuItem alloc] initWithTitle: _NS( p_item->ppsz_list_text[i] ) action:NULL keyEquivalent: @""]; - else if( p_item->ppsz_list[i] && p_item->ppsz_list[i] == "" ) + else if( p_item->ppsz_list[i] && strcmp(p_item->ppsz_list[i],"") == 0 ) { [[object menu] addItem: [NSMenuItem separatorItem]]; continue; } else if( p_item->ppsz_list[i] ) mi = [[NSMenuItem alloc] initWithTitle: [NSString stringWithUTF8String: p_item->ppsz_list[i]] action:NULL keyEquivalent: @""]; - else NSLog( @"item %d of pref %s failed to be created", i, name); + else + msg_Err( p_intf, "item %d of pref %s failed to be created", i, name ); [mi setRepresentedObject:[NSString stringWithUTF8String: p_item->ppsz_list[i]]]; [[object menu] addItem: [mi autorelease]]; if( p_item->value.psz && !strcmp( p_item->value.psz, p_item->ppsz_list[i] ) ) @@ -351,7 +386,8 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des mi = [[NSMenuItem alloc] initWithTitle: _NS( p_item->ppsz_list_text[i] ) action:NULL keyEquivalent: @""]; else if( p_item->pi_list[i] ) mi = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"%d", p_item->pi_list[i]] action:NULL keyEquivalent: @""]; - else NSLog( @"item %d of pref %s failed to be created", i, name); + else + msg_Err( p_intf, "item %d of pref %s failed to be created", i, name); [mi setRepresentedObject:[NSNumber numberWithInt: p_item->pi_list[i]]]; [[object menu] addItem: [mi autorelease]]; if( p_item->value.i == p_item->pi_list[i] ) @@ -365,15 +401,15 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des module_config_t *p_item; module_t *p_parser, **p_list; int y = 0; - + [object removeAllItems]; - + p_item = config_FindConfig( VLC_OBJECT(p_intf), name ); p_list = module_list_get( NULL ); if( !p_item ||!p_list ) { - if( p_list ) vlc_list_release(p_list); - NSLog( @"serious problem, item or list not found" ); + if( p_list ) module_list_free(p_list); + msg_Err( p_intf, "serious problem, item or list not found" ); return; } @@ -392,6 +428,20 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des [object setToolTip: _NS(p_item->psz_longtext)]; } +- (void)setupButton: (NSButton *)object forBoolValue: (const char *)name +{ + [object setState: config_GetInt( p_intf, name )]; + [object setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, name )]]; +} + +- (void)setupField:(NSTextField *)o_object forOption:(const char *)psz_option +{ + char *psz_tmp = config_GetPsz( p_intf, psz_option ); + [o_object setStringValue: [NSString stringWithUTF8String: psz_tmp ?: ""]]; + [o_object setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, psz_option )]]; + free( psz_tmp ); +} + - (void)resetControls { module_config_t *p_item; @@ -400,47 +450,59 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des [[o_sprefs_basicFull_matrix cellAtRow:0 column:0] setState: NSOnState]; [[o_sprefs_basicFull_matrix cellAtRow:0 column:1] setState: NSOffState]; - + /********************** * interface settings * **********************/ [self setupButton: o_intf_lang_pop forStringList: "language"]; [self setupButton: o_intf_art_pop forIntList: "album-art"]; - [o_intf_fspanel_ckb setState: config_GetInt( p_intf, "macosx-fspanel" )]; - [o_intf_embedded_ckb setState: config_GetInt( p_intf, "embedded-video" )]; + [self setupButton: o_intf_fspanel_ckb forBoolValue: "macosx-fspanel"]; + [self setupButton: o_intf_embedded_ckb forBoolValue: "embedded-video"]; + [self setupButton: o_intf_appleremote_ckb forBoolValue: "macosx-appleremote"]; + [self setupButton: o_intf_mediakeys_ckb forBoolValue: "macosx-mediakeys"]; + [self setupButton: o_intf_mediakeys_bg_ckb forBoolValue: "macosx-mediakeys-background"]; + [o_intf_mediakeys_bg_ckb setEnabled: [o_intf_mediakeys_ckb state]]; + if( [[SUUpdater sharedUpdater] lastUpdateCheckDate] != NULL ) + [o_intf_last_update_lbl setStringValue: [NSString stringWithFormat: _NS("Last check on: %@"), [[[SUUpdater sharedUpdater] lastUpdateCheckDate] descriptionWithLocale: [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]]]]; + else + [o_intf_last_update_lbl setStringValue: _NS("No check was performed yet.")]; /****************** * audio settings * ******************/ - [o_audio_enable_ckb setState: config_GetInt( p_intf, "audio" )]; - [o_audio_vol_fld setIntValue: config_GetInt( p_intf, "volume" )]; - [o_audio_vol_sld setIntValue: config_GetInt( p_intf, "volume" )]; + [self setupButton: o_audio_enable_ckb forBoolValue: "audio"]; + i = (config_GetInt( p_intf, "volume" ) * 0.390625); + [o_audio_vol_fld setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, "volume")]]; + [o_audio_vol_fld setIntValue: i]; + [o_audio_vol_sld setToolTip: [o_audio_vol_fld toolTip]]; + [o_audio_vol_sld setIntValue: i]; - [o_audio_spdif_ckb setState: config_GetInt( p_intf, "spdif" )]; + [self setupButton: o_audio_spdif_ckb forBoolValue: "spdif"]; [self setupButton: o_audio_dolby_pop forIntList: "force-dolby-surround"]; + [self setupField: o_audio_lang_fld forOption: "audio-language"]; - [o_audio_lang_fld setStringValue: [NSString stringWithUTF8String: config_GetPsz( p_intf, "audio-language" ) ?: ""]]; + [self setupButton: o_audio_headphone_ckb forBoolValue: "headphone-dolby"]; - [o_audio_headphone_ckb setState: config_GetInt( p_intf, "headphone-dolby" )]; - psz_tmp = config_GetPsz( p_intf, "audio-filter" ); if( psz_tmp ) { - [o_audio_norm_ckb setState: (int)strstr( psz_tmp, "volnorm" )]; + [o_audio_norm_ckb setState: (NSInteger)strstr( psz_tmp, "volnorm" )]; [o_audio_norm_fld setEnabled: [o_audio_norm_ckb state]]; [o_audio_norm_stepper setEnabled: [o_audio_norm_ckb state]]; + free( psz_tmp ); } [o_audio_norm_fld setFloatValue: config_GetFloat( p_intf, "norm-max-level" )]; + [o_audio_norm_fld setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, "norm-max-level")]]; [self setupButton: o_audio_visual_pop forModuleList: "audio-visual"]; /* Last.FM is optional */ if( module_exists( "audioscrobbler" ) ) { - [o_audio_lastuser_fld setStringValue: [NSString stringWithUTF8String: config_GetPsz( p_intf, "lastfm-username" ) ?: ""]]; - [o_audio_lastpwd_sfld setStringValue: [NSString stringWithUTF8String: config_GetPsz( p_intf, "lastfm-password" ) ?: ""]]; + [self setupField: o_audio_lastuser_fld forOption:"lastfm-username"]; + [self setupField: o_audio_lastpwd_sfld forOption:"lastfm-password"]; if( config_ExistIntf( VLC_OBJECT( p_intf ), "audioscrobbler" ) ) { @@ -461,11 +523,11 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des /****************** * video settings * ******************/ - [o_video_enable_ckb setState: config_GetInt( p_intf, "video" )]; - [o_video_fullscreen_ckb setState: config_GetInt( p_intf, "fullscreen" )]; - [o_video_onTop_ckb setState: config_GetInt( p_intf, "video-on-top" )]; - [o_video_skipFrames_ckb setState: config_GetInt( p_intf, "skip-frames" )]; - [o_video_black_ckb setState: config_GetInt( p_intf, "macosx-black" )]; + [self setupButton: o_video_enable_ckb forBoolValue: "video"]; + [self setupButton: o_video_fullscreen_ckb forBoolValue: "fullscreen"]; + [self setupButton: o_video_onTop_ckb forBoolValue: "video-on-top"]; + [self setupButton: o_video_skipFrames_ckb forBoolValue: "skip-frames"]; + [self setupButton: o_video_black_ckb forBoolValue: "macosx-black"]; [self setupButton: o_video_output_pop forModuleList: "vout"]; @@ -477,7 +539,7 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des while( i < y ) { NSRect s_rect = [[[NSScreen screens] objectAtIndex: i] frame]; - [o_video_device_pop addItemWithTitle: + [o_video_device_pop addItemWithTitle: [NSString stringWithFormat: @"%@ %i (%ix%i)", _NS("Screen"), i+1, (int)s_rect.size.width, (int)s_rect.size.height]]; [[o_video_device_pop lastItem] setTag: (int)[[[NSScreen screens] objectAtIndex: i] displayID]]; @@ -486,36 +548,28 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des [o_video_device_pop selectItemAtIndex: 0]; [o_video_device_pop selectItemWithTag: config_GetInt( p_intf, "macosx-vdev" )]; - [o_video_snap_folder_fld setStringValue: [NSString stringWithUTF8String: config_GetPsz( p_intf, "snapshot-path" ) ?: ""]]; - [o_video_snap_prefix_fld setStringValue: [NSString stringWithUTF8String: config_GetPsz( p_intf, "snapshot-prefix" ) ?: ""]]; - [o_video_snap_seqnum_ckb setState: config_GetInt( p_intf, "snapshot-sequential" )]; + [self setupField: o_video_snap_folder_fld forOption:"snapshot-path"]; + [self setupField: o_video_snap_prefix_fld forOption:"snapshot-prefix"]; + [self setupButton: o_video_snap_seqnum_ckb forBoolValue: "snapshot-sequential"]; [self setupButton: o_video_snap_format_pop forStringList: "snapshot-format"]; /*************************** * input & codecs settings * ***************************/ - [o_input_serverport_fld setIntValue: config_GetInt( p_intf, "server-port" )]; - if( config_GetPsz( p_intf, "http-proxy" ) != NULL ) - [o_input_httpproxy_fld setStringValue: [NSString stringWithUTF8String: config_GetPsz( p_intf, "http-proxy" ) ?: ""]]; - if( config_GetPsz( p_intf, "http-proxy" ) != NULL ) - [o_input_httpproxypwd_sfld setStringValue: [NSString stringWithUTF8String: config_GetPsz( p_intf, "http-proxy-pwd" ) ?: ""]]; - [o_input_postproc_fld setIntValue: config_GetInt( p_intf, "ffmpeg-pp-q" )]; + [o_input_serverport_fld setIntValue: config_GetInt( p_intf, "server-port")]; + [o_input_serverport_fld setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, "server-port")]]; + [self setupField: o_input_httpproxy_fld forOption:"http-proxy"]; + [self setupField: o_input_httpproxypwd_sfld forOption:"http-proxy-pwd"]; + [o_input_postproc_fld setIntValue: config_GetInt( p_intf, "postproc-q")]; + [o_input_postproc_fld setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, "postproc-q")]]; [self setupButton: o_input_avi_pop forIntList: "avi-index"]; - [o_input_rtsp_ckb setState: config_GetInt( p_intf, "rtsp-tcp" )]; - - psz_tmp = config_GetPsz( p_intf, "access-filter" ); - if( psz_tmp ) - { - [o_input_record_ckb setState: (int)strstr( psz_tmp, "record" )]; - [o_input_dump_ckb setState: (int)strstr( psz_tmp, "dump" )]; - [o_input_bandwidth_ckb setState: (int)strstr( psz_tmp, "bandwidth" )]; - [o_input_timeshift_ckb setState: (int)strstr( psz_tmp, "timeshift" )]; - } + [self setupButton: o_input_rtsp_ckb forBoolValue: "rtsp-tcp"]; + [self setupButton: o_input_skipLoop_pop forIntList: "ffmpeg-skiploopfilter"]; [o_input_cachelevel_pop removeAllItems]; - [o_input_cachelevel_pop addItemsWithTitles: + [o_input_cachelevel_pop addItemsWithTitles: [NSArray arrayWithObjects: _NS("Custom"), _NS("Lowest latency"), _NS("Low latency"), _NS("Normal"), _NS("High latency"), _NS("Higher latency"), nil]]; [[o_input_cachelevel_pop itemAtIndex: 0] setTag: 0]; @@ -524,7 +578,7 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des [[o_input_cachelevel_pop itemAtIndex: 3] setTag: 300]; [[o_input_cachelevel_pop itemAtIndex: 4] setTag: 400]; [[o_input_cachelevel_pop itemAtIndex: 5] setTag: 500]; - + #define TestCaC( name ) \ b_cache_equal = b_cache_equal && \ ( i_cache == config_GetInt( p_intf, name ) ) @@ -536,14 +590,13 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des /* Select the accurate value of the PopupButton */ bool b_cache_equal = true; int i_cache = config_GetInt( p_intf, "file-caching"); - + TestCaC( "udp-caching" ); if( module_exists ("dvdread") ) TestCaC( "dvdread-caching" ); if( module_exists ("dvdnav") ) TestCaC( "dvdnav-caching" ); TestCaC( "tcp-caching" ); - TestCaC( "fake-caching" ); TestCaC( "cdda-caching" ); TestCaC( "screen-caching" ); TestCaC( "vcd-caching" ); @@ -567,21 +620,33 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des /********************* * subtitle settings * *********************/ - [o_osd_osd_ckb setState: config_GetInt( p_intf, "osd" )]; - + [self setupButton: o_osd_osd_ckb forBoolValue: "osd"]; + [self setupButton: o_osd_encoding_pop forStringList: "subsdec-encoding"]; - - [o_osd_lang_fld setStringValue: [NSString stringWithUTF8String: config_GetPsz( p_intf, "sub-language" ) ?: ""]]; - if( config_GetPsz( p_intf, "quartztext-font" ) != NULL ) - [o_osd_font_fld setStringValue: [NSString stringWithUTF8String: config_GetPsz( p_intf, "quartztext-font" ) ?: ""]]; + [self setupField: o_osd_lang_fld forOption: "sub-language" ]; + + if( module_exists( "quartztext" ) ) + { + [self setupField: o_osd_font_fld forOption: "quartztext-font"]; + [self setupButton: o_osd_font_color_pop forIntList: "quartztext-color"]; + [self setupButton: o_osd_font_size_pop forIntList: "quartztext-rel-fontsize"]; + } + else + { + /* fallback on freetype */ + [self setupField: o_osd_font_fld forOption: "freetype-font"]; + [self setupButton: o_osd_font_color_pop forIntList: "freetype-color"]; + [self setupButton: o_osd_font_size_pop forIntList: "freetype-rel-fontsize"]; + /* selector button is useless in this case */ + [o_osd_font_btn setEnabled: NO]; + } - [self setupButton: o_osd_font_color_pop forIntList: "quartztext-color"]; - [self setupButton: o_osd_font_size_pop forIntList: "quartztext-rel-fontsize"]; /******************** * hotkeys settings * ********************/ - struct hotkey *p_hotkeys = p_intf->p_libvlc->p_hotkeys; + const struct hotkey *p_hotkeys = p_intf->p_libvlc->p_hotkeys; + [o_hotkeySettings release]; o_hotkeySettings = [[NSMutableArray alloc] init]; NSMutableArray *o_tempArray_desc = [[NSMutableArray alloc] init]; i = 1; @@ -597,6 +662,7 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des i++; } + [o_hotkeyDescriptions release]; o_hotkeyDescriptions = [[NSArray alloc] initWithArray: o_tempArray_desc copyItems: YES]; [o_tempArray_desc release]; [o_hotkeys_listbox reloadData]; @@ -610,7 +676,7 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des [[o_sprefs_win toolbar] setSelectedItemIdentifier: VLCIntfSettingToolbarIdentifier]; [self showInterfaceSettings]; } - + [self resetControls]; [o_sprefs_win center]; @@ -637,13 +703,13 @@ create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_des [o_sprefs_win orderOut: self]; [[o_sprefs_basicFull_matrix cellAtRow:0 column:0] setState: NSOffState]; [[o_sprefs_basicFull_matrix cellAtRow:0 column:1] setState: NSOnState]; - [[[VLCMain sharedInstance] getPreferences] showPrefs]; + [[[VLCMain sharedInstance] preferences] showPrefs]; } else msg_Warn( p_intf, "unknown buttonAction sender" ); } -- (void)sheetDidEnd:(NSWindow *)o_sheet +- (void)sheetDidEnd:(NSWindow *)o_sheet returnCode:(int)i_return contextInfo:(void *)o_context { @@ -672,7 +738,10 @@ static inline void save_string_list( intf_thread_t * p_intf, id object, const ch p_item = config_FindConfig( VLC_OBJECT(p_intf), name ); p_stringobject = (NSString *)[[object selectedItem] representedObject]; assert([p_stringobject isKindOfClass:[NSString class]]); - if( p_stringobject ) config_PutPsz( p_intf, name, [p_stringobject UTF8String] ); + if( p_stringobject ) + { + config_PutPsz( p_intf, name, [p_stringobject UTF8String] ); + } } static inline void save_module_list( intf_thread_t * p_intf, id object, const char * name ) @@ -705,10 +774,9 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch { char *psz_tmp; int i; - NSString *p_stringobject; - + #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 ) save_module_list( p_intf, object, name ) @@ -723,6 +791,18 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch config_PutInt( p_intf, "macosx-fspanel", [o_intf_fspanel_ckb state] ); config_PutInt( p_intf, "embedded-video", [o_intf_embedded_ckb state] ); + config_PutInt( p_intf, "macosx-appleremote", [o_intf_appleremote_ckb state] ); + config_PutInt( p_intf, "macosx-mediakeys", [o_intf_mediakeys_ckb state] ); + config_PutInt( p_intf, "macosx-mediakeys-background", [o_intf_mediakeys_bg_ckb state] ); + + /* activate stuff without restart */ + if( [o_intf_appleremote_ckb state] == YES ) + [[[VLCMain sharedInstance] appleRemoteController] startListening: [VLCMain sharedInstance]]; + else + [[[VLCMain sharedInstance] appleRemoteController] stopListening: [VLCMain sharedInstance]]; + [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged" + object: nil + userInfo: nil]; /* okay, let's save our changes to vlcrc */ i = config_SaveConfigFile( p_intf, "main" ); @@ -731,21 +811,21 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch if( i != 0 ) { msg_Err( p_intf, "An error occurred while saving the Interface settings using SimplePrefs (%i)", i ); - intf_UserFatal( p_intf, false, _("Interface Settings not saved"), + dialog_Fatal( p_intf, _("Interface Settings not saved"), _("An error occured while saving your settings via SimplePrefs (%i)."), i ); i = 0; } b_intfSettingChanged = NO; } - + /****************** * audio settings * ******************/ if( b_audioSettingChanged ) { config_PutInt( p_intf, "audio", [o_audio_enable_ckb state] ); - config_PutInt( p_intf, "volume", [o_audio_vol_sld intValue] ); + config_PutInt( p_intf, "volume", ([o_audio_vol_sld intValue] * 2.56)); config_PutInt( p_intf, "spdif", [o_audio_spdif_ckb state] ); SaveIntList( o_audio_dolby_pop, "force-dolby-surround" ); @@ -758,11 +838,12 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch psz_tmp = config_GetPsz( p_intf, "audio-filter" ); if(! psz_tmp) config_PutPsz( p_intf, "audio-filter", "volnorm" ); - else if( (int)strstr( psz_tmp, "normvol" ) == NO ) + else if( (NSInteger)strstr( psz_tmp, "normvol" ) == NO ) { /* work-around a GCC 4.0.1 bug */ psz_tmp = (char *)[[NSString stringWithFormat: @"%s:volnorm", psz_tmp] UTF8String]; config_PutPsz( p_intf, "audio-filter", psz_tmp ); + free( psz_tmp ); } } else @@ -770,10 +851,11 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch psz_tmp = config_GetPsz( p_intf, "audio-filter" ); if( psz_tmp ) { - psz_tmp = (char *)[[[NSString stringWithUTF8String: psz_tmp] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@":volnorm"]] UTF8String]; - psz_tmp = (char *)[[[NSString stringWithUTF8String: psz_tmp] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@"volnorm:"]] UTF8String]; - psz_tmp = (char *)[[[NSString stringWithUTF8String: psz_tmp] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@"volnorm"]] UTF8String]; + char *psz_tmp2 = (char *)[[[NSString stringWithUTF8String: psz_tmp] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@":volnorm"]] UTF8String]; + psz_tmp2 = (char *)[[[NSString stringWithUTF8String: psz_tmp2] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@"volnorm:"]] UTF8String]; + psz_tmp2 = (char *)[[[NSString stringWithUTF8String: psz_tmp2] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@"volnorm"]] UTF8String]; config_PutPsz( p_intf, "audio-filter", psz_tmp ); + free( psz_tmp ); } } config_PutFloat( p_intf, "norm-max-level", [o_audio_norm_fld floatValue] ); @@ -782,7 +864,7 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch /* Last.FM is optional */ if( module_exists( "audioscrobbler" ) ) - { + { [o_audio_last_ckb setEnabled: YES]; if( [o_audio_last_ckb state] == NSOnState ) config_AddIntf( p_intf, "audioscrobbler" ); @@ -803,14 +885,14 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch if( i != 0 ) { msg_Err( p_intf, "An error occurred while saving the Audio settings using SimplePrefs (%i)", i ); - intf_UserFatal( p_intf, false, _("Audio Settings not saved"), + dialog_Fatal( p_intf, _("Audio Settings not saved"), _("An error occured while saving your settings via SimplePrefs (%i)."), i ); - + i = 0; } b_audioSettingChanged = NO; } - + /****************** * video settings * ******************/ @@ -836,13 +918,13 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch if( i != 0 ) { msg_Err( p_intf, "An error occurred while saving the Video settings using SimplePrefs (%i)", i ); - intf_UserFatal( p_intf, false, _("Video Settings not saved"), + dialog_Fatal( p_intf, _("Video Settings not saved"), _("An error occured while saving your settings via SimplePrefs (%i)."), i ); i = 0; } b_videoSettingChanged = NO; } - + /*************************** * input & codecs settings * ***************************/ @@ -851,15 +933,16 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch config_PutInt( p_intf, "server-port", [o_input_serverport_fld intValue] ); config_PutPsz( p_intf, "http-proxy", [[o_input_httpproxy_fld stringValue] UTF8String] ); config_PutPsz( p_intf, "http-proxy-pwd", [[o_input_httpproxypwd_sfld stringValue] UTF8String] ); - config_PutInt( p_intf, "ffmpeg-pp-q", [o_input_postproc_fld intValue] ); + config_PutInt( p_intf, "postproc-q", [o_input_postproc_fld intValue] ); SaveIntList( o_input_avi_pop, "avi-index" ); config_PutInt( p_intf, "rtsp-tcp", [o_input_rtsp_ckb state] ); + SaveIntList( o_input_skipLoop_pop, "ffmpeg-skiploopfilter" ); #define CaCi( name, int ) config_PutInt( p_intf, name, int * [[o_input_cachelevel_pop selectedItem] tag] ) #define CaC( name ) CaCi( name, 1 ) - msg_Dbg( p_intf, "Adjusting all cache values to: %i", [[o_input_cachelevel_pop selectedItem] tag] ); + msg_Dbg( p_intf, "Adjusting all cache values to: %i", (int)[[o_input_cachelevel_pop selectedItem] tag] ); CaC( "udp-caching" ); if( module_exists ( "dvdread" ) ) { @@ -872,7 +955,7 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch i = i + config_SaveConfigFile( p_intf, "dvdnav" ); } CaC( "tcp-caching" ); CaC( "vcd-caching" ); - CaC( "fake-caching" ); CaC( "cdda-caching" ); CaC( "file-caching" ); + CaC( "cdda-caching" ); CaC( "file-caching" ); CaC( "screen-caching" ); CaCi( "rtsp-caching", 4 ); CaCi( "ftp-caching", 2 ); CaCi( "http-caching", 4 ); @@ -883,33 +966,12 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch } CaCi( "mms-caching", 19 ); - #define SaveAccessFilter( object, name ) \ - if( [object state] == NSOnState ) \ - { \ - if( b_first ) \ - { \ - [o_temp appendString: name]; \ - b_first = NO; \ - } \ - else \ - [o_temp appendFormat: @":%@", name]; \ - } - - BOOL b_first = YES; - NSMutableString *o_temp = [[NSMutableString alloc] init]; - SaveAccessFilter( o_input_record_ckb, @"record" ); - SaveAccessFilter( o_input_dump_ckb, @"dump" ); - SaveAccessFilter( o_input_bandwidth_ckb, @"bandwidth" ); - SaveAccessFilter( o_input_timeshift_ckb, @"timeshift" ); - config_PutPsz( p_intf, "access-filter", [o_temp UTF8String] ); - [o_temp release]; - i = config_SaveConfigFile( p_intf, "main" ); - i = i + config_SaveConfigFile( p_intf, "ffmpeg" ); + i = i + config_SaveConfigFile( p_intf, "avcodec" ); + i = i + config_SaveConfigFile( p_intf, "postproc" ); i = i + config_SaveConfigFile( p_intf, "access_http" ); i = i + config_SaveConfigFile( p_intf, "access_file" ); i = i + config_SaveConfigFile( p_intf, "access_tcp" ); - i = i + config_SaveConfigFile( p_intf, "access_fake" ); i = i + config_SaveConfigFile( p_intf, "cdda" ); i = i + config_SaveConfigFile( p_intf, "screen" ); i = i + config_SaveConfigFile( p_intf, "vcd" ); @@ -921,13 +983,13 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch if( i != 0 ) { msg_Err( p_intf, "An error occurred while saving the Input settings using SimplePrefs (%i)", i ); - intf_UserFatal( p_intf, false, _("Input Settings not saved"), + dialog_Fatal( p_intf, _("Input Settings not saved"), _("An error occured while saving your settings via SimplePrefs (%i)."), i ); i = 0; } b_inputSettingChanged = NO; } - + /********************** * subtitles settings * **********************/ @@ -936,45 +998,57 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch config_PutInt( p_intf, "osd", [o_osd_osd_ckb state] ); if( [o_osd_encoding_pop indexOfSelectedItem] >= 0 ) - config_PutPsz( p_intf, "subsdec-encoding", [[[o_osd_encoding_pop selectedItem] title] UTF8String] ); + SaveStringList( o_osd_encoding_pop, "subsdec-encoding" ); + else + config_PutPsz( p_intf, "subsdec-encoding", "" ); config_PutPsz( p_intf, "sub-language", [[o_osd_lang_fld stringValue] UTF8String] ); - config_PutPsz( p_intf, "quartztext-font", [[o_osd_font_fld stringValue] UTF8String] ); - SaveIntList( o_osd_font_color_pop, "quartztext-color" ); - SaveIntList( o_osd_font_size_pop, "quartztext-rel-fontsize" ); + if( module_exists( "quartztext" ) ) + { + config_PutPsz( p_intf, "quartztext-font", [[o_osd_font_fld stringValue] UTF8String] ); + SaveIntList( o_osd_font_color_pop, "quartztext-color" ); + SaveIntList( o_osd_font_size_pop, "quartztext-rel-fontsize" ); + } + else + { + /* fallback on freetype */ + config_PutPsz( p_intf, "freetype-font", [[o_osd_font_fld stringValue] UTF8String] ); + SaveIntList( o_osd_font_color_pop, "freetype-color" ); + SaveIntList( o_osd_font_size_pop, "freetype-rel-fontsize" ); + } i = config_SaveConfigFile( p_intf, NULL ); if( i != 0 ) { msg_Err( p_intf, "An error occurred while saving the OSD/Subtitle settings using SimplePrefs (%i)", i ); - intf_UserFatal( p_intf, false, _("On Screen Display/Subtitle Settings not saved"), + dialog_Fatal( p_intf, _("On Screen Display/Subtitle Settings not saved"), _("An error occured while saving your settings via SimplePrefs (%i)."), i ); i = 0; } b_osdSettingChanged = NO; } - + /******************** * hotkeys settings * ********************/ if( b_hotkeyChanged ) { - struct hotkey *p_hotkeys = p_intf->p_libvlc->p_hotkeys; + const struct hotkey *p_hotkeys = p_intf->p_libvlc->p_hotkeys; i = 1; while( i < [o_hotkeySettings count] ) { config_PutInt( p_intf, p_hotkeys[i].psz_action, [[o_hotkeySettings objectAtIndex: i-1] intValue] ); i++; - } + } i = config_SaveConfigFile( p_intf, "main" ); if( i != 0 ) { msg_Err( p_intf, "An error occurred while saving the Hotkey settings using SimplePrefs (%i)", i ); - intf_UserFatal( p_intf, false, _("Hotkeys not saved"), + dialog_Fatal( p_intf, _("Hotkeys not saved"), _("An error occured while saving your settings via SimplePrefs (%i)."), i ); i = 0; } @@ -987,7 +1061,7 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch NSRect o_win_rect, o_view_rect, o_old_view_rect; o_win_rect = [o_sprefs_win frame]; o_view_rect = [o_new_category_view frame]; - + if( o_currentlyShownCategoryView != nil ) { /* restore our window's height, if we've shown another category previously */ @@ -998,20 +1072,20 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch /* remove our previous category view */ [o_currentlyShownCategoryView removeFromSuperviewWithoutNeedingDisplay]; } - + o_win_rect.size.height = o_win_rect.size.height + o_view_rect.size.height; - + [o_sprefs_win displayIfNeeded]; [o_sprefs_win setFrame: o_win_rect display:YES animate: YES]; - - [o_new_category_view setFrame: NSMakeRect( 0, - [o_sprefs_controls_box frame].size.height, - o_view_rect.size.width, + + [o_new_category_view setFrame: NSMakeRect( 0, + [o_sprefs_controls_box frame].size.height, + o_view_rect.size.width, o_view_rect.size.height )]; [o_new_category_view setNeedsDisplay: YES]; [o_new_category_view setAutoresizesSubviews: YES]; [[o_sprefs_win contentView] addSubview: o_new_category_view]; - + /* keep our current category for further reference */ [o_currentlyShownCategoryView release]; o_currentlyShownCategoryView = o_new_category_view; @@ -1020,6 +1094,8 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch - (IBAction)interfaceSettingChanged:(id)sender { + if( sender == o_intf_mediakeys_ckb ) + [o_intf_mediakeys_bg_ckb setEnabled: [o_intf_mediakeys_ckb state]]; b_intfSettingChanged = YES; } @@ -1040,8 +1116,8 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch { [o_audio_norm_stepper setEnabled: [o_audio_norm_ckb state]]; [o_audio_norm_fld setEnabled: [o_audio_norm_ckb state]]; - } - + } + if( sender == o_audio_last_ckb ) { if( [o_audio_last_ckb state] == NSOnState ) @@ -1076,8 +1152,8 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch [o_selectFolderPanel setMessage: _NS("Choose the folder to save your video snapshots to.")]; [o_selectFolderPanel setCanCreateDirectories: YES]; [o_selectFolderPanel setPrompt: _NS("Choose")]; - [o_selectFolderPanel beginSheetForDirectory: nil file: nil modalForWindow: o_sprefs_win - modalDelegate: self + [o_selectFolderPanel beginSheetForDirectory: nil file: nil modalForWindow: o_sprefs_win + modalDelegate: self didEndSelector: @selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo: o_video_snap_folder_btn]; } @@ -1094,11 +1170,6 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch [o_video_snap_folder_fld setStringValue: [o_selectFolderPanel filename]]; b_videoSettingChanged = YES; } - else if( contextInfo == o_osd_font_btn ) - { - [o_osd_font_fld setStringValue: [o_selectFolderPanel filename]]; - b_osdSettingChanged = YES; - } } [o_selectFolderPanel release]; @@ -1121,18 +1192,20 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch - (IBAction)showFontPicker:(id)sender { - char * font = config_GetPsz( p_intf, "quartztext-font" ); - NSString * fontFamilyName = font ? [NSString stringWithUTF8String: font] : nil; - free(font); - if( fontFamilyName ) - { - NSFontDescriptor * fd = [NSFontDescriptor fontDescriptorWithFontAttributes:nil]; - NSFont * font = [NSFont fontWithDescriptor:[fd fontDescriptorWithFamily:fontFamilyName] textTransform:nil]; - [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:NO]; - } - [[NSFontManager sharedFontManager] setDelegate: self]; - [o_sprefs_win makeFirstResponder: o_sprefs_win]; - [[NSFontPanel sharedFontPanel] orderFront:self]; + if( module_exists( "quartztext" ) ) + { + char * font = config_GetPsz( p_intf, "quartztext-font" ); + NSString * fontFamilyName = font ? [NSString stringWithUTF8String: font] : nil; + free(font); + if( fontFamilyName ) + { + NSFontDescriptor * fd = [NSFontDescriptor fontDescriptorWithFontAttributes:nil]; + NSFont * font = [NSFont fontWithDescriptor:[fd fontDescriptorWithFamily:fontFamilyName] textTransform:nil]; + [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:NO]; + } + [[NSFontManager sharedFontManager] setTarget: self]; + [[NSFontPanel sharedFontPanel] orderFront:self]; + } } - (void)changeFont:(id)sender @@ -1164,7 +1237,7 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch { if( sender == o_hotkeys_change_btn || sender == o_hotkeys_listbox ) { - [o_hotkeys_change_lbl setStringValue: [NSString stringWithFormat: _NS("Press new keys for\n\"%@\""), + [o_hotkeys_change_lbl setStringValue: [NSString stringWithFormat: _NS("Press new keys for\n\"%@\""), [o_hotkeyDescriptions objectAtIndex: [o_hotkeys_listbox selectedRow]]]]; [o_hotkeys_change_keys_lbl setStringValue: [self OSXKeyToString:[[o_hotkeySettings objectAtIndex: [o_hotkeys_listbox selectedRow]] intValue]]]; [o_hotkeys_change_taken_lbl setStringValue: @""]; @@ -1179,7 +1252,7 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch } else if( sender == o_hotkeys_change_ok_btn ) { - int i_returnValue; + NSInteger i_returnValue; if(! o_keyInTransition ) { [NSApp stopModal]; @@ -1234,7 +1307,7 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch - (BOOL)changeHotkeyTo: (int)i_theNewKey { - int i_returnValue; + NSInteger i_returnValue; i_returnValue = [o_hotkeysNonUseableKeys indexOfObject: [NSNumber numberWithInt: i_theNewKey]]; if( i_returnValue != NSNotFound || i_theNewKey == 0 ) { @@ -1266,7 +1339,7 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch return YES; } } - + @end /******************** @@ -1312,7 +1385,7 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch if( key ) { i_key |= CocoaKeyToVLC( key ); - return [[[VLCMain sharedInstance] getSimplePreferences] changeHotkeyTo: i_key]; + return [[[VLCMain sharedInstance] simplePreferences] changeHotkeyTo: i_key]; } return FALSE; } @@ -1328,6 +1401,6 @@ static inline void save_module_list( intf_thread_t * p_intf, id object, const ch - (void)changeFont:(id)sender { - [[[VLCMain sharedInstance] getSimplePreferences] changeFont: sender]; + [[[VLCMain sharedInstance] simplePreferences] changeFont: sender]; } @end