API 0.9.5
AppKit/CPImageView.j
Go to the documentation of this file.
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
 All Classes Files Functions Variables Defines