/* Bookmark model abstraction Copyright 2013 Thincast Technologies GmbH, Author: Dorian Johnson This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #import "Bookmark.h" #import "TSXAdditions.h" #import "Utils.h" #import "GlobalDefaults.h" @interface ComputerBookmark (Private) - (void)willChangeValueForKeyPath:(NSString *)keyPath; - (void)didChangeValueForKeyPath:(NSString *)keyPath; @end @implementation ComputerBookmark @synthesize parent=_parent, uuid=_uuid, label=_label, image=_image; @synthesize params=_connection_params, conntectedViaWLAN = _connected_via_wlan; - (id)init { if (!(self = [super init])) return nil; _uuid = [[NSString stringWithUUID] retain]; _label = @""; _connected_via_wlan = NO; return self; } // Designated initializer. - (id)initWithConnectionParameters:(ConnectionParams*)params { if (!(self = [self init])) return nil; _connection_params = [params copy]; _connected_via_wlan = NO; return self; } - (id)initWithCoder:(NSCoder *)decoder { if (!(self = [self init])) return nil; if (![decoder allowsKeyedCoding]) [NSException raise:NSInvalidArgumentException format:@"coder must support keyed archiving"]; if ([decoder containsValueForKey:@"uuid"]) { [_uuid release]; _uuid = [[decoder decodeObjectForKey:@"uuid"] retain]; } if ([decoder containsValueForKey:@"label"]) [self setLabel:[decoder decodeObjectForKey:@"label"]]; if ([decoder containsValueForKey:@"connectionParams"]) { [_connection_params release]; _connection_params = [[decoder decodeObjectForKey:@"connectionParams"] retain]; } return self; } - (id)initWithBaseDefaultParameters { return [self initWithConnectionParameters:[[[ConnectionParams alloc] initWithBaseDefaultParameters] autorelease]]; } - (id)copy { ComputerBookmark* copy = [[[self class] alloc] init]; [copy setLabel:[self label]]; copy->_connection_params = [_connection_params copy]; return copy; } - (id)copyWithUUID { ComputerBookmark* copy = [self copy]; copy->_uuid = [[self uuid] copy]; return copy; } - (void)encodeWithCoder:(NSCoder *)coder { if (![coder allowsKeyedCoding]) [NSException raise:NSInvalidArgumentException format:@"coder must support keyed archiving"]; [coder encodeObject:_uuid forKey:@"uuid"]; [coder encodeObject:_label forKey:@"label"]; [coder encodeObject:_connection_params forKey:@"connectionParams"]; } - (void)dealloc { _parent = nil; [_label release]; _label = nil; [_uuid release]; _uuid = nil; [_connection_params release]; _connection_params = nil; [super dealloc]; } - (UIImage*)image { return nil; } - (BOOL)isEqual:(id)object { return [object respondsToSelector:@selector(uuid)] && [[object uuid] isEqual:_uuid]; } - (NSString*)description { return ([self label] != nil) ? [self label] : _uuid; } - (BOOL)validateValue:(id *)val forKey:(NSString *)key error:(NSError **)error { NSString* string_value = *val; if ([key isEqualToString:@"label"]) { if (![[string_value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length]) { if (error) *error = [NSError errorWithDomain:@"" code:NSKeyValueValidationError userInfo: [NSDictionary dictionaryWithObjectsAndKeys: NSLocalizedString(@"Connection labels cannot be blank", @"Bookmark data validation: label blank title."), NSLocalizedDescriptionKey, NSLocalizedString(@"Please enter the short description of this Connection that will appear in the Connection list.", @"Bookmark data validation: label blank message."), NSLocalizedRecoverySuggestionErrorKey, nil]]; return NO; } } return YES; } - (BOOL)validateValue:(id *)val forKeyPath:(NSString *)keyPath error:(NSError **)error { // Could be used to validate params.hostname, params.password, params.port, etc. return [super validateValue:val forKeyPath:keyPath error:error]; } - (BOOL)isDeletable { return YES; } - (BOOL)isMovable { return YES; } - (BOOL)isRenamable { return YES; } - (BOOL)hasImmutableHost { return NO; } #pragma mark Custom KVC - (void)setValue:(id)value forKeyPath:(NSString *)keyPath { if ([keyPath isEqualToString:@"params.resolution"]) { int width, height; TSXScreenOptions type; if (ScanScreenResolution(value, &width, &height, &type)) { [_connection_params willChangeValueForKey:@"resolution"]; [[self params] setInt:type forKey:@"screen_resolution_type"]; if (type == TSXScreenOptionFixed) { [[self params] setInt:width forKey:@"width"]; [[self params] setInt:height forKey:@"height"]; } [_connection_params didChangeValueForKey:@"resolution"]; } else [NSException raise:NSInvalidArgumentException format:@"%s got invalid screen resolution '%@'", __func__, value]; } else { [self willChangeValueForKeyPath:keyPath]; [super setValue:value forKeyPath:keyPath]; [self didChangeValueForKeyPath:keyPath]; } } - (id)valueForKeyPath:(NSString *)keyPath { if ([keyPath isEqualToString:@"params.resolution"]) return ScreenResolutionDescription([[self params] intForKey:@"screen_resolution_type"], [[self params] intForKey:@"width"], [[self params] intForKey:@"height"]); return [super valueForKeyPath:keyPath]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]) [self willChangeValueForKeyPath:keyPath]; else [self didChangeValueForKeyPath:keyPath]; } - (NSDictionary*)targetForChangeNotificationForKeyPath:(NSString*)keyPath { NSString* changed_key = keyPath; NSObject* changed_object = self; if ([keyPath rangeOfString:@"params."].location == 0) { changed_key = [keyPath substringFromIndex:[@"params." length]]; changed_object = _connection_params; } return [NSDictionary dictionaryWithObjectsAndKeys:changed_key, @"key", changed_object, @"object", nil]; } - (void)willChangeValueForKeyPath:(NSString *)keyPath { NSDictionary* target = [self targetForChangeNotificationForKeyPath:keyPath]; [[target objectForKey:@"object"] willChangeValueForKey:[target objectForKey:@"key"]]; } - (void)didChangeValueForKeyPath:(NSString *)keyPath { NSDictionary* target = [self targetForChangeNotificationForKeyPath:keyPath]; [[target objectForKey:@"object"] didChangeValueForKey:[target objectForKey:@"key"]]; } - (ConnectionParams*)copyMarkedParams { ConnectionParams* param_copy = [[self params] copy]; [param_copy setValue:[self uuid] forKey:@"_bookmark_uuid"]; return param_copy; } #pragma mark No children - (NSUInteger)numberOfChildren { return 0; } - (NSUInteger)numberOfDescendants { return 1; } - (BookmarkBase *)childAtIndex:(NSUInteger)index { return nil; } - (NSUInteger)indexOfChild:(BookmarkBase *)child { return 0; } - (void)removeChild:(BookmarkBase *)child { } - (void)addChild:(BookmarkBase *)child { } - (void)addChild:(BookmarkBase *)child afterExistingChild:(BookmarkBase *)existingChild { } - (void)addChild:(BookmarkBase *)child atIndex:(NSInteger)index { } - (BOOL)hasDescendant:(BookmarkBase *)needle { return NO; } - (BOOL)canContainChildren { return NO; } @end