API  0.9.7
 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  _CPPopoverWindow _popoverWindow;
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 (![_popoverWindow isVisible])
99  return CGRectMakeZero();
100 
101  return [_popoverWindow frame];
102 }
103 
107 - (void)setPositioningRect:(CGRect)aRect
108 {
109  if (![_popoverWindow isVisible])
110  return;
111 
112  [_popoverWindow setFrame:aRect];
113 }
114 
120 - (CGSize)contentSize
121 {
122  if (![_popoverWindow isVisible])
123  return CGRectMakeZero();
124 
125  return [[_contentViewController view] frameSize];
126 }
127 
133 - (void)setContentSize:(CGSize)aSize
134 {
135  [[_contentViewController view] setFrameSize:aSize];
136 }
137 
143 - (BOOL)isShown
144 {
145  return [_popoverWindow isVisible];
146 }
147 
153 - (void)setAnimates:(BOOL)shouldAnimate
154 {
155  if (_animates == shouldAnimate)
156  return;
157 
158  _animates = shouldAnimate;
159  [_popoverWindow setAnimates:_animates];
160 }
161 
170 - (void)setBehavior:(int)aBehavior
171 {
172  if (_behavior == aBehavior)
173  return;
174 
175  _behavior = aBehavior;
176  [_popoverWindow setStyleMask:[self styleMaskForBehavior]];
177 }
178 
179 - (void)setDelegate:(id)aDelegate
180 {
181  if (_delegate === aDelegate)
182  return;
183 
184  _delegate = aDelegate;
185  _implementedDelegateMethods = 0;
186 
187  if ([_delegate respondsToSelector:@selector(popoverWillShow:)])
188  _implementedDelegateMethods |= CPPopoverDelegate_popover_willShow_;
189 
190  if ([_delegate respondsToSelector:@selector(popoverDidShow:)])
191  _implementedDelegateMethods |= CPPopoverDelegate_popover_didShow_;
192 
193  if ([_delegate respondsToSelector:@selector(popoverShouldClose:)])
194  _implementedDelegateMethods |= CPPopoverDelegate_popover_shouldClose_;
195 
196  if ([_delegate respondsToSelector:@selector(popoverWillClose:)])
197  _implementedDelegateMethods |= CPPopoverDelegate_popover_willClose_;
198 
199  if ([_delegate respondsToSelector:@selector(popoverDidClose:)])
200  _implementedDelegateMethods |= CPPopoverDelegate_popover_didClose_;
201 }
202 
203 #pragma mark -
204 #pragma mark Positioning
205 
213 - (void)showRelativeToRect:(CGRect)positioningRect ofView:(CPView)positioningView preferredEdge:(CPRectEdge)preferredEdge
214 {
215  if (!positioningView)
216  [CPException raise:CPInvalidArgumentException reason:"positionView must not be nil"];
217 
218  if (!_contentViewController)
219  [CPException raise:CPInternalInconsistencyException reason:@"contentViewController must not be nil"];
220 
221  // If the popover is currently closing, do nothing. That is what Cocoa does.
222  if ([_popoverWindow isClosing])
223  return;
224 
225  if (_implementedDelegateMethods & CPPopoverDelegate_popover_willShow_)
226  [_delegate popoverWillShow:self];
227 
228  if (!_popoverWindow)
229  {
230  _popoverWindow = [[_CPPopoverWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:[self styleMaskForBehavior]];
231  }
232 
233  [_popoverWindow setPlatformWindow:[[positioningView window] platformWindow]];
234  [_popoverWindow setAppearance:_appearance];
235  [_popoverWindow setAnimates:_animates];
236  [_popoverWindow setDelegate:self];
237  [_popoverWindow setMovableByWindowBackground:NO];
238  [_popoverWindow setFrame:[_popoverWindow frameRectForContentRect:[[_contentViewController view] frame]]];
239  [_popoverWindow setContentView:[_contentViewController view]];
240  [_popoverWindow positionRelativeToRect:positioningRect ofView:positioningView preferredEdge:preferredEdge];
241 
242  if (!_animates && _implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
243  [_delegate popoverDidShow:self];
244 }
245 
246 - (unsigned)styleMaskForBehavior
247 {
248  return (_behavior == CPPopoverBehaviorTransient) ? CPClosableOnBlurWindowMask : 0;
249 }
250 
254 - (void)close
255 {
256  [self _close];
257 }
258 
262 - (void)_close
263 {
264  if ([_popoverWindow isClosing] || ![self isShown])
265  return;
266 
267  if (_implementedDelegateMethods & CPPopoverDelegate_popover_willClose_)
268  [_delegate popoverWillClose:self];
269 
270  [_popoverWindow close];
271 
272  // popoverDidClose will be sent from popoverWindowDidClose, since
273  // the popover window will close asynchronously when animating.
274 }
275 
276 
277 #pragma mark -
278 #pragma mark Action
279 
285 - (IBAction)performClose:(id)sender
286 {
287  if ([_popoverWindow isClosing])
288  return;
289 
290  if (_implementedDelegateMethods & CPPopoverDelegate_popover_shouldClose_)
291  if (![_delegate popoverShouldClose:self])
292  return;
293 
294  [self _close];
295 }
296 
297 
298 #pragma mark -
299 #pragma mark Delegates
300 
302 - (BOOL)popoverWindowShouldClose:(_CPPopoverWindow)aPopoverWindow
303 {
304  [self performClose:self];
305 
306  // We return NO, because we want the CPPopover to determine
307  // if the popover window can be closed and to give us a chance
308  // to send delegate messages.
309  return NO;
310 }
311 
313 - (void)popoverWindowDidClose:(_CPPopoverWindow)aPopoverWindow
314 {
315  if (_implementedDelegateMethods & CPPopoverDelegate_popover_didClose_)
316  [_delegate popoverDidClose:self];
317 }
318 
320 - (void)popoverWindowDidShow:(_CPPopoverWindow)aPopoverWindow
321 {
322  if (_implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
323  [_delegate popoverDidShow:self];
324 }
325 
326 @end
327 
328 @implementation CPPopover (Deprecated)
329 
330 - (void)setBehaviour:(int)aBehavior
331 {
332  _CPReportLenientDeprecation(self, _cmd, @selector(setBehavior:));
333 
334  [self setBehavior:aBehavior];
335 }
336 
337 @end
338 
339 var CPPopoverNeedsNewPopoverWindowKey = @"CPPopoverNeedsNewPopoverWindowKey",
340  CPPopoverAppearanceKey = @"CPPopoverAppearanceKey",
341  CPPopoverAnimatesKey = @"CPPopoverAnimatesKey",
342  CPPopoverContentViewControllerKey = @"CPPopoverContentViewControllerKey",
343  CPPopoverDelegateKey = @"CPPopoverDelegateKey",
344  CPPopoverBehaviorKey = @"CPPopoverBehaviorKey";
345 
346 @implementation CPPopover (CPCoding)
347 
348 - (id)initWithCoder:(CPCoder)aCoder
349 {
350  self = [super initWithCoder:aCoder];
351 
352  if (self)
353  {
354  _appearance = [aCoder decodeIntForKey:CPPopoverAppearanceKey];
355  _animates = [aCoder decodeBoolForKey:CPPopoverAnimatesKey];
356  _contentViewController = [aCoder decodeObjectForKey:CPPopoverContentViewControllerKey];
357  [self setDelegate:[aCoder decodeObjectForKey:CPPopoverDelegateKey]];
358  [self setBehavior:[aCoder decodeIntForKey:CPPopoverBehaviorKey]];
359  }
360  return self;
361 }
362 
363 - (void)encodeWithCoder:(CPCoder)aCoder
364 {
365  [super encodeWithCoder:aCoder];
366 
367  [aCoder encodeInt:_appearance forKey:CPPopoverAppearanceKey];
368  [aCoder encodeBool:_animates forKey:CPPopoverAnimatesKey];
369  [aCoder encodeObject:_contentViewController forKey:CPPopoverContentViewControllerKey];
370  [aCoder encodeObject:_delegate forKey:CPPopoverDelegateKey];
371  [aCoder encodeInt:_behavior forKey:CPPopoverBehaviorKey];
372 }
373 
374 @end
375 
377 
381 - (CPViewController)contentViewController
382 {
383  return _contentViewController;
384 }
385 
389 - (void)setContentViewController:(CPViewController)aValue
390 {
391  _contentViewController = aValue;
392 }
393 
397 - (id)delegate
398 {
399  return _delegate;
400 }
401 
405 - (BOOL)animates
406 {
407  return _animates;
408 }
409 
413 - (int)appearance
414 {
415  return _appearance;
416 }
417 
421 - (void)setAppearance:(int)aValue
422 {
423  _appearance = aValue;
424 }
425 
429 - (int)behavior
430 {
431  return _behavior;
432 }
433 
434 @end