├── 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 | Inherits from
16 | Conforms to
21 | Declared in PSMTabBarControl.h
22 |
23 |
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 |
29 | The control's "tabView" outlet must be connected to the desired NSTabView instance in Interface Builder, or set programmatically with the setTabView: method.
30 | The "delegate" outlet of the NSTabView being controlled must be connected to the instance of PSMTabBarControl, either programmatically or in IB.
31 | Optionally, you may connect the control's "partnerView" outlet to a view that will be resized to compensate when PSMTabBarControl changes size due to hide/show behavior. If no connection is made, PSMTabBarControl will resize the window in response to hide/show messages.
32 |
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 | For each tab that is created, your application should have a model object that keeps the state of model attributes: isProcessing (a BOOL), objectCount (an int), and icon (an NSImage). You can pick and choose any number of these to support, or none, at your option.
43 | Your application should create an NSObjectController to control your model (the model object is the "content" of the NSObjectController instance).
44 | When you create new tabs (via the NSTabView interface), you should set the object controller instance to be the identifier object of the newly created NSTabViewItem object.
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 |
--------------------------------------------------------------------------------