├── English.lproj ├── InfoPlist.strings ├── MainMenu.nib │ ├── classes.nib │ ├── info.nib │ ├── keyedobjects.nib │ └── objects.nib └── Window.nib │ └── keyedobjects.nib ├── Info.plist ├── PSMTabBarControl.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── PSMTabBarControlFramework-Info.plist ├── README.md ├── ReadMe.rtfd ├── TXT.rtf └── startpage.gif ├── TabBarControlDemo-Info.plist ├── documentation ├── PSMTabBarControlDoc.html └── frameset_styles.css ├── images ├── 32x32_log.png ├── 32x32cancel.png ├── AdiumGradient.png ├── AquaTabCloseDirty_Front.png ├── AquaTabCloseDirty_Front_Pressed.png ├── AquaTabCloseDirty_Front_Rollover.png ├── AquaTabClose_Front.png ├── AquaTabClose_Front_Pressed.png ├── AquaTabClose_Front_Rollover.png ├── AquaTabNew.png ├── AquaTabNewPressed.png ├── AquaTabNewRollover.png ├── AquaTabsBackground.png ├── AquaTabsDown.png ├── AquaTabsDownGraphite.png ├── AquaTabsDownNonKey.png ├── AquaTabsSeparator.png ├── AquaTabsSeparatorDown.png ├── Folder.png ├── Globe.png ├── TabClose_Dirty.png ├── TabClose_Dirty_Pressed.png ├── TabClose_Dirty_Rollover.png ├── TabClose_Front.png ├── TabClose_Front_Pressed.png ├── TabClose_Front_Rollover.png ├── TabNewMetal.png ├── TabNewMetalPressed.png ├── TabNewMetalRollover.png ├── Warning.png ├── largeImage.png ├── mater_large.jpg ├── mcqueen_large.jpg ├── overflowImage.png ├── overflowImagePressed.png ├── pi.png └── sally_large.jpg ├── source ├── Framework_Prefix.pch ├── NSBezierPath_AMShading.h ├── NSBezierPath_AMShading.m ├── NSString_AITruncation.h ├── NSString_AITruncation.m ├── PSMMetalTabStyle.h ├── PSMMetalTabStyle.m ├── PSMOverflowPopUpButton.h ├── PSMOverflowPopUpButton.m ├── PSMProgressIndicator.h ├── PSMProgressIndicator.m ├── PSMRolloverButton.h ├── PSMRolloverButton.m ├── PSMTabBarCell.h ├── PSMTabBarCell.m ├── PSMTabBarControl.h ├── PSMTabBarControl.m ├── PSMTabBarController.h ├── PSMTabBarController.m ├── PSMTabDragAssistant.h ├── PSMTabDragAssistant.m ├── PSMTabDragView.h ├── PSMTabDragView.m ├── PSMTabDragWindow.h ├── PSMTabDragWindow.m ├── PSMTabDragWindowController.h ├── PSMTabDragWindowController.m ├── PSMTabStyle.h └── demo │ ├── AppController.h │ ├── AppController.m │ ├── FakeModel.h │ ├── FakeModel.m │ ├── WindowController.h │ ├── WindowController.m │ └── main.m └── version.plist /English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /English.lproj/MainMenu.nib/classes.nib: -------------------------------------------------------------------------------- 1 | { 2 | IBClasses = ( 3 | { 4 | ACTIONS = {newWindow = id; }; 5 | CLASS = AppController; 6 | LANGUAGE = ObjC; 7 | SUPERCLASS = NSObject; 8 | }, 9 | {CLASS = FakeModel; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 10 | { 11 | ACTIONS = {addNewTab = id; closeTab = id; }; 12 | CLASS = FirstResponder; 13 | LANGUAGE = ObjC; 14 | SUPERCLASS = NSObject; 15 | }, 16 | { 17 | CLASS = PSMTabBarControl; 18 | LANGUAGE = ObjC; 19 | OUTLETS = {delegate = id; partnerView = id; style = id; tabView = NSTabView; }; 20 | SUPERCLASS = NSControl; 21 | } 22 | ); 23 | IBVersion = 1; 24 | } -------------------------------------------------------------------------------- /English.lproj/MainMenu.nib/info.nib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IBDocumentLocation 6 | 135 107 356 240 0 0 1680 1028 7 | IBEditorPositions 8 | 9 | 29 10 | 130 357 371 44 0 0 1680 1028 11 | 12 | IBFramework Version 13 | 443.0 14 | IBOpenObjects 15 | 16 | 29 17 | 18 | IBSystem Version 19 | 8H14 20 | 21 | 22 | -------------------------------------------------------------------------------- /English.lproj/MainMenu.nib/keyedobjects.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/English.lproj/MainMenu.nib/keyedobjects.nib -------------------------------------------------------------------------------- /English.lproj/MainMenu.nib/objects.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/English.lproj/MainMenu.nib/objects.nib -------------------------------------------------------------------------------- /English.lproj/Window.nib/keyedobjects.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/English.lproj/Window.nib/keyedobjects.nib -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | PSMTabBarControl 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.positivespinmedia.PSMTabBarControl 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleSignature 20 | PSM1 21 | CFBundleVersion 22 | 1.1 23 | 24 | 25 | -------------------------------------------------------------------------------- /PSMTabBarControl.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PSMTabBarControlFramework-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | PSMTabBarControl 9 | CFBundleIdentifier 10 | com.positivespinmedia.PSMTabBarControlFramework 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | FMWK 15 | CFBundleSignature 16 | PSM1 17 | CFBundleVersion 18 | 1.1 19 | NSPrincipalClass 20 | PSMTabBarControl 21 | 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This my fork of PSMTabBarControl. It was originally created by [http://www.positivespinmedia.com/dev/PSMTabBarControl.html](http://www.positivespinmedia.com/dev/PSMTabBarControl.html), then updated at [http://code.google.com/p/maccode/source/browse/#svn/trunk/Utilities/PSMTabBarControl](http://code.google.com/p/maccode/source/browse/#svn/trunk/Utilities/PSMTabBarControl). The initial commit of my fork is from r291 of the Google code repo. Project hasn't been updated on either site in a few years, but still works well. My fork is an attempt to modernize some of the code and interface, but I can't promise I'll keep maintaining it. I'm also stripping out stuff I'm not planning on using, so I'm no longer supporting vertical orientation or multiple tab styles. 2 | 3 | ## Changes 4 | 5 | The inital commit is an unmodified import from the svn repo. Everything after are updates by me: 6 | 7 | - The original project included an Interface Builder palette which is no longer supported and has been removed 8 | - Vertical orientation is no longer supported 9 | - Multiple tab styles no longer supported 10 | - Only supporting 10.6 and later 11 | 12 | ## License 13 | 14 | It is released under a BSD license, same as the original, which is included here: 15 | 16 | I should note that portions of this source code were inspired by the Shiira project's implementation of Safari-style tabs. While I made some different design decisions, the drawing and some other aspects are only slight modifications of their excellent work. As such, I note their copyright under their BSD licence: 17 | 18 | Portions of this software Copyright 2004 The Shiira Project. All rights reserved. 19 | Check them out at: http://hmdt-web.net/shiira/ 20 | 21 | This source code is provided under BSD license, the conditions of which are listed below. I hope you'll make note somewhere in your about window or ReadMe stating the sweet coding goodness of Positive Spin Media and link to the fascinating and informative website at www.positivespinmedia.com 22 | 23 | Copyright (c) 2005, Positive Spin Media
All rights reserved. 24 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 25 | • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 26 | • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 27 | • Neither the name of Positive Spin Media nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /ReadMe.rtfd/TXT.rtf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/ReadMe.rtfd/TXT.rtf -------------------------------------------------------------------------------- /ReadMe.rtfd/startpage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/ReadMe.rtfd/startpage.gif -------------------------------------------------------------------------------- /TabBarControlDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.positivespinmedia.TabBarControlDemo 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | APPL 15 | CFBundleSignature 16 | ???? 17 | CFBundleVersion 18 | 1.1 19 | NSMainNibFile 20 | MainMenu 21 | NSPrincipalClass 22 | NSApplication 23 | 24 | 25 | -------------------------------------------------------------------------------- /documentation/PSMTabBarControlDoc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PSMTabBarControl - Safari-style Tabs (Objective-C) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

PSMTabBarControl

15 | 16 | 21 | 23 |
Inherits from
Conforms to
Declared in
PSMTabBarControl.h
22 |

Class Description

24 | 25 |

PSMTabBarControl provides a user interface for the management of tabbed views that has an appearance similar to Apple's Safari web browser.

26 |

Internally, PSMTabBarControl uses a custom cell class and supporting style objects to implement its user interface.

27 |

Currently, this object supports "Aqua" and "Metal" tab styles to present an appearance consistent with the rest of your application. Numerous configuration options exist to customize behavior, as outlined below. PSMTabBarControl instances require a few specific connections with other application objects to perform properly at runtime:

28 | 33 |

A PSMTabBarControl instance should occupy the width of the window, and should be precisely 22 pixels in height. It should be resizable in width, but not height. It can be placed at the top or botton of the window (or anywhere in between, if desired).

34 |

Outside of configuring it, your application should have little interaction with this class. The presented tabs will change in conjunction with changes your application makes in the NSTabView being controlled.

35 | 36 |

A Usage Pattern

37 | 38 |

PSMTabBarControl becomes even more powerful if your application design matches an expected pattern: the control attempts to bind a number of tab attributes to the NSTabViewItem's identifier object if possible. These visible attributes include a progress indicator, an icon, and an object counter. Additionally, the control binds each tab's title to the label of the represented NSTabViewItem.

39 | 40 |

Taking advantage of these features requires no glue code on your part, but it does require providing a particular object graph. Here's the basics...

41 | 42 | 45 | 46 |

The demo application included with this project illustrates a very quick way to accomplish these goals. If this pattern is followed, PSMTabBarControl will take care of the rest.

47 | 48 | 49 |

Methods by Task

50 |

Setting needed connections

51 |
– setTabView:

Specifies the instance of NSTabView to be controlled.

52 | 53 |
– tabView

Returns the instance of NSTabView being controlled.

54 | 55 |

Setting optional connections

56 |
– setPartnerView:

Specifies a view that will resize to compensate for control size changes in response to hide and show messages.

57 | 58 |
– partnerView

Returns the instance of a view that will resize to compensate for control size changes in response to hide and show messages.

59 | 60 |
– setDelegate:

Specifies an object that will receive delegate messages as passed through from the NSTabView instance.

61 | 62 |
– delegate:

Returns the specified delegate object.

63 | 64 |

Control configuration

65 |
– canCloseOnlyTab

Returns YES if the user is allowed to close a tab when it is the only tab left, NO otherwise.

66 | 67 |
– setCanCloseOnlyTab:

Controls whether the receiver will present a close button for a single tab in a tab bar.

68 | 69 |
– styleName

Returns the name of the current drawing style. "Aqua", "Metal", and "Unified" are the currently supported options.

70 | 71 |
– setStyleNamed:

Specifies the style in which the tabs and control are drawn. "Aqua", "Metal", and "Unified" are the currently supported options.

72 | 73 |
– hideForSingleTab

Returns YES if the control will hide if there is only one tab left, NO otherwise.

74 | 75 |
– setHideForSingleTab:

Controls whether the receiver will hide when the user closes all but a single tab in a tab bar.

76 | 77 |
– showAddTabButton

Returns YES if the control will display a small "add tab" button at the rightmost end of the tabs, NO otherwise.

78 | 79 |
– setShowAddTabButton:

Controls whether the receiver will will display a small "add tab" button at the rightmost end of the tabs.

80 | 81 |
– cellMinWidth

Returns the minimum width (in pixels) that a tab will be allowed to occupy.

82 | 83 |
– setCellMinWidth:

Specifies the maximum width (in pixels) that a tab will be allowed to occupy.

84 | 85 |
– cellMaxWidth

Returns the maximum width (in pixels) that a tab will be allowed to occupy.

86 | 87 |
– setCellMaxWidth:

Specifies the maximum width (in pixels) that a tab will be allowed to occupy.

88 | 89 |
– cellOptimumWidth

Returns the default width (in pixels) that a tab will be allowed to occupy when tabs are drawn with uniform size.

90 | 91 |
– setCellMaxWidth:

Specifies the default width (in pixels) that a tab will be allowed to occupy when tabs are drawn with uniform size.

92 | 93 |
– sizeCellsToFit

Returns YES if the control will make the tabs sized to fit the content of the tab, NO otherwise.

94 | 95 |
– setSizeCellsToFit:

Controls whether the receiver will make the tabs sized to fit the content of the tab.

96 | 97 |
– allowsDragBetweenWindows

Returns YES if the control allows a user to drag a tab to another instance of this control, NO otherwise.

98 | 99 |
– setAllowsDragBetweenWindows:

Controls whether the receiver will allow a user to drag a tab to another instance of this control.

100 | 101 |

Internal UI components

102 |
– addTabButton

Returns an instance of an NSButton subclass that is used to present the "add tab" button. If "showAddTabButton" is YES, developers must use this method to access the button and specify a target and action for the button.

103 | 104 |
– overflowPopUpButton

Returns an instance of an NSPopUpButton subclass that is used to present the overflow menu (which shows when there are more tabs than can fit across the control).

105 | 106 |
– representedTabViewItems

Returns an array of the NSTabViewItems represented by the tabs in the control. Useful if you want to archive the order of the tabs between runs of your program.

107 | 108 |

Visibility

109 |
– hideTabBar:animate:

If desired, obejcts can tell the tab bar to hide (reduce in size to a single pixel line spanning the window) or show, and optionally whether to animate this effect or not.

110 | 111 |

Instance Methods

112 | 113 |

addTabButton

114 |

Returns an instance of an NSButton subclass that is used to present the "add tab" button. If "showAddTabButton" is YES, developers must use this method to access the button and specify a target and action for the button.

- (PSMRolloverButton *)addTabButton;

115 |
Discussion

If you have configured the control to show the add tab button, you must use this method to access the button and set the target and action for it. Example:

[[tabBar addTabButton] setTarget:self];
116 | [[tabBar addTabButton] setAction:@selector(addNewTab:)];

117 |
See Also
119 | 120 |

allowsDragBetweenWindows

121 |

Returns YES if the control allows a user to drag a tab to another instance of this control, NO otherwise.

- (BOOL)allowsDragBetweenWindows

122 |
Discussion

The default is YES.

123 |
See Also
125 | 126 |

canCloseOnlyTab

127 |

Returns YES if the receiver has been configured to allow users to close a single remaining tab.

- (BOOL)canCloseOnlyTab

128 |
Discussion

The default is NO.

129 |
See Also
131 | 132 |

cellMaxWidth

133 |

Returns the maximum width (in pixels) that a tab will be allowed to occupy.

- (int)cellMaxWidth

134 |
Discussion

The cellMaxWidth value applies to both uniformly sized tabs, and tabs that are sized to fit. No tab will be drawn wider than the specified value.

135 |
See Also
137 | 138 |

cellMinWidth

139 |

Returns the minimum width (in pixels) that a tab will be allowed to occupy.

- (int)cellMinWidth

140 |
Discussion

The cellMinWidth value applies only to uniformly sized tabs. No tab will be drawn smaller than the specified value. Size-to-fit tabs ignore this value, and are made just small enough to fit their content.

141 |
See Also
143 | 144 |

cellOptimumWidth

145 |

Returns the width (in pixels) that a tab will be made to occupy if the tabs are uniformly sized.

- (int)cellOptimumWidth

146 |
Discussion

The cellOptimumWidth value applies only to uniformly sized tabs. All tabs will be drawn at the specified value. Size-to-fit tabs ignore this value, and are made just small enough to fit their content.

147 |
See Also
149 | 150 |

delegate

151 |

Returns the object that will be sent passed-through NSTabView delegate messages.

- (id)delegate

152 |
See Also
154 | 155 |

hideForSingleTab

156 |

Returns YES if the receiver has been configured to hide if there is a single remaining tab.

- (BOOL)hideForSingleTab

157 |
Discussion

The default is NO.

158 |
See Also
160 | 161 |

hideTabBar:animate:

162 |

If desired, obejcts can tell the tab bar to hide (reduce in size to a single pixel line spanning the window) or show, and optionally whether to animate this effect or not.

- (void)hideTabBar:(BOOL)hide animate:(BOOL)animate

163 |
Discussion

If hide is YES the control will shrink to a single pixel line spanning the window; otherwise the control will expand to its normal appearance. If animate is YES, the shrinking and expanding will happen in a visible animation; otherwise the transition will be instant. There is no effect if the control is already in the specified state.

164 | 165 |

overflowPopUpButton

166 |

Returns an instance of an NSPopUpButton subclass that is used to present the overflow menu.

- (PSMOverflowPopUpButton *)overflowPopUpButton;

167 |
Discussion

This method could be used to modify the button or menu if desired.

168 | 169 |

partnerView

170 |

Returns the object that will be resized to compensate for the changing size of the control during hide/show behavior.

- (id)partnerView

171 |
Discussion

If a partnerView has not been specified, the window will be resized during hide/show.

172 |
See Also
174 | 175 |

representedTabViewItems

176 |

Returns an array of NSTabViewItems, ordered according to the display order of the tabs in the control.

- (NSMutableArray *)representedTabViewItems;

177 |
Discussion

This method could be used archive the order of the tabs between application runs. When the user reorders tabs via drag and drop, the represented NSTabView does not change order.

178 | 179 |

setAllowsDragBetweenWindows

180 |

If set to YES, the receiver is configured to allow users to drag a tab to an instance of PSMTabBarControl in another window.

- (void)setAllowsDragBetweenWindows:(BOOL)value

181 |
Discussion

The default is YES.

182 |
See Also
184 | 185 |

setCanCloseOnlyTab:

186 |

If set to YES, the receiver is configured to allow users to close a single remaining tab.

- (void)setCanCloseOnlyTab:(BOOL)value

187 |
Discussion

The default is NO.

188 |
See Also
190 | 191 |

setCellMaxWidth:

192 |

Specifies the maximum width (in pixels) that a tab will be allowed to occupy.

- (void)setCellMaxWidth:(int)value

193 |
Discussion

No tab will be drawn any wider than the specified value.

194 |
See Also
196 | 197 |

setCellMinWidth:

198 |

Specifies the minimum width (in pixels) that a tab will be allowed to occupy.

- (void)setCellMinWidth:(int)value

199 |
Discussion

No tab will be drawn any smaller than the specified value. This value is ignored when drawing in size-to-fit; tabs are made just small enough to fit their content.

200 |
See Also
202 | 203 |

setCellOptimumWidth:

204 |

Specifies the width (in pixels) that a tab will occupy when tabs are uniformly sized.

- (void)setCellOptimumWidth:(int)value

205 |
Discussion

This value is ignored when drawing in size-to-fit; tabs are made just small enough to fit their content.

206 |
See Also
208 | 209 |

setDelegate:

210 |

Specifies an object that will receive delegate messages as passed through from the NSTabView instance.

- (void)setDelegate:(id)object

211 |
See Also
213 | 214 |

setHideForSingleTab:

215 |

If set to YES, the receiver is configured to hide when there is a single remaining tab.

- (void)setHideForSingleTab:(BOOL)value

216 |
Discussion

The default is NO.

217 |
See Also
219 | 220 |

setPartnerView:

221 |

Specifies a view that will resize to compensate for control size changes in response to hide and show messages.

- (void)setPartnerView:(id)view

222 |
Discussion

if nil, the control will resize the window in response to hide/show messages.

223 |
See Also
225 | 226 |

setStyleNamed:

227 |

Specifies the style in which the tabs and control are drawn.

- (void)setStyleNamed:(NSString *)name

228 |
Discussion

"Aqua" and "Metal" are the currently supported options.

229 |
See Also
231 | 232 |

setShowAddTabButton:

233 |

Controls whether the receiver will will display a small "add tab" button at the rightmost end of the tabs.

- (void)setShowAddTabButton:(BOOL)value

234 |
Discussion

Default is NO.

235 |
See Also
237 | 238 |

setSizeCellsToFit:

239 |

Controls whether the receiver will make the tabs sized to fit the content of the tab.

- (void)setSizeCellsToFit:(BOOL)value

240 |
Discussion

Default is NO.

241 |
See Also
243 | 244 |

setTabView:

245 |

Specifies the instance of NSTabView to be controlled.

- (void)setTabView:(NSTabView *)view

246 |
Discussion

This class will not function properly without this outlet being set.

247 |
See Also
249 | 250 |

sizeCellsToFit

251 |

Returns YES if the control will make the tabs sized to fit the content of the tab, NO otherwise.

252 |

Discussion

The default is NO.

253 |
See Also
255 | 256 |

styleName:

257 |

Returns the name of the current drawing style.

- (NSString *)styleName

258 |
Discussion

"Aqua" and "Metal" are the currently supported options.

259 |
See Also
261 | 262 |

tabView:

263 |

Returns the instance of NSTabView being controlled.

- (NSTabView *)tabView

264 |
Discussion

This class will not function properly if this object is nil.

265 |
See Also
267 | 268 |

Delegate Methods

269 | 270 |

You application controller can keep track of activity from the tab view and the user via the following delegate methods.

271 | 272 |

tabView:shouldCloseTabViewItem:

273 |

Sent when a user clicks the close button on a tab.

- (BOOL)tabView:(NSTabView *)tabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem

274 |
Discussion

If you return NO, the tab will not be closed. Please make sure to alert the user as to why with a sheet or dialog.

275 | 276 |

tabView:willCloseTabViewItem:

277 |

Sent when tab is about to be closed.

- (void)tabView:(NSTabView *)tabView willCloseTabViewItem:(NSTabViewItem *)tabViewItem

278 |
Discussion

This presents you with an opportunity to clean up the application objects/events/sessions represented by the tab.

279 | 280 |

tabView:didCloseTabViewItem:

281 |

Sent after a tab has been closed.

- (void)tabView:(NSTabView *)tabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem

282 |
Discussion

This presents you with an opportunity to clean up the application objects/events/sessions represented by the tab. Don't go trying to modify the tabViewItem - it is about to be released, and has already been removed from the tabView!

283 | 284 |

tabView:didSelectTabViewItem:

285 |

Informs the delegate that tabView has selected tabViewItem.

- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem

286 | 287 |

tabView:shouldSelectTabViewItem:

288 |

Invoked just before tabViewItem in tabView is selected.

- (BOOL)tabView:(NSTabView *)tabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem

289 |
Discussion

The delegate can return NO to prevent selection of specific tabs.

290 | 291 |

tabView:willSelectTabViewItem:

292 |

Informs the delegate that tabView is about to select tabViewItem.

- (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem

293 | 294 |

tabViewDidChangeNumberOfTabViewItems:

295 |

Informs the delegate that the number of tab view items in tabView has changed.

- (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)tabView

296 | 297 | 298 | 299 | 300 | 301 | -------------------------------------------------------------------------------- /documentation/frameset_styles.css: -------------------------------------------------------------------------------- 1 | /* link classes */ 2 | A:link { COLOR: #0000FF; TEXT-DECORATION: none; } 3 | A:link:hover { COLOR: #FF6600; TEXT-DECORATION: underline; } 4 | A:active { COLOR: #FF6600; TEXT-DECORATION: underline; } 5 | 6 | /* apple.com site does not explicitly define visited link properties, but we do here */ 7 | A:visited { COLOR: #0000AA; TEXT-DECORATION: none; } 8 | A:visited:hover { COLOR: #FF6600; TEXT-DECORATION: underline; } 9 | 10 | /* used to convert otherCodeCharacters to code, thus saving character space in HTML for smaller file sizes */ 11 | tt { 12 | FONT-SIZE: 11px; FONT-FAMILY: monaco, courier, monospace; } 13 | 14 | /* redefine preformated text and code blocks */ 15 | PRE { 16 | FONT-SIZE: 11px; FONT-FAMILY: monaco, courier, monospace; margin-top: 5px; margin-bottom: 10px;} 17 | CODE { 18 | FONT-SIZE: 11px; FONT-FAMILY: monaco, courier, monospace; } 19 | 20 | 21 | /* JavaScript toc frame */ 22 | .jtoc_closed { background-color: #e9e9e9; } 23 | .jtoc_open { background-color: #FFFFFF; padding-bottom: 10px; } 24 | .jtoc_open_top_line {border-top: 1px solid #CCC; background-color: #fff; padding-bottom: 10px;} 25 | .jtoc_open_bottom_line {border-bottom: 1px solid #CCC; background-color: #fff; padding-bottom: 10px;} 26 | .jtoc_open_both_lines {border-bottom: 1px solid #CCC; border-top: 1px solid #CCC; background-color: #fff; padding-bottom: 10px;} 27 | 28 | /* frameset: toc frame */ 29 | .toc_contents_text { 30 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-size: 12px; font-weight: bold; padding-top: 4px 0; 31 | color: #0000FF; 32 | } 33 | .toc_contents_text_open { 34 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-size: 12px; 35 | background: #FFFFFF; color: #0000FF; 36 | } 37 | .low_level_text { 38 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-size: 11px; padding: 4px 5px 4px 5px 39 | color: #0000FF; 40 | } 41 | 42 | #toc_contents_title { 43 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-size: 16px; color: #FFFFFF; font-weight: bold; 44 | } 45 | .toc_contents_heading { 46 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-size: 12px; font-weight: bold; 47 | } 48 | 49 | /* Special TOC heading for Help books only */ 50 | .toc_contents_help_heading { 51 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-size: 14px; color: #330099; font-weight: bold; 52 | } 53 | 54 | /* frameset: toc frame styles WEB AS. used for any document that uses the disclosure model for TOC like conceptural and procedural C documents*/ 55 | #toc { 56 | padding: 0 0 0 0; 57 | } 58 | 59 | #toc p.download { 60 | padding: 5px 10px; 61 | margin: 0; 62 | font-weight: normal; font-size: 11px; 63 | } 64 | 65 | #toc_PDFbottomborder { 66 | padding-top: 5px; 67 | border-bottom: 1px solid #CCC; 68 | } 69 | 70 | 71 | #toc h2 { 72 | margin: 0; 73 | padding: 10px; 74 | font-size: 15px; font-weight: bold; 75 | border-bottom: 0px solid; 76 | } 77 | 78 | #toc h3 { 79 | margin: 6px 5px 0 10px; 80 | font-size: 13px; font-weight: bold; color: black; 81 | } 82 | 83 | #toc h4 { 84 | font-size: 11px; font-weight: bold; color: black; 85 | margin: -5px 0px 0 14px; 86 | } 87 | 88 | #toc_staticbox { 89 | padding: 0 0 0 0; 90 | border: 1px solid #919699; 91 | background: #e9e9e9; 92 | } 93 | 94 | #toc ul { 95 | list-style: none outside; 96 | margin-left: 20px; margin-bottom: -2px; 97 | padding: 0px; 98 | } 99 | 100 | #toc ul ul{ 101 | list-style: none outside; 102 | margin-left: 10px; margin-bottom: -2px; 103 | padding: 0px; 104 | } 105 | 106 | #toc li a { 107 | margin-left: 5px; 108 | display: block; 109 | padding: 0px 5px 0px 5px; 110 | } 111 | 112 | #toc li a.location { 113 | font-weight: bold; color: #000; 114 | text-decoration: none; 115 | } 116 | 117 | #toc li{ 118 | font-weight: normal; font-size: 11px; 119 | padding: 0px 5px 0px 0px; 120 | list-style-type: none; background: url(../Images/bullet.gif) no-repeat 0px .5em; 121 | } 122 | 123 | #toc li li a { 124 | margin-left: 0px; 125 | } 126 | 127 | 128 | #toc ul ul li { 129 | background: url(../Images/dash.gif) no-repeat 0px .6em; 130 | } 131 | 132 | #toc ul ul ul li { 133 | background: url(../Images/sm_bullet.gif) no-repeat 0px .5em; 134 | } 135 | 136 | #toc li.open { 137 | border-top: 1px solid #CCC; border-bottom: 1px solid #CCC; background-color: #fff; 138 | } 139 | 140 | #toc .open ul { 141 | background-color: #fff; 142 | } 143 | 144 | li img { 145 | margin-left: 0px; 146 | } 147 | /*#toc li.open { background: #FFF;}*/ 148 | 149 | 150 | /* frameset: toc frame styles WEB AS. used for any document that uses the static model for TOC topics documents*/ 151 | 152 | #topicstoc { 153 | padding: 0px 0px 0px; 154 | } 155 | 156 | 157 | #topicstoc p.download { 158 | border-bottom: 1px solid #CCC; 159 | padding: 5px 10px; 160 | margin: 0px; 161 | font-weight: normal; font-size: 11px; 162 | } 163 | 164 | #topicstoc h2 { 165 | margin: 0; 166 | padding: 10px; 167 | font-size: 15px; font-weight: bold; 168 | border-bottom: 0px solid; 169 | } 170 | 171 | #topicstoc h3 { 172 | margin: 6px 5px 0 10px; 173 | font-size: 13px; font-weight: bold; color: black; 174 | } 175 | 176 | #topicstoc h4 { 177 | font-size: 11px; font-weight: bold; color: black; 178 | margin: 2px 0px 0px 14px; 179 | } 180 | 181 | #topicstoc ul { 182 | list-style: none outside; 183 | margin-left: 13px; margin-bottom: -2px; 184 | padding: 0px; 185 | } 186 | 187 | 188 | #topicstoc ul ul{ 189 | list-style: none outside; 190 | margin-left: 10px; margin-bottom: -2px; 191 | padding: 0px; 192 | } 193 | 194 | #topicstoc li{ 195 | font-weight: normal; font-size: 12px; 196 | padding: 0px 5px 2px 10px; 197 | list-style-type: none; background: url(../Images/bullet.gif) no-repeat 0px .5em; 198 | } 199 | 200 | #topicstoc li.intro { 201 | font-weight: normal; 202 | padding: 0px 0px; 203 | list-style-type: none; background: none; 204 | } 205 | 206 | #topicstoc li a { 207 | display: block; 208 | padding: 0px 5px 0px 0px; 209 | } 210 | 211 | #topicstoc li a.location { 212 | font-weight: bold; color: #000; 213 | text-decoration: none; 214 | } 215 | 216 | #topicstoc li.intro a { 217 | margin-left: -5px; 218 | display: block; 219 | } 220 | 221 | 222 | img.toplevel { float: left; } 223 | 224 | 225 | 226 | /* frameset: content frame */ 227 | BODY { 228 | margin-top: 0; 229 | color: #000; 230 | font: 12px lucida grande, geneva, helvetica, arial, sans-serif; 231 | } 232 | 233 | /* frameset: H1,H2,H3,H4,H5,Head for code voice */ 234 | 235 | h1 { 236 | margin-top: 1em; 237 | margin-bottom: 25px; 238 | font: bold 30px lucida grande, geneva, helvetica, arial, sans-serif; 239 | color: #000; 240 | } 241 | h2 { 242 | margin-top: 2.5em; 243 | font-size: 24px; 244 | color: #000; 245 | padding-bottom: 2px; border-bottom: 1px solid black; 246 | } 247 | h3 { 248 | margin-top: 2em; 249 | margin-bottom: .5em; 250 | font-size: 19px; 251 | color: #000; 252 | } 253 | h3.tight { 254 | margin-top: 3em; 255 | margin-bottom: -.25em; 256 | font-size: 19px; 257 | color: #000; 258 | } 259 | h4 { 260 | margin-top: 2em; 261 | margin-bottom: .5em; 262 | font-size: 15px; 263 | color: #000; 264 | } 265 | h5 { 266 | margin: 20 0 0 0; 267 | padding: 0; 268 | font-size: 13px; 269 | color: #000; 270 | } 271 | .mach4{ 272 | margin-top: 40; 273 | margin-bottom: 0; 274 | padding-top: 0; 275 | font: bold 16px lucida grande, geneva, helvetica, arial, sans-serif; 276 | color: #000; 277 | } 278 | .mach5{ 279 | margin: 30 0 -9 0; 280 | font: bold 13px lucida grande, geneva, helvetica, arial, sans-serif; 281 | color: #000; 282 | } 283 | h5.tight{ 284 | margin: 1.5em 0 2px 0; 285 | font: bold 13px lucida grande, geneva, helvetica, arial, sans-serif; 286 | color: #000; 287 | } 288 | 289 | 290 | .code_head{ 291 | FONT-SIZE: 18px; FONT-FAMILY: monaco, courier, monospace; font-weight: bold; 292 | } 293 | p { 294 | margin-top: 0px; margin-bottom: 10px; font: 12px lucida grande, geneva, helvetica, arial, sans-serif; 295 | } 296 | p.spaceabove { 297 | margin-top: 13px; margin-bottom: 10px; font: 12px lucida grande, geneva, helvetica, arial, sans-serif; 298 | } 299 | p.spaceabovemethod { 300 | font: 11px monaco, courier, monospace; margin-top: 13px; margin-bottom: 10px; 301 | } 302 | h3.tight + p { 303 | margin-top: 13px; margin-bottom: 10px; font: 12px lucida grande, geneva, helvetica, arial, sans-serif; 304 | } 305 | h3.tight + p.spaceabovemethod { 306 | font: 11px monaco, courier, monospace; margin-top: 13px; margin-bottom: 10px; 307 | } 308 | .content_text{ 309 | margin-top: 0px; margin-bottom: 10px; font: 12px lucida grande, geneva, helvetica, arial, sans-serif; 310 | } 311 | p.blockquote{ 312 | padding-left: 50pt; 313 | padding-right: 50pt; 314 | } 315 | ul.availability { 316 | list-style-type: none; 317 | margin: 0 0 -10px 0; 318 | } 319 | .availability li { 320 | margin: 2px 0 0 -6px; 321 | } 322 | 323 | /* frameset: content frame bold style for text*/ 324 | b{ 325 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-size: 12px; font-weight: bold; 326 | } 327 | 328 | /* Used for text that is sligtly larger than bold text like lables and captions*/ 329 | .content_text_label{ 330 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-size: 12px; 331 | } 332 | 333 | /* frameset: content frame italic style for text*/ 334 | i{ 335 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-size: 12px; font-style: italic; 336 | } 337 | 338 | /* Used for reference heads in ProcedualC and AppleScript Language*/ 339 | .content_ref_head{ 340 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; 341 | font-size: 16px; font-weight: bold; margin-top: 50px; padding-bottom: 4px; border-bottom: 1px solid black 342 | } 343 | 344 | .content_ref_head_code{ 345 | font-family: monaco, courier, monospace; 346 | font-size: 16px; font-weight: bold; margin-top: 50px; padding-bottom: 4px; border-bottom: 1px solid black 347 | } 348 | 349 | /* frameset: page title */ 350 | .page_title{ 351 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; 352 | font-size: 34px; 353 | font-weight: bold; 354 | color: #000000; 355 | padding-top: 10px; 356 | } 357 | 358 | /* frameset: Unordered List */ 359 | 360 | ul.spaceabove { 361 | list-style: square outside; 362 | margin: 0 0 0 30px; 363 | padding: 7 0 12px 6px; 364 | } 365 | 366 | ul { 367 | list-style: square outside; 368 | margin: 0 0 0 30px; 369 | padding: 0 0 12px 6px; 370 | } 371 | 372 | li { 373 | margin-top: 7px; 374 | } 375 | 376 | 377 | li p { 378 | margin-top: 8px; 379 | } 380 | 381 | ul ul { 382 | list-style: circle outside; 383 | margin: 6 0 0 30px; 384 | padding: 0 0 12px 6px; 385 | } 386 | ul.3head { 387 | list-style: square outside; 388 | margin: 0 0 0 20px; 389 | padding: 0 0 0px 0px; 390 | } 391 | 392 | 393 | /* alternate mappings for 2nd level bulleted list that are still in testing phase*/ 394 | .nested li { 395 | list-style-image: url(../Images/openbullet.gif); 396 | list-style-position: outside; 397 | } 398 | 399 | ul.nested { 400 | list-style: none; 401 | margin: 6 0 0 30px; 402 | } 403 | 404 | ol { 405 | list-style-type: decimal; 406 | list-style-position: outside; 407 | margin: 0 0 0 30px; 408 | padding: 0 0 12px 6px; 409 | } 410 | 411 | ol ol { 412 | list-style-type: lower-alpha; 413 | list-style-position: outside; 414 | margin: 7 0 0 30px; 415 | padding: 0 0 12px 10px; 416 | } 417 | 418 | ul.simple-spaceabove { 419 | list-style-type: none; 420 | margin: 5 0 0 20px; 421 | } 422 | .simple-spaceabove li { 423 | margin-top: 1px; 424 | } 425 | 426 | ul.simple { 427 | list-style-type: none; 428 | margin: 0 0 0 30px; 429 | } 430 | .simple li { 431 | margin-top: -1px; 432 | } 433 | 434 | dl.table-display { 435 | clear: both; 436 | width: auto; 437 | margin: 0; 438 | padding: 0px; 439 | } 440 | 441 | .table-display dt { 442 | width: 8em; 443 | float: left; 444 | margin: 0 0 0 0; 445 | padding: .1em; 446 | } 447 | 448 | /* commented backslash hack for mac-ie5 \*/ 449 | dt { clear: both; } 450 | 451 | 452 | .table-display dd { 453 | float: left; 454 | width: 80%; 455 | margin: 0 0 0 0; 456 | padding: .1em; 457 | display: block; 458 | } 459 | 460 | .clear { 461 | clear: both; 462 | } 463 | dl.termdef { 464 | margin-top: 0px; 465 | margin-bottom: 10px; } 466 | .termdef dt { 467 | margin-top: 0px; } 468 | .termdef dd { 469 | margin-left: 15px; 470 | margin-top: 1px; 471 | margin-bottom: 6px; } 472 | .termdef p{ 473 | margin-left: 15px; 474 | margin-top: -1px; 475 | margin-bottom: 6px; } 476 | h3.tight + dl.termdef { 477 | margin-top: 13px; 478 | margin-bottom: 10px; } 479 | 480 | /* frameset: list items */ 481 | /* Everything in a list item is wrapped in an element now. */ 482 | /* First para in a list item should be inline, others should be block. */ 483 | li>p { display: inline } 484 | li>p+p { display: block } 485 | 486 | 487 | /* frameset: Index styles for docs */ 488 | /* frameset: Index styles for alpah listing */ 489 | .index_alpa{ 490 | font-size: 18px; padding-bottom: 5px; margin: 25px 0 15px; border-bottom: 1px solid #91969C; } 491 | 492 | /* frameset: Index styles for singal and page range entries */ 493 | .libindex{ 494 | font-size: 12px; padding: 0 3px; background-color: #FFFFFF; margin: 0 3px; } 495 | 496 | 497 | 498 | /* frameset: mini navigation style (Hide/Show TOC & next/prev) */ 499 | .mini_nav_text { 500 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; 501 | font-size: 9px; 502 | font-weight: normal; 503 | } 504 | 505 | .breadcrumb { 506 | font-family: lucida grande, geneva, helvetica, arial, sans-serif; 507 | font-size: 10px; 508 | font-weight: normal; 509 | margin-left: 10px; 510 | margin-top: 8px; 511 | } 512 | 513 | /* ADC header/footer mappings for Getting Started */ 514 | /* header */ 515 | #adcnavheader td { 516 | font: 10px lucida grande, geneva, helvetica, arial, sans-serif; 517 | } 518 | 519 | #adcnavheader input { 520 | margin: 0 3px 0 0; 521 | padding: 0; 522 | } 523 | 524 | #adcnavheader .textpadding { 525 | padding-top: 2px; 526 | vertical-align: middle; 527 | } 528 | 529 | #adcnavheader .searchbutton { 530 | border: 0; 531 | } 532 | 533 | #adcnavheader h6 { 534 | margin: 0; 535 | padding: 0; 536 | font: normal 12px lucida grande, geneva, helvetica, arial, sans-serif; 537 | color: #000; 538 | } 539 | 540 | #adcnavheader form { 541 | margin: 0; 542 | } 543 | 544 | 545 | /* footer */ 546 | #footer td, #footer p { 547 | font-size: 10px; 548 | } 549 | 550 | 551 | /* Getting Started styles */ 552 | 553 | /* font definitions */ 554 | 555 | /* keep ".gettingstarted pre, code" above ".gettingstarted h1" below otherwise, 556 | the main h1 tag above will be used instead of".gettingstarted h1" */ 557 | .gettingstarted pre, code { 558 | font: 11px Monaco, Courier, monospace; 559 | } 560 | 561 | /* Added this style since the ADC template was trying to do this with a graphic (Note: their h2 562 | attached to the image tag is only used by search engines; their h2 does not function if the 563 | graphic is missing).--DA */ 564 | .gettingstarted h2 { 565 | margin: 0; 566 | margin-bottom: 15px; 567 | padding: 0px; 568 | font: bold 32px 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; 569 | color: #000; 570 | } 571 | 572 | .gettingstarted h3 { 573 | margin: 0 0 5px 0; 574 | padding: 0px; 575 | font: bold 16px 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; 576 | color: #000; 577 | } 578 | 579 | .gettingstarted p + .gettingstarted h3 { 580 | margin-top: 20px; 581 | } 582 | 583 | .gettingstarted ol + .gettingstarted h3 { 584 | margin-top: 20px; 585 | } 586 | 587 | .gettingstarted ul + .gettingstarted h3 { 588 | margin-top: 20px; 589 | } 590 | 591 | .gettingstarted h4 { 592 | margin: 0px; 593 | padding: 0px; 594 | font: bold 12px 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; 595 | color: #000; 596 | } 597 | 598 | .gettingstarted h4 + .gettingstarted table { 599 | margin-top: 10px; 600 | } 601 | 602 | .gettingstarted p { 603 | margin-top: 0; 604 | margin-bottom: 10px; 605 | padding: 0; 606 | font: 12px 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; 607 | color: #000; 608 | } 609 | 610 | .gettingstarted th { 611 | font-weight: bold; 612 | text-align: left; 613 | } 614 | 615 | 616 | /* list definitions */ 617 | .gettingstarted ul { 618 | font: 12px 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; 619 | padding-left: 10px; 620 | margin-top: 0; 621 | margin-left: 10px; 622 | margin-bottom: 10px; 623 | list-style-type: none; 624 | } 625 | 626 | .gettingstarted li { 627 | margin-top: 3px; 628 | } 629 | 630 | 631 | .gettingstarted ul li { 632 | list-style: square outside; 633 | margin: 0 0 0 30px; 634 | padding: 0 0 4px 0; 635 | } 636 | 637 | .gettingstarted ul ul { 638 | margin-left: 20px; 639 | } 640 | 641 | .gettingstarted ol { 642 | font: 12px 'Lucida Grande', Geneva, Verdana, Arial, Helvetica, sans-serif; 643 | margin-top: 0; 644 | margin-left: 1.5em; 645 | margin-bottom: 10px; 646 | padding-left: 1.5em; 647 | } 648 | 649 | .gettingstarted ul.inline, .gettingstarted ol.inline, .gettingstarted p.inline { 650 | margin-top: -7px; 651 | } 652 | 653 | /* table styles */ 654 | caption.tablecaption { 655 | margin-bottom: 5px; 656 | text-align: left; 657 | } 658 | .sourcecodebox { 659 | border: 1px solid #c7cfd5; 660 | background: #f1f5f9; 661 | margin: 20px 0; 662 | } 663 | 664 | div.tableholder { 665 | margin-top: 20px; 666 | margin-bottom: 20px; 667 | } 668 | 669 | p.tableholder { 670 | margin-bottom: 7px; 671 | font: 12px lucida grande, geneva, helvetica, arial, sans-serif; 672 | } 673 | 674 | .graybox { 675 | border-top: 1px solid #919699; 676 | border-left: 1px solid #919699; 677 | } 678 | 679 | .graybox th { 680 | padding: 4px 8px 4px 8px; 681 | background: #E2E2E2; 682 | font-size: 12px; 683 | border-bottom: 1px solid #919699; 684 | border-right: 1px solid #919699; 685 | } 686 | .graybox th p { 687 | font-weight: bold; 688 | margin-bottom: 0px; 689 | } 690 | 691 | .graybox td { 692 | padding: 8px; 693 | font-size: 12px; 694 | text-align: left; 695 | vertical-align: top; 696 | border-bottom: 1px solid #919699; 697 | border-right: 1px solid #919699; 698 | } 699 | .graybox td p { 700 | margin-bottom: 0px; 701 | } 702 | .graybox td p + p { 703 | margin-top: 5px; 704 | } 705 | .graybox td p + p + p { 706 | margin-top: 5px; 707 | } 708 | 709 | 710 | /* footnote definitions */ 711 | .footnote h4, .footnote p { 712 | color: #76797C; 713 | font-size: 11px; 714 | } 715 | 716 | .gettingstarted .footnote { 717 | font-size: 11px; 718 | color: #76797C; 719 | } 720 | 721 | 722 | .notebox { 723 | border: 1px solid #a1a5a9; 724 | background-color: #f7f7f7; 725 | margin: 20px 0; 726 | padding: 0px 8px 1px 9px; 727 | text-align: left; 728 | } 729 | .notebox p { 730 | font: 12px lucida grande, geneva, helvetica, arial, sans-serif; 731 | margin-top: 7px; 732 | margin-bottom: 0px; 733 | } 734 | .importantbox { 735 | border: 1px solid #111; 736 | background-color: #e8e8e8; 737 | margin: 20px 0; 738 | padding: 0px 8px 1px 9px; 739 | text-align: left; 740 | } 741 | .importantbox p { 742 | font: 12px lucida grande, geneva, helvetica, arial, sans-serif; 743 | margin-top: 7px; 744 | margin-bottom: 0px; 745 | } 746 | .warningbox { 747 | border: 1px solid #000; 748 | background-color: #fff; 749 | margin: 20px 0; 750 | padding: 8px; 751 | text-align: left; 752 | } 753 | .warningicon { 754 | background-color: transparent; 755 | padding-right: 10px; 756 | float: left; 757 | } 758 | .warningbox p { 759 | border-style: none; 760 | font: 12px lucida grande, geneva, helvetica, arial, sans-serif; 761 | margin: -8px 0 -8px 30px; 762 | } 763 | div.codesample { 764 | margin: 20px 0; 765 | } 766 | .codesample pre { 767 | font-size: 11px; 768 | font-family: monaco, courier, monospace; 769 | margin: -1px 4px -3px 6px; 770 | white-space: pre; 771 | } 772 | .codesample span { 773 | margin-right: 8px; 774 | float: right; 775 | } 776 | p.codesample { 777 | margin-top: 20px; margin-bottom: -15px; font: 12px lucida grande, geneva, helvetica, arial, sans-serif; 778 | } 779 | 780 | 781 | /* Controller Layer Bindings styles */ 782 | 783 | .class_binding_block { 784 | } 785 | 786 | .binding_category_block { 787 | margin-left: 1em 788 | } 789 | 790 | .binding_category_name { 791 | font-size: 24px; font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-weight: bold; line-height: 35px; padding-bottom: 1px; border-top: 2px solid black 792 | } 793 | 794 | .binding_block { 795 | margin-left: 2em 796 | } 797 | 798 | .binding_name { 799 | font-size: 18px; font-family: monaco, courier, monospace; font-weight: 400; margin-top: 10px; margin-bottom: 12px; border-bottom: 1px solid #69f 800 | } 801 | 802 | .bindings_tablehead { font-size: 14px; font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-weight: bold; position: relative; top: -5px; margin-left: -20px 803 | } 804 | 805 | .placeholder_options_block { 806 | margin-left: 2em 807 | } 808 | 809 | .availabilityList { 810 | border: none; 811 | margin-top: 5px; 812 | margin-bottom: 0px; 813 | font-size: 12px; 814 | text-align: left; 815 | } 816 | 817 | .availabilityItem { 818 | margin-top: -15px; 819 | margin-bottom: 15px; 820 | padding-left: 78px; 821 | } 822 | 823 | 824 | .metadata_attributes_name { 825 | font-size: 24px; font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-weight: bold; padding-top: 5px; margin-bottom: -10px; border-top: 2px solid black 826 | } 827 | 828 | .metadata_attribute_name { 829 | font-size: 18px; font-family: monaco, courier, monospace; font-weight: 400; margin-top: 10px; padding-bottom: 2px; 830 | } 831 | 832 | 833 | /* Spec Sheet Info Box */ 834 | /* Used in AppKit Obj-C, Appkit Java, Foundation Obj-C, and Foundation Java References */ 835 | 836 | .spec_sheet_info_box { margin-left: 1em } 837 | 838 | /* This builds a table */ 839 | .specbox { 840 | border-top: 1px solid #919699; 841 | border-left: 1px solid #919699; 842 | border-right: 1px solid #919699; 843 | margin-bottom: 10px; 844 | } 845 | 846 | .specbox td { 847 | padding: 8px; 848 | font-size: 12px; 849 | text-align: left; 850 | vertical-align: top; 851 | border-bottom: 1px solid #919699; 852 | } 853 | 854 | /* This alternates colors in up to six table rows (light blue for odd, white for even)*/ 855 | 856 | .specbox tr { 857 | background: #F0F5F9; 858 | } 859 | 860 | .specbox tr + tr { 861 | background: #FFFFFF; 862 | } 863 | 864 | .specbox tr + tr + tr { 865 | background: #F0F5F9; 866 | } 867 | 868 | .specbox tr + tr + tr + tr { 869 | background: #FFFFFF; 870 | } 871 | 872 | .specbox tr + tr + tr +tr + tr { 873 | background: #F0F5F9; 874 | } 875 | 876 | .specbox tr + tr + tr + tr + tr + tr { 877 | background: #FFFFFF; 878 | } 879 | 880 | /* informal protocol subtitling */ 881 | 882 | .protocol_subtitle { 883 | margin-top: -25px; 884 | margin-bottom: 25px; 885 | font-size: 13px; 886 | } 887 | 888 | /* HeaderDoc headings */ 889 | 890 | .hd_tocAccess { 891 | margin-left: 16px; 892 | margin-top: 3px; 893 | display: block; 894 | font-weight: bold; 895 | } 896 | .hd_tocAccessSpace { 897 | display: block; 898 | font-size: 8px; 899 | } 900 | 901 | .hd_tocGroup { 902 | margin-left: 8px; 903 | margin-top: 5px; 904 | display: block; 905 | font-style: italic; 906 | } 907 | .hd_tocGroupSpace { 908 | display: block; 909 | font-size: 8px; 910 | } 911 | 912 | /* "Collection page" mappings */ 913 | 914 | .forums { margin-bottom: 5px;} 915 | .forums b, 916 | .forums a:link, 917 | .forums a:visited { color: #017; font-family: lucida grande, geneva, helvetica, arial, sans-serif; font-size: 11px; font-weight: bold; line-height: 13px;} 918 | .forums a:hover { color: #00F; text-decoration: underline; font-weight: bold;} 919 | .collection { margin-bottom: 5px;} 920 | .collection h3, 921 | .collection a:link, 922 | .collection a:visited { font-size: 13px; color: #76797C; padding-bottom: 2px; border-bottom: 1px dotted #a1a5a9; margin-bottom: 0px; margin-right: 3px;} 923 | .collection a:hover { color: #00F; text-decoration: underline; font-weight: bold;} 924 | 925 | .collection_title { width: 100%; 926 | background-color: #7E91A4; 927 | padding: 15px; 928 | padding-right: 15px; 929 | padding-bottom: 2px; 930 | padding-left: 15px; 931 | } 932 | 933 | .collection_title a:link, 934 | .collection_title a:visited { color: #E8F3FD; } 935 | 936 | h1.collections { 937 | margin-top: 0px; 938 | margin-bottom: 25px; 939 | font: bold 30px lucida grande, geneva, helvetica, arial, sans-serif; 940 | color: #ffffff; 941 | text-align: center; } 942 | 943 | -------------------------------------------------------------------------------- /images/32x32_log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/32x32_log.png -------------------------------------------------------------------------------- /images/32x32cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/32x32cancel.png -------------------------------------------------------------------------------- /images/AdiumGradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AdiumGradient.png -------------------------------------------------------------------------------- /images/AquaTabCloseDirty_Front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabCloseDirty_Front.png -------------------------------------------------------------------------------- /images/AquaTabCloseDirty_Front_Pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabCloseDirty_Front_Pressed.png -------------------------------------------------------------------------------- /images/AquaTabCloseDirty_Front_Rollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabCloseDirty_Front_Rollover.png -------------------------------------------------------------------------------- /images/AquaTabClose_Front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabClose_Front.png -------------------------------------------------------------------------------- /images/AquaTabClose_Front_Pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabClose_Front_Pressed.png -------------------------------------------------------------------------------- /images/AquaTabClose_Front_Rollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabClose_Front_Rollover.png -------------------------------------------------------------------------------- /images/AquaTabNew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabNew.png -------------------------------------------------------------------------------- /images/AquaTabNewPressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabNewPressed.png -------------------------------------------------------------------------------- /images/AquaTabNewRollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabNewRollover.png -------------------------------------------------------------------------------- /images/AquaTabsBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabsBackground.png -------------------------------------------------------------------------------- /images/AquaTabsDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabsDown.png -------------------------------------------------------------------------------- /images/AquaTabsDownGraphite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabsDownGraphite.png -------------------------------------------------------------------------------- /images/AquaTabsDownNonKey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabsDownNonKey.png -------------------------------------------------------------------------------- /images/AquaTabsSeparator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabsSeparator.png -------------------------------------------------------------------------------- /images/AquaTabsSeparatorDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/AquaTabsSeparatorDown.png -------------------------------------------------------------------------------- /images/Folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/Folder.png -------------------------------------------------------------------------------- /images/Globe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/Globe.png -------------------------------------------------------------------------------- /images/TabClose_Dirty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/TabClose_Dirty.png -------------------------------------------------------------------------------- /images/TabClose_Dirty_Pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/TabClose_Dirty_Pressed.png -------------------------------------------------------------------------------- /images/TabClose_Dirty_Rollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/TabClose_Dirty_Rollover.png -------------------------------------------------------------------------------- /images/TabClose_Front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/TabClose_Front.png -------------------------------------------------------------------------------- /images/TabClose_Front_Pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/TabClose_Front_Pressed.png -------------------------------------------------------------------------------- /images/TabClose_Front_Rollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/TabClose_Front_Rollover.png -------------------------------------------------------------------------------- /images/TabNewMetal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/TabNewMetal.png -------------------------------------------------------------------------------- /images/TabNewMetalPressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/TabNewMetalPressed.png -------------------------------------------------------------------------------- /images/TabNewMetalRollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/TabNewMetalRollover.png -------------------------------------------------------------------------------- /images/Warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/Warning.png -------------------------------------------------------------------------------- /images/largeImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/largeImage.png -------------------------------------------------------------------------------- /images/mater_large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/mater_large.jpg -------------------------------------------------------------------------------- /images/mcqueen_large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/mcqueen_large.jpg -------------------------------------------------------------------------------- /images/overflowImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/overflowImage.png -------------------------------------------------------------------------------- /images/overflowImagePressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/overflowImagePressed.png -------------------------------------------------------------------------------- /images/pi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/pi.png -------------------------------------------------------------------------------- /images/sally_large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zachwaugh/PSMTabBarControl/823eb00c00d3b9233c7dbd27145867be9a4eb341/images/sally_large.jpg -------------------------------------------------------------------------------- /source/Framework_Prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | # import 3 | #endif 4 | -------------------------------------------------------------------------------- /source/NSBezierPath_AMShading.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSBezierPath_AMShading.h 3 | // ------------------------ 4 | // 5 | // Created by Andreas on 2005-06-01. 6 | // Copyright 2005 Andreas Mayer. All rights reserved. 7 | // 8 | // based on http://www.cocoadev.com/index.pl?GradientFill 9 | 10 | 11 | #import 12 | 13 | @interface NSBezierPath (AMShading) 14 | 15 | - (void)customHorizontalFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor; 16 | - (void)customVerticalFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor; 17 | 18 | - (void)linearGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor; 19 | - (void)linearVerticalGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor; 20 | 21 | - (void)bilinearGradientFillWithOuterColor:(NSColor *)outerColor innerColor:(NSColor *)innerColor; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /source/NSBezierPath_AMShading.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSBezierPath_AMShading.m 3 | // ------------------------ 4 | // 5 | // Created by Andreas on 2005-06-01. 6 | // Copyright 2005 Andreas Mayer. All rights reserved. 7 | // 8 | 9 | #import "NSBezierPath_AMShading.h" 10 | 11 | 12 | @implementation NSBezierPath (AMShading) 13 | 14 | static void linearShadedColor(void *info, const CGFloat *in, CGFloat *out) 15 | { 16 | CGFloat *colors = (CGFloat *)info; 17 | *out++ = colors[0] + *in * colors[8]; 18 | *out++ = colors[1] + *in * colors[9]; 19 | *out++ = colors[2] + *in * colors[10]; 20 | *out++ = colors[3] + *in * colors[11]; 21 | } 22 | 23 | static void bilinearShadedColor(void *info, const CGFloat *in, CGFloat *out) 24 | { 25 | CGFloat *colors = (CGFloat *)info; 26 | CGFloat factor = (*in)*2.0; 27 | if (*in > 0.5) { 28 | factor = 2-factor; 29 | } 30 | *out++ = colors[0] + factor * colors[8]; 31 | *out++ = colors[1] + factor * colors[9]; 32 | *out++ = colors[2] + factor * colors[10]; 33 | *out++ = colors[3] + factor * colors[11]; 34 | } 35 | 36 | - (void)linearGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor 37 | { 38 | static const CGFunctionCallbacks callbacks = {0, &linearShadedColor, NULL}; 39 | 40 | [self customHorizontalFillWithCallbacks:callbacks firstColor:startColor secondColor:endColor]; 41 | } 42 | 43 | - (void)linearVerticalGradientFillWithStartColor:(NSColor *)startColor endColor:(NSColor *)endColor 44 | { 45 | static const CGFunctionCallbacks callbacks = {0, &linearShadedColor, NULL}; 46 | 47 | [self customVerticalFillWithCallbacks:callbacks firstColor:startColor secondColor:endColor]; 48 | } 49 | 50 | - (void)bilinearGradientFillWithOuterColor:(NSColor *)outerColor innerColor:(NSColor *)innerColor 51 | { 52 | static const CGFunctionCallbacks callbacks = {0, &bilinearShadedColor, NULL}; 53 | 54 | [self customHorizontalFillWithCallbacks:callbacks firstColor:innerColor secondColor:outerColor]; 55 | } 56 | 57 | - (void)customFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint 58 | { 59 | CGColorSpaceRef colorspace; 60 | CGShadingRef shading; 61 | CGFunctionRef function; 62 | CGFloat colors[12]; // pointer to color values 63 | 64 | // get my context 65 | CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; 66 | 67 | NSColor *deviceDependentFirstColor = [firstColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]; 68 | NSColor *deviceDependentSecondColor = [secondColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]; 69 | 70 | // set up colors for gradient 71 | colors[0] = [deviceDependentFirstColor redComponent]; 72 | colors[1] = [deviceDependentFirstColor greenComponent]; 73 | colors[2] = [deviceDependentFirstColor blueComponent]; 74 | colors[3] = [deviceDependentFirstColor alphaComponent]; 75 | 76 | colors[4] = [deviceDependentSecondColor redComponent]; 77 | colors[5] = [deviceDependentSecondColor greenComponent]; 78 | colors[6] = [deviceDependentSecondColor blueComponent]; 79 | colors[7] = [deviceDependentSecondColor alphaComponent]; 80 | 81 | // difference between start and end color for each color components 82 | colors[8] = (colors[4]-colors[0]); 83 | colors[9] = (colors[5]-colors[1]); 84 | colors[10] = (colors[6]-colors[2]); 85 | colors[11] = (colors[7]-colors[3]); 86 | 87 | // draw gradient 88 | colorspace = CGColorSpaceCreateDeviceRGB(); 89 | size_t components = 1 + CGColorSpaceGetNumberOfComponents(colorspace); 90 | static const CGFloat domain[2] = {0.0, 1.0}; 91 | static const CGFloat range[10] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1}; 92 | //static const CGFunctionCallbacks callbacks = {0, &bilinearShadedColor, NULL}; 93 | 94 | // Create a CGFunctionRef that describes a function taking 1 input and kChannelsPerColor outputs. 95 | function = CGFunctionCreate(colors, 1, domain, components, range, &functionCallbacks); 96 | 97 | shading = CGShadingCreateAxial(colorspace, startPoint, endPoint, function, NO, NO); 98 | 99 | CGContextSaveGState(currentContext); 100 | [self addClip]; 101 | CGContextDrawShading(currentContext, shading); 102 | CGContextRestoreGState(currentContext); 103 | 104 | CGShadingRelease(shading); 105 | CGFunctionRelease(function); 106 | CGColorSpaceRelease(colorspace); 107 | } 108 | 109 | - (void)customHorizontalFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor 110 | { 111 | [self customFillWithCallbacks:functionCallbacks 112 | firstColor:firstColor 113 | secondColor:secondColor 114 | startPoint:CGPointMake(0, NSMinY([self bounds])) 115 | endPoint:CGPointMake(0, NSMaxY([self bounds]))]; 116 | } 117 | 118 | - (void)customVerticalFillWithCallbacks:(CGFunctionCallbacks)functionCallbacks firstColor:(NSColor *)firstColor secondColor:(NSColor *)secondColor 119 | { 120 | [self customFillWithCallbacks:functionCallbacks 121 | firstColor:firstColor 122 | secondColor:secondColor 123 | startPoint:CGPointMake(NSMinX([self bounds]), 0) 124 | endPoint:CGPointMake(NSMaxX([self bounds]), 0)]; 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /source/NSString_AITruncation.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString_AITruncation.h 3 | // PSMTabBarControl 4 | // 5 | // Created by Evan Schoenberg on 7/14/07. 6 | // 7 | 8 | #import 9 | 10 | @interface NSString (AITruncation) 11 | - (NSString *)stringWithEllipsisByTruncatingToLength:(NSUInteger)length; 12 | @end 13 | -------------------------------------------------------------------------------- /source/NSString_AITruncation.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString_AITruncation.m 3 | // PSMTabBarControl 4 | // 5 | // Created by Evan Schoenberg on 7/14/07. 6 | // From Adium, which is licensed under the GPL. Used in PSMTabBarControl with permission. 7 | // The contents of this remain licensed under the GPL. 8 | // 9 | 10 | #import "NSString_AITruncation.h" 11 | 12 | @implementation NSString (AITruncation) 13 | 14 | + (id)ellipsis 15 | { 16 | return [NSString stringWithUTF8String:"\xE2\x80\xA6"]; 17 | } 18 | 19 | - (NSString *)stringWithEllipsisByTruncatingToLength:(NSUInteger)length 20 | { 21 | NSString *returnString; 22 | 23 | if (length < [self length]) { 24 | //Truncate and append the ellipsis 25 | returnString = [[self substringToIndex:length-1] stringByAppendingString:[NSString ellipsis]]; 26 | } else { 27 | //We don't need to truncate, so don't append an ellipsis 28 | returnString = [[self copy] autorelease]; 29 | } 30 | 31 | return returnString; 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /source/PSMMetalTabStyle.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMMetalTabStyle.h 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 2/17/06. 6 | // Copyright 2006 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PSMTabStyle.h" 11 | 12 | @interface PSMMetalTabStyle : NSObject { 13 | NSImage *metalCloseButton; 14 | NSImage *metalCloseButtonDown; 15 | NSImage *metalCloseButtonOver; 16 | NSImage *metalCloseDirtyButton; 17 | NSImage *metalCloseDirtyButtonDown; 18 | NSImage *metalCloseDirtyButtonOver; 19 | NSImage *_addTabButtonImage; 20 | NSImage *_addTabButtonPressedImage; 21 | NSImage *_addTabButtonRolloverImage; 22 | 23 | NSDictionary *_objectCountStringAttributes; 24 | 25 | PSMTabBarOrientation orientation; 26 | PSMTabBarControl *tabBar; 27 | } 28 | 29 | - (void)drawInteriorWithTabCell:(PSMTabBarCell *)cell inView:(NSView*)controlView; 30 | 31 | - (void)encodeWithCoder:(NSCoder *)aCoder; 32 | - (id)initWithCoder:(NSCoder *)aDecoder; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /source/PSMMetalTabStyle.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMMetalTabStyle.m 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 2/17/06. 6 | // Copyright 2006 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import "PSMMetalTabStyle.h" 10 | #import "PSMTabBarCell.h" 11 | #import "PSMTabBarControl.h" 12 | 13 | #define kPSMMetalObjectCounterRadius 7.0 14 | #define kPSMMetalCounterMinWidth 20 15 | #define kPSMMetalPadding 20 16 | 17 | @implementation PSMMetalTabStyle 18 | 19 | - (NSString *)name 20 | { 21 | return @"Metal"; 22 | } 23 | 24 | #pragma mark - 25 | #pragma mark Creation/Destruction 26 | 27 | - (id) init 28 | { 29 | if ( (self = [super init]) ) { 30 | metalCloseButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Front"]]; 31 | metalCloseButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Front_Pressed"]]; 32 | metalCloseButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Front_Rollover"]]; 33 | 34 | metalCloseDirtyButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Dirty"]]; 35 | metalCloseDirtyButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Dirty_Pressed"]]; 36 | metalCloseDirtyButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Dirty_Rollover"]]; 37 | 38 | _addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabNewMetal"]]; 39 | _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabNewMetalPressed"]]; 40 | _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabNewMetalRollover"]]; 41 | 42 | _objectCountStringAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:[[NSFontManager sharedFontManager] convertFont:[NSFont fontWithName:@"Helvetica" size:11.0] toHaveTrait:NSBoldFontMask], NSFontAttributeName, 43 | [[NSColor whiteColor] colorWithAlphaComponent:0.85], NSForegroundColorAttributeName, 44 | nil, nil]; 45 | } 46 | return self; 47 | } 48 | 49 | - (void)dealloc 50 | { 51 | [metalCloseButton release]; 52 | [metalCloseButtonDown release]; 53 | [metalCloseButtonOver release]; 54 | [metalCloseDirtyButton release]; 55 | [metalCloseDirtyButtonDown release]; 56 | [metalCloseDirtyButtonOver release]; 57 | [_addTabButtonImage release]; 58 | [_addTabButtonPressedImage release]; 59 | [_addTabButtonRolloverImage release]; 60 | 61 | [_objectCountStringAttributes release]; 62 | 63 | [super dealloc]; 64 | } 65 | 66 | #pragma mark - 67 | #pragma mark Control Specific 68 | 69 | - (CGFloat)leftMarginForTabBarControl 70 | { 71 | return 2.0f; 72 | } 73 | 74 | - (CGFloat)rightMarginForTabBarControl 75 | { 76 | return 24.0f; 77 | } 78 | 79 | - (CGFloat)topMarginForTabBarControl 80 | { 81 | return 10.0f; 82 | } 83 | 84 | - (void)setOrientation:(PSMTabBarOrientation)value 85 | { 86 | orientation = value; 87 | } 88 | 89 | #pragma mark - 90 | #pragma mark Add Tab Button 91 | 92 | - (NSImage *)addTabButtonImage 93 | { 94 | return _addTabButtonImage; 95 | } 96 | 97 | - (NSImage *)addTabButtonPressedImage 98 | { 99 | return _addTabButtonPressedImage; 100 | } 101 | 102 | - (NSImage *)addTabButtonRolloverImage 103 | { 104 | return _addTabButtonRolloverImage; 105 | } 106 | 107 | #pragma mark - 108 | #pragma mark Cell Specific 109 | 110 | - (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell orientation:(PSMTabBarOrientation)tabOrientation 111 | { 112 | NSRect dragRect = [cell frame]; 113 | dragRect.size.width++; 114 | 115 | if ([cell tabState] & PSMTab_SelectedMask) { 116 | if (tabOrientation == PSMTabBarHorizontalOrientation) { 117 | dragRect.size.height -= 2.0; 118 | } else { 119 | dragRect.size.height += 1.0; 120 | dragRect.origin.y -= 1.0; 121 | dragRect.origin.x += 2.0; 122 | dragRect.size.width -= 3.0; 123 | } 124 | } else if (tabOrientation == PSMTabBarVerticalOrientation) { 125 | dragRect.origin.x--; 126 | } 127 | 128 | return dragRect; 129 | } 130 | 131 | - (NSRect)closeButtonRectForTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)cellFrame 132 | { 133 | if ([cell hasCloseButton] == NO) { 134 | return NSZeroRect; 135 | } 136 | 137 | NSRect result; 138 | result.size = [metalCloseButton size]; 139 | result.origin.x = cellFrame.origin.x + MARGIN_X; 140 | result.origin.y = cellFrame.origin.y + MARGIN_Y + 2.0; 141 | 142 | if ([cell state] == NSOnState) { 143 | result.origin.y -= 1; 144 | } 145 | 146 | return result; 147 | } 148 | 149 | - (NSRect)iconRectForTabCell:(PSMTabBarCell *)cell 150 | { 151 | NSRect cellFrame = [cell frame]; 152 | 153 | if ([cell hasIcon] == NO) { 154 | return NSZeroRect; 155 | } 156 | 157 | NSRect result; 158 | result.size = NSMakeSize(kPSMTabBarIconWidth, kPSMTabBarIconWidth); 159 | result.origin.x = cellFrame.origin.x + MARGIN_X; 160 | result.origin.y = cellFrame.origin.y + MARGIN_Y; 161 | 162 | if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) { 163 | result.origin.x += [metalCloseButton size].width + kPSMTabBarCellPadding; 164 | } 165 | 166 | if ([cell state] == NSOnState) { 167 | result.origin.y -= 1; 168 | } 169 | 170 | return result; 171 | } 172 | 173 | - (NSRect)indicatorRectForTabCell:(PSMTabBarCell *)cell 174 | { 175 | NSRect cellFrame = [cell frame]; 176 | 177 | if ([[cell indicator] isHidden]) { 178 | return NSZeroRect; 179 | } 180 | 181 | NSRect result; 182 | result.size = NSMakeSize(kPSMTabBarIndicatorWidth, kPSMTabBarIndicatorWidth); 183 | result.origin.x = cellFrame.origin.x + cellFrame.size.width - MARGIN_X - kPSMTabBarIndicatorWidth; 184 | result.origin.y = cellFrame.origin.y + MARGIN_Y; 185 | 186 | if ([cell state] == NSOnState) { 187 | result.origin.y -= 1; 188 | } 189 | 190 | return result; 191 | } 192 | 193 | - (NSRect)objectCounterRectForTabCell:(PSMTabBarCell *)cell 194 | { 195 | NSRect cellFrame = [cell frame]; 196 | 197 | if ([cell count] == 0) { 198 | return NSZeroRect; 199 | } 200 | 201 | CGFloat countWidth = [[self attributedObjectCountValueForTabCell:cell] size].width; 202 | countWidth += (2 * kPSMMetalObjectCounterRadius - 6.0); 203 | if (countWidth < kPSMMetalCounterMinWidth) { 204 | countWidth = kPSMMetalCounterMinWidth; 205 | } 206 | 207 | NSRect result; 208 | result.size = NSMakeSize(countWidth, 2 * kPSMMetalObjectCounterRadius); // temp 209 | result.origin.x = cellFrame.origin.x + cellFrame.size.width - MARGIN_X - result.size.width; 210 | result.origin.y = cellFrame.origin.y + MARGIN_Y + 1.0; 211 | 212 | if (![[cell indicator] isHidden]) { 213 | result.origin.x -= kPSMTabBarIndicatorWidth + kPSMTabBarCellPadding; 214 | } 215 | 216 | return result; 217 | } 218 | 219 | 220 | - (CGFloat)minimumWidthOfTabCell:(PSMTabBarCell *)cell 221 | { 222 | CGFloat resultWidth = 0.0; 223 | 224 | // left margin 225 | resultWidth = MARGIN_X; 226 | 227 | // close button? 228 | if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) { 229 | resultWidth += [metalCloseButton size].width + kPSMTabBarCellPadding; 230 | } 231 | 232 | // icon? 233 | if ([cell hasIcon]) { 234 | resultWidth += kPSMTabBarIconWidth + kPSMTabBarCellPadding; 235 | } 236 | 237 | // the label 238 | resultWidth += kPSMMinimumTitleWidth; 239 | 240 | // object counter? 241 | if ([cell count] > 0) { 242 | resultWidth += [self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding; 243 | } 244 | 245 | // indicator? 246 | if ([[cell indicator] isHidden] == NO) 247 | resultWidth += kPSMTabBarCellPadding + kPSMTabBarIndicatorWidth; 248 | 249 | // right margin 250 | resultWidth += MARGIN_X; 251 | 252 | return ceil(resultWidth); 253 | } 254 | 255 | - (CGFloat)desiredWidthOfTabCell:(PSMTabBarCell *)cell 256 | { 257 | CGFloat resultWidth = 0.0; 258 | 259 | // left margin 260 | resultWidth = MARGIN_X; 261 | 262 | // close button? 263 | if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed]) 264 | resultWidth += [metalCloseButton size].width + kPSMTabBarCellPadding; 265 | 266 | // icon? 267 | if ([cell hasIcon]) { 268 | resultWidth += kPSMTabBarIconWidth + kPSMTabBarCellPadding; 269 | } 270 | 271 | // the label 272 | resultWidth += [[cell attributedStringValue] size].width; 273 | 274 | // object counter? 275 | if ([cell count] > 0) { 276 | resultWidth += [self objectCounterRectForTabCell:cell].size.width + kPSMTabBarCellPadding; 277 | } 278 | 279 | // indicator? 280 | if ([[cell indicator] isHidden] == NO) 281 | resultWidth += kPSMTabBarCellPadding + kPSMTabBarIndicatorWidth; 282 | 283 | // right margin 284 | resultWidth += MARGIN_X; 285 | 286 | return ceil(resultWidth); 287 | } 288 | 289 | - (CGFloat)tabCellHeight 290 | { 291 | return kPSMTabBarControlHeight; 292 | } 293 | 294 | #pragma mark - 295 | #pragma mark Cell Values 296 | 297 | - (NSAttributedString *)attributedObjectCountValueForTabCell:(PSMTabBarCell *)cell 298 | { 299 | NSString *contents = [NSString stringWithFormat:@"%lu", (unsigned long)[cell count]]; 300 | return [[[NSMutableAttributedString alloc] initWithString:contents attributes:_objectCountStringAttributes] autorelease]; 301 | } 302 | 303 | - (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell 304 | { 305 | NSMutableAttributedString *attrStr; 306 | NSString *contents = [cell stringValue]; 307 | attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease]; 308 | NSRange range = NSMakeRange(0, [contents length]); 309 | 310 | // Add font attribute 311 | [attrStr addAttribute:NSFontAttributeName value:[NSFont boldSystemFontOfSize:11.0] range:range]; 312 | [attrStr addAttribute:NSForegroundColorAttributeName value:[[NSColor textColor] colorWithAlphaComponent:([[tabBar window] isMainWindow] ? 0.75 : 0.5)] range:range]; 313 | 314 | // Add shadow attribute 315 | NSShadow* shadow = shadow = [[[NSShadow alloc] init] autorelease]; 316 | [shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.5]]; 317 | [shadow setShadowOffset:NSMakeSize(0, -1)]; 318 | [shadow setShadowBlurRadius:1.0]; 319 | [attrStr addAttribute:NSShadowAttributeName value:shadow range:range]; 320 | 321 | // Paragraph Style for Truncating Long Text 322 | static NSMutableParagraphStyle *TruncatingTailParagraphStyle = nil; 323 | if (!TruncatingTailParagraphStyle) { 324 | TruncatingTailParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain]; 325 | [TruncatingTailParagraphStyle setLineBreakMode:NSLineBreakByTruncatingTail]; 326 | [TruncatingTailParagraphStyle setAlignment:NSCenterTextAlignment]; 327 | } 328 | 329 | [attrStr addAttribute:NSParagraphStyleAttributeName value:TruncatingTailParagraphStyle range:range]; 330 | 331 | return attrStr; 332 | } 333 | 334 | #pragma mark - 335 | #pragma mark ---- drawing ---- 336 | 337 | - (void)drawTabCell:(PSMTabBarCell *)cell 338 | { 339 | NSRect cellFrame = [cell frame]; 340 | NSColor *lineColor = ([[tabBar window] isMainWindow]) ? [NSColor darkGrayColor] : [NSColor grayColor]; 341 | NSBezierPath *bezier = [NSBezierPath bezierPath]; 342 | 343 | if ([cell state] == NSOnState) 344 | { 345 | // selected tab 346 | NSRect tabRect = NSOffsetRect(NSInsetRect(cellFrame, 0.5, -10), 0, -10.5); 347 | bezier = [NSBezierPath bezierPathWithRoundedRect:tabRect xRadius:5 yRadius:5]; 348 | [lineColor set]; 349 | [bezier setLineWidth:1.0]; 350 | 351 | // special case of hidden control; need line across top of cell 352 | if ([[cell controlView] frame].size.height < 2) 353 | { 354 | NSRectFillUsingOperation(tabRect, NSCompositeSourceOver); 355 | } 356 | else 357 | { 358 | // background 359 | [NSGraphicsContext saveGraphicsState]; 360 | [bezier addClip]; 361 | NSDrawWindowBackground(cellFrame); 362 | [NSGraphicsContext restoreGraphicsState]; 363 | 364 | [bezier stroke]; 365 | } 366 | } 367 | else 368 | { 369 | // unselected tab 370 | NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height); 371 | aRect.origin.y += 0.5; 372 | aRect.origin.x += 1.5; 373 | aRect.size.width -= 1; 374 | 375 | [lineColor set]; 376 | 377 | aRect.origin.x -= 1; 378 | aRect.size.width += 1; 379 | 380 | // frame 381 | [bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y)]; 382 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y)]; 383 | if (!([cell tabState] & PSMTab_RightIsSelectedMask)) 384 | { 385 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y + aRect.size.height)]; 386 | } 387 | 388 | [bezier stroke]; 389 | } 390 | 391 | [self drawInteriorWithTabCell:cell inView:[cell controlView]]; 392 | } 393 | 394 | 395 | - (void)drawInteriorWithTabCell:(PSMTabBarCell *)cell inView:(NSView*)controlView 396 | { 397 | NSRect cellFrame = [cell frame]; 398 | 399 | // close button - only show if mouse over cell 400 | if ([cell hasCloseButton] && ![cell isCloseButtonSuppressed] && [cell isHighlighted]) 401 | { 402 | NSSize closeButtonSize = NSZeroSize; 403 | NSRect closeButtonRect = [cell closeButtonRectForFrame:cellFrame]; 404 | NSImage *closeButton = nil; 405 | 406 | closeButton = [cell isEdited] ? metalCloseDirtyButton : metalCloseButton; 407 | if ([cell closeButtonOver]) closeButton = [cell isEdited] ? metalCloseDirtyButtonOver : metalCloseButtonOver; 408 | if ([cell closeButtonPressed]) closeButton = [cell isEdited] ? metalCloseDirtyButtonDown : metalCloseButtonDown; 409 | 410 | closeButtonSize = [closeButton size]; 411 | if ([controlView isFlipped]) { 412 | closeButtonRect.origin.y += closeButtonRect.size.height; 413 | } 414 | 415 | [closeButton compositeToPoint:closeButtonRect.origin operation:NSCompositeSourceOver fraction:1.0]; 416 | } 417 | 418 | // icon 419 | // if ([cell hasIcon]) 420 | // { 421 | // NSRect iconRect = [self iconRectForTabCell:cell]; 422 | // NSImage *icon = [[[cell representedObject] identifier] icon]; 423 | // 424 | // if ([controlView isFlipped]) 425 | // { 426 | // iconRect.origin.y += iconRect.size.height; 427 | // } 428 | // 429 | // // center in available space (in case icon image is smaller than kPSMTabBarIconWidth) 430 | // if ([icon size].width < kPSMTabBarIconWidth) 431 | // { 432 | // iconRect.origin.x += (kPSMTabBarIconWidth - [icon size].width)/2.0; 433 | // } 434 | // 435 | // if ([icon size].height < kPSMTabBarIconWidth) 436 | // { 437 | // iconRect.origin.y -= (kPSMTabBarIconWidth - [icon size].height)/2.0; 438 | // } 439 | // 440 | // [icon compositeToPoint:iconRect.origin operation:NSCompositeSourceOver fraction:1.0]; 441 | // } 442 | 443 | // object counter 444 | if ([cell count] > 0) 445 | { 446 | [[cell countColor] ?: [NSColor colorWithCalibratedWhite:0.3 alpha:0.6] set]; 447 | NSBezierPath *path = [NSBezierPath bezierPath]; 448 | NSRect myRect = [self objectCounterRectForTabCell:cell]; 449 | if ([cell state] == NSOnState) { 450 | myRect.origin.y -= 1.0; 451 | } 452 | [path moveToPoint:NSMakePoint(myRect.origin.x + kPSMMetalObjectCounterRadius, myRect.origin.y)]; 453 | [path lineToPoint:NSMakePoint(myRect.origin.x + myRect.size.width - kPSMMetalObjectCounterRadius, myRect.origin.y)]; 454 | [path appendBezierPathWithArcWithCenter:NSMakePoint(myRect.origin.x + myRect.size.width - kPSMMetalObjectCounterRadius, myRect.origin.y + kPSMMetalObjectCounterRadius) radius:kPSMMetalObjectCounterRadius startAngle:270.0 endAngle:90.0]; 455 | [path lineToPoint:NSMakePoint(myRect.origin.x + kPSMMetalObjectCounterRadius, myRect.origin.y + myRect.size.height)]; 456 | [path appendBezierPathWithArcWithCenter:NSMakePoint(myRect.origin.x + kPSMMetalObjectCounterRadius, myRect.origin.y + kPSMMetalObjectCounterRadius) radius:kPSMMetalObjectCounterRadius startAngle:90.0 endAngle:270.0]; 457 | [path fill]; 458 | 459 | // draw attributed string centered in area 460 | NSRect counterStringRect; 461 | NSAttributedString *counterString = [self attributedObjectCountValueForTabCell:cell]; 462 | counterStringRect.size = [counterString size]; 463 | counterStringRect.origin.x = myRect.origin.x + ((myRect.size.width - counterStringRect.size.width) / 2.0) + 0.25; 464 | counterStringRect.origin.y = myRect.origin.y + ((myRect.size.height - counterStringRect.size.height) / 2.0) + 0.5; 465 | [counterString drawInRect:counterStringRect]; 466 | } 467 | 468 | // draw label 469 | NSRect labelRect = cellFrame; 470 | NSAttributedString *string = [cell attributedStringValue]; 471 | NSSize textSize = [string size]; 472 | float textHeight = textSize.height; 473 | float labelWidth = MIN(cellFrame.size.width - (kPSMMetalPadding * 2), textSize.width); 474 | labelRect.size.height = textHeight; 475 | labelRect.size.width = labelWidth; 476 | labelRect.origin.x = cellFrame.origin.x + ((cellFrame.size.width - labelWidth) / 2); 477 | labelRect.origin.y = ((cellFrame.size.height - textHeight) / 2); 478 | 479 | [string drawInRect:labelRect]; 480 | } 481 | 482 | - (void)drawBackgroundInRect:(NSRect)rect 483 | { 484 | //Draw for our whole bounds; it'll be automatically clipped to fit the appropriate drawing area 485 | rect = [tabBar bounds]; 486 | 487 | [NSGraphicsContext saveGraphicsState]; 488 | [[NSGraphicsContext currentContext] setShouldAntialias:NO]; 489 | 490 | [[NSColor colorWithCalibratedWhite:0.0 alpha:([[tabBar window] isMainWindow] ? 0.1 : 0.05)] set]; 491 | NSRectFillUsingOperation(rect, NSCompositeSourceAtop); 492 | 493 | NSGradient *shadow = [[NSGradient alloc ] initWithStartingColor:[NSColor colorWithDeviceWhite:0 alpha:([[tabBar window] isMainWindow] ? 0.15 : 0.1)] endingColor:[NSColor clearColor]]; 494 | NSRect shadowRect = NSMakeRect(rect.origin.x, rect.origin.y, rect.size.width, 7); 495 | [shadow drawInRect:shadowRect angle:90]; 496 | [shadow release]; 497 | 498 | if ([[tabBar window] isMainWindow]) 499 | { 500 | [[NSColor darkGrayColor] set]; 501 | } 502 | else 503 | { 504 | [[NSColor grayColor] set]; 505 | } 506 | 507 | [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x, rect.origin.y + 0.5) toPoint:NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + 0.5)]; 508 | [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height - 0.5) toPoint:NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height - 0.5)]; 509 | 510 | [NSGraphicsContext restoreGraphicsState]; 511 | } 512 | 513 | 514 | - (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect 515 | { 516 | if (tabBar != bar) { 517 | tabBar = bar; 518 | } 519 | 520 | [self drawBackgroundInRect:rect]; 521 | 522 | // no tab view == not connected 523 | if (![bar tabView]) 524 | { 525 | NSRect labelRect = rect; 526 | labelRect.size.height -= 4.0; 527 | labelRect.origin.y += 4.0; 528 | NSMutableAttributedString *attrStr; 529 | NSString *contents = @"PSMTabBarControl"; 530 | attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease]; 531 | NSRange range = NSMakeRange(0, [contents length]); 532 | [attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range]; 533 | NSMutableParagraphStyle *centeredParagraphStyle = nil; 534 | if (!centeredParagraphStyle) { 535 | centeredParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain]; 536 | [centeredParagraphStyle setAlignment:NSCenterTextAlignment]; 537 | } 538 | [attrStr addAttribute:NSParagraphStyleAttributeName value:centeredParagraphStyle range:range]; 539 | [attrStr drawInRect:labelRect]; 540 | return; 541 | } 542 | 543 | // draw cells 544 | NSEnumerator *e = [[bar cells] objectEnumerator]; 545 | PSMTabBarCell *cell; 546 | while ( (cell = [e nextObject]) ) { 547 | if ([bar isAnimating] || (![cell isInOverflowMenu] && NSIntersectsRect([cell frame], rect))) { 548 | [cell drawWithFrame:[cell frame] inView:bar]; 549 | } 550 | } 551 | } 552 | 553 | #pragma mark - 554 | #pragma mark Archiving 555 | 556 | - (void)encodeWithCoder:(NSCoder *)aCoder 557 | { 558 | //[super encodeWithCoder:aCoder]; 559 | if ([aCoder allowsKeyedCoding]) { 560 | [aCoder encodeObject:metalCloseButton forKey:@"metalCloseButton"]; 561 | [aCoder encodeObject:metalCloseButtonDown forKey:@"metalCloseButtonDown"]; 562 | [aCoder encodeObject:metalCloseButtonOver forKey:@"metalCloseButtonOver"]; 563 | [aCoder encodeObject:metalCloseDirtyButton forKey:@"metalCloseDirtyButton"]; 564 | [aCoder encodeObject:metalCloseDirtyButtonDown forKey:@"metalCloseDirtyButtonDown"]; 565 | [aCoder encodeObject:metalCloseDirtyButtonOver forKey:@"metalCloseDirtyButtonOver"]; 566 | [aCoder encodeObject:_addTabButtonImage forKey:@"addTabButtonImage"]; 567 | [aCoder encodeObject:_addTabButtonPressedImage forKey:@"addTabButtonPressedImage"]; 568 | [aCoder encodeObject:_addTabButtonRolloverImage forKey:@"addTabButtonRolloverImage"]; 569 | } 570 | } 571 | 572 | - (id)initWithCoder:(NSCoder *)aDecoder 573 | { 574 | // self = [super initWithCoder:aDecoder]; 575 | //if (self) { 576 | if ([aDecoder allowsKeyedCoding]) { 577 | metalCloseButton = [[aDecoder decodeObjectForKey:@"metalCloseButton"] retain]; 578 | metalCloseButtonDown = [[aDecoder decodeObjectForKey:@"metalCloseButtonDown"] retain]; 579 | metalCloseButtonOver = [[aDecoder decodeObjectForKey:@"metalCloseButtonOver"] retain]; 580 | metalCloseDirtyButton = [[aDecoder decodeObjectForKey:@"metalCloseDirtyButton"] retain]; 581 | metalCloseDirtyButtonDown = [[aDecoder decodeObjectForKey:@"metalCloseDirtyButtonDown"] retain]; 582 | metalCloseDirtyButtonOver = [[aDecoder decodeObjectForKey:@"metalCloseDirtyButtonOver"] retain]; 583 | _addTabButtonImage = [[aDecoder decodeObjectForKey:@"addTabButtonImage"] retain]; 584 | _addTabButtonPressedImage = [[aDecoder decodeObjectForKey:@"addTabButtonPressedImage"] retain]; 585 | _addTabButtonRolloverImage = [[aDecoder decodeObjectForKey:@"addTabButtonRolloverImage"] retain]; 586 | } 587 | //} 588 | return self; 589 | } 590 | 591 | @end 592 | -------------------------------------------------------------------------------- /source/PSMOverflowPopUpButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMOverflowPopUpButton.h 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 11/4/05. 6 | // Copyright 2005 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface PSMOverflowPopUpButton : NSPopUpButton { 13 | NSImage *_PSMTabBarOverflowPopUpImage; 14 | NSImage *_PSMTabBarOverflowDownPopUpImage; 15 | BOOL _down; 16 | BOOL _animatingAlternateImage; 17 | NSTimer *_animationTimer; 18 | CGFloat _animationValue; 19 | } 20 | 21 | //alternate image display 22 | - (BOOL)animatingAlternateImage; 23 | - (void)setAnimatingAlternateImage:(BOOL)flag; 24 | 25 | // archiving 26 | - (void)encodeWithCoder:(NSCoder *)aCoder; 27 | - (id)initWithCoder:(NSCoder *)aDecoder; 28 | @end 29 | -------------------------------------------------------------------------------- /source/PSMOverflowPopUpButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMOverflowPopUpButton.m 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 11/4/05. 6 | // Copyright 2005 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import "PSMOverflowPopUpButton.h" 10 | #import "PSMTabBarControl.h" 11 | 12 | #define TIMER_INTERVAL 1.0 / 15.0 13 | #define ANIMATION_STEP 0.033f 14 | 15 | @implementation PSMOverflowPopUpButton 16 | 17 | - (id)initWithFrame:(NSRect)frameRect pullsDown:(BOOL)flag 18 | { 19 | if (self = [super initWithFrame:frameRect pullsDown:YES]) { 20 | [self setBezelStyle:NSRegularSquareBezelStyle]; 21 | [self setBordered:NO]; 22 | [self setTitle:@""]; 23 | [self setPreferredEdge:NSMaxXEdge]; 24 | _PSMTabBarOverflowPopUpImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"overflowImage"]]; 25 | _PSMTabBarOverflowDownPopUpImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"overflowImagePressed"]]; 26 | _animatingAlternateImage = NO; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)dealloc 32 | { 33 | [_PSMTabBarOverflowPopUpImage release]; 34 | [_PSMTabBarOverflowDownPopUpImage release]; 35 | [super dealloc]; 36 | } 37 | 38 | - (void)drawRect:(NSRect)rect 39 | { 40 | if(_PSMTabBarOverflowPopUpImage == nil){ 41 | [super drawRect:rect]; 42 | return; 43 | } 44 | 45 | NSImage *image = (_down) ? _PSMTabBarOverflowDownPopUpImage : _PSMTabBarOverflowPopUpImage; 46 | NSSize imageSize = [image size]; 47 | NSRect bounds = [self bounds]; 48 | 49 | NSPoint drawPoint = NSMakePoint(NSMidX(bounds) - (imageSize.width * 0.5f), NSMidY(bounds) - (imageSize.height * 0.5f)); 50 | 51 | if ([self isFlipped]) { 52 | drawPoint.y += imageSize.height; 53 | } 54 | 55 | [image compositeToPoint:drawPoint operation:NSCompositeSourceOver fraction:(_animatingAlternateImage ? 0.7f : 1.0f)]; 56 | 57 | if (_animatingAlternateImage) { 58 | NSImage *alternateImage = [self alternateImage]; 59 | NSSize altImageSize = [alternateImage size]; 60 | drawPoint = NSMakePoint(NSMidX(bounds) - (altImageSize.width * 0.5f), NSMidY(bounds) - (altImageSize.height * 0.5f)); 61 | 62 | if ([self isFlipped]) { 63 | drawPoint.y += altImageSize.height; 64 | } 65 | 66 | [[self alternateImage] compositeToPoint:drawPoint operation:NSCompositeSourceOver fraction:sin(_animationValue * M_PI)]; 67 | } 68 | } 69 | 70 | - (void)mouseDown:(NSEvent *)event 71 | { 72 | _down = YES; 73 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationReceived:) name:NSMenuDidEndTrackingNotification object:[self menu]]; 74 | [self setNeedsDisplay:YES]; 75 | [super mouseDown:event]; 76 | } 77 | 78 | - (void)setHidden:(BOOL)value 79 | { 80 | if ([self isHidden] != value) { 81 | if (value) { 82 | // Stop any animating alternate image if we hide 83 | [_animationTimer invalidate], _animationTimer = nil; 84 | } else if (_animatingAlternateImage) { 85 | // Restart any animating alternate image if we unhide 86 | _animationValue = ANIMATION_STEP; 87 | _animationTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(animateStep:) userInfo:nil repeats:YES]; 88 | [[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSEventTrackingRunLoopMode]; 89 | } 90 | } 91 | 92 | [super setHidden:value]; 93 | } 94 | 95 | - (void)notificationReceived:(NSNotification *)notification 96 | { 97 | _down = NO; 98 | [self setNeedsDisplay:YES]; 99 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 100 | } 101 | 102 | - (void)setAnimatingAlternateImage:(BOOL)flag 103 | { 104 | if (_animatingAlternateImage != flag) { 105 | _animatingAlternateImage = flag; 106 | 107 | if (![self isHidden]) { 108 | if (flag) { 109 | _animationValue = ANIMATION_STEP; 110 | _animationTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(animateStep:) userInfo:nil repeats:YES]; 111 | [[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSEventTrackingRunLoopMode]; 112 | 113 | } else { 114 | [_animationTimer invalidate], _animationTimer = nil; 115 | } 116 | 117 | [self setNeedsDisplay:YES]; 118 | } 119 | } 120 | } 121 | 122 | - (BOOL)animatingAlternateImage; 123 | { 124 | return _animatingAlternateImage; 125 | } 126 | 127 | - (void)animateStep:(NSTimer *)timer 128 | { 129 | _animationValue += ANIMATION_STEP; 130 | 131 | if (_animationValue >= 1) { 132 | _animationValue = ANIMATION_STEP; 133 | } 134 | 135 | [self setNeedsDisplay:YES]; 136 | } 137 | 138 | #pragma mark - 139 | #pragma mark Archiving 140 | 141 | - (void)encodeWithCoder:(NSCoder *)aCoder { 142 | [super encodeWithCoder:aCoder]; 143 | if ([aCoder allowsKeyedCoding]) { 144 | [aCoder encodeObject:_PSMTabBarOverflowPopUpImage forKey:@"PSMTabBarOverflowPopUpImage"]; 145 | [aCoder encodeObject:_PSMTabBarOverflowDownPopUpImage forKey:@"PSMTabBarOverflowDownPopUpImage"]; 146 | [aCoder encodeBool:_animatingAlternateImage forKey:@"PSMTabBarOverflowAnimatingAlternateImage"]; 147 | } 148 | } 149 | 150 | - (id)initWithCoder:(NSCoder *)aDecoder { 151 | if ( (self = [super initWithCoder:aDecoder]) ) { 152 | if ([aDecoder allowsKeyedCoding]) { 153 | _PSMTabBarOverflowPopUpImage = [[aDecoder decodeObjectForKey:@"PSMTabBarOverflowPopUpImage"] retain]; 154 | _PSMTabBarOverflowDownPopUpImage = [[aDecoder decodeObjectForKey:@"PSMTabBarOverflowDownPopUpImage"] retain]; 155 | [self setAnimatingAlternateImage:[aDecoder decodeBoolForKey:@"PSMTabBarOverflowAnimatingAlternateImage"]]; 156 | } 157 | } 158 | return self; 159 | } 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /source/PSMProgressIndicator.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMProgressIndicator.h 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 2/23/06. 6 | // Copyright 2006 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PSMTabBarControl.h" 11 | 12 | 13 | @interface PSMProgressIndicator : NSProgressIndicator { 14 | 15 | } 16 | 17 | @end 18 | 19 | @interface PSMTabBarControl (LayoutPlease) 20 | 21 | - (void)update; 22 | 23 | @end -------------------------------------------------------------------------------- /source/PSMProgressIndicator.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMProgressIndicator.m 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 2/23/06. 6 | // Copyright 2006 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import "PSMProgressIndicator.h" 10 | 11 | @implementation PSMProgressIndicator 12 | 13 | // overrides to make tab bar control re-layout things if status changes 14 | - (void)setHidden:(BOOL)flag 15 | { 16 | [super setHidden:flag]; 17 | [(PSMTabBarControl *)[self superview] update]; 18 | } 19 | 20 | - (void)stopAnimation:(id)sender 21 | { 22 | [NSObject cancelPreviousPerformRequestsWithTarget:self 23 | selector:@selector(startAnimation:) 24 | object:nil]; 25 | [super stopAnimation:sender]; 26 | } 27 | @end 28 | -------------------------------------------------------------------------------- /source/PSMRolloverButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMOverflowPopUpButton.h 3 | // NetScrape 4 | // 5 | // Created by John Pannell on 8/4/04. 6 | // Copyright 2004 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PSMRolloverButton : NSButton 12 | { 13 | NSImage *_rolloverImage; 14 | NSImage *_usualImage; 15 | NSTrackingRectTag _myTrackingRectTag; 16 | } 17 | 18 | // the regular image 19 | - (void)setUsualImage:(NSImage *)newImage; 20 | - (NSImage *)usualImage; 21 | 22 | // the rollover image 23 | - (void)setRolloverImage:(NSImage *)newImage; 24 | - (NSImage *)rolloverImage; 25 | 26 | // tracking rect for mouse events 27 | - (void)addTrackingRect; 28 | - (void)removeTrackingRect; 29 | @end -------------------------------------------------------------------------------- /source/PSMRolloverButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMOverflowPopUpButton.m 3 | // NetScrape 4 | // 5 | // Created by John Pannell on 8/4/04. 6 | // Copyright 2004 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import "PSMRolloverButton.h" 10 | 11 | @implementation PSMRolloverButton 12 | 13 | - (void)awakeFromNib 14 | { 15 | if ([[self superclass] instancesRespondToSelector:@selector(awakeFromNib)]) { 16 | [super awakeFromNib]; 17 | } 18 | 19 | [[NSNotificationCenter defaultCenter] addObserver:self 20 | selector:@selector(rolloverFrameDidChange:) 21 | name:NSViewFrameDidChangeNotification 22 | object:self]; 23 | [self setPostsFrameChangedNotifications:YES]; 24 | [self resetCursorRects]; 25 | 26 | _myTrackingRectTag = -1; 27 | } 28 | 29 | - (void)dealloc 30 | { 31 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 32 | 33 | [self removeTrackingRect]; 34 | 35 | [super dealloc]; 36 | } 37 | 38 | // the regular image 39 | - (void)setUsualImage:(NSImage *)newImage 40 | { 41 | [newImage retain]; 42 | [_usualImage release]; 43 | _usualImage = newImage; 44 | 45 | [self setImage:_usualImage]; 46 | } 47 | 48 | - (NSImage *)usualImage 49 | { 50 | return _usualImage; 51 | } 52 | 53 | - (void)setRolloverImage:(NSImage *)newImage 54 | { 55 | [newImage retain]; 56 | [_rolloverImage release]; 57 | _rolloverImage = newImage; 58 | } 59 | 60 | - (NSImage *)rolloverImage 61 | { 62 | return _rolloverImage; 63 | } 64 | 65 | //Remove old tracking rects when we change superviews 66 | - (void)viewWillMoveToSuperview:(NSView *)newSuperview 67 | { 68 | [self removeTrackingRect]; 69 | 70 | [super viewWillMoveToSuperview:newSuperview]; 71 | } 72 | 73 | - (void)viewDidMoveToSuperview 74 | { 75 | [super viewDidMoveToSuperview]; 76 | 77 | [self resetCursorRects]; 78 | } 79 | 80 | - (void)viewWillMoveToWindow:(NSWindow *)newWindow 81 | { 82 | [self removeTrackingRect]; 83 | 84 | [super viewWillMoveToWindow:newWindow]; 85 | } 86 | 87 | - (void)viewDidMoveToWindow 88 | { 89 | [super viewDidMoveToWindow]; 90 | 91 | [self resetCursorRects]; 92 | } 93 | 94 | - (void)rolloverFrameDidChange:(NSNotification *)inNotification 95 | { 96 | [self resetCursorRects]; 97 | } 98 | 99 | - (void)addTrackingRect 100 | { 101 | // assign a tracking rect to watch for mouse enter/exit 102 | NSRect trackRect = [self bounds]; 103 | NSPoint localPoint = [self convertPoint:[[self window] convertScreenToBase:[NSEvent mouseLocation]] 104 | fromView:nil]; 105 | BOOL mouseInside = NSPointInRect(localPoint, trackRect); 106 | 107 | _myTrackingRectTag = [self addTrackingRect:trackRect owner:self userData:nil assumeInside:mouseInside]; 108 | if (mouseInside) 109 | [self mouseEntered:nil]; 110 | else 111 | [self mouseExited:nil]; 112 | } 113 | 114 | - (void)removeTrackingRect 115 | { 116 | if (_myTrackingRectTag != -1) { 117 | [self removeTrackingRect:_myTrackingRectTag]; 118 | } 119 | _myTrackingRectTag = -1; 120 | } 121 | 122 | // override for rollover effect 123 | - (void)mouseEntered:(NSEvent *)theEvent; 124 | { 125 | // set rollover image 126 | [self setImage:_rolloverImage]; 127 | 128 | [super mouseEntered:theEvent]; 129 | } 130 | 131 | - (void)mouseExited:(NSEvent *)theEvent; 132 | { 133 | // restore usual image 134 | [self setImage:_usualImage]; 135 | 136 | [super mouseExited:theEvent]; 137 | } 138 | 139 | - (void)resetCursorRects 140 | { 141 | // called when the button rect has been changed 142 | [self removeTrackingRect]; 143 | [self addTrackingRect]; 144 | } 145 | 146 | - (void)setFrame:(NSRect)rect 147 | { 148 | [super setFrame:rect]; 149 | [self resetCursorRects]; 150 | } 151 | 152 | - (void)setBounds:(NSRect)rect 153 | { 154 | [super setBounds:rect]; 155 | [self resetCursorRects]; 156 | } 157 | 158 | #pragma mark - 159 | #pragma mark Archiving 160 | 161 | - (void)encodeWithCoder:(NSCoder *)aCoder { 162 | [super encodeWithCoder:aCoder]; 163 | if ([aCoder allowsKeyedCoding]) { 164 | [aCoder encodeObject:_rolloverImage forKey:@"rolloverImage"]; 165 | [aCoder encodeObject:_usualImage forKey:@"usualImage"]; 166 | [aCoder encodeInteger:_myTrackingRectTag forKey:@"myTrackingRectTag"]; 167 | } 168 | } 169 | 170 | - (id)initWithCoder:(NSCoder *)aDecoder { 171 | self = [super initWithCoder:aDecoder]; 172 | if (self) { 173 | if ([aDecoder allowsKeyedCoding]) { 174 | _rolloverImage = [[aDecoder decodeObjectForKey:@"rolloverImage"] retain]; 175 | _usualImage = [[aDecoder decodeObjectForKey:@"usualImage"] retain]; 176 | _myTrackingRectTag = [aDecoder decodeIntegerForKey:@"myTrackingRectTag"]; 177 | } 178 | } 179 | return self; 180 | } 181 | 182 | 183 | @end 184 | -------------------------------------------------------------------------------- /source/PSMTabBarCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabBarCell.h 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 10/13/05. 6 | // Copyright 2005 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PSMTabBarControl.h" 11 | 12 | @class PSMTabBarControl; 13 | @class PSMProgressIndicator; 14 | 15 | @interface PSMTabBarCell : NSActionCell { 16 | // sizing 17 | NSRect _frame; 18 | NSSize _stringSize; 19 | NSInteger _currentStep; 20 | BOOL _isPlaceholder; 21 | 22 | // state 23 | NSInteger _tabState; 24 | NSTrackingRectTag _closeButtonTrackingTag; // left side tracking, if dragging 25 | NSTrackingRectTag _cellTrackingTag; // right side tracking, if dragging 26 | BOOL _closeButtonOver; 27 | BOOL _closeButtonPressed; 28 | PSMProgressIndicator *_indicator; 29 | BOOL _isInOverflowMenu; 30 | BOOL _hasCloseButton; 31 | BOOL _isCloseButtonSuppressed; 32 | BOOL _hasIcon; 33 | BOOL _hasLargeImage; 34 | NSInteger _count; 35 | NSColor *_countColor; 36 | BOOL _isEdited; 37 | } 38 | 39 | // creation/destruction 40 | - (id)initWithControlView:(PSMTabBarControl *)controlView; 41 | - (id)initPlaceholderWithFrame:(NSRect)frame expanded:(BOOL)value inControlView:(PSMTabBarControl *)controlView; 42 | - (void)dealloc; 43 | 44 | // accessors 45 | - (NSTrackingRectTag)closeButtonTrackingTag; 46 | - (void)setCloseButtonTrackingTag:(NSTrackingRectTag)tag; 47 | - (NSTrackingRectTag)cellTrackingTag; 48 | - (void)setCellTrackingTag:(NSTrackingRectTag)tag; 49 | - (CGFloat)width; 50 | - (NSRect)frame; 51 | - (void)setFrame:(NSRect)rect; 52 | - (void)setStringValue:(NSString *)aString; 53 | - (NSSize)stringSize; 54 | - (NSAttributedString *)attributedStringValue; 55 | - (NSInteger)tabState; 56 | - (void)setTabState:(NSInteger)state; 57 | - (NSProgressIndicator *)indicator; 58 | - (BOOL)isInOverflowMenu; 59 | - (void)setIsInOverflowMenu:(BOOL)value; 60 | - (BOOL)closeButtonPressed; 61 | - (void)setCloseButtonPressed:(BOOL)value; 62 | - (BOOL)closeButtonOver; 63 | - (void)setCloseButtonOver:(BOOL)value; 64 | - (BOOL)hasCloseButton; 65 | - (void)setHasCloseButton:(BOOL)set; 66 | - (void)setCloseButtonSuppressed:(BOOL)suppress; 67 | - (BOOL)isCloseButtonSuppressed; 68 | - (BOOL)hasIcon; 69 | - (void)setHasIcon:(BOOL)value; 70 | - (BOOL)hasLargeImage; 71 | - (void)setHasLargeImage:(BOOL)value; 72 | - (NSInteger)count; 73 | - (void)setCount:(NSInteger)value; 74 | - (NSColor *)countColor; 75 | - (void)setCountColor:(NSColor *)value; 76 | - (BOOL)isPlaceholder; 77 | - (void)setIsPlaceholder:(BOOL)value; 78 | - (NSInteger)currentStep; 79 | - (void)setCurrentStep:(NSInteger)value; 80 | - (BOOL)isEdited; 81 | - (void)setIsEdited:(BOOL)value; 82 | 83 | // component attributes 84 | - (NSRect)indicatorRectForFrame:(NSRect)cellFrame; 85 | - (NSRect)closeButtonRectForFrame:(NSRect)cellFrame; 86 | - (CGFloat)minimumWidthOfCell; 87 | - (CGFloat)desiredWidthOfCell; 88 | 89 | // drawing 90 | - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView; 91 | 92 | // tracking the mouse 93 | - (void)mouseEntered:(NSEvent *)theEvent; 94 | - (void)mouseExited:(NSEvent *)theEvent; 95 | 96 | // drag support 97 | - (NSImage *)dragImage; 98 | 99 | // archiving 100 | - (void)encodeWithCoder:(NSCoder *)aCoder; 101 | - (id)initWithCoder:(NSCoder *)aDecoder; 102 | 103 | @end 104 | 105 | @interface PSMTabBarControl (CellAccessors) 106 | 107 | - (id)style; 108 | 109 | @end 110 | 111 | @interface NSObject (IdentifierAccesors) 112 | 113 | - (NSImage *)largeImage; 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /source/PSMTabBarCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabBarCell.m 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 10/13/05. 6 | // Copyright 2005 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import "PSMTabBarCell.h" 10 | #import "PSMTabBarControl.h" 11 | #import "PSMTabStyle.h" 12 | #import "PSMProgressIndicator.h" 13 | #import "PSMTabDragAssistant.h" 14 | 15 | @interface PSMTabBarControl (Private) 16 | - (void)update; 17 | @end 18 | 19 | @implementation PSMTabBarCell 20 | 21 | #pragma mark - 22 | #pragma mark Creation/Destruction 23 | - (id)initWithControlView:(PSMTabBarControl *)controlView 24 | { 25 | if ( (self = [super init]) ) { 26 | [self setControlView:controlView]; 27 | _closeButtonTrackingTag = 0; 28 | _cellTrackingTag = 0; 29 | _closeButtonOver = NO; 30 | _closeButtonPressed = NO; 31 | _indicator = [[PSMProgressIndicator alloc] initWithFrame:NSMakeRect(0.0,0.0,kPSMTabBarIndicatorWidth,kPSMTabBarIndicatorWidth)]; 32 | [_indicator setStyle:NSProgressIndicatorSpinningStyle]; 33 | [_indicator setAutoresizingMask:NSViewMinYMargin]; 34 | _hasCloseButton = YES; 35 | _isCloseButtonSuppressed = NO; 36 | _count = 0; 37 | _countColor = nil; 38 | _isEdited = NO; 39 | _isPlaceholder = NO; 40 | } 41 | return self; 42 | } 43 | 44 | - (id)initPlaceholderWithFrame:(NSRect)frame expanded:(BOOL)value inControlView:(PSMTabBarControl *)controlView 45 | { 46 | if ( (self = [super init]) ) { 47 | [self setControlView:controlView]; 48 | _isPlaceholder = YES; 49 | if (!value) { 50 | if ([controlView orientation] == PSMTabBarHorizontalOrientation) { 51 | frame.size.width = 0.0; 52 | } else { 53 | frame.size.height = 0.0; 54 | } 55 | } 56 | [self setFrame:frame]; 57 | _closeButtonTrackingTag = 0; 58 | _cellTrackingTag = 0; 59 | _closeButtonOver = NO; 60 | _closeButtonPressed = NO; 61 | _indicator = nil; 62 | _hasCloseButton = YES; 63 | _isCloseButtonSuppressed = NO; 64 | _count = 0; 65 | _countColor = nil; 66 | _isEdited = NO; 67 | 68 | if (value) { 69 | [self setCurrentStep:(kPSMTabDragAnimationSteps - 1)]; 70 | } else { 71 | [self setCurrentStep:0]; 72 | } 73 | } 74 | return self; 75 | } 76 | 77 | - (void)dealloc 78 | { 79 | [_countColor release]; 80 | 81 | [_indicator removeFromSuperviewWithoutNeedingDisplay]; 82 | 83 | [_indicator release]; 84 | [super dealloc]; 85 | } 86 | 87 | #pragma mark - 88 | #pragma mark Accessors 89 | 90 | - (NSTrackingRectTag)closeButtonTrackingTag 91 | { 92 | return _closeButtonTrackingTag; 93 | } 94 | 95 | - (void)setCloseButtonTrackingTag:(NSTrackingRectTag)tag 96 | { 97 | _closeButtonTrackingTag = tag; 98 | } 99 | 100 | - (NSTrackingRectTag)cellTrackingTag 101 | { 102 | return _cellTrackingTag; 103 | } 104 | 105 | - (void)setCellTrackingTag:(NSTrackingRectTag)tag 106 | { 107 | _cellTrackingTag = tag; 108 | } 109 | 110 | - (CGFloat)width 111 | { 112 | return _frame.size.width; 113 | } 114 | 115 | - (NSRect)frame 116 | { 117 | return _frame; 118 | } 119 | 120 | - (void)setFrame:(NSRect)rect 121 | { 122 | _frame = rect; 123 | 124 | //move the status indicator along with the rest of the cell 125 | if (![[self indicator] isHidden] && ![(PSMTabBarControl *)[self controlView] isTabBarHidden]) { 126 | [[self indicator] setFrame:[self indicatorRectForFrame:rect]]; 127 | } 128 | } 129 | 130 | - (void)setStringValue:(NSString *)aString 131 | { 132 | [super setStringValue:aString]; 133 | _stringSize = [[self attributedStringValue] size]; 134 | // need to redisplay now - binding observation was too quick. 135 | [(PSMTabBarControl *)[self controlView] update]; 136 | } 137 | 138 | - (NSSize)stringSize 139 | { 140 | return _stringSize; 141 | } 142 | 143 | - (NSAttributedString *)attributedStringValue 144 | { 145 | return [(id )[(PSMTabBarControl *)[self controlView] style] attributedStringValueForTabCell:self]; 146 | } 147 | 148 | - (NSInteger)tabState 149 | { 150 | return _tabState; 151 | } 152 | 153 | - (void)setTabState:(NSInteger)state 154 | { 155 | _tabState = state; 156 | } 157 | 158 | - (NSProgressIndicator *)indicator 159 | { 160 | return _indicator; 161 | } 162 | 163 | - (BOOL)isInOverflowMenu 164 | { 165 | return _isInOverflowMenu; 166 | } 167 | 168 | - (void)setIsInOverflowMenu:(BOOL)value 169 | { 170 | if (_isInOverflowMenu != value) { 171 | _isInOverflowMenu = value; 172 | if ([[(PSMTabBarControl *)[self controlView] delegate] respondsToSelector:@selector(tabView:tabViewItem:isInOverflowMenu:)]) { 173 | [[(PSMTabBarControl *)[self controlView] delegate] tabView:[(PSMTabBarControl *)[self controlView] tabView] tabViewItem:[self representedObject] isInOverflowMenu:_isInOverflowMenu]; 174 | } 175 | } 176 | } 177 | 178 | - (BOOL)closeButtonPressed 179 | { 180 | return _closeButtonPressed; 181 | } 182 | 183 | - (void)setCloseButtonPressed:(BOOL)value 184 | { 185 | _closeButtonPressed = value; 186 | } 187 | 188 | - (BOOL)closeButtonOver 189 | { 190 | return (_closeButtonOver && ([(PSMTabBarControl *)[self controlView] allowsBackgroundTabClosing] || ([self tabState] & PSMTab_SelectedMask) || [[NSApp currentEvent] modifierFlags] & NSCommandKeyMask)); 191 | } 192 | 193 | - (void)setCloseButtonOver:(BOOL)value 194 | { 195 | _closeButtonOver = value; 196 | } 197 | 198 | - (BOOL)hasCloseButton 199 | { 200 | return _hasCloseButton; 201 | } 202 | 203 | - (void)setHasCloseButton:(BOOL)set; 204 | { 205 | _hasCloseButton = set; 206 | } 207 | 208 | - (void)setCloseButtonSuppressed:(BOOL)suppress; 209 | { 210 | _isCloseButtonSuppressed = suppress; 211 | } 212 | 213 | - (BOOL)isCloseButtonSuppressed; 214 | { 215 | return _isCloseButtonSuppressed; 216 | } 217 | 218 | - (BOOL)hasIcon 219 | { 220 | return _hasIcon; 221 | } 222 | 223 | - (void)setHasIcon:(BOOL)value 224 | { 225 | _hasIcon = value; 226 | //[[self controlView] update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast 227 | } 228 | 229 | - (BOOL)hasLargeImage 230 | { 231 | return _hasLargeImage; 232 | } 233 | 234 | - (void)setHasLargeImage:(BOOL)value 235 | { 236 | _hasLargeImage = value; 237 | } 238 | 239 | 240 | - (NSInteger)count 241 | { 242 | return _count; 243 | } 244 | 245 | - (void)setCount:(NSInteger)value 246 | { 247 | _count = value; 248 | //[[self controlView] update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast 249 | } 250 | 251 | - (NSColor *)countColor 252 | { 253 | return _countColor; 254 | } 255 | 256 | - (void)setCountColor:(NSColor *)color 257 | { 258 | [_countColor release]; 259 | _countColor = [color retain]; 260 | } 261 | 262 | - (BOOL)isPlaceholder 263 | { 264 | return _isPlaceholder; 265 | } 266 | 267 | - (void)setIsPlaceholder:(BOOL)value; 268 | { 269 | _isPlaceholder = value; 270 | } 271 | 272 | - (NSInteger)currentStep 273 | { 274 | return _currentStep; 275 | } 276 | 277 | - (void)setCurrentStep:(NSInteger)value 278 | { 279 | if(value < 0) 280 | value = 0; 281 | 282 | if(value > (kPSMTabDragAnimationSteps - 1)) 283 | value = (kPSMTabDragAnimationSteps - 1); 284 | 285 | _currentStep = value; 286 | } 287 | 288 | - (BOOL)isEdited 289 | { 290 | return _isEdited; 291 | } 292 | 293 | - (void)setIsEdited:(BOOL)value 294 | { 295 | _isEdited = value; 296 | //[[self controlView] update:[[self controlView] automaticallyAnimates]]; // binding notice is too fast 297 | } 298 | 299 | #pragma mark - 300 | #pragma mark Bindings 301 | 302 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 303 | { 304 | // the progress indicator, label, icon, or count has changed - redraw the control view 305 | //[[self controlView] update]; 306 | //I seem to have run into some odd issue with update not being called at the right time. This seems to avoid the problem. 307 | [[self controlView] performSelector:@selector(update) withObject:nil afterDelay:0.0]; 308 | } 309 | 310 | #pragma mark - 311 | #pragma mark Component Attributes 312 | 313 | - (NSRect)indicatorRectForFrame:(NSRect)cellFrame 314 | { 315 | return [(id )[(PSMTabBarControl *)[self controlView] style] indicatorRectForTabCell:self]; 316 | } 317 | 318 | - (NSRect)closeButtonRectForFrame:(NSRect)cellFrame 319 | { 320 | return [(id )[(PSMTabBarControl *)[self controlView] style] closeButtonRectForTabCell:self withFrame:cellFrame]; 321 | } 322 | 323 | - (CGFloat)minimumWidthOfCell 324 | { 325 | return [(id )[(PSMTabBarControl *)[self controlView] style] minimumWidthOfTabCell:self]; 326 | } 327 | 328 | - (CGFloat)desiredWidthOfCell 329 | { 330 | return [(id )[(PSMTabBarControl *)[self controlView] style] desiredWidthOfTabCell:self]; 331 | } 332 | 333 | #pragma mark - 334 | #pragma mark Drawing 335 | 336 | - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView 337 | { 338 | if (_isPlaceholder) { 339 | [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set]; 340 | NSRectFillUsingOperation(cellFrame, NSCompositeSourceAtop); 341 | return; 342 | } 343 | 344 | [(id )[(PSMTabBarControl *)[self controlView] style] drawTabCell:self]; 345 | } 346 | 347 | #pragma mark - 348 | #pragma mark Tracking 349 | 350 | - (void)mouseEntered:(NSEvent *)theEvent 351 | { 352 | // check for which tag 353 | if ([theEvent trackingNumber] == _closeButtonTrackingTag) { 354 | _closeButtonOver = YES; 355 | } 356 | if ([theEvent trackingNumber] == _cellTrackingTag) { 357 | [self setHighlighted:YES]; 358 | [[self controlView] setNeedsDisplay:NO]; 359 | } 360 | 361 | // scrubtastic 362 | if ([(PSMTabBarControl *)[self controlView] allowsScrubbing] && ([theEvent modifierFlags] & NSAlternateKeyMask)) 363 | [[self controlView] performSelector:@selector(tabClick:) withObject:self]; 364 | 365 | // tell the control we only need to redraw the affected tab 366 | [[self controlView] setNeedsDisplayInRect:NSInsetRect([self frame], -2, -2)]; 367 | } 368 | 369 | - (void)mouseExited:(NSEvent *)theEvent 370 | { 371 | // check for which tag 372 | if ([theEvent trackingNumber] == _closeButtonTrackingTag) { 373 | _closeButtonOver = NO; 374 | } 375 | 376 | if ([theEvent trackingNumber] == _cellTrackingTag) { 377 | [self setHighlighted:NO]; 378 | [[self controlView] setNeedsDisplay:NO]; 379 | } 380 | 381 | //tell the control we only need to redraw the affected tab 382 | [[self controlView] setNeedsDisplayInRect:NSInsetRect([self frame], -2, -2)]; 383 | } 384 | 385 | #pragma mark - 386 | #pragma mark Drag Support 387 | 388 | - (NSImage *)dragImage 389 | { 390 | NSRect cellFrame = [(id )[(PSMTabBarControl *)[self controlView] style] dragRectForTabCell:self orientation:(PSMTabBarOrientation)[(PSMTabBarControl *)[self controlView] orientation]]; 391 | //NSRect cellFrame = [self frame]; 392 | 393 | [[self controlView] lockFocus]; 394 | NSBitmapImageRep *rep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:cellFrame] autorelease]; 395 | [[self controlView] unlockFocus]; 396 | NSImage *image = [[[NSImage alloc] initWithSize:[rep size]] autorelease]; 397 | [image addRepresentation:rep]; 398 | NSImage *returnImage = [[[NSImage alloc] initWithSize:[rep size]] autorelease]; 399 | [returnImage lockFocus]; 400 | [image compositeToPoint:NSMakePoint(0.0, 0.0) operation:NSCompositeSourceOver fraction:1.0]; 401 | [returnImage unlockFocus]; 402 | if (![[self indicator] isHidden]) { 403 | NSImage *pi = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"pi"]]; 404 | [returnImage lockFocus]; 405 | NSPoint indicatorPoint = NSMakePoint([self frame].size.width - MARGIN_X - kPSMTabBarIndicatorWidth, MARGIN_Y); 406 | [pi compositeToPoint:indicatorPoint operation:NSCompositeSourceOver fraction:1.0]; 407 | [returnImage unlockFocus]; 408 | [pi release]; 409 | } 410 | return returnImage; 411 | } 412 | 413 | #pragma mark - 414 | #pragma mark Archiving 415 | 416 | - (void)encodeWithCoder:(NSCoder *)aCoder { 417 | [super encodeWithCoder:aCoder]; 418 | if ([aCoder allowsKeyedCoding]) { 419 | [aCoder encodeRect:_frame forKey:@"frame"]; 420 | [aCoder encodeSize:_stringSize forKey:@"stringSize"]; 421 | [aCoder encodeInteger:_currentStep forKey:@"currentStep"]; 422 | [aCoder encodeBool:_isPlaceholder forKey:@"isPlaceholder"]; 423 | [aCoder encodeInteger:_tabState forKey:@"tabState"]; 424 | [aCoder encodeInteger:_closeButtonTrackingTag forKey:@"closeButtonTrackingTag"]; 425 | [aCoder encodeInteger:_cellTrackingTag forKey:@"cellTrackingTag"]; 426 | [aCoder encodeBool:_closeButtonOver forKey:@"closeButtonOver"]; 427 | [aCoder encodeBool:_closeButtonPressed forKey:@"closeButtonPressed"]; 428 | [aCoder encodeObject:_indicator forKey:@"indicator"]; 429 | [aCoder encodeBool:_isInOverflowMenu forKey:@"isInOverflowMenu"]; 430 | [aCoder encodeBool:_hasCloseButton forKey:@"hasCloseButton"]; 431 | [aCoder encodeBool:_isCloseButtonSuppressed forKey:@"isCloseButtonSuppressed"]; 432 | [aCoder encodeBool:_hasIcon forKey:@"hasIcon"]; 433 | [aCoder encodeBool:_hasLargeImage forKey:@"hasLargeImage"]; 434 | [aCoder encodeInteger:_count forKey:@"count"]; 435 | [aCoder encodeBool:_isEdited forKey:@"isEdited"]; 436 | } 437 | } 438 | 439 | - (id)initWithCoder:(NSCoder *)aDecoder { 440 | self = [super initWithCoder:aDecoder]; 441 | if (self) { 442 | if ([aDecoder allowsKeyedCoding]) { 443 | _frame = [aDecoder decodeRectForKey:@"frame"]; 444 | _stringSize = [aDecoder decodeSizeForKey:@"stringSize"]; 445 | _currentStep = [aDecoder decodeIntegerForKey:@"currentStep"]; 446 | _isPlaceholder = [aDecoder decodeBoolForKey:@"isPlaceholder"]; 447 | _tabState = [aDecoder decodeIntegerForKey:@"tabState"]; 448 | _closeButtonTrackingTag = [aDecoder decodeIntegerForKey:@"closeButtonTrackingTag"]; 449 | _cellTrackingTag = [aDecoder decodeIntegerForKey:@"cellTrackingTag"]; 450 | _closeButtonOver = [aDecoder decodeBoolForKey:@"closeButtonOver"]; 451 | _closeButtonPressed = [aDecoder decodeBoolForKey:@"closeButtonPressed"]; 452 | _indicator = [[aDecoder decodeObjectForKey:@"indicator"] retain]; 453 | _isInOverflowMenu = [aDecoder decodeBoolForKey:@"isInOverflowMenu"]; 454 | _hasCloseButton = [aDecoder decodeBoolForKey:@"hasCloseButton"]; 455 | _isCloseButtonSuppressed = [aDecoder decodeBoolForKey:@"isCloseButtonSuppressed"]; 456 | _hasIcon = [aDecoder decodeBoolForKey:@"hasIcon"]; 457 | _hasLargeImage = [aDecoder decodeBoolForKey:@"hasLargeImage"]; 458 | _count = [aDecoder decodeIntegerForKey:@"count"]; 459 | _isEdited = [aDecoder decodeBoolForKey:@"isEdited"]; 460 | } 461 | } 462 | return self; 463 | } 464 | 465 | #pragma mark - 466 | #pragma mark Accessibility 467 | 468 | -(BOOL)accessibilityIsIgnored { 469 | return NO; 470 | } 471 | 472 | - (id)accessibilityAttributeValue:(NSString *)attribute { 473 | id attributeValue = nil; 474 | 475 | if ([attribute isEqualToString: NSAccessibilityRoleAttribute]) { 476 | attributeValue = NSAccessibilityButtonRole; 477 | } else if ([attribute isEqualToString: NSAccessibilityHelpAttribute]) { 478 | if ([[(PSMTabBarControl *)[self controlView] delegate] respondsToSelector:@selector(accessibilityStringForTabView:objectCount:)]) { 479 | attributeValue = [NSString stringWithFormat:@"%@, %lu %@", [self stringValue], 480 | (unsigned long)[self count], 481 | [[(PSMTabBarControl *)[self controlView] delegate] accessibilityStringForTabView:[(PSMTabBarControl *)[self controlView] tabView] objectCount:[self count]]]; 482 | } else { 483 | attributeValue = [self stringValue]; 484 | } 485 | } else if ([attribute isEqualToString: NSAccessibilityFocusedAttribute]) { 486 | attributeValue = [NSNumber numberWithBool:([self tabState] == 2)]; 487 | } else { 488 | attributeValue = [super accessibilityAttributeValue:attribute]; 489 | } 490 | 491 | return attributeValue; 492 | } 493 | 494 | - (NSArray *)accessibilityActionNames 495 | { 496 | static NSArray *actions; 497 | 498 | if (!actions) { 499 | actions = [[NSArray alloc] initWithObjects:NSAccessibilityPressAction, nil]; 500 | } 501 | return actions; 502 | } 503 | 504 | - (NSString *)accessibilityActionDescription:(NSString *)action 505 | { 506 | return NSAccessibilityActionDescription(action); 507 | } 508 | 509 | - (void)accessibilityPerformAction:(NSString *)action { 510 | if ([action isEqualToString:NSAccessibilityPressAction]) { 511 | // this tab was selected 512 | [[self controlView] performSelector:@selector(tabClick:) withObject:self]; 513 | } 514 | } 515 | 516 | - (id)accessibilityHitTest:(NSPoint)point { 517 | return NSAccessibilityUnignoredAncestor(self); 518 | } 519 | 520 | - (id)accessibilityFocusedUIElement:(NSPoint)point { 521 | return NSAccessibilityUnignoredAncestor(self); 522 | } 523 | 524 | @end 525 | -------------------------------------------------------------------------------- /source/PSMTabBarControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabBarControl.h 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 10/13/05. 6 | // Copyright 2005 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | /* 10 | This view provides a control interface to manage a regular NSTabView. It looks and works like the tabbed browsing interface of many popular browsers. 11 | */ 12 | 13 | #import 14 | 15 | #define PSMTabDragDidEndNotification @"PSMTabDragDidEndNotification" 16 | #define PSMTabDragDidBeginNotification @"PSMTabDragDidBeginNotification" 17 | 18 | #define kPSMTabBarControlHeight 22 19 | // internal cell border 20 | #define MARGIN_X 6 21 | #define MARGIN_Y 3 22 | // padding between objects 23 | #define kPSMTabBarCellPadding 4 24 | // fixed size objects 25 | #define kPSMMinimumTitleWidth 30 26 | #define kPSMTabBarIndicatorWidth 16.0 27 | #define kPSMTabBarIconWidth 16.0 28 | #define kPSMHideAnimationSteps 3.0 29 | 30 | // Value used in _currentStep to indicate that resizing operation is not in progress 31 | #define kPSMIsNotBeingResized -1 32 | 33 | // Value used in _currentStep when a resizing operation has just been started 34 | #define kPSMStartResizeAnimation 0 35 | 36 | @class PSMOverflowPopUpButton, PSMRolloverButton, PSMTabBarCell, PSMTabBarController; 37 | @protocol PSMTabStyle; 38 | 39 | typedef enum { 40 | PSMTabBarHorizontalOrientation, 41 | PSMTabBarVerticalOrientation 42 | } PSMTabBarOrientation; 43 | 44 | typedef enum { 45 | PSMTabBarTearOffAlphaWindow, 46 | PSMTabBarTearOffMiniwindow 47 | } PSMTabBarTearOffStyle; 48 | 49 | enum { 50 | PSMTab_SelectedMask = 1 << 1, 51 | PSMTab_LeftIsSelectedMask = 1 << 2, 52 | PSMTab_RightIsSelectedMask = 1 << 3, 53 | PSMTab_PositionLeftMask = 1 << 4, 54 | PSMTab_PositionMiddleMask = 1 << 5, 55 | PSMTab_PositionRightMask = 1 << 6, 56 | PSMTab_PositionSingleMask = 1 << 7 57 | }; 58 | 59 | @interface PSMTabBarControl : NSControl { 60 | 61 | // control basics 62 | NSMutableArray *_cells; // the cells that draw the tabs 63 | IBOutlet NSTabView *tabView; // the tab view being navigated 64 | PSMOverflowPopUpButton *_overflowPopUpButton; // for too many tabs 65 | PSMRolloverButton *_addTabButton; 66 | PSMTabBarController *_controller; 67 | 68 | // Spring-loading. 69 | NSTimer *_springTimer; 70 | NSTabViewItem *_tabViewItemWithSpring; 71 | 72 | // drawing style 73 | id style; 74 | BOOL _canCloseOnlyTab; 75 | BOOL _disableTabClose; 76 | BOOL _hideForSingleTab; 77 | BOOL _showAddTabButton; 78 | BOOL _sizeCellsToFit; 79 | BOOL _useOverflowMenu; 80 | BOOL _alwaysShowActiveTab; 81 | BOOL _allowsScrubbing; 82 | NSInteger _resizeAreaCompensation; 83 | PSMTabBarOrientation _orientation; 84 | BOOL _automaticallyAnimates; 85 | NSTimer *_animationTimer; 86 | PSMTabBarTearOffStyle _tearOffStyle; 87 | 88 | // behavior 89 | BOOL _allowsBackgroundTabClosing; 90 | BOOL _selectsTabsOnMouseDown; 91 | 92 | // vertical tab resizing 93 | BOOL _allowsResizing; 94 | BOOL _resizing; 95 | 96 | // cell width 97 | NSInteger _cellMinWidth; 98 | NSInteger _cellMaxWidth; 99 | NSInteger _cellOptimumWidth; 100 | 101 | // animation for hide/show 102 | NSInteger _currentStep; 103 | BOOL _isHidden; 104 | IBOutlet id partnerView; // gets resized when hide/show 105 | BOOL _awakenedFromNib; 106 | NSInteger _tabBarWidth; 107 | NSTimer *_showHideAnimationTimer; 108 | 109 | // drag and drop 110 | NSEvent *_lastMouseDownEvent; // keep this for dragging reference 111 | BOOL _didDrag; 112 | BOOL _closeClicked; 113 | 114 | // MVC help 115 | IBOutlet id delegate; 116 | } 117 | 118 | // control characteristics 119 | + (NSBundle *)bundle; 120 | - (CGFloat)availableCellWidth; 121 | - (NSRect)genericCellRect; 122 | 123 | // control configuration 124 | - (PSMTabBarOrientation)orientation; 125 | - (void)setOrientation:(PSMTabBarOrientation)value; 126 | - (BOOL)canCloseOnlyTab; 127 | - (void)setCanCloseOnlyTab:(BOOL)value; 128 | - (BOOL)disableTabClose; 129 | - (void)setDisableTabClose:(BOOL)value; 130 | - (id)style; 131 | - (void)setStyle:(id )newStyle; 132 | - (NSString *)styleName; 133 | - (void)setStyleNamed:(NSString *)name; 134 | - (BOOL)hideForSingleTab; 135 | - (void)setHideForSingleTab:(BOOL)value; 136 | - (BOOL)showAddTabButton; 137 | - (void)setShowAddTabButton:(BOOL)value; 138 | - (NSInteger)cellMinWidth; 139 | - (void)setCellMinWidth:(NSInteger)value; 140 | - (NSInteger)cellMaxWidth; 141 | - (void)setCellMaxWidth:(NSInteger)value; 142 | - (NSInteger)cellOptimumWidth; 143 | - (void)setCellOptimumWidth:(NSInteger)value; 144 | - (BOOL)sizeCellsToFit; 145 | - (void)setSizeCellsToFit:(BOOL)value; 146 | - (BOOL)useOverflowMenu; 147 | - (void)setUseOverflowMenu:(BOOL)value; 148 | - (BOOL)allowsBackgroundTabClosing; 149 | - (void)setAllowsBackgroundTabClosing:(BOOL)value; 150 | - (BOOL)allowsResizing; 151 | - (void)setAllowsResizing:(BOOL)value; 152 | - (BOOL)selectsTabsOnMouseDown; 153 | - (void)setSelectsTabsOnMouseDown:(BOOL)value; 154 | - (BOOL)automaticallyAnimates; 155 | - (void)setAutomaticallyAnimates:(BOOL)value; 156 | - (BOOL)alwaysShowActiveTab; 157 | - (void)setAlwaysShowActiveTab:(BOOL)value; 158 | - (BOOL)allowsScrubbing; 159 | - (void)setAllowsScrubbing:(BOOL)value; 160 | - (PSMTabBarTearOffStyle)tearOffStyle; 161 | - (void)setTearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle; 162 | 163 | // accessors 164 | - (NSTabView *)tabView; 165 | - (void)setTabView:(NSTabView *)view; 166 | - (id)delegate; 167 | - (void)setDelegate:(id)object; 168 | - (id)partnerView; 169 | - (void)setPartnerView:(id)view; 170 | 171 | // the buttons 172 | - (PSMRolloverButton *)addTabButton; 173 | - (PSMOverflowPopUpButton *)overflowPopUpButton; 174 | 175 | // tab information 176 | - (NSMutableArray *)representedTabViewItems; 177 | - (NSInteger)numberOfVisibleTabs; 178 | - (PSMTabBarCell *)lastVisibleTab; 179 | 180 | // special effects 181 | - (void)hideTabBar:(BOOL)hide animate:(BOOL)animate; 182 | - (BOOL)isTabBarHidden; 183 | - (BOOL)isAnimating; 184 | 185 | // internal bindings methods also used by the tab drag assistant 186 | - (void)bindPropertiesForCell:(PSMTabBarCell *)cell andTabViewItem:(NSTabViewItem *)item; 187 | - (void)removeTabForCell:(PSMTabBarCell *)cell; 188 | 189 | @end 190 | 191 | 192 | @interface NSObject (TabBarControlDelegateMethods) 193 | 194 | //Standard NSTabView methods 195 | - (BOOL)tabView:(NSTabView *)aTabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem; 196 | - (void)tabView:(NSTabView *)aTabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem; 197 | 198 | //"Spring-loaded" tabs methods 199 | - (NSArray *)allowedDraggedTypesForTabView:(NSTabView *)aTabView; 200 | - (void)tabView:(NSTabView *)aTabView acceptedDraggingInfo:(id )draggingInfo onTabViewItem:(NSTabViewItem *)tabViewItem; 201 | 202 | //Contextual menu method 203 | - (NSMenu *)tabView:(NSTabView *)aTabView menuForTabViewItem:(NSTabViewItem *)tabViewItem; 204 | 205 | //Drag and drop methods 206 | - (BOOL)tabView:(NSTabView *)aTabView shouldDragTabViewItem:(NSTabViewItem *)tabViewItem fromTabBar:(PSMTabBarControl *)tabBarControl; 207 | - (BOOL)tabView:(NSTabView *)aTabView shouldDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl; 208 | - (BOOL)tabView:(NSTabView *)aTabView shouldAllowTabViewItem:(NSTabViewItem *)tabViewItem toLeaveTabBar:(PSMTabBarControl *)tabBarControl; 209 | - (void)tabView:(NSTabView*)aTabView didDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl; 210 | 211 | 212 | //Tear-off tabs methods 213 | - (NSImage *)tabView:(NSTabView *)aTabView imageForTabViewItem:(NSTabViewItem *)tabViewItem offset:(NSSize *)offset styleMask:(NSUInteger *)styleMask; 214 | - (PSMTabBarControl *)tabView:(NSTabView *)aTabView newTabBarForDraggedTabViewItem:(NSTabViewItem *)tabViewItem atPoint:(NSPoint)point; 215 | - (void)tabView:(NSTabView *)aTabView closeWindowForLastTabViewItem:(NSTabViewItem *)tabViewItem; 216 | 217 | //Overflow menu validation 218 | - (BOOL)tabView:(NSTabView *)aTabView validateOverflowMenuItem:(NSMenuItem *)menuItem forTabViewItem:(NSTabViewItem *)tabViewItem; 219 | - (void)tabView:(NSTabView *)aTabView tabViewItem:(NSTabViewItem *)tabViewItem isInOverflowMenu:(BOOL)inOverflowMenu; 220 | 221 | //tab bar hiding methods 222 | - (void)tabView:(NSTabView *)aTabView tabBarDidHide:(PSMTabBarControl *)tabBarControl; 223 | - (void)tabView:(NSTabView *)aTabView tabBarDidUnhide:(PSMTabBarControl *)tabBarControl; 224 | - (CGFloat)desiredWidthForVerticalTabBar:(PSMTabBarControl *)tabBarControl; 225 | 226 | //closing 227 | - (BOOL)tabView:(NSTabView *)aTabView disableTabCloseForTabViewItem:(NSTabViewItem *)tabViewItem; 228 | 229 | //tooltips 230 | - (NSString *)tabView:(NSTabView *)aTabView toolTipForTabViewItem:(NSTabViewItem *)tabViewItem; 231 | 232 | //accessibility 233 | - (NSString *)accessibilityStringForTabView:(NSTabView *)aTabView objectCount:(NSInteger)objectCount; 234 | 235 | @end 236 | -------------------------------------------------------------------------------- /source/PSMTabBarController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabBarController.h 3 | // PSMTabBarControl 4 | // 5 | // Created by Kent Sutherland on 11/24/06. 6 | // Copyright 2006 Kent Sutherland. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class PSMTabBarControl, PSMTabBarCell; 12 | 13 | @interface PSMTabBarController : NSObject { 14 | PSMTabBarControl *_control; 15 | NSMutableArray *_cellTrackingRects, *_closeButtonTrackingRects; 16 | NSMutableArray *_cellFrames; 17 | NSRect _addButtonRect; 18 | NSMenu *_overflowMenu; 19 | } 20 | 21 | - (id)initWithTabBarControl:(PSMTabBarControl *)control; 22 | 23 | - (NSRect)addButtonRect; 24 | - (NSMenu *)overflowMenu; 25 | - (NSRect)cellTrackingRectAtIndex:(NSInteger)index; 26 | - (NSRect)closeButtonTrackingRectAtIndex:(NSInteger)index; 27 | - (NSRect)cellFrameAtIndex:(NSInteger)index; 28 | 29 | - (void)setSelectedCell:(PSMTabBarCell *)cell; 30 | 31 | - (void)layoutCells; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /source/PSMTabBarController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabBarController.m 3 | // PSMTabBarControl 4 | // 5 | // Created by Kent Sutherland on 11/24/06. 6 | // Copyright 2006 Kent Sutherland. All rights reserved. 7 | // 8 | 9 | #import "PSMTabBarController.h" 10 | #import "PSMTabBarControl.h" 11 | #import "PSMTabBarCell.h" 12 | #import "PSMTabStyle.h" 13 | #import "NSString_AITruncation.h" 14 | 15 | #define MAX_OVERFLOW_MENUITEM_TITLE_LENGTH 60 16 | 17 | @interface PSMTabBarController (Private) 18 | - (NSArray *)_generateWidthsFromCells:(NSArray *)cells; 19 | - (void)_setupCells:(NSArray *)cells withWidths:(NSArray *)widths; 20 | @end 21 | 22 | @implementation PSMTabBarController 23 | 24 | /*! 25 | @method initWithTabBarControl: 26 | @abstract Creates a new PSMTabBarController instance. 27 | @discussion Creates a new PSMTabBarController for controlling a PSMTabBarControl. Should only be called by 28 | PSMTabBarControl. 29 | @param A PSMTabBarControl. 30 | @returns A newly created PSMTabBarController instance. 31 | */ 32 | 33 | - (id)initWithTabBarControl:(PSMTabBarControl *)control 34 | { 35 | if ( (self = [super init]) ) { 36 | _control = control; 37 | _cellTrackingRects = [[NSMutableArray alloc] init]; 38 | _closeButtonTrackingRects = [[NSMutableArray alloc] init]; 39 | _cellFrames = [[NSMutableArray alloc] init]; 40 | _addButtonRect = NSZeroRect; 41 | } 42 | return self; 43 | } 44 | 45 | - (void)dealloc 46 | { 47 | [_cellTrackingRects release]; 48 | [_closeButtonTrackingRects release]; 49 | [_cellFrames release]; 50 | [super dealloc]; 51 | } 52 | 53 | /*! 54 | @method addButtonRect 55 | @abstract Returns the position for the add tab button. 56 | @discussion Returns the position for the add tab button. 57 | @returns The rect for the add button rect. 58 | */ 59 | 60 | - (NSRect)addButtonRect 61 | { 62 | return _addButtonRect; 63 | } 64 | 65 | /*! 66 | @method overflowMenu 67 | @abstract Returns current overflow menu or nil if there is none. 68 | @discussion Returns current overflow menu or nil if there is none. 69 | @returns The current overflow menu. 70 | */ 71 | 72 | - (NSMenu *)overflowMenu 73 | { 74 | return _overflowMenu; 75 | } 76 | 77 | /*! 78 | @method cellTrackingRectAtIndex: 79 | @abstract Returns the rect for the tracking rect at the requested index. 80 | @discussion Returns the rect for the tracking rect at the requested index. 81 | @param Index of a cell. 82 | @returns The tracking rect of the cell at the requested index. 83 | */ 84 | 85 | - (NSRect)cellTrackingRectAtIndex:(NSInteger)index 86 | { 87 | NSRect rect; 88 | if (index > -1 && index < [_cellTrackingRects count]) { 89 | rect = [[_cellTrackingRects objectAtIndex:index] rectValue]; 90 | } else { 91 | NSLog(@"cellTrackingRectAtIndex: Invalid index (%ld)", (long)index); 92 | rect = NSZeroRect; 93 | } 94 | return rect; 95 | } 96 | 97 | /*! 98 | @method closeButtonTrackingRectAtIndex: 99 | @abstract Returns the tracking rect for the close button at the requested index. 100 | @discussion Returns the tracking rect for the close button at the requested index. 101 | @param Index of a cell. 102 | @returns The close button tracking rect of the cell at the requested index. 103 | */ 104 | 105 | - (NSRect)closeButtonTrackingRectAtIndex:(NSInteger)index 106 | { 107 | NSRect rect; 108 | if (index > -1 && index < [_closeButtonTrackingRects count]) { 109 | rect = [[_closeButtonTrackingRects objectAtIndex:index] rectValue]; 110 | } else { 111 | NSLog(@"closeButtonTrackingRectAtIndex: Invalid index (%ld)", (long)index); 112 | rect = NSZeroRect; 113 | } 114 | return rect; 115 | } 116 | 117 | /*! 118 | @method cellFrameAtIndex: 119 | @abstract Returns the frame for the cell at the requested index. 120 | @discussion Returns the frame for the cell at the requested index. 121 | @param Index of a cell. 122 | @returns The frame of the cell at the requested index. 123 | */ 124 | 125 | - (NSRect)cellFrameAtIndex:(NSInteger)index 126 | { 127 | NSRect rect; 128 | 129 | if (index > -1 && index < [_cellFrames count]) { 130 | rect = [[_cellFrames objectAtIndex:index] rectValue]; 131 | } else { 132 | NSLog(@"cellFrameAtIndex: Invalid index (%ld)", (long)index); 133 | rect = NSZeroRect; 134 | } 135 | return rect; 136 | } 137 | 138 | /*! 139 | @method setSelectedCell: 140 | @abstract Changes the cell states so the given cell is the currently selected cell. 141 | @discussion Makes the given cell the active cell and properly recalculates the tab states for surrounding cells. 142 | @param An instance of PSMTabBarCell to make active. 143 | */ 144 | 145 | - (void)setSelectedCell:(PSMTabBarCell *)cell 146 | { 147 | NSArray *cells = [_control cells]; 148 | NSEnumerator *enumerator = [cells objectEnumerator]; 149 | PSMTabBarCell *lastCell = nil, *nextCell; 150 | 151 | //deselect the previously selected tab 152 | while ( (nextCell = [enumerator nextObject]) && ([nextCell state] == NSOffState) ) { 153 | lastCell = nextCell; 154 | } 155 | 156 | [nextCell setState:NSOffState]; 157 | [nextCell setTabState:PSMTab_PositionMiddleMask]; 158 | 159 | if (lastCell && lastCell != [_control lastVisibleTab]) { 160 | [lastCell setTabState:~[lastCell tabState] & PSMTab_RightIsSelectedMask]; 161 | } 162 | 163 | if ( (nextCell = [enumerator nextObject]) ) { 164 | [nextCell setTabState:~[lastCell tabState] & PSMTab_LeftIsSelectedMask]; 165 | } 166 | 167 | [cell setState:NSOnState]; 168 | [cell setTabState:PSMTab_SelectedMask]; 169 | 170 | if (![cell isInOverflowMenu]) { 171 | NSInteger cellIndex = [cells indexOfObject:cell]; 172 | 173 | if (cellIndex > 0) { 174 | nextCell = [cells objectAtIndex:cellIndex - 1]; 175 | [nextCell setTabState:[nextCell tabState] | PSMTab_RightIsSelectedMask]; 176 | } 177 | 178 | if (cellIndex < [cells count] - 1) { 179 | nextCell = [cells objectAtIndex:cellIndex + 1]; 180 | [nextCell setTabState:[nextCell tabState] | PSMTab_LeftIsSelectedMask]; 181 | } 182 | } 183 | } 184 | 185 | /*! 186 | @method layoutCells 187 | @abstract Recalculates cell positions and states. 188 | @discussion This method calculates the proper frame, tabState and overflow menu status for all cells in the 189 | tab bar control. 190 | */ 191 | 192 | - (void)layoutCells 193 | { 194 | NSArray *cells = [_control cells]; 195 | NSInteger cellCount = [cells count]; 196 | 197 | // make sure all of our tabs are accounted for before updating 198 | if ([[_control tabView] numberOfTabViewItems] != cellCount) { 199 | return; 200 | } 201 | 202 | [_cellTrackingRects removeAllObjects]; 203 | [_closeButtonTrackingRects removeAllObjects]; 204 | [_cellFrames removeAllObjects]; 205 | 206 | NSArray *cellWidths = [self _generateWidthsFromCells:cells]; 207 | [self _setupCells:cells withWidths:cellWidths]; 208 | 209 | //set up the rect from the add tab button 210 | _addButtonRect = [_control genericCellRect]; 211 | _addButtonRect.size = [[_control addTabButton] frame].size; 212 | if ([_control orientation] == PSMTabBarHorizontalOrientation) { 213 | _addButtonRect.origin.y = MARGIN_Y; 214 | _addButtonRect.origin.x += [[cellWidths valueForKeyPath:@"@sum.floatValue"] doubleValue] + 2; 215 | } else { 216 | _addButtonRect.origin.x = 0; 217 | _addButtonRect.origin.y = [[cellWidths lastObject] doubleValue]; 218 | } 219 | } 220 | 221 | /*! 222 | * @method _shrinkWidths:towardMinimum:withAvailableWidth: 223 | * @abstract Decreases widths in an array toward a minimum until they fit within availableWidth, if possible 224 | * @param An array of NSNumbers 225 | * @param The target minimum 226 | * @param The maximum available width 227 | * @returns The amount by which the total array width was shrunk 228 | */ 229 | - (NSInteger)_shrinkWidths:(NSMutableArray *)newWidths towardMinimum:(NSInteger)minimum withAvailableWidth:(CGFloat)availableWidth 230 | { 231 | BOOL changed = NO; 232 | NSInteger count = [newWidths count]; 233 | NSInteger totalWidths = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue]; 234 | NSInteger originalTotalWidths = totalWidths; 235 | 236 | do { 237 | changed = NO; 238 | 239 | for (NSInteger q = (count - 1); q >= 0; q--) { 240 | CGFloat cellWidth = [[newWidths objectAtIndex:q] doubleValue]; 241 | if (cellWidth - 1 >= minimum) { 242 | cellWidth--; 243 | totalWidths--; 244 | 245 | [newWidths replaceObjectAtIndex:q 246 | withObject:[NSNumber numberWithDouble:cellWidth]]; 247 | 248 | changed = YES; 249 | } 250 | } 251 | 252 | } while (changed && (totalWidths > availableWidth)); 253 | 254 | return (originalTotalWidths - totalWidths); 255 | } 256 | 257 | /*! 258 | * @function potentialMinimumForArray() 259 | * @abstract Calculate the minimum total for a given array of widths 260 | * @discussion The array is summed using, for each item, the minimum between the current value and the passed minimum value. 261 | * This is useful for getting a sum if the array has size-to-fit widths which will be allowed to be less than the 262 | * specified minimum. 263 | * @param An array of widths 264 | * @param The minimum 265 | * @returns The smallest possible sum for the array 266 | */ 267 | static NSInteger potentialMinimumForArray(NSArray *array, NSInteger minimum) 268 | { 269 | NSInteger runningTotal = 0; 270 | NSInteger count = [array count]; 271 | 272 | for (NSInteger i = 0; i < count; i++) { 273 | NSInteger currentValue = [[array objectAtIndex:i] integerValue]; 274 | runningTotal += MIN(currentValue, minimum); 275 | } 276 | 277 | return runningTotal; 278 | } 279 | 280 | /*! 281 | @method _generateWidthsFromCells: 282 | @abstract Calculates the width of cells that would be visible. 283 | @discussion Calculates the width of cells in the tab bar and returns an array of widths for the cells that would be 284 | visible. Uses large blocks of code that were previously in PSMTabBarControl's update method. 285 | @param An array of PSMTabBarCells. 286 | @returns An array of numbers representing the widths of cells that would be visible. 287 | */ 288 | 289 | - (NSArray *)_generateWidthsFromCells:(NSArray *)cells 290 | { 291 | NSInteger cellCount = [cells count], i, numberOfVisibleCells = ([_control orientation] == PSMTabBarHorizontalOrientation) ? 1 : 0; 292 | NSMutableArray *newWidths = [NSMutableArray arrayWithCapacity:cellCount]; 293 | id style = [_control style]; 294 | CGFloat availableWidth = [_control availableCellWidth], currentOrigin = 0, totalOccupiedWidth = 0.0, width; 295 | NSRect cellRect = [_control genericCellRect], controlRect = [_control frame]; 296 | PSMTabBarCell *currentCell; 297 | 298 | if ([_control orientation] == PSMTabBarVerticalOrientation) { 299 | currentOrigin = [style topMarginForTabBarControl]; 300 | } 301 | 302 | //Don't let cells overlap the add tab button if it is visible 303 | if ([_control showAddTabButton]) { 304 | availableWidth -= [self addButtonRect].size.width; 305 | } 306 | 307 | for (i = 0; i < cellCount; i++) { 308 | currentCell = [cells objectAtIndex:i]; 309 | 310 | // supress close button? 311 | [currentCell setCloseButtonSuppressed:((cellCount == 1 && [_control canCloseOnlyTab] == NO) || 312 | [_control disableTabClose] || 313 | ([[_control delegate] respondsToSelector:@selector(tabView:disableTabCloseForTabViewItem:)] && 314 | [[_control delegate] tabView:[_control tabView] disableTabCloseForTabViewItem:[currentCell representedObject]]))]; 315 | 316 | if ([_control orientation] == PSMTabBarHorizontalOrientation) { 317 | // Determine cell width 318 | if ([_control sizeCellsToFit]) { 319 | width = [currentCell desiredWidthOfCell]; 320 | if (width > [_control cellMaxWidth]) { 321 | width = [_control cellMaxWidth]; 322 | } 323 | } else { 324 | width = [_control cellOptimumWidth]; 325 | } 326 | 327 | width = ceil(width); 328 | 329 | //check to see if there is not enough space to place all tabs as preferred 330 | if (totalOccupiedWidth + width >= availableWidth) { 331 | //There's not enough space to add currentCell at its preferred width! 332 | 333 | //If we're not going to use the overflow menu, cram all the tab cells into the bar regardless of minimum width 334 | if (![_control useOverflowMenu]) { 335 | NSInteger j, averageWidth = (availableWidth / cellCount); 336 | 337 | numberOfVisibleCells = cellCount; 338 | [newWidths removeAllObjects]; 339 | 340 | for (j = 0; j < cellCount; j++) { 341 | CGFloat desiredWidth = [[cells objectAtIndex:j] desiredWidthOfCell]; 342 | [newWidths addObject:[NSNumber numberWithDouble:(desiredWidth < averageWidth && [_control sizeCellsToFit]) ? desiredWidth : averageWidth]]; 343 | } 344 | 345 | totalOccupiedWidth = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue]; 346 | break; 347 | } 348 | 349 | //We'll be using the overflow menu if needed. 350 | numberOfVisibleCells = i; 351 | if ([_control sizeCellsToFit]) { 352 | BOOL remainingCellsMustGoToOverflow = NO; 353 | 354 | totalOccupiedWidth = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue]; 355 | 356 | /* Can I squeeze it in without violating min cell width? This is the width we would take up 357 | * if every cell so far were at the control minimum size (or their current size if that is less than the control minimum). 358 | */ 359 | if ((potentialMinimumForArray(newWidths, [_control cellMinWidth]) + MIN(width, [_control cellMinWidth])) <= availableWidth) { 360 | /* It's definitely possible for cells so far to be visible. 361 | * Shrink other cells to allow this one to fit 362 | */ 363 | NSInteger cellMinWidth = [_control cellMinWidth]; 364 | 365 | /* Start off adding it to the array; we know that it will eventually fit because 366 | * (the potential minimum <= availableWidth) 367 | * 368 | * This allows average and minimum aggregates on the NSArray to work. 369 | */ 370 | [newWidths addObject:[NSNumber numberWithDouble:width]]; 371 | numberOfVisibleCells++; 372 | 373 | totalOccupiedWidth += width; 374 | 375 | //First, try to shrink tabs toward the average. Tabs smaller than average won't change 376 | totalOccupiedWidth -= [self _shrinkWidths:newWidths 377 | towardMinimum:[[newWidths valueForKeyPath:@"@avg.intValue"] integerValue] 378 | withAvailableWidth:availableWidth]; 379 | 380 | 381 | 382 | if (totalOccupiedWidth > availableWidth) { 383 | //Next, shrink tabs toward the smallest of the existing tabs. The smallest tab won't change. 384 | NSInteger smallestTabWidth = [[newWidths valueForKeyPath:@"@min.intValue"] integerValue]; 385 | if (smallestTabWidth > cellMinWidth) { 386 | totalOccupiedWidth -= [self _shrinkWidths:newWidths 387 | towardMinimum:smallestTabWidth 388 | withAvailableWidth:availableWidth]; 389 | } 390 | } 391 | 392 | if (totalOccupiedWidth > availableWidth) { 393 | //Finally, shrink tabs toward the imposed minimum size. All tabs larger than the minimum wll change. 394 | totalOccupiedWidth -= [self _shrinkWidths:newWidths 395 | towardMinimum:cellMinWidth 396 | withAvailableWidth:availableWidth]; 397 | } 398 | 399 | if (totalOccupiedWidth > availableWidth) { 400 | NSLog(@"**** -[PSMTabBarController generateWidthsFromCells:] This is a failure (available %f, total %f, width is %f)", 401 | availableWidth, totalOccupiedWidth, width); 402 | remainingCellsMustGoToOverflow = YES; 403 | } 404 | 405 | if (totalOccupiedWidth < availableWidth) { 406 | /* We're not using all available space not but exceeded available width before; 407 | * stretch all cells to fully fit the bar 408 | */ 409 | NSInteger leftoverWidth = availableWidth - totalOccupiedWidth; 410 | if (leftoverWidth > 0) { 411 | NSInteger q; 412 | for (q = numberOfVisibleCells - 1; q >= 0; q--) { 413 | NSInteger desiredAddition = (NSInteger)leftoverWidth / (q + 1); 414 | NSInteger newCellWidth = (NSInteger)[[newWidths objectAtIndex:q] doubleValue] + desiredAddition; 415 | [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:newCellWidth]]; 416 | leftoverWidth -= desiredAddition; 417 | totalOccupiedWidth += desiredAddition; 418 | } 419 | } 420 | } 421 | 422 | } else { 423 | // stretch - distribute leftover room among cells, since we can't add this cell 424 | NSInteger leftoverWidth = availableWidth - totalOccupiedWidth; 425 | NSInteger q; 426 | for (q = i - 1; q >= 0; q--) { 427 | NSInteger desiredAddition = (NSInteger)leftoverWidth / (q + 1); 428 | NSInteger newCellWidth = (NSInteger)[[newWidths objectAtIndex:q] doubleValue] + desiredAddition; 429 | [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:newCellWidth]]; 430 | leftoverWidth -= desiredAddition; 431 | } 432 | 433 | remainingCellsMustGoToOverflow = YES; 434 | } 435 | 436 | // done assigning widths; remaining cells go in overflow menu 437 | if (remainingCellsMustGoToOverflow) { 438 | break; 439 | } 440 | 441 | } else { 442 | //We're not using size-to-fit 443 | NSInteger revisedWidth = availableWidth / (i + 1); 444 | if (revisedWidth >= [_control cellMinWidth]) { 445 | NSUInteger q; 446 | totalOccupiedWidth = 0; 447 | 448 | for (q = 0; q < [newWidths count]; q++) { 449 | [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:revisedWidth]]; 450 | totalOccupiedWidth += revisedWidth; 451 | } 452 | // just squeezed this one in... 453 | [newWidths addObject:[NSNumber numberWithDouble:revisedWidth]]; 454 | totalOccupiedWidth += revisedWidth; 455 | numberOfVisibleCells++; 456 | } else { 457 | // couldn't fit that last one... 458 | break; 459 | } 460 | } 461 | } else { 462 | //(totalOccupiedWidth < availableWidth) 463 | numberOfVisibleCells = cellCount; 464 | [newWidths addObject:[NSNumber numberWithDouble:width]]; 465 | totalOccupiedWidth += width; 466 | } 467 | 468 | } else { 469 | //lay out vertical tabs 470 | if (currentOrigin + cellRect.size.height <= controlRect.size.height) { 471 | [newWidths addObject:[NSNumber numberWithDouble:currentOrigin]]; 472 | numberOfVisibleCells++; 473 | currentOrigin += cellRect.size.height; 474 | } else { 475 | //out of room, the remaining tabs go into overflow 476 | if ([newWidths count] > 0 && controlRect.size.height - currentOrigin < 17) { 477 | [newWidths removeLastObject]; 478 | numberOfVisibleCells--; 479 | } 480 | break; 481 | } 482 | } 483 | } 484 | 485 | //make sure there are at least two items in the horizontal tab bar 486 | if ([_control orientation] == PSMTabBarHorizontalOrientation) { 487 | if (numberOfVisibleCells < 2 && [cells count] > 1) { 488 | PSMTabBarCell *cell1 = [cells objectAtIndex:0], *cell2 = [cells objectAtIndex:1]; 489 | NSNumber *cellWidth; 490 | 491 | [newWidths removeAllObjects]; 492 | totalOccupiedWidth = 0; 493 | 494 | cellWidth = [NSNumber numberWithDouble:[cell1 desiredWidthOfCell] < availableWidth * 0.5f ? [cell1 desiredWidthOfCell] : availableWidth * 0.5f]; 495 | [newWidths addObject:cellWidth]; 496 | totalOccupiedWidth += [cellWidth doubleValue]; 497 | 498 | cellWidth = [NSNumber numberWithDouble:[cell2 desiredWidthOfCell] < (availableWidth - totalOccupiedWidth) ? [cell2 desiredWidthOfCell] : (availableWidth - totalOccupiedWidth)]; 499 | [newWidths addObject:cellWidth]; 500 | totalOccupiedWidth += [cellWidth doubleValue]; 501 | 502 | if (totalOccupiedWidth < availableWidth) { 503 | [newWidths replaceObjectAtIndex:0 withObject:[NSNumber numberWithDouble:availableWidth - [cellWidth doubleValue]]]; 504 | } 505 | 506 | numberOfVisibleCells = 2; 507 | } 508 | } 509 | 510 | return newWidths; 511 | } 512 | 513 | /*! 514 | @method _setupCells:withWidths 515 | @abstract Creates tracking rect arrays and sets the frames of the visible cells. 516 | @discussion Creates tracking rect arrays and sets the cells given in the widths array. 517 | */ 518 | 519 | - (void)_setupCells:(NSArray *)cells withWidths:(NSArray *)widths 520 | { 521 | NSInteger i, tabState, cellCount = [cells count]; 522 | NSRect cellRect = [_control genericCellRect]; 523 | PSMTabBarCell *cell; 524 | NSTabViewItem *selectedTabViewItem = [[_control tabView] selectedTabViewItem]; 525 | NSMenuItem *menuItem; 526 | 527 | [_overflowMenu release], _overflowMenu = nil; 528 | 529 | for (i = 0; i < cellCount; i++) { 530 | cell = [cells objectAtIndex:i]; 531 | 532 | if (i < [widths count]) { 533 | tabState = 0; 534 | 535 | // set cell frame 536 | if ([_control orientation] == PSMTabBarHorizontalOrientation) { 537 | cellRect.size.width = [[widths objectAtIndex:i] doubleValue]; 538 | } else { 539 | cellRect.size.width = [_control frame].size.width; 540 | cellRect.origin.y = [[widths objectAtIndex:i] doubleValue]; 541 | cellRect.origin.x = 0; 542 | } 543 | 544 | [_cellFrames addObject:[NSValue valueWithRect:cellRect]]; 545 | 546 | //add tracking rects to arrays 547 | [_closeButtonTrackingRects addObject:[NSValue valueWithRect:[cell closeButtonRectForFrame:cellRect]]]; 548 | [_cellTrackingRects addObject:[NSValue valueWithRect:cellRect]]; 549 | 550 | if ([[cell representedObject] isEqualTo:selectedTabViewItem]) { 551 | [cell setState:NSOnState]; 552 | tabState |= PSMTab_SelectedMask; 553 | // previous cell 554 | if (i > 0) { 555 | [[cells objectAtIndex:i - 1] setTabState:([(PSMTabBarCell *)[cells objectAtIndex:i - 1] tabState] | PSMTab_RightIsSelectedMask)]; 556 | } 557 | // next cell - see below 558 | } else { 559 | [cell setState:NSOffState]; 560 | // see if prev cell was selected 561 | if ( (i > 0) && ([[cells objectAtIndex:i - 1] state] == NSOnState) ) { 562 | tabState |= PSMTab_LeftIsSelectedMask; 563 | } 564 | } 565 | 566 | // more tab states 567 | if ([widths count] == 1) { 568 | tabState |= PSMTab_PositionLeftMask | PSMTab_PositionRightMask | PSMTab_PositionSingleMask; 569 | } else if (i == 0) { 570 | tabState |= PSMTab_PositionLeftMask; 571 | } else if (i == [widths count] - 1) { 572 | tabState |= PSMTab_PositionRightMask; 573 | } 574 | 575 | [cell setTabState:tabState]; 576 | [cell setIsInOverflowMenu:NO]; 577 | 578 | // indicator 579 | if (![[cell indicator] isHidden] && ![_control isTabBarHidden]) { 580 | if (![[_control subviews] containsObject:[cell indicator]]) { 581 | [_control addSubview:[cell indicator]]; 582 | [[cell indicator] startAnimation:self]; 583 | } 584 | } 585 | 586 | // next... 587 | cellRect.origin.x += [[widths objectAtIndex:i] doubleValue]; 588 | } else { 589 | [cell setState:NSOffState]; 590 | [cell setIsInOverflowMenu:YES]; 591 | [[cell indicator] removeFromSuperview]; 592 | 593 | //position the cell well offscreen 594 | if ([_control orientation] == PSMTabBarHorizontalOrientation) { 595 | cellRect.origin.x += [[_control style] rightMarginForTabBarControl] + 20; 596 | } else { 597 | cellRect.origin.y = [_control frame].size.height + 2; 598 | } 599 | 600 | [_cellFrames addObject:[NSValue valueWithRect:cellRect]]; 601 | 602 | if (_overflowMenu == nil) { 603 | _overflowMenu = [[NSMenu alloc] init]; 604 | [_overflowMenu insertItemWithTitle:@"" action:nil keyEquivalent:@"" atIndex:0]; // Because the overflowPupUpButton is a pull down menu 605 | [_overflowMenu setDelegate:self]; 606 | } 607 | 608 | // Each item's title is limited to 60 characters. If more than 60 characters, use an ellipsis to indicate that more exists. 609 | menuItem = [_overflowMenu addItemWithTitle:[[[cell attributedStringValue] string] stringWithEllipsisByTruncatingToLength:MAX_OVERFLOW_MENUITEM_TITLE_LENGTH] 610 | action:@selector(overflowMenuAction:) 611 | keyEquivalent:@""]; 612 | [menuItem setTarget:_control]; 613 | [menuItem setRepresentedObject:[cell representedObject]]; 614 | 615 | if ([cell count] > 0) { 616 | [menuItem setTitle:[[menuItem title] stringByAppendingFormat:@" (%lu)", (unsigned long)[cell count]]]; 617 | } 618 | } 619 | } 620 | } 621 | 622 | - (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)menuItem atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel 623 | { 624 | if (menu == _overflowMenu) { 625 | if ([[[menuItem representedObject] identifier] respondsToSelector:@selector(icon)]) { 626 | [menuItem setImage:[[[menuItem representedObject] identifier] valueForKey:@"icon"]]; 627 | } 628 | } 629 | 630 | return TRUE; 631 | } 632 | 633 | - (NSInteger)numberOfItemsInMenu:(NSMenu *)menu 634 | { 635 | if (menu == _overflowMenu) { 636 | return [_overflowMenu numberOfItems]; 637 | 638 | } else { 639 | NSLog(@"Warning: Unexpected menu delegate call for menu %@",menu); 640 | return 0; 641 | } 642 | } 643 | 644 | @end 645 | 646 | /* 647 | PSMTabBarController will store what the current tab frame state should be like based off the last layout. PSMTabBarControl 648 | has to handle fetching the new frame and then changing the tab cell frame. 649 | Tab states will probably be changed immediately. 650 | 651 | Tabs that aren't going to be visible need to have their frame set offscreen. Treat them as if they were visible. 652 | 653 | The overflow menu is rebuilt and stored by the controller. 654 | 655 | Arrays of tracking rects will be created here, but not applied. 656 | Tracking rects are removed and added by PSMTabBarControl at the end of an animate/display cycle. 657 | 658 | The add tab button frame is handled by this controller. Visibility and location are set by the control. 659 | 660 | isInOverflowMenu should probably be removed in favor of a call that returns yes/no to if a cell is in overflow. (Not yet implemented) 661 | 662 | Still need to rewrite most of the code in PSMTabDragAssistant. 663 | */ 664 | -------------------------------------------------------------------------------- /source/PSMTabDragAssistant.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabDragAssistant.h 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 4/10/06. 6 | // Copyright 2006 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | /* 10 | This class is a sigleton that manages the details of a tab drag and drop. The details were beginning to overwhelm me when keeping all of this in the control and cells :-) 11 | */ 12 | 13 | #import 14 | #import "PSMTabBarControl.h" 15 | 16 | #define kPSMTabDragAnimationSteps 8 17 | 18 | @class PSMTabBarCell, PSMTabDragWindowController; 19 | 20 | @interface PSMTabDragAssistant : NSObject { 21 | PSMTabBarControl *_sourceTabBar; 22 | PSMTabBarControl *_destinationTabBar; 23 | NSMutableSet *_participatingTabBars; 24 | PSMTabBarCell *_draggedCell; 25 | NSInteger _draggedCellIndex; // for snap back 26 | BOOL _isDragging; 27 | 28 | // Support for dragging into new windows 29 | PSMTabDragWindowController *_draggedTab, *_draggedView; 30 | NSSize _dragWindowOffset; 31 | NSTimer *_fadeTimer; 32 | BOOL _centersDragWindows; 33 | PSMTabBarTearOffStyle _currentTearOffStyle; 34 | 35 | // Animation 36 | NSTimer *_animationTimer; 37 | NSMutableArray *_sineCurveWidths; 38 | NSPoint _currentMouseLoc; 39 | PSMTabBarCell *_targetCell; 40 | } 41 | 42 | // Creation/destruction 43 | + (PSMTabDragAssistant *)sharedDragAssistant; 44 | 45 | // Accessors 46 | - (PSMTabBarControl *)sourceTabBar; 47 | - (void)setSourceTabBar:(PSMTabBarControl *)tabBar; 48 | - (PSMTabBarControl *)destinationTabBar; 49 | - (void)setDestinationTabBar:(PSMTabBarControl *)tabBar; 50 | - (PSMTabBarCell *)draggedCell; 51 | - (void)setDraggedCell:(PSMTabBarCell *)cell; 52 | - (NSInteger)draggedCellIndex; 53 | - (void)setDraggedCellIndex:(NSInteger)value; 54 | - (BOOL)isDragging; 55 | - (void)setIsDragging:(BOOL)value; 56 | - (NSPoint)currentMouseLoc; 57 | - (void)setCurrentMouseLoc:(NSPoint)point; 58 | - (PSMTabBarCell *)targetCell; 59 | - (void)setTargetCell:(PSMTabBarCell *)cell; 60 | 61 | // Functionality 62 | - (void)startDraggingCell:(PSMTabBarCell *)cell fromTabBar:(PSMTabBarControl *)control withMouseDownEvent:(NSEvent *)event; 63 | - (void)draggingEnteredTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc; 64 | - (void)draggingUpdatedInTabBar:(PSMTabBarControl *)control atPoint:(NSPoint)mouseLoc; 65 | - (void)draggingExitedTabBar:(PSMTabBarControl *)control; 66 | - (void)performDragOperation; 67 | - (void)draggedImageEndedAt:(NSPoint)aPoint operation:(NSDragOperation)operation; 68 | - (void)finishDrag; 69 | 70 | - (void)draggingBeganAt:(NSPoint)aPoint; 71 | - (void)draggingMovedTo:(NSPoint)aPoint; 72 | 73 | // Animation 74 | - (void)animateDrag:(NSTimer *)timer; 75 | - (void)calculateDragAnimationForTabBar:(PSMTabBarControl *)control; 76 | 77 | // Placeholder 78 | - (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control withDraggedCell:(PSMTabBarCell *)cell; 79 | - (void)distributePlaceholdersInTabBar:(PSMTabBarControl *)control; 80 | - (void)removeAllPlaceholdersFromTabBar:(PSMTabBarControl *)control; 81 | 82 | @end 83 | 84 | @interface PSMTabBarControl (DragAccessors) 85 | 86 | - (id)style; 87 | - (NSMutableArray *)cells; 88 | - (void)setControlView:(id)view; 89 | - (id)cellForPoint:(NSPoint)point cellFrame:(NSRectPointer)outFrame; 90 | - (PSMTabBarCell *)lastVisibleTab; 91 | - (NSInteger)numberOfVisibleTabs; 92 | 93 | @end 94 | 95 | void CGContextCopyWindowCaptureContentsToRect(void *grafport, CGRect rect, NSInteger cid, NSInteger wid, NSInteger zero); 96 | OSStatus CGSSetWindowTransform(NSInteger cid, NSInteger wid, CGAffineTransform transform); 97 | 98 | @interface NSApplication (CoreGraphicsUndocumented) 99 | - (NSInteger)contextID; 100 | @end 101 | -------------------------------------------------------------------------------- /source/PSMTabDragView.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabDragView.h 3 | // PSMTabBarControl 4 | // 5 | // Created by Kent Sutherland on 6/17/07. 6 | // Copyright 2007 Kent Sutherland. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PSMTabDragView : NSView { 12 | NSImage *_image, *_alternateImage; 13 | CGFloat _alpha; 14 | } 15 | - (void)setFadeValue:(CGFloat)value; 16 | - (NSImage *)image; 17 | - (void)setImage:(NSImage *)image; 18 | - (NSImage *)alternateImage; 19 | - (void)setAlternateImage:(NSImage *)image; 20 | @end 21 | -------------------------------------------------------------------------------- /source/PSMTabDragView.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabDragView.m 3 | // PSMTabBarControl 4 | // 5 | // Created by Kent Sutherland on 6/17/07. 6 | // Copyright 2007 Kent Sutherland. All rights reserved. 7 | // 8 | 9 | #import "PSMTabDragView.h" 10 | 11 | 12 | @implementation PSMTabDragView 13 | 14 | - (id)initWithFrame:(NSRect)frame { 15 | if ( (self = [super initWithFrame:frame]) ) { 16 | _alpha = 1.0; 17 | } 18 | return self; 19 | } 20 | 21 | - (void)dealloc 22 | { 23 | [_image release]; 24 | [_alternateImage release]; 25 | [super dealloc]; 26 | } 27 | 28 | - (void)drawRect:(NSRect)rect { 29 | //1.0 fade means show the primary image 30 | //0.0 fade means show the secondary image 31 | CGFloat primaryAlpha = _alpha + 0.001f, alternateAlpha = 1.001f - _alpha; 32 | NSRect srcRect; 33 | srcRect.origin = NSZeroPoint; 34 | srcRect.size = [_image size]; 35 | 36 | [_image drawInRect:[self bounds] fromRect:srcRect operation:NSCompositeSourceOver fraction:primaryAlpha]; 37 | srcRect.size = [_alternateImage size]; 38 | [_alternateImage drawInRect:[self bounds] fromRect:srcRect operation:NSCompositeSourceOver fraction:alternateAlpha]; 39 | } 40 | 41 | - (void)setFadeValue:(CGFloat)value 42 | { 43 | _alpha = value; 44 | } 45 | 46 | - (NSImage *)image 47 | { 48 | return _image; 49 | } 50 | 51 | - (void)setImage:(NSImage *)image 52 | { 53 | [_image release]; 54 | _image = [image retain]; 55 | } 56 | 57 | - (NSImage *)alternateImage 58 | { 59 | return _alternateImage; 60 | } 61 | 62 | - (void)setAlternateImage:(NSImage *)image 63 | { 64 | [_alternateImage release]; 65 | _alternateImage = [image retain]; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /source/PSMTabDragWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabDragWindow.h 3 | // PSMTabBarControl 4 | // 5 | // Created by Kent Sutherland on 6/1/06. 6 | // Copyright 2006 Kent Sutherland. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class PSMTabDragView; 12 | 13 | @interface PSMTabDragWindow : NSWindow { 14 | PSMTabDragView *_dragView; 15 | } 16 | + (PSMTabDragWindow *)dragWindowWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask; 17 | 18 | - (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask; 19 | - (PSMTabDragView *)dragView; 20 | @end 21 | -------------------------------------------------------------------------------- /source/PSMTabDragWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabDragWindow.m 3 | // PSMTabBarControl 4 | // 5 | // Created by Kent Sutherland on 6/1/06. 6 | // Copyright 2006 Kent Sutherland. All rights reserved. 7 | // 8 | 9 | #import "PSMTabDragWindow.h" 10 | #import "PSMTabDragView.h" 11 | 12 | @implementation PSMTabDragWindow 13 | 14 | + (PSMTabDragWindow *)dragWindowWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask 15 | { 16 | return [[[PSMTabDragWindow alloc] initWithImage:image styleMask:styleMask] autorelease]; 17 | } 18 | 19 | - (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask 20 | { 21 | NSSize size = [image size]; 22 | 23 | if ( (self = [super initWithContentRect:NSMakeRect(0, 0, size.width, size.height) styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]) ) { 24 | _dragView = [[[PSMTabDragView alloc] initWithFrame:NSMakeRect(0, 0, size.width, size.height)] autorelease]; 25 | [self setContentView:_dragView]; 26 | [self setLevel:NSStatusWindowLevel]; 27 | [self setIgnoresMouseEvents:YES]; 28 | [self setOpaque:NO]; 29 | 30 | [_dragView setImage:image]; 31 | 32 | //Set the size of the window to be the exact size of the drag image 33 | NSRect windowFrame = [self frame]; 34 | windowFrame.origin.y += windowFrame.size.height - size.height; 35 | windowFrame.size = size; 36 | 37 | if (styleMask | NSBorderlessWindowMask) { 38 | windowFrame.size.height += 22; 39 | } 40 | 41 | [self setFrame:windowFrame display:YES]; 42 | } 43 | return self; 44 | } 45 | 46 | - (PSMTabDragView *)dragView 47 | { 48 | return _dragView; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /source/PSMTabDragWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabDragWindowController.h 3 | // PSMTabBarControl 4 | // 5 | // Created by Kent Sutherland on 6/18/07. 6 | // Copyright 2007 Kent Sutherland. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PSMTabBarControl.h" 11 | 12 | #define kPSMTabDragWindowAlpha 0.75 13 | #define kPSMTabDragAlphaInterval 0.15 14 | 15 | @class PSMTabDragView; 16 | 17 | @interface PSMTabDragWindowController : NSWindowController { 18 | PSMTabBarTearOffStyle _tearOffStyle; 19 | PSMTabDragView *_view; 20 | NSAnimation *_animation; 21 | NSTimer *_timer; 22 | 23 | BOOL _showingAlternate; 24 | NSRect _originalWindowFrame; 25 | } 26 | - (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask tearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle; 27 | 28 | - (NSImage *)image; 29 | - (NSImage *)alternateImage; 30 | - (void)setAlternateImage:(NSImage *)image; 31 | - (BOOL)isAnimating; 32 | - (void)switchImages; 33 | @end 34 | -------------------------------------------------------------------------------- /source/PSMTabDragWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabDragWindowController.m 3 | // PSMTabBarControl 4 | // 5 | // Created by Kent Sutherland on 6/18/07. 6 | // Copyright 2007 Kent Sutherland. All rights reserved. 7 | // 8 | 9 | #import "PSMTabDragWindowController.h" 10 | #import "PSMTabDragWindow.h" 11 | #import "PSMTabDragView.h" 12 | 13 | @implementation PSMTabDragWindowController 14 | 15 | - (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask tearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle 16 | { 17 | PSMTabDragWindow *window = [PSMTabDragWindow dragWindowWithImage:image styleMask:styleMask]; 18 | if ( (self = [super initWithWindow:window]) ) { 19 | _view = [[window dragView] retain]; 20 | _tearOffStyle = tearOffStyle; 21 | 22 | if (tearOffStyle == PSMTabBarTearOffMiniwindow) { 23 | [window setBackgroundColor:[NSColor clearColor]]; 24 | [window setHasShadow:YES]; 25 | } 26 | 27 | [window setAlphaValue:kPSMTabDragWindowAlpha]; 28 | } 29 | return self; 30 | } 31 | 32 | - (void)dealloc 33 | { 34 | if (_timer) { 35 | [_timer invalidate]; 36 | } 37 | 38 | if (_animation) { 39 | [_animation release]; 40 | } 41 | 42 | [_view release]; 43 | [super dealloc]; 44 | } 45 | 46 | - (NSImage *)image 47 | { 48 | return [_view image]; 49 | } 50 | 51 | - (NSImage *)alternateImage 52 | { 53 | return [_view alternateImage]; 54 | } 55 | 56 | - (void)setAlternateImage:(NSImage *)image 57 | { 58 | [_view setAlternateImage:image]; 59 | } 60 | 61 | - (BOOL)isAnimating 62 | { 63 | return _animation != nil; 64 | } 65 | 66 | - (void)switchImages 67 | { 68 | if (_tearOffStyle != PSMTabBarTearOffMiniwindow || ![_view alternateImage]) { 69 | return; 70 | } 71 | 72 | CGFloat progress = 0; 73 | _showingAlternate = !_showingAlternate; 74 | 75 | if (_animation) { 76 | //An animation already exists, get the current progress 77 | progress = 1.0f - [_animation currentProgress]; 78 | [_animation stopAnimation]; 79 | [_animation release]; 80 | } 81 | 82 | //begin animating 83 | _animation = [[NSAnimation alloc] initWithDuration:0.25 animationCurve:NSAnimationEaseInOut]; 84 | [_animation setAnimationBlockingMode:NSAnimationNonblocking]; 85 | [_animation setCurrentProgress:progress]; 86 | [_animation startAnimation]; 87 | 88 | _originalWindowFrame = [[self window] frame]; 89 | 90 | if (_timer) { 91 | [_timer invalidate]; 92 | } 93 | _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f / 30.0f target:self selector:@selector(animateTimer:) userInfo:nil repeats:YES]; 94 | } 95 | 96 | - (void)animateTimer:(NSTimer *)timer 97 | { 98 | NSRect frame = _originalWindowFrame; 99 | NSImage *currentImage = _showingAlternate ? [_view alternateImage] : [_view image]; 100 | NSSize size = [currentImage size]; 101 | NSPoint mousePoint = [NSEvent mouseLocation]; 102 | CGFloat animationValue = [_animation currentValue]; 103 | 104 | frame.size.width = _originalWindowFrame.size.width + (size.width - _originalWindowFrame.size.width) * animationValue; 105 | frame.size.height = _originalWindowFrame.size.height + (size.height - _originalWindowFrame.size.height) * animationValue; 106 | frame.origin.x = mousePoint.x - (frame.size.width / 2); 107 | frame.origin.y = mousePoint.y - (frame.size.height / 2); 108 | 109 | [_view setFadeValue:_showingAlternate ? 1.0f - animationValue : animationValue]; 110 | [[self window] setFrame:frame display:YES]; 111 | 112 | if (![_animation isAnimating]) { 113 | [_animation release], _animation = nil; 114 | [timer invalidate]; 115 | _timer = nil; 116 | } 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /source/PSMTabStyle.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabStyle.h 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 2/17/06. 6 | // Copyright 2006 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | /* 10 | Protocol to be observed by all style delegate objects. These objects handle the drawing responsibilities for PSMTabBarCell; once the control has been assigned a style, the background and cells draw consistent with that style. Design pattern and implementation by David Smith, Seth Willits, and Chris Forsythe, all touch up and errors by John P. :-) 11 | */ 12 | 13 | #import "PSMTabBarCell.h" 14 | #import "PSMTabBarControl.h" 15 | 16 | @protocol PSMTabStyle 17 | 18 | // identity 19 | - (NSString *)name; 20 | 21 | // control specific parameters 22 | - (CGFloat)leftMarginForTabBarControl; 23 | - (CGFloat)rightMarginForTabBarControl; 24 | - (CGFloat)topMarginForTabBarControl; 25 | - (void)setOrientation:(PSMTabBarOrientation)value; 26 | 27 | // add tab button 28 | - (NSImage *)addTabButtonImage; 29 | - (NSImage *)addTabButtonPressedImage; 30 | - (NSImage *)addTabButtonRolloverImage; 31 | 32 | // cell specific parameters 33 | - (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell orientation:(PSMTabBarOrientation)orientation; 34 | - (NSRect)closeButtonRectForTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)cellFrame; 35 | - (NSRect)iconRectForTabCell:(PSMTabBarCell *)cell; 36 | - (NSRect)indicatorRectForTabCell:(PSMTabBarCell *)cell; 37 | - (NSRect)objectCounterRectForTabCell:(PSMTabBarCell *)cell; 38 | - (CGFloat)minimumWidthOfTabCell:(PSMTabBarCell *)cell; 39 | - (CGFloat)desiredWidthOfTabCell:(PSMTabBarCell *)cell; 40 | - (CGFloat)tabCellHeight; 41 | 42 | // cell values 43 | - (NSAttributedString *)attributedObjectCountValueForTabCell:(PSMTabBarCell *)cell; 44 | - (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell; 45 | 46 | // drawing 47 | - (void)drawTabCell:(PSMTabBarCell *)cell; 48 | - (void)drawBackgroundInRect:(NSRect)rect; 49 | - (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect; 50 | 51 | @end 52 | 53 | @interface PSMTabBarControl (StyleAccessors) 54 | 55 | - (NSMutableArray *)cells; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /source/demo/AppController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppController.h 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 12/19/05. 6 | // Copyright 2005 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppController : NSObject { 12 | 13 | } 14 | 15 | - (IBAction)newWindow:(id)sender; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /source/demo/AppController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppController.m 3 | // TabBarControl 4 | // 5 | // Created by John Pannell on 12/19/05. 6 | // Copyright 2005 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import "AppController.h" 10 | #import "WindowController.h" 11 | 12 | @implementation AppController 13 | 14 | - (void)awakeFromNib 15 | { 16 | [self newWindow:self]; 17 | NSRect frontFrame = [[NSApp keyWindow] frame]; 18 | frontFrame.origin.x += 400; 19 | [[NSApp keyWindow] setFrame:frontFrame display:YES]; 20 | } 21 | 22 | - (IBAction)newWindow:(id)sender 23 | { 24 | // put up a window 25 | WindowController *newWindow = [[WindowController alloc] initWithWindowNibName:@"Window"]; 26 | [newWindow showWindow:self]; 27 | [newWindow addDefaultTabs]; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /source/demo/FakeModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // FakeModel.h 3 | // TabBarControl 4 | // 5 | // Created by John Pannell on 12/19/05. 6 | // Copyright 2005 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface FakeModel : NSObject { 13 | BOOL _isProcessing; 14 | NSImage *_icon; 15 | NSString *_iconName; 16 | NSInteger _objectCount; 17 | BOOL _isEdited; 18 | } 19 | 20 | // creation/destruction 21 | - (id)init; 22 | 23 | // accessors 24 | - (BOOL)isProcessing; 25 | - (void)setIsProcessing:(BOOL)value; 26 | - (NSImage *)icon; 27 | - (void)setIcon:(NSImage *)icon; 28 | - (NSString *)iconName; 29 | - (void)setIconName:(NSString *)iconName; 30 | - (NSInteger)objectCount; 31 | - (void)setObjectCount:(NSInteger)value; 32 | - (BOOL)isEdited; 33 | - (void)setIsEdited:(BOOL)value; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /source/demo/FakeModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // FakeModel.m 3 | // TabBarControl 4 | // 5 | // Created by John Pannell on 12/19/05. 6 | // Copyright 2005 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import "FakeModel.h" 10 | 11 | 12 | @implementation FakeModel 13 | 14 | - (id)init 15 | { 16 | if ( (self = [super init]) ) { 17 | _isProcessing = NO; 18 | _icon = nil; 19 | _iconName = nil; 20 | _objectCount = 2; 21 | _isEdited = NO; 22 | } 23 | return self; 24 | } 25 | 26 | 27 | // accessors 28 | - (BOOL)isProcessing 29 | { 30 | return _isProcessing; 31 | } 32 | 33 | - (void)setIsProcessing:(BOOL)value 34 | { 35 | _isProcessing = value; 36 | } 37 | 38 | - (NSImage *)icon 39 | { 40 | return _icon; 41 | } 42 | 43 | - (void)setIcon:(NSImage *)icon 44 | { 45 | [icon retain]; 46 | [_icon release]; 47 | _icon = icon; 48 | } 49 | 50 | - (NSString *)iconName 51 | { 52 | return _iconName; 53 | } 54 | 55 | - (void)setIconName:(NSString *)iconName 56 | { 57 | [iconName retain]; 58 | [_iconName release]; 59 | _iconName = iconName; 60 | } 61 | 62 | - (NSInteger)objectCount 63 | { 64 | return _objectCount; 65 | } 66 | 67 | - (void)setObjectCount:(NSInteger)value 68 | { 69 | _objectCount = value; 70 | } 71 | 72 | - (BOOL)isEdited 73 | { 74 | return _isEdited; 75 | } 76 | 77 | - (void)setIsEdited:(BOOL)value 78 | { 79 | _isEdited = value; 80 | } 81 | 82 | 83 | - (NSImage *)largeImage 84 | { 85 | return [NSImage imageNamed:@"largeImage"]; 86 | } 87 | 88 | - (void)setLargeImage:(NSImage *)icon 89 | { 90 | [icon retain]; 91 | [_icon release]; 92 | _icon = icon; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /source/demo/WindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WindowController.h 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 4/6/06. 6 | // Copyright 2006 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import 10 | @class PSMTabBarControl; 11 | 12 | @interface WindowController : NSWindowController { 13 | IBOutlet NSTabView *tabView; 14 | IBOutlet NSTextField *tabField; 15 | IBOutlet NSDrawer *drawer; 16 | 17 | IBOutlet PSMTabBarControl *tabBar; 18 | 19 | IBOutlet NSButton *isProcessingButton; 20 | IBOutlet NSButton *isEditedButton; 21 | IBOutlet NSTextField *objectCounterField; 22 | IBOutlet NSPopUpButton *iconButton; 23 | 24 | IBOutlet NSPopUpButton *popUp_style; 25 | IBOutlet NSPopUpButton *popUp_orientation; 26 | IBOutlet NSPopUpButton *popUp_tearOff; 27 | IBOutlet NSButton *button_canCloseOnlyTab; 28 | IBOutlet NSButton *button_disableTabClosing; 29 | IBOutlet NSButton *button_hideForSingleTab; 30 | IBOutlet NSButton *button_showAddTab; 31 | IBOutlet NSButton *button_useOverflow; 32 | IBOutlet NSButton *button_automaticallyAnimate; 33 | IBOutlet NSButton *button_allowScrubbing; 34 | IBOutlet NSButton *button_sizeToFit; 35 | IBOutlet NSTextField *textField_minWidth; 36 | IBOutlet NSTextField *textField_maxWidth; 37 | IBOutlet NSTextField *textField_optimumWidth; 38 | 39 | } 40 | 41 | - (void)addDefaultTabs; 42 | 43 | // UI 44 | - (IBAction)addNewTab:(id)sender; 45 | - (IBAction)closeTab:(id)sender; 46 | - (IBAction)stopProcessing:(id)sender; 47 | - (IBAction)setIconNamed:(id)sender; 48 | - (IBAction)setObjectCount:(id)sender; 49 | - (IBAction)setTabLabel:(id)sender; 50 | 51 | // Actions 52 | - (IBAction)isProcessingAction:(id)sender; 53 | - (IBAction)isEditedAction:(id)sender; 54 | 55 | - (PSMTabBarControl *)tabBar; 56 | 57 | // tab bar config 58 | - (void)configStyle:(id)sender; 59 | - (void)configOrientation:(id)sender; 60 | - (void)configCanCloseOnlyTab:(id)sender; 61 | - (void)configDisableTabClose:(id)sender; 62 | - (void)configHideForSingleTab:(id)sender; 63 | - (void)configAddTabButton:(id)sender; 64 | - (void)configTabMinWidth:(id)sender; 65 | - (void)configTabMaxWidth:(id)sender; 66 | - (void)configTabOptimumWidth:(id)sender; 67 | - (void)configTabSizeToFit:(id)sender; 68 | - (void)configTearOffStyle:(id)sender; 69 | - (void)configUseOverflowMenu:(id)sender; 70 | - (void)configAutomaticallyAnimates:(id)sender; 71 | - (void)configAllowsScrubbing:(id)sender; 72 | 73 | // delegate 74 | - (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem; 75 | - (BOOL)tabView:(NSTabView *)aTabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem; 76 | - (void)tabView:(NSTabView *)aTabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem; 77 | 78 | // toolbar 79 | - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag; 80 | - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar; 81 | - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar; 82 | - (IBAction)toggleToolbar:(id)sender; 83 | - (BOOL)validateToolbarItem:(NSToolbarItem *)theItem; 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /source/demo/WindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WindowController.m 3 | // PSMTabBarControl 4 | // 5 | // Created by John Pannell on 4/6/06. 6 | // Copyright 2006 Positive Spin Media. All rights reserved. 7 | // 8 | 9 | #import "WindowController.h" 10 | #import "FakeModel.h" 11 | #import "PSMTabBarControl.h" 12 | #import "PSMTabStyle.h" 13 | 14 | @interface WindowController (PRIVATE) 15 | - (void)configureTabBarInitially; 16 | @end 17 | 18 | @implementation WindowController 19 | 20 | - (void)awakeFromNib 21 | { 22 | [[NSUserDefaults standardUserDefaults] registerDefaults: 23 | [NSDictionary dictionaryWithObjectsAndKeys: 24 | @"Metal", @"Style", 25 | @"Horizontal", @"Orientation", 26 | @"Alpha Window", @"Tear-Off", 27 | @"100", @"TabMinWidth", 28 | @"280", @"TabMaxWidth", 29 | @"130", @"TabOptimalWidth", 30 | [NSNumber numberWithBool:YES], @"UseOverflowMenu", 31 | nil]]; 32 | 33 | // toolbar 34 | NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"DemoToolbar"]; 35 | [toolbar setDelegate:self]; 36 | [toolbar setAllowsUserCustomization:YES]; 37 | [toolbar setAutosavesConfiguration:YES]; 38 | [toolbar setShowsBaselineSeparator:NO]; 39 | [[self window] setToolbar:[toolbar autorelease]]; 40 | 41 | // hook up add tab button 42 | [[tabBar addTabButton] setTarget:self]; 43 | [[tabBar addTabButton] setAction:@selector(addNewTab:)]; 44 | 45 | // remove any tabs present in the nib 46 | NSArray *existingItems = [tabView tabViewItems]; 47 | NSEnumerator *e = [existingItems objectEnumerator]; 48 | NSTabViewItem *item; 49 | while (item = [e nextObject]) { 50 | [tabView removeTabViewItem:item]; 51 | } 52 | 53 | [self performSelector:@selector(configureTabBarInitially) 54 | withObject:nil 55 | afterDelay:0]; 56 | 57 | // open drawer 58 | //[drawer toggle:self]; 59 | } 60 | 61 | - (void)addDefaultTabs 62 | { 63 | [self addNewTab:self]; 64 | [self addNewTab:self]; 65 | [self addNewTab:self]; 66 | [[tabView tabViewItemAtIndex:0] setLabel:@"Tab"]; 67 | [[tabView tabViewItemAtIndex:1] setLabel:@"Bar"]; 68 | [[tabView tabViewItemAtIndex:2] setLabel:@"Control"]; 69 | NSRect tabViewBounds = [[[tabView tabViewItemAtIndex:0] view] bounds]; 70 | [[tabView tabViewItemAtIndex:0] setView:[[[NSImageView alloc] initWithFrame:tabViewBounds] autorelease]]; 71 | [[[tabView tabViewItemAtIndex:0] view] setImage:[NSImage imageNamed:@"mcqueen_large"]]; 72 | [[tabView tabViewItemAtIndex:1] setView:[[[NSImageView alloc] initWithFrame:tabViewBounds] autorelease]]; 73 | [[[tabView tabViewItemAtIndex:1] view] setImage:[NSImage imageNamed:@"mater_large"]]; 74 | [[tabView tabViewItemAtIndex:2] setView:[[[NSImageView alloc] initWithFrame:tabViewBounds] autorelease]]; 75 | [[[tabView tabViewItemAtIndex:2] view] setImage:[NSImage imageNamed:@"sally_large"]]; 76 | } 77 | 78 | - (IBAction)addNewTab:(id)sender 79 | { 80 | FakeModel *newModel = [[FakeModel alloc] init]; 81 | NSTabViewItem *newItem = [[[NSTabViewItem alloc] initWithIdentifier:newModel] autorelease]; 82 | [newItem setLabel:@"Untitled"]; 83 | [tabView addTabViewItem:newItem]; 84 | [tabView selectTabViewItem:newItem]; // this is optional, but expected behavior 85 | [newModel release]; 86 | } 87 | 88 | - (IBAction)closeTab:(id)sender 89 | { 90 | [tabView removeTabViewItem:[tabView selectedTabViewItem]]; 91 | } 92 | 93 | - (void)stopProcessing:(id)sender 94 | { 95 | [[[tabView selectedTabViewItem] identifier] setValue:[NSNumber numberWithBool:NO] forKeyPath:@"isProcessing"]; 96 | } 97 | 98 | - (void)setIconNamed:(id)sender 99 | { 100 | NSString *iconName = [sender titleOfSelectedItem]; 101 | if ([iconName isEqualToString:@"None"]) { 102 | [[[tabView selectedTabViewItem] identifier] setValue:nil forKeyPath:@"icon"]; 103 | [[[tabView selectedTabViewItem] identifier] setValue:@"None" forKeyPath:@"iconName"]; 104 | } else { 105 | NSImage *newIcon = [NSImage imageNamed:iconName]; 106 | [[[tabView selectedTabViewItem] identifier] setValue:newIcon forKeyPath:@"icon"]; 107 | [[[tabView selectedTabViewItem] identifier] setValue:iconName forKeyPath:@"iconName"]; 108 | } 109 | } 110 | 111 | - (void)setObjectCount:(id)sender 112 | { 113 | [[[tabView selectedTabViewItem] identifier] setValue:[NSNumber numberWithInteger:[sender integerValue]] forKeyPath:@"objectCount"]; 114 | } 115 | 116 | - (IBAction)isProcessingAction:(id)sender 117 | { 118 | [[[tabView selectedTabViewItem] identifier] setValue:[NSNumber numberWithBool:[sender state]] forKeyPath:@"isProcessing"]; 119 | } 120 | 121 | - (IBAction)isEditedAction:(id)sender 122 | { 123 | [[[tabView selectedTabViewItem] identifier] setValue:[NSNumber numberWithBool:[sender state]] forKeyPath:@"isEdited"]; 124 | } 125 | 126 | - (IBAction)setTabLabel:(id)sender 127 | { 128 | [[tabView selectedTabViewItem] setLabel:[sender stringValue]]; 129 | } 130 | 131 | - (BOOL)validateMenuItem:(NSMenuItem *)menuItem 132 | { 133 | if ([menuItem action] == @selector(closeTab:)) { 134 | if (![tabBar canCloseOnlyTab] && ([tabView numberOfTabViewItems] <= 1)) { 135 | return NO; 136 | } 137 | } 138 | return YES; 139 | } 140 | 141 | - (PSMTabBarControl *)tabBar 142 | { 143 | return tabBar; 144 | } 145 | 146 | - (void)windowWillClose:(NSNotification *)note 147 | { 148 | [self autorelease]; 149 | } 150 | 151 | #pragma mark - 152 | #pragma mark ---- tab bar config ---- 153 | 154 | - (void)configStyle:(id)sender 155 | { 156 | [tabBar setStyleNamed:[sender titleOfSelectedItem]]; 157 | 158 | [[NSUserDefaults standardUserDefaults] setObject:[sender titleOfSelectedItem] 159 | forKey:@"Style"]; 160 | } 161 | 162 | - (void)configOrientation:(id)sender 163 | { 164 | PSMTabBarOrientation orientation = ([sender indexOfSelectedItem] == 0) ? PSMTabBarHorizontalOrientation : PSMTabBarVerticalOrientation; 165 | 166 | if (orientation == [tabBar orientation]) { 167 | return; 168 | } 169 | 170 | //change the frame of the tab bar according to the orientation 171 | NSRect tabBarFrame = [tabBar frame], tabViewFrame = [tabView frame]; 172 | NSRect totalFrame = NSUnionRect(tabBarFrame, tabViewFrame); 173 | 174 | if (orientation == PSMTabBarHorizontalOrientation) { 175 | tabBarFrame.size.height = [tabBar isTabBarHidden] ? 1 : 22; 176 | tabBarFrame.size.width = totalFrame.size.width; 177 | tabBarFrame.origin.y = totalFrame.origin.y + totalFrame.size.height - tabBarFrame.size.height; 178 | tabViewFrame.origin.x = 13; 179 | tabViewFrame.size.width = totalFrame.size.width - 23; 180 | tabViewFrame.size.height = totalFrame.size.height - tabBarFrame.size.height - 2; 181 | [tabBar setAutoresizingMask:NSViewMinYMargin | NSViewWidthSizable]; 182 | } else { 183 | tabBarFrame.size.height = totalFrame.size.height; 184 | tabBarFrame.size.width = [tabBar isTabBarHidden] ? 1 : 120; 185 | tabBarFrame.origin.y = totalFrame.origin.y; 186 | tabViewFrame.origin.x = tabBarFrame.origin.x + tabBarFrame.size.width; 187 | tabViewFrame.size.width = totalFrame.size.width - tabBarFrame.size.width; 188 | tabViewFrame.size.height = totalFrame.size.height; 189 | [tabBar setAutoresizingMask:NSViewHeightSizable]; 190 | } 191 | 192 | tabBarFrame.origin.x = totalFrame.origin.x; 193 | tabViewFrame.origin.y = totalFrame.origin.y; 194 | 195 | [tabView setFrame:tabViewFrame]; 196 | [tabBar setFrame:tabBarFrame]; 197 | 198 | [tabBar setOrientation:orientation]; 199 | [[self window] display]; 200 | 201 | [[NSUserDefaults standardUserDefaults] setObject:[sender title] 202 | forKey:@"Orientation"]; 203 | } 204 | 205 | - (void)configCanCloseOnlyTab:(id)sender 206 | { 207 | [tabBar setCanCloseOnlyTab:[sender state]]; 208 | 209 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender state]] 210 | forKey:@"CanCloseOnlyTab"]; 211 | } 212 | 213 | - (void)configDisableTabClose:(id)sender 214 | { 215 | [tabBar setDisableTabClose:[sender state]]; 216 | 217 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender state]] 218 | forKey:@"DisableTabClose"]; 219 | } 220 | 221 | - (void)configHideForSingleTab:(id)sender 222 | { 223 | [tabBar setHideForSingleTab:[sender state]]; 224 | 225 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender state]] 226 | forKey:@"HideForSingleTab"]; 227 | } 228 | 229 | - (void)configAddTabButton:(id)sender 230 | { 231 | [tabBar setShowAddTabButton:[sender state]]; 232 | 233 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender state]] 234 | forKey:@"ShowAddTabButton"]; 235 | } 236 | 237 | - (void)configTabMinWidth:(id)sender 238 | { 239 | if ([tabBar cellOptimumWidth] < [sender integerValue]) { 240 | [tabBar setCellMinWidth:[tabBar cellOptimumWidth]]; 241 | [sender setIntegerValue:[tabBar cellOptimumWidth]]; 242 | return; 243 | } 244 | 245 | [tabBar setCellMinWidth:[sender integerValue]]; 246 | 247 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:[sender integerValue]] 248 | forKey:@"TabMinWidth"]; 249 | } 250 | 251 | - (void)configTabMaxWidth:(id)sender 252 | { 253 | if ([tabBar cellOptimumWidth] > [sender integerValue]) { 254 | [tabBar setCellMaxWidth:[tabBar cellOptimumWidth]]; 255 | [sender setIntegerValue:[tabBar cellOptimumWidth]]; 256 | return; 257 | } 258 | 259 | [tabBar setCellMaxWidth:[sender integerValue]]; 260 | 261 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:[sender integerValue]] 262 | forKey:@"TabMaxWidth"]; 263 | } 264 | 265 | - (void)configTabOptimumWidth:(id)sender 266 | { 267 | if ([tabBar cellMaxWidth] < [sender integerValue]) { 268 | [tabBar setCellOptimumWidth:[tabBar cellMaxWidth]]; 269 | [sender setIntegerValue:[tabBar cellMaxWidth]]; 270 | return; 271 | } 272 | 273 | if ([tabBar cellMinWidth] > [sender integerValue]) { 274 | [tabBar setCellOptimumWidth:[tabBar cellMinWidth]]; 275 | [sender setIntegerValue:[tabBar cellMinWidth]]; 276 | return; 277 | } 278 | 279 | [tabBar setCellOptimumWidth:[sender integerValue]]; 280 | 281 | } 282 | 283 | - (void)configTabSizeToFit:(id)sender 284 | { 285 | [tabBar setSizeCellsToFit:[sender state]]; 286 | 287 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender integerValue]] 288 | forKey:@"SizeToFit"]; 289 | } 290 | 291 | - (void)configTearOffStyle:(id)sender 292 | { 293 | [tabBar setTearOffStyle:([sender indexOfSelectedItem] == 0) ? PSMTabBarTearOffAlphaWindow : PSMTabBarTearOffMiniwindow]; 294 | 295 | [[NSUserDefaults standardUserDefaults] setObject:[sender title] 296 | forKey:@"Tear-Off"]; 297 | } 298 | 299 | - (void)configUseOverflowMenu:(id)sender 300 | { 301 | [tabBar setUseOverflowMenu:[sender state]]; 302 | 303 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender integerValue]] 304 | forKey:@"UseOverflowMenu"]; 305 | } 306 | 307 | - (void)configAutomaticallyAnimates:(id)sender 308 | { 309 | [tabBar setAutomaticallyAnimates:[sender state]]; 310 | 311 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender integerValue]] 312 | forKey:@"AutomaticallyAnimates"]; 313 | } 314 | 315 | - (void)configAllowsScrubbing:(id)sender 316 | { 317 | [tabBar setAllowsScrubbing:[sender state]]; 318 | 319 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender integerValue]] 320 | forKey:@"AllowScrubbing"]; 321 | } 322 | 323 | #pragma mark - 324 | #pragma mark ---- delegate ---- 325 | 326 | - (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem 327 | { 328 | // need to update bound values to match the selected tab 329 | if ([[tabViewItem identifier] respondsToSelector:@selector(objectCount)]) { 330 | [objectCounterField setIntegerValue:[[tabViewItem identifier] objectCount]]; 331 | } 332 | 333 | if ([[tabViewItem identifier] respondsToSelector:@selector(isProcessing)]) { 334 | [isProcessingButton setState:[[tabViewItem identifier] isProcessing]]; 335 | } 336 | 337 | if ([[tabViewItem identifier] respondsToSelector:@selector(isEdited)]) { 338 | [isEditedButton setState:[[tabViewItem identifier] isEdited]]; 339 | } 340 | 341 | if ([[tabViewItem identifier] respondsToSelector:@selector(iconName)]) { 342 | NSString *newName = [[tabViewItem identifier] iconName]; 343 | if (newName) { 344 | [iconButton selectItem:[[iconButton menu] itemWithTitle:newName]]; 345 | } else { 346 | [iconButton selectItem:[[iconButton menu] itemWithTitle:@"None"]]; 347 | } 348 | } 349 | } 350 | 351 | - (BOOL)tabView:(NSTabView *)aTabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem 352 | { 353 | if ([[tabViewItem label] isEqualToString:@"Drake"]) { 354 | NSAlert *drakeAlert = [NSAlert alertWithMessageText:@"No Way!" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"I refuse to close a tab named \"Drake\""]; 355 | [drakeAlert beginSheetModalForWindow:[NSApp keyWindow] modalDelegate:nil didEndSelector:nil contextInfo:nil]; 356 | return NO; 357 | } 358 | return YES; 359 | } 360 | 361 | - (void)tabView:(NSTabView *)aTabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem 362 | { 363 | NSLog(@"didCloseTabViewItem: %@", [tabViewItem label]); 364 | } 365 | 366 | - (NSArray *)allowedDraggedTypesForTabView:(NSTabView *)aTabView 367 | { 368 | return [NSArray arrayWithObjects:NSFilenamesPboardType, NSStringPboardType, nil]; 369 | } 370 | 371 | - (void)tabView:(NSTabView *)aTabView acceptedDraggingInfo:(id )draggingInfo onTabViewItem:(NSTabViewItem *)tabViewItem 372 | { 373 | NSLog(@"acceptedDraggingInfo: %@ onTabViewItem: %@", [[draggingInfo draggingPasteboard] stringForType:[[[draggingInfo draggingPasteboard] types] objectAtIndex:0]], [tabViewItem label]); 374 | } 375 | 376 | - (NSMenu *)tabView:(NSTabView *)aTabView menuForTabViewItem:(NSTabViewItem *)tabViewItem 377 | { 378 | NSLog(@"menuForTabViewItem: %@", [tabViewItem label]); 379 | return nil; 380 | } 381 | 382 | - (BOOL)tabView:(NSTabView*)aTabView shouldDragTabViewItem:(NSTabViewItem *)tabViewItem fromTabBar:(PSMTabBarControl *)tabBarControl 383 | { 384 | return YES; 385 | } 386 | 387 | - (BOOL)tabView:(NSTabView*)aTabView shouldDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl 388 | { 389 | return YES; 390 | } 391 | 392 | - (void)tabView:(NSTabView*)aTabView didDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl 393 | { 394 | NSLog(@"didDropTabViewItem: %@ inTabBar: %@", [tabViewItem label], tabBarControl); 395 | } 396 | 397 | - (NSImage *)tabView:(NSTabView *)aTabView imageForTabViewItem:(NSTabViewItem *)tabViewItem offset:(NSSize *)offset styleMask:(NSUInteger *)styleMask 398 | { 399 | // grabs whole window image 400 | NSImage *viewImage = [[[NSImage alloc] init] autorelease]; 401 | NSRect contentFrame = [[[self window] contentView] frame]; 402 | [[[self window] contentView] lockFocus]; 403 | NSBitmapImageRep *viewRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:contentFrame] autorelease]; 404 | [viewImage addRepresentation:viewRep]; 405 | [[[self window] contentView] unlockFocus]; 406 | 407 | // grabs snapshot of dragged tabViewItem's view (represents content being dragged) 408 | NSView *viewForImage = [tabViewItem view]; 409 | NSRect viewRect = [viewForImage frame]; 410 | NSImage *tabViewImage = [[[NSImage alloc] initWithSize:viewRect.size] autorelease]; 411 | [tabViewImage lockFocus]; 412 | [viewForImage drawRect:[viewForImage bounds]]; 413 | [tabViewImage unlockFocus]; 414 | 415 | [viewImage lockFocus]; 416 | NSPoint tabOrigin = [tabView frame].origin; 417 | tabOrigin.x += 10; 418 | tabOrigin.y += 13; 419 | [tabViewImage compositeToPoint:tabOrigin operation:NSCompositeSourceOver]; 420 | [viewImage unlockFocus]; 421 | 422 | //draw over where the tab bar would usually be 423 | NSRect tabFrame = [tabBar frame]; 424 | [viewImage lockFocus]; 425 | [[NSColor windowBackgroundColor] set]; 426 | NSRectFill(tabFrame); 427 | //draw the background flipped, which is actually the right way up 428 | NSAffineTransform *transform = [NSAffineTransform transform]; 429 | [transform scaleXBy:1.0 yBy:-1.0]; 430 | [transform concat]; 431 | tabFrame.origin.y = -tabFrame.origin.y - tabFrame.size.height; 432 | [(id )[(PSMTabBarControl *)[aTabView delegate] style] drawBackgroundInRect:tabFrame]; 433 | [transform invert]; 434 | [transform concat]; 435 | 436 | [viewImage unlockFocus]; 437 | 438 | if ([(PSMTabBarControl *)[aTabView delegate] orientation] == PSMTabBarHorizontalOrientation) { 439 | offset->width = [(id )[(PSMTabBarControl *)[aTabView delegate] style] leftMarginForTabBarControl]; 440 | offset->height = 22; 441 | } else { 442 | offset->width = 0; 443 | offset->height = 22 + [(id )[(PSMTabBarControl *)[aTabView delegate] style] leftMarginForTabBarControl]; 444 | } 445 | 446 | if (styleMask) { 447 | *styleMask = NSTitledWindowMask | NSTexturedBackgroundWindowMask; 448 | } 449 | 450 | return viewImage; 451 | } 452 | 453 | - (PSMTabBarControl *)tabView:(NSTabView *)aTabView newTabBarForDraggedTabViewItem:(NSTabViewItem *)tabViewItem atPoint:(NSPoint)point 454 | { 455 | NSLog(@"newTabBarForDraggedTabViewItem: %@ atPoint: %@", [tabViewItem label], NSStringFromPoint(point)); 456 | 457 | //create a new window controller with no tab items 458 | WindowController *controller = [[WindowController alloc] initWithWindowNibName:@"Window"]; 459 | id style = (id )[(PSMTabBarControl *)[aTabView delegate] style]; 460 | 461 | NSRect windowFrame = [[controller window] frame]; 462 | point.y += windowFrame.size.height - [[[controller window] contentView] frame].size.height; 463 | point.x -= [style leftMarginForTabBarControl]; 464 | 465 | [[controller window] setFrameTopLeftPoint:point]; 466 | [[controller tabBar] setStyle:style]; 467 | 468 | return [controller tabBar]; 469 | } 470 | 471 | - (void)tabView:(NSTabView *)aTabView closeWindowForLastTabViewItem:(NSTabViewItem *)tabViewItem 472 | { 473 | NSLog(@"closeWindowForLastTabViewItem: %@", [tabViewItem label]); 474 | [[self window] close]; 475 | } 476 | 477 | - (void)tabView:(NSTabView *)aTabView tabBarDidHide:(PSMTabBarControl *)tabBarControl 478 | { 479 | NSLog(@"tabBarDidHide: %@", tabBarControl); 480 | } 481 | 482 | - (void)tabView:(NSTabView *)aTabView tabBarDidUnhide:(PSMTabBarControl *)tabBarControl 483 | { 484 | NSLog(@"tabBarDidUnhide: %@", tabBarControl); 485 | } 486 | 487 | - (NSString *)tabView:(NSTabView *)aTabView toolTipForTabViewItem:(NSTabViewItem *)tabViewItem 488 | { 489 | return [tabViewItem label]; 490 | } 491 | 492 | - (NSString *)accessibilityStringForTabView:(NSTabView *)aTabView objectCount:(NSInteger)objectCount 493 | { 494 | return (objectCount == 1) ? @"item" : @"items"; 495 | } 496 | 497 | #pragma mark - 498 | #pragma mark ---- toolbar ---- 499 | 500 | - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag 501 | { 502 | NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier]; 503 | 504 | if ([itemIdentifier isEqualToString:@"TabField"]) { 505 | [item setPaletteLabel:@"Tab Label"]; 506 | [item setLabel:@"Tab Label"]; 507 | [item setView:tabField]; 508 | [item setMinSize:NSMakeSize(100, [tabField frame].size.height)]; 509 | [item setMaxSize:NSMakeSize(500, [tabField frame].size.height)]; 510 | } else if ([itemIdentifier isEqualToString:@"DrawerItem"]) { 511 | [item setPaletteLabel:@"Configuration"]; 512 | [item setLabel:@"Configuration"]; 513 | [item setToolTip:@"Configuration"]; 514 | [item setImage:[NSImage imageNamed:@"32x32_log"]]; 515 | [item setTarget:drawer]; 516 | [item setAction:@selector(toggle:)]; 517 | } 518 | 519 | return [item autorelease]; 520 | } 521 | 522 | - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar 523 | { 524 | return [NSArray arrayWithObjects:@"TabField", 525 | NSToolbarFlexibleSpaceItemIdentifier, 526 | @"DrawerItem", 527 | nil]; 528 | } 529 | 530 | - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar 531 | { 532 | return [NSArray arrayWithObjects:@"TabField", 533 | NSToolbarFlexibleSpaceItemIdentifier, 534 | @"DrawerItem", 535 | nil]; 536 | } 537 | 538 | - (IBAction)toggleToolbar:(id)sender 539 | { 540 | [[[self window] toolbar] setVisible:![[[self window] toolbar] isVisible]]; 541 | } 542 | 543 | - (BOOL)validateToolbarItem:(NSToolbarItem *)theItem 544 | { 545 | return YES; 546 | } 547 | 548 | 549 | - (void)configureTabBarInitially 550 | { 551 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 552 | [popUp_style selectItemWithTitle:[defaults stringForKey:@"Style"]]; 553 | [popUp_orientation selectItemWithTitle:[defaults stringForKey:@"Orientation"]]; 554 | [popUp_tearOff selectItemWithTitle:[defaults stringForKey:@"Tear-Off"]]; 555 | 556 | [button_canCloseOnlyTab setState:[defaults boolForKey:@"CanCloseOnlyTab"]]; 557 | [button_disableTabClosing setState:[defaults boolForKey:@"DisableTabClosing"]]; 558 | [button_hideForSingleTab setState:[defaults boolForKey:@"HideForSingleTab"]]; 559 | [button_showAddTab setState:[defaults boolForKey:@"ShowAddTabButton"]]; 560 | [button_sizeToFit setState:[defaults boolForKey:@"SizeToFit"]]; 561 | [button_useOverflow setState:[defaults boolForKey:@"UseOverflowMenu"]]; 562 | [button_automaticallyAnimate setState:[defaults boolForKey:@"AutomaticallyAnimates"]]; 563 | [button_allowScrubbing setState:[defaults boolForKey:@"AllowScrubbing"]]; 564 | 565 | [self configStyle:popUp_style]; 566 | [self configOrientation:popUp_orientation]; 567 | [self configCanCloseOnlyTab:button_canCloseOnlyTab]; 568 | [self configDisableTabClose:button_disableTabClosing]; 569 | [self configHideForSingleTab:button_hideForSingleTab]; 570 | [self configAddTabButton:button_showAddTab]; 571 | [self configTabMinWidth:textField_minWidth]; 572 | [self configTabMaxWidth:textField_maxWidth]; 573 | [self configTabOptimumWidth:textField_optimumWidth]; 574 | [self configTabSizeToFit:button_sizeToFit]; 575 | [self configTearOffStyle:popUp_tearOff]; 576 | [self configUseOverflowMenu:button_useOverflow]; 577 | [self configAutomaticallyAnimates:button_automaticallyAnimate]; 578 | [self configAllowsScrubbing:button_allowScrubbing]; 579 | } 580 | @end 581 | -------------------------------------------------------------------------------- /source/demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TabBarControl 4 | // 5 | // Created by John Pannell on 12/18/05. 6 | // Copyright Positive Spin Media 2005. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **) argv); 14 | } 15 | -------------------------------------------------------------------------------- /version.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildVersion 6 | 92 7 | CFBundleVersion 8 | 1.0 9 | ProductBuildVersion 10 | 7K571 11 | ProjectName 12 | NibPBTemplates 13 | SourceVersion 14 | 1200000 15 | 16 | 17 | --------------------------------------------------------------------------------