![]() |
API 0.9.5
|
00001 /* 00002 * CPImageView.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 CPScaleProportionally = 0; 00027 CPScaleToFit = 1; 00028 CPScaleNone = 2; 00029 00030 CPImageAlignCenter = 0; 00031 CPImageAlignTop = 1; 00032 CPImageAlignTopLeft = 2; 00033 CPImageAlignTopRight = 3; 00034 CPImageAlignLeft = 4; 00035 CPImageAlignBottom = 5; 00036 CPImageAlignBottomLeft = 6; 00037 CPImageAlignBottomRight = 7; 00038 CPImageAlignRight = 8; 00039 00040 var CPImageViewShadowBackgroundColor = nil, 00041 CPImageViewEmptyPlaceholderImage = nil; 00042 00043 var LEFT_SHADOW_INSET = 3.0, 00044 RIGHT_SHADOW_INSET = 3.0, 00045 TOP_SHADOW_INSET = 3.0, 00046 BOTTOM_SHADOW_INSET = 5.0, 00047 VERTICAL_SHADOW_INSET = TOP_SHADOW_INSET + BOTTOM_SHADOW_INSET, 00048 HORIZONTAL_SHADOW_INSET = LEFT_SHADOW_INSET + RIGHT_SHADOW_INSET; 00049 00056 @implementation CPImageView : CPControl 00057 { 00058 DOMElement _DOMImageElement; 00059 00060 BOOL _hasShadow; 00061 CPView _shadowView; 00062 00063 BOOL _isEditable; 00064 00065 CGRect _imageRect; 00066 CPImageAlignment _imageAlignment; 00067 } 00068 00069 + (void)initialize 00070 { 00071 var bundle = [CPBundle bundleForClass:[CPView class]]; 00072 00073 CPImageViewEmptyPlaceholderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"empty.png"]]; 00074 } 00075 00076 - (id)initWithFrame:(CGRect)aFrame 00077 { 00078 self = [super initWithFrame:aFrame]; 00079 00080 if (self) 00081 { 00082 #if PLATFORM(DOM) 00083 _DOMImageElement = document.createElement("img"); 00084 _DOMImageElement.style.position = "absolute"; 00085 _DOMImageElement.style.left = "0px"; 00086 _DOMImageElement.style.top = "0px"; 00087 00088 if ([CPPlatform supportsDragAndDrop]) 00089 { 00090 _DOMImageElement.setAttribute("draggable", "true"); 00091 _DOMImageElement.style["-khtml-user-drag"] = "element"; 00092 } 00093 00094 CPDOMDisplayServerAppendChild(_DOMElement, _DOMImageElement); 00095 00096 _DOMImageElement.style.visibility = "hidden"; 00097 #endif 00098 } 00099 00100 return self; 00101 } 00102 00106 - (CPImage)image 00107 { 00108 return [self objectValue]; 00109 } 00110 00111 - (void)setImage:(CPImage)anImage 00112 { 00113 [self setObjectValue:anImage]; 00114 } 00115 00117 - (void)setObjectValue:(CPImage)anImage 00118 { 00119 var oldImage = [self objectValue]; 00120 00121 if (oldImage === anImage) 00122 return; 00123 00124 [super setObjectValue:anImage]; 00125 00126 var defaultCenter = [CPNotificationCenter defaultCenter]; 00127 00128 if (oldImage) 00129 [defaultCenter removeObserver:self name:CPImageDidLoadNotification object:oldImage]; 00130 00131 var newImage = [self objectValue]; 00132 00133 #if PLATFORM(DOM) 00134 _DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename]; 00135 #endif 00136 00137 var size = [newImage size]; 00138 00139 if (size && size.width === -1 && size.height === -1) 00140 { 00141 [defaultCenter addObserver:self selector:@selector(imageDidLoad:) name:CPImageDidLoadNotification object:newImage]; 00142 00143 #if PLATFORM(DOM) 00144 _DOMImageElement.width = 0; 00145 _DOMImageElement.height = 0; 00146 #endif 00147 00148 [_shadowView setHidden:YES]; 00149 } 00150 else 00151 { 00152 [self hideOrDisplayContents]; 00153 [self setNeedsLayout]; 00154 [self setNeedsDisplay:YES]; 00155 } 00156 } 00157 00158 - (void)imageDidLoad:(CPNotification)aNotification 00159 { 00160 [self hideOrDisplayContents]; 00161 00162 [self setNeedsLayout]; 00163 [self setNeedsDisplay:YES]; 00164 } 00165 00170 - (BOOL)hasShadow 00171 { 00172 return _hasShadow; 00173 } 00174 00179 - (void)setHasShadow:(BOOL)shouldHaveShadow 00180 { 00181 if (_hasShadow == shouldHaveShadow) 00182 return; 00183 00184 _hasShadow = shouldHaveShadow; 00185 00186 if (_hasShadow) 00187 { 00188 _shadowView = [[CPShadowView alloc] initWithFrame:[self bounds]]; 00189 00190 [self addSubview:_shadowView]; 00191 00192 [self setNeedsLayout]; 00193 [self setNeedsDisplay:YES]; 00194 } 00195 else 00196 { 00197 [_shadowView removeFromSuperview]; 00198 00199 _shadowView = nil; 00200 } 00201 00202 [self hideOrDisplayContents]; 00203 } 00204 00210 - (void)setImageAlignment:(CPImageAlignment)anImageAlignment 00211 { 00212 if (_imageAlignment == anImageAlignment) 00213 return; 00214 00215 _imageAlignment = anImageAlignment; 00216 00217 if (![self image]) 00218 return; 00219 00220 [self setNeedsLayout]; 00221 [self setNeedsDisplay:YES]; 00222 } 00223 00224 - (unsigned)imageAlignment 00225 { 00226 return _imageAlignment; 00227 } 00228 00234 - (void)setImageScaling:(CPImageScaling)anImageScaling 00235 { 00236 [super setImageScaling:anImageScaling]; 00237 00238 #if PLATFORM(DOM) 00239 if ([self currentValueForThemeAttribute:@"image-scaling"] === CPScaleToFit) 00240 { 00241 CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, 0.0, 0.0); 00242 } 00243 #endif 00244 00245 [self setNeedsLayout]; 00246 [self setNeedsDisplay:YES]; 00247 } 00248 00249 - (unsigned)imageScaling 00250 { 00251 return [self currentValueForThemeAttribute:@"image-scaling"]; 00252 } 00253 00257 - (void)hideOrDisplayContents 00258 { 00259 if (![self image]) 00260 { 00261 #if PLATFORM(DOM) 00262 _DOMImageElement.style.visibility = "hidden"; 00263 #endif 00264 [_shadowView setHidden:YES]; 00265 } 00266 else 00267 { 00268 #if PLATFORM(DOM) 00269 _DOMImageElement.style.visibility = "visible"; 00270 #endif 00271 [_shadowView setHidden:NO]; 00272 } 00273 } 00274 00278 - (CGRect)imageRect 00279 { 00280 return _imageRect; 00281 } 00282 00286 - (void)layoutSubviews 00287 { 00288 if (![self image]) 00289 return; 00290 00291 var bounds = [self bounds], 00292 image = [self image], 00293 imageScaling = [self currentValueForThemeAttribute:@"image-scaling"], 00294 x = 0.0, 00295 y = 0.0, 00296 insetWidth = (_hasShadow ? HORIZONTAL_SHADOW_INSET : 0.0), 00297 insetHeight = (_hasShadow ? VERTICAL_SHADOW_INSET : 0.0), 00298 boundsWidth = _CGRectGetWidth(bounds), 00299 boundsHeight = _CGRectGetHeight(bounds), 00300 width = boundsWidth - insetWidth, 00301 height = boundsHeight - insetHeight; 00302 00303 if (imageScaling === CPScaleToFit) 00304 { 00305 #if PLATFORM(DOM) 00306 _DOMImageElement.width = ROUND(width); 00307 _DOMImageElement.height = ROUND(height); 00308 #endif 00309 } 00310 else 00311 { 00312 var size = [image size]; 00313 00314 if (size.width == -1 && size.height == -1) 00315 return; 00316 00317 if (imageScaling === CPScaleProportionally) 00318 { 00319 // The max size it can be is size.width x size.height, so only 00320 // only proportion otherwise. 00321 if (width >= size.width && height >= size.height) 00322 { 00323 width = size.width; 00324 height = size.height; 00325 } 00326 else 00327 { 00328 var imageRatio = size.width / size.height, 00329 viewRatio = width / height; 00330 00331 if (viewRatio > imageRatio) 00332 width = height * imageRatio; 00333 else 00334 height = width / imageRatio; 00335 } 00336 00337 #if PLATFORM(DOM) 00338 _DOMImageElement.width = ROUND(width); 00339 _DOMImageElement.height = ROUND(height); 00340 #endif 00341 } 00342 else 00343 { 00344 width = size.width; 00345 height = size.height; 00346 } 00347 00348 if (imageScaling == CPScaleNone) 00349 { 00350 #if PLATFORM(DOM) 00351 _DOMImageElement.width = ROUND(size.width); 00352 _DOMImageElement.height = ROUND(size.height); 00353 #endif 00354 } 00355 00356 var x, 00357 y; 00358 00359 switch (_imageAlignment) 00360 { 00361 case CPImageAlignLeft: 00362 case CPImageAlignTopLeft: 00363 case CPImageAlignBottomLeft: 00364 x = 0.0; 00365 break; 00366 00367 case CPImageAlignRight: 00368 case CPImageAlignTopRight: 00369 case CPImageAlignBottomRight: 00370 x = boundsWidth - width; 00371 break; 00372 00373 default: 00374 x = (boundsWidth - width) / 2.0; 00375 break; 00376 } 00377 00378 switch (_imageAlignment) 00379 { 00380 case CPImageAlignTop: 00381 case CPImageAlignTopLeft: 00382 case CPImageAlignTopRight: 00383 y = 0.0; 00384 break; 00385 00386 case CPImageAlignBottom: 00387 case CPImageAlignBottomLeft: 00388 case CPImageAlignBottomRight: 00389 y = boundsHeight - height; 00390 break; 00391 00392 default: 00393 y = (boundsHeight - height) / 2.0; 00394 break; 00395 } 00396 00397 #if PLATFORM(DOM) 00398 CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, x, y); 00399 #endif 00400 } 00401 00402 _imageRect = _CGRectMake(x, y, width, height); 00403 00404 if (_hasShadow) 00405 [_shadowView setFrame:_CGRectMake(x - LEFT_SHADOW_INSET, y - TOP_SHADOW_INSET, width + insetWidth, height + insetHeight)]; 00406 } 00407 00408 - (void)mouseDown:(CPEvent)anEvent 00409 { 00410 // Should we do something with this event? 00411 [[self nextResponder] mouseDown:anEvent]; 00412 } 00413 00414 - (void)setEditable:(BOOL)shouldBeEditable 00415 { 00416 if (_isEditable === shouldBeEditable) 00417 return; 00418 00419 _isEditable = shouldBeEditable; 00420 00421 if (_isEditable) 00422 [self registerForDraggedTypes:[CPImagesPboardType]]; 00423 00424 else 00425 { 00426 var draggedTypes = [self registeredDraggedTypes]; 00427 00428 [self unregisterDraggedTypes]; 00429 00430 [draggedTypes removeObjectIdenticalTo:CPImagesPboardType]; 00431 00432 [self registerForDraggedTypes:draggedTypes]; 00433 } 00434 } 00435 00436 - (BOOL)isEditable 00437 { 00438 return _isEditable; 00439 } 00440 00441 - (BOOL)performDragOperation:(CPDraggingInfo)aSender 00442 { 00443 var images = [CPKeyedUnarchiver unarchiveObjectWithData:[[aSender draggingPasteboard] dataForType:CPImagesPboardType]]; 00444 00445 if ([images count]) 00446 { 00447 [self setImage:images[0]]; 00448 [self sendAction:[self action] to:[self target]]; 00449 } 00450 00451 return YES; 00452 } 00453 00454 @end 00455 00456 var CPImageViewImageKey = @"CPImageViewImageKey", 00457 CPImageViewImageScalingKey = @"CPImageViewImageScalingKey", 00458 CPImageViewImageAlignmentKey = @"CPImageViewImageAlignmentKey", 00459 CPImageViewHasShadowKey = @"CPImageViewHasShadowKey", 00460 CPImageViewIsEditableKey = @"CPImageViewIsEditableKey"; 00461 00462 @implementation CPImageView (CPCoding) 00463 00469 - (id)initWithCoder:(CPCoder)aCoder 00470 { 00471 #if PLATFORM(DOM) 00472 _DOMImageElement = document.createElement("img"); 00473 _DOMImageElement.style.position = "absolute"; 00474 _DOMImageElement.style.left = "0px"; 00475 _DOMImageElement.style.top = "0px"; 00476 _DOMImageElement.style.visibility = "hidden"; 00477 if ([CPPlatform supportsDragAndDrop]) 00478 { 00479 _DOMImageElement.setAttribute("draggable", "true"); 00480 _DOMImageElement.style["-khtml-user-drag"] = "element"; 00481 } 00482 #endif 00483 00484 self = [super initWithCoder:aCoder]; 00485 00486 if (self) 00487 { 00488 #if PLATFORM(DOM) 00489 _DOMElement.appendChild(_DOMImageElement); 00490 #endif 00491 00492 [self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]]; 00493 [self setImageAlignment:[aCoder decodeIntForKey:CPImageViewImageAlignmentKey]]; 00494 00495 if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey] || NO) 00496 [self setEditable:YES]; 00497 00498 [self setNeedsLayout]; 00499 [self setNeedsDisplay:YES]; 00500 } 00501 00502 return self; 00503 } 00504 00510 - (void)encodeWithCoder:(CPCoder)aCoder 00511 { 00512 // We do this in order to avoid encoding the _shadowView, which 00513 // should just automatically be created programmatically as needed. 00514 if (_shadowView) 00515 { 00516 var actualSubviews = _subviews; 00517 00518 _subviews = [_subviews copy]; 00519 [_subviews removeObjectIdenticalTo:_shadowView]; 00520 } 00521 00522 [super encodeWithCoder:aCoder]; 00523 00524 if (_shadowView) 00525 _subviews = actualSubviews; 00526 00527 [aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey]; 00528 [aCoder encodeInt:_imageAlignment forKey:CPImageViewImageAlignmentKey]; 00529 00530 if (_isEditable) 00531 [aCoder encodeBool:_isEditable forKey:CPImageViewIsEditableKey]; 00532 } 00533 00534 @end