API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPPopover.j
Go to the documentation of this file.
1 /*
2  * CPPopover.j
3  * AppKit
4  *
5  * Created by Antoine Mercadal.
6  * Copyright 2011 Antoine Mercadal.
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 
25 
29 
35 
36 
53 @implementation CPPopover : CPResponder
54 {
55  @outlet CPViewController _contentViewController;
56  @outlet id _delegate;
57 
58  BOOL _animates;
59  int _appearance;
60  int _behavior;
61 
62  _CPAttachedWindow _attachedWindow;
63  int _implementedDelegateMethods;
64 }
65 
66 
67 #pragma mark -
68 #pragma mark Initialization
69 
75 - (CPPopover)init
76 {
77  if (self = [super init])
78  {
79  _animates = YES;
80  _appearance = CPPopoverAppearanceMinimal;
82  }
83 
84  return self;
85 }
86 
87 
88 #pragma mark -
89 #pragma mark Getters / Setters
90 
96 - (CGRect)positioningRect
97 {
98  if (![_attachedWindow isVisible])
99  return CGRectMakeZero();
100  return [_attachedWindow frame];
101 }
102 
106 - (void)setPositioningRect:(CGRect)aRect
107 {
108  if (![_attachedWindow isVisible])
109  return;
110  [_attachedWindow setFrame:aRect];
111 }
112 
118 - (CGSize)contentSize
119 {
120  if (![_attachedWindow isVisible])
121  return CGRectMakeZero();
122  return [[_contentViewController view] frameSize];
123 }
124 
130 - (void)setContentSize:(CPSize)aSize
131 {
132  [[_contentViewController view] setFrameSize:aSize];
133 }
134 
140 - (BOOL)isShown
141 {
142  return [_attachedWindow isVisible];
143 }
144 
153 - (void)setBehavior:(int)aBehavior
154 {
155  if (_behavior == aBehavior)
156  return;
157 
158  _behavior = aBehavior;
159  [_attachedWindow setStyleMask:[self styleMaskForBehavior]];
160 }
161 
162 - (void)setDelegate:(id)aDelegate
163 {
164  if (_delegate === aDelegate)
165  return;
166 
167  _delegate = aDelegate;
168  _implementedDelegateMethods = 0;
169 
170  if ([_delegate respondsToSelector:@selector(popoverWillShow:)])
171  _implementedDelegateMethods |= CPPopoverDelegate_popover_willShow_;
172 
173  if ([_delegate respondsToSelector:@selector(popoverDidShow:)])
174  _implementedDelegateMethods |= CPPopoverDelegate_popover_didShow_;
175 
176  if ([_delegate respondsToSelector:@selector(popoverShouldClose:)])
177  _implementedDelegateMethods |= CPPopoverDelegate_popover_shouldClose_;
178 
179  if ([_delegate respondsToSelector:@selector(popoverWillClose:)])
180  _implementedDelegateMethods |= CPPopoverDelegate_popover_willClose_;
181 
182  if ([_delegate respondsToSelector:@selector(popoverDidClose:)])
183  _implementedDelegateMethods |= CPPopoverDelegate_popover_didClose_;
184 }
185 
186 #pragma mark -
187 #pragma mark Positioning
188 
196 - (void)showRelativeToRect:(CGRect)positioningRect ofView:(CPView)positioningView preferredEdge:(CPRectEdge)preferredEdge
197 {
198  if (!positioningView)
199  [CPException raise:CPInvalidArgumentException reason:"positionView must not be nil"];
200 
201  if (!_contentViewController)
202  [CPException raise:CPInternalInconsistencyException reason:@"contentViewController must not be nil"];
203 
204  // If the popover is currently closing, do nothing. That is what Cocoa does.
205  if ([_attachedWindow isClosing])
206  return;
207 
208  if (_implementedDelegateMethods & CPPopoverDelegate_popover_willShow_)
209  [_delegate popoverWillShow:self];
210 
211  if (!_attachedWindow)
212  {
213  _attachedWindow = [[_CPAttachedWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:[self styleMaskForBehavior]];
214 
215  var parentWindow = [positioningView window];
216 
217  if (![parentWindow isKindOfClass:_CPAttachedWindow])
218  [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(parentWindowWillClose:) name:CPWindowWillCloseNotification object:parentWindow];
219  }
220 
221  [_attachedWindow setAppearance:_appearance];
222  [_attachedWindow setAnimates:_animates];
223  [_attachedWindow setDelegate:self];
224  [_attachedWindow setMovableByWindowBackground:NO];
225  [_attachedWindow setFrame:[_attachedWindow frameRectForContentRect:[[_contentViewController view] frame]]];
226  [_attachedWindow setContentView:[_contentViewController view]];
227  [_attachedWindow positionRelativeToRect:positioningRect ofView:positioningView preferredEdge:preferredEdge];
228 
229  if (!_animates && _implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
230  [_delegate popoverDidShow:self];
231 }
232 
233 - (unsigned)styleMaskForBehavior
234 {
235  return (_behavior == CPPopoverBehaviorTransient) ? CPClosableOnBlurWindowMask : 0;
236 }
237 
241 - (void)close
242 {
243  [self _close];
244 }
245 
249 - (void)_close
250 {
251  if ([_attachedWindow isClosing] || ![self isShown])
252  return;
253 
254  if (_implementedDelegateMethods & CPPopoverDelegate_popover_willClose_)
255  [_delegate popoverWillClose:self];
256 
257  [_attachedWindow close];
258 
259  // popoverDidClose will be sent from attachedWindowDidClose, since
260  // the attached window will close asynchronously when animating.
261 }
262 
263 
264 #pragma mark -
265 #pragma mark Action
266 
272 - (IBAction)performClose:(id)sender
273 {
274  if ([_attachedWindow isClosing])
275  return;
276 
277  if (_implementedDelegateMethods & CPPopoverDelegate_popover_shouldClose_)
278  if (![_delegate popoverShouldClose:self])
279  return;
280 
281  [self _close];
282 }
283 
284 
285 #pragma mark -
286 #pragma mark Delegates
287 
289 - (BOOL)attachedWindowShouldClose:(_CPAttachedWindow)anAttachedWindow
290 {
291  [self performClose:self];
292 
293  // We return NO, because we want the CPPopover to determine
294  // if the attached window can be closed and to give us a chance
295  // to send delegate messages.
296  return NO;
297 }
298 
300 - (void)attachedWindowDidClose:(_CPAttachedWindow)anAttachedWindow
301 {
302  if (_implementedDelegateMethods & CPPopoverDelegate_popover_didClose_)
303  [_delegate popoverDidClose:self];
304 }
305 
307 - (void)attachedWindowDidShow:(_CPAttachedWindow)anAttachedWindow
308 {
309  if (_implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
310  [_delegate popoverDidShow:self];
311 }
312 
313 
314 #pragma mark -
315 #pragma mark Notifications
316 
323 - (void)parentWindowWillClose:(CPNotification)aNotification
324 {
325  [_attachedWindow orderOut:nil];
326  [self performClose:nil];
327 }
328 
329 @end
330 
331 @implementation CPPopover (Deprecated)
332 
333 - (void)setBehaviour:(int)aBehavior
334 {
335  _CPReportLenientDeprecation(self, _cmd, @selector(setBehavior:));
336 
337  [self setBehavior:aBehavior];
338 }
339 
340 @end
341 
342 var CPPopoverNeedsNewAttachedWindowKey = @"CPPopoverNeedsNewAttachedWindowKey",
343  CPPopoverAppearanceKey = @"CPPopoverAppearanceKey",
344  CPPopoverAnimatesKey = @"CPPopoverAnimatesKey",
345  CPPopoverContentViewControllerKey = @"CPPopoverContentViewControllerKey",
346  CPPopoverDelegateKey = @"CPPopoverDelegateKey",
347  CPPopoverBehaviorKey = @"CPPopoverBehaviorKey";
348 
349 @implementation CPPopover (CPCoding)
350 
351 - (id)initWithCoder:(CPCoder)aCoder
352 {
353  self = [super initWithCoder:aCoder];
354 
355  if (self)
356  {
357  _appearance = [aCoder decodeIntForKey:CPPopoverAppearanceKey];
358  _animates = [aCoder decodeBoolForKey:CPPopoverAnimatesKey];
359  _contentViewController = [aCoder decodeObjectForKey:CPPopoverContentViewControllerKey];
360  [self setDelegate:[aCoder decodeObjectForKey:CPPopoverDelegateKey]];
361  [self setBehavior:[aCoder decodeIntForKey:CPPopoverBehaviorKey]];
362  }
363  return self;
364 }
365 
366 - (void)encodeWithCoder:(CPCoder)aCoder
367 {
368  [super encodeWithCoder:aCoder];
369 
370  [aCoder encodeInt:_appearance forKey:CPPopoverAppearanceKey];
371  [aCoder encodeBool:_animates forKey:CPPopoverAnimatesKey];
372  [aCoder encodeObject:_contentViewController forKey:CPPopoverContentViewControllerKey];
373  [aCoder encodeObject:_delegate forKey:CPPopoverDelegateKey];
374  [aCoder encodeInt:_behavior forKey:CPPopoverBehaviorKey];
375 }
376 
377 @end
378 
380 
384 - (CPViewController)contentViewController
385 {
386  return _contentViewController;
387 }
388 
392 - (void)setContentViewController:(CPViewController)aValue
393 {
394  _contentViewController = aValue;
395 }
396 
400 - (id)delegate
401 {
402  return _delegate;
403 }
404 
408 - (BOOL)animates
409 {
410  return _animates;
411 }
412 
416 - (void)setAnimates:(BOOL)aValue
417 {
418  _animates = aValue;
419 }
420 
424 - (int)appearance
425 {
426  return _appearance;
427 }
428 
432 - (void)setAppearance:(int)aValue
433 {
434  _appearance = aValue;
435 }
436 
440 - (int)behavior
441 {
442  return _behavior;
443 }
444 
445 @end