1/* 2 * Copyright © 2011 William Hua 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 16 * 17 * Author: William Hua <william@attente.ca> 18 */ 19 20#include "config.h" 21 22#include "gsettingsbackendinternal.h" 23#include "gsimplepermission.h" 24#include "giomodule.h" 25 26#import <Foundation/Foundation.h> 27 28GType g_nextstep_settings_backend_get_type (void); 29 30#define G_NEXTSTEP_SETTINGS_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), g_nextstep_settings_backend_get_type (), GNextstepSettingsBackend)) 31 32typedef struct _GNextstepSettingsBackend GNextstepSettingsBackend; 33typedef GSettingsBackendClass GNextstepSettingsBackendClass; 34 35struct _GNextstepSettingsBackend 36{ 37 GSettingsBackend parent_instance; 38 39 /*< private >*/ 40 NSUserDefaults *user_defaults; 41 GMutex mutex; 42}; 43 44G_DEFINE_TYPE_WITH_CODE (GNextstepSettingsBackend, 45 g_nextstep_settings_backend, 46 G_TYPE_SETTINGS_BACKEND, 47 g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME, 48 g_define_type_id, "nextstep", 90)); 49 50static void g_nextstep_settings_backend_finalize (GObject *backend); 51 52static GVariant * g_nextstep_settings_backend_read (GSettingsBackend *backend, 53 const gchar *key, 54 const GVariantType *expected_type, 55 gboolean default_value); 56 57static gboolean g_nextstep_settings_backend_get_writable (GSettingsBackend *backend, 58 const gchar *key); 59 60static gboolean g_nextstep_settings_backend_write (GSettingsBackend *backend, 61 const gchar *key, 62 GVariant *value, 63 gpointer origin_tag); 64 65static gboolean g_nextstep_settings_backend_write_tree (GSettingsBackend *backend, 66 GTree *tree, 67 gpointer origin_tag); 68 69static void g_nextstep_settings_backend_reset (GSettingsBackend *backend, 70 const gchar *key, 71 gpointer origin_tag); 72 73static void g_nextstep_settings_backend_subscribe (GSettingsBackend *backend, 74 const gchar *name); 75 76static void g_nextstep_settings_backend_unsubscribe (GSettingsBackend *backend, 77 const gchar *name); 78 79static void g_nextstep_settings_backend_sync (GSettingsBackend *backend); 80 81static GPermission * g_nextstep_settings_backend_get_permission (GSettingsBackend *backend, 82 const gchar *path); 83 84static gboolean g_nextstep_settings_backend_write_pair (gpointer name, 85 gpointer value, 86 gpointer data); 87 88static GVariant * g_nextstep_settings_backend_get_g_variant (id object, 89 const GVariantType *type); 90 91static id g_nextstep_settings_backend_get_ns_object (GVariant *variant); 92 93static void 94g_nextstep_settings_backend_class_init (GNextstepSettingsBackendClass *class) 95{ 96 G_OBJECT_CLASS (class)->finalize = g_nextstep_settings_backend_finalize; 97 class->read = g_nextstep_settings_backend_read; 98 class->get_writable = g_nextstep_settings_backend_get_writable; 99 class->write = g_nextstep_settings_backend_write; 100 class->write_tree = g_nextstep_settings_backend_write_tree; 101 class->reset = g_nextstep_settings_backend_reset; 102 class->subscribe = g_nextstep_settings_backend_subscribe; 103 class->unsubscribe = g_nextstep_settings_backend_unsubscribe; 104 class->sync = g_nextstep_settings_backend_sync; 105 class->get_permission = g_nextstep_settings_backend_get_permission; 106} 107 108static void 109g_nextstep_settings_backend_init (GNextstepSettingsBackend *self) 110{ 111 NSAutoreleasePool *pool; 112 113 pool = [[NSAutoreleasePool alloc] init]; 114 115 self->user_defaults = [[NSUserDefaults standardUserDefaults] retain]; 116 117 g_mutex_init (&self->mutex); 118 119 [pool drain]; 120} 121 122static void 123g_nextstep_settings_backend_finalize (GObject *self) 124{ 125 GNextstepSettingsBackend *backend = G_NEXTSTEP_SETTINGS_BACKEND (self); 126 NSAutoreleasePool *pool; 127 128 pool = [[NSAutoreleasePool alloc] init]; 129 130 g_mutex_clear (&backend->mutex); 131 132 [backend->user_defaults release]; 133 134 [pool drain]; 135 136 G_OBJECT_CLASS (g_nextstep_settings_backend_parent_class)->finalize (self); 137} 138 139static GVariant * 140g_nextstep_settings_backend_read (GSettingsBackend *backend, 141 const gchar *key, 142 const GVariantType *expected_type, 143 gboolean default_value) 144{ 145 GNextstepSettingsBackend *self = G_NEXTSTEP_SETTINGS_BACKEND (backend); 146 NSAutoreleasePool *pool; 147 NSString *name; 148 id value; 149 GVariant *variant; 150 151 if (default_value) 152 return NULL; 153 154 pool = [[NSAutoreleasePool alloc] init]; 155 name = [NSString stringWithUTF8String:key]; 156 157 g_mutex_lock (&self->mutex); 158 value = [self->user_defaults objectForKey:name]; 159 g_mutex_unlock (&self->mutex); 160 161 variant = g_nextstep_settings_backend_get_g_variant (value, expected_type); 162 163 [pool drain]; 164 165 return variant; 166} 167 168static gboolean 169g_nextstep_settings_backend_get_writable (GSettingsBackend *backend, 170 const gchar *key) 171{ 172 return TRUE; 173} 174 175static gboolean 176g_nextstep_settings_backend_write (GSettingsBackend *backend, 177 const gchar *key, 178 GVariant *value, 179 gpointer origin_tag) 180{ 181 GNextstepSettingsBackend *self = G_NEXTSTEP_SETTINGS_BACKEND (backend); 182 NSAutoreleasePool *pool; 183 184 pool = [[NSAutoreleasePool alloc] init]; 185 186 g_mutex_lock (&self->mutex); 187 g_nextstep_settings_backend_write_pair ((gpointer) key, value, self); 188 g_mutex_unlock (&self->mutex); 189 190 g_settings_backend_changed (backend, key, origin_tag); 191 192 [pool drain]; 193 194 return TRUE; 195} 196 197static gboolean 198g_nextstep_settings_backend_write_tree (GSettingsBackend *backend, 199 GTree *tree, 200 gpointer origin_tag) 201{ 202 GNextstepSettingsBackend *self = G_NEXTSTEP_SETTINGS_BACKEND (backend); 203 NSAutoreleasePool *pool; 204 205 pool = [[NSAutoreleasePool alloc] init]; 206 207 g_mutex_lock (&self->mutex); 208 g_tree_foreach (tree, g_nextstep_settings_backend_write_pair, self); 209 g_mutex_unlock (&self->mutex); 210 g_settings_backend_changed_tree (backend, tree, origin_tag); 211 212 [pool drain]; 213 214 return TRUE; 215} 216 217static void 218g_nextstep_settings_backend_reset (GSettingsBackend *backend, 219 const gchar *key, 220 gpointer origin_tag) 221{ 222 GNextstepSettingsBackend *self = G_NEXTSTEP_SETTINGS_BACKEND (backend); 223 NSAutoreleasePool *pool; 224 NSString *name; 225 226 pool = [[NSAutoreleasePool alloc] init]; 227 name = [NSString stringWithUTF8String:key]; 228 229 g_mutex_lock (&self->mutex); 230 [self->user_defaults removeObjectForKey:name]; 231 g_mutex_unlock (&self->mutex); 232 233 g_settings_backend_changed (backend, key, origin_tag); 234 235 [pool drain]; 236} 237 238static void 239g_nextstep_settings_backend_subscribe (GSettingsBackend *backend, 240 const gchar *name) 241{ 242} 243 244static void 245g_nextstep_settings_backend_unsubscribe (GSettingsBackend *backend, 246 const gchar *name) 247{ 248} 249 250static void 251g_nextstep_settings_backend_sync (GSettingsBackend *backend) 252{ 253 GNextstepSettingsBackend *self = G_NEXTSTEP_SETTINGS_BACKEND (backend); 254 NSAutoreleasePool *pool; 255 256 pool = [[NSAutoreleasePool alloc] init]; 257 258 g_mutex_lock (&self->mutex); 259 [self->user_defaults synchronize]; 260 g_mutex_unlock (&self->mutex); 261 262 [pool drain]; 263} 264 265static GPermission * 266g_nextstep_settings_backend_get_permission (GSettingsBackend *backend, 267 const gchar *path) 268{ 269 return g_simple_permission_new (TRUE); 270} 271 272static gboolean 273g_nextstep_settings_backend_write_pair (gpointer name, 274 gpointer value, 275 gpointer data) 276{ 277 GNextstepSettingsBackend *backend = G_NEXTSTEP_SETTINGS_BACKEND (data); 278 NSString *key; 279 id object; 280 281 key = [NSString stringWithUTF8String:name]; 282 object = g_nextstep_settings_backend_get_ns_object (value); 283 284 [backend->user_defaults setObject:object forKey:key]; 285 286 return FALSE; 287} 288 289static GVariant * 290g_nextstep_settings_backend_get_g_variant (id object, 291 const GVariantType *type) 292{ 293 if ([object isKindOfClass:[NSData class]]) 294 return g_variant_parse (type, [[[[NSString alloc] initWithData:object encoding:NSUTF8StringEncoding] autorelease] UTF8String], NULL, NULL, NULL); 295 else if ([object isKindOfClass:[NSNumber class]]) 296 { 297 if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN)) 298 return g_variant_new_boolean ([object boolValue]); 299 else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE)) 300 return g_variant_new_byte ([object unsignedCharValue]); 301 else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16)) 302 return g_variant_new_int16 ([object shortValue]); 303 else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16)) 304 return g_variant_new_uint16 ([object unsignedShortValue]); 305 else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) 306 return g_variant_new_int32 ([object longValue]); 307 else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) 308 return g_variant_new_uint32 ([object unsignedLongValue]); 309 else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64)) 310 return g_variant_new_int64 ([object longLongValue]); 311 else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64)) 312 return g_variant_new_uint64 ([object unsignedLongLongValue]); 313 else if (g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE)) 314 return g_variant_new_handle ([object longValue]); 315 else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE)) 316 return g_variant_new_double ([object doubleValue]); 317 } 318 else if ([object isKindOfClass:[NSString class]]) 319 { 320 const char *string; 321 322 string = [object UTF8String]; 323 324 if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) 325 return g_variant_new_string (string); 326 else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH)) 327 return g_variant_is_object_path (string) ? 328 g_variant_new_object_path (string) : NULL; 329 else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE)) 330 return g_variant_is_signature (string) ? 331 g_variant_new_signature (string) : NULL; 332 } 333 else if ([object isKindOfClass:[NSDictionary class]]) 334 { 335 if (g_variant_type_is_subtype_of (type, G_VARIANT_TYPE ("a{s*}"))) 336 { 337 const GVariantType *value_type; 338 GVariantBuilder builder; 339 NSString *key; 340 341 value_type = g_variant_type_value (g_variant_type_element (type)); 342 343 g_variant_builder_init (&builder, type); 344 345#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 346 for(key in object) 347#else 348 NSEnumerator *enumerator = [object objectEnumerator]; 349 while((key = [enumerator nextObject])) 350#endif 351 { 352 GVariant *name; 353 id value; 354 GVariant *variant; 355 GVariant *entry; 356 357 name = g_variant_new_string ([key UTF8String]); 358 value = [object objectForKey:key]; 359 variant = g_nextstep_settings_backend_get_g_variant (value, value_type); 360 361 if (variant == NULL) 362 { 363 g_variant_builder_clear (&builder); 364 365 return NULL; 366 } 367 368 entry = g_variant_new_dict_entry (name, variant); 369 g_variant_builder_add_value (&builder, entry); 370 } 371 372 return g_variant_builder_end (&builder); 373 } 374 } 375 else if ([object isKindOfClass:[NSArray class]]) 376 { 377 if (g_variant_type_is_subtype_of (type, G_VARIANT_TYPE_ARRAY)) 378 { 379 const GVariantType *value_type; 380 GVariantBuilder builder; 381 id value; 382 383 value_type = g_variant_type_element (type); 384 g_variant_builder_init (&builder, type); 385 386#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 387 for(value in object) 388#else 389 NSEnumerator *enumerator = [object objectEnumerator]; 390 while((value = [enumerator nextObject])) 391#endif 392 { 393 GVariant *variant = g_nextstep_settings_backend_get_g_variant (value, value_type); 394 395 if (variant == NULL) 396 { 397 g_variant_builder_clear (&builder); 398 399 return NULL; 400 } 401 402 g_variant_builder_add_value (&builder, variant); 403 } 404 405 return g_variant_builder_end (&builder); 406 } 407 } 408 409 return NULL; 410} 411 412static id 413g_nextstep_settings_backend_get_ns_object (GVariant *variant) 414{ 415 if (variant == NULL) 416 return nil; 417 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) 418 return [NSNumber numberWithBool:g_variant_get_boolean (variant)]; 419 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTE)) 420 return [NSNumber numberWithUnsignedChar:g_variant_get_byte (variant)]; 421 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT16)) 422 return [NSNumber numberWithShort:g_variant_get_int16 (variant)]; 423 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT16)) 424 return [NSNumber numberWithUnsignedShort:g_variant_get_uint16 (variant)]; 425 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT32)) 426 return [NSNumber numberWithLong:g_variant_get_int32 (variant)]; 427 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)) 428 return [NSNumber numberWithUnsignedLong:g_variant_get_uint32 (variant)]; 429 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT64)) 430 return [NSNumber numberWithLongLong:g_variant_get_int64 (variant)]; 431 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT64)) 432 return [NSNumber numberWithUnsignedLongLong:g_variant_get_uint64 (variant)]; 433 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_HANDLE)) 434 return [NSNumber numberWithLong:g_variant_get_handle (variant)]; 435 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_DOUBLE)) 436 return [NSNumber numberWithDouble:g_variant_get_double (variant)]; 437 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) 438 return [NSString stringWithUTF8String:g_variant_get_string (variant, NULL)]; 439 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_OBJECT_PATH)) 440 return [NSString stringWithUTF8String:g_variant_get_string (variant, NULL)]; 441 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_SIGNATURE)) 442 return [NSString stringWithUTF8String:g_variant_get_string (variant, NULL)]; 443 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{s*}"))) 444 { 445 NSMutableDictionary *dictionary; 446 GVariantIter iter; 447 GVariant *name; 448 GVariant *value; 449 450 dictionary = [NSMutableDictionary dictionaryWithCapacity:g_variant_iter_init (&iter, variant)]; 451 452 while (g_variant_iter_loop (&iter, "{s*}", &name, &value)) 453 { 454 NSString *key; 455 id object; 456 457 key = [NSString stringWithUTF8String:g_variant_get_string (name, NULL)]; 458 object = g_nextstep_settings_backend_get_ns_object (value); 459 460 [dictionary setObject:object forKey:key]; 461 } 462 463 return dictionary; 464 } 465 else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_ARRAY)) 466 { 467 NSMutableArray *array; 468 GVariantIter iter; 469 GVariant *value; 470 471 array = [NSMutableArray arrayWithCapacity:g_variant_iter_init (&iter, variant)]; 472 473 while ((value = g_variant_iter_next_value (&iter)) != NULL) 474 [array addObject:g_nextstep_settings_backend_get_ns_object (value)]; 475 476 return array; 477 } 478 else 479 return [[NSString stringWithUTF8String:g_variant_print (variant, TRUE)] dataUsingEncoding:NSUTF8StringEncoding]; 480} 481