├── .gitignore ├── OriginalGraphics ├── AquaTabNew.psd ├── AquaTabNewPressed.psd └── AquaTabNewRollover.psd ├── PSMTabBarControl.xcodeproj ├── default.pbxuser └── project.pbxproj ├── README.mdown ├── Resources ├── AdiumGradient.png ├── AquaTabCloseDirty_Front.png ├── AquaTabCloseDirty_Front_Pressed.png ├── AquaTabCloseDirty_Front_Rollover.png ├── AquaTabClose_Front.png ├── AquaTabClose_Front@2x.png ├── AquaTabClose_Front_Pressed.png ├── AquaTabClose_Front_Pressed@2x.png ├── AquaTabClose_Front_Rollover.png ├── AquaTabClose_Front_Rollover@2x.png ├── AquaTabNew.png ├── AquaTabNew@2x.png ├── AquaTabNewPressed.png ├── AquaTabNewPressed@2x.png ├── AquaTabNewRollover.png ├── AquaTabNewRollover@2x.png ├── AquaTabsBackground.png ├── AquaTabsDown.png ├── AquaTabsDownGraphite.png ├── AquaTabsDownNonKey.png ├── AquaTabsSeparator.png ├── AquaTabsSeparatorDown.png ├── DemoWindow.xib ├── English.lproj │ └── InfoPlist.strings ├── Japanese.lproj │ ├── InfoPlist.strings │ ├── Inspector.xib │ └── Library.nib │ │ ├── designable.nib │ │ └── keyedobjects.nib ├── PSMTabBarControlDemo.xib ├── TabClose_Dirty.png ├── TabClose_Dirty@2x.png ├── TabClose_Dirty_Pressed.png ├── TabClose_Dirty_Pressed@2x.png ├── TabClose_Dirty_Rollover.png ├── TabClose_Dirty_Rollover@2x.png ├── TabClose_Front.png ├── TabClose_Front@2x.png ├── TabClose_Front_Pressed.png ├── TabClose_Front_Pressed@2x.png ├── TabClose_Front_Rollover.png ├── TabClose_Front_Rollover@2x.png ├── TabNewMetal.png ├── TabNewMetalPressed.png ├── TabNewMetalRollover.png ├── largeImage.png ├── overflowImage.png ├── overflowImage@2x.png ├── overflowImagePressed.png ├── overflowImagePressed@2x.png └── pi.png ├── Source ├── DemoAppController.h ├── DemoAppController.m ├── DemoFakeModel.h ├── DemoFakeModel.m ├── DemoWindowController.h ├── DemoWindowController.m ├── NSString_AITruncation.h ├── NSString_AITruncation.m ├── PSMAdiumTabStyle.h ├── PSMAdiumTabStyle.m ├── PSMAquaTabStyle.h ├── PSMAquaTabStyle.m ├── PSMCardTabStyle.h ├── PSMCardTabStyle.m ├── PSMLiveChatTabStyle.h ├── PSMLiveChatTabStyle.m ├── PSMMetalTabStyle.h ├── PSMMetalTabStyle.m ├── PSMOverflowPopUpButton.h ├── PSMOverflowPopUpButton.m ├── PSMProgressIndicator.h ├── PSMProgressIndicator.m ├── PSMRolloverButton.h ├── PSMRolloverButton.m ├── PSMTabBarCell.h ├── PSMTabBarCell.m ├── PSMTabBarControl-Info.plist ├── PSMTabBarControl.h ├── PSMTabBarControl.m ├── PSMTabBarControlDemo-Info.plist ├── PSMTabBarController.h ├── PSMTabBarController.m ├── PSMTabDragAssistant.h ├── PSMTabDragAssistant.m ├── PSMTabDragView.h ├── PSMTabDragView.m ├── PSMTabDragWindow.h ├── PSMTabDragWindow.m ├── PSMTabDragWindowController.h ├── PSMTabDragWindowController.m ├── PSMTabStyle.h ├── PSMUnifiedTabStyle.h ├── PSMUnifiedTabStyle.m ├── README └── demo_main.m ├── build.sh └── xcconfigs ├── Base.xcconfig ├── Debug.xcconfig ├── PSMTabBarControlDemo-Base.xcconfig ├── PSMTabBarControlDemo-Debug.xcconfig ├── PSMTabBarControlDemo-Release.xcconfig ├── PSMTabBarControlFramework-Base.xcconfig ├── PSMTabBarControlFramework-Debug.xcconfig ├── PSMTabBarControlFramework-Release.xcconfig └── Release.xcconfig /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /OriginalGraphics/AquaTabNew.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/OriginalGraphics/AquaTabNew.psd -------------------------------------------------------------------------------- /OriginalGraphics/AquaTabNewPressed.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/OriginalGraphics/AquaTabNewPressed.psd -------------------------------------------------------------------------------- /OriginalGraphics/AquaTabNewRollover.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/OriginalGraphics/AquaTabNewRollover.psd -------------------------------------------------------------------------------- /PSMTabBarControl.xcodeproj/default.pbxuser: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | 0259C573FE90428111CA0C5A /* Project object */ = { 4 | activeArchitecturePreference = x86_64; 5 | activeBuildConfigurationName = Debug; 6 | activeExecutable = C096A9340C03CEAB00263BB0 /* Interface Builder */; 7 | activeTarget = 53DF6901067E5B8E0090B5B0 /* All */; 8 | codeSenseManager = C096A9330C03CEA700263BB0 /* Code sense */; 9 | executables = ( 10 | C096A9340C03CEAB00263BB0 /* Interface Builder */, 11 | ); 12 | perUserDictionary = { 13 | PBXConfiguration.PBXFileTableDataSource3.PBXExecutablesDataSource = { 14 | PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; 15 | PBXFileTableDataSourceColumnSortingKey = PBXExecutablesDataSource_NameID; 16 | PBXFileTableDataSourceColumnWidthsKey = ( 17 | 22, 18 | 300, 19 | 252, 20 | ); 21 | PBXFileTableDataSourceColumnsKey = ( 22 | PBXExecutablesDataSource_ActiveFlagID, 23 | PBXExecutablesDataSource_NameID, 24 | PBXExecutablesDataSource_CommentsID, 25 | ); 26 | }; 27 | PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { 28 | PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; 29 | PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; 30 | PBXFileTableDataSourceColumnWidthsKey = ( 31 | 20, 32 | 364, 33 | 20, 34 | 48, 35 | 43, 36 | 43, 37 | 20, 38 | ); 39 | PBXFileTableDataSourceColumnsKey = ( 40 | PBXFileDataSource_FiletypeID, 41 | PBXFileDataSource_Filename_ColumnID, 42 | PBXFileDataSource_Built_ColumnID, 43 | PBXFileDataSource_ObjectSize_ColumnID, 44 | PBXFileDataSource_Errors_ColumnID, 45 | PBXFileDataSource_Warnings_ColumnID, 46 | PBXFileDataSource_Target_ColumnID, 47 | ); 48 | }; 49 | PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = { 50 | PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; 51 | PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; 52 | PBXFileTableDataSourceColumnWidthsKey = ( 53 | 20, 54 | 324, 55 | 60, 56 | 20, 57 | 48.16259765625, 58 | 43, 59 | 43, 60 | ); 61 | PBXFileTableDataSourceColumnsKey = ( 62 | PBXFileDataSource_FiletypeID, 63 | PBXFileDataSource_Filename_ColumnID, 64 | PBXTargetDataSource_PrimaryAttribute, 65 | PBXFileDataSource_Built_ColumnID, 66 | PBXFileDataSource_ObjectSize_ColumnID, 67 | PBXFileDataSource_Errors_ColumnID, 68 | PBXFileDataSource_Warnings_ColumnID, 69 | ); 70 | }; 71 | PBXPerProjectTemplateStateSaveDate = 232924969; 72 | PBXWorkspaceStateSaveDate = 232924969; 73 | }; 74 | sourceControlManager = C096A9320C03CEA700263BB0 /* Source Control */; 75 | userBuildSettings = { 76 | }; 77 | }; 78 | 53DF68FC067E5B5A0090B5B0 /* PSMTabBarControlFramework */ = { 79 | activeExec = 0; 80 | }; 81 | 53DF6901067E5B8E0090B5B0 /* All */ = { 82 | activeExec = 0; 83 | }; 84 | 8D1AC9600486D14A00FE50C9 /* PSMTabBarControl */ = { 85 | activeExec = 0; 86 | }; 87 | C096A9320C03CEA700263BB0 /* Source Control */ = { 88 | isa = PBXSourceControlManager; 89 | fallbackIsa = XCSourceControlManager; 90 | isSCMEnabled = 0; 91 | scmConfiguration = { 92 | }; 93 | }; 94 | C096A9330C03CEA700263BB0 /* Code sense */ = { 95 | isa = PBXCodeSenseManager; 96 | indexTemplatePath = ""; 97 | }; 98 | C096A9340C03CEAB00263BB0 /* Interface Builder */ = { 99 | isa = PBXExecutable; 100 | activeArgIndices = ( 101 | YES, 102 | ); 103 | argumentStrings = ( 104 | "-NSOpen \"PSMTabBarControl.ibplugin\"", 105 | ); 106 | autoAttachOnCrash = 1; 107 | breakpointsEnabled = 1; 108 | configStateDict = { 109 | "PBXLSLaunchAction-0" = { 110 | PBXLSLaunchAction = 0; 111 | PBXLSLaunchStartAction = 1; 112 | PBXLSLaunchStdioStyle = 2; 113 | PBXLSLaunchStyle = 0; 114 | class = PBXLSRunLaunchConfig; 115 | commandLineArgs = ( 116 | ); 117 | displayName = "Executable Runner"; 118 | environment = { 119 | }; 120 | identifier = com.apple.Xcode.launch.runConfig; 121 | remoteHostInfo = ""; 122 | startActionInfo = ""; 123 | }; 124 | }; 125 | customDataFormattersEnabled = 1; 126 | debuggerPlugin = GDBDebugging; 127 | disassemblyDisplayState = 0; 128 | dylibVariantSuffix = ""; 129 | enableDebugStr = 1; 130 | environmentEntries = ( 131 | ); 132 | executableSystemSymbolLevel = 0; 133 | executableUserSymbolLevel = 0; 134 | launchableReference = C096A9350C03CEAB00263BB0 /* Interface Builder.app */; 135 | libgmallocEnabled = 0; 136 | name = "Interface Builder"; 137 | sourceDirectories = ( 138 | ); 139 | startupPath = "<>"; 140 | }; 141 | C096A9350C03CEAB00263BB0 /* Interface Builder.app */ = { 142 | isa = PBXFileReference; 143 | lastKnownFileType = wrapper.application; 144 | name = "Interface Builder.app"; 145 | path = "Applications/Interface Builder.app"; 146 | sourceTree = DEVELOPER_DIR; 147 | }; 148 | } 149 | -------------------------------------------------------------------------------- /README.mdown: -------------------------------------------------------------------------------- 1 | About dorianj/PSMTabBarControl 2 | === 3 | PSMTabBarControl remains the best way to have Safari-style tabs in your app. This fork makes it easy to use PSMTabBarControl when developing apps for 10.6, 10.7, and 10.8 using Xcode 4. It is also Retina-ready. 4 | 5 | This fork contains none of the IBPlugin stuff, and removes unnecessary graphics to get a much smaller framework size (about 668kb uncompressed, 232kb deflated). 6 | 7 | If you make any improvements, please submit them as pull requests. 8 | 9 | Building 10 | ==== 11 | 12 | To build, simply open a terminal window in the PSMTabBarControl repo and run ./build.sh. The framework will be in build/Release. 13 | 14 | Installing 15 | ==== 16 | 17 | Add the .framework bundle to your xcode project, and add it to the Linked Frameworks and Libraries (under Target -> Summary). Next, under Target -> Build Phases, Add a new build phase that copies it to the Frameworks directory of your app. (Add Build Phase > Copy Files. Destination: Frameworks) 18 | 19 | Copying 20 | ==== 21 | 22 | This package was originally created by Positive Spin Media, and is BSD licensed. See: http://www.positivespinmedia.com/dev/PSMTabBarControl.html 23 | 24 | License 25 | ==== 26 | 27 | Copyright © 2005, Positive Spin Media. All rights reserved. 28 | 29 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 30 | 31 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 32 | * 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. 33 | * 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. 34 | 35 | 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. 36 | -------------------------------------------------------------------------------- /Resources/AdiumGradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AdiumGradient.png -------------------------------------------------------------------------------- /Resources/AquaTabCloseDirty_Front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabCloseDirty_Front.png -------------------------------------------------------------------------------- /Resources/AquaTabCloseDirty_Front_Pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabCloseDirty_Front_Pressed.png -------------------------------------------------------------------------------- /Resources/AquaTabCloseDirty_Front_Rollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabCloseDirty_Front_Rollover.png -------------------------------------------------------------------------------- /Resources/AquaTabClose_Front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabClose_Front.png -------------------------------------------------------------------------------- /Resources/AquaTabClose_Front@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabClose_Front@2x.png -------------------------------------------------------------------------------- /Resources/AquaTabClose_Front_Pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabClose_Front_Pressed.png -------------------------------------------------------------------------------- /Resources/AquaTabClose_Front_Pressed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabClose_Front_Pressed@2x.png -------------------------------------------------------------------------------- /Resources/AquaTabClose_Front_Rollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabClose_Front_Rollover.png -------------------------------------------------------------------------------- /Resources/AquaTabClose_Front_Rollover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabClose_Front_Rollover@2x.png -------------------------------------------------------------------------------- /Resources/AquaTabNew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabNew.png -------------------------------------------------------------------------------- /Resources/AquaTabNew@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabNew@2x.png -------------------------------------------------------------------------------- /Resources/AquaTabNewPressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabNewPressed.png -------------------------------------------------------------------------------- /Resources/AquaTabNewPressed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabNewPressed@2x.png -------------------------------------------------------------------------------- /Resources/AquaTabNewRollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabNewRollover.png -------------------------------------------------------------------------------- /Resources/AquaTabNewRollover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabNewRollover@2x.png -------------------------------------------------------------------------------- /Resources/AquaTabsBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabsBackground.png -------------------------------------------------------------------------------- /Resources/AquaTabsDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabsDown.png -------------------------------------------------------------------------------- /Resources/AquaTabsDownGraphite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabsDownGraphite.png -------------------------------------------------------------------------------- /Resources/AquaTabsDownNonKey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabsDownNonKey.png -------------------------------------------------------------------------------- /Resources/AquaTabsSeparator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabsSeparator.png -------------------------------------------------------------------------------- /Resources/AquaTabsSeparatorDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/AquaTabsSeparatorDown.png -------------------------------------------------------------------------------- /Resources/English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Resources/Japanese.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/Japanese.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Resources/Japanese.lproj/Library.nib/keyedobjects.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/Japanese.lproj/Library.nib/keyedobjects.nib -------------------------------------------------------------------------------- /Resources/TabClose_Dirty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Dirty.png -------------------------------------------------------------------------------- /Resources/TabClose_Dirty@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Dirty@2x.png -------------------------------------------------------------------------------- /Resources/TabClose_Dirty_Pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Dirty_Pressed.png -------------------------------------------------------------------------------- /Resources/TabClose_Dirty_Pressed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Dirty_Pressed@2x.png -------------------------------------------------------------------------------- /Resources/TabClose_Dirty_Rollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Dirty_Rollover.png -------------------------------------------------------------------------------- /Resources/TabClose_Dirty_Rollover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Dirty_Rollover@2x.png -------------------------------------------------------------------------------- /Resources/TabClose_Front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Front.png -------------------------------------------------------------------------------- /Resources/TabClose_Front@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Front@2x.png -------------------------------------------------------------------------------- /Resources/TabClose_Front_Pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Front_Pressed.png -------------------------------------------------------------------------------- /Resources/TabClose_Front_Pressed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Front_Pressed@2x.png -------------------------------------------------------------------------------- /Resources/TabClose_Front_Rollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Front_Rollover.png -------------------------------------------------------------------------------- /Resources/TabClose_Front_Rollover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabClose_Front_Rollover@2x.png -------------------------------------------------------------------------------- /Resources/TabNewMetal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabNewMetal.png -------------------------------------------------------------------------------- /Resources/TabNewMetalPressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabNewMetalPressed.png -------------------------------------------------------------------------------- /Resources/TabNewMetalRollover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/TabNewMetalRollover.png -------------------------------------------------------------------------------- /Resources/largeImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/largeImage.png -------------------------------------------------------------------------------- /Resources/overflowImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/overflowImage.png -------------------------------------------------------------------------------- /Resources/overflowImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/overflowImage@2x.png -------------------------------------------------------------------------------- /Resources/overflowImagePressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/overflowImagePressed.png -------------------------------------------------------------------------------- /Resources/overflowImagePressed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/overflowImagePressed@2x.png -------------------------------------------------------------------------------- /Resources/pi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorianj/PSMTabBarControl/1c6eaaecb51ad8595678d6b6fc86a11b34a5279a/Resources/pi.png -------------------------------------------------------------------------------- /Source/DemoAppController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMTabBarControlDemoAppDelegate.h 3 | // PSMTabBarControl 4 | // 5 | // Created by Robert Payne on 4/26/10. 6 | // Copyright 2010 Zwopple. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface DemoAppController : NSObject 13 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 14 | 15 | #endif 16 | { 17 | } 18 | 19 | - (IBAction)newWindow:(id)pSender; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Source/DemoAppController.m: -------------------------------------------------------------------------------- 1 | #import "DemoAppController.h" 2 | #import "DemoWindowController.h" 3 | 4 | 5 | @implementation DemoAppController 6 | 7 | - (void)applicationDidFinishLaunching:(NSNotification *)pNotification { 8 | [self newWindow:self]; 9 | [self newWindow:self]; 10 | NSRect frontFrame = [[NSApp keyWindow] frame]; 11 | frontFrame.origin.x += 400; 12 | [[NSApp keyWindow] setFrame:frontFrame display:YES]; 13 | } 14 | - (IBAction)newWindow:(id)sender { 15 | // put up a window 16 | DemoWindowController *newWindow = [[DemoWindowController alloc] initWithWindowNibName:@"DemoWindow"]; 17 | [newWindow showWindow:self]; 18 | [newWindow addDefaultTabs]; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Source/DemoFakeModel.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 | @interface DemoFakeModel : NSObject { 12 | BOOL _isProcessing; 13 | NSImage *_icon; 14 | NSImage *_largeImage; 15 | NSString *_iconName; 16 | NSInteger _objectCount; 17 | BOOL _isEdited; 18 | } 19 | 20 | @property (retain) NSImage *largeImage; 21 | @property (retain) NSImage *icon; 22 | @property (retain) NSString *iconName; 23 | 24 | @property (assign) BOOL isProcessing; 25 | @property (assign) NSInteger objectCount; 26 | @property (assign) BOOL isEdited; 27 | 28 | // designated initializer 29 | - (id)init; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Source/DemoFakeModel.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 "DemoFakeModel.h" 10 | 11 | 12 | @implementation DemoFakeModel 13 | 14 | @synthesize largeImage = _largeImage; 15 | @synthesize icon = _icon; 16 | @synthesize iconName = _iconName; 17 | 18 | @synthesize isProcessing = _isProcessing; 19 | @synthesize objectCount = _objectCount; 20 | @synthesize isEdited = _isEdited; 21 | 22 | - (id)init { 23 | if((self = [super init])) { 24 | _isProcessing = NO; 25 | _icon = nil; 26 | _iconName = nil; 27 | _largeImage = nil; 28 | _objectCount = 2; 29 | _isEdited = NO; 30 | } 31 | return self; 32 | } 33 | 34 | -(void)dealloc { 35 | 36 | [_icon release], _icon = nil; 37 | [_iconName release], _iconName = nil; 38 | [_largeImage release], _largeImage = nil; 39 | 40 | [super dealloc]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Source/DemoWindowController.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 | 13 | @interface DemoWindowController : NSWindowController { 14 | IBOutlet NSTabView *tabView; 15 | IBOutlet NSTextField *tabField; 16 | IBOutlet NSDrawer *drawer; 17 | 18 | IBOutlet PSMTabBarControl *tabBar; 19 | 20 | IBOutlet NSButton *isProcessingButton; 21 | IBOutlet NSButton *isEditedButton; 22 | IBOutlet NSButton *hasLargeImageButton; 23 | IBOutlet NSTextField *objectCounterField; 24 | IBOutlet NSPopUpButton *iconButton; 25 | 26 | IBOutlet NSPopUpButton *popUp_style; 27 | IBOutlet NSPopUpButton *popUp_orientation; 28 | IBOutlet NSPopUpButton *popUp_tearOff; 29 | IBOutlet NSButton *button_canCloseOnlyTab; 30 | IBOutlet NSButton *button_disableTabClosing; 31 | IBOutlet NSButton *button_hideForSingleTab; 32 | IBOutlet NSButton *button_showAddTab; 33 | IBOutlet NSButton *button_useOverflow; 34 | IBOutlet NSButton *button_automaticallyAnimate; 35 | IBOutlet NSButton *button_allowScrubbing; 36 | IBOutlet NSButton *button_sizeToFit; 37 | IBOutlet NSTextField *textField_minWidth; 38 | IBOutlet NSTextField *textField_maxWidth; 39 | IBOutlet NSTextField *textField_optimumWidth; 40 | } 41 | 42 | - (void)addDefaultTabs; 43 | 44 | // UI 45 | - (IBAction)addNewTab:(id)sender; 46 | - (IBAction)closeTab:(id)sender; 47 | - (IBAction)stopProcessing:(id)sender; 48 | - (IBAction)setIconNamed:(id)sender; 49 | - (IBAction)setObjectCount:(id)sender; 50 | - (IBAction)setTabLabel:(id)sender; 51 | 52 | // Actions 53 | - (IBAction)isProcessingAction:(id)sender; 54 | - (IBAction)isEditedAction:(id)sender; 55 | - (IBAction)hasLargeImageAction:(id)sender; 56 | 57 | - (PSMTabBarControl *)tabBar; 58 | 59 | // delegate 60 | - (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem; 61 | - (BOOL)tabView:(NSTabView *)aTabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem; 62 | - (void)tabView:(NSTabView *)aTabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem; 63 | 64 | // toolbar 65 | - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag; 66 | - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar; 67 | - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar; 68 | - (IBAction)toggleToolbar:(id)sender; 69 | - (BOOL)validateToolbarItem:(NSToolbarItem *)theItem; 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Source/DemoWindowController.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 "DemoWindowController.h" 10 | #import "DemoFakeModel.h" 11 | #import "PSMTabBarControl.h" 12 | #import "PSMTabStyle.h" 13 | 14 | @interface DemoWindowController (PRIVATE) 15 | - (void)configureTabBarInitially; 16 | @end 17 | 18 | @interface DemoWindowController(ConfigActions) 19 | 20 | // tab bar config 21 | - (IBAction)configStyle:(id)sender; 22 | - (IBAction)configOrientation:(id)sender; 23 | - (IBAction)configCanCloseOnlyTab:(id)sender; 24 | - (IBAction)configDisableTabClose:(id)sender; 25 | - (IBAction)configHideForSingleTab:(id)sender; 26 | - (IBAction)configAddTabButton:(id)sender; 27 | - (IBAction)configTabMinWidth:(id)sender; 28 | - (IBAction)configTabMaxWidth:(id)sender; 29 | - (IBAction)configTabOptimumWidth:(id)sender; 30 | - (IBAction)configTabSizeToFit:(id)sender; 31 | - (IBAction)configTearOffStyle:(id)sender; 32 | - (IBAction)configUseOverflowMenu:(id)sender; 33 | - (IBAction)configAutomaticallyAnimates:(id)sender; 34 | - (IBAction)configAllowsScrubbing:(id)sender; 35 | 36 | @end 37 | 38 | @implementation DemoWindowController 39 | 40 | - (void)awakeFromNib { 41 | 42 | [super awakeFromNib]; 43 | 44 | [[NSUserDefaults standardUserDefaults] registerDefaults: 45 | [NSDictionary dictionaryWithObjectsAndKeys: 46 | @"Metal", @"Style", 47 | @"Horizontal", @"Orientation", 48 | @"Alpha Window", @"Tear-Off", 49 | @"100", @"TabMinWidth", 50 | @"280", @"TabMaxWidth", 51 | @"130", @"TabOptimalWidth", 52 | [NSNumber numberWithBool:YES], @"UseOverflowMenu", 53 | nil]]; 54 | 55 | // toolbar 56 | NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"DemoToolbar"]; 57 | [toolbar setDelegate:self]; 58 | [toolbar setAllowsUserCustomization:YES]; 59 | [toolbar setAutosavesConfiguration:YES]; 60 | SInt32 MacVersion; 61 | if(Gestalt(gestaltSystemVersion, &MacVersion) == noErr) { 62 | if(MacVersion >= 0x1040) { 63 | // this call is Tiger only 64 | [toolbar setShowsBaselineSeparator:NO]; 65 | } 66 | } 67 | [[self window] setToolbar:[toolbar autorelease]]; 68 | 69 | // hook up add tab button 70 | [[tabBar addTabButton] setTarget:self]; 71 | [[tabBar addTabButton] setAction:@selector(addNewTab:)]; 72 | 73 | // remove any tabs present in the nib 74 | for (NSTabViewItem *item in [tabView tabViewItems]) { 75 | [tabView removeTabViewItem:item]; 76 | } 77 | 78 | [self performSelector:@selector(configureTabBarInitially) 79 | withObject:nil 80 | afterDelay:0]; 81 | 82 | // open drawer 83 | //[drawer toggle:self]; 84 | } 85 | 86 | - (void)addDefaultTabs { 87 | [self addNewTab:self]; 88 | [self addNewTab:self]; 89 | [self addNewTab:self]; 90 | [[tabView tabViewItemAtIndex:0] setLabel:@"Tab"]; 91 | [[tabView tabViewItemAtIndex:1] setLabel:@"Bar"]; 92 | [[tabView tabViewItemAtIndex:2] setLabel:@"Control"]; 93 | } 94 | 95 | - (IBAction)addNewTab:(id)sender { 96 | DemoFakeModel *newModel = [[DemoFakeModel alloc] init]; 97 | NSTabViewItem *newItem = [[(NSTabViewItem*)[NSTabViewItem alloc] initWithIdentifier:newModel] autorelease]; 98 | [newItem setLabel:@"Untitled"]; 99 | [tabView addTabViewItem:newItem]; 100 | [tabView selectTabViewItem:newItem]; // this is optional, but expected behavior 101 | [newModel release]; 102 | } 103 | 104 | - (IBAction)closeTab:(id)sender { 105 | 106 | NSTabViewItem *tabViewItem = [tabView selectedTabViewItem]; 107 | 108 | if (([tabBar delegate]) && ([[tabBar delegate] respondsToSelector:@selector(tabView:shouldCloseTabViewItem:)])) { 109 | if(![[tabBar delegate] tabView:tabView shouldCloseTabViewItem:tabViewItem]) { 110 | return; 111 | } 112 | } 113 | 114 | if (([tabBar delegate]) && ([[tabBar delegate] respondsToSelector:@selector(tabView:willCloseTabViewItem:)])) { 115 | [[tabBar delegate] tabView:tabView willCloseTabViewItem:tabViewItem]; 116 | } 117 | 118 | [tabView removeTabViewItem:[[tabViewItem retain] autorelease]]; 119 | 120 | if (([tabBar delegate]) && ([[tabBar delegate] respondsToSelector:@selector(tabView:didCloseTabViewItem:)])) { 121 | [[tabBar delegate] tabView:tabView didCloseTabViewItem:tabViewItem]; 122 | } 123 | } 124 | 125 | - (void)stopProcessing:(id)sender { 126 | [[[tabView selectedTabViewItem] identifier] setValue:[NSNumber numberWithBool:NO] forKeyPath:@"isProcessing"]; 127 | } 128 | 129 | - (void)setIconNamed:(id)sender { 130 | NSString *iconName = [sender titleOfSelectedItem]; 131 | if([iconName isEqualToString:@"None"]) { 132 | [[[tabView selectedTabViewItem] identifier] setValue:nil forKeyPath:@"icon"]; 133 | [[[tabView selectedTabViewItem] identifier] setValue:@"None" forKeyPath:@"iconName"]; 134 | } else { 135 | NSImage *newIcon = [NSImage imageNamed:iconName]; 136 | [[[tabView selectedTabViewItem] identifier] setValue:newIcon forKeyPath:@"icon"]; 137 | [[[tabView selectedTabViewItem] identifier] setValue:iconName forKeyPath:@"iconName"]; 138 | } 139 | } 140 | 141 | - (void)setObjectCount:(id)sender { 142 | [[[tabView selectedTabViewItem] identifier] setValue:[NSNumber numberWithInteger:[sender integerValue]] forKeyPath:@"objectCount"]; 143 | } 144 | 145 | - (IBAction)isProcessingAction:(id)sender { 146 | [[[tabView selectedTabViewItem] identifier] setValue:[NSNumber numberWithBool:[sender state]] forKeyPath:@"isProcessing"]; 147 | } 148 | 149 | - (IBAction)isEditedAction:(id)sender { 150 | [[[tabView selectedTabViewItem] identifier] setValue:[NSNumber numberWithBool:[sender state]] forKeyPath:@"isEdited"]; 151 | } 152 | 153 | - (IBAction)hasLargeImageAction:(id)sender { 154 | 155 | if ([sender state] == NSOnState) { 156 | [[[tabView selectedTabViewItem] identifier] setValue:[NSImage imageNamed:@"largeImage"] forKeyPath:@"largeImage"]; 157 | } else { 158 | [[[tabView selectedTabViewItem] identifier] setValue:nil forKeyPath:@"largeImage"]; 159 | } 160 | } 161 | 162 | - (IBAction)setTabLabel:(id)sender { 163 | [[tabView selectedTabViewItem] setLabel:[sender stringValue]]; 164 | } 165 | 166 | - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { 167 | if([menuItem action] == @selector(closeTab:)) { 168 | if(![tabBar canCloseOnlyTab] && ([tabView numberOfTabViewItems] <= 1)) { 169 | return NO; 170 | } 171 | } 172 | return YES; 173 | } 174 | 175 | - (PSMTabBarControl *)tabBar { 176 | return tabBar; 177 | } 178 | 179 | - (void)windowWillClose:(NSNotification *)note { 180 | [self autorelease]; 181 | } 182 | 183 | #pragma mark - 184 | #pragma mark ---- tab bar config ---- 185 | 186 | - (void)configStyle:(id)sender { 187 | [tabBar setStyleNamed:[sender titleOfSelectedItem]]; 188 | 189 | [[NSUserDefaults standardUserDefaults] setObject:[sender titleOfSelectedItem] 190 | forKey:@"Style"]; 191 | } 192 | 193 | - (void)configOrientation:(id)sender { 194 | PSMTabBarOrientation orientation = ([sender indexOfSelectedItem] == 0) ? PSMTabBarHorizontalOrientation : PSMTabBarVerticalOrientation; 195 | 196 | if(orientation == [tabBar orientation]) { 197 | return; 198 | } 199 | 200 | //change the frame of the tab bar according to the orientation 201 | NSRect tabBarFrame = [tabBar frame], tabViewFrame = [tabView frame]; 202 | NSRect totalFrame = NSUnionRect(tabBarFrame, tabViewFrame); 203 | 204 | if(orientation == PSMTabBarHorizontalOrientation) { 205 | tabBarFrame.size.height = [tabBar isTabBarHidden] ? 1 : 22; 206 | tabBarFrame.size.width = totalFrame.size.width; 207 | tabBarFrame.origin.y = totalFrame.origin.y + totalFrame.size.height - tabBarFrame.size.height; 208 | tabViewFrame.origin.x = 13; 209 | tabViewFrame.size.width = totalFrame.size.width - 23; 210 | tabViewFrame.size.height = totalFrame.size.height - tabBarFrame.size.height - 2; 211 | [tabBar setAutoresizingMask:NSViewMinYMargin | NSViewWidthSizable]; 212 | } else { 213 | tabBarFrame.size.height = totalFrame.size.height; 214 | tabBarFrame.size.width = [tabBar isTabBarHidden] ? 1 : 120; 215 | tabBarFrame.origin.y = totalFrame.origin.y; 216 | tabViewFrame.origin.x = tabBarFrame.origin.x + tabBarFrame.size.width; 217 | tabViewFrame.size.width = totalFrame.size.width - tabBarFrame.size.width; 218 | tabViewFrame.size.height = totalFrame.size.height; 219 | [tabBar setAutoresizingMask:NSViewHeightSizable]; 220 | } 221 | 222 | tabBarFrame.origin.x = totalFrame.origin.x; 223 | tabViewFrame.origin.y = totalFrame.origin.y; 224 | 225 | [tabView setFrame:tabViewFrame]; 226 | [tabBar setFrame:tabBarFrame]; 227 | 228 | [tabBar setOrientation:orientation]; 229 | [[self window] display]; 230 | 231 | [[NSUserDefaults standardUserDefaults] setObject:[sender title] 232 | forKey:@"Orientation"]; 233 | } 234 | 235 | - (void)configCanCloseOnlyTab:(id)sender { 236 | [tabBar setCanCloseOnlyTab:[sender state]]; 237 | 238 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender state]] 239 | forKey:@"CanCloseOnlyTab"]; 240 | } 241 | 242 | - (void)configDisableTabClose:(id)sender { 243 | [tabBar setDisableTabClose:[sender state]]; 244 | 245 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender state]] 246 | forKey:@"DisableTabClose"]; 247 | } 248 | 249 | - (void)configHideForSingleTab:(id)sender { 250 | [tabBar setHideForSingleTab:[sender state]]; 251 | 252 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender state]] 253 | forKey:@"HideForSingleTab"]; 254 | } 255 | 256 | - (void)configAddTabButton:(id)sender { 257 | [tabBar setShowAddTabButton:[sender state]]; 258 | 259 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender state]] 260 | forKey:@"ShowAddTabButton"]; 261 | } 262 | 263 | - (void)configTabMinWidth:(id)sender { 264 | if([tabBar cellOptimumWidth] < [sender integerValue]) { 265 | [tabBar setCellMinWidth:[tabBar cellOptimumWidth]]; 266 | [sender setIntegerValue:[tabBar cellOptimumWidth]]; 267 | return; 268 | } 269 | 270 | [tabBar setCellMinWidth:[sender integerValue]]; 271 | 272 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:[sender integerValue]] 273 | forKey:@"TabMinWidth"]; 274 | } 275 | 276 | - (void)configTabMaxWidth:(id)sender { 277 | if([tabBar cellOptimumWidth] > [sender integerValue]) { 278 | [tabBar setCellMaxWidth:[tabBar cellOptimumWidth]]; 279 | [sender setIntegerValue:[tabBar cellOptimumWidth]]; 280 | return; 281 | } 282 | 283 | [tabBar setCellMaxWidth:[sender integerValue]]; 284 | 285 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInteger:[sender integerValue]] 286 | forKey:@"TabMaxWidth"]; 287 | } 288 | 289 | - (void)configTabOptimumWidth:(id)sender { 290 | if([tabBar cellMaxWidth] < [sender integerValue]) { 291 | [tabBar setCellOptimumWidth:[tabBar cellMaxWidth]]; 292 | [sender setIntegerValue:[tabBar cellMaxWidth]]; 293 | return; 294 | } 295 | 296 | if([tabBar cellMinWidth] > [sender integerValue]) { 297 | [tabBar setCellOptimumWidth:[tabBar cellMinWidth]]; 298 | [sender setIntegerValue:[tabBar cellMinWidth]]; 299 | return; 300 | } 301 | 302 | [tabBar setCellOptimumWidth:[sender integerValue]]; 303 | } 304 | 305 | - (void)configTabSizeToFit:(id)sender { 306 | [tabBar setSizeCellsToFit:[sender state]]; 307 | 308 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender integerValue]] 309 | forKey:@"SizeToFit"]; 310 | } 311 | 312 | - (void)configTearOffStyle:(id)sender { 313 | [tabBar setTearOffStyle:([sender indexOfSelectedItem] == 0) ? PSMTabBarTearOffAlphaWindow : PSMTabBarTearOffMiniwindow]; 314 | 315 | [[NSUserDefaults standardUserDefaults] setObject:[sender title] 316 | forKey:@"Tear-Off"]; 317 | } 318 | 319 | - (void)configUseOverflowMenu:(id)sender { 320 | [tabBar setUseOverflowMenu:[sender state]]; 321 | 322 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender integerValue]] 323 | forKey:@"UseOverflowMenu"]; 324 | } 325 | 326 | - (void)configAutomaticallyAnimates:(id)sender { 327 | [tabBar setAutomaticallyAnimates:[sender state]]; 328 | 329 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender integerValue]] 330 | forKey:@"AutomaticallyAnimates"]; 331 | } 332 | 333 | - (void)configAllowsScrubbing:(id)sender { 334 | [tabBar setAllowsScrubbing:[sender state]]; 335 | 336 | [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:[sender integerValue]] 337 | forKey:@"AllowScrubbing"]; 338 | } 339 | 340 | #pragma mark - 341 | #pragma mark ---- delegate ---- 342 | 343 | - (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem { 344 | // need to update bound values to match the selected tab 345 | if([[tabViewItem identifier] respondsToSelector:@selector(objectCount)]) { 346 | [objectCounterField setIntegerValue:[[tabViewItem identifier] objectCount]]; 347 | } 348 | 349 | if([[tabViewItem identifier] respondsToSelector:@selector(isProcessing)]) { 350 | [isProcessingButton setState:[[tabViewItem identifier] isProcessing]]; 351 | } 352 | 353 | if([[tabViewItem identifier] respondsToSelector:@selector(isEdited)]) { 354 | [isEditedButton setState:[[tabViewItem identifier] isEdited]]; 355 | } 356 | 357 | if([[tabViewItem identifier] respondsToSelector:@selector(largeImage)]) { 358 | [hasLargeImageButton setState:[[tabViewItem identifier] largeImage] != nil]; 359 | } 360 | 361 | if([[tabViewItem identifier] respondsToSelector:@selector(iconName)]) { 362 | NSString *newName = [[tabViewItem identifier] iconName]; 363 | if(newName) { 364 | [iconButton selectItem:[[iconButton menu] itemWithTitle:newName]]; 365 | } else { 366 | [iconButton selectItem:[[iconButton menu] itemWithTitle:@"None"]]; 367 | } 368 | } 369 | } 370 | 371 | - (BOOL)tabView:(NSTabView *)aTabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem { 372 | if([[tabViewItem label] isEqualToString:@"Drake"]) { 373 | NSAlert *drakeAlert = [NSAlert alertWithMessageText:@"No Way!" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"I refuse to close a tab named \"Drake\""]; 374 | [drakeAlert beginSheetModalForWindow:[NSApp keyWindow] modalDelegate:nil didEndSelector:nil contextInfo:nil]; 375 | return NO; 376 | } 377 | return YES; 378 | } 379 | 380 | - (void)tabView:(NSTabView *)aTabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem { 381 | NSLog(@"didCloseTabViewItem: %@", [tabViewItem label]); 382 | } 383 | 384 | - (NSArray *)allowedDraggedTypesForTabView:(NSTabView *)aTabView { 385 | return [NSArray arrayWithObjects:NSFilenamesPboardType, NSStringPboardType, nil]; 386 | } 387 | 388 | - (void)tabView:(NSTabView *)aTabView acceptedDraggingInfo:(id )draggingInfo onTabViewItem:(NSTabViewItem *)tabViewItem { 389 | NSLog(@"acceptedDraggingInfo: %@ onTabViewItem: %@", [[draggingInfo draggingPasteboard] stringForType:[[[draggingInfo draggingPasteboard] types] objectAtIndex:0]], [tabViewItem label]); 390 | } 391 | 392 | - (NSMenu *)tabView:(NSTabView *)aTabView menuForTabViewItem:(NSTabViewItem *)tabViewItem { 393 | NSLog(@"menuForTabViewItem: %@", [tabViewItem label]); 394 | return nil; 395 | } 396 | 397 | - (BOOL)tabView:(NSTabView*)aTabView shouldDragTabViewItem:(NSTabViewItem *)tabViewItem fromTabBar:(PSMTabBarControl *)tabBarControl { 398 | return YES; 399 | } 400 | 401 | - (BOOL)tabView:(NSTabView*)aTabView shouldDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl { 402 | return YES; 403 | } 404 | 405 | - (void)tabView:(NSTabView*)aTabView didDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl { 406 | NSLog(@"didDropTabViewItem: %@ inTabBar: %@", [tabViewItem label], tabBarControl); 407 | } 408 | 409 | - (NSImage *)tabView:(NSTabView *)aTabView imageForTabViewItem:(NSTabViewItem *)tabViewItem offset:(NSSize *)offset styleMask:(NSUInteger *)styleMask { 410 | // grabs whole window image 411 | NSImage *viewImage = [[[NSImage alloc] init] autorelease]; 412 | NSRect contentFrame = [[[self window] contentView] frame]; 413 | [[[self window] contentView] lockFocus]; 414 | NSBitmapImageRep *viewRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:contentFrame] autorelease]; 415 | [viewImage addRepresentation:viewRep]; 416 | [[[self window] contentView] unlockFocus]; 417 | 418 | // grabs snapshot of dragged tabViewItem's view (represents content being dragged) 419 | NSView *viewForImage = [tabViewItem view]; 420 | NSRect viewRect = [viewForImage frame]; 421 | NSImage *tabViewImage = [[[NSImage alloc] initWithSize:viewRect.size] autorelease]; 422 | [tabViewImage lockFocus]; 423 | [viewForImage drawRect:[viewForImage bounds]]; 424 | [tabViewImage unlockFocus]; 425 | 426 | [viewImage lockFocus]; 427 | NSPoint tabOrigin = [tabView frame].origin; 428 | tabOrigin.x += 10; 429 | tabOrigin.y += 13; 430 | [tabViewImage drawAtPoint:tabOrigin fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; 431 | // [tabViewImage compositeToPoint:tabOrigin operation:NSCompositeSourceOver]; 432 | [viewImage unlockFocus]; 433 | 434 | PSMTabBarControl *tabBarControl = (PSMTabBarControl*)[aTabView delegate]; 435 | 436 | //draw over where the tab bar would usually be 437 | NSRect tabFrame = [tabBar frame]; 438 | [viewImage lockFocus]; 439 | [[NSColor windowBackgroundColor] set]; 440 | NSRectFill(tabFrame); 441 | //draw the background flipped, which is actually the right way up 442 | NSAffineTransform *transform = [NSAffineTransform transform]; 443 | [transform scaleXBy:1.0 yBy:-1.0]; 444 | [transform concat]; 445 | tabFrame.origin.y = -tabFrame.origin.y - tabFrame.size.height; 446 | [[tabBarControl style] drawBezelOfTabBarControl:tabBarControl inRect:tabFrame]; 447 | [transform invert]; 448 | [transform concat]; 449 | 450 | [viewImage unlockFocus]; 451 | 452 | if([tabBarControl orientation] == PSMTabBarHorizontalOrientation) { 453 | offset->width = [(id < PSMTabStyle >)[tabBarControl style] leftMarginForTabBarControl:tabBarControl]; 454 | offset->height = 22; 455 | } else { 456 | offset->width = 0; 457 | offset->height = 22 + [(id < PSMTabStyle >)[tabBarControl style] leftMarginForTabBarControl:tabBarControl]; 458 | } 459 | 460 | if(styleMask) { 461 | *styleMask = NSTitledWindowMask | NSTexturedBackgroundWindowMask; 462 | } 463 | 464 | return viewImage; 465 | } 466 | 467 | - (PSMTabBarControl *)tabView:(NSTabView *)aTabView newTabBarForDraggedTabViewItem:(NSTabViewItem *)tabViewItem atPoint:(NSPoint)point { 468 | NSLog(@"newTabBarForDraggedTabViewItem: %@ atPoint: %@", [tabViewItem label], NSStringFromPoint(point)); 469 | 470 | //create a new window controller with no tab items 471 | DemoWindowController *controller = [[DemoWindowController alloc] initWithWindowNibName:@"DemoWindow"]; 472 | 473 | PSMTabBarControl *tabBarControl = (PSMTabBarControl*)[aTabView delegate]; 474 | 475 | id style = (id )[tabBarControl style]; 476 | 477 | NSRect windowFrame = [[controller window] frame]; 478 | point.y += windowFrame.size.height - [[[controller window] contentView] frame].size.height; 479 | point.x -= [style leftMarginForTabBarControl:tabBarControl]; 480 | 481 | [[controller window] setFrameTopLeftPoint:point]; 482 | [[controller tabBar] setStyle:style]; 483 | 484 | return [controller tabBar]; 485 | } 486 | 487 | - (void)tabView:(NSTabView *)aTabView closeWindowForLastTabViewItem:(NSTabViewItem *)tabViewItem { 488 | NSLog(@"closeWindowForLastTabViewItem: %@", [tabViewItem label]); 489 | [[self window] close]; 490 | } 491 | 492 | - (void)tabView:(NSTabView *)aTabView tabBarDidHide:(PSMTabBarControl *)tabBarControl { 493 | NSLog(@"tabBarDidHide: %@", tabBarControl); 494 | } 495 | 496 | - (void)tabView:(NSTabView *)aTabView tabBarDidUnhide:(PSMTabBarControl *)tabBarControl { 497 | NSLog(@"tabBarDidUnhide: %@", tabBarControl); 498 | } 499 | 500 | - (NSString *)tabView:(NSTabView *)aTabView toolTipForTabViewItem:(NSTabViewItem *)tabViewItem { 501 | return [tabViewItem label]; 502 | } 503 | 504 | - (NSString *)accessibilityStringForTabView:(NSTabView *)aTabView objectCount:(NSInteger)objectCount { 505 | return (objectCount == 1) ? @"item" : @"items"; 506 | } 507 | 508 | #pragma mark - 509 | #pragma mark ---- toolbar ---- 510 | 511 | - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag { 512 | NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier]; 513 | 514 | if([itemIdentifier isEqualToString:@"TabField"]) { 515 | [item setPaletteLabel:@"Tab Label"]; 516 | [item setLabel:@"Tab Label"]; 517 | [item setView:tabField]; 518 | [item setMinSize:NSMakeSize(100, [tabField frame].size.height)]; 519 | [item setMaxSize:NSMakeSize(500, [tabField frame].size.height)]; 520 | } else if([itemIdentifier isEqualToString:@"DrawerItem"]) { 521 | [item setPaletteLabel:@"Configuration"]; 522 | [item setLabel:@"Configuration"]; 523 | [item setToolTip:@"Configuration"]; 524 | [item setImage:[NSImage imageNamed:NSImageNamePreferencesGeneral]]; 525 | [item setTarget:drawer]; 526 | [item setAction:@selector(toggle:)]; 527 | } 528 | 529 | return [item autorelease]; 530 | } 531 | 532 | - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar { 533 | return [NSArray arrayWithObjects:@"TabField", 534 | NSToolbarFlexibleSpaceItemIdentifier, 535 | @"DrawerItem", 536 | nil]; 537 | } 538 | 539 | - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar { 540 | return [NSArray arrayWithObjects:@"TabField", 541 | NSToolbarFlexibleSpaceItemIdentifier, 542 | @"DrawerItem", 543 | nil]; 544 | } 545 | 546 | - (IBAction)toggleToolbar:(id)sender { 547 | [[[self window] toolbar] setVisible:![[[self window] toolbar] isVisible]]; 548 | } 549 | 550 | - (BOOL)validateToolbarItem:(NSToolbarItem *)theItem { 551 | return YES; 552 | } 553 | 554 | 555 | - (void)configureTabBarInitially { 556 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 557 | [popUp_style selectItemWithTitle:[defaults stringForKey:@"Style"]]; 558 | [popUp_orientation selectItemWithTitle:[defaults stringForKey:@"Orientation"]]; 559 | [popUp_tearOff selectItemWithTitle:[defaults stringForKey:@"Tear-Off"]]; 560 | 561 | [button_canCloseOnlyTab setState:[defaults boolForKey:@"CanCloseOnlyTab"]]; 562 | [button_disableTabClosing setState:[defaults boolForKey:@"DisableTabClosing"]]; 563 | [button_hideForSingleTab setState:[defaults boolForKey:@"HideForSingleTab"]]; 564 | [button_showAddTab setState:[defaults boolForKey:@"ShowAddTabButton"]]; 565 | [button_sizeToFit setState:[defaults boolForKey:@"SizeToFit"]]; 566 | [button_useOverflow setState:[defaults boolForKey:@"UseOverflowMenu"]]; 567 | [button_automaticallyAnimate setState:[defaults boolForKey:@"AutomaticallyAnimates"]]; 568 | [button_allowScrubbing setState:[defaults boolForKey:@"AllowScrubbing"]]; 569 | 570 | [self configStyle:popUp_style]; 571 | [self configOrientation:popUp_orientation]; 572 | [self configCanCloseOnlyTab:button_canCloseOnlyTab]; 573 | [self configDisableTabClose:button_disableTabClosing]; 574 | [self configHideForSingleTab:button_hideForSingleTab]; 575 | [self configAddTabButton:button_showAddTab]; 576 | [self configTabMinWidth:textField_minWidth]; 577 | [self configTabMaxWidth:textField_maxWidth]; 578 | [self configTabOptimumWidth:textField_optimumWidth]; 579 | [self configTabSizeToFit:button_sizeToFit]; 580 | [self configTearOffStyle:popUp_tearOff]; 581 | [self configUseOverflowMenu:button_useOverflow]; 582 | [self configAutomaticallyAnimates:button_automaticallyAnimate]; 583 | [self configAllowsScrubbing:button_allowScrubbing]; 584 | } 585 | @end 586 | -------------------------------------------------------------------------------- /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 | return [NSString stringWithUTF8String:"\xE2\x80\xA6"]; 16 | } 17 | 18 | - (NSString *)stringWithEllipsisByTruncatingToLength:(NSUInteger)length { 19 | NSString *returnString; 20 | 21 | if(length < [self length]) { 22 | //Truncate and append the ellipsis 23 | returnString = [[self substringToIndex:length - 1] stringByAppendingString:[NSString ellipsis]]; 24 | } else { 25 | //We don't need to truncate, so don't append an ellipsis 26 | returnString = [[self copy] autorelease]; 27 | } 28 | 29 | return returnString; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Source/PSMAdiumTabStyle.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMAdiumTabStyle.h 3 | // PSMTabBarControl 4 | // 5 | // Created by Kent Sutherland on 5/26/06. 6 | // Copyright 2006 Kent Sutherland. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PSMTabStyle.h" 11 | 12 | @interface PSMAdiumTabStyle : NSObject 13 | { 14 | NSImage *_closeButton; 15 | NSImage *_closeButtonDown; 16 | NSImage *_closeButtonOver; 17 | NSImage *_closeDirtyButton; 18 | NSImage *_closeDirtyButtonDown; 19 | NSImage *_closeDirtyButtonOver; 20 | NSImage *_addTabButtonImage; 21 | NSImage *_addTabButtonPressedImage; 22 | NSImage *_addTabButtonRolloverImage; 23 | NSImage *_gradientImage; 24 | 25 | BOOL _drawsUnified; 26 | BOOL _drawsRight; 27 | } 28 | 29 | - (void)loadImages; 30 | 31 | - (BOOL)drawsUnified; 32 | - (void)setDrawsUnified:(BOOL)value; 33 | - (BOOL)drawsRight; 34 | - (void)setDrawsRight:(BOOL)value; 35 | 36 | - (void)encodeWithCoder:(NSCoder *)aCoder; 37 | - (id)initWithCoder:(NSCoder *)aDecoder; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Source/PSMAquaTabStyle.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMAquaTabStyle.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 PSMAquaTabStyle : NSObject { 13 | NSImage *aquaTabBg; 14 | NSImage *aquaTabBgDown; 15 | NSImage *aquaTabBgDownGraphite; 16 | NSImage *aquaTabBgDownNonKey; 17 | NSImage *aquaDividerDown; 18 | NSImage *aquaDivider; 19 | NSImage *aquaCloseButton; 20 | NSImage *aquaCloseButtonDown; 21 | NSImage *aquaCloseButtonOver; 22 | NSImage *aquaCloseDirtyButton; 23 | NSImage *aquaCloseDirtyButtonDown; 24 | NSImage *aquaCloseDirtyButtonOver; 25 | NSImage *_addTabButtonImage; 26 | NSImage *_addTabButtonPressedImage; 27 | NSImage *_addTabButtonRolloverImage; 28 | } 29 | 30 | - (void)loadImages; 31 | 32 | - (void)encodeWithCoder:(NSCoder *)aCoder; 33 | - (id)initWithCoder:(NSCoder *)aDecoder; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Source/PSMAquaTabStyle.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMAquaTabStyle.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 "PSMAquaTabStyle.h" 10 | #import "PSMTabBarCell.h" 11 | #import "PSMTabBarControl.h" 12 | 13 | @implementation PSMAquaTabStyle 14 | 15 | + (NSString *)name { 16 | return @"Aqua"; 17 | } 18 | 19 | - (NSString *)name { 20 | return [[self class] name]; 21 | } 22 | 23 | #pragma mark - 24 | #pragma mark Creation/Destruction 25 | 26 | - (id) init { 27 | if((self = [super init])) { 28 | [self loadImages]; 29 | } 30 | return self; 31 | } 32 | 33 | - (void) loadImages { 34 | // Aqua Tabs Images 35 | aquaTabBg = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabsBackground"]]; 36 | [aquaTabBg setFlipped:YES]; 37 | 38 | aquaTabBgDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabsDown"]]; 39 | [aquaTabBgDown setFlipped:YES]; 40 | 41 | aquaTabBgDownGraphite = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabsDownGraphite"]]; 42 | [aquaTabBgDown setFlipped:YES]; 43 | 44 | aquaTabBgDownNonKey = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabsDownNonKey"]]; 45 | [aquaTabBgDown setFlipped:YES]; 46 | 47 | aquaDividerDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabsSeparatorDown"]]; 48 | [aquaDivider setFlipped:NO]; 49 | 50 | aquaDivider = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabsSeparator"]]; 51 | [aquaDivider setFlipped:NO]; 52 | 53 | aquaCloseButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front"]]; 54 | aquaCloseButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Pressed"]]; 55 | aquaCloseButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Rollover"]]; 56 | 57 | aquaCloseDirtyButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front"]]; 58 | aquaCloseDirtyButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Pressed"]]; 59 | aquaCloseDirtyButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Rollover"]]; 60 | 61 | _addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNew"]]; 62 | _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewPressed"]]; 63 | _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewRollover"]]; 64 | } 65 | 66 | - (void)dealloc { 67 | [aquaTabBg release]; 68 | [aquaTabBgDown release]; 69 | [aquaDividerDown release]; 70 | [aquaDivider release]; 71 | [aquaCloseButton release]; 72 | [aquaCloseButtonDown release]; 73 | [aquaCloseButtonOver release]; 74 | [aquaCloseDirtyButton release]; 75 | [aquaCloseDirtyButtonDown release]; 76 | [aquaCloseDirtyButtonOver release]; 77 | [_addTabButtonImage release]; 78 | [_addTabButtonPressedImage release]; 79 | [_addTabButtonRolloverImage release]; 80 | 81 | [super dealloc]; 82 | } 83 | 84 | #pragma mark - 85 | #pragma mark Control Specifics 86 | 87 | - (CGFloat)leftMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 88 | return 0.0f; 89 | } 90 | 91 | - (CGFloat)rightMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 92 | return 0.0f; 93 | } 94 | 95 | - (CGFloat)topMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 96 | return 0.0f; 97 | } 98 | 99 | #pragma mark - 100 | #pragma mark Add Tab Button 101 | 102 | - (NSImage *)addTabButtonImage { 103 | return _addTabButtonImage; 104 | } 105 | 106 | - (NSImage *)addTabButtonPressedImage { 107 | return _addTabButtonPressedImage; 108 | } 109 | 110 | - (NSImage *)addTabButtonRolloverImage { 111 | return _addTabButtonRolloverImage; 112 | } 113 | 114 | #pragma mark - 115 | #pragma mark Providing Images 116 | 117 | - (NSImage *)closeButtonImageOfType:(PSMCloseButtonImageType)type forTabCell:(PSMTabBarCell *)cell 118 | { 119 | switch (type) { 120 | case PSMCloseButtonImageTypeStandard: 121 | return aquaCloseButton; 122 | case PSMCloseButtonImageTypeRollover: 123 | return aquaCloseButtonOver; 124 | case PSMCloseButtonImageTypePressed: 125 | return aquaCloseButtonDown; 126 | 127 | case PSMCloseButtonImageTypeDirty: 128 | return aquaCloseDirtyButton; 129 | case PSMCloseButtonImageTypeDirtyRollover: 130 | return aquaCloseDirtyButtonOver; 131 | case PSMCloseButtonImageTypeDirtyPressed: 132 | return aquaCloseDirtyButtonDown; 133 | 134 | default: 135 | break; 136 | } 137 | 138 | } 139 | 140 | #pragma mark - 141 | #pragma mark Drawing 142 | 143 | - (void)drawBezelOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl { 144 | 145 | NSRect cellFrame = frame; 146 | 147 | // Selected Tab 148 | if([cell state] == NSOnState) { 149 | NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height - 2.5); 150 | aRect.size.height -= 0.5; 151 | 152 | // proper tint 153 | NSControlTint currentTint; 154 | if([cell controlTint] == NSDefaultControlTint) { 155 | currentTint = [NSColor currentControlTint]; 156 | } else{ 157 | currentTint = [cell controlTint]; 158 | } 159 | 160 | if(![tabBarControl isWindowActive]) { 161 | currentTint = NSClearControlTint; 162 | } 163 | 164 | NSImage *bgImage; 165 | switch(currentTint) { 166 | case NSGraphiteControlTint: 167 | bgImage = aquaTabBgDownGraphite; 168 | break; 169 | case NSClearControlTint: 170 | bgImage = aquaTabBgDownNonKey; 171 | break; 172 | case NSBlueControlTint: 173 | default: 174 | bgImage = aquaTabBgDown; 175 | break; 176 | } 177 | 178 | [bgImage drawInRect:cellFrame fromRect:NSMakeRect(0.0, 0.0, 1.0, 22.0) operation:NSCompositeSourceOver fraction:1.0 respectFlipped:NO hints:nil]; 179 | 180 | [aquaDivider drawAtPoint:NSMakePoint(cellFrame.origin.x + cellFrame.size.width - 1.0, cellFrame.origin.y) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; 181 | 182 | aRect.size.height += 0.5; 183 | } else { // Unselected Tab 184 | NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height); 185 | aRect.origin.y += 0.5; 186 | aRect.origin.x += 1.5; 187 | aRect.size.width -= 1; 188 | 189 | aRect.origin.x -= 1; 190 | aRect.size.width += 1; 191 | 192 | // Rollover 193 | if([cell isHighlighted]) { 194 | [[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] set]; 195 | NSRectFillUsingOperation(aRect, NSCompositeSourceAtop); 196 | } 197 | 198 | [aquaDivider drawAtPoint:NSMakePoint(cellFrame.origin.x + cellFrame.size.width - 1.0, cellFrame.origin.y) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; 199 | } 200 | } 201 | 202 | - (void)drawBezelOfTabBarControl:(PSMTabBarControl *)tabBarControl inRect:(NSRect)rect { 203 | if(rect.size.height <= 22.0) { 204 | //Draw for our whole bounds; it'll be automatically clipped to fit the appropriate drawing area 205 | rect = [tabBarControl bounds]; 206 | 207 | [aquaTabBg drawInRect:rect fromRect:NSMakeRect(0.0, 0.0, 1.0, 22.0) operation:NSCompositeSourceOver fraction:1.0 respectFlipped:NO hints:nil]; 208 | } 209 | } 210 | 211 | #pragma mark - 212 | #pragma mark Archiving 213 | 214 | - (void)encodeWithCoder:(NSCoder *)aCoder { 215 | //[super encodeWithCoder:aCoder]; 216 | if([aCoder allowsKeyedCoding]) { 217 | [aCoder encodeObject:aquaTabBg forKey:@"aquaTabBg"]; 218 | [aCoder encodeObject:aquaTabBgDown forKey:@"aquaTabBgDown"]; 219 | [aCoder encodeObject:aquaTabBgDownGraphite forKey:@"aquaTabBgDownGraphite"]; 220 | [aCoder encodeObject:aquaTabBgDownNonKey forKey:@"aquaTabBgDownNonKey"]; 221 | [aCoder encodeObject:aquaDividerDown forKey:@"aquaDividerDown"]; 222 | [aCoder encodeObject:aquaDivider forKey:@"aquaDivider"]; 223 | [aCoder encodeObject:aquaCloseButton forKey:@"aquaCloseButton"]; 224 | [aCoder encodeObject:aquaCloseButtonDown forKey:@"aquaCloseButtonDown"]; 225 | [aCoder encodeObject:aquaCloseButtonOver forKey:@"aquaCloseButtonOver"]; 226 | [aCoder encodeObject:aquaCloseDirtyButton forKey:@"aquaCloseDirtyButton"]; 227 | [aCoder encodeObject:aquaCloseDirtyButtonDown forKey:@"aquaCloseDirtyButtonDown"]; 228 | [aCoder encodeObject:aquaCloseDirtyButtonOver forKey:@"aquaCloseDirtyButtonOver"]; 229 | [aCoder encodeObject:_addTabButtonImage forKey:@"addTabButtonImage"]; 230 | [aCoder encodeObject:_addTabButtonPressedImage forKey:@"addTabButtonPressedImage"]; 231 | [aCoder encodeObject:_addTabButtonRolloverImage forKey:@"addTabButtonRolloverImage"]; 232 | } 233 | } 234 | 235 | - (id)initWithCoder:(NSCoder *)aDecoder { 236 | //self = [super initWithCoder:aDecoder]; 237 | //if (self) { 238 | if([aDecoder allowsKeyedCoding]) { 239 | aquaTabBg = [[aDecoder decodeObjectForKey:@"aquaTabBg"] retain]; 240 | aquaTabBgDown = [[aDecoder decodeObjectForKey:@"aquaTabBgDown"] retain]; 241 | aquaTabBgDownGraphite = [[aDecoder decodeObjectForKey:@"aquaTabBgDownGraphite"] retain]; 242 | aquaTabBgDownNonKey = [[aDecoder decodeObjectForKey:@"aquaTabBgDownNonKey"] retain]; 243 | aquaDividerDown = [[aDecoder decodeObjectForKey:@"aquaDividerDown"] retain]; 244 | aquaDivider = [[aDecoder decodeObjectForKey:@"aquaDivider"] retain]; 245 | aquaCloseButton = [[aDecoder decodeObjectForKey:@"aquaCloseButton"] retain]; 246 | aquaCloseButtonDown = [[aDecoder decodeObjectForKey:@"aquaCloseButtonDown"] retain]; 247 | aquaCloseButtonOver = [[aDecoder decodeObjectForKey:@"aquaCloseButtonOver"] retain]; 248 | aquaCloseDirtyButton = [[aDecoder decodeObjectForKey:@"aquaCloseDirtyButton"] retain]; 249 | aquaCloseDirtyButtonDown = [[aDecoder decodeObjectForKey:@"aquaCloseDirtyButtonDown"] retain]; 250 | aquaCloseDirtyButtonOver = [[aDecoder decodeObjectForKey:@"aquaCloseDirtyButtonOver"] retain]; 251 | _addTabButtonImage = [[aDecoder decodeObjectForKey:@"addTabButtonImage"] retain]; 252 | _addTabButtonPressedImage = [[aDecoder decodeObjectForKey:@"addTabButtonPressedImage"] retain]; 253 | _addTabButtonRolloverImage = [[aDecoder decodeObjectForKey:@"addTabButtonRolloverImage"] retain]; 254 | } 255 | //} 256 | return self; 257 | } 258 | 259 | @end 260 | -------------------------------------------------------------------------------- /Source/PSMCardTabStyle.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMCardTabStyle.h 3 | // PSMTabBarControl 4 | // 5 | // Created by Michael Monscheuer on 9/3/12. 6 | // 7 | // 8 | 9 | #import 10 | #import "PSMTabStyle.h" 11 | 12 | @interface PSMCardTabStyle : NSObject 13 | 14 | { 15 | NSImage *cardCloseButton; 16 | NSImage *cardCloseButtonDown; 17 | NSImage *cardCloseButtonOver; 18 | NSImage *cardCloseDirtyButton; 19 | NSImage *cardCloseDirtyButtonDown; 20 | NSImage *cardCloseDirtyButtonOver; 21 | NSImage *_addTabButtonImage; 22 | NSImage *_addTabButtonPressedImage; 23 | NSImage *_addTabButtonRolloverImage; 24 | 25 | CGFloat _leftMargin; 26 | } 27 | 28 | @property (assign) CGFloat leftMarginForTabBarControl; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Source/PSMCardTabStyle.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMCardTabStyle.m 3 | // PSMTabBarControl 4 | // 5 | // Created by Michael Monscheuer on 9/3/12. 6 | // 7 | // 8 | 9 | #import "PSMCardTabStyle.h" 10 | 11 | @interface PSMTabBarControl(SharedPrivates) 12 | 13 | - (void)_drawInteriorInRect:(NSRect)rect; 14 | - (NSRect)_addTabButtonRect; 15 | 16 | @end 17 | 18 | @implementation PSMCardTabStyle 19 | 20 | @synthesize leftMarginForTabBarControl = _leftMargin; 21 | 22 | + (NSString *)name { 23 | return @"Card"; 24 | } 25 | 26 | - (NSString *)name { 27 | return [[self class] name]; 28 | } 29 | 30 | #pragma mark - 31 | #pragma mark Creation/Destruction 32 | 33 | - (id) init { 34 | if ( (self = [super init]) ) { 35 | cardCloseButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front"]]; 36 | cardCloseButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Pressed"]]; 37 | cardCloseButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Rollover"]]; 38 | 39 | cardCloseDirtyButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front"]]; 40 | cardCloseDirtyButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Pressed"]]; 41 | cardCloseDirtyButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Rollover"]]; 42 | 43 | _addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNew"]]; 44 | _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewPressed"]]; 45 | _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewRollover"]]; 46 | 47 | _leftMargin = 5.0; 48 | } 49 | return self; 50 | } 51 | 52 | - (void)dealloc { 53 | [cardCloseButton release]; 54 | [cardCloseButtonDown release]; 55 | [cardCloseButtonOver release]; 56 | [cardCloseDirtyButton release]; 57 | [cardCloseDirtyButtonDown release]; 58 | [cardCloseDirtyButtonOver release]; 59 | [_addTabButtonImage release]; 60 | [_addTabButtonPressedImage release]; 61 | [_addTabButtonRolloverImage release]; 62 | 63 | [super dealloc]; 64 | } 65 | 66 | #pragma mark - 67 | #pragma mark Control Specific 68 | 69 | - (CGFloat)leftMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 70 | return _leftMargin; 71 | } 72 | 73 | - (CGFloat)rightMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 74 | return _leftMargin; 75 | } 76 | 77 | - (CGFloat)topMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 78 | return 10.0f; 79 | } 80 | 81 | #pragma mark - 82 | #pragma mark Add Tab Button 83 | 84 | - (NSImage *)addTabButtonImage { 85 | return _addTabButtonImage; 86 | } 87 | 88 | - (NSImage *)addTabButtonPressedImage { 89 | return _addTabButtonPressedImage; 90 | } 91 | 92 | - (NSImage *)addTabButtonRolloverImage { 93 | return _addTabButtonRolloverImage; 94 | } 95 | 96 | #pragma mark - 97 | #pragma mark Drag Support 98 | 99 | - (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell ofTabBarControl:(PSMTabBarControl *)tabBarControl { 100 | NSRect dragRect = [cell frame]; 101 | dragRect.size.width++; 102 | return dragRect; 103 | } 104 | 105 | #pragma mark - 106 | #pragma mark Providing Images 107 | 108 | - (NSImage *)closeButtonImageOfType:(PSMCloseButtonImageType)type forTabCell:(PSMTabBarCell *)cell 109 | { 110 | switch (type) { 111 | case PSMCloseButtonImageTypeStandard: 112 | return cardCloseButton; 113 | case PSMCloseButtonImageTypeRollover: 114 | return cardCloseButtonOver; 115 | case PSMCloseButtonImageTypePressed: 116 | return cardCloseButtonDown; 117 | 118 | case PSMCloseButtonImageTypeDirty: 119 | return cardCloseDirtyButton; 120 | case PSMCloseButtonImageTypeDirtyRollover: 121 | return cardCloseDirtyButtonOver; 122 | case PSMCloseButtonImageTypeDirtyPressed: 123 | return cardCloseDirtyButtonDown; 124 | 125 | default: 126 | break; 127 | } 128 | } 129 | 130 | #pragma mark - 131 | #pragma mark Drawing 132 | 133 | - (void)drawBezelOfTabBarControl:(PSMTabBarControl *)tabBarControl inRect:(NSRect)rect { 134 | //Draw for our whole bounds; it'll be automatically clipped to fit the appropriate drawing area 135 | NSRect bounds = [tabBarControl bounds]; 136 | 137 | bounds.size.height -= 1.0; 138 | 139 | NSGradient *gradient = nil; 140 | 141 | if ([tabBarControl isWindowActive]) { 142 | // gray bar gradient 143 | gradient = [[NSGradient alloc] initWithColorsAndLocations: 144 | [NSColor colorWithCalibratedWhite:0.678 alpha:1.000],0.0f, 145 | [NSColor colorWithCalibratedWhite:0.821 alpha:1.000],1.0f, 146 | nil]; 147 | } else { 148 | // light gray bar gradient 149 | gradient = [[NSGradient alloc] initWithColorsAndLocations: 150 | [NSColor colorWithCalibratedWhite:0.821 alpha:1.000],0.0f, 151 | [NSColor colorWithCalibratedWhite:0.956 alpha:1.000],1.0f, 152 | nil]; 153 | } 154 | 155 | if (gradient) { 156 | [gradient drawInRect:bounds angle:270]; 157 | 158 | [gradient release]; 159 | } 160 | } 161 | 162 | - (void)drawInteriorOfTabBarControl:(PSMTabBarControl *)tabBarControl inRect:(NSRect)rect { 163 | 164 | // draw interior first 165 | [tabBarControl _drawInteriorInRect:rect]; 166 | 167 | // draw separation line left and right of selected tab (no separation line at selected tab) 168 | for(PSMTabBarCell *cell in [tabBarControl cells]) { 169 | if([cell state] == NSOnState) { 170 | [[NSColor colorWithCalibratedWhite:0.576 alpha:1.0] set]; 171 | 172 | [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x,NSMaxY(rect)-0.5) 173 | toPoint:NSMakePoint(NSMinX([cell frame]),NSMaxY(rect)-0.5)]; 174 | [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX([cell frame]),NSMaxY(rect)-0.5) 175 | toPoint:NSMakePoint(NSMaxX(rect),NSMaxY(rect)-0.5)]; 176 | } 177 | } 178 | 179 | } 180 | 181 | - (void)drawBezelOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl { 182 | 183 | NSRect cellFrame = [cell frame]; 184 | 185 | NSColor * lineColor = nil; 186 | NSBezierPath *bezier = [NSBezierPath bezierPath]; 187 | lineColor = [NSColor colorWithCalibratedWhite:0.576 alpha:1.0]; 188 | 189 | NSRect aRect = NSMakeRect(cellFrame.origin.x+.5, cellFrame.origin.y+0.5, cellFrame.size.width-1.0, cellFrame.size.height-1.0); 190 | 191 | // frame 192 | CGFloat radius = MIN(6.0, 0.5f * MIN(NSWidth(aRect), NSHeight(aRect)))-0.5; 193 | 194 | [bezier moveToPoint: NSMakePoint(NSMinX(aRect),NSMaxY(aRect)+1.0)]; 195 | [bezier appendBezierPathWithArcFromPoint:NSMakePoint(NSMinX(aRect),NSMinY(aRect)) toPoint:NSMakePoint(NSMidX(aRect),NSMinY(aRect)) radius:radius]; 196 | [bezier appendBezierPathWithArcFromPoint:NSMakePoint(NSMaxX(aRect),NSMinY(aRect)) toPoint:NSMakePoint(NSMaxX(aRect),NSMaxY(aRect)) radius:radius]; 197 | [bezier lineToPoint: NSMakePoint(NSMaxX(aRect),NSMaxY(aRect)+1.0)]; 198 | 199 | NSGradient *gradient = nil; 200 | 201 | if([tabBarControl isWindowActive]) { 202 | if ([cell state] == NSOnState) { 203 | gradient = [[NSGradient alloc] initWithStartingColor:[NSColor whiteColor] endingColor:[NSColor colorWithDeviceWhite:0.929 alpha:1.000]]; 204 | } else if ([cell isHighlighted]) { 205 | 206 | gradient = [[NSGradient alloc] 207 | initWithStartingColor: [NSColor colorWithCalibratedWhite:0.80 alpha:1.0] 208 | endingColor:[NSColor colorWithCalibratedWhite:0.80 alpha:1.0]]; 209 | } else { 210 | 211 | gradient = [[NSGradient alloc] 212 | initWithStartingColor:[NSColor colorWithCalibratedWhite:0.835 alpha:1.0] 213 | endingColor:[NSColor colorWithCalibratedWhite:0.843 alpha:1.0]]; 214 | } 215 | 216 | if (gradient != nil) { 217 | [gradient drawInBezierPath:bezier angle:90.0f]; 218 | [gradient release], gradient = nil; 219 | } 220 | } else { 221 | [[NSColor windowBackgroundColor] set]; 222 | NSRectFill(aRect); 223 | } 224 | 225 | [lineColor set]; 226 | [bezier stroke]; 227 | } 228 | 229 | #pragma mark - 230 | #pragma mark Archiving 231 | 232 | - (void)encodeWithCoder:(NSCoder *)aCoder { 233 | // ... do not encode anything 234 | } 235 | 236 | - (id)initWithCoder:(NSCoder *)aDecoder { 237 | // ... do not read anything 238 | return [self init]; 239 | } 240 | 241 | @end 242 | -------------------------------------------------------------------------------- /Source/PSMLiveChatTabStyle.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMLiveChatTabStyle.h 3 | // -------------------- 4 | // 5 | // Created by Keith Blount on 30/04/2006. 6 | // Copyright 2006 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PSMTabStyle.h" 11 | 12 | @interface PSMLiveChatTabStyle : NSObject { 13 | NSImage *liveChatCloseButton; 14 | NSImage *liveChatCloseButtonDown; 15 | NSImage *liveChatCloseButtonOver; 16 | NSImage *liveChatCloseDirtyButton; 17 | NSImage *liveChatCloseDirtyButtonDown; 18 | NSImage *liveChatCloseDirtyButtonOver; 19 | NSImage *_addTabButtonImage; 20 | NSImage *_addTabButtonPressedImage; 21 | NSImage *_addTabButtonRolloverImage; 22 | 23 | NSDictionary *_objectCountStringAttributes; 24 | 25 | CGFloat _leftMargin; 26 | } 27 | 28 | @property (assign) CGFloat leftMarginForTabBarControl; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Source/PSMLiveChatTabStyle.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMLiveChatTabStyle.m 3 | // -------------------- 4 | // 5 | // Created by Keith Blount on 30/04/2006. 6 | // Copyright 2006 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "PSMLiveChatTabStyle.h" 10 | #import "PSMTabBarCell.h" 11 | #import "PSMTabBarControl.h" 12 | 13 | @implementation PSMLiveChatTabStyle 14 | 15 | @synthesize leftMarginForTabBarControl = _leftMargin; 16 | 17 | + (NSString *)name { 18 | return @"LiveChat"; 19 | } 20 | 21 | - (NSString *)name { 22 | return [[self class] name]; 23 | } 24 | 25 | #pragma mark - 26 | #pragma mark Creation/Destruction 27 | 28 | - (id) init { 29 | if((self = [super init])) { 30 | liveChatCloseButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front"]]; 31 | liveChatCloseButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Pressed"]]; 32 | liveChatCloseButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Rollover"]]; 33 | 34 | liveChatCloseDirtyButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front"]]; 35 | liveChatCloseDirtyButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Pressed"]]; 36 | liveChatCloseDirtyButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Rollover"]]; 37 | 38 | _addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNew"]]; 39 | _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewPressed"]]; 40 | _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewRollover"]]; 41 | 42 | _objectCountStringAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: 43 | [[NSColor whiteColor] colorWithAlphaComponent:0.85], NSForegroundColorAttributeName, 44 | [[NSFontManager sharedFontManager] convertFont:[NSFont fontWithName:@"Lucida Grande" size:11.0] toHaveTrait:NSBoldFontMask], NSFontAttributeName, 45 | nil]; 46 | _leftMargin = 5.0; 47 | } 48 | return self; 49 | } 50 | 51 | - (void)dealloc { 52 | [liveChatCloseButton release]; 53 | [liveChatCloseButtonDown release]; 54 | [liveChatCloseButtonOver release]; 55 | [liveChatCloseDirtyButton release]; 56 | [liveChatCloseDirtyButtonDown release]; 57 | [liveChatCloseDirtyButtonOver release]; 58 | [_addTabButtonImage release]; 59 | [_addTabButtonPressedImage release]; 60 | [_addTabButtonRolloverImage release]; 61 | 62 | [_objectCountStringAttributes release]; 63 | 64 | [super dealloc]; 65 | } 66 | 67 | #pragma mark - 68 | #pragma mark Control Specific 69 | 70 | - (CGFloat)leftMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 71 | return _leftMargin; 72 | } 73 | 74 | - (CGFloat)rightMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 75 | return _leftMargin; 76 | } 77 | 78 | - (CGFloat)topMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 79 | return 10.0f; 80 | } 81 | 82 | #pragma mark - 83 | #pragma mark Add Tab Button 84 | 85 | - (NSImage *)addTabButtonImage { 86 | return _addTabButtonImage; 87 | } 88 | 89 | - (NSImage *)addTabButtonPressedImage { 90 | return _addTabButtonPressedImage; 91 | } 92 | 93 | - (NSImage *)addTabButtonRolloverImage { 94 | return _addTabButtonRolloverImage; 95 | } 96 | 97 | #pragma mark - 98 | #pragma mark Drag Support 99 | 100 | - (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell ofTabBarControl:(PSMTabBarControl *)tabBarControl { 101 | NSRect dragRect = [cell frame]; 102 | dragRect.size.width++; 103 | return dragRect; 104 | } 105 | 106 | #pragma mark - 107 | #pragma mark Providing Images 108 | 109 | - (NSImage *)closeButtonImageOfType:(PSMCloseButtonImageType)type forTabCell:(PSMTabBarCell *)cell 110 | { 111 | switch (type) { 112 | case PSMCloseButtonImageTypeStandard: 113 | return liveChatCloseButton; 114 | case PSMCloseButtonImageTypeRollover: 115 | return liveChatCloseButtonOver; 116 | case PSMCloseButtonImageTypePressed: 117 | return liveChatCloseButtonDown; 118 | 119 | case PSMCloseButtonImageTypeDirty: 120 | return liveChatCloseDirtyButton; 121 | case PSMCloseButtonImageTypeDirtyRollover: 122 | return liveChatCloseDirtyButtonOver; 123 | case PSMCloseButtonImageTypeDirtyPressed: 124 | return liveChatCloseDirtyButtonDown; 125 | 126 | default: 127 | break; 128 | } 129 | } 130 | 131 | #pragma mark - 132 | #pragma mark Determining Cell Size 133 | 134 | - (NSRect)iconRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell { 135 | 136 | if (![cell hasIcon]) 137 | return NSZeroRect; 138 | 139 | NSImage *icon = [[(NSTabViewItem*)[cell representedObject] identifier] icon]; 140 | if (!icon) 141 | return NSZeroRect; 142 | 143 | PSMTabBarControl *tabBarControl = [cell controlView]; 144 | PSMTabBarOrientation orientation = [tabBarControl orientation]; 145 | 146 | if ([cell hasLargeImage] && orientation == PSMTabBarVerticalOrientation) 147 | return NSZeroRect; 148 | 149 | // calculate rect 150 | NSRect drawingRect = [cell drawingRectForBounds:theRect]; 151 | 152 | NSSize iconSize = [icon size]; 153 | 154 | NSSize scaledIconSize = [cell scaleImageWithSize:iconSize toFitInSize:NSMakeSize(iconSize.width, drawingRect.size.height) scalingType:NSImageScaleProportionallyDown]; 155 | 156 | NSRect result = NSMakeRect(drawingRect.origin.x, drawingRect.origin.y, scaledIconSize.width, scaledIconSize.height); 157 | 158 | // center in available space (in case icon image is smaller than kPSMTabBarIconWidth) 159 | if(scaledIconSize.width < kPSMTabBarIconWidth) { 160 | result.origin.x += ceil((kPSMTabBarIconWidth - scaledIconSize.width) / 2.0); 161 | } 162 | 163 | if(scaledIconSize.height < kPSMTabBarIconWidth) { 164 | result.origin.y += ceil((kPSMTabBarIconWidth - scaledIconSize.height) / 2.0 - 0.5); 165 | } 166 | 167 | return NSIntegralRect(result); 168 | } 169 | 170 | - (NSRect)titleRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell { 171 | 172 | NSRect drawingRect = [cell drawingRectForBounds:theRect]; 173 | 174 | NSRect constrainedDrawingRect = drawingRect; 175 | 176 | NSRect largeImageRect = [cell largeImageRectForBounds:theRect]; 177 | if (!NSEqualRects(largeImageRect, NSZeroRect)) { 178 | constrainedDrawingRect.origin.x += NSWidth(largeImageRect) + kPSMTabBarCellPadding; 179 | constrainedDrawingRect.size.width -= NSWidth(largeImageRect) + kPSMTabBarCellPadding; 180 | } else { 181 | NSRect iconRect = [cell iconRectForBounds:theRect]; 182 | if (!NSEqualRects(iconRect, NSZeroRect)) { 183 | constrainedDrawingRect.origin.x += NSWidth(iconRect) + kPSMTabBarCellPadding; 184 | constrainedDrawingRect.size.width -= NSWidth(iconRect) + kPSMTabBarCellPadding; 185 | } 186 | } 187 | 188 | NSRect indicatorRect = [cell indicatorRectForBounds:theRect]; 189 | if (!NSEqualRects(indicatorRect, NSZeroRect)) { 190 | constrainedDrawingRect.size.width -= NSWidth(indicatorRect) + kPSMTabBarCellPadding; 191 | } 192 | 193 | NSRect counterBadgeRect = [cell objectCounterRectForBounds:theRect]; 194 | if (!NSEqualRects(counterBadgeRect, NSZeroRect)) { 195 | constrainedDrawingRect.size.width -= NSWidth(counterBadgeRect) + kPSMTabBarCellPadding; 196 | } 197 | 198 | NSRect closeButtonRect = [cell closeButtonRectForBounds:theRect]; 199 | if (!NSEqualRects(closeButtonRect, NSZeroRect)) { 200 | constrainedDrawingRect.size.width -= NSWidth(closeButtonRect) + kPSMTabBarCellPadding; 201 | } 202 | 203 | NSAttributedString *attrString = [cell attributedStringValue]; 204 | if ([attrString length] == 0) 205 | return NSZeroRect; 206 | 207 | NSSize stringSize = [attrString size]; 208 | 209 | NSRect result = NSMakeRect(constrainedDrawingRect.origin.x, drawingRect.origin.y+ceil((drawingRect.size.height-stringSize.height)/2), constrainedDrawingRect.size.width, stringSize.height); 210 | 211 | return NSIntegralRect(result); 212 | } 213 | 214 | - (NSRect)objectCounterRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell { 215 | 216 | if([cell count] == 0) { 217 | return NSZeroRect; 218 | } 219 | 220 | NSRect drawingRect = [cell drawingRectForBounds:theRect]; 221 | 222 | NSRect constrainedDrawingRect = drawingRect; 223 | 224 | NSRect indicatorRect = [cell indicatorRectForBounds:theRect]; 225 | if (!NSEqualRects(indicatorRect, NSZeroRect)) 226 | { 227 | constrainedDrawingRect.size.width -= NSWidth(indicatorRect) + kPSMTabBarCellPadding; 228 | } 229 | 230 | NSRect closeButtonRect = [cell closeButtonRectForBounds:theRect]; 231 | if (!NSEqualRects(closeButtonRect, NSZeroRect)) 232 | { 233 | constrainedDrawingRect.size.width -= NSWidth(closeButtonRect) + kPSMTabBarCellPadding; 234 | } 235 | 236 | NSSize counterBadgeSize = [cell objectCounterSize]; 237 | 238 | // calculate rect 239 | NSRect result; 240 | result.size = counterBadgeSize; // temp 241 | result.origin.x = NSMaxX(constrainedDrawingRect)-counterBadgeSize.width; 242 | result.origin.y = ceil(constrainedDrawingRect.origin.y+(constrainedDrawingRect.size.height-result.size.height)/2); 243 | 244 | return NSIntegralRect(result); 245 | } 246 | 247 | - (NSRect)indicatorRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell { 248 | 249 | if([[cell indicator] isHidden]) { 250 | return NSZeroRect; 251 | } 252 | 253 | // calculate rect 254 | NSRect drawingRect = [cell drawingRectForBounds:theRect]; 255 | 256 | NSRect constrainedDrawingRect = drawingRect; 257 | 258 | NSRect closeButtonRect = [cell closeButtonRectForBounds:theRect]; 259 | if (!NSEqualRects(closeButtonRect, NSZeroRect)) 260 | { 261 | constrainedDrawingRect.size.width -= NSWidth(closeButtonRect) + kPSMTabBarCellPadding; 262 | } 263 | 264 | NSSize indicatorSize = NSMakeSize(kPSMTabBarIndicatorWidth, kPSMTabBarIndicatorWidth); 265 | 266 | NSRect result = NSMakeRect(NSMaxX(constrainedDrawingRect)-indicatorSize.width,NSMidY(constrainedDrawingRect)-ceil(indicatorSize.height/2),indicatorSize.width,indicatorSize.height); 267 | 268 | return NSIntegralRect(result); 269 | } 270 | 271 | - (NSRect)closeButtonRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell { 272 | 273 | if ([cell shouldDrawCloseButton] == NO) { 274 | return NSZeroRect; 275 | } 276 | 277 | // ask style for image 278 | NSImage *image = [cell closeButtonImageOfType:PSMCloseButtonImageTypeStandard]; 279 | if (!image) 280 | return NSZeroRect; 281 | 282 | // calculate rect 283 | NSRect drawingRect = [cell drawingRectForBounds:theRect]; 284 | 285 | NSSize imageSize = [image size]; 286 | 287 | NSSize scaledImageSize = [cell scaleImageWithSize:imageSize toFitInSize:NSMakeSize(imageSize.width, drawingRect.size.height) scalingType:NSImageScaleProportionallyDown]; 288 | 289 | NSRect result = NSMakeRect(NSMaxX(drawingRect)-scaledImageSize.width, drawingRect.origin.y, scaledImageSize.width, scaledImageSize.height); 290 | 291 | if(scaledImageSize.height < drawingRect.size.height) { 292 | result.origin.y += ceil((drawingRect.size.height - scaledImageSize.height) / 2.0); 293 | } 294 | 295 | return NSIntegralRect(result); 296 | } 297 | 298 | -(NSRect)largeImageRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell 299 | { 300 | if ([cell hasLargeImage] == NO) { 301 | return NSZeroRect; 302 | } 303 | 304 | // calculate rect 305 | NSRect drawingRect = [cell drawingRectForBounds:theRect]; 306 | 307 | NSRect constrainedDrawingRect = drawingRect; 308 | 309 | NSImage *image = [[[cell representedObject] identifier] largeImage]; 310 | if (!image) 311 | return NSZeroRect; 312 | 313 | NSSize scaledImageSize = [cell scaleImageWithSize:[image size] toFitInSize:NSMakeSize(constrainedDrawingRect.size.width, constrainedDrawingRect.size.height) scalingType:NSImageScaleProportionallyUpOrDown]; 314 | 315 | NSRect result = NSMakeRect(constrainedDrawingRect.origin.x, 316 | constrainedDrawingRect.origin.y - ((constrainedDrawingRect.size.height - scaledImageSize.height) / 2), 317 | scaledImageSize.width, scaledImageSize.height); 318 | 319 | if(scaledImageSize.width < kPSMTabBarIconWidth) { 320 | result.origin.x += (kPSMTabBarIconWidth - scaledImageSize.width) / 2.0; 321 | } 322 | if(scaledImageSize.height < constrainedDrawingRect.size.height) { 323 | result.origin.y += (constrainedDrawingRect.size.height - scaledImageSize.height) / 2.0; 324 | } 325 | 326 | return result; 327 | } // -largeImageRectForBounds:ofTabCell: 328 | 329 | #pragma mark - 330 | #pragma mark Cell Values 331 | 332 | - (NSAttributedString *)attributedObjectCountStringValueForTabCell:(PSMTabBarCell *)cell { 333 | NSString *contents = [NSString stringWithFormat:@"%lu", (unsigned long)[cell count]]; 334 | return [[[NSMutableAttributedString alloc] initWithString:contents attributes:_objectCountStringAttributes] autorelease]; 335 | } 336 | 337 | - (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell { 338 | NSMutableAttributedString *attrStr; 339 | NSString * contents = [cell title]; 340 | attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease]; 341 | NSRange range = NSMakeRange(0, [contents length]); 342 | 343 | [attrStr addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:11.0] range:range]; 344 | 345 | // Paragraph Style for Truncating Long Text 346 | static NSMutableParagraphStyle *TruncatingTailParagraphStyle = nil; 347 | if(!TruncatingTailParagraphStyle) { 348 | TruncatingTailParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain]; 349 | [TruncatingTailParagraphStyle setLineBreakMode:NSLineBreakByTruncatingTail]; 350 | } 351 | [attrStr addAttribute:NSParagraphStyleAttributeName value:TruncatingTailParagraphStyle range:range]; 352 | 353 | return attrStr; 354 | } 355 | 356 | #pragma mark - 357 | #pragma mark Drawing 358 | 359 | - (void)drawBezelOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl { 360 | 361 | NSRect cellFrame = frame; 362 | 363 | NSToolbar *toolbar = [[[cell controlView] window] toolbar]; 364 | BOOL showsBaselineSeparator = (toolbar && [toolbar respondsToSelector:@selector(showsBaselineSeparator)] && [toolbar showsBaselineSeparator]); 365 | if(!showsBaselineSeparator) { 366 | cellFrame.origin.y += 1.0; 367 | cellFrame.size.height -= 1.0; 368 | } 369 | 370 | NSColor * lineColor = nil; 371 | lineColor = [NSColor colorWithCalibratedWhite:0.576 alpha:1.0]; 372 | 373 | BOOL selected = [cell state] == NSOnState; 374 | if(!showsBaselineSeparator || selected) { 375 | // selected tab 376 | NSRect aRect = NSMakeRect(cellFrame.origin.x + 0.5, cellFrame.origin.y - 0.5, cellFrame.size.width, cellFrame.size.height); 377 | if(selected) { 378 | aRect.origin.y -= 1.0; 379 | aRect.size.height += 1.0; 380 | } 381 | 382 | // frame 383 | CGFloat radius = MIN(6.0, 0.5f * MIN(NSWidth(aRect), NSHeight(aRect))); 384 | NSRect rect = NSInsetRect(aRect, radius, radius); 385 | 386 | NSPoint cornerPoint = NSMakePoint(NSMinX(aRect), NSMinY(aRect)); 387 | NSBezierPath* bezier = [NSBezierPath bezierPath]; 388 | [bezier appendBezierPathWithPoints:&cornerPoint count:1]; 389 | 390 | [bezier appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMaxY(rect)) radius:radius startAngle:180.0 endAngle:90.0 clockwise:YES]; 391 | 392 | [bezier appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMaxY(rect)) radius:radius startAngle:90.0 endAngle:360.0 clockwise:YES]; 393 | 394 | cornerPoint = NSMakePoint(NSMaxX(aRect), NSMinY(aRect)); 395 | [bezier appendBezierPathWithPoints:&cornerPoint count:1]; 396 | 397 | NSColor *startColor = nil; 398 | NSColor *endColor = nil; 399 | 400 | if([tabBarControl isWindowActive]) { 401 | if([cell state] == NSOnState) { 402 | startColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; 403 | endColor = [NSColor colorWithCalibratedWhite:0.95 alpha:1.0]; 404 | } else if([cell isHighlighted]) { 405 | 406 | startColor = [NSColor colorWithCalibratedWhite:0.80 alpha:1.0]; 407 | endColor = [NSColor colorWithCalibratedWhite:0.80 alpha:1.0]; 408 | } 409 | } else if([cell state] == NSOnState) { 410 | startColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; 411 | endColor = [NSColor colorWithCalibratedWhite:0.95 alpha:1.0]; 412 | } 413 | 414 | NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:startColor endingColor:endColor]; 415 | [gradient drawInBezierPath:bezier angle:90.0]; 416 | [gradient release]; 417 | 418 | [lineColor set]; 419 | [bezier stroke]; 420 | } else { 421 | // unselected tab 422 | NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height); 423 | aRect.origin.y += 0.5; 424 | aRect.origin.x += 1.5; 425 | aRect.size.width -= 1; 426 | 427 | aRect.origin.x -= 1; 428 | aRect.size.width += 1; 429 | 430 | // rollover 431 | if([cell isHighlighted]) { 432 | [[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] set]; 433 | NSRectFillUsingOperation(aRect, NSCompositeSourceAtop); 434 | } 435 | 436 | // frame 437 | [lineColor set]; 438 | if(!([cell tabState] & PSMTab_RightIsSelectedMask)) { 439 | [NSBezierPath strokeLineFromPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y + aRect.size.height - 0.5) toPoint:NSMakePoint(NSMaxX(aRect), NSMaxY(aRect))]; 440 | } 441 | // Create a thin lighter line next to the dividing line for a bezel effect 442 | if(!([cell tabState] & PSMTab_RightIsSelectedMask)) { 443 | [[[NSColor whiteColor] colorWithAlphaComponent:0.5] set]; 444 | [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX(aRect) + 1.0, aRect.origin.y - 0.5) 445 | toPoint:NSMakePoint(NSMaxX(aRect) + 1.0, NSMaxY(aRect) - 2.5)]; 446 | } 447 | } 448 | } 449 | 450 | - (void)drawBezelOfTabBarControl:(PSMTabBarControl *)tabBarControl inRect:(NSRect)rect { 451 | //Draw for our whole bounds; it'll be automatically clipped to fit the appropriate drawing area 452 | rect = [tabBarControl bounds]; 453 | 454 | if([tabBarControl isWindowActive]) { 455 | NSRect gradientRect = rect; 456 | gradientRect.origin.y += 1.0; 457 | NSBezierPath *path = [NSBezierPath bezierPathWithRect:gradientRect]; 458 | NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:0.75 alpha:1.0] endingColor:[NSColor colorWithCalibratedWhite:0.75 alpha:0.0]]; 459 | [gradient drawInBezierPath:path angle:90.0]; 460 | [gradient release]; 461 | } 462 | 463 | [[NSColor colorWithCalibratedWhite:0.576 alpha:1.0] set]; 464 | [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x, NSMinY(rect) + 0.5) 465 | toPoint:NSMakePoint(NSMaxX(rect), NSMinY(rect) + 0.5)]; 466 | } 467 | 468 | #pragma mark - 469 | #pragma mark Archiving 470 | 471 | - (void)encodeWithCoder:(NSCoder *)aCoder { 472 | // ... do not encode anything 473 | } 474 | 475 | - (id)initWithCoder:(NSCoder *)aDecoder { 476 | // ... do not read anything 477 | return [self init]; 478 | } 479 | 480 | @end 481 | -------------------------------------------------------------------------------- /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 | 26 | - (void)encodeWithCoder:(NSCoder *)aCoder; 27 | - (id)initWithCoder:(NSCoder *)aDecoder; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /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 | @implementation PSMMetalTabStyle 14 | 15 | + (NSString *)name { 16 | return @"Metal"; 17 | } 18 | 19 | - (NSString *)name { 20 | return [[self class] name]; 21 | } 22 | 23 | #pragma mark - 24 | #pragma mark Creation/Destruction 25 | 26 | - (id) init { 27 | if((self = [super init])) { 28 | metalCloseButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Front"]]; 29 | metalCloseButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Front_Pressed"]]; 30 | metalCloseButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Front_Rollover"]]; 31 | 32 | metalCloseDirtyButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Dirty"]]; 33 | metalCloseDirtyButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Dirty_Pressed"]]; 34 | metalCloseDirtyButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabClose_Dirty_Rollover"]]; 35 | 36 | _addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabNewMetal"]]; 37 | _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabNewMetalPressed"]]; 38 | _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"TabNewMetalRollover"]]; 39 | 40 | _objectCountStringAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:[[NSFontManager sharedFontManager] convertFont:[NSFont fontWithName:@"Helvetica" size:11.0] toHaveTrait:NSBoldFontMask], NSFontAttributeName, 41 | [[NSColor whiteColor] colorWithAlphaComponent:0.85], NSForegroundColorAttributeName, 42 | nil, nil]; 43 | } 44 | return self; 45 | } 46 | 47 | - (void)dealloc { 48 | [metalCloseButton release]; 49 | [metalCloseButtonDown release]; 50 | [metalCloseButtonOver release]; 51 | [metalCloseDirtyButton release]; 52 | [metalCloseDirtyButtonDown release]; 53 | [metalCloseDirtyButtonOver release]; 54 | [_addTabButtonImage release]; 55 | [_addTabButtonPressedImage release]; 56 | [_addTabButtonRolloverImage release]; 57 | 58 | [_objectCountStringAttributes release]; 59 | 60 | [super dealloc]; 61 | } 62 | 63 | #pragma mark - 64 | #pragma mark Control Specific 65 | 66 | - (CGFloat)leftMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 67 | return 10.0f; 68 | } 69 | 70 | - (CGFloat)rightMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 71 | return 10.0f; 72 | } 73 | 74 | - (CGFloat)topMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 75 | return 10.0f; 76 | } 77 | 78 | #pragma mark - 79 | #pragma mark Add Tab Button 80 | 81 | - (NSImage *)addTabButtonImage { 82 | return _addTabButtonImage; 83 | } 84 | 85 | - (NSImage *)addTabButtonPressedImage { 86 | return _addTabButtonPressedImage; 87 | } 88 | 89 | - (NSImage *)addTabButtonRolloverImage { 90 | return _addTabButtonRolloverImage; 91 | } 92 | 93 | #pragma mark - 94 | #pragma mark Drag Support 95 | 96 | - (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell ofTabBarControl:(PSMTabBarControl *)tabBarControl { 97 | NSRect dragRect = [cell frame]; 98 | dragRect.size.width++; 99 | 100 | PSMTabBarOrientation orientation = [tabBarControl orientation]; 101 | 102 | if([cell tabState] & PSMTab_SelectedMask) { 103 | if(orientation == PSMTabBarHorizontalOrientation) { 104 | dragRect.size.height -= 2.0; 105 | } else { 106 | dragRect.size.height += 1.0; 107 | dragRect.origin.y -= 1.0; 108 | dragRect.origin.x += 2.0; 109 | dragRect.size.width -= 3.0; 110 | } 111 | } else if(orientation == PSMTabBarVerticalOrientation) { 112 | dragRect.origin.x--; 113 | } 114 | 115 | return dragRect; 116 | } 117 | 118 | #pragma mark - 119 | #pragma mark Providing Images 120 | 121 | - (NSImage *)closeButtonImageOfType:(PSMCloseButtonImageType)type forTabCell:(PSMTabBarCell *)cell 122 | { 123 | switch (type) { 124 | case PSMCloseButtonImageTypeStandard: 125 | return metalCloseButton; 126 | case PSMCloseButtonImageTypeRollover: 127 | return metalCloseButtonOver; 128 | case PSMCloseButtonImageTypePressed: 129 | return metalCloseButtonDown; 130 | 131 | case PSMCloseButtonImageTypeDirty: 132 | return metalCloseDirtyButton; 133 | case PSMCloseButtonImageTypeDirtyRollover: 134 | return metalCloseDirtyButtonOver; 135 | case PSMCloseButtonImageTypeDirtyPressed: 136 | return metalCloseDirtyButtonDown; 137 | 138 | default: 139 | break; 140 | } 141 | 142 | } 143 | 144 | #pragma mark - 145 | #pragma mark Cell Values 146 | 147 | - (NSAttributedString *)attributedObjectCountStringValueForTabCell:(PSMTabBarCell *)cell { 148 | NSString *contents = [NSString stringWithFormat:@"%lu", (unsigned long)[cell count]]; 149 | return [[[NSMutableAttributedString alloc] initWithString:contents attributes:_objectCountStringAttributes] autorelease]; 150 | } 151 | 152 | - (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell { 153 | NSMutableAttributedString *attrStr; 154 | NSString *contents = [cell title]; 155 | attrStr = [[[NSMutableAttributedString alloc] initWithString:contents] autorelease]; 156 | NSRange range = NSMakeRange(0, [contents length]); 157 | 158 | // Add font attribute 159 | [attrStr addAttribute:NSFontAttributeName value:[NSFont boldSystemFontOfSize:11.0] range:range]; 160 | [attrStr addAttribute:NSForegroundColorAttributeName value:[[NSColor textColor] colorWithAlphaComponent:0.75] range:range]; 161 | 162 | // Add shadow attribute 163 | NSShadow* shadow; 164 | shadow = [[[NSShadow alloc] init] autorelease]; 165 | CGFloat shadowAlpha; 166 | if(([cell state] == NSOnState) || [cell isHighlighted]) { 167 | shadowAlpha = 0.8; 168 | } else { 169 | shadowAlpha = 0.5; 170 | } 171 | [shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0 alpha:shadowAlpha]]; 172 | [shadow setShadowOffset:NSMakeSize(0, -1)]; 173 | [shadow setShadowBlurRadius:1.0]; 174 | [attrStr addAttribute:NSShadowAttributeName value:shadow range:range]; 175 | 176 | // Paragraph Style for Truncating Long Text 177 | static NSMutableParagraphStyle *TruncatingTailParagraphStyle = nil; 178 | if(!TruncatingTailParagraphStyle) { 179 | TruncatingTailParagraphStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] retain]; 180 | [TruncatingTailParagraphStyle setLineBreakMode:NSLineBreakByTruncatingTail]; 181 | [TruncatingTailParagraphStyle setAlignment:NSCenterTextAlignment]; 182 | } 183 | [attrStr addAttribute:NSParagraphStyleAttributeName value:TruncatingTailParagraphStyle range:range]; 184 | 185 | return attrStr; 186 | } 187 | 188 | #pragma mark - 189 | #pragma mark Drawing 190 | 191 | - (BOOL)_shouldDrawHorizontalTopBorderLineInView:(id)controlView 192 | { 193 | NSWindow *window = [controlView window]; 194 | NSToolbar *toolbar = [window toolbar]; 195 | if (!toolbar || ![toolbar isVisible] || ([toolbar isVisible] && [toolbar showsBaselineSeparator])) 196 | return NO; 197 | 198 | return YES; 199 | } 200 | 201 | - (NSRect)drawingRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell 202 | { 203 | NSRect resultRect; 204 | 205 | if ([(PSMTabBarControl *)[cell controlView] orientation] == PSMTabBarHorizontalOrientation && [cell state] == NSOnState) { 206 | resultRect = NSInsetRect(theRect,MARGIN_X,0.0); 207 | resultRect.origin.y += 1; 208 | resultRect.size.height -= MARGIN_Y + 2; 209 | } else { 210 | resultRect = NSInsetRect(theRect, MARGIN_X, MARGIN_Y); 211 | resultRect.size.height -= 1; 212 | } 213 | 214 | return resultRect; 215 | } 216 | 217 | - (void)drawBezelOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl { 218 | 219 | NSRect cellFrame = [cell frame]; 220 | NSColor *lineColor = nil; 221 | NSBezierPath *bezier = [NSBezierPath bezierPath]; 222 | lineColor = [NSColor darkGrayColor]; 223 | 224 | PSMTabBarOrientation orientation = [(PSMTabBarControl *)[cell controlView] orientation]; 225 | 226 | //disable antialiasing of bezier paths 227 | [NSGraphicsContext saveGraphicsState]; 228 | [[NSGraphicsContext currentContext] setShouldAntialias:NO]; 229 | 230 | if([cell state] == NSOnState) { 231 | // selected tab 232 | if(orientation == PSMTabBarHorizontalOrientation) { 233 | NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height - 2.5); 234 | 235 | // background 236 | aRect.origin.x += 1.0; 237 | aRect.size.width--; 238 | aRect.size.height -= 0.5; 239 | 240 | [[NSColor windowBackgroundColor] set]; 241 | NSRectFill(aRect); 242 | 243 | aRect.size.width++; 244 | aRect.size.height += 0.5; 245 | 246 | // frame 247 | aRect.origin.x -= 0.5; 248 | [lineColor set]; 249 | [bezier setLineWidth:1.0]; 250 | [bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y)]; 251 | [bezier lineToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y + aRect.size.height - 1.5)]; 252 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + 1.5, aRect.origin.y + aRect.size.height)]; 253 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width - 2.5, aRect.origin.y + aRect.size.height)]; 254 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y + aRect.size.height - 1.5)]; 255 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y)]; 256 | if([[cell controlView] frame].size.height < 2) { 257 | // special case of hidden control; need line across top of cell 258 | [bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y + 0.5)]; 259 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y + 0.5)]; 260 | } 261 | } else { 262 | NSRect aRect = NSMakeRect(cellFrame.origin.x + 2, cellFrame.origin.y, cellFrame.size.width - 2, cellFrame.size.height); 263 | 264 | // background 265 | aRect.origin.x++; 266 | aRect.size.height--; 267 | 268 | [[NSColor windowBackgroundColor] set]; 269 | NSRectFill(aRect); 270 | 271 | aRect.origin.x--; 272 | aRect.size.height++; 273 | 274 | // frame 275 | [lineColor set]; 276 | [bezier setLineWidth:1.0]; 277 | [bezier moveToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y)]; 278 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + 2, aRect.origin.y)]; 279 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + 0.5, aRect.origin.y + 2)]; 280 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + 0.5, aRect.origin.y + aRect.size.height - 3)]; 281 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + 3, aRect.origin.y + aRect.size.height)]; 282 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y + aRect.size.height)]; 283 | } 284 | 285 | [bezier stroke]; 286 | } else { 287 | // unselected tab 288 | NSRect aRect = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width, cellFrame.size.height); 289 | aRect.origin.y += 0.5; 290 | aRect.origin.x += 1.5; 291 | aRect.size.width -= 1; 292 | 293 | // rollover 294 | if([cell isHighlighted]) { 295 | [[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] set]; 296 | NSRectFillUsingOperation(aRect, NSCompositeSourceAtop); 297 | } 298 | 299 | [lineColor set]; 300 | 301 | if(orientation == PSMTabBarHorizontalOrientation) { 302 | aRect.origin.x -= 1; 303 | aRect.size.width += 1; 304 | 305 | // frame 306 | if ([self _shouldDrawHorizontalTopBorderLineInView:tabBarControl]) { 307 | [bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y)]; 308 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y)]; 309 | } else { 310 | [bezier moveToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y)]; 311 | } 312 | 313 | if(!([cell tabState] & PSMTab_RightIsSelectedMask)) { 314 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y + aRect.size.height)]; 315 | 316 | } 317 | } else { 318 | if(!([cell tabState] & PSMTab_LeftIsSelectedMask)) { 319 | [bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y)]; 320 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y)]; 321 | } 322 | 323 | if(!([cell tabState] & PSMTab_RightIsSelectedMask)) { 324 | [bezier moveToPoint:NSMakePoint(aRect.origin.x, aRect.origin.y + aRect.size.height)]; 325 | [bezier lineToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y + aRect.size.height)]; 326 | } 327 | } 328 | 329 | [bezier stroke]; 330 | } 331 | 332 | [NSGraphicsContext restoreGraphicsState]; 333 | } 334 | 335 | - (void)drawBezelOfTabBarControl:(PSMTabBarControl *)tabBarControl inRect:(NSRect)rect { 336 | 337 | //Draw for our whole bounds; it'll be automatically clipped to fit the appropriate drawing area 338 | rect = [tabBarControl bounds]; 339 | 340 | PSMTabBarOrientation orientation = [tabBarControl orientation]; 341 | 342 | if(orientation == PSMTabBarVerticalOrientation && [tabBarControl frame].size.width < 2) { 343 | return; 344 | } 345 | 346 | [NSGraphicsContext saveGraphicsState]; 347 | [[NSGraphicsContext currentContext] setShouldAntialias:NO]; 348 | 349 | [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set]; 350 | NSRectFillUsingOperation(rect, NSCompositeSourceAtop); 351 | [[NSColor darkGrayColor] set]; 352 | 353 | if(orientation == PSMTabBarHorizontalOrientation) { 354 | 355 | if ([self _shouldDrawHorizontalTopBorderLineInView:tabBarControl]) { 356 | [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x, rect.origin.y + 0.5) toPoint:NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + 0.5)]; 357 | } 358 | 359 | [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)]; 360 | } else { 361 | [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x, rect.origin.y + 0.5) toPoint:NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height + 0.5)]; 362 | [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + 0.5) toPoint:NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height + 0.5)]; 363 | } 364 | 365 | [NSGraphicsContext restoreGraphicsState]; 366 | } 367 | 368 | #pragma mark - 369 | #pragma mark Archiving 370 | 371 | - (void)encodeWithCoder:(NSCoder *)aCoder { 372 | //[super encodeWithCoder:aCoder]; 373 | if([aCoder allowsKeyedCoding]) { 374 | [aCoder encodeObject:metalCloseButton forKey:@"metalCloseButton"]; 375 | [aCoder encodeObject:metalCloseButtonDown forKey:@"metalCloseButtonDown"]; 376 | [aCoder encodeObject:metalCloseButtonOver forKey:@"metalCloseButtonOver"]; 377 | [aCoder encodeObject:metalCloseDirtyButton forKey:@"metalCloseDirtyButton"]; 378 | [aCoder encodeObject:metalCloseDirtyButtonDown forKey:@"metalCloseDirtyButtonDown"]; 379 | [aCoder encodeObject:metalCloseDirtyButtonOver forKey:@"metalCloseDirtyButtonOver"]; 380 | [aCoder encodeObject:_addTabButtonImage forKey:@"addTabButtonImage"]; 381 | [aCoder encodeObject:_addTabButtonPressedImage forKey:@"addTabButtonPressedImage"]; 382 | [aCoder encodeObject:_addTabButtonRolloverImage forKey:@"addTabButtonRolloverImage"]; 383 | } 384 | } 385 | 386 | - (id)initWithCoder:(NSCoder *)aDecoder { 387 | // self = [super initWithCoder:aDecoder]; 388 | //if (self) { 389 | if([aDecoder allowsKeyedCoding]) { 390 | metalCloseButton = [[aDecoder decodeObjectForKey:@"metalCloseButton"] retain]; 391 | metalCloseButtonDown = [[aDecoder decodeObjectForKey:@"metalCloseButtonDown"] retain]; 392 | metalCloseButtonOver = [[aDecoder decodeObjectForKey:@"metalCloseButtonOver"] retain]; 393 | metalCloseDirtyButton = [[aDecoder decodeObjectForKey:@"metalCloseDirtyButton"] retain]; 394 | metalCloseDirtyButtonDown = [[aDecoder decodeObjectForKey:@"metalCloseDirtyButtonDown"] retain]; 395 | metalCloseDirtyButtonOver = [[aDecoder decodeObjectForKey:@"metalCloseDirtyButtonOver"] retain]; 396 | _addTabButtonImage = [[aDecoder decodeObjectForKey:@"addTabButtonImage"] retain]; 397 | _addTabButtonPressedImage = [[aDecoder decodeObjectForKey:@"addTabButtonPressedImage"] retain]; 398 | _addTabButtonRolloverImage = [[aDecoder decodeObjectForKey:@"addTabButtonRolloverImage"] retain]; 399 | } 400 | //} 401 | return self; 402 | } 403 | 404 | @end 405 | -------------------------------------------------------------------------------- /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 | if(self = [super initWithFrame:frameRect pullsDown:YES]) { 19 | [self setBezelStyle:NSRegularSquareBezelStyle]; 20 | [self setBordered:NO]; 21 | [self setTitle:@""]; 22 | [self setPreferredEdge:NSMaxXEdge]; 23 | _PSMTabBarOverflowPopUpImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"overflowImage"]]; 24 | _PSMTabBarOverflowDownPopUpImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"overflowImagePressed"]]; 25 | _animatingAlternateImage = NO; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)dealloc { 31 | [_PSMTabBarOverflowPopUpImage release]; 32 | [_PSMTabBarOverflowDownPopUpImage release]; 33 | [super dealloc]; 34 | } 35 | 36 | - (void)drawRect:(NSRect)rect { 37 | if(_PSMTabBarOverflowPopUpImage == nil) { 38 | [super drawRect:rect]; 39 | return; 40 | } 41 | 42 | NSImage *image = (_down) ? _PSMTabBarOverflowDownPopUpImage : _PSMTabBarOverflowPopUpImage; 43 | NSSize imageSize = [image size]; 44 | NSRect bounds = [self bounds]; 45 | 46 | NSRect drawRect = NSMakeRect(NSMidX(bounds) - (imageSize.width * 0.5f), NSMidY(bounds) - (imageSize.height * 0.5f), imageSize.width, imageSize.height); 47 | 48 | [image drawInRect:drawRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:(_animatingAlternateImage ? 0.7f : 1.0f) respectFlipped:YES hints:nil]; 49 | 50 | if(_animatingAlternateImage) { 51 | NSImage *alternateImage = [self alternateImage]; 52 | NSSize altImageSize = [alternateImage size]; 53 | 54 | NSRect drawRect = NSMakeRect(NSMidX(bounds) - (altImageSize.width * 0.5f), NSMidY(bounds) - (altImageSize.height * 0.5f), altImageSize.width, altImageSize.height); 55 | 56 | [[self alternateImage] drawInRect:drawRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:sin(_animationValue * M_PI) respectFlipped:YES hints:nil]; 57 | } 58 | } 59 | 60 | - (void)mouseDown:(NSEvent *)event { 61 | _down = YES; 62 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationReceived:) name:NSMenuDidEndTrackingNotification object:[self menu]]; 63 | [self setNeedsDisplay:YES]; 64 | [super mouseDown:event]; 65 | } 66 | 67 | - (void)setHidden:(BOOL)value { 68 | if([self isHidden] != value) { 69 | if(value) { 70 | // Stop any animating alternate image if we hide 71 | [_animationTimer invalidate], _animationTimer = nil; 72 | } else if(_animatingAlternateImage) { 73 | // Restart any animating alternate image if we unhide 74 | _animationValue = ANIMATION_STEP; 75 | _animationTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(animateStep:) userInfo:nil repeats:YES]; 76 | [[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSEventTrackingRunLoopMode]; 77 | } 78 | } 79 | 80 | [super setHidden:value]; 81 | } 82 | 83 | - (void)notificationReceived:(NSNotification *)notification { 84 | _down = NO; 85 | [self setNeedsDisplay:YES]; 86 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 87 | } 88 | 89 | - (void)setAnimatingAlternateImage:(BOOL)flag { 90 | if(_animatingAlternateImage != flag) { 91 | _animatingAlternateImage = flag; 92 | 93 | if(![self isHidden]) { 94 | if(flag) { 95 | _animationValue = ANIMATION_STEP; 96 | _animationTimer = [NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(animateStep:) userInfo:nil repeats:YES]; 97 | [[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSEventTrackingRunLoopMode]; 98 | } else { 99 | [_animationTimer invalidate], _animationTimer = nil; 100 | } 101 | 102 | [self setNeedsDisplay:YES]; 103 | } 104 | } 105 | } 106 | 107 | - (BOOL)animatingAlternateImage; 108 | { 109 | return _animatingAlternateImage; 110 | } 111 | 112 | - (void)animateStep:(NSTimer *)timer { 113 | _animationValue += ANIMATION_STEP; 114 | 115 | if(_animationValue >= 1) { 116 | _animationValue = ANIMATION_STEP; 117 | } 118 | 119 | [self setNeedsDisplay:YES]; 120 | } 121 | 122 | #pragma mark - 123 | #pragma mark Archiving 124 | 125 | - (void)encodeWithCoder:(NSCoder *)aCoder { 126 | [super encodeWithCoder:aCoder]; 127 | if([aCoder allowsKeyedCoding]) { 128 | [aCoder encodeObject:_PSMTabBarOverflowPopUpImage forKey:@"PSMTabBarOverflowPopUpImage"]; 129 | [aCoder encodeObject:_PSMTabBarOverflowDownPopUpImage forKey:@"PSMTabBarOverflowDownPopUpImage"]; 130 | [aCoder encodeBool:_animatingAlternateImage forKey:@"PSMTabBarOverflowAnimatingAlternateImage"]; 131 | } 132 | } 133 | 134 | - (id)initWithCoder:(NSCoder *)aDecoder { 135 | if((self = [super initWithCoder:aDecoder])) { 136 | if([aDecoder allowsKeyedCoding]) { 137 | _PSMTabBarOverflowPopUpImage = [[aDecoder decodeObjectForKey:@"PSMTabBarOverflowPopUpImage"] retain]; 138 | _PSMTabBarOverflowDownPopUpImage = [[aDecoder decodeObjectForKey:@"PSMTabBarOverflowDownPopUpImage"] retain]; 139 | [self setAnimatingAlternateImage:[aDecoder decodeBoolForKey:@"PSMTabBarOverflowAnimatingAlternateImage"]]; 140 | } 141 | } 142 | return self; 143 | } 144 | 145 | @end 146 | -------------------------------------------------------------------------------- /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 | 11 | 12 | @interface PSMProgressIndicator : NSProgressIndicator { 13 | } 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /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 | #import "PSMTabBarControl.h" 11 | 12 | @interface PSMTabBarControl (PSMProgressIndicatorExtensions) 13 | 14 | - (void)update; 15 | 16 | @end 17 | 18 | @implementation PSMProgressIndicator 19 | 20 | // overrides to make tab bar control re-layout things if status changes 21 | - (void)setHidden:(BOOL)flag { 22 | [super setHidden:flag]; 23 | [(PSMTabBarControl *)[self superview] update]; 24 | } 25 | 26 | - (void)stopAnimation:(id)sender { 27 | [NSObject cancelPreviousPerformRequestsWithTarget:self 28 | selector:@selector(startAnimation:) 29 | object:nil]; 30 | [super stopAnimation:sender]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /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 | NSImage *_rolloverImage; 13 | NSImage *_usualImage; 14 | } 15 | 16 | @property (retain) NSImage *usualImage; 17 | @property (retain) NSImage *rolloverImage; 18 | 19 | @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 | @synthesize usualImage = _usualImage; 14 | @synthesize rolloverImage = _rolloverImage; 15 | 16 | - (id)initWithFrame:(NSRect)frameRect { 17 | self = [super initWithFrame:frameRect]; 18 | if (self) { 19 | 20 | [self addObserver:self forKeyPath:@"usualImage" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial context:NULL]; 21 | 22 | } 23 | return self; 24 | } 25 | 26 | - (void)dealloc { 27 | 28 | [self removeObserver:self forKeyPath:@"usualImage"]; 29 | 30 | [_usualImage release], _usualImage = nil; 31 | [_rolloverImage release], _rolloverImage = nil; 32 | 33 | [super dealloc]; 34 | } 35 | 36 | // override for rollover effect 37 | - (void)mouseEntered:(NSEvent *)theEvent; 38 | { 39 | // set rollover image 40 | [self setImage:_rolloverImage]; 41 | 42 | [super mouseEntered:theEvent]; 43 | } 44 | 45 | - (void)mouseExited:(NSEvent *)theEvent; 46 | { 47 | // restore usual image 48 | [self setImage:_usualImage]; 49 | 50 | [super mouseExited:theEvent]; 51 | } 52 | 53 | #pragma mark - 54 | #pragma mark KVO 55 | 56 | -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 57 | 58 | if ([keyPath isEqualToString:@"usualImage"]) 59 | { 60 | [self setImage:[self usualImage]]; 61 | } 62 | else 63 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 64 | } 65 | 66 | #pragma mark - 67 | #pragma mark Tracking Area Support 68 | 69 | - (void)addTrackingAreasInRect:(NSRect)cellFrame withUserInfo:(NSDictionary *)userInfo mouseLocation:(NSPoint)mouseLocation { 70 | 71 | NSTrackingAreaOptions options = 0; 72 | BOOL mouseIsInside = NO; 73 | NSTrackingArea *area = nil; 74 | 75 | // ---- add tracking area for hover effect ---- 76 | 77 | options = NSTrackingEnabledDuringMouseDrag | NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways; 78 | 79 | mouseIsInside = NSMouseInRect(mouseLocation, cellFrame, [self isFlipped]); 80 | if (mouseIsInside) { 81 | options |= NSTrackingAssumeInside; 82 | } 83 | 84 | // We make the view the owner, and it delegates the calls back to the cell after it is properly setup for the corresponding row/column in the outlineview 85 | area = [[NSTrackingArea alloc] initWithRect:cellFrame options:options owner:self userInfo:userInfo]; 86 | [self addTrackingArea:area]; 87 | [area release], area = nil; 88 | } 89 | 90 | -(void)updateTrackingAreas { 91 | 92 | [super updateTrackingAreas]; 93 | 94 | // remove all tracking rects 95 | for (NSTrackingArea *area in [self trackingAreas]) { 96 | // We have to uniquely identify our own tracking areas 97 | if ([area owner] == self) { 98 | [self removeTrackingArea:area]; 99 | 100 | // restore usual image 101 | [self setImage:_usualImage]; 102 | } 103 | } 104 | 105 | // recreate tracking areas and tool tip rects 106 | NSPoint mouseLocation = [self convertPoint:[[self window] convertScreenToBase:[NSEvent mouseLocation]] fromView:nil]; 107 | 108 | [self addTrackingAreasInRect:[self bounds] withUserInfo:nil mouseLocation:mouseLocation]; 109 | } 110 | 111 | #pragma mark - 112 | #pragma mark Archiving 113 | 114 | - (void)encodeWithCoder:(NSCoder *)aCoder { 115 | [super encodeWithCoder:aCoder]; 116 | if([aCoder allowsKeyedCoding]) { 117 | [aCoder encodeObject:_rolloverImage forKey:@"rolloverImage"]; 118 | [aCoder encodeObject:_usualImage forKey:@"usualImage"]; 119 | } 120 | } 121 | 122 | - (id)initWithCoder:(NSCoder *)aDecoder { 123 | self = [super initWithCoder:aDecoder]; 124 | if(self) { 125 | if([aDecoder allowsKeyedCoding]) { 126 | _rolloverImage = [[aDecoder decodeObjectForKey:@"rolloverImage"] retain]; 127 | _usualImage = [[aDecoder decodeObjectForKey:@"usualImage"] retain]; 128 | } 129 | } 130 | return self; 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /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 | #import "PSMProgressIndicator.h" 12 | 13 | typedef enum PSMCloseButtonImageType : NSUInteger 14 | { 15 | PSMCloseButtonImageTypeStandard = 0, 16 | PSMCloseButtonImageTypeRollover, 17 | PSMCloseButtonImageTypePressed, 18 | PSMCloseButtonImageTypeDirty, 19 | PSMCloseButtonImageTypeDirtyRollover, 20 | PSMCloseButtonImageTypeDirtyPressed 21 | } PSMCloseButtonImageType; 22 | 23 | typedef enum PSMTabBarCellTrackingAreaType : NSUInteger 24 | { 25 | PSMTabBarCellTrackingAreaCellFrameType = 0, 26 | PSMTabBarCellTrackingAreaCloseButtonType = 1 27 | } PSMTabBarCellTrackingAreaType; 28 | 29 | @interface PSMTabBarCell : NSActionCell { 30 | // sizing 31 | NSRect _frame; 32 | NSSize _attributedStringSize; 33 | NSInteger _currentStep; 34 | BOOL _isPlaceholder; 35 | 36 | // state 37 | PSMTabStateMask _tabState; 38 | BOOL _closeButtonOver; 39 | BOOL _closeButtonPressed; 40 | PSMProgressIndicator *_indicator; 41 | BOOL _isInOverflowMenu; 42 | BOOL _hasCloseButton; 43 | BOOL _isCloseButtonSuppressed; 44 | BOOL _hasIcon; 45 | BOOL _hasLargeImage; 46 | NSInteger _count; 47 | NSColor *_countColor; 48 | BOOL _isEdited; 49 | } 50 | 51 | @property (assign) PSMTabStateMask tabState; 52 | @property (assign) BOOL hasCloseButton; 53 | @property (assign) BOOL hasIcon; 54 | @property (assign) BOOL hasLargeImage; 55 | @property (assign) NSInteger count; 56 | @property (retain) NSColor *countColor; 57 | @property (assign) BOOL isPlaceholder; 58 | @property (assign) BOOL isEdited; 59 | @property (assign) BOOL closeButtonPressed; 60 | 61 | #pragma mark Creation/Destruction 62 | - (id)init; 63 | - (id)initPlaceholderWithFrame:(NSRect) frame expanded:(BOOL) value inTabBarControl:(PSMTabBarControl *)tabBarControl; 64 | - (void)dealloc; 65 | 66 | #pragma mark Accessors 67 | - (PSMTabBarControl *)controlView; 68 | - (void)setControlView:(PSMTabBarControl *)newControl; 69 | - (CGFloat)width; 70 | - (NSRect)frame; 71 | - (void)setFrame:(NSRect)rect; 72 | - (NSSize)attributedStringSize; 73 | - (NSAttributedString *)attributedStringValue; 74 | - (NSAttributedString *)attributedObjectCountStringValue; 75 | - (NSProgressIndicator *)indicator; 76 | - (BOOL)isInOverflowMenu; 77 | - (void)setIsInOverflowMenu:(BOOL)value; 78 | - (BOOL)closeButtonOver; 79 | - (void)setCloseButtonOver:(BOOL)value; 80 | - (void)setCloseButtonSuppressed:(BOOL)suppress; 81 | - (BOOL)isCloseButtonSuppressed; 82 | - (NSInteger)currentStep; 83 | - (void)setCurrentStep:(NSInteger)value; 84 | 85 | #pragma mark Providing Images 86 | - (NSImage *)closeButtonImageOfType:(PSMCloseButtonImageType)type; 87 | 88 | #pragma mark Determining Cell Size 89 | - (NSRect)drawingRectForBounds:(NSRect)theRect; 90 | - (NSRect)titleRectForBounds:(NSRect)theRect ; 91 | - (NSRect)iconRectForBounds:(NSRect)theRect; 92 | - (NSRect)largeImageRectForBounds:(NSRect)theRect; 93 | - (NSRect)indicatorRectForBounds:(NSRect)theRect; 94 | - (NSSize)objectCounterSize; 95 | - (NSRect)objectCounterRectForBounds:(NSRect)theRect; 96 | - (NSRect)closeButtonRectForBounds:(NSRect)theRect; 97 | 98 | - (CGFloat)minimumWidthOfCell; 99 | - (CGFloat)desiredWidthOfCell; 100 | 101 | #pragma mark Image Scaling 102 | - (NSSize)scaleImageWithSize:(NSSize)imageSize toFitInSize:(NSSize)canvasSize scalingType:(NSImageScaling)scalingType; 103 | 104 | #pragma mark Drawing 105 | - (BOOL)shouldDrawCloseButton; 106 | - (BOOL)shouldDrawObjectCounter; 107 | - (void)drawWithFrame:(NSRect) cellFrame inTabBarControl:(PSMTabBarControl *)tabBarControl; 108 | - (void)drawInteriorWithFrame:(NSRect)cellFrame inTabBarControl:(PSMTabBarControl *)tabBarControl; 109 | - (void)drawLargeImageWithFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 110 | - (void)drawIconWithFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 111 | - (void)drawTitleWithFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 112 | - (void)drawObjectCounterWithFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 113 | - (void)drawIndicatorWithFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 114 | - (void)drawCloseButtonWithFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 115 | 116 | #pragma mark Tracking Area Support 117 | - (void)addTrackingAreasForView:(NSView *)view inRect:(NSRect)cellFrame withUserInfo:(NSDictionary *)userInfo mouseLocation:(NSPoint)currentPoint; 118 | - (void)mouseEntered:(NSEvent *)theEvent; 119 | - (void)mouseExited:(NSEvent *)theEvent; 120 | 121 | #pragma mark Drag Support 122 | - (NSRect)draggingRect; 123 | - (NSImage *)dragImage; 124 | 125 | #pragma mark Archiving 126 | - (void)encodeWithCoder:(NSCoder *)aCoder; 127 | - (id)initWithCoder:(NSCoder *)aDecoder; 128 | 129 | @end 130 | 131 | @interface PSMTabBarControl (CellAccessors) 132 | 133 | - (id)style; 134 | 135 | @end 136 | 137 | @interface NSObject (IdentifierAccesors) 138 | 139 | - (NSImage *)largeImage; 140 | 141 | @end 142 | 143 | 144 | -------------------------------------------------------------------------------- /Source/PSMTabBarControl-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.positivespinmedia.PSMTabBarControlFramework 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1.0 21 | 22 | 23 | -------------------------------------------------------------------------------- /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 | #define kPSMObjectCounterMinWidth 20.0 30 | #define kPSMObjectCounterRadius 7.0 31 | #define kPSMTabBarControlSourceListHeight 28 32 | 33 | // Value used in _currentStep to indicate that resizing operation is not in progress 34 | #define kPSMIsNotBeingResized -1 35 | 36 | // Value used in _currentStep when a resizing operation has just been started 37 | #define kPSMStartResizeAnimation 0 38 | 39 | @class PSMOverflowPopUpButton; 40 | @class PSMRolloverButton; 41 | @class PSMTabBarCell; 42 | @class PSMTabBarController; 43 | @protocol PSMTabStyle; 44 | 45 | typedef enum PSMTabBarOrientation : NSUInteger { 46 | PSMTabBarHorizontalOrientation, 47 | PSMTabBarVerticalOrientation 48 | } PSMTabBarOrientation; 49 | 50 | typedef enum PSMTabBarTearOffStyle : NSUInteger { 51 | PSMTabBarTearOffAlphaWindow, 52 | PSMTabBarTearOffMiniwindow 53 | } PSMTabBarTearOffStyle; 54 | 55 | typedef enum PSMTabStateMask : NSUInteger { 56 | PSMTab_SelectedMask = 1 << 1, 57 | PSMTab_LeftIsSelectedMask = 1 << 2, 58 | PSMTab_RightIsSelectedMask = 1 << 3, 59 | PSMTab_PositionLeftMask = 1 << 4, 60 | PSMTab_PositionMiddleMask = 1 << 5, 61 | PSMTab_PositionRightMask = 1 << 6, 62 | PSMTab_PositionSingleMask = 1 << 7, 63 | } PSMTabStateMask; 64 | 65 | @protocol PSMTabBarControlDelegate; 66 | 67 | @interface PSMTabBarControl : NSControl { 68 | 69 | // control basics 70 | NSMutableArray *_cells; // the cells that draw the tabs 71 | IBOutlet NSTabView *tabView; // the tab view being navigated 72 | PSMOverflowPopUpButton *_overflowPopUpButton; // for too many tabs 73 | PSMRolloverButton *_addTabButton; 74 | PSMTabBarController *_controller; 75 | 76 | // Spring-loading. 77 | NSTimer *_springTimer; 78 | NSTabViewItem *_tabViewItemWithSpring; 79 | 80 | // drawing style 81 | id style; 82 | BOOL _canCloseOnlyTab; 83 | BOOL _disableTabClose; 84 | BOOL _hideForSingleTab; 85 | BOOL _showAddTabButton; 86 | BOOL _sizeCellsToFit; 87 | BOOL _useOverflowMenu; 88 | BOOL _alwaysShowActiveTab; 89 | BOOL _allowsScrubbing; 90 | NSInteger _resizeAreaCompensation; 91 | PSMTabBarOrientation _orientation; 92 | BOOL _automaticallyAnimates; 93 | NSTimer *_animationTimer; 94 | PSMTabBarTearOffStyle _tearOffStyle; 95 | 96 | // behavior 97 | BOOL _allowsBackgroundTabClosing; 98 | BOOL _selectsTabsOnMouseDown; 99 | 100 | // vertical tab resizing 101 | BOOL _allowsResizing; 102 | BOOL _resizing; 103 | 104 | // cell width 105 | NSInteger _cellMinWidth; 106 | NSInteger _cellMaxWidth; 107 | NSInteger _cellOptimumWidth; 108 | 109 | // animation for hide/show 110 | NSInteger _currentStep; 111 | BOOL _isHidden; 112 | IBOutlet id partnerView; // gets resized when hide/show 113 | BOOL _awakenedFromNib; 114 | NSInteger _tabBarWidth; 115 | NSTimer *_showHideAnimationTimer; 116 | 117 | // drag and drop 118 | NSEvent *_lastMouseDownEvent; // keep this for dragging reference 119 | BOOL _didDrag; 120 | BOOL _closeClicked; 121 | 122 | // MVC help 123 | IBOutlet id delegate; 124 | } 125 | 126 | #pragma mark Control Characteristics 127 | 128 | + (NSBundle *)bundle; 129 | - (CGFloat)availableCellWidth; 130 | - (CGFloat)availableCellHeight; 131 | - (NSRect)genericCellRect; 132 | - (BOOL)isWindowActive; 133 | 134 | #pragma mark Style Class Registry 135 | 136 | + (void)registerDefaultTabStyleClasses; 137 | + (void)registerTabStyleClass:(Class )aStyleClass; 138 | + (void)unregisterTabStyleClass:(Class )aStyleClass; 139 | + (NSArray *)registeredTabStyleClasses; 140 | + (Class )registeredClassForStyleName:(NSString *)name; 141 | 142 | #pragma mark Cell Management (KVC Compliant) 143 | 144 | - (NSArray *)cells; 145 | - (void)addCell:(PSMTabBarCell *)aCell; 146 | - (void)insertCell:(PSMTabBarCell *)aCell atIndex:(NSUInteger)index; 147 | - (void)removeCellAtIndex:(NSUInteger)index; 148 | - (void)replaceCellAtIndex:(NSUInteger)index withCell:(PSMTabBarCell *)aCell; 149 | 150 | #pragma mark Control Configuration 151 | 152 | - (PSMTabBarOrientation)orientation; 153 | - (void)setOrientation:(PSMTabBarOrientation)value; 154 | - (BOOL)canCloseOnlyTab; 155 | - (void)setCanCloseOnlyTab:(BOOL)value; 156 | - (BOOL)disableTabClose; 157 | - (void)setDisableTabClose:(BOOL)value; 158 | - (id)style; 159 | - (void)setStyle:(id )newStyle; 160 | - (NSString *)styleName; 161 | - (void)setStyleNamed:(NSString *)name; 162 | - (BOOL)hideForSingleTab; 163 | - (void)setHideForSingleTab:(BOOL)value; 164 | - (BOOL)showAddTabButton; 165 | - (void)setShowAddTabButton:(BOOL)value; 166 | - (NSInteger)cellMinWidth; 167 | - (void)setCellMinWidth:(NSInteger)value; 168 | - (NSInteger)cellMaxWidth; 169 | - (void)setCellMaxWidth:(NSInteger)value; 170 | - (NSInteger)cellOptimumWidth; 171 | - (void)setCellOptimumWidth:(NSInteger)value; 172 | - (BOOL)sizeCellsToFit; 173 | - (void)setSizeCellsToFit:(BOOL)value; 174 | - (BOOL)useOverflowMenu; 175 | - (void)setUseOverflowMenu:(BOOL)value; 176 | - (BOOL)allowsBackgroundTabClosing; 177 | - (void)setAllowsBackgroundTabClosing:(BOOL)value; 178 | - (BOOL)allowsResizing; 179 | - (void)setAllowsResizing:(BOOL)value; 180 | - (BOOL)selectsTabsOnMouseDown; 181 | - (void)setSelectsTabsOnMouseDown:(BOOL)value; 182 | - (BOOL)automaticallyAnimates; 183 | - (void)setAutomaticallyAnimates:(BOOL)value; 184 | - (BOOL)alwaysShowActiveTab; 185 | - (void)setAlwaysShowActiveTab:(BOOL)value; 186 | - (BOOL)allowsScrubbing; 187 | - (void)setAllowsScrubbing:(BOOL)value; 188 | - (PSMTabBarTearOffStyle)tearOffStyle; 189 | - (void)setTearOffStyle:(PSMTabBarTearOffStyle)tearOffStyle; 190 | - (CGFloat)heightOfTabCells; 191 | 192 | #pragma mark Accessors 193 | 194 | - (NSTabView *)tabView; 195 | - (void)setTabView:(NSTabView *)view; 196 | - (id)delegate; 197 | - (void)setDelegate:(id)object; 198 | - (id)partnerView; 199 | - (void)setPartnerView:(id)view; 200 | 201 | #pragma mark - 202 | #pragma mark Determining Sizes 203 | 204 | - (NSSize)addTabButtonSize; 205 | - (NSRect)addTabButtonRect; 206 | - (NSSize)overflowButtonSize; 207 | - (NSRect)overflowButtonRect; 208 | 209 | #pragma mark - 210 | #pragma mark Determining Margins 211 | 212 | - (CGFloat)rightMargin; 213 | - (CGFloat)leftMargin; 214 | - (CGFloat)topMargin; 215 | - (CGFloat)bottomMargin; 216 | 217 | #pragma mark The Buttons 218 | 219 | - (PSMRolloverButton *)addTabButton; 220 | - (PSMOverflowPopUpButton *)overflowPopUpButton; 221 | 222 | #pragma mark Tab Information 223 | 224 | - (NSMutableArray *)representedTabViewItems; 225 | - (NSInteger)numberOfVisibleTabs; 226 | - (PSMTabBarCell *)lastVisibleTab; 227 | 228 | #pragma mark Special Effects 229 | 230 | - (void)hideTabBar:(BOOL) hide animate:(BOOL)animate; 231 | - (BOOL)isTabBarHidden; 232 | - (BOOL)isAnimating; 233 | 234 | // internal bindings methods also used by the tab drag assistant 235 | - (void)bindPropertiesForCell:(PSMTabBarCell *)cell andTabViewItem:(NSTabViewItem *)item; 236 | - (void)removeTabForCell:(PSMTabBarCell *)cell; 237 | 238 | @end 239 | 240 | @protocol PSMTabBarControlDelegate 241 | 242 | @optional 243 | 244 | //Standard NSTabView methods 245 | - (BOOL)tabView:(NSTabView *)aTabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem; 246 | - (void)tabView:(NSTabView *)aTabView willCloseTabViewItem:(NSTabViewItem *)tabViewItem; 247 | - (void)tabView:(NSTabView *)aTabView didCloseTabViewItem:(NSTabViewItem *)tabViewItem; 248 | - (void)tabView:(NSTabView *)aTabView didDetachTabViewItem:(NSTabViewItem *)tabViewItem; 249 | 250 | //"Spring-loaded" tabs methods 251 | - (NSArray *)allowedDraggedTypesForTabView:(NSTabView *)aTabView; 252 | - (void)tabView:(NSTabView *)aTabView acceptedDraggingInfo:(id ) draggingInfo onTabViewItem:(NSTabViewItem *)tabViewItem; 253 | 254 | //Contextual menu method 255 | - (NSMenu *)tabView:(NSTabView *)aTabView menuForTabViewItem:(NSTabViewItem *)tabViewItem; 256 | 257 | //Event method 258 | - (void)tabView:(NSTabView *)aTabView tabViewItem:(NSTabViewItem *)tabViewItem event:(NSEvent *)event; 259 | 260 | //Drag and drop methods 261 | - (BOOL)tabView:(NSTabView *)aTabView shouldDragTabViewItem:(NSTabViewItem *)tabViewItem fromTabBar:(PSMTabBarControl *)tabBarControl; 262 | - (BOOL)tabView:(NSTabView *)aTabView shouldDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl; 263 | - (BOOL)tabView:(NSTabView *)aTabView shouldAllowTabViewItem:(NSTabViewItem *)tabViewItem toLeaveTabBar:(PSMTabBarControl *)tabBarControl; 264 | - (void)tabView:(NSTabView*)aTabView didDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl; 265 | 266 | 267 | //Tear-off tabs methods 268 | - (NSImage *)tabView:(NSTabView *)aTabView imageForTabViewItem:(NSTabViewItem *)tabViewItem offset:(NSSize *)offset styleMask:(NSUInteger *)styleMask; 269 | - (PSMTabBarControl *)tabView:(NSTabView *)aTabView newTabBarForDraggedTabViewItem:(NSTabViewItem *)tabViewItem atPoint:(NSPoint)point; 270 | - (void)tabView:(NSTabView *)aTabView closeWindowForLastTabViewItem:(NSTabViewItem *)tabViewItem; 271 | 272 | //Overflow menu validation 273 | - (BOOL)tabView:(NSTabView *)aTabView validateOverflowMenuItem:(NSMenuItem *)menuItem forTabViewItem:(NSTabViewItem *)tabViewItem; 274 | - (void)tabView:(NSTabView *)aTabView tabViewItem:(NSTabViewItem *)tabViewItem isInOverflowMenu:(BOOL)inOverflowMenu; 275 | 276 | //tab bar hiding methods 277 | - (void)tabView:(NSTabView *)aTabView tabBarDidHide:(PSMTabBarControl *)tabBarControl; 278 | - (void)tabView:(NSTabView *)aTabView tabBarDidUnhide:(PSMTabBarControl *)tabBarControl; 279 | - (CGFloat)desiredWidthForVerticalTabBar:(PSMTabBarControl *)tabBarControl; 280 | 281 | //closing 282 | - (BOOL)tabView:(NSTabView *)aTabView disableTabCloseForTabViewItem:(NSTabViewItem *)tabViewItem; 283 | 284 | //tooltips 285 | - (NSString *)tabView:(NSTabView *)aTabView toolTipForTabViewItem:(NSTabViewItem *)tabViewItem; 286 | 287 | //accessibility 288 | - (NSString *)accessibilityStringForTabView:(NSTabView *)aTabView objectCount:(NSInteger)objectCount; 289 | 290 | @end 291 | -------------------------------------------------------------------------------- /Source/PSMTabBarControlDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | APPL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | LSMinimumSystemVersion 22 | ${MACOSX_DEPLOYMENT_TARGET} 23 | NSMainNibFile 24 | PSMTabBarControlDemo 25 | NSPrincipalClass 26 | NSApplication 27 | 28 | 29 | -------------------------------------------------------------------------------- /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 | { 15 | PSMTabBarControl *_control; 16 | NSMutableArray *_cellFrames; 17 | NSMenu *_overflowMenu; 18 | } 19 | 20 | - (id)initWithTabBarControl:(PSMTabBarControl *)control; 21 | 22 | - (NSMenu *)overflowMenu; 23 | - (NSRect)cellFrameAtIndex:(NSInteger)index; 24 | 25 | - (void)setSelectedCell:(PSMTabBarCell *)cell; 26 | 27 | - (void)layoutCells; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /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 | if((self = [super init])) { 35 | _control = control; 36 | _cellFrames = [[NSMutableArray alloc] init]; 37 | } 38 | return self; 39 | } 40 | 41 | - (void)dealloc { 42 | [_cellFrames release]; 43 | [super dealloc]; 44 | } 45 | 46 | /*! 47 | @method overflowMenu 48 | @abstract Returns current overflow menu or nil if there is none. 49 | @discussion Returns current overflow menu or nil if there is none. 50 | @returns The current overflow menu. 51 | */ 52 | 53 | - (NSMenu *)overflowMenu { 54 | return _overflowMenu; 55 | } 56 | 57 | /*! 58 | @method cellFrameAtIndex: 59 | @abstract Returns the frame for the cell at the requested index. 60 | @discussion Returns the frame for the cell at the requested index. 61 | @param Index of a cell. 62 | @returns The frame of the cell at the requested index. 63 | */ 64 | 65 | - (NSRect)cellFrameAtIndex:(NSInteger)index { 66 | NSRect rect; 67 | 68 | if(index > -1 && index < [_cellFrames count]) { 69 | rect = [[_cellFrames objectAtIndex:index] rectValue]; 70 | } else { 71 | NSLog(@"cellFrameAtIndex: Invalid index (%ld)", (long)index); 72 | rect = NSZeroRect; 73 | } 74 | return rect; 75 | } 76 | 77 | /*! 78 | @method setSelectedCell: 79 | @abstract Changes the cell states so the given cell is the currently selected cell. 80 | @discussion Makes the given cell the active cell and properly recalculates the tab states for surrounding cells. 81 | @param An instance of PSMTabBarCell to make active. 82 | */ 83 | 84 | - (void)setSelectedCell:(PSMTabBarCell *)cell { 85 | NSArray *cells = [_control cells]; 86 | NSEnumerator *enumerator = [cells objectEnumerator]; 87 | PSMTabBarCell *lastCell = nil, *nextCell; 88 | 89 | //deselect the previously selected tab 90 | while((nextCell = [enumerator nextObject]) && ([nextCell state] == NSOffState)) { 91 | lastCell = nextCell; 92 | } 93 | 94 | [nextCell setState:NSOffState]; 95 | [nextCell setTabState:PSMTab_PositionMiddleMask]; 96 | 97 | if(lastCell && lastCell != [_control lastVisibleTab]) { 98 | [lastCell setTabState:~[lastCell tabState] & PSMTab_RightIsSelectedMask]; 99 | } 100 | 101 | if((nextCell = [enumerator nextObject])) { 102 | [nextCell setTabState:~[lastCell tabState] & PSMTab_LeftIsSelectedMask]; 103 | } 104 | 105 | [cell setState:NSOnState]; 106 | [cell setTabState:PSMTab_SelectedMask]; 107 | 108 | if(![cell isInOverflowMenu]) { 109 | NSInteger cellIndex = [cells indexOfObject:cell]; 110 | 111 | if(cellIndex > 0) { 112 | nextCell = [cells objectAtIndex:cellIndex - 1]; 113 | [nextCell setTabState:[nextCell tabState] | PSMTab_RightIsSelectedMask]; 114 | } 115 | 116 | if(cellIndex < [cells count] - 1) { 117 | nextCell = [cells objectAtIndex:cellIndex + 1]; 118 | [nextCell setTabState:[nextCell tabState] | PSMTab_LeftIsSelectedMask]; 119 | } 120 | } 121 | } 122 | 123 | /*! 124 | @method layoutCells 125 | @abstract Recalculates cell positions and states. 126 | @discussion This method calculates the proper frame, tabState and overflow menu status for all cells in the 127 | tab bar control. 128 | */ 129 | 130 | - (void)layoutCells { 131 | NSArray *cells = [_control cells]; 132 | NSInteger cellCount = [cells count]; 133 | 134 | // make sure all of our tabs are accounted for before updating 135 | if([[_control tabView] numberOfTabViewItems] != cellCount) { 136 | return; 137 | } 138 | 139 | [_cellFrames removeAllObjects]; 140 | 141 | NSArray *cellWidths = [self _generateWidthsFromCells:cells]; 142 | [self _setupCells:cells withWidths:cellWidths]; 143 | } 144 | 145 | /*! 146 | * @method _shrinkWidths:towardMinimum:withAvailableWidth: 147 | * @abstract Decreases widths in an array toward a minimum until they fit within availableWidth, if possible 148 | * @param An array of NSNumbers 149 | * @param The target minimum 150 | * @param The maximum available width 151 | * @returns The amount by which the total array width was shrunk 152 | */ 153 | - (NSInteger)_shrinkWidths:(NSMutableArray *)newWidths towardMinimum:(NSInteger)minimum withAvailableWidth:(CGFloat)availableWidth { 154 | BOOL changed = NO; 155 | NSInteger count = [newWidths count]; 156 | NSInteger totalWidths = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue]; 157 | NSInteger originalTotalWidths = totalWidths; 158 | 159 | do { 160 | changed = NO; 161 | 162 | for(NSInteger q = (count - 1); q >= 0; q--) { 163 | CGFloat cellWidth = [[newWidths objectAtIndex:q] doubleValue]; 164 | if(cellWidth - 1 >= minimum) { 165 | cellWidth--; 166 | totalWidths--; 167 | 168 | [newWidths replaceObjectAtIndex:q 169 | withObject:[NSNumber numberWithDouble:cellWidth]]; 170 | 171 | changed = YES; 172 | } 173 | } 174 | } while(changed && (totalWidths > availableWidth)); 175 | 176 | return(originalTotalWidths - totalWidths); 177 | } 178 | 179 | /*! 180 | * @function potentialMinimumForArray() 181 | * @abstract Calculate the minimum total for a given array of widths 182 | * @discussion The array is summed using, for each item, the minimum between the current value and the passed minimum value. 183 | * This is useful for getting a sum if the array has size-to-fit widths which will be allowed to be less than the 184 | * specified minimum. 185 | * @param An array of widths 186 | * @param The minimum 187 | * @returns The smallest possible sum for the array 188 | */ 189 | static NSInteger potentialMinimumForArray(NSArray *array, NSInteger minimum){ 190 | NSInteger runningTotal = 0; 191 | NSInteger count = [array count]; 192 | 193 | for(NSInteger i = 0; i < count; i++) { 194 | NSInteger currentValue = [[array objectAtIndex:i] integerValue]; 195 | runningTotal += MIN(currentValue, minimum); 196 | } 197 | 198 | return runningTotal; 199 | } 200 | 201 | /*! 202 | @method _generateWidthsFromCells: 203 | @abstract Calculates the width of cells that would be visible. 204 | @discussion Calculates the width of cells in the tab bar and returns an array of widths for the cells that would be 205 | visible. Uses large blocks of code that were previously in PSMTabBarControl's update method. 206 | @param An array of PSMTabBarCells. 207 | @returns An array of numbers representing the widths of cells that would be visible. 208 | */ 209 | 210 | - (NSArray *)_generateWidthsFromCells:(NSArray *)cells { 211 | NSInteger cellCount = [cells count], i, numberOfVisibleCells = ([_control orientation] == PSMTabBarHorizontalOrientation) ? 1 : 0; 212 | NSMutableArray *newWidths = [NSMutableArray arrayWithCapacity:cellCount]; 213 | id style = [_control style]; 214 | CGFloat availableWidth = [_control availableCellWidth], currentOrigin = 0, totalOccupiedWidth = 0.0, width; 215 | 216 | NSRect cellRect = [_control genericCellRect]; 217 | PSMTabBarCell *currentCell; 218 | 219 | if([_control orientation] == PSMTabBarVerticalOrientation) { 220 | currentOrigin = [style topMarginForTabBarControl:_control]; 221 | } 222 | 223 | for(i = 0; i < cellCount; i++) { 224 | currentCell = [cells objectAtIndex:i]; 225 | 226 | // supress close button? 227 | [currentCell setCloseButtonSuppressed:((cellCount == 1 && [_control canCloseOnlyTab] == NO) || 228 | [_control disableTabClose] || 229 | ([[_control delegate] respondsToSelector:@selector(tabView:disableTabCloseForTabViewItem:)] && 230 | [[_control delegate] tabView:[_control tabView] disableTabCloseForTabViewItem:[currentCell representedObject]]))]; 231 | 232 | if([_control orientation] == PSMTabBarHorizontalOrientation) { 233 | // Determine cell width 234 | if([_control sizeCellsToFit]) { 235 | width = [currentCell desiredWidthOfCell]; 236 | if(width > [_control cellMaxWidth]) { 237 | width = [_control cellMaxWidth]; 238 | } 239 | } else { 240 | width = [_control cellOptimumWidth]; 241 | } 242 | 243 | width = ceil(width); 244 | 245 | //check to see if there is not enough space to place all tabs as preferred 246 | if(totalOccupiedWidth + width >= availableWidth) { 247 | //There's not enough space to add currentCell at its preferred width! 248 | 249 | //If we're not going to use the overflow menu, cram all the tab cells into the bar regardless of minimum width 250 | if(![_control useOverflowMenu]) { 251 | NSInteger j, averageWidth = (availableWidth / cellCount); 252 | 253 | numberOfVisibleCells = cellCount; 254 | [newWidths removeAllObjects]; 255 | 256 | for(j = 0; j < cellCount; j++) { 257 | CGFloat desiredWidth = [[cells objectAtIndex:j] desiredWidthOfCell]; 258 | [newWidths addObject:[NSNumber numberWithDouble:(desiredWidth < averageWidth && [_control sizeCellsToFit]) ? desiredWidth : averageWidth]]; 259 | } 260 | 261 | break; 262 | } 263 | 264 | //We'll be using the overflow menu if needed. 265 | numberOfVisibleCells = i; 266 | if([_control sizeCellsToFit]) { 267 | BOOL remainingCellsMustGoToOverflow = NO; 268 | 269 | totalOccupiedWidth = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue]; 270 | 271 | /* Can I squeeze it in without violating min cell width? This is the width we would take up 272 | * if every cell so far were at the control minimum size (or their current size if that is less than the control minimum). 273 | */ 274 | if((potentialMinimumForArray(newWidths, [_control cellMinWidth]) + MIN(width, [_control cellMinWidth])) <= availableWidth) { 275 | /* It's definitely possible for cells so far to be visible. 276 | * Shrink other cells to allow this one to fit 277 | */ 278 | NSInteger cellMinWidth = [_control cellMinWidth]; 279 | 280 | /* Start off adding it to the array; we know that it will eventually fit because 281 | * (the potential minimum <= availableWidth) 282 | * 283 | * This allows average and minimum aggregates on the NSArray to work. 284 | */ 285 | [newWidths addObject:[NSNumber numberWithDouble:width]]; 286 | numberOfVisibleCells++; 287 | 288 | totalOccupiedWidth += width; 289 | 290 | //First, try to shrink tabs toward the average. Tabs smaller than average won't change 291 | totalOccupiedWidth -= [self _shrinkWidths:newWidths 292 | towardMinimum:[[newWidths valueForKeyPath:@"@avg.intValue"] integerValue] 293 | withAvailableWidth:availableWidth]; 294 | 295 | 296 | 297 | if(totalOccupiedWidth > availableWidth) { 298 | //Next, shrink tabs toward the smallest of the existing tabs. The smallest tab won't change. 299 | NSInteger smallestTabWidth = [[newWidths valueForKeyPath:@"@min.intValue"] integerValue]; 300 | if(smallestTabWidth > cellMinWidth) { 301 | totalOccupiedWidth -= [self _shrinkWidths:newWidths 302 | towardMinimum:smallestTabWidth 303 | withAvailableWidth:availableWidth]; 304 | } 305 | } 306 | 307 | if(totalOccupiedWidth > availableWidth) { 308 | //Finally, shrink tabs toward the imposed minimum size. All tabs larger than the minimum wll change. 309 | totalOccupiedWidth -= [self _shrinkWidths:newWidths 310 | towardMinimum:cellMinWidth 311 | withAvailableWidth:availableWidth]; 312 | } 313 | 314 | if(totalOccupiedWidth > availableWidth) { 315 | NSLog(@"**** -[PSMTabBarController generateWidthsFromCells:] This is a failure (available %f, total %f, width is %f)", 316 | availableWidth, totalOccupiedWidth, width); 317 | remainingCellsMustGoToOverflow = YES; 318 | } 319 | 320 | if(totalOccupiedWidth < availableWidth) { 321 | /* We're not using all available space not but exceeded available width before; 322 | * stretch all cells to fully fit the bar 323 | */ 324 | NSInteger leftoverWidth = availableWidth - totalOccupiedWidth; 325 | if(leftoverWidth > 0) { 326 | NSInteger q; 327 | for(q = numberOfVisibleCells - 1; q >= 0; q--) { 328 | NSInteger desiredAddition = (NSInteger)leftoverWidth / (q + 1); 329 | NSInteger newCellWidth = (NSInteger)[[newWidths objectAtIndex:q] doubleValue] + desiredAddition; 330 | [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:newCellWidth]]; 331 | leftoverWidth -= desiredAddition; 332 | totalOccupiedWidth += desiredAddition; 333 | } 334 | } 335 | } 336 | } else { 337 | 338 | // adjust available width for overflow button 339 | availableWidth -= ([_control overflowButtonSize].width + kPSMTabBarCellPadding); 340 | if (![_control showAddTabButton]) 341 | availableWidth -= kPSMTabBarCellPadding; 342 | 343 | // stretch - distribute leftover room among cells, since we can't add this cell 344 | NSInteger leftoverWidth = availableWidth - totalOccupiedWidth; 345 | NSInteger q; 346 | for(q = i - 1; q >= 0; q--) { 347 | NSInteger desiredAddition = (NSInteger)leftoverWidth / (q + 1); 348 | NSInteger newCellWidth = (NSInteger)[[newWidths objectAtIndex:q] doubleValue] + desiredAddition; 349 | [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:newCellWidth]]; 350 | leftoverWidth -= desiredAddition; 351 | } 352 | 353 | remainingCellsMustGoToOverflow = YES; 354 | } 355 | 356 | // done assigning widths; remaining cells go in overflow menu 357 | if(remainingCellsMustGoToOverflow) { 358 | break; 359 | } 360 | } else { 361 | //We're not using size-to-fit 362 | NSInteger revisedWidth = availableWidth / (i + 1); 363 | if(revisedWidth >= [_control cellMinWidth]) { 364 | NSUInteger q; 365 | totalOccupiedWidth = 0; 366 | 367 | for(q = 0; q < [newWidths count]; q++) { 368 | [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:revisedWidth]]; 369 | totalOccupiedWidth += revisedWidth; 370 | } 371 | // just squeezed this one in... 372 | [newWidths addObject:[NSNumber numberWithDouble:revisedWidth]]; 373 | totalOccupiedWidth += revisedWidth; 374 | numberOfVisibleCells++; 375 | 376 | // couldn't fit that last one... 377 | } else { 378 | // adjust available width for overflow button 379 | availableWidth -= ([_control overflowButtonSize].width + kPSMTabBarCellPadding); 380 | if (![_control showAddTabButton]) 381 | availableWidth -= kPSMTabBarCellPadding; 382 | 383 | revisedWidth = availableWidth / i; 384 | 385 | if(revisedWidth >= [_control cellMinWidth]) { 386 | NSUInteger q; 387 | totalOccupiedWidth = 0; 388 | 389 | for(q = 0; q < [newWidths count]; q++) { 390 | [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:revisedWidth]]; 391 | totalOccupiedWidth += revisedWidth; 392 | } 393 | } else { 394 | [self _shrinkWidths:newWidths towardMinimum:[_control cellMinWidth] withAvailableWidth:availableWidth]; 395 | NSInteger usedWidth = [[newWidths valueForKeyPath:@"@sum.intValue"] integerValue]; 396 | // cells still do not fit in available width? -> remove last cell 397 | if (availableWidth < usedWidth) { 398 | totalOccupiedWidth -= [[newWidths lastObject] intValue]; 399 | numberOfVisibleCells--; 400 | [newWidths removeLastObject]; 401 | 402 | revisedWidth = availableWidth / numberOfVisibleCells; 403 | 404 | if(revisedWidth >= [_control cellMinWidth]) { 405 | NSUInteger q; 406 | totalOccupiedWidth = 0; 407 | 408 | for(q = 0; q < [newWidths count]; q++) { 409 | [newWidths replaceObjectAtIndex:q withObject:[NSNumber numberWithDouble:revisedWidth]]; 410 | totalOccupiedWidth += revisedWidth; 411 | } 412 | } 413 | } 414 | } 415 | 416 | break; 417 | } 418 | } 419 | } else { 420 | //(totalOccupiedWidth < availableWidth) 421 | numberOfVisibleCells = cellCount; 422 | [newWidths addObject:[NSNumber numberWithDouble:width]]; 423 | totalOccupiedWidth += width; 424 | } 425 | } else { 426 | //lay out vertical tabs 427 | if(currentOrigin + cellRect.size.height <= [_control availableCellHeight]) { 428 | [newWidths addObject:[NSNumber numberWithDouble:currentOrigin]]; 429 | numberOfVisibleCells++; 430 | currentOrigin += cellRect.size.height; 431 | } else { 432 | break; 433 | } 434 | } 435 | } 436 | 437 | //make sure there are at least two items in the horizontal tab bar 438 | if([_control orientation] == PSMTabBarHorizontalOrientation) { 439 | if(numberOfVisibleCells < 2 && [cells count] > 1) { 440 | PSMTabBarCell *cell1 = [cells objectAtIndex:0], *cell2 = [cells objectAtIndex:1]; 441 | NSNumber *cellWidth; 442 | 443 | [newWidths removeAllObjects]; 444 | totalOccupiedWidth = 0; 445 | 446 | cellWidth = [NSNumber numberWithDouble:[cell1 desiredWidthOfCell] < availableWidth * 0.5f ?[cell1 desiredWidthOfCell] : availableWidth * 0.5f]; 447 | [newWidths addObject:cellWidth]; 448 | totalOccupiedWidth += [cellWidth doubleValue]; 449 | 450 | cellWidth = [NSNumber numberWithDouble:[cell2 desiredWidthOfCell] < (availableWidth - totalOccupiedWidth) ?[cell2 desiredWidthOfCell] : (availableWidth - totalOccupiedWidth)]; 451 | [newWidths addObject:cellWidth]; 452 | totalOccupiedWidth += [cellWidth doubleValue]; 453 | 454 | if(totalOccupiedWidth < availableWidth) { 455 | [newWidths replaceObjectAtIndex:0 withObject:[NSNumber numberWithDouble:availableWidth - [cellWidth doubleValue]]]; 456 | } 457 | } 458 | } 459 | 460 | return newWidths; 461 | } 462 | 463 | /*! 464 | @method _setupCells:withWidths 465 | @abstract Creates tracking rect arrays and sets the frames of the visible cells. 466 | @discussion Creates tracking rect arrays and sets the cells given in the widths array. 467 | */ 468 | 469 | - (void)_setupCells:(NSArray *)cells withWidths:(NSArray *)widths { 470 | NSInteger i, tabState, cellCount = [cells count]; 471 | NSRect cellRect = [_control genericCellRect]; 472 | PSMTabBarCell *cell; 473 | NSTabViewItem *selectedTabViewItem = [[_control tabView] selectedTabViewItem]; 474 | NSMenuItem *menuItem; 475 | 476 | [_overflowMenu release], _overflowMenu = nil; 477 | 478 | for(i = 0; i < cellCount; i++) { 479 | cell = [cells objectAtIndex:i]; 480 | 481 | if(i < [widths count]) { 482 | tabState = 0; 483 | 484 | // set cell frame 485 | if([_control orientation] == PSMTabBarHorizontalOrientation) { 486 | cellRect.size.width = [[widths objectAtIndex:i] doubleValue]; 487 | } else { 488 | cellRect.size.width = [_control frame].size.width; 489 | cellRect.origin.y = [[widths objectAtIndex:i] doubleValue]; 490 | cellRect.origin.x = 0; 491 | } 492 | 493 | [_cellFrames addObject:[NSValue valueWithRect:cellRect]]; 494 | 495 | if([[cell representedObject] isEqualTo:selectedTabViewItem]) { 496 | [cell setState:NSOnState]; 497 | tabState |= PSMTab_SelectedMask; 498 | // previous cell 499 | if(i > 0) { 500 | [[cells objectAtIndex:i - 1] setTabState:([(PSMTabBarCell *)[cells objectAtIndex:i - 1] tabState] | PSMTab_RightIsSelectedMask)]; 501 | } 502 | // next cell - see below 503 | } else { 504 | [cell setState:NSOffState]; 505 | // see if prev cell was selected 506 | if((i > 0) && ([[cells objectAtIndex:i - 1] state] == NSOnState)) { 507 | tabState |= PSMTab_LeftIsSelectedMask; 508 | } 509 | } 510 | 511 | // more tab states 512 | if([widths count] == 1) { 513 | tabState |= PSMTab_PositionLeftMask | PSMTab_PositionRightMask | PSMTab_PositionSingleMask; 514 | } else if(i == 0) { 515 | tabState |= PSMTab_PositionLeftMask; 516 | } else if(i == [widths count] - 1) { 517 | tabState |= PSMTab_PositionRightMask; 518 | } 519 | 520 | [cell setTabState:tabState]; 521 | [cell setIsInOverflowMenu:NO]; 522 | 523 | // indicator 524 | if(![[cell indicator] isHidden] && ![_control isTabBarHidden]) { 525 | if(![[_control subviews] containsObject:[cell indicator]]) { 526 | [_control addSubview:[cell indicator]]; 527 | [[cell indicator] startAnimation:self]; 528 | } 529 | } 530 | 531 | // next... 532 | if ([_control orientation] == PSMTabBarHorizontalOrientation) 533 | cellRect.origin.x += [[widths objectAtIndex:i] doubleValue]; 534 | else 535 | cellRect.origin.y += cellRect.size.height; 536 | } else { 537 | [cell setState:NSOffState]; 538 | [cell setIsInOverflowMenu:YES]; 539 | [[cell indicator] removeFromSuperview]; 540 | 541 | //position the cell well offscreen 542 | if([_control orientation] == PSMTabBarHorizontalOrientation) { 543 | cellRect.origin.x += [[_control style] rightMarginForTabBarControl:_control] + 20; 544 | } else { 545 | cellRect.origin.y = [_control frame].size.height + 2; 546 | } 547 | 548 | [_cellFrames addObject:[NSValue valueWithRect:cellRect]]; 549 | 550 | if(_overflowMenu == nil) { 551 | _overflowMenu = [[NSMenu alloc] init]; 552 | [_overflowMenu insertItemWithTitle:@"" action:nil keyEquivalent:@"" atIndex:0]; // Because the overflowPupUpButton is a pull down menu 553 | [_overflowMenu setDelegate:self]; 554 | } 555 | 556 | // Each item's title is limited to 60 characters. If more than 60 characters, use an ellipsis to indicate that more exists. 557 | menuItem = [_overflowMenu addItemWithTitle:[[[cell attributedStringValue] string] stringWithEllipsisByTruncatingToLength:MAX_OVERFLOW_MENUITEM_TITLE_LENGTH] 558 | action:@selector(overflowMenuAction:) 559 | keyEquivalent:@""]; 560 | [menuItem setTarget:_control]; 561 | [menuItem setRepresentedObject:[cell representedObject]]; 562 | 563 | if([cell count] > 0) { 564 | [menuItem setTitle:[[menuItem title] stringByAppendingFormat:@" (%lu)", (unsigned long)[cell count]]]; 565 | } 566 | } 567 | } 568 | } 569 | 570 | - (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)menuItem atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel { 571 | if(menu == _overflowMenu) { 572 | if([[[menuItem representedObject] identifier] respondsToSelector:@selector(icon)]) { 573 | [menuItem setImage:[[[menuItem representedObject] identifier] valueForKey:@"icon"]]; 574 | } 575 | } 576 | 577 | return TRUE; 578 | } 579 | 580 | - (NSInteger)numberOfItemsInMenu:(NSMenu *)menu { 581 | if(menu == _overflowMenu) { 582 | return [_overflowMenu numberOfItems]; 583 | } else { 584 | NSLog(@"Warning: Unexpected menu delegate call for menu %@", menu); 585 | return 0; 586 | } 587 | } 588 | 589 | @end 590 | 591 | /* 592 | PSMTabBarController will store what the current tab frame state should be like based off the last layout. PSMTabBarControl 593 | has to handle fetching the new frame and then changing the tab cell frame. 594 | Tab states will probably be changed immediately. 595 | 596 | Tabs that aren't going to be visible need to have their frame set offscreen. Treat them as if they were visible. 597 | 598 | The overflow menu is rebuilt and stored by the controller. 599 | 600 | Arrays of tracking rects will be created here, but not applied. 601 | Tracking rects are removed and added by PSMTabBarControl at the end of an animate/display cycle. 602 | 603 | The add tab button frame is handled by this controller. Visibility and location are set by the control. 604 | 605 | isInOverflowMenu should probably be removed in favor of a call that returns yes/no to if a cell is in overflow. (Not yet implemented) 606 | 607 | Still need to rewrite most of the code in PSMTabDragAssistant. 608 | */ 609 | -------------------------------------------------------------------------------- /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; 30 | PSMTabDragWindowController *_draggedView; 31 | NSSize _dragWindowOffset; 32 | NSTimer *_fadeTimer; 33 | BOOL _centersDragWindows; 34 | PSMTabBarTearOffStyle _currentTearOffStyle; 35 | 36 | // Animation 37 | NSTimer *_animationTimer; 38 | NSMutableArray *_sineCurveWidths; 39 | NSPoint _currentMouseLoc; 40 | PSMTabBarCell *_targetCell; 41 | } 42 | 43 | // Creation/destruction 44 | + (PSMTabDragAssistant *)sharedDragAssistant; 45 | 46 | // Accessors 47 | - (PSMTabBarControl *)sourceTabBar; 48 | - (void)setSourceTabBar:(PSMTabBarControl *)tabBar; 49 | - (PSMTabBarControl *)destinationTabBar; 50 | - (void)setDestinationTabBar:(PSMTabBarControl *)tabBar; 51 | - (PSMTabBarCell *)draggedCell; 52 | - (void)setDraggedCell:(PSMTabBarCell *)cell; 53 | - (NSInteger)draggedCellIndex; 54 | - (void)setDraggedCellIndex:(NSInteger)value; 55 | - (BOOL)isDragging; 56 | - (void)setIsDragging:(BOOL)value; 57 | - (NSPoint)currentMouseLoc; 58 | - (void)setCurrentMouseLoc:(NSPoint)point; 59 | - (PSMTabBarCell *)targetCell; 60 | - (void)setTargetCell:(PSMTabBarCell *)cell; 61 | 62 | // Functionality 63 | - (void)startDraggingCell:(PSMTabBarCell *)cell fromTabBarControl:(PSMTabBarControl *)tabBarControl withMouseDownEvent:(NSEvent *)event; 64 | - (void)draggingEnteredTabBarControl:(PSMTabBarControl *)tabBarControl atPoint:(NSPoint)mouseLoc; 65 | - (void)draggingUpdatedInTabBarControl:(PSMTabBarControl *)tabBarControl atPoint:(NSPoint)mouseLoc; 66 | - (void)draggingExitedTabBarControl:(PSMTabBarControl *)tabBarControl; 67 | - (void)performDragOperation; 68 | - (void)draggedImageEndedAt:(NSPoint)aPoint operation:(NSDragOperation)operation; 69 | - (void)finishDrag; 70 | 71 | - (void)draggingBeganAt:(NSPoint)aPoint; 72 | - (void)draggingMovedTo:(NSPoint)aPoint; 73 | 74 | // Animation 75 | - (void)animateDrag:(NSTimer *)timer; 76 | - (void)calculateDragAnimationForTabBarControl:(PSMTabBarControl *)tabBarControl; 77 | 78 | // Placeholder 79 | - (void)distributePlaceholdersInTabBarControl:(PSMTabBarControl *)tabBarControl withDraggedCell:(PSMTabBarCell *)cell; 80 | - (void)distributePlaceholdersInTabBarControl:(PSMTabBarControl *)tabBarControl; 81 | - (void)removeAllPlaceholdersFromTabBarControl:(PSMTabBarControl *)tabBarControl; 82 | 83 | @end 84 | 85 | void CGContextCopyWindowCaptureContentsToRect(void *grafport, CGRect rect, NSInteger cid, NSInteger wid, NSInteger zero); 86 | OSStatus CGSSetWindowTransform(NSInteger cid, NSInteger wid, CGAffineTransform transform); 87 | 88 | @interface NSApplication (CoreGraphicsUndocumented) 89 | - (NSInteger)contextID; 90 | @end 91 | -------------------------------------------------------------------------------- /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; 13 | NSImage *_alternateImage; 14 | CGFloat _alpha; 15 | } 16 | - (void)setFadeValue:(CGFloat)value; 17 | - (NSImage *)image; 18 | - (void)setImage:(NSImage *)image; 19 | - (NSImage *)alternateImage; 20 | - (void)setAlternateImage:(NSImage *)image; 21 | @end 22 | -------------------------------------------------------------------------------- /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 | [_image release]; 23 | [_alternateImage release]; 24 | [super dealloc]; 25 | } 26 | 27 | - (void)drawRect:(NSRect)rect { 28 | //1.0 fade means show the primary image 29 | //0.0 fade means show the secondary image 30 | CGFloat primaryAlpha = _alpha + 0.001f, alternateAlpha = 1.001f - _alpha; 31 | NSRect srcRect; 32 | srcRect.origin = NSZeroPoint; 33 | srcRect.size = [_image size]; 34 | 35 | [_image drawInRect:[self bounds] fromRect:srcRect operation:NSCompositeSourceOver fraction:primaryAlpha respectFlipped:YES hints:nil]; 36 | srcRect.size = [_alternateImage size]; 37 | [_alternateImage drawInRect:[self bounds] fromRect:srcRect operation:NSCompositeSourceOver fraction:alternateAlpha respectFlipped:YES hints:nil]; 38 | } 39 | 40 | - (void)setFadeValue:(CGFloat)value { 41 | _alpha = value; 42 | } 43 | 44 | - (NSImage *)image { 45 | return _image; 46 | } 47 | 48 | - (void)setImage:(NSImage *)image { 49 | [_image release]; 50 | _image = [image retain]; 51 | } 52 | 53 | - (NSImage *)alternateImage { 54 | return _alternateImage; 55 | } 56 | 57 | - (void)setAlternateImage:(NSImage *)image { 58 | [_alternateImage release]; 59 | _alternateImage = [image retain]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /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 | return [[[PSMTabDragWindow alloc] initWithImage:image styleMask:styleMask] autorelease]; 16 | } 17 | 18 | - (id)initWithImage:(NSImage *)image styleMask:(NSUInteger)styleMask { 19 | NSSize size = [image size]; 20 | 21 | if((self = [super initWithContentRect:NSMakeRect(0, 0, size.width, size.height) styleMask:styleMask backing:NSBackingStoreBuffered defer:NO])) { 22 | _dragView = [[[PSMTabDragView alloc] initWithFrame:NSMakeRect(0, 0, size.width, size.height)] autorelease]; 23 | [self setContentView:_dragView]; 24 | [self setLevel:NSStatusWindowLevel]; 25 | [self setIgnoresMouseEvents:YES]; 26 | [self setOpaque:NO]; 27 | 28 | [_dragView setImage:image]; 29 | 30 | //Set the size of the window to be the exact size of the drag image 31 | NSRect windowFrame = [self frame]; 32 | windowFrame.origin.y += windowFrame.size.height - size.height; 33 | windowFrame.size = size; 34 | 35 | if(styleMask | NSBorderlessWindowMask) { 36 | windowFrame.size.height += 22; 37 | } 38 | 39 | [self setFrame:windowFrame display:YES]; 40 | } 41 | return self; 42 | } 43 | 44 | - (PSMTabDragView *)dragView { 45 | return _dragView; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /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 | PSMTabDragWindow *window = [PSMTabDragWindow dragWindowWithImage:image styleMask:styleMask]; 17 | if((self = [super initWithWindow:window])) { 18 | _view = [[window dragView] retain]; 19 | _tearOffStyle = tearOffStyle; 20 | 21 | if(tearOffStyle == PSMTabBarTearOffMiniwindow) { 22 | [window setBackgroundColor:[NSColor clearColor]]; 23 | [window setHasShadow:YES]; 24 | } 25 | 26 | [window setAlphaValue:kPSMTabDragWindowAlpha]; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)dealloc { 32 | if(_timer) { 33 | [_timer invalidate]; 34 | } 35 | 36 | if(_animation) { 37 | [_animation release]; 38 | } 39 | 40 | [_view release]; 41 | [super dealloc]; 42 | } 43 | 44 | - (NSImage *)image { 45 | return [_view image]; 46 | } 47 | 48 | - (NSImage *)alternateImage { 49 | return [_view alternateImage]; 50 | } 51 | 52 | - (void)setAlternateImage:(NSImage *)image { 53 | [_view setAlternateImage:image]; 54 | } 55 | 56 | - (BOOL)isAnimating { 57 | return _animation != nil; 58 | } 59 | 60 | - (void)switchImages { 61 | if(_tearOffStyle != PSMTabBarTearOffMiniwindow || ![_view alternateImage]) { 62 | return; 63 | } 64 | 65 | CGFloat progress = 0; 66 | _showingAlternate = !_showingAlternate; 67 | 68 | if(_animation) { 69 | //An animation already exists, get the current progress 70 | progress = 1.0f - [_animation currentProgress]; 71 | [_animation stopAnimation]; 72 | [_animation release]; 73 | } 74 | 75 | //begin animating 76 | _animation = [[NSAnimation alloc] initWithDuration:0.25 animationCurve:NSAnimationEaseInOut]; 77 | [_animation setAnimationBlockingMode:NSAnimationNonblocking]; 78 | [_animation setCurrentProgress:progress]; 79 | [_animation startAnimation]; 80 | 81 | _originalWindowFrame = [[self window] frame]; 82 | 83 | if(_timer) { 84 | [_timer invalidate]; 85 | } 86 | _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f / 30.0f target:self selector:@selector(animateTimer:) userInfo:nil repeats:YES]; 87 | } 88 | 89 | - (void)animateTimer:(NSTimer *)timer { 90 | NSRect frame = _originalWindowFrame; 91 | NSImage *currentImage = _showingAlternate ?[_view alternateImage] :[_view image]; 92 | NSSize size = [currentImage size]; 93 | NSPoint mousePoint = [NSEvent mouseLocation]; 94 | CGFloat animationValue = [_animation currentValue]; 95 | 96 | frame.size.width = _originalWindowFrame.size.width + (size.width - _originalWindowFrame.size.width) * animationValue; 97 | frame.size.height = _originalWindowFrame.size.height + (size.height - _originalWindowFrame.size.height) * animationValue; 98 | frame.origin.x = mousePoint.x - (frame.size.width / 2); 99 | frame.origin.y = mousePoint.y - (frame.size.height / 2); 100 | 101 | [_view setFadeValue:_showingAlternate ? 1.0f - animationValue : animationValue]; 102 | [[self window] setFrame:frame display:YES]; 103 | 104 | if(![_animation isAnimating]) { 105 | [_animation release], _animation = nil; 106 | [timer invalidate]; 107 | _timer = nil; 108 | } 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /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 | - (NSString *)name; 21 | 22 | // add tab button 23 | - (NSImage *)addTabButtonImage; 24 | - (NSImage *)addTabButtonPressedImage; 25 | - (NSImage *)addTabButtonRolloverImage; 26 | 27 | @optional 28 | 29 | // control specific parameters 30 | - (CGFloat)leftMarginForTabBarControl:(PSMTabBarControl *)tabBarControl; 31 | - (CGFloat)rightMarginForTabBarControl:(PSMTabBarControl *)tabBarControl; 32 | - (CGFloat)topMarginForTabBarControl:(PSMTabBarControl *)tabBarControl; 33 | - (CGFloat)bottomMarginForTabBarControl:(PSMTabBarControl *)tabBarControl; 34 | - (NSSize)addTabButtonSizeForTabBarControl:(PSMTabBarControl *)tabBarControl; 35 | - (NSRect)addTabButtonRectForTabBarControl:(PSMTabBarControl *)tabBarControl; 36 | - (NSSize)overflowButtonSizeForTabBarControl:(PSMTabBarControl *)tabBarControl; 37 | - (NSRect)overflowButtonRectForTabBarControl:(PSMTabBarControl *)tabBarControl; 38 | 39 | // cell values 40 | - (NSAttributedString *)attributedObjectCountStringValueForTabCell:(PSMTabBarCell *)cell; 41 | - (NSAttributedString *)attributedStringValueForTabCell:(PSMTabBarCell *)cell; 42 | 43 | // Constraints 44 | - (CGFloat)minimumWidthOfTabCell:(PSMTabBarCell *)cell; 45 | - (CGFloat)desiredWidthOfTabCell:(PSMTabBarCell *)cell; 46 | 47 | // Providing Images 48 | - (NSImage *)closeButtonImageOfType:(PSMCloseButtonImageType)type forTabCell:(PSMTabBarCell *)cell; 49 | 50 | // Determining Cell Size 51 | - (CGFloat)heightOfTabCellsForTabBarControl:(PSMTabBarControl *)tabBarControl; 52 | - (NSRect)drawingRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell; 53 | - (NSRect)titleRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell; 54 | - (NSRect)iconRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell; 55 | - (NSRect)largeImageRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell; 56 | - (NSRect)indicatorRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell; 57 | - (NSSize)objectCounterSizeOfTabCell:(PSMTabBarCell *)cell; 58 | - (NSRect)objectCounterRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell; 59 | - (NSRect)closeButtonRectForBounds:(NSRect)theRect ofTabCell:(PSMTabBarCell *)cell; 60 | 61 | // Drawing 62 | - (void)drawTabBarControl:(PSMTabBarControl *)tabBarControl inRect:(NSRect)rect; 63 | - (void)drawBezelOfTabBarControl:(PSMTabBarControl *)tabBarControl inRect:(NSRect)rect; 64 | - (void)drawInteriorOfTabBarControl:(PSMTabBarControl *)tabBarControl inRect:(NSRect)rect; 65 | 66 | - (void)drawTabBarCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 67 | - (void)drawBezelOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 68 | - (void)drawInteriorOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 69 | - (void)drawTitleOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 70 | - (void)drawIconOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl;; 71 | - (void)drawLargeImageOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 72 | - (void)drawIndicatorOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 73 | - (void)drawObjectCounterOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 74 | - (void)drawCloseButtonOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl; 75 | 76 | // Dragging Support 77 | - (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell ofTabBarControl:(PSMTabBarControl *)tabBarControl; 78 | 79 | // Deprecated Stuff 80 | 81 | - (void)drawTabCell:(PSMTabBarCell *)cell DEPRECATED_ATTRIBUTE; 82 | - (NSRect)closeButtonRectForTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)cellFrame DEPRECATED_ATTRIBUTE; 83 | - (NSRect)iconRectForTabCell:(PSMTabBarCell *)cell DEPRECATED_ATTRIBUTE; 84 | - (NSRect)indicatorRectForTabCell:(PSMTabBarCell *)cell DEPRECATED_ATTRIBUTE; 85 | - (NSRect)objectCounterRectForTabCell:(PSMTabBarCell *)cell DEPRECATED_ATTRIBUTE; 86 | - (void)setOrientation:(PSMTabBarOrientation)value DEPRECATED_ATTRIBUTE; 87 | - (void)drawBackgroundInRect:(NSRect)rect DEPRECATED_ATTRIBUTE; 88 | - (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect DEPRECATED_ATTRIBUTE; 89 | - (CGFloat)tabCellHeight DEPRECATED_ATTRIBUTE; 90 | - (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell orientation:(PSMTabBarOrientation)orientation DEPRECATED_ATTRIBUTE; 91 | - (NSAttributedString *)attributedObjectCountValueForTabCell:(PSMTabBarCell *)cell DEPRECATED_ATTRIBUTE; 92 | - (CGFloat)leftMarginForTabBarControl DEPRECATED_ATTRIBUTE; 93 | - (CGFloat)rightMarginForTabBarControl DEPRECATED_ATTRIBUTE; 94 | - (CGFloat)topMarginForTabBarControl DEPRECATED_ATTRIBUTE; 95 | @end 96 | 97 | -------------------------------------------------------------------------------- /Source/PSMUnifiedTabStyle.h: -------------------------------------------------------------------------------- 1 | // 2 | // PSMUnifiedTabStyle.h 3 | // -------------------- 4 | // 5 | // Created by Keith Blount on 30/04/2006. 6 | // Copyright 2006 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PSMTabStyle.h" 11 | 12 | @interface PSMUnifiedTabStyle : NSObject { 13 | 14 | NSImage *unifiedCloseButton; 15 | NSImage *unifiedCloseButtonDown; 16 | NSImage *unifiedCloseButtonOver; 17 | NSImage *unifiedCloseDirtyButton; 18 | NSImage *unifiedCloseDirtyButtonDown; 19 | NSImage *unifiedCloseDirtyButtonOver; 20 | NSImage *_addTabButtonImage; 21 | NSImage *_addTabButtonPressedImage; 22 | NSImage *_addTabButtonRolloverImage; 23 | 24 | CGFloat _leftMargin; 25 | } 26 | 27 | @property (assign) CGFloat leftMarginForTabBarControl; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Source/PSMUnifiedTabStyle.m: -------------------------------------------------------------------------------- 1 | // 2 | // PSMUnifiedTabStyle.m 3 | // -------------------- 4 | // 5 | // Created by Keith Blount on 30/04/2006. 6 | // Copyright 2006 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "PSMUnifiedTabStyle.h" 10 | #import "PSMTabBarCell.h" 11 | #import "PSMTabBarControl.h" 12 | 13 | @implementation PSMUnifiedTabStyle 14 | 15 | @synthesize leftMarginForTabBarControl = _leftMargin; 16 | 17 | + (NSString *)name { 18 | return @"Unified"; 19 | } 20 | 21 | - (NSString *)name { 22 | return [[self class] name]; 23 | } 24 | 25 | #pragma mark - 26 | #pragma mark Creation/Destruction 27 | 28 | - (id) init { 29 | if((self = [super init])) { 30 | unifiedCloseButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front"]]; 31 | unifiedCloseButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Pressed"]]; 32 | unifiedCloseButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabClose_Front_Rollover"]]; 33 | 34 | unifiedCloseDirtyButton = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front"]]; 35 | unifiedCloseDirtyButtonDown = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Pressed"]]; 36 | unifiedCloseDirtyButtonOver = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabCloseDirty_Front_Rollover"]]; 37 | 38 | _addTabButtonImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNew"]]; 39 | _addTabButtonPressedImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewPressed"]]; 40 | _addTabButtonRolloverImage = [[NSImage alloc] initByReferencingFile:[[PSMTabBarControl bundle] pathForImageResource:@"AquaTabNewRollover"]]; 41 | 42 | _leftMargin = 0.0; 43 | } 44 | return self; 45 | } 46 | 47 | - (void)dealloc { 48 | [unifiedCloseButton release]; 49 | [unifiedCloseButtonDown release]; 50 | [unifiedCloseButtonOver release]; 51 | [unifiedCloseDirtyButton release]; 52 | [unifiedCloseDirtyButtonDown release]; 53 | [unifiedCloseDirtyButtonOver release]; 54 | [_addTabButtonImage release]; 55 | [_addTabButtonPressedImage release]; 56 | [_addTabButtonRolloverImage release]; 57 | 58 | [super dealloc]; 59 | } 60 | 61 | #pragma mark - 62 | #pragma mark Control Specific 63 | 64 | - (CGFloat)leftMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 65 | return _leftMargin; 66 | } 67 | 68 | - (CGFloat)rightMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 69 | return _leftMargin; 70 | } 71 | 72 | - (CGFloat)topMarginForTabBarControl:(PSMTabBarControl *)tabBarControl { 73 | return 10.0f; 74 | } 75 | 76 | #pragma mark - 77 | #pragma mark Add Tab Button 78 | 79 | - (NSImage *)addTabButtonImage { 80 | return _addTabButtonImage; 81 | } 82 | 83 | - (NSImage *)addTabButtonPressedImage { 84 | return _addTabButtonPressedImage; 85 | } 86 | 87 | - (NSImage *)addTabButtonRolloverImage { 88 | return _addTabButtonRolloverImage; 89 | } 90 | 91 | #pragma mark - 92 | #pragma mark Drag Support 93 | 94 | - (NSRect)dragRectForTabCell:(PSMTabBarCell *)cell ofTabBarControl:(PSMTabBarControl *)tabBarControl { 95 | NSRect dragRect = [cell frame]; 96 | dragRect.size.width++; 97 | return dragRect; 98 | } 99 | 100 | #pragma mark - 101 | #pragma mark Providing Images 102 | 103 | - (NSImage *)closeButtonImageOfType:(PSMCloseButtonImageType)type forTabCell:(PSMTabBarCell *)cell 104 | { 105 | switch (type) { 106 | case PSMCloseButtonImageTypeStandard: 107 | return unifiedCloseButton; 108 | case PSMCloseButtonImageTypeRollover: 109 | return unifiedCloseButtonOver; 110 | case PSMCloseButtonImageTypePressed: 111 | return unifiedCloseButtonDown; 112 | 113 | case PSMCloseButtonImageTypeDirty: 114 | return unifiedCloseDirtyButton; 115 | case PSMCloseButtonImageTypeDirtyRollover: 116 | return unifiedCloseDirtyButtonOver; 117 | case PSMCloseButtonImageTypeDirtyPressed: 118 | return unifiedCloseDirtyButtonDown; 119 | 120 | default: 121 | break; 122 | } 123 | 124 | } 125 | 126 | #pragma mark - 127 | #pragma mark Drawing 128 | 129 | -(void)drawBezelOfTabCell:(PSMTabBarCell *)cell withFrame:(NSRect)frame inTabBarControl:(PSMTabBarControl *)tabBarControl 130 | { 131 | NSWindow *window = [tabBarControl window]; 132 | NSToolbar *toolbar = [window toolbar]; 133 | 134 | NSBezierPath *bezier = [NSBezierPath bezierPath]; 135 | NSColor *lineColor = [NSColor colorWithCalibratedWhite:0.576 alpha:1.0]; 136 | 137 | if (toolbar && [toolbar isVisible]) { 138 | 139 | NSRect aRect = NSMakeRect(frame.origin.x + 0.5, frame.origin.y - 0.5, frame.size.width, frame.size.height); 140 | 141 | if ([cell isHighlighted] && [cell state] == NSOffState) 142 | { 143 | aRect.origin.y += 1.5; 144 | aRect.size.height -= 1.5; 145 | } 146 | 147 | CGFloat radius = MIN(6.0, 0.5f * MIN(NSWidth(aRect), NSHeight(aRect))); 148 | NSRect rect = NSInsetRect(aRect, radius, radius); 149 | 150 | NSPoint cornerPoint = NSMakePoint(NSMaxX(aRect), NSMinY(aRect)); 151 | [bezier appendBezierPathWithPoints:&cornerPoint count:1]; 152 | 153 | [bezier appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMaxY(rect)) radius:radius startAngle:0.0 endAngle:90.0]; 154 | 155 | [bezier appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMaxY(rect)) radius:radius startAngle:90.0 endAngle:180.0]; 156 | 157 | cornerPoint = NSMakePoint(NSMinX(aRect), NSMinY(aRect)); 158 | [bezier appendBezierPathWithPoints:&cornerPoint count:1]; 159 | 160 | if ([tabBarControl isWindowActive]) { 161 | if ([cell state] == NSOnState) { 162 | NSColor *startColor = [NSColor colorWithDeviceWhite:0.698 alpha:1.000]; 163 | NSColor *endColor = [NSColor colorWithDeviceWhite:0.663 alpha:1.000]; 164 | NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:startColor endingColor:endColor]; 165 | [gradient drawInBezierPath:bezier angle:80.0]; 166 | [gradient release]; 167 | } else if ([cell isHighlighted]) { 168 | NSColor *startColor = [NSColor colorWithDeviceWhite:0.8 alpha:1.000]; 169 | NSColor *endColor = [NSColor colorWithDeviceWhite:0.8 alpha:1.000]; 170 | NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:startColor endingColor:endColor]; 171 | [gradient drawInBezierPath:bezier angle:80.0]; 172 | [gradient release]; 173 | } 174 | 175 | } else { 176 | if ([cell state] == NSOnState) { 177 | NSColor *startColor = [NSColor colorWithDeviceWhite:0.875 alpha:1.000]; 178 | NSColor *endColor = [NSColor colorWithDeviceWhite:0.902 alpha:1.000]; 179 | NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:startColor endingColor:endColor]; 180 | [[NSGraphicsContext currentContext] setShouldAntialias:NO]; 181 | [gradient drawInBezierPath:bezier angle:90.0]; 182 | [[NSGraphicsContext currentContext] setShouldAntialias:YES]; 183 | [gradient release]; 184 | } 185 | } 186 | 187 | [lineColor set]; 188 | [bezier stroke]; 189 | } else { 190 | 191 | NSRect aRect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); 192 | aRect.origin.y += 0.5; 193 | aRect.origin.x += 1.5; 194 | aRect.size.width -= 1; 195 | 196 | aRect.origin.x -= 1; 197 | aRect.size.width += 1; 198 | 199 | 200 | if ([cell state] == NSOnState) { 201 | [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set]; 202 | NSRectFillUsingOperation(aRect, NSCompositeSourceAtop); 203 | } else if([cell isHighlighted]) { 204 | [[NSColor colorWithCalibratedWhite:0.0 alpha:0.1] set]; 205 | NSRectFillUsingOperation(aRect, NSCompositeSourceAtop); 206 | } 207 | 208 | // frame 209 | [lineColor set]; 210 | [bezier moveToPoint:NSMakePoint(aRect.origin.x + aRect.size.width, aRect.origin.y - 0.5)]; 211 | if(!([cell tabState] & PSMTab_RightIsSelectedMask)) { 212 | [bezier lineToPoint:NSMakePoint(NSMaxX(aRect), NSMaxY(aRect))]; 213 | } 214 | 215 | [bezier stroke]; 216 | 217 | // Create a thin lighter line next to the dividing line for a bezel effect 218 | if(!([cell tabState] & PSMTab_RightIsSelectedMask)) { 219 | [[[NSColor whiteColor] colorWithAlphaComponent:0.5] set]; 220 | [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX(aRect) + 1.0, aRect.origin.y - 0.5) 221 | toPoint:NSMakePoint(NSMaxX(aRect) + 1.0, NSMaxY(aRect) - 2.5)]; 222 | } 223 | 224 | // If this is the leftmost tab, we want to draw a line on the left, too 225 | if([cell tabState] & PSMTab_PositionLeftMask) { 226 | [lineColor set]; 227 | [NSBezierPath strokeLineFromPoint:NSMakePoint(aRect.origin.x, aRect.origin.y - 0.5) 228 | toPoint:NSMakePoint(aRect.origin.x, NSMaxY(aRect) - 2.5)]; 229 | [[[NSColor whiteColor] colorWithAlphaComponent:0.5] set]; 230 | [NSBezierPath strokeLineFromPoint:NSMakePoint(aRect.origin.x + 1.0, aRect.origin.y - 0.5) 231 | toPoint:NSMakePoint(aRect.origin.x + 1.0, NSMaxY(aRect) - 2.5)]; 232 | } 233 | } 234 | } 235 | 236 | - (void)drawBezelOfTabBarControl:(PSMTabBarControl *)tabBarControl inRect:(NSRect)rect { 237 | //Draw for our whole bounds; it'll be automatically clipped to fit the appropriate drawing area 238 | rect = [tabBarControl bounds]; 239 | 240 | NSRect gradientRect = rect; 241 | gradientRect.size.height -= 1.0; 242 | 243 | if(![tabBarControl isWindowActive]) { 244 | [[NSColor windowBackgroundColor] set]; 245 | NSRectFill(gradientRect); 246 | } else { 247 | NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:0.835 alpha:1.0] endingColor:[NSColor colorWithCalibratedWhite:0.843 alpha:1.0]]; 248 | [gradient drawInRect:gradientRect angle:90.0]; 249 | [gradient release]; 250 | } 251 | 252 | [[NSColor colorWithCalibratedWhite:0.576 alpha:1.0] set]; 253 | [NSBezierPath strokeLineFromPoint:NSMakePoint(rect.origin.x, NSMinY(rect) + 0.5) 254 | toPoint:NSMakePoint(NSMaxX(rect), NSMinY(rect) + 0.5)]; 255 | } 256 | 257 | #pragma mark - 258 | #pragma mark Archiving 259 | 260 | - (void)encodeWithCoder:(NSCoder *)aCoder { 261 | //[super encodeWithCoder:aCoder]; 262 | if([aCoder allowsKeyedCoding]) { 263 | [aCoder encodeObject:unifiedCloseButton forKey:@"unifiedCloseButton"]; 264 | [aCoder encodeObject:unifiedCloseButtonDown forKey:@"unifiedCloseButtonDown"]; 265 | [aCoder encodeObject:unifiedCloseButtonOver forKey:@"unifiedCloseButtonOver"]; 266 | [aCoder encodeObject:unifiedCloseDirtyButton forKey:@"unifiedCloseDirtyButton"]; 267 | [aCoder encodeObject:unifiedCloseDirtyButtonDown forKey:@"unifiedCloseDirtyButtonDown"]; 268 | [aCoder encodeObject:unifiedCloseDirtyButtonOver forKey:@"unifiedCloseDirtyButtonOver"]; 269 | [aCoder encodeObject:_addTabButtonImage forKey:@"addTabButtonImage"]; 270 | [aCoder encodeObject:_addTabButtonPressedImage forKey:@"addTabButtonPressedImage"]; 271 | [aCoder encodeObject:_addTabButtonRolloverImage forKey:@"addTabButtonRolloverImage"]; 272 | } 273 | } 274 | 275 | - (id)initWithCoder:(NSCoder *)aDecoder { 276 | // self = [super initWithCoder:aDecoder]; 277 | //if (self) { 278 | if([aDecoder allowsKeyedCoding]) { 279 | unifiedCloseButton = [[aDecoder decodeObjectForKey:@"unifiedCloseButton"] retain]; 280 | unifiedCloseButtonDown = [[aDecoder decodeObjectForKey:@"unifiedCloseButtonDown"] retain]; 281 | unifiedCloseButtonOver = [[aDecoder decodeObjectForKey:@"unifiedCloseButtonOver"] retain]; 282 | unifiedCloseDirtyButton = [[aDecoder decodeObjectForKey:@"unifiedCloseDirtyButton"] retain]; 283 | unifiedCloseDirtyButtonDown = [[aDecoder decodeObjectForKey:@"unifiedCloseDirtyButtonDown"] retain]; 284 | unifiedCloseDirtyButtonOver = [[aDecoder decodeObjectForKey:@"unifiedCloseDirtyButtonOver"] retain]; 285 | _addTabButtonImage = [[aDecoder decodeObjectForKey:@"addTabButtonImage"] retain]; 286 | _addTabButtonPressedImage = [[aDecoder decodeObjectForKey:@"addTabButtonPressedImage"] retain]; 287 | _addTabButtonRolloverImage = [[aDecoder decodeObjectForKey:@"addTabButtonRolloverImage"] retain]; 288 | } 289 | //} 290 | return self; 291 | } 292 | 293 | @end 294 | -------------------------------------------------------------------------------- /Source/README: -------------------------------------------------------------------------------- 1 | AUTHOR: Robert Payne 2 | WEBSITE: http://robertjpayne.com 3 | 4 | PSMTabBarControl was originally written by John Pannell 5 | 6 | This port of it is solely to update the Interface Builder plugin and keep it working with 10.6+ 7 | since Interface Builder palettes are now obsolete. This port has removed support for Power-PC 8 | architectures and is intended for Snow Leopard application development though it may work in 9 | Tiger. 10 | 11 | If you find issues with the framework code or ibplugin please submit a patch via git or log 12 | an issue in the ticket tracker on this repository. -------------------------------------------------------------------------------- /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 | return NSApplicationMain(argc, (const char **)argv); 13 | } 14 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | xcodebuild build -configuration Release -target PSMTabBarControlFramework SYMROOT=build OBJROOT=build 3 | 4 | -------------------------------------------------------------------------------- /xcconfigs/Base.xcconfig: -------------------------------------------------------------------------------- 1 | ARCHS = $(ARCHS_STANDARD_32_64_BIT) 2 | GCC_WARN_ABOUT_RETURN_TYPE = YES 3 | GCC_C_LANGUAGE_STANDARD = gnu99 4 | GCC_WARN_UNUSED_VARIABLE = YES 5 | MACOSX_DEPLOYMENT_TARGET = 10.6 6 | SDKROOT = macosx 7 | COMBINE_HIDPI_IMAGES = NO 8 | WARNING_CFLAGS = -Wmost -Wno-four-char-constants -Wno-unknown-pragmas 9 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0 10 | GCC_PREFIX_HEADER = $(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h 11 | GCC_PRECOMPILE_PREFIX_HEADER = YES 12 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES 13 | 14 | -------------------------------------------------------------------------------- /xcconfigs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Base.xcconfig" 2 | ONLY_ACTIVE_ARCH = YES 3 | COPY_PHASE_STRIP = NO 4 | GCC_OPTIMIZATION_LEVEL = 0 5 | GCC_DYNAMIC_NO_PIC = NO 6 | GCC_MODEL_TUNING = G5 7 | -------------------------------------------------------------------------------- /xcconfigs/PSMTabBarControlDemo-Base.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Base.xcconfig" 2 | OTHER_LDFLAGS = -framework Foundation -framework AppKit 3 | PRODUCT_NAME = PSMTabBarControlDemo 4 | INSTALL_PATH = $(HOME)/Applications 5 | INFOPLIST_FILE = Source/PSMTabBarControlDemo-Info.plist 6 | ALWAYS_SEARCH_USER_PATHS = NO 7 | -------------------------------------------------------------------------------- /xcconfigs/PSMTabBarControlDemo-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Debug.xcconfig" 2 | #include "PSMTabBarControlDemo-Base.xcconfig" 3 | -------------------------------------------------------------------------------- /xcconfigs/PSMTabBarControlDemo-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Release.xcconfig" 2 | #include "PSMTabBarControlDemo-Base.xcconfig" 3 | 4 | COPY_PHASE_STRIP = YES 5 | ZERO_LINK = NO 6 | -------------------------------------------------------------------------------- /xcconfigs/PSMTabBarControlFramework-Base.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Base.xcconfig" 2 | PRODUCT_NAME = PSMTabBarControl 3 | DYLIB_CURRENT_VERSION = 1 4 | SKIP_INSTALL = NO 5 | GCC_ENABLE_OBJC_GC = unsupported 6 | FRAMEWORK_VERSION = A 7 | INSTALL_PATH = @rpath 8 | INFOPLIST_FILE = Source/PSMTabBarControl-Info.plist 9 | DYLIB_COMPATIBILITY_VERSION = 1 10 | -------------------------------------------------------------------------------- /xcconfigs/PSMTabBarControlFramework-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Debug.xcconfig" 2 | #include "PSMTabBarControlFramework-Base.xcconfig" 3 | -------------------------------------------------------------------------------- /xcconfigs/PSMTabBarControlFramework-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Release.xcconfig" 2 | #include "PSMTabBarControlFramework-Base.xcconfig" 3 | INSTALL_MODE_FLAG = go-w,a+rX 4 | -------------------------------------------------------------------------------- /xcconfigs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Base.xcconfig" 2 | DEBUG_INFORMATION_FORMAT = dwarf-with-dsym 3 | --------------------------------------------------------------------------------