API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPImageView.j
Go to the documentation of this file.
1 /*
2  * CPImageView.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
34 
36 
43 @implementation CPImageView : CPControl
44 {
45  DOMElement _DOMImageElement;
46 
47  BOOL _hasShadow;
48  CPView _shadowView;
49 
50  BOOL _isEditable;
51 
52  CGRect _imageRect;
53  CPImageAlignment _imageAlignment;
54 }
55 
56 + (void)initialize
57 {
58  if (self !== [CPImageView class])
59  return;
60 
61  var bundle = [CPBundle bundleForClass:[CPView class]];
62 
63  CPImageViewEmptyPlaceholderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"empty.png"]];
64 }
65 
66 + (Class)_binderClassForBinding:(CPString)theBinding
67 {
68  if (theBinding === CPValueBinding || theBinding === CPValueURLBinding || theBinding === CPValuePathBinding || theBinding === CPDataBinding)
70 
71  return [super _binderClassForBinding:theBinding];
72 }
73 
74 - (id)initWithFrame:(CGRect)aFrame
75 {
76  self = [super initWithFrame:aFrame];
77 
78  if (self)
79  {
80 #if PLATFORM(DOM)
81  _DOMImageElement = document.createElement("img");
82  _DOMImageElement.style.position = "absolute";
83  _DOMImageElement.style.left = "0px";
84  _DOMImageElement.style.top = "0px";
85 
86  if ([CPPlatform supportsDragAndDrop])
87  {
88  _DOMImageElement.setAttribute("draggable", "true");
89  _DOMImageElement.style["-khtml-user-drag"] = "element";
90  }
91 
92  CPDOMDisplayServerAppendChild(_DOMElement, _DOMImageElement);
93 
94  _DOMImageElement.style.visibility = "hidden";
95 #endif
96  }
97 
98  return self;
99 }
100 
104 - (CPImage)image
105 {
106  return [self objectValue];
107 }
108 
109 - (void)setImage:(CPImage)anImage
110 {
111  [self setObjectValue:anImage];
112 }
113 
115 - (void)setObjectValue:(CPImage)anImage
116 {
117  var oldImage = [self objectValue];
118 
119  if (oldImage === anImage)
120  return;
121 
122  [super setObjectValue:anImage];
123 
124  var defaultCenter = [CPNotificationCenter defaultCenter];
125 
126  if (oldImage)
127  [defaultCenter removeObserver:self name:CPImageDidLoadNotification object:oldImage];
128 
129  var newImage = [self objectValue];
130 
131 #if PLATFORM(DOM)
132  _DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
133 #endif
134 
135  var size = [newImage size];
136 
137  if (size && size.width === -1 && size.height === -1)
138  {
139  [defaultCenter addObserver:self selector:@selector(imageDidLoad:) name:CPImageDidLoadNotification object:newImage];
140 
141 #if PLATFORM(DOM)
142  _DOMImageElement.width = 0;
143  _DOMImageElement.height = 0;
144 #endif
145 
146  [_shadowView setHidden:YES];
147  }
148  else
149  {
150  [self hideOrDisplayContents];
151  [self setNeedsLayout];
152  [self setNeedsDisplay:YES];
153  }
154 }
155 
156 - (void)imageDidLoad:(CPNotification)aNotification
157 {
158  [self hideOrDisplayContents];
159 
160  [self setNeedsLayout];
161  [self setNeedsDisplay:YES];
162 }
163 
168 - (BOOL)hasShadow
169 {
170  return _hasShadow;
171 }
172 
177 - (void)setHasShadow:(BOOL)shouldHaveShadow
178 {
179  if (_hasShadow == shouldHaveShadow)
180  return;
181 
182  _hasShadow = shouldHaveShadow;
183 
184  if (_hasShadow)
185  {
186  _shadowView = [[CPShadowView alloc] initWithFrame:[self bounds]];
187 
188  [self addSubview:_shadowView];
189 
190  [self setNeedsLayout];
191  [self setNeedsDisplay:YES];
192  }
193  else
194  {
195  [_shadowView removeFromSuperview];
196 
197  _shadowView = nil;
198  }
199 
200  [self hideOrDisplayContents];
201 }
202 
208 - (void)setImageAlignment:(CPImageAlignment)anImageAlignment
209 {
210  if (_imageAlignment == anImageAlignment)
211  return;
212 
213  _imageAlignment = anImageAlignment;
214 
215  if (![self image])
216  return;
217 
218  [self setNeedsLayout];
219  [self setNeedsDisplay:YES];
220 }
221 
222 - (unsigned)imageAlignment
223 {
224  return _imageAlignment;
225 }
226 
232 - (void)setImageScaling:(CPImageScaling)anImageScaling
233 {
234  [super setImageScaling:anImageScaling];
235 
236 #if PLATFORM(DOM)
237  if ([self currentValueForThemeAttribute:@"image-scaling"] === CPImageScaleAxesIndependently)
238  {
239  CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, 0.0, 0.0);
240  }
241 #endif
242 
243  [self setNeedsLayout];
244  [self setNeedsDisplay:YES];
245 }
246 
247 - (unsigned)imageScaling
248 {
249  return [self currentValueForThemeAttribute:@"image-scaling"];
250 }
251 
255 - (void)hideOrDisplayContents
256 {
257  if (![self image])
258  {
259 #if PLATFORM(DOM)
260  _DOMImageElement.style.visibility = "hidden";
261 #endif
262  [_shadowView setHidden:YES];
263  }
264  else
265  {
266 #if PLATFORM(DOM)
267  _DOMImageElement.style.visibility = "visible";
268 #endif
269  [_shadowView setHidden:NO];
270  }
271 }
272 
276 - (CGRect)imageRect
277 {
278  return _imageRect;
279 }
280 
284 - (void)layoutSubviews
285 {
286  if (![self image])
287  return;
288 
289  var bounds = [self bounds],
290  image = [self image],
291  imageScaling = [self currentValueForThemeAttribute:@"image-scaling"],
292  x = 0.0,
293  y = 0.0,
294  insetWidth = (_hasShadow ? [_shadowView horizontalInset] : 0.0),
295  insetHeight = (_hasShadow ? [_shadowView verticalInset] : 0.0),
296  boundsWidth = _CGRectGetWidth(bounds),
297  boundsHeight = _CGRectGetHeight(bounds),
298  width = boundsWidth - insetWidth,
299  height = boundsHeight - insetHeight;
300 
301  if (imageScaling === CPImageScaleAxesIndependently)
302  {
303  #if PLATFORM(DOM)
304  _DOMImageElement.width = ROUND(width);
305  _DOMImageElement.height = ROUND(height);
306  #endif
307  }
308  else
309  {
310  var size = [image size];
311 
312  if (size.width == -1 && size.height == -1)
313  return;
314 
315  switch (imageScaling)
316  {
318  if (width >= size.width && height >= size.height)
319  {
320  width = size.width;
321  height = size.height;
322  break;
323  }
324 
325  // intentionally fall through to the next case
326 
328  var imageRatio = size.width / size.height,
329  viewRatio = width / height;
330 
331  if (viewRatio > imageRatio)
332  width = height * imageRatio;
333  else
334  height = width / imageRatio;
335  break;
336 
338  case CPImageScaleNone:
339  width = size.width;
340  height = size.height;
341  break;
342  }
343 
344  #if PLATFORM(DOM)
345  _DOMImageElement.width = ROUND(width);
346  _DOMImageElement.height = ROUND(height);
347  #endif
348 
349  var x,
350  y;
351 
352  switch (_imageAlignment)
353  {
354  case CPImageAlignLeft:
355  case CPImageAlignTopLeft:
357  x = 0.0;
358  break;
359 
360  case CPImageAlignRight:
363  x = boundsWidth - width;
364  break;
365 
366  default:
367  x = (boundsWidth - width) / 2.0;
368  break;
369  }
370 
371  switch (_imageAlignment)
372  {
373  case CPImageAlignTop:
374  case CPImageAlignTopLeft:
376  y = 0.0;
377  break;
378 
379  case CPImageAlignBottom:
382  y = boundsHeight - height;
383  break;
384 
385  default:
386  y = (boundsHeight - height) / 2.0;
387  break;
388  }
389 
390 #if PLATFORM(DOM)
391  CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, x, y);
392 #endif
393  }
394 
395  _imageRect = _CGRectMake(x, y, width, height);
396 
397  if (_hasShadow)
398  [_shadowView setFrame:_CGRectMake(x - [_shadowView leftInset], y - [_shadowView topInset], width + insetWidth, height + insetHeight)];
399 }
400 
401 - (void)mouseDown:(CPEvent)anEvent
402 {
403  // Should we do something with this event?
404  [[self nextResponder] mouseDown:anEvent];
405 }
406 
407 - (void)setEditable:(BOOL)shouldBeEditable
408 {
409  if (_isEditable === shouldBeEditable)
410  return;
411 
412  _isEditable = shouldBeEditable;
413 
414  if (_isEditable)
415  [self registerForDraggedTypes:[CPImagesPboardType]];
416 
417  else
418  {
419  var draggedTypes = [self registeredDraggedTypes];
420 
421  [self unregisterDraggedTypes];
422 
423  [draggedTypes removeObjectIdenticalTo:CPImagesPboardType];
424 
425  [self registerForDraggedTypes:draggedTypes];
426  }
427 }
428 
429 - (BOOL)isEditable
430 {
431  return _isEditable;
432 }
433 
434 - (BOOL)performDragOperation:(CPDraggingInfo)aSender
435 {
436  var images = [CPKeyedUnarchiver unarchiveObjectWithData:[[aSender draggingPasteboard] dataForType:CPImagesPboardType]];
437 
438  if ([images count])
439  {
440  [self setImage:images[0]];
441  [self sendAction:[self action] to:[self target]];
442  }
443 
444  return YES;
445 }
446 
447 @end
449 {
450  id __doxygen__;
451 }
452 
453 - (void)_updatePlaceholdersWithOptions:(CPDictionary)options
454 {
455  [self _setPlaceholder:nil forMarker:CPMultipleValuesMarker isDefault:YES];
456  [self _setPlaceholder:nil forMarker:CPNoSelectionMarker isDefault:YES];
457  [self _setPlaceholder:nil forMarker:CPNotApplicableMarker isDefault:YES];
458  [self _setPlaceholder:nil forMarker:CPNullMarker isDefault:YES];
459 }
460 
461 - (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
462 {
463  [_source setImage:nil];
464 }
465 
466 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
467 {
468  var image;
469 
470  if (aBinding === CPDataBinding)
471  image = [[CPImage alloc] initWithData:aValue];
472  else if (aBinding === CPValueURLBinding || aBinding === CPValuePathBinding)
473  image = [[CPImage alloc] initWithContentsOfFile:aValue];
474  else if (aBinding === CPValueBinding)
475  image = aValue;
476 
477  [_source setImage:image];
478 }
479 
480 @end
481 
482 var CPImageViewImageKey = @"CPImageViewImageKey",
483  CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
484  CPImageViewImageAlignmentKey = @"CPImageViewImageAlignmentKey",
485  CPImageViewHasShadowKey = @"CPImageViewHasShadowKey",
486  CPImageViewIsEditableKey = @"CPImageViewIsEditableKey";
487 
488 @implementation CPImageView (CPCoding)
489 
495 - (id)initWithCoder:(CPCoder)aCoder
496 {
497 #if PLATFORM(DOM)
498  _DOMImageElement = document.createElement("img");
499  _DOMImageElement.style.position = "absolute";
500  _DOMImageElement.style.left = "0px";
501  _DOMImageElement.style.top = "0px";
502  _DOMImageElement.style.visibility = "hidden";
503  if ([CPPlatform supportsDragAndDrop])
504  {
505  _DOMImageElement.setAttribute("draggable", "true");
506  _DOMImageElement.style["-khtml-user-drag"] = "element";
507  }
508 
509  if (typeof(appkit_tag_dom_elements) !== "undefined" && !!appkit_tag_dom_elements)
510  _DOMImageElement.setAttribute("data-cappuccino-view", [self className]);
511 #endif
512 
513  self = [super initWithCoder:aCoder];
514 
515  if (self)
516  {
517 #if PLATFORM(DOM)
518  _DOMElement.appendChild(_DOMImageElement);
519 #endif
520 
521  [self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]];
522  [self setImageAlignment:[aCoder decodeIntForKey:CPImageViewImageAlignmentKey]];
523 
524  if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey])
525  [self setEditable:YES];
526 
527  [self setNeedsLayout];
528  [self setNeedsDisplay:YES];
529  }
530 
531  return self;
532 }
533 
539 - (void)encodeWithCoder:(CPCoder)aCoder
540 {
541  // We do this in order to avoid encoding the _shadowView, which
542  // should just automatically be created programmatically as needed.
543  if (_shadowView)
544  {
545  var actualSubviews = _subviews;
546 
547  _subviews = [_subviews copy];
548  [_subviews removeObjectIdenticalTo:_shadowView];
549  }
550 
551  [super encodeWithCoder:aCoder];
552 
553  if (_shadowView)
554  _subviews = actualSubviews;
555 
556  [aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey];
557  [aCoder encodeInt:_imageAlignment forKey:CPImageViewImageAlignmentKey];
558 
559  if (_isEditable)
560  [aCoder encodeBool:_isEditable forKey:CPImageViewIsEditableKey];
561 }
562 
563 @end