![]() |
API 0.9.5
|
00001 /* 00002 * CPImage.j 00003 * AppKit 00004 * 00005 * Created by Francisco Tolmasky. 00006 * Copyright 2008, 280 North, Inc. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 00025 00026 CPImageLoadStatusInitialized = 0; 00027 CPImageLoadStatusLoading = 1; 00028 CPImageLoadStatusCompleted = 2; 00029 CPImageLoadStatusCancelled = 3; 00030 CPImageLoadStatusInvalidData = 4; 00031 CPImageLoadStatusUnexpectedEOF = 5; 00032 CPImageLoadStatusReadError = 6; 00033 00034 CPImageDidLoadNotification = @"CPImageDidLoadNotification"; 00035 00036 // Image Names 00037 CPImageNameColorPanel = @"CPImageNameColorPanel"; 00038 CPImageNameColorPanelHighlighted = @"CPImageNameColorPanelHighlighted"; 00039 00040 var imagesForNames = { }, 00041 AppKitImageForNames = { }, 00042 ImageDescriptionFormat = "%s {\n filename: \"%s\",\n size: { width:%f, height:%f }\n}"; 00043 00044 AppKitImageForNames[CPImageNameColorPanel] = CGSizeMake(26.0, 29.0); 00045 AppKitImageForNames[CPImageNameColorPanelHighlighted] = CGSizeMake(26.0, 29.0); 00046 00047 function CPImageInBundle(aFilename, aSize, aBundle) 00048 { 00049 if (!aBundle) 00050 aBundle = [CPBundle mainBundle]; 00051 00052 if (aSize) 00053 return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename] size:aSize]; 00054 00055 return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename]]; 00056 } 00057 00058 function CPAppKitImage(aFilename, aSize) 00059 { 00060 return CPImageInBundle(aFilename, aSize, [CPBundle bundleForClass:[CPView class]]); 00061 } 00062 00084 @implementation CPImage : CPObject 00085 { 00086 CGSize _size; 00087 CPString _filename; 00088 CPString _name; 00089 00090 id _delegate; 00091 unsigned _loadStatus; 00092 00093 Image _image; 00094 } 00095 00096 - (id)init 00097 { 00098 return [self initByReferencingFile:@"" size:CGSizeMake(-1, -1)]; 00099 } 00100 00109 - (id)initByReferencingFile:(CPString)aFilename size:(CGSize)aSize 00110 { 00111 self = [super init]; 00112 00113 if (self) 00114 { 00115 _size = CPSizeCreateCopy(aSize); 00116 _filename = aFilename; 00117 _loadStatus = CPImageLoadStatusInitialized; 00118 } 00119 00120 return self; 00121 } 00122 00129 - (id)initWithContentsOfFile:(CPString)aFilename size:(CGSize)aSize 00130 { 00131 self = [self initByReferencingFile:aFilename size:aSize]; 00132 00133 if (self) 00134 [self load]; 00135 00136 return self; 00137 } 00138 00145 - (id)initWithContentsOfFile:(CPString)aFilename 00146 { 00147 self = [self initByReferencingFile:aFilename size:CGSizeMake(-1, -1)]; 00148 00149 if (self) 00150 [self load]; 00151 00152 return self; 00153 } 00154 00158 - (CPString)filename 00159 { 00160 return _filename; 00161 } 00162 00167 - (void)setSize:(CGSize)aSize 00168 { 00169 _size = CGSizeMakeCopy(aSize); 00170 } 00171 00175 - (CGSize)size 00176 { 00177 return _size; 00178 } 00179 00180 + (id)imageNamed:(CPString)aName 00181 { 00182 var image = imagesForNames[aName]; 00183 00184 if (image) 00185 return image; 00186 00187 var imageOrSize = AppKitImageForNames[aName]; 00188 00189 if (!imageOrSize) 00190 return nil; 00191 00192 if (!imageOrSize.isa) 00193 { 00194 imageOrSize = CPAppKitImage("CPImage/" + aName + ".png", imageOrSize); 00195 00196 [imageOrSize setName:aName]; 00197 00198 AppKitImageForNames[aName] = imageOrSize; 00199 } 00200 00201 return imageOrSize; 00202 } 00203 00204 - (BOOL)setName:(CPString)aName 00205 { 00206 if (_name === aName) 00207 return YES; 00208 00209 if (imagesForNames[aName]) 00210 return NO; 00211 00212 _name = aName; 00213 00214 imagesForNames[aName] = self; 00215 00216 return YES; 00217 } 00218 00219 - (CPString)name 00220 { 00221 return _name; 00222 } 00223 00228 - (void)setDelegate:(id)aDelegate 00229 { 00230 _delegate = aDelegate; 00231 } 00232 00236 - (id)delegate 00237 { 00238 return _delegate; 00239 } 00240 00244 - (unsigned)loadStatus 00245 { 00246 return _loadStatus; 00247 } 00248 00255 - (void)load 00256 { 00257 if (_loadStatus == CPImageLoadStatusLoading || _loadStatus == CPImageLoadStatusCompleted) 00258 return; 00259 00260 _loadStatus = CPImageLoadStatusLoading; 00261 00262 #if PLATFORM(DOM) 00263 _image = new Image(); 00264 00265 var isSynchronous = YES; 00266 00267 // FIXME: We need a better/performance way of doing this. 00268 _image.onload = function () 00269 { 00270 if (isSynchronous) 00271 window.setTimeout(function() { [self _imageDidLoad]; }, 0); 00272 else 00273 { 00274 [self _imageDidLoad]; 00275 [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; 00276 } 00277 [self _derefFromImage]; 00278 } 00279 00280 _image.onerror = function () 00281 { 00282 if (isSynchronous) 00283 window.setTimeout(function() { [self _imageDidError]; }, 0); 00284 else 00285 { 00286 [self _imageDidError]; 00287 [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; 00288 } 00289 [self _derefFromImage]; 00290 } 00291 00292 _image.onabort = function () 00293 { 00294 if (isSynchronous) 00295 window.setTimeout(function() { [self _imageDidAbort]; }, 0); 00296 else 00297 { 00298 [self _imageDidAbort]; 00299 [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; 00300 } 00301 [self _derefFromImage]; 00302 } 00303 00304 _image.src = _filename; 00305 00306 // onload and friends may fire after this point but BEFORE the end of the run loop, 00307 // crazy, I know. So don't set isSynchronous here, rather wait a bit longer. 00308 window.setTimeout(function() { isSynchronous = NO; }, 0); 00309 #endif 00310 } 00311 00312 - (BOOL)isThreePartImage 00313 { 00314 return NO; 00315 } 00316 00317 - (BOOL)isNinePartImage 00318 { 00319 return NO; 00320 } 00321 00322 - (CPString)description 00323 { 00324 var filename = [self filename], 00325 size = [self size]; 00326 00327 if (filename.indexOf("data:") === 0) 00328 { 00329 var index = filename.indexOf(","); 00330 00331 if (index > 0) 00332 filename = [CPString stringWithFormat:@"%s,%s...%s", filename.substr(0, index), filename.substr(index + 1, 10), filename.substr(filename.length - 10)]; 00333 else 00334 filename = "data:<unknown type>"; 00335 } 00336 00337 return [CPString stringWithFormat:ImageDescriptionFormat, [super description], filename, size.width, size.height]; 00338 } 00339 00340 /* @ignore */ 00341 - (void)_derefFromImage 00342 { 00343 _image.onload = null; 00344 _image.onerror = null; 00345 _image.onabort = null; 00346 } 00347 00348 /* @ignore */ 00349 - (void)_imageDidLoad 00350 { 00351 _loadStatus = CPImageLoadStatusCompleted; 00352 00353 // FIXME: IE is wrong on image sizes???? 00354 if (!_size || (_size.width == -1 && _size.height == -1)) 00355 _size = CGSizeMake(_image.width, _image.height); 00356 00357 [[CPNotificationCenter defaultCenter] 00358 postNotificationName:CPImageDidLoadNotification 00359 object:self]; 00360 00361 if ([_delegate respondsToSelector:@selector(imageDidLoad:)]) 00362 [_delegate imageDidLoad:self]; 00363 } 00364 00365 /* @ignore */ 00366 - (void)_imageDidError 00367 { 00368 _loadStatus = CPImageLoadStatusReadError; 00369 00370 if ([_delegate respondsToSelector:@selector(imageDidError:)]) 00371 [_delegate imageDidError:self]; 00372 } 00373 00374 /* @ignore */ 00375 - (void)_imageDidAbort 00376 { 00377 _loadStatus = CPImageLoadStatusCancelled; 00378 00379 if ([_delegate respondsToSelector:@selector(imageDidAbort:)]) 00380 [_delegate imageDidAbort:self]; 00381 } 00382 00383 @end 00384 00385 @implementation CPImage (CPCoding) 00386 00392 - (id)initWithCoder:(CPCoder)aCoder 00393 { 00394 return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]]; 00395 } 00396 00401 - (void)encodeWithCoder:(CPCoder)aCoder 00402 { 00403 [aCoder encodeObject:_filename forKey:@"CPFilename"]; 00404 [aCoder encodeSize:_size forKey:@"CPSize"]; 00405 } 00406 00407 @end 00408 00409 @implementation CPThreePartImage : CPObject 00410 { 00411 CPArray _imageSlices; 00412 BOOL _isVertical; 00413 } 00414 00415 - (id)initWithImageSlices:(CPArray)imageSlices isVertical:(BOOL)isVertical 00416 { 00417 self = [super init]; 00418 00419 if (self) 00420 { 00421 _imageSlices = imageSlices; 00422 _isVertical = isVertical; 00423 } 00424 00425 return self; 00426 } 00427 00428 - (CPString)filename 00429 { 00430 return @""; 00431 } 00432 00433 - (CPArray)imageSlices 00434 { 00435 return _imageSlices; 00436 } 00437 00438 - (BOOL)isVertical 00439 { 00440 return _isVertical; 00441 } 00442 00443 - (BOOL)isThreePartImage 00444 { 00445 return YES; 00446 } 00447 00448 - (BOOL)isNinePartImage 00449 { 00450 return NO; 00451 } 00452 00453 @end 00454 00455 var CPThreePartImageImageSlicesKey = @"CPThreePartImageImageSlicesKey", 00456 CPThreePartImageIsVerticalKey = @"CPThreePartImageIsVerticalKey"; 00457 00458 @implementation CPThreePartImage (CPCoding) 00459 00460 - (id)initWithCoder:(CPCoder)aCoder 00461 { 00462 self = [super init]; 00463 00464 if (self) 00465 { 00466 _imageSlices = [aCoder decodeObjectForKey:CPThreePartImageImageSlicesKey]; 00467 _isVertical = [aCoder decodeBoolForKey:CPThreePartImageIsVerticalKey]; 00468 } 00469 00470 return self; 00471 } 00472 00473 - (void)encodeWithCoder:(CPCoder)aCoder 00474 { 00475 [aCoder encodeObject:_imageSlices forKey:CPThreePartImageImageSlicesKey]; 00476 [aCoder encodeBool:_isVertical forKey:CPThreePartImageIsVerticalKey]; 00477 } 00478 00479 @end 00480 00481 00482 @implementation CPNinePartImage : CPObject 00483 { 00484 CPArray _imageSlices; 00485 } 00486 00487 - (id)initWithImageSlices:(CPArray)imageSlices 00488 { 00489 self = [super init]; 00490 00491 if (self) 00492 _imageSlices = imageSlices; 00493 00494 return self; 00495 } 00496 00497 - (CPString)filename 00498 { 00499 return @""; 00500 } 00501 00502 - (CPArray)imageSlices 00503 { 00504 return _imageSlices; 00505 } 00506 00507 - (BOOL)isThreePartImage 00508 { 00509 return NO; 00510 } 00511 00512 - (BOOL)isNinePartImage 00513 { 00514 return YES; 00515 } 00516 00517 @end 00518 00519 var CPNinePartImageImageSlicesKey = @"CPNinePartImageImageSlicesKey"; 00520 00521 @implementation CPNinePartImage (CPCoding) 00522 00523 - (id)initWithCoder:(CPCoder)aCoder 00524 { 00525 self = [super init]; 00526 00527 if (self) 00528 _imageSlices = [aCoder decodeObjectForKey:CPNinePartImageImageSlicesKey]; 00529 00530 return self; 00531 } 00532 00533 - (void)encodeWithCoder:(CPCoder)aCoder 00534 { 00535 [aCoder encodeObject:_imageSlices forKey:CPNinePartImageImageSlicesKey]; 00536 } 00537 00538 @end