API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPTabView.j
Go to the documentation of this file.
1 /*
2  * CPTabView.j
3  * AppKit
4  *
5  * Created by Derek Hammer.
6  * Copyright 2010, Derek Hammer.
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 
25 //CPLeftTabsBezelBorder = 1;
27 //CPRightTabsBezelBorder = 3;
28 CPNoTabsBezelBorder = 4; //Displays no tabs and has a bezeled border.
29 CPNoTabsLineBorder = 5; //Has no tabs and displays a line border.
30 CPNoTabsNoBorder = 6; //Displays no tabs and no border.
31 
36 
45 @implementation CPTabView : CPView
46 {
47  CPArray _items;
48 
49  CPSegmentedControl _tabs;
50  CPBox _box;
51 
52  CPNumber _selectedIndex;
53 
54  CPTabViewType _type;
55  CPFont _font;
56 
57  id _delegate;
58  unsigned _delegateSelectors;
59 }
60 
61 - (id)initWithFrame:(CGRect)aFrame
62 {
63  if (self = [super initWithFrame:aFrame])
64  {
65  _items = [CPArray array];
66 
67  [self _init];
68  [self setTabViewType:CPTopTabsBezelBorder];
69  }
70 
71  return self;
72 }
73 
74 - (void)_init
75 {
76  _selectedIndex = CPNotFound;
77 
78  _tabs = [[CPSegmentedControl alloc] initWithFrame:_CGRectMake(0, 0, 0, 0)];
79  [_tabs setHitTests:NO];
80 
81  var height = [_tabs valueForThemeAttribute:@"default-height"];
82  [_tabs setFrameSize:_CGSizeMake(0, height)];
83 
84  _box = [[CPBox alloc] initWithFrame:[self bounds]];
85  [self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]];
86 
87  [self addSubview:_box];
88  [self addSubview:_tabs];
89 }
90 
91 // Adding and Removing Tabs
96 - (void)addTabViewItem:(CPTabViewItem)aTabViewItem
97 {
98  [self insertTabViewItem:aTabViewItem atIndex:[_items count]];
99 }
100 
106 - (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex
107 {
108  [_items insertObject:aTabViewItem atIndex:anIndex];
109 
110  [self _updateItems];
111  [self _repositionTabs];
112 
113  [aTabViewItem _setTabView:self];
114 
115  if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
116  [_delegate tabViewDidChangeNumberOfTabViewItems:self];
117 }
118 
123 - (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
124 {
125  var count = [_items count];
126  for (var i = 0; i < count; i++)
127  {
128  if ([_items objectAtIndex:i] === aTabViewItem)
129  {
130  [_items removeObjectAtIndex:i];
131  break;
132  }
133  }
134 
135  [self _updateItems];
136  [self _repositionTabs];
137 
138  [aTabViewItem _setTabView:nil];
139 
140  if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
141  [_delegate tabViewDidChangeNumberOfTabViewItems:self];
142 }
143 
144 // Accessing Tabs
150 - (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
151 {
152  return [_items indexOfObjectIdenticalTo:aTabViewItem];
153 }
154 
160 - (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
161 {
162  for (var index = [_items count]; index >= 0; index--)
163  if ([[_items[index] identifier] isEqual:anIdentifier])
164  return index;
165 
166  return CPNotFound;
167 }
168 
173 - (unsigned)numberOfTabViewItems
174 {
175  return [_items count];
176 }
177 
182 - (CPTabViewItem)tabViewItemAtIndex:(unsigned)anIndex
183 {
184  return [_items objectAtIndex:anIndex];
185 }
186 
191 - (CPArray)tabViewItems
192 {
193  return [_items copy]; // Copy?
194 }
195 
196 // Selecting a Tab
201 - (void)selectFirstTabViewItem:(id)aSender
202 {
203  if ([_items count] === 0)
204  return; // throw?
205 
206  [self selectTabViewItemAtIndex:0];
207 }
208 
213 - (void)selectLastTabViewItem:(id)aSender
214 {
215  if ([_items count] === 0)
216  return; // throw?
217 
218  [self selectTabViewItemAtIndex:[_items count] - 1];
219 }
220 
225 - (void)selectNextTabViewItem:(id)aSender
226 {
227  if (_selectedIndex === CPNotFound)
228  return;
229 
230  var nextIndex = _selectedIndex + 1;
231 
232  if (nextIndex === [_items count])
233  // does nothing. According to spec at (http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSTabView_Class/Reference/Reference.html#//apple_ref/occ/instm/NSTabView/selectNextTabViewItem:)
234  return;
235 
236  [self selectTabViewItemAtIndex:nextIndex];
237 }
238 
243 - (void)selectPreviousTabViewItem:(id)aSender
244 {
245  if (_selectedIndex === CPNotFound)
246  return;
247 
248  var previousIndex = _selectedIndex - 1;
249 
250  if (previousIndex < 0)
251  return; // does nothing. See above.
252 
253  [self selectTabViewItemAtIndex:previousIndex];
254 }
255 
260 - (void)selectTabViewItem:(CPTabViewItem)aTabViewItem
261 {
262  [self selectTabViewItemAtIndex:[self indexOfTabViewItem:aTabViewItem]];
263 }
264 
269 - (BOOL)selectTabViewItemAtIndex:(unsigned)anIndex
270 {
271  if (anIndex === _selectedIndex)
272  return;
273 
274  var aTabViewItem = [self tabViewItemAtIndex:anIndex];
275 
276  if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
277  return NO;
278 
279  if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
280  [_delegate tabView:self willSelectTabViewItem:aTabViewItem];
281 
282  [_tabs selectSegmentWithTag:anIndex];
283  [self _setSelectedIndex:anIndex];
284 
285  if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
286  [_delegate tabView:self didSelectTabViewItem:aTabViewItem];
287 
288  return YES;
289 }
290 
295 - (CPTabViewItem)selectedTabViewItem
296 {
297  if (_selectedIndex != CPNotFound)
298  return [_items objectAtIndex:_selectedIndex];
299 
300  return nil;
301 }
302 
303 // Modifying the font
308 - (CPFont)font
309 {
310  return _font;
311 }
312 
317 - (void)setFont:(CPFont)font
318 {
319  if ([_font isEqual:font])
320  return;
321 
322  _font = font;
323  [_tabs setFont:_font];
324 }
325 
326 //
331 - (void)setTabViewType:(CPTabViewType)aTabViewType
332 {
333  if (_type === aTabViewType)
334  return;
335 
336  _type = aTabViewType;
337 
338  if (_type !== CPTopTabsBezelBorder && _type !== CPBottomTabsBezelBorder)
339  [_tabs removeFromSuperview];
340  else
341  [self addSubview:_tabs];
342 
343  switch (_type)
344  {
347  case CPNoTabsBezelBorder:
348  [_box setBorderType:CPBezelBorder];
349  break;
350  case CPNoTabsLineBorder:
351  [_box setBorderType:CPLineBorder];
352  break;
353  case CPNoTabsNoBorder:
354  [_box setBorderType:CPNoBorder];
355  break;
356  }
357 
358  [self setNeedsLayout];
359 }
360 
361 - (void)layoutSubviews
362 {
363  // Even if CPTabView's autoresizesSubviews is NO, _tabs and _box has to be laid out.
364  // This means we can't rely on autoresize masks.
365  if (_type !== CPTopTabsBezelBorder && _type !== CPBottomTabsBezelBorder)
366  {
367  [_box setFrame:[self bounds]];
368  }
369  else
370  {
371  var aFrame = [self frame],
372  segmentedHeight = _CGRectGetHeight([_tabs frame]),
373  origin = _type === CPTopTabsBezelBorder ? segmentedHeight / 2 : 0;
374 
375  [_box setFrame:_CGRectMake(0, origin, _CGRectGetWidth(aFrame),
376  _CGRectGetHeight(aFrame) - segmentedHeight / 2)];
377 
378  [self _repositionTabs];
379  }
380 }
381 
386 - (CPTabViewType)tabViewType
387 {
388  return _type;
389 }
390 
395 - (id)delegate
396 {
397  return _delegate;
398 }
399 
404 - (void)setDelegate:(id)aDelegate
405 {
406  if (_delegate == aDelegate)
407  return;
408 
409  _delegate = aDelegate;
410 
411  _delegateSelectors = 0;
412 
413  if ([_delegate respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)])
414  _delegateSelectors |= CPTabViewShouldSelectTabViewItemSelector;
415 
416  if ([_delegate respondsToSelector:@selector(tabView:willSelectTabViewItem:)])
417  _delegateSelectors |= CPTabViewWillSelectTabViewItemSelector;
418 
419  if ([_delegate respondsToSelector:@selector(tabView:didSelectTabViewItem:)])
420  _delegateSelectors |= CPTabViewDidSelectTabViewItemSelector;
421 
422  if ([_delegate respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)])
424 }
425 
426 - (void)setBackgroundColor:(CPColor)aColor
427 {
428  [_box setBackgroundColor:aColor];
429 }
430 
431 - (CPColor)backgroundColor
432 {
433  return [_box backgroundColor];
434 }
435 
436 - (void)mouseDown:(CPEvent)anEvent
437 {
438  var segmentIndex = [_tabs testSegment:[_tabs convertPoint:[anEvent locationInWindow] fromView:nil]];
439 
440  if (segmentIndex != CPNotFound && [self selectTabViewItemAtIndex:segmentIndex])
441  [_tabs trackSegment:anEvent];
442 }
443 
444 - (void)_repositionTabs
445 {
446  var horizontalCenterOfSelf = _CGRectGetWidth([self bounds]) / 2,
447  verticalCenterOfTabs = _CGRectGetHeight([_tabs bounds]) / 2;
448 
449  if (_type === CPBottomTabsBezelBorder)
450  [_tabs setCenter:_CGPointMake(horizontalCenterOfSelf, _CGRectGetHeight([self bounds]) - verticalCenterOfTabs)];
451  else
452  [_tabs setCenter:_CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
453 }
454 
455 - (void)_setSelectedIndex:(CPNumber)index
456 {
457  _selectedIndex = index;
458  [self _setContentViewFromItem:[_items objectAtIndex:_selectedIndex]];
459 }
460 
461 - (void)_setContentViewFromItem:(CPTabViewItem)anItem
462 {
463  [_box setContentView:[anItem view]];
464 }
465 
466 - (void)_updateItems
467 {
468  var count = [_items count];
469  [_tabs setSegmentCount:count];
470 
471  for (var i = 0; i < count; i++)
472  {
473  [_tabs setLabel:[[_items objectAtIndex:i] label] forSegment:i];
474  [_tabs setTag:i forSegment:i];
475  }
476 
477  if (_selectedIndex === CPNotFound)
478  [self selectFirstTabViewItem:self];
479 }
480 
481 @end
482 
483 var CPTabViewItemsKey = "CPTabViewItemsKey",
484  CPTabViewSelectedItemKey = "CPTabViewSelectedItemKey",
485  CPTabViewTypeKey = "CPTabViewTypeKey",
486  CPTabViewFontKey = "CPTabViewFontKey",
487  CPTabViewDelegateKey = "CPTabViewDelegateKey";
488 
489 @implementation CPTabView (CPCoding)
490 
491 - (id)initWithCoder:(CPCoder)aCoder
492 {
493  if (self = [super initWithCoder:aCoder])
494  {
495  [self _init];
496 
497  _font = [aCoder decodeObjectForKey:CPTabViewFontKey];
498  [_tabs setFont:_font];
499 
500  _items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
501  [_items makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
502 
503  [self _updateItems];
504 
505  [self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
506 
507  var selected = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
508  if (selected)
509  [self selectTabViewItem:selected];
510 
511  [self setTabViewType:[aCoder decodeIntForKey:CPTabViewTypeKey]];
512 
513  [self setNeedsLayout];
514  }
515 
516  return self;
517 }
518 
519 - (void)encodeWithCoder:(CPCoder)aCoder
520 {
521  [super encodeWithCoder:aCoder];
522 
523  [aCoder encodeObject:_items forKey:CPTabViewItemsKey];
524 
525  var selected = [self selectedTabViewItem];
526  if (selected)
527  [aCoder encodeObject:selected forKey:CPTabViewSelectedItemKey];
528 
529  [aCoder encodeInt:_type forKey:CPTabViewTypeKey];
530  [aCoder encodeObject:_font forKey:CPTabViewFontKey];
531 
532  [aCoder encodeConditionalObject:_delegate forKey:CPTabViewDelegateKey];
533 }
534 
535 @end