├── .gitattributes ├── .gitignore ├── GITMate.tmplugin.zip ├── Libraries ├── libapr-1.0.2.12.dylib └── libaprutil-1.0.2.12.dylib ├── ProjectPlus.xcodeproj └── project.pbxproj ├── README.markdown ├── Rakefile ├── dsa_pub.pem ├── libs ├── README ├── apr-1.3.2.tar.bz2 ├── apr-util-1.3.2.tar.bz2 ├── convert.sh └── subversion-1.6.0.tar.bz2 ├── release_notes.txt ├── release_notes_template.html.erb ├── releases └── shot.png ├── resources ├── English.lproj │ └── InfoPlist.strings ├── Info.plist ├── Preferences.xib ├── bkg.tiff ├── gear.tiff ├── gearpressed.tiff ├── gitlogo.tiff ├── icons │ ├── Bookmark │ │ ├── Added.png │ │ ├── Conflicted.png │ │ ├── Deleted.png │ │ ├── Locked.png │ │ ├── Modified.png │ │ ├── ReadOnly.png │ │ ├── Versioned.png │ │ └── badges.svg │ ├── CVSClassic │ │ ├── Added.ico │ │ ├── Conflicted.ico │ │ ├── Deleted.ico │ │ ├── Locked.ico │ │ ├── Modified.ico │ │ ├── ReadOnly.ico │ │ └── Versioned.ico │ ├── Classic │ │ ├── Added.ico │ │ ├── Conflicted.ico │ │ ├── Deleted.ico │ │ ├── Locked.ico │ │ ├── Modified.ico │ │ ├── ReadOnly.ico │ │ └── Versioned.ico │ ├── Modern │ │ ├── Added.ico │ │ ├── Conflicted.ico │ │ ├── Deleted.ico │ │ ├── Locked.ico │ │ ├── Modified.ico │ │ ├── ReadOnly.ico │ │ └── Versioned.ico │ ├── Straight │ │ ├── Added.ico │ │ ├── Conflicted.ico │ │ ├── Deleted.ico │ │ ├── Locked.ico │ │ ├── Modified.ico │ │ ├── ReadOnly.ico │ │ └── Versioned.ico │ ├── Subclipse │ │ ├── Added.ico │ │ ├── Conflicted.ico │ │ ├── Deleted.ico │ │ ├── Locked.ico │ │ ├── Modified.ico │ │ ├── ReadOnly.ico │ │ └── Versioned.ico │ ├── XPStyle │ │ ├── Added.ico │ │ ├── Conflicted.ico │ │ ├── Deleted.ico │ │ ├── Locked.ico │ │ ├── Modified.ico │ │ ├── ReadOnly.ico │ │ └── Versioned.ico │ └── license.txt ├── info.tiff ├── infodisabled.tiff ├── infopressed.tiff ├── plus.tiff ├── plusdir.tiff ├── plusdirpressed.tiff ├── pluspressed.tiff └── replacement-icons │ ├── Executable.tiff │ ├── Folder.tiff │ ├── FolderRef.tiff │ ├── Oak-Generic.tiff │ ├── Oak-Text.tiff │ ├── PBX-actionscript-Icon.tiff │ ├── PBX-applescript-Iconf.tif │ ├── PBX-bundle-Icon.tiff │ ├── PBX-c++-Icon.tiff │ ├── PBX-c-Icon.tiff │ ├── PBX-h-Icon.tiff │ ├── PBX-html-Icon.tiff │ ├── PBX-java-Icon.tiff │ ├── PBX-lib-Icon.tiff │ ├── PBX-m-Icon.tiff │ ├── PBX-php-Icon.tiff │ ├── PBX-plist-Icon.tiff │ ├── PBX-r-Icon.tiff │ ├── PBX-rsrc-Icon.tiff │ ├── PBX-ruby-Icon.tiff │ ├── SmartFolder.tiff │ ├── SymLink.png │ └── txtIcon.png └── src ├── OpenWith.mm ├── ProjectPlus.h ├── ProjectPlus.mm ├── ProjectTree.mm ├── Sorting.mm ├── TMLabels.mm ├── etc ├── JRSwizzle.h ├── JRSwizzle.m ├── ProjectPlus_Prefix.pch └── TextMate.h └── scm ├── GitIcons.mm ├── SCMIcons.h └── SCMIcons.mm /.gitattributes: -------------------------------------------------------------------------------- 1 | Preferences.xib -diff 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | ProjectPlus.xcodeproj/ciaran.* 3 | *.tmproj 4 | .DS_Store 5 | dsa_priv.pem 6 | -------------------------------------------------------------------------------- /GITMate.tmplugin.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/GITMate.tmplugin.zip -------------------------------------------------------------------------------- /Libraries/libapr-1.0.2.12.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/Libraries/libapr-1.0.2.12.dylib -------------------------------------------------------------------------------- /Libraries/libaprutil-1.0.2.12.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/Libraries/libaprutil-1.0.2.12.dylib -------------------------------------------------------------------------------- /ProjectPlus.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0E17634A113C7F3C00F1C618 /* gitlogo.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0E176349113C7F3C00F1C618 /* gitlogo.tiff */; }; 11 | 0EE8F50410A3E17600DD8EBA /* bkg.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0EE8F50110A3E17600DD8EBA /* bkg.tiff */; }; 12 | 0EE8F50510A3E17600DD8EBA /* gear.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0EE8F50210A3E17600DD8EBA /* gear.tiff */; }; 13 | 0EE8F50610A3E17600DD8EBA /* plus.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0EE8F50310A3E17600DD8EBA /* plus.tiff */; }; 14 | 0EE8F5FB10A3E85300DD8EBA /* plusdir.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0EE8F5FA10A3E85300DD8EBA /* plusdir.tiff */; }; 15 | 0EE8F60710A3E94400DD8EBA /* pluspressed.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0EE8F60610A3E94400DD8EBA /* pluspressed.tiff */; }; 16 | 0EE8F61A10A3EB3300DD8EBA /* gearpressed.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0EE8F61910A3EB3300DD8EBA /* gearpressed.tiff */; }; 17 | 0EE8F62D10A3ECE100DD8EBA /* plusdirpressed.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0EE8F62C10A3ECE100DD8EBA /* plusdirpressed.tiff */; }; 18 | 0EE8F6B710A3EF5600DD8EBA /* info.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0EE8F6B410A3EF5600DD8EBA /* info.tiff */; }; 19 | 0EE8F6B810A3EF5600DD8EBA /* infodisabled.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0EE8F6B510A3EF5600DD8EBA /* infodisabled.tiff */; }; 20 | 0EE8F6B910A3EF5600DD8EBA /* infopressed.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 0EE8F6B610A3EF5600DD8EBA /* infopressed.tiff */; }; 21 | 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; 22 | 913A313F0CFFC75600436C23 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 913A313E0CFFC75600436C23 /* Security.framework */; }; 23 | 915AD42B0ED9E2070017679A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 915AD3DA0ED9E2070017679A /* InfoPlist.strings */; }; 24 | 915AD4600ED9E2070017679A /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 915AD4180ED9E2070017679A /* Preferences.xib */; }; 25 | 915AD4F30ED9E6AD0017679A /* OpenWith.mm in Sources */ = {isa = PBXBuildFile; fileRef = 915AD4D60ED9E6AD0017679A /* OpenWith.mm */; }; 26 | 915AD4F40ED9E6AD0017679A /* ProjectPlus.mm in Sources */ = {isa = PBXBuildFile; fileRef = 915AD4D80ED9E6AD0017679A /* ProjectPlus.mm */; }; 27 | 915AD4F50ED9E6AD0017679A /* ProjectTree.mm in Sources */ = {isa = PBXBuildFile; fileRef = 915AD4DA0ED9E6AD0017679A /* ProjectTree.mm */; }; 28 | 915AD4F70ED9E6AD0017679A /* GitIcons.mm in Sources */ = {isa = PBXBuildFile; fileRef = 915AD4DF0ED9E6AD0017679A /* GitIcons.mm */; }; 29 | 915AD4F90ED9E6AD0017679A /* SCMIcons.mm in Sources */ = {isa = PBXBuildFile; fileRef = 915AD4E30ED9E6AD0017679A /* SCMIcons.mm */; }; 30 | 915AD4FF0ED9E6AD0017679A /* Sorting.mm in Sources */ = {isa = PBXBuildFile; fileRef = 915AD4EE0ED9E6AD0017679A /* Sorting.mm */; }; 31 | 915AD5000ED9E6AD0017679A /* TMLabels.mm in Sources */ = {isa = PBXBuildFile; fileRef = 915AD4F00ED9E6AD0017679A /* TMLabels.mm */; }; 32 | 915AD5100ED9E7170017679A /* JRSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 915AD50B0ED9E7170017679A /* JRSwizzle.m */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXCopyFilesBuildPhase section */ 36 | 911112A10E5C8D9900BF76B4 /* CopyFiles */ = { 37 | isa = PBXCopyFilesBuildPhase; 38 | buildActionMask = 2147483647; 39 | dstPath = ""; 40 | dstSubfolderSpec = 10; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXCopyFilesBuildPhase section */ 46 | 47 | /* Begin PBXFileReference section */ 48 | 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 49 | 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 50 | 0E176349113C7F3C00F1C618 /* gitlogo.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = gitlogo.tiff; sourceTree = ""; }; 51 | 0EE8F50110A3E17600DD8EBA /* bkg.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = bkg.tiff; sourceTree = ""; }; 52 | 0EE8F50210A3E17600DD8EBA /* gear.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = gear.tiff; sourceTree = ""; }; 53 | 0EE8F50310A3E17600DD8EBA /* plus.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = plus.tiff; sourceTree = ""; }; 54 | 0EE8F5FA10A3E85300DD8EBA /* plusdir.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = plusdir.tiff; sourceTree = ""; }; 55 | 0EE8F60610A3E94400DD8EBA /* pluspressed.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = pluspressed.tiff; sourceTree = ""; }; 56 | 0EE8F61910A3EB3300DD8EBA /* gearpressed.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = gearpressed.tiff; sourceTree = ""; }; 57 | 0EE8F62C10A3ECE100DD8EBA /* plusdirpressed.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = plusdirpressed.tiff; sourceTree = ""; }; 58 | 0EE8F6B410A3EF5600DD8EBA /* info.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = info.tiff; sourceTree = ""; }; 59 | 0EE8F6B510A3EF5600DD8EBA /* infodisabled.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = infodisabled.tiff; sourceTree = ""; }; 60 | 0EE8F6B610A3EF5600DD8EBA /* infopressed.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = infopressed.tiff; sourceTree = ""; }; 61 | 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 62 | 8D5B49B6048680CD000E48DA /* GITMate.tmplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GITMate.tmplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 913A313E0CFFC75600436C23 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = ""; }; 64 | 915AD3DB0ED9E2070017679A /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 65 | 915AD4180ED9E2070017679A /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = ""; }; 66 | 915AD4D60ED9E6AD0017679A /* OpenWith.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OpenWith.mm; sourceTree = ""; }; 67 | 915AD4D70ED9E6AD0017679A /* ProjectPlus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectPlus.h; sourceTree = ""; }; 68 | 915AD4D80ED9E6AD0017679A /* ProjectPlus.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ProjectPlus.mm; sourceTree = ""; }; 69 | 915AD4DA0ED9E6AD0017679A /* ProjectTree.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ProjectTree.mm; sourceTree = ""; }; 70 | 915AD4DF0ED9E6AD0017679A /* GitIcons.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GitIcons.mm; sourceTree = ""; }; 71 | 915AD4E20ED9E6AD0017679A /* SCMIcons.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCMIcons.h; sourceTree = ""; }; 72 | 915AD4E30ED9E6AD0017679A /* SCMIcons.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SCMIcons.mm; sourceTree = ""; }; 73 | 915AD4EE0ED9E6AD0017679A /* Sorting.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Sorting.mm; sourceTree = ""; }; 74 | 915AD4F00ED9E6AD0017679A /* TMLabels.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TMLabels.mm; sourceTree = ""; }; 75 | 915AD50A0ED9E7170017679A /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = ""; }; 76 | 915AD50B0ED9E7170017679A /* JRSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JRSwizzle.m; sourceTree = ""; }; 77 | 915AD50E0ED9E7170017679A /* ProjectPlus_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectPlus_Prefix.pch; sourceTree = ""; }; 78 | 915AD50F0ED9E7170017679A /* TextMate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextMate.h; sourceTree = ""; }; 79 | D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; 80 | /* End PBXFileReference section */ 81 | 82 | /* Begin PBXFrameworksBuildPhase section */ 83 | 8D5B49B3048680CD000E48DA /* Frameworks */ = { 84 | isa = PBXFrameworksBuildPhase; 85 | buildActionMask = 2147483647; 86 | files = ( 87 | 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */, 88 | 913A313F0CFFC75600436C23 /* Security.framework in Frameworks */, 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | /* End PBXFrameworksBuildPhase section */ 93 | 94 | /* Begin PBXGroup section */ 95 | 089C166AFE841209C02AAC07 /* ProjectPlus */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 915AD4D30ED9E6AD0017679A /* src */, 99 | 915AD3D90ED9E2070017679A /* resources */, 100 | 089C1671FE841209C02AAC07 /* Frameworks and Libraries */, 101 | 19C28FB8FE9D52D311CA2CBB /* Products */, 102 | ); 103 | name = ProjectPlus; 104 | sourceTree = ""; 105 | }; 106 | 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 91A39A180E16DB11003FECC8 /* Libraries */, 110 | 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */, 111 | 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */, 112 | ); 113 | name = "Frameworks and Libraries"; 114 | sourceTree = ""; 115 | }; 116 | 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */, 120 | ); 121 | name = "Linked Frameworks"; 122 | sourceTree = ""; 123 | }; 124 | 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 913A313E0CFFC75600436C23 /* Security.framework */, 128 | 089C167FFE841241C02AAC07 /* AppKit.framework */, 129 | D2F7E65807B2D6F200F64583 /* CoreData.framework */, 130 | 089C1672FE841209C02AAC07 /* Foundation.framework */, 131 | ); 132 | name = "Other Frameworks"; 133 | sourceTree = ""; 134 | }; 135 | 19C28FB8FE9D52D311CA2CBB /* Products */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 8D5B49B6048680CD000E48DA /* GITMate.tmplugin */, 139 | ); 140 | name = Products; 141 | sourceTree = ""; 142 | }; 143 | 915AD3D90ED9E2070017679A /* resources */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 0EE8F6B410A3EF5600DD8EBA /* info.tiff */, 147 | 0EE8F6B510A3EF5600DD8EBA /* infodisabled.tiff */, 148 | 0E176349113C7F3C00F1C618 /* gitlogo.tiff */, 149 | 0EE8F6B610A3EF5600DD8EBA /* infopressed.tiff */, 150 | 0EE8F62C10A3ECE100DD8EBA /* plusdirpressed.tiff */, 151 | 0EE8F61910A3EB3300DD8EBA /* gearpressed.tiff */, 152 | 0EE8F60610A3E94400DD8EBA /* pluspressed.tiff */, 153 | 0EE8F5FA10A3E85300DD8EBA /* plusdir.tiff */, 154 | 0EE8F50110A3E17600DD8EBA /* bkg.tiff */, 155 | 0EE8F50210A3E17600DD8EBA /* gear.tiff */, 156 | 0EE8F50310A3E17600DD8EBA /* plus.tiff */, 157 | 915AD3DA0ED9E2070017679A /* InfoPlist.strings */, 158 | 915AD4180ED9E2070017679A /* Preferences.xib */, 159 | ); 160 | path = resources; 161 | sourceTree = ""; 162 | }; 163 | 915AD4D30ED9E6AD0017679A /* src */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 915AD4DB0ED9E6AD0017679A /* scm */, 167 | 915AD5090ED9E7170017679A /* etc */, 168 | 915AD4D60ED9E6AD0017679A /* OpenWith.mm */, 169 | 915AD4D70ED9E6AD0017679A /* ProjectPlus.h */, 170 | 915AD4D80ED9E6AD0017679A /* ProjectPlus.mm */, 171 | 915AD4DA0ED9E6AD0017679A /* ProjectTree.mm */, 172 | 915AD4EE0ED9E6AD0017679A /* Sorting.mm */, 173 | 915AD4F00ED9E6AD0017679A /* TMLabels.mm */, 174 | ); 175 | path = src; 176 | sourceTree = ""; 177 | }; 178 | 915AD4DB0ED9E6AD0017679A /* scm */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | 915AD4DF0ED9E6AD0017679A /* GitIcons.mm */, 182 | 915AD4E20ED9E6AD0017679A /* SCMIcons.h */, 183 | 915AD4E30ED9E6AD0017679A /* SCMIcons.mm */, 184 | ); 185 | path = scm; 186 | sourceTree = ""; 187 | }; 188 | 915AD5090ED9E7170017679A /* etc */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 915AD50A0ED9E7170017679A /* JRSwizzle.h */, 192 | 915AD50B0ED9E7170017679A /* JRSwizzle.m */, 193 | 915AD50E0ED9E7170017679A /* ProjectPlus_Prefix.pch */, 194 | 915AD50F0ED9E7170017679A /* TextMate.h */, 195 | ); 196 | path = etc; 197 | sourceTree = ""; 198 | }; 199 | 91A39A180E16DB11003FECC8 /* Libraries */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | ); 203 | path = Libraries; 204 | sourceTree = ""; 205 | }; 206 | /* End PBXGroup section */ 207 | 208 | /* Begin PBXNativeTarget section */ 209 | 8D5B49AC048680CD000E48DA /* GITMate */ = { 210 | isa = PBXNativeTarget; 211 | buildConfigurationList = 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "GITMate" */; 212 | buildPhases = ( 213 | 8D5B49AF048680CD000E48DA /* Resources */, 214 | 911112A10E5C8D9900BF76B4 /* CopyFiles */, 215 | 8D5B49B1048680CD000E48DA /* Sources */, 216 | 8D5B49B3048680CD000E48DA /* Frameworks */, 217 | 913A31260CFFC21600436C23 /* ShellScript */, 218 | 177E4DB50913322B0064163D /* ShellScript */, 219 | ); 220 | buildRules = ( 221 | ); 222 | dependencies = ( 223 | ); 224 | name = GITMate; 225 | productInstallPath = "$(HOME)/Library/Bundles"; 226 | productName = ProjectPlus; 227 | productReference = 8D5B49B6048680CD000E48DA /* GITMate.tmplugin */; 228 | productType = "com.apple.product-type.bundle"; 229 | }; 230 | /* End PBXNativeTarget section */ 231 | 232 | /* Begin PBXProject section */ 233 | 089C1669FE841209C02AAC07 /* Project object */ = { 234 | isa = PBXProject; 235 | buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "ProjectPlus" */; 236 | compatibilityVersion = "Xcode 3.2"; 237 | hasScannedForEncodings = 1; 238 | mainGroup = 089C166AFE841209C02AAC07 /* ProjectPlus */; 239 | projectDirPath = ""; 240 | projectRoot = ""; 241 | targets = ( 242 | 8D5B49AC048680CD000E48DA /* GITMate */, 243 | ); 244 | }; 245 | /* End PBXProject section */ 246 | 247 | /* Begin PBXResourcesBuildPhase section */ 248 | 8D5B49AF048680CD000E48DA /* Resources */ = { 249 | isa = PBXResourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | 915AD42B0ED9E2070017679A /* InfoPlist.strings in Resources */, 253 | 915AD4600ED9E2070017679A /* Preferences.xib in Resources */, 254 | 0EE8F50410A3E17600DD8EBA /* bkg.tiff in Resources */, 255 | 0EE8F50510A3E17600DD8EBA /* gear.tiff in Resources */, 256 | 0EE8F50610A3E17600DD8EBA /* plus.tiff in Resources */, 257 | 0EE8F5FB10A3E85300DD8EBA /* plusdir.tiff in Resources */, 258 | 0EE8F60710A3E94400DD8EBA /* pluspressed.tiff in Resources */, 259 | 0EE8F61A10A3EB3300DD8EBA /* gearpressed.tiff in Resources */, 260 | 0EE8F62D10A3ECE100DD8EBA /* plusdirpressed.tiff in Resources */, 261 | 0EE8F6B710A3EF5600DD8EBA /* info.tiff in Resources */, 262 | 0EE8F6B810A3EF5600DD8EBA /* infodisabled.tiff in Resources */, 263 | 0EE8F6B910A3EF5600DD8EBA /* infopressed.tiff in Resources */, 264 | 0E17634A113C7F3C00F1C618 /* gitlogo.tiff in Resources */, 265 | ); 266 | runOnlyForDeploymentPostprocessing = 0; 267 | }; 268 | /* End PBXResourcesBuildPhase section */ 269 | 270 | /* Begin PBXShellScriptBuildPhase section */ 271 | 177E4DB50913322B0064163D /* ShellScript */ = { 272 | isa = PBXShellScriptBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | ); 276 | inputPaths = ( 277 | ); 278 | outputPaths = ( 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | shellPath = /bin/sh; 282 | shellScript = "mkdir -p \"$HOME/Library/Application Support/TextMate/PlugIns\"\nrm -rf \"$HOME/Library/Application Support/TextMate/PlugIns/${FULL_PRODUCT_NAME}\"\ncp -pR \"${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}\" \"$HOME/Library/Application Support/TextMate/PlugIns\""; 283 | }; 284 | 913A31260CFFC21600436C23 /* ShellScript */ = { 285 | isa = PBXShellScriptBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | ); 289 | inputPaths = ( 290 | ); 291 | outputPaths = ( 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | shellPath = /bin/sh; 295 | shellScript = "cp -R resources/icons \"$TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH\"\ncp -R Libraries \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH\"\n"; 296 | }; 297 | /* End PBXShellScriptBuildPhase section */ 298 | 299 | /* Begin PBXSourcesBuildPhase section */ 300 | 8D5B49B1048680CD000E48DA /* Sources */ = { 301 | isa = PBXSourcesBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | 915AD4F30ED9E6AD0017679A /* OpenWith.mm in Sources */, 305 | 915AD4F40ED9E6AD0017679A /* ProjectPlus.mm in Sources */, 306 | 915AD4F50ED9E6AD0017679A /* ProjectTree.mm in Sources */, 307 | 915AD4F70ED9E6AD0017679A /* GitIcons.mm in Sources */, 308 | 915AD4F90ED9E6AD0017679A /* SCMIcons.mm in Sources */, 309 | 915AD4FF0ED9E6AD0017679A /* Sorting.mm in Sources */, 310 | 915AD5000ED9E6AD0017679A /* TMLabels.mm in Sources */, 311 | 915AD5100ED9E7170017679A /* JRSwizzle.m in Sources */, 312 | ); 313 | runOnlyForDeploymentPostprocessing = 0; 314 | }; 315 | /* End PBXSourcesBuildPhase section */ 316 | 317 | /* Begin PBXVariantGroup section */ 318 | 915AD3DA0ED9E2070017679A /* InfoPlist.strings */ = { 319 | isa = PBXVariantGroup; 320 | children = ( 321 | 915AD3DB0ED9E2070017679A /* English */, 322 | ); 323 | name = InfoPlist.strings; 324 | sourceTree = ""; 325 | }; 326 | /* End PBXVariantGroup section */ 327 | 328 | /* Begin XCBuildConfiguration section */ 329 | 1DEB913B08733D840010E9CD /* Debug */ = { 330 | isa = XCBuildConfiguration; 331 | buildSettings = { 332 | COPY_PHASE_STRIP = NO; 333 | FRAMEWORK_SEARCH_PATHS = ( 334 | "$(SRCROOT)/Frameworks", 335 | "$(inherited)", 336 | ); 337 | GCC_DYNAMIC_NO_PIC = NO; 338 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 339 | GCC_MODEL_TUNING = G5; 340 | GCC_OPTIMIZATION_LEVEL = 0; 341 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 342 | GCC_PREFIX_HEADER = src/etc/ProjectPlus_Prefix.pch; 343 | HEADER_SEARCH_PATHS = ( 344 | "/usr/include/apr-1/", 345 | "/usr/include/subversion-1/", 346 | ); 347 | INFOPLIST_FILE = resources/Info.plist; 348 | LIBRARY_SEARCH_PATHS = ( 349 | "$(SRCROOT)/Libraries", 350 | "$(inherited)", 351 | ); 352 | PRODUCT_NAME = ProjectPlus; 353 | WRAPPER_EXTENSION = tmplugin; 354 | }; 355 | name = Debug; 356 | }; 357 | 1DEB913C08733D840010E9CD /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ARCHS = ( 361 | i386, 362 | ppc, 363 | ); 364 | FRAMEWORK_SEARCH_PATHS = ( 365 | "$(SRCROOT)/Frameworks", 366 | "$(inherited)", 367 | ); 368 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 369 | GCC_MODEL_TUNING = ""; 370 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 371 | GCC_PREFIX_HEADER = src/etc/ProjectPlus_Prefix.pch; 372 | INFOPLIST_FILE = resources/Info.plist; 373 | LIBRARY_SEARCH_PATHS = ( 374 | "$(SRCROOT)/Libraries", 375 | "$(inherited)", 376 | ); 377 | PRODUCT_NAME = GITMate; 378 | WRAPPER_EXTENSION = tmplugin; 379 | }; 380 | name = Release; 381 | }; 382 | 1DEB913F08733D840010E9CD /* Debug */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | FRAMEWORK_SEARCH_PATHS = "Frameworks/**"; 386 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 387 | GCC_WARN_UNUSED_VARIABLE = YES; 388 | HEADER_SEARCH_PATHS = "libs/**"; 389 | SDKROOT = macosx10.6; 390 | }; 391 | name = Debug; 392 | }; 393 | 1DEB914008733D840010E9CD /* Release */ = { 394 | isa = XCBuildConfiguration; 395 | buildSettings = { 396 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 397 | FRAMEWORK_SEARCH_PATHS = "Frameworks/**"; 398 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 399 | GCC_WARN_UNUSED_VARIABLE = YES; 400 | HEADER_SEARCH_PATHS = ( 401 | "/usr/include/apr-1/", 402 | "/usr/include/subversion-1/", 403 | ); 404 | PREBINDING = YES; 405 | SDKROOT = macosx10.6; 406 | VALID_ARCHS = i386; 407 | }; 408 | name = Release; 409 | }; 410 | /* End XCBuildConfiguration section */ 411 | 412 | /* Begin XCConfigurationList section */ 413 | 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "GITMate" */ = { 414 | isa = XCConfigurationList; 415 | buildConfigurations = ( 416 | 1DEB913B08733D840010E9CD /* Debug */, 417 | 1DEB913C08733D840010E9CD /* Release */, 418 | ); 419 | defaultConfigurationIsVisible = 0; 420 | defaultConfigurationName = Release; 421 | }; 422 | 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "ProjectPlus" */ = { 423 | isa = XCConfigurationList; 424 | buildConfigurations = ( 425 | 1DEB913F08733D840010E9CD /* Debug */, 426 | 1DEB914008733D840010E9CD /* Release */, 427 | ); 428 | defaultConfigurationIsVisible = 0; 429 | defaultConfigurationName = Release; 430 | }; 431 | /* End XCConfigurationList section */ 432 | }; 433 | rootObject = 089C1669FE841209C02AAC07 /* Project object */; 434 | } 435 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | ![Screen Shot](http://github.com/rsence/projectplus/raw/master/releases/shot.png) 2 | 3 | An reduced-functionality fork of ProjectPlus called GITMate. 4 | 5 | As I like the default look and feel of TextMate, I didn't like the added features, added slowness and added instability effects of ProjectPlus. However, I really needed badging for GIT repositories, just like SVNMate is for SVN repositories, so I forked ProjectPlus and removed all the added bloat and left only GIT Support. 6 | 7 | Summary: 8 | 9 | * Works with Snow Leopard and TextMate 1.5.9 (1589) 10 | 11 | * Fast 12 | 13 | * Doesn't seem to conflict with other plugins (with my limited testing) 14 | 15 | * GITMate supports only GIT icon badging, it's like SVNMate, just for GIT. 16 | 17 | * Removed sidebars, sorting and all kinds of crashy, patchy features 18 | 19 | -- Juha-Jarmo Heinonen -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "rubygems" 2 | require "rake" 3 | 4 | require "choctop" 5 | 6 | ChocTop.new do |s| 7 | s.info_plist_path = 'resources/Info.plist' 8 | s.remote_dir = 'ciaranwal.sh/projectplus-files' 9 | s.target = "ProjectPlus.tmplugin" 10 | end 11 | -------------------------------------------------------------------------------- /dsa_pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIDOjCCAi0GByqGSM44BAEwggIgAoIBAQDpmd941RlsHn2X7rudy+xwTb/LnE0v 3 | o1NYqZc3DLhxRciOwBuoE/iuAre59nrfYNjwjaEXrkoOvqd8ZJY8iAf+G4xMijsq 4 | F8o+zfL1POwyoY45ARt/JlhXu1+Y8Ls1OKviyYvrmfekQlcB3xvETeqtGRR8IIlU 5 | 1y6pDR/V6GEn+Tm2NIlTW11JwrtGw8PI1CcT51S2IiwK8Fho2gDPT3NDynIkNF3m 6 | rMuLVbezPq7KfzyJHAfN1hvzkwmdsu82fijILygoNb03BjTfF7AF/+7nMSpLYT1+ 7 | iq/HAZ9OPvPZUVChsk7WXytPVhkivo86Xs3sUVTc4PZUdtVSM45K/5THAhUAoAbd 8 | cebX63vJQ6xtr1pvl4JOwzcCggEAEoCVNekBKnjxbO1smYeFf+ffS/HETjOgQAN+ 9 | EbPgzk1QJike0otDcRN3bzyTmApZBcmilDAPsp9ao6kBntLz215jf4oNCCCSQNh2 10 | AEX+z9MGfuXxBRkkZAyqSpkkLCcpUK+DMK75dNaePMDMiZpmse5WaQVd5Bk1f/gp 11 | 5OBrZmzbPkcHx3nDdIQwP11xZXOtAdS1K3eLtc+0IZggrp7ralqBoIZo7c1Dy8c0 12 | DZeBEC6yRAaHlew54huOJNOl58RaHM01rol6AqoE308w+G5rjoLx8CUf6/8N/zgl 13 | Fqnz0MQctkbtx0QCVTajv5Lz/wVI8edj7rXH0mhvUys8QfGXqgOCAQUAAoIBAFQw 14 | aC3BGsochKBBvHXgN+gg4UI2zl5fW3KOqs6wnig5cPj+2jiwPvQrmj5Tipv8qnvK 15 | jaCj+qP4pRqWMLlvZPznwB7eMArmympw26WmB1c18VZboIo+vBJCQtSlHFRmGc4j 16 | 4iz4tJOcajzlV9f5NsJgUoU8MIiZHvw+Cz58BshElyp/wVO2d+QSp8tW8oD9zBFR 17 | 8DhBAhPajDcfFXa1q677Lo3Y3Zz1kvO3+CwQh6gFPTyKl9PHg2OONtYvf+wMYTDT 18 | hshtgZHNH0EW4DW9bo6Pw9sbDW0xxvXuZi/EtKpMWzJvs+3xV9U0rw2bMpQo+DZe 19 | BoO+H8Q0GHSET+7vyeQ= 20 | -----END PUBLIC KEY----- 21 | -------------------------------------------------------------------------------- /libs/README: -------------------------------------------------------------------------------- 1 | Download APR from http://www.smudge-it.co.uk/pub/apache/apr/apr-1.3.2.tar.bz2 2 | 3 | * configure 4 | * Edit Makefile: change libdir to libdir = @loader_path/../Libraries 5 | * Remove the check in ./libtool to allow for relative rpath values 6 | * make EXTRA_CFLAGS='-arch ppc -arch i386' 7 | 8 | Download APR-Util http://www.smudge-it.co.uk/pub/apache/apr/apr-util-1.3.2.tar.bz2 9 | 10 | * configure --with-apr=/path/to/apr/download 11 | * Edit Makefile: change APU_DSO_LIBDIR and libdir to @loader_path/../Libraries 12 | * make EXTRA_CFLAGS='-arch ppc -arch i386' 13 | 14 | Downloaded SVN from http://subversion.tigris.org/downloads/subversion-1.6.0.tar.bz2 15 | 16 | * configure --with-apr=/path/to/apr/download --with-apr-util=/path/to/apr-util/download --without-neon 17 | * Edit the libdir= line in the Makefile to be: libdir = @loader_path/../Libraries 18 | * Remove the check in ./libtool to allow for relative rpath values 19 | * Run make EXTRA_CFLAGS='-arch ppc -arch i386' 20 | -------------------------------------------------------------------------------- /libs/apr-1.3.2.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/libs/apr-1.3.2.tar.bz2 -------------------------------------------------------------------------------- /libs/apr-util-1.3.2.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/libs/apr-util-1.3.2.tar.bz2 -------------------------------------------------------------------------------- /libs/convert.sh: -------------------------------------------------------------------------------- 1 | for lib in "$1"/*.dylib; do 2 | echo "Processing $lib" 3 | install_path="$(otool -D "$lib"|tail -n1)" 4 | new_install_path="${install_path/executable_path/loader_path}" 5 | if [[ "${install_path}" == "${new_install_path}" ]]; then 6 | echo "- Install path already uses @loader_path" 7 | else 8 | echo "- Changing install path from $install_path to ${new_install_path}" 9 | fi 10 | install_name_tool -id "${new_install_path}" "$lib" 11 | otool -L "$lib"|egrep --only-matching '^ @executable_path.+\.dylib'|while read dependancy_path; do 12 | echo "- Switching dependancy path ${dependancy_path} to @loader_path${dependancy_path#@executable_path}" 13 | install_name_tool -change "${dependancy_path}" "@loader_path${dependancy_path#@executable_path}" "$lib" 14 | done 15 | done -------------------------------------------------------------------------------- /libs/subversion-1.6.0.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/libs/subversion-1.6.0.tar.bz2 -------------------------------------------------------------------------------- /release_notes.txt: -------------------------------------------------------------------------------- 1 | h1. GITMate Version 1.1 2 | * Removed extra styling from the shelf (sidebar). Now icons align up nicely when used with SVNMate. 3 | 4 | h1. GITMate Version 1.0 5 | * Removed extra code from ProjectPlus until only GIT Badges were supported. 6 | * ProjectPlus itself just hung TextMate and I prefer the default TextMate sidebar. 7 | * Didn't do too much else about it. 8 | 9 | h1. ProjectPlus Version 1.3 10 | 11 | * Updated Subversion libraries to 1.6 12 | * Preferences for default sorting options (thanks to Lakshmi Vya) 13 | 14 | h1. ProjectPlus Version 1.2 15 | 16 | * SCM Badges would not display in some cases 17 | ** When a project was saved in a directory other than the project root, badges would not display. 18 | ** Also fixes some other less common cases which would prevent badges from working. 19 | 20 | Please report any issues you find at the "bug tracker":http://ciaranwal.sh/projectplus-files/ticket. 21 | 22 | h1. ProjectPlus Version 1.1 23 | 24 | * Sidebar 25 | ** TextMate does not load plug-ins until after the application is finished launching, which was causing problems when launching by double-clicking a project file (the project would open without a drawer or pane). This is now handled gracefully, but projects opened in this way will always have the drawer as they are opened before ProjectPlus is loaded. 26 | * SCM Badges 27 | ** Added support for Mercurial, Svk and Bazaar (these should be considered experimental – I don’t use them so please report issues) 28 | ** Hopefully more to come, based on demand and how easy they are to implement 29 | ** Since there are now quite a few, and some of them are expensive to have enabled (as all except SVN use shell execution), all of the SCM modules are disabled by default and you must selectively enable the ones you want 30 | ** Fixed a memory leak that could occur when using SVN badging 31 | ** Misc. performance improvements and bugfixes 32 | * General: 33 | ** Added Sparkle for automatic updates to future versions 34 | ** Added an icon (thanks to Oliver Busch) 35 | ** Other miscellaneous tweaks and improvements 36 | 37 | Please report any issues you find at the new "bug tracker":http://ciaranwal.sh/projectplus-files/ticket. 38 | -------------------------------------------------------------------------------- /release_notes_template.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | Release Notes 4 | 9 | 10 | 11 | <%= release_notes_html %> 12 | 13 | -------------------------------------------------------------------------------- /releases/shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/releases/shot.png -------------------------------------------------------------------------------- /resources/English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | gitlogo.tiff 11 | CFBundleIdentifier 12 | com.sorsacode.gitmate 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | NSPrincipalClass 24 | GITMate 25 | ReliesOnClassesFromVersion 26 | 1400 27 | 28 | 29 | -------------------------------------------------------------------------------- /resources/bkg.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/bkg.tiff -------------------------------------------------------------------------------- /resources/gear.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/gear.tiff -------------------------------------------------------------------------------- /resources/gearpressed.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/gearpressed.tiff -------------------------------------------------------------------------------- /resources/gitlogo.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/gitlogo.tiff -------------------------------------------------------------------------------- /resources/icons/Bookmark/Added.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Bookmark/Added.png -------------------------------------------------------------------------------- /resources/icons/Bookmark/Conflicted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Bookmark/Conflicted.png -------------------------------------------------------------------------------- /resources/icons/Bookmark/Deleted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Bookmark/Deleted.png -------------------------------------------------------------------------------- /resources/icons/Bookmark/Locked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Bookmark/Locked.png -------------------------------------------------------------------------------- /resources/icons/Bookmark/Modified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Bookmark/Modified.png -------------------------------------------------------------------------------- /resources/icons/Bookmark/ReadOnly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Bookmark/ReadOnly.png -------------------------------------------------------------------------------- /resources/icons/Bookmark/Versioned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Bookmark/Versioned.png -------------------------------------------------------------------------------- /resources/icons/Bookmark/badges.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 52 | 60 | 61 | 63 | 70 | 75 | 76 | 79 | 83 | 87 | 88 | 95 | 99 | 100 | 107 | 111 | 112 | 121 | 130 | 131 | 133 | 134 | 136 | image/svg+xml 137 | 139 | 140 | 142 | image/svg+xml 143 | 145 | 146 | 147 | 148 | 153 | 162 | 171 | 180 | 189 | 198 | 207 | 215 | 224 | 233 | 242 | 251 | 260 | 269 | 282 | versioned 293 | modified 304 | conflicted 315 | added 326 | locked 337 | read-only 348 | deleted 359 | 364 | 367 | 375 | 384 | 392 | 401 | 402 | 411 | 1. Original shape 424 | 2. Group of clones, with some styles applied andareas of desired color left unset 441 | 3. Clone of 2. Fill color applied to the group onlyapplies to the unset portions 470 | 483 | 492 | 500 | 509 | 517 | 526 | 527 | 546 | 565 | 584 | 603 | 622 | 641 | 660 | 669 | 678 | 687 | 696 | 705 | 714 | 723 | 732 | 741 | 750 | 759 | 768 | 777 | 786 | Export thoseto PNGs 801 | 805 | 806 | 807 | -------------------------------------------------------------------------------- /resources/icons/CVSClassic/Added.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/CVSClassic/Added.ico -------------------------------------------------------------------------------- /resources/icons/CVSClassic/Conflicted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/CVSClassic/Conflicted.ico -------------------------------------------------------------------------------- /resources/icons/CVSClassic/Deleted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/CVSClassic/Deleted.ico -------------------------------------------------------------------------------- /resources/icons/CVSClassic/Locked.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/CVSClassic/Locked.ico -------------------------------------------------------------------------------- /resources/icons/CVSClassic/Modified.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/CVSClassic/Modified.ico -------------------------------------------------------------------------------- /resources/icons/CVSClassic/ReadOnly.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/CVSClassic/ReadOnly.ico -------------------------------------------------------------------------------- /resources/icons/CVSClassic/Versioned.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/CVSClassic/Versioned.ico -------------------------------------------------------------------------------- /resources/icons/Classic/Added.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Classic/Added.ico -------------------------------------------------------------------------------- /resources/icons/Classic/Conflicted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Classic/Conflicted.ico -------------------------------------------------------------------------------- /resources/icons/Classic/Deleted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Classic/Deleted.ico -------------------------------------------------------------------------------- /resources/icons/Classic/Locked.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Classic/Locked.ico -------------------------------------------------------------------------------- /resources/icons/Classic/Modified.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Classic/Modified.ico -------------------------------------------------------------------------------- /resources/icons/Classic/ReadOnly.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Classic/ReadOnly.ico -------------------------------------------------------------------------------- /resources/icons/Classic/Versioned.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Classic/Versioned.ico -------------------------------------------------------------------------------- /resources/icons/Modern/Added.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Modern/Added.ico -------------------------------------------------------------------------------- /resources/icons/Modern/Conflicted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Modern/Conflicted.ico -------------------------------------------------------------------------------- /resources/icons/Modern/Deleted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Modern/Deleted.ico -------------------------------------------------------------------------------- /resources/icons/Modern/Locked.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Modern/Locked.ico -------------------------------------------------------------------------------- /resources/icons/Modern/Modified.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Modern/Modified.ico -------------------------------------------------------------------------------- /resources/icons/Modern/ReadOnly.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Modern/ReadOnly.ico -------------------------------------------------------------------------------- /resources/icons/Modern/Versioned.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Modern/Versioned.ico -------------------------------------------------------------------------------- /resources/icons/Straight/Added.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Straight/Added.ico -------------------------------------------------------------------------------- /resources/icons/Straight/Conflicted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Straight/Conflicted.ico -------------------------------------------------------------------------------- /resources/icons/Straight/Deleted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Straight/Deleted.ico -------------------------------------------------------------------------------- /resources/icons/Straight/Locked.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Straight/Locked.ico -------------------------------------------------------------------------------- /resources/icons/Straight/Modified.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Straight/Modified.ico -------------------------------------------------------------------------------- /resources/icons/Straight/ReadOnly.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Straight/ReadOnly.ico -------------------------------------------------------------------------------- /resources/icons/Straight/Versioned.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Straight/Versioned.ico -------------------------------------------------------------------------------- /resources/icons/Subclipse/Added.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Subclipse/Added.ico -------------------------------------------------------------------------------- /resources/icons/Subclipse/Conflicted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Subclipse/Conflicted.ico -------------------------------------------------------------------------------- /resources/icons/Subclipse/Deleted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Subclipse/Deleted.ico -------------------------------------------------------------------------------- /resources/icons/Subclipse/Locked.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Subclipse/Locked.ico -------------------------------------------------------------------------------- /resources/icons/Subclipse/Modified.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Subclipse/Modified.ico -------------------------------------------------------------------------------- /resources/icons/Subclipse/ReadOnly.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Subclipse/ReadOnly.ico -------------------------------------------------------------------------------- /resources/icons/Subclipse/Versioned.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/Subclipse/Versioned.ico -------------------------------------------------------------------------------- /resources/icons/XPStyle/Added.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/XPStyle/Added.ico -------------------------------------------------------------------------------- /resources/icons/XPStyle/Conflicted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/XPStyle/Conflicted.ico -------------------------------------------------------------------------------- /resources/icons/XPStyle/Deleted.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/XPStyle/Deleted.ico -------------------------------------------------------------------------------- /resources/icons/XPStyle/Locked.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/XPStyle/Locked.ico -------------------------------------------------------------------------------- /resources/icons/XPStyle/Modified.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/XPStyle/Modified.ico -------------------------------------------------------------------------------- /resources/icons/XPStyle/ReadOnly.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/XPStyle/ReadOnly.ico -------------------------------------------------------------------------------- /resources/icons/XPStyle/Versioned.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/icons/XPStyle/Versioned.ico -------------------------------------------------------------------------------- /resources/icons/license.txt: -------------------------------------------------------------------------------- 1 | The CVSClassic, Classic, Modern, Straight, Subclipse and XPStyle icon packs used in this plug-in were taken from the TortoiseSVN Project, under the following license, found at 2 | http://tortoisesvn.tigris.org/svn/tortoisesvn/trunk/src/Resources/license.txt 3 | =================================================================== 4 | 5 | 6 | 7 | The icons in these folders are available either under the terms 8 | of the GNU Public License, or under the terms of the TortoiseSVN 9 | Icon License below, unless the folder contains an alternative 10 | license file. 11 | 12 | ==================================================================== 13 | Copyright (c) 2006 The TortoiseSVN Project. All rights reserved. 14 | 15 | Redistribution and use of these icons, with or without modification, 16 | are permitted provided that the following conditions are met: 17 | 18 | 1. Redistributions of these icons as part of a source or binary 19 | distribution must retain the above copyright notice and this 20 | list of conditions. 21 | 22 | 2. The icons may only be used for a Subversion client application, 23 | or for an application with Subversion support (e.g. an IDE). 24 | You may not use these icons for to represent actions which are 25 | not directly related to Subversion. If the application supports 26 | other version control systems besides Subversion, the icons may 27 | be used for those other version control systems too. 28 | 29 | 3. Software using these icons must provide an acknowledgement 30 | that the icons were derived from the TortoiseSVN project, 31 | with a link to the project website (tortoisesvn.tigris.org) 32 | in one or more of the following places: 33 | a) an "about" box. 34 | b) the product user manual. 35 | c) a textfile in the installation folder of the application. 36 | d) a contributors page on the product web page. 37 | 38 | 4. The name "TortoiseSVN" must not be used to endorse or 39 | promote products using these icons without prior written 40 | permission. For written permission, please contact 41 | dev@tortoisesvn.tigris.org. 42 | 43 | 5. If you provide additional icon sets for Subversion actions or 44 | status besides those you use from us, you must give us 45 | permission to use your icon sets too. -------------------------------------------------------------------------------- /resources/info.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/info.tiff -------------------------------------------------------------------------------- /resources/infodisabled.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/infodisabled.tiff -------------------------------------------------------------------------------- /resources/infopressed.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/infopressed.tiff -------------------------------------------------------------------------------- /resources/plus.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/plus.tiff -------------------------------------------------------------------------------- /resources/plusdir.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/plusdir.tiff -------------------------------------------------------------------------------- /resources/plusdirpressed.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/plusdirpressed.tiff -------------------------------------------------------------------------------- /resources/pluspressed.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/pluspressed.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/Executable.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/Executable.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/Folder.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/Folder.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/FolderRef.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/FolderRef.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/Oak-Generic.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/Oak-Generic.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/Oak-Text.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/Oak-Text.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-actionscript-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-actionscript-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-applescript-Iconf.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-applescript-Iconf.tif -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-bundle-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-bundle-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-c++-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-c++-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-c-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-c-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-h-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-h-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-html-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-html-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-java-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-java-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-lib-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-lib-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-m-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-m-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-php-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-php-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-plist-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-plist-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-r-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-r-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-rsrc-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-rsrc-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/PBX-ruby-Icon.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/PBX-ruby-Icon.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/SmartFolder.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/SmartFolder.tiff -------------------------------------------------------------------------------- /resources/replacement-icons/SymLink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/SymLink.png -------------------------------------------------------------------------------- /resources/replacement-icons/txtIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsence/projectplus/2de7c506115a3ed62fec76f356d3f1c612e3a944/resources/replacement-icons/txtIcon.png -------------------------------------------------------------------------------- /src/OpenWith.mm: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #import "TextMate.h" 4 | 5 | @interface OpenWith : NSObject 6 | + (BOOL)useOpenWith; 7 | + (NSArray*)applicationsForURL:(NSURL*)URL; 8 | @end 9 | 10 | @implementation NSButton (OakMenuButton_OpenWith) 11 | - (void)OpenWith_awakeFromNib 12 | { 13 | [self OpenWith_awakeFromNib]; 14 | 15 | if(not [OpenWith useOpenWith]) 16 | return; 17 | 18 | if(not [[self window] isKindOfClass:NSClassFromString(@"NSDrawerWindow")]) 19 | return; 20 | 21 | NSMenu *menu = [self valueForKey:@"actionMenu"]; 22 | 23 | NSArray* items = [menu itemArray]; 24 | for(int index = 0; index < [items count]; index++) 25 | { 26 | if([[items objectAtIndex:index] action] == @selector(openFileWithFinder:)) 27 | { 28 | NSMenuItem *openWithMenuItem = [[NSMenuItem alloc] initWithTitle:@"Open With…" action:nil keyEquivalent:@""]; 29 | { 30 | NSMenu *openWithSubMenu = [[NSMenu alloc] init]; 31 | [openWithSubMenu setDelegate:self]; 32 | 33 | [openWithMenuItem setSubmenu:openWithSubMenu]; 34 | [openWithSubMenu release]; 35 | } 36 | [menu insertItem:openWithMenuItem atIndex:index+1]; 37 | [openWithMenuItem release]; 38 | break; 39 | } 40 | } 41 | 42 | } 43 | 44 | - (NSURL*)URLForOpeningApp 45 | { 46 | NSOutlineView* outlineView = [self valueForKey:@"outlineView"]; 47 | NSDictionary* item = [outlineView itemAtRow:[outlineView selectedRow]]; 48 | NSString* path = nil; 49 | if([item objectForKey:@"filename"]) 50 | path = [item objectForKey:@"filename"]; 51 | else if([item objectForKey:@"sourceDirectory"]) 52 | path = [item objectForKey:@"sourceDirectory"]; 53 | return [NSURL fileURLWithPath:path]; 54 | } 55 | 56 | - (int)numberOfItemsInMenu:(NSMenu*)menu 57 | { 58 | return [[OpenWith applicationsForURL:[self URLForOpeningApp]] count]; 59 | } 60 | 61 | - (BOOL)menu:(NSMenu*)menu updateItem:(NSMenuItem*)item atIndex:(int)index shouldCancel:(BOOL)flag 62 | { 63 | NSURL* URL = [[OpenWith applicationsForURL:[self URLForOpeningApp]] objectAtIndex:index]; 64 | NSString* title; 65 | LSCopyDisplayNameForURL((CFURLRef)URL, (CFStringRef*)&title); 66 | [item setTitle:title]; 67 | [title release]; 68 | [item setTarget:self]; 69 | [item setTag:index]; 70 | NSImage* icon = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]; 71 | { 72 | [icon lockFocus]; 73 | [[[NSWorkspace sharedWorkspace] iconForFile:[URL path]] drawInRect:NSMakeRect(0, 0, 16, 16) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; 74 | [item setImage:icon]; 75 | [icon unlockFocus]; 76 | } 77 | [icon release]; 78 | [item setAction:@selector(openSelectedItemWith:)]; 79 | 80 | return YES; 81 | } 82 | 83 | - (void)openSelectedItemWith:(id )sender 84 | { 85 | NSString* filePath = [[self URLForOpeningApp] path]; 86 | NSString* appPath = [[[OpenWith applicationsForURL:[self URLForOpeningApp]] objectAtIndex:[sender tag]] path]; 87 | [[NSWorkspace sharedWorkspace] openFile:filePath withApplication:appPath]; 88 | } 89 | 90 | - (BOOL)OpenWith_validateMenuItem:(id )item 91 | { 92 | if([item action] == @selector(openSelectedItemWith:)) 93 | return YES; 94 | 95 | return [self OpenWith_validateMenuItem:item]; 96 | } 97 | @end 98 | 99 | static NSMutableDictionary* applicationBindings = [[NSMutableDictionary alloc] init]; 100 | 101 | @implementation OpenWith 102 | + (void)load 103 | { 104 | [OakMenuButton jr_swizzleMethod:@selector(awakeFromNib) withMethod:@selector(OpenWith_awakeFromNib) error:NULL]; 105 | [OakMenuButton jr_swizzleMethod:@selector(validateMenuItem:) withMethod:@selector(OpenWith_validateMenuItem:) error:NULL]; 106 | } 107 | 108 | + (BOOL)useOpenWith 109 | { 110 | return YES; 111 | } 112 | 113 | + (NSArray*)applicationsForURL:(NSURL*)URL 114 | { 115 | if(! [applicationBindings objectForKey:URL]) 116 | { 117 | NSArray* apps = (NSArray*)LSCopyApplicationURLsForURL((CFURLRef)URL, kLSRolesAll); 118 | [applicationBindings setObject:(NSArray*)apps forKey:URL]; 119 | [apps release]; 120 | } 121 | return [applicationBindings objectForKey:URL]; 122 | } 123 | @end 124 | -------------------------------------------------------------------------------- /src/ProjectPlus.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | extern NSString* ProjectPlus_redrawRequired; 4 | 5 | @protocol TMPlugInController 6 | - (float)version; 7 | @end 8 | 9 | @interface ProjectPlus : NSObject 10 | { 11 | NSImage* icon; 12 | BOOL quickLookAvailable; 13 | IBOutlet NSView *preferencesView; 14 | IBOutlet NSTabView* preferencesTabView; 15 | IBOutlet NSWindow* sortingDefaultsSheet; 16 | } 17 | + (ProjectPlus*)sharedInstance; 18 | - (id)initWithPlugInController:(id )aController; 19 | 20 | - (IBAction)showSortingDefaultsSheet:(id)sender; 21 | - (IBAction)orderOutShortingDefaultSheet:(id)sender; 22 | 23 | - (IBAction)notifyOutlineViewsAsDirty:(id)sender; 24 | - (void)watchDefaultsKey:(NSString*)keyPath; 25 | 26 | - (NSView*)preferencesView; 27 | - (NSImage*)iconImage; 28 | @end -------------------------------------------------------------------------------- /src/ProjectPlus.mm: -------------------------------------------------------------------------------- 1 | #import "ProjectPlus.h" 2 | #import "TextMate.h" 3 | 4 | NSString* ProjectPlus_redrawRequired = @"ProjectPlus_redrawRequired"; 5 | 6 | float ToolbarHeightForWindow(NSWindow *window) 7 | { 8 | NSToolbar *toolbar; 9 | float toolbarHeight = 0.0; 10 | NSRect windowFrame; 11 | 12 | toolbar = [window toolbar]; 13 | 14 | if(toolbar && [toolbar isVisible]) 15 | { 16 | windowFrame = [NSWindow contentRectForFrameRect:[window frame] styleMask:[window styleMask]]; 17 | toolbarHeight = NSHeight(windowFrame) - NSHeight([[window contentView] frame]); 18 | } 19 | 20 | return toolbarHeight; 21 | } 22 | 23 | static const NSString* PROJECTPLUS_PREFERENCES_LABEL = @"GITMate"; 24 | 25 | @implementation NSWindowController (PreferenceAdditions) 26 | - (NSArray*)ProjectPlus_toolbarAllowedItemIdentifiers:(id)sender 27 | { 28 | return [[self ProjectPlus_toolbarAllowedItemIdentifiers:sender] arrayByAddingObject:PROJECTPLUS_PREFERENCES_LABEL]; 29 | } 30 | - (NSArray*)ProjectPlus_toolbarDefaultItemIdentifiers:(id)sender 31 | { 32 | return [[self ProjectPlus_toolbarDefaultItemIdentifiers:sender] arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:PROJECTPLUS_PREFERENCES_LABEL,nil]]; 33 | } 34 | - (NSArray*)ProjectPlus_toolbarSelectableItemIdentifiers:(id)sender 35 | { 36 | return [[self ProjectPlus_toolbarSelectableItemIdentifiers:sender] arrayByAddingObject:PROJECTPLUS_PREFERENCES_LABEL]; 37 | } 38 | 39 | - (NSToolbarItem*)ProjectPlus_toolbar:(NSToolbar*)toolbar itemForItemIdentifier:(NSString*)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag 40 | { 41 | NSToolbarItem *item = [self ProjectPlus_toolbar:toolbar itemForItemIdentifier:itemIdentifier willBeInsertedIntoToolbar:flag]; 42 | if([itemIdentifier isEqualToString:PROJECTPLUS_PREFERENCES_LABEL]) 43 | [item setImage:[[ProjectPlus sharedInstance] iconImage]]; 44 | return item; 45 | } 46 | 47 | - (void)ProjectPlus_selectToolbarItem:(id)item 48 | { 49 | if ([[item label] isEqualToString:PROJECTPLUS_PREFERENCES_LABEL]) { 50 | if ([[self valueForKey:@"selectedToolbarItem"] isEqualToString:[item label]]) return; 51 | [[self window] setTitle:[item label]]; 52 | [self setValue:[item label] forKey:@"selectedToolbarItem"]; 53 | 54 | NSSize prefsSize = [[[ProjectPlus sharedInstance] preferencesView] frame].size; 55 | NSRect frame = [[self window] frame]; 56 | prefsSize.width = [[self window] contentMinSize].width; 57 | 58 | [[self window] setContentView:[[ProjectPlus sharedInstance] preferencesView]]; 59 | 60 | float newHeight = prefsSize.height + ToolbarHeightForWindow([self window]) + 22; 61 | frame.origin.y += frame.size.height - newHeight; 62 | frame.size.height = newHeight; 63 | frame.size.width = prefsSize.width; 64 | [[self window] setFrame:frame display:YES animate:YES]; 65 | } else { 66 | [self ProjectPlus_selectToolbarItem:item]; 67 | } 68 | } 69 | @end 70 | 71 | @implementation NSWindowController (OakProjectController_Redrawing) 72 | - (id)ProjectPlus_init 73 | { 74 | self = [self ProjectPlus_init]; 75 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ProjectPlus_redrawRequired:) name:ProjectPlus_redrawRequired object:nil]; 76 | return self; 77 | } 78 | 79 | - (void)ProjectPlus_redrawRequired:(NSNotification*)notification 80 | { 81 | [(NSOutlineView*)[self valueForKey:@"outlineView"] setNeedsDisplay:YES]; 82 | } 83 | @end 84 | 85 | static ProjectPlus* SharedInstance = nil; 86 | @implementation ProjectPlus 87 | + (ProjectPlus*)sharedInstance 88 | { 89 | return SharedInstance ?: [[self new] autorelease]; 90 | } 91 | 92 | - (id)init 93 | { 94 | if(SharedInstance) 95 | { 96 | [self release]; 97 | } 98 | else if(self = SharedInstance = [[super init] retain]) 99 | { 100 | quickLookAvailable = [[NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/QuickLookUI.framework"] load]; 101 | 102 | NSApp = [NSApplication sharedApplication]; 103 | 104 | // Preferences 105 | NSString* nibPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"Preferences" ofType:@"nib"]; 106 | NSWindowController *controller = [[NSWindowController alloc] initWithWindowNibPath:nibPath owner:self]; 107 | [controller showWindow:self]; 108 | 109 | 110 | [OakPreferencesManager jr_swizzleMethod:@selector(toolbarAllowedItemIdentifiers:) withMethod:@selector(ProjectPlus_toolbarAllowedItemIdentifiers:) error:NULL]; 111 | [OakPreferencesManager jr_swizzleMethod:@selector(toolbarDefaultItemIdentifiers:) withMethod:@selector(ProjectPlus_toolbarDefaultItemIdentifiers:) error:NULL]; 112 | [OakPreferencesManager jr_swizzleMethod:@selector(toolbarSelectableItemIdentifiers:) withMethod:@selector(ProjectPlus_toolbarSelectableItemIdentifiers:) error:NULL]; 113 | [OakPreferencesManager jr_swizzleMethod:@selector(toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:) withMethod:@selector(ProjectPlus_toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:) error:NULL]; 114 | [OakPreferencesManager jr_swizzleMethod:@selector(selectToolbarItem:) withMethod:@selector(ProjectPlus_selectToolbarItem:) error:NULL]; 115 | 116 | [OakProjectController jr_swizzleMethod:@selector(init) withMethod:@selector(ProjectPlus_init) error:NULL]; 117 | } 118 | 119 | return SharedInstance; 120 | } 121 | 122 | - (id)initWithPlugInController:(id )aController 123 | { 124 | if(self = [self init]) 125 | { 126 | NSString* iconPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"projectplus" ofType:@"tiff"]; 127 | icon = [[NSImage alloc] initByReferencingFile:iconPath]; 128 | } 129 | return self; 130 | } 131 | 132 | - (void)dealloc 133 | { 134 | [icon release]; 135 | [super dealloc]; 136 | } 137 | 138 | - (void)awakeFromNib 139 | { 140 | if([[NSUserDefaults standardUserDefaults] stringForKey:@"ProjectPlus Selected Tab Identifier"]) 141 | [preferencesTabView selectTabViewItemWithIdentifier:[[NSUserDefaults standardUserDefaults] stringForKey:@"ProjectPlus Selected Tab Identifier"]]; 142 | } 143 | 144 | - (IBAction)showSortingDefaultsSheet:(id)sender 145 | { 146 | [NSApp beginSheet:sortingDefaultsSheet modalForWindow:[preferencesTabView window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; 147 | } 148 | 149 | - (IBAction)orderOutShortingDefaultSheet:(id)sender 150 | { 151 | [sortingDefaultsSheet orderOut:nil]; 152 | [NSApp endSheet:sortingDefaultsSheet]; 153 | } 154 | 155 | - (void)tabView:(NSTabView*)tabView didSelectTabViewItem:(NSTabViewItem*)tabViewItem 156 | { 157 | [[NSUserDefaults standardUserDefaults] setObject:[tabViewItem identifier] forKey:@"ProjectPlus Selected Tab Identifier"]; 158 | } 159 | 160 | - (IBAction)notifyOutlineViewsAsDirty:(id)sender; 161 | { 162 | [[NSNotificationCenter defaultCenter] postNotificationName:ProjectPlus_redrawRequired object:nil]; 163 | } 164 | 165 | - (void)watchDefaultsKey:(NSString*)keyPath 166 | { 167 | [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:keyPath options:NULL context:NULL]; 168 | } 169 | 170 | - (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)changes context:(void*)context 171 | { 172 | [self notifyOutlineViewsAsDirty:self]; 173 | } 174 | 175 | - (NSView*)preferencesView 176 | { 177 | return preferencesView; 178 | } 179 | 180 | - (NSImage*)iconImage; 181 | { 182 | return icon; 183 | } 184 | 185 | - (BOOL)quickLookAvailable 186 | { 187 | return quickLookAvailable; 188 | } 189 | @end 190 | -------------------------------------------------------------------------------- /src/ProjectTree.mm: -------------------------------------------------------------------------------- 1 | #import "JRSwizzle.h" 2 | #import "ProjectPlus.h" 3 | 4 | @interface ProjectTree : NSObject 5 | + (BOOL)preserveTreeState; 6 | @end 7 | 8 | @implementation NSWindowController (OakProjectController) 9 | - (void)expandItems:(NSArray*)items inOutlineView:(NSOutlineView*)outlineView toState:(NSDictionary*)treeState 10 | { 11 | unsigned int itemCount = [items count]; 12 | 13 | for(unsigned int index = 0; index < itemCount; index += 1) 14 | { 15 | NSDictionary *item = [items objectAtIndex:index]; 16 | 17 | if(not [item objectForKey:@"children"]) 18 | continue; // We are only interested in directories 19 | 20 | if(NSDictionary *treeItem = [treeState objectForKey:[item objectForKey:@"displayName"]]) 21 | { 22 | if([[treeItem objectForKey:@"isExpanded"] boolValue]) 23 | [outlineView expandItem:item]; 24 | 25 | if([[treeItem objectForKey:@"subItems"] count]) 26 | [self expandItems:[item objectForKey:@"children"] inOutlineView:outlineView toState:[treeItem objectForKey:@"subItems"]]; 27 | } 28 | } 29 | } 30 | 31 | - (void)ProjectTree_windowDidLoad 32 | { 33 | [self ProjectTree_windowDidLoad]; 34 | 35 | if(not [ProjectTree preserveTreeState]) 36 | return; 37 | 38 | [[self valueForKey:@"outlineView"] reloadData]; 39 | 40 | NSOutlineView *outlineView = [self valueForKey:@"outlineView"]; 41 | 42 | NSDictionary *treeState = [[NSDictionary dictionaryWithContentsOfFile:[self valueForKey:@"filename"]] objectForKey:@"treeState"]; 43 | if(treeState) 44 | { 45 | NSArray *rootItems = [self valueForKey:@"rootItems"]; 46 | [self expandItems:rootItems inOutlineView:outlineView toState:treeState]; 47 | } 48 | 49 | 50 | 51 | 52 | 53 | 54 | /***** MARK HUOT ******/ 55 | 56 | // Get the NSScrollView and remove the border 57 | NSScrollView *scrollView = [[outlineView superview] superview]; 58 | // [scrollView setBorderType: NSNoBorder]; 59 | 60 | // Switch the NSOutlineView to use `source` styling 61 | // [outlineView setSelectionHighlightStyle: NSTableViewSelectionHighlightStyleSourceList]; 62 | // [outlineView setRowHeight: 14]; 63 | // [outlineView setIntercellSpacing: (NSSize){3, 6}]; 64 | 65 | // Set the background of the entire window to debug 66 | //[[self window] setBackgroundColor:[NSColor blueColor]]; 67 | 68 | // Grab the width of the "drawer" (or project frame) 69 | // not sure why we add 24 here, maybe the width of the scroll bar? 70 | CGFloat frameWidth = [scrollView frame].size.width; 71 | 72 | // Create the background image for the icons and make sure it's sized correctly 73 | NSString *bkgPath = [[NSBundle bundleForClass:[ProjectPlus class]] pathForResource:@"bkg" ofType:@"tiff"]; 74 | NSImage *image = [[NSImage alloc] initByReferencingFile:bkgPath]; 75 | [image setSize: (NSSize){3000,23}]; 76 | NSImageView *imageView = [[NSImageView alloc] initWithFrame: [[self window] frame]]; 77 | [imageView setImage:image]; 78 | [imageView setFrame:NSMakeRect(0,0,frameWidth+24,23)]; 79 | [imageView setFrameOrigin:(NSPoint){0,0}]; 80 | [imageView setAutoresizingMask:NSViewWidthSizable]; 81 | [imageView setImageScaling:NSScaleNone]; 82 | 83 | // Finally add the background image as the bottom layer 84 | NSArray *siblings = [[scrollView superview] subviews]; 85 | [[scrollView superview] addSubview:imageView positioned:NSWindowBelow relativeTo:[siblings objectAtIndex:0]]; 86 | 87 | // Update the ADD image 88 | NSString *plusImagePath = [[NSBundle bundleForClass:[ProjectPlus class]] pathForResource:@"plus" ofType:@"tiff"]; 89 | NSImage *plusImage = [[NSImage alloc] initByReferencingFile:plusImagePath]; 90 | [[siblings objectAtIndex:4] setImage:plusImage]; 91 | 92 | NSString *plusPressedImagePath = [[NSBundle bundleForClass:[ProjectPlus class]] pathForResource:@"pluspressed" ofType:@"tiff"]; 93 | NSImage *plusPressedImage = [[NSImage alloc] initByReferencingFile:plusPressedImagePath]; 94 | [[siblings objectAtIndex:4] setAlternateImage:plusPressedImage]; 95 | 96 | [[siblings objectAtIndex:4] setFrame:(NSRect){0,0,31,23}]; 97 | 98 | // Update the ADD DIR image 99 | NSString *plusDirImagePath = [[NSBundle bundleForClass:[ProjectPlus class]] pathForResource:@"plusdir" ofType:@"tiff"]; 100 | NSImage *plusDirImage = [[NSImage alloc] initByReferencingFile:plusDirImagePath]; 101 | [[siblings objectAtIndex:2] setImage:plusDirImage]; 102 | 103 | NSString *plusDirPressedImagePath = [[NSBundle bundleForClass:[ProjectPlus class]] pathForResource:@"plusdirpressed" ofType:@"tiff"]; 104 | NSImage *plusDirPressedImage = [[NSImage alloc] initByReferencingFile:plusDirPressedImagePath]; 105 | [[siblings objectAtIndex:2] setAlternateImage:plusDirPressedImage]; 106 | 107 | [[siblings objectAtIndex:2] setFrame:(NSRect){31,0,31,23}]; 108 | 109 | // Update the GEAR image 110 | NSString *gearImagePath = [[NSBundle bundleForClass:[ProjectPlus class]] pathForResource:@"gear" ofType:@"tiff"]; 111 | NSImage *gearImage = [[NSImage alloc] initByReferencingFile:gearImagePath]; 112 | [[siblings objectAtIndex:3] setImage:gearImage]; 113 | 114 | NSString *gearPressedImagePath = [[NSBundle bundleForClass:[ProjectPlus class]] pathForResource:@"gearpressed" ofType:@"tiff"]; 115 | NSImage *gearPressedImage = [[NSImage alloc] initByReferencingFile:gearPressedImagePath]; 116 | [[siblings objectAtIndex:3] setAlternateImage:gearPressedImage]; 117 | 118 | [[siblings objectAtIndex:3] setFrame:(NSRect){62,0,31,23}]; 119 | 120 | // Update the INFO image 121 | NSString *infoImagePath = [[NSBundle bundleForClass:[ProjectPlus class]] pathForResource:@"info" ofType:@"tiff"]; 122 | NSImage *infoImage = [[NSImage alloc] initByReferencingFile:infoImagePath]; 123 | [[siblings objectAtIndex:1] setImage:infoImage]; 124 | 125 | NSString *infoPressedImagePath = [[NSBundle bundleForClass:[ProjectPlus class]] pathForResource:@"infopressed" ofType:@"tiff"]; 126 | NSImage *infoPressedImage = [[NSImage alloc] initByReferencingFile:infoPressedImagePath]; 127 | [[siblings objectAtIndex:1] setAlternateImage:infoPressedImage]; 128 | 129 | [[siblings objectAtIndex:1] setFrame:(NSRect){frameWidth-31,0,31,23}]; 130 | 131 | /***** /MARK HUOT ******/ 132 | } 133 | 134 | - (NSDictionary*)outlineView:(NSOutlineView*)outlineView stateForItems:(NSArray*)items 135 | { 136 | NSMutableDictionary *treeState = [NSMutableDictionary dictionaryWithCapacity:3]; 137 | unsigned int itemCount = [items count]; 138 | 139 | for(unsigned int index = 0; index < itemCount; index += 1) 140 | { 141 | NSDictionary *item = [items objectAtIndex:index]; 142 | if([outlineView isItemExpanded:item]) 143 | { 144 | NSDictionary *subTreeState = [self outlineView:outlineView stateForItems:[item objectForKey:@"children"]]; 145 | [treeState setObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],@"isExpanded", 146 | subTreeState,@"subItems", 147 | nil] forKey:[item objectForKey:@"displayName"]]; 148 | } 149 | } 150 | 151 | return treeState; 152 | } 153 | 154 | - (BOOL)ProjectTree_writeToFile:(NSString*)fileName 155 | { 156 | BOOL result = [self ProjectTree_writeToFile:fileName]; 157 | if(result && [ProjectTree preserveTreeState]) 158 | { 159 | NSMutableDictionary *project = [NSMutableDictionary dictionaryWithContentsOfFile:fileName]; 160 | NSDictionary *treeState = [self outlineView:[self valueForKey:@"outlineView"] stateForItems:[self valueForKey:@"rootItems"]]; 161 | [project setObject:treeState forKey:@"treeState"]; 162 | result = [project writeToFile:fileName atomically:NO]; 163 | } 164 | return result; 165 | } 166 | @end 167 | 168 | @implementation ProjectTree 169 | + (void)load 170 | { 171 | [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: 172 | [NSNumber numberWithBool:YES],@"ProjectPlus Preserve Tree", 173 | nil]]; 174 | 175 | [NSClassFromString(@"OakProjectController") jr_swizzleMethod:@selector(windowDidLoad) withMethod:@selector(ProjectTree_windowDidLoad) error:NULL]; 176 | [NSClassFromString(@"OakProjectController") jr_swizzleMethod:@selector(writeToFile:) withMethod:@selector(ProjectTree_writeToFile:) error:NULL]; 177 | } 178 | 179 | + (BOOL)preserveTreeState; 180 | { 181 | return [[NSUserDefaults standardUserDefaults] boolForKey:@"ProjectPlus Preserve Tree"]; 182 | } 183 | @end 184 | -------------------------------------------------------------------------------- /src/Sorting.mm: -------------------------------------------------------------------------------- 1 | #import "TextMate.h" 2 | 3 | @interface ProjectPlusSorting : NSObject 4 | { 5 | } 6 | + (BOOL)useSorting; 7 | + (BOOL)descending; 8 | + (BOOL)byExtension; 9 | + (BOOL)foldersOnTop; 10 | 11 | + (void)addProjectController:(id)projectController; 12 | + (void)removeProjectController:(id)projectController; 13 | + (NSMutableDictionary*)sortDescriptorForProjectController:(id)projectController; 14 | @end 15 | 16 | struct item_sort_descriptor 17 | { 18 | BOOL ascending; 19 | BOOL by_extension; 20 | BOOL folders_on_top; 21 | }; 22 | 23 | int sort_items(id a, id b, void *context) 24 | { 25 | item_sort_descriptor *sortDescriptor = (item_sort_descriptor*)context; 26 | NSString *aText = [a objectForKey:@"displayName"]; 27 | NSString *bText = [b objectForKey:@"displayName"]; 28 | BOOL ignoreExtensions = NO; 29 | 30 | if(sortDescriptor->folders_on_top) 31 | { 32 | BOOL aIsDir = [a objectForKey:@"children"] != nil; 33 | BOOL bIsDir = [b objectForKey:@"children"] != nil; 34 | 35 | if(aIsDir && bIsDir) 36 | ignoreExtensions = NO; // Fall through to name sorting but ignore extensions 37 | else if(aIsDir) 38 | return NSOrderedAscending; 39 | else if(bIsDir) 40 | return NSOrderedDescending; 41 | } 42 | 43 | if(sortDescriptor->by_extension && ! ignoreExtensions) 44 | { 45 | aText = [aText pathExtension]; 46 | bText = [bText pathExtension]; 47 | } 48 | 49 | int result = [aText caseInsensitiveCompare:bText]; 50 | if (not sortDescriptor->ascending) 51 | result = -result; 52 | return result; 53 | } 54 | 55 | @interface NSMutableArray (RecursiveSort) 56 | - (void)recursiveSortItemsAscending:(BOOL)ascending 57 | byExtension:(BOOL)byExtension 58 | foldersOnTop:(BOOL)foldersOnTop; 59 | @end 60 | 61 | 62 | @implementation NSMutableArray (RecursiveSort) 63 | - (void)recursiveSortItemsAscending:(BOOL)ascending 64 | byExtension:(BOOL)byExtension 65 | foldersOnTop:(BOOL)foldersOnTop 66 | { 67 | struct item_sort_descriptor sortDescriptor; 68 | sortDescriptor.ascending = ascending; 69 | sortDescriptor.by_extension = byExtension; 70 | sortDescriptor.folders_on_top = foldersOnTop; 71 | 72 | unsigned int itemCount = [self count]; 73 | 74 | for(unsigned int index = 0; index < itemCount; index += 1) 75 | { 76 | id item = [self objectAtIndex:index]; 77 | 78 | if([item objectForKey:@"children"]) 79 | [[item objectForKey:@"children"] recursiveSortItemsAscending:ascending byExtension:byExtension foldersOnTop:foldersOnTop]; 80 | } 81 | 82 | [self sortUsingFunction:sort_items context:&sortDescriptor]; 83 | } 84 | @end 85 | 86 | @implementation NSWindowController (OakProjectController_Sorting) 87 | - (NSMutableDictionary*)sortDescriptor 88 | { 89 | return [ProjectPlusSorting sortDescriptorForProjectController:self]; 90 | } 91 | 92 | - (void)resortItems 93 | { 94 | NSMutableArray* rootItems = [self valueForKey:@"rootItems"]; 95 | [rootItems recursiveSortItemsAscending:! [[[self sortDescriptor] objectForKey:@"descending"] boolValue] 96 | byExtension:[[[self sortDescriptor] objectForKey:@"byExtension"] boolValue] 97 | foldersOnTop:[[[self sortDescriptor] objectForKey:@"foldersOnTop"] boolValue]]; 98 | [[self valueForKey:@"outlineView"] reloadData]; 99 | } 100 | 101 | - (void)ProjectPlus_Sorting_windowDidLoad 102 | { 103 | [self ProjectPlus_Sorting_windowDidLoad]; 104 | 105 | if(not [ProjectPlusSorting useSorting]) 106 | return; 107 | 108 | [ProjectPlusSorting addProjectController:self]; 109 | [[self sortDescriptor] setObject:[NSNumber numberWithBool:[ProjectPlusSorting foldersOnTop]] forKey:@"foldersOnTop"]; 110 | [[self sortDescriptor] setObject:[NSNumber numberWithBool:[ProjectPlusSorting byExtension]] forKey:@"byExtension"]; 111 | [[self sortDescriptor] setObject:[NSNumber numberWithBool:[ProjectPlusSorting descending]] forKey:@"descending"]; 112 | [self resortItems]; 113 | } 114 | 115 | - (void)toggleDescending:(id )menuItem 116 | { 117 | [menuItem setState:! [menuItem state]]; 118 | [[self sortDescriptor] setObject:[NSNumber numberWithBool:[menuItem state]] forKey:@"descending"]; 119 | [self resortItems]; 120 | } 121 | 122 | - (void)toggleByExtension:(id )menuItem 123 | { 124 | [menuItem setState:! [menuItem state]]; 125 | [[self sortDescriptor] setObject:[NSNumber numberWithBool:[menuItem state]] forKey:@"byExtension"]; 126 | [self resortItems]; 127 | } 128 | 129 | - (void)toggleFoldersOnTop:(id )menuItem 130 | { 131 | [menuItem setState:! [menuItem state]]; 132 | [[self sortDescriptor] setObject:[NSNumber numberWithBool:[menuItem state]] forKey:@"foldersOnTop"]; 133 | [self resortItems]; 134 | } 135 | @end 136 | 137 | @implementation NSButton (OakMenuButton_ProjectPlus_Sorting) 138 | - (void)ProjectPlus_Sorting_awakeFromNib 139 | { 140 | [self ProjectPlus_Sorting_awakeFromNib]; 141 | 142 | if(not [[self window] isKindOfClass:NSClassFromString(@"NSDrawerWindow")]) 143 | return; 144 | 145 | NSMenu *menu = (NSMenu*)[self valueForKey:@"actionMenu"]; 146 | [menu addItem:[NSMenuItem separatorItem]]; 147 | 148 | NSMenuItem *sortingMenu = [[NSMenuItem alloc] initWithTitle:@"Sort" action:nil keyEquivalent:@""]; 149 | { 150 | NSMenu *sortingSubMenu = [[NSMenu alloc] init]; 151 | NSMenuItem *item; 152 | 153 | item = [[NSMenuItem alloc] initWithTitle:@"Descending" 154 | action:@selector(toggleDescending:) 155 | keyEquivalent:@""]; 156 | [item setTarget:[self valueForKey:@"delegate"]]; 157 | [item setState:[ProjectPlusSorting descending]]; 158 | [sortingSubMenu addItem:item]; 159 | [item release]; 160 | 161 | item = [[NSMenuItem alloc] initWithTitle:@"By Extension" 162 | action:@selector(toggleByExtension:) 163 | keyEquivalent:@""]; 164 | [item setTarget:[self valueForKey:@"delegate"]]; 165 | [item setState:[ProjectPlusSorting byExtension]]; 166 | [sortingSubMenu addItem:item]; 167 | [item release]; 168 | 169 | item = [[NSMenuItem alloc] initWithTitle:@"Folders on Top" 170 | action:@selector(toggleFoldersOnTop:) 171 | keyEquivalent:@""]; 172 | [item setTarget:[self valueForKey:@"delegate"]]; 173 | [item setState:[ProjectPlusSorting foldersOnTop]]; 174 | [sortingSubMenu addItem:item]; 175 | [item release]; 176 | 177 | [sortingMenu setSubmenu:sortingSubMenu]; 178 | [sortingSubMenu release]; 179 | } 180 | [menu addItem:sortingMenu]; 181 | [sortingMenu release]; 182 | } 183 | 184 | - (BOOL)ProjectPlus_Sorting_validateMenuItem:(id )menuItem 185 | { 186 | if([menuItem action] == @selector(toggleDescending:) || [menuItem action] == @selector(toggleByExtension:)) 187 | return YES; 188 | 189 | return [self ProjectPlus_Sorting_validateMenuItem:menuItem]; 190 | } 191 | @end 192 | 193 | static NSMutableArray* sortDescriptors = [[NSMutableArray alloc] initWithCapacity:1]; 194 | 195 | @implementation ProjectPlusSorting 196 | + (void)load 197 | { 198 | [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"ProjectPlus Use Sorting"]]; 199 | 200 | [OakProjectController jr_swizzleMethod:@selector(windowDidLoad) withMethod:@selector(ProjectPlus_Sorting_windowDidLoad) error:NULL]; 201 | [OakMenuButton jr_swizzleMethod:@selector(awakeFromNib) withMethod:@selector(ProjectPlus_Sorting_awakeFromNib) error:NULL]; 202 | [OakMenuButton jr_swizzleMethod:@selector(validateMenuItem:) withMethod:@selector(ProjectPlus_Sorting_validateMenuItem:) error:NULL]; 203 | } 204 | 205 | + (BOOL)foldersOnTop 206 | { 207 | return [[NSUserDefaults standardUserDefaults] boolForKey:@"ProjectPlusSortingFoldersOnTop"]; 208 | } 209 | 210 | + (BOOL)byExtension 211 | { 212 | return [[NSUserDefaults standardUserDefaults] boolForKey:@"ProjectPlusSortingByExtension"]; 213 | } 214 | 215 | + (BOOL)descending 216 | { 217 | return [[NSUserDefaults standardUserDefaults] boolForKey:@"ProjectPlusSortingDescending"]; 218 | } 219 | 220 | + (BOOL)useSorting 221 | { 222 | return [[NSUserDefaults standardUserDefaults] boolForKey:@"ProjectPlus Use Sorting"]; 223 | } 224 | 225 | + (void)addProjectController:(id)projectController; 226 | { 227 | NSMutableDictionary* sortDescriptor = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],@"descending", 228 | [NSNumber numberWithBool:NO],@"byExtension", 229 | [NSNumber numberWithBool:NO],@"foldersOnTop", 230 | nil]; 231 | [sortDescriptors addObject:[NSDictionary dictionaryWithObjectsAndKeys:projectController,@"controller",sortDescriptor,@"sortDescriptor",nil]]; 232 | } 233 | 234 | + (void)removeProjectController:(id)projectController; 235 | { 236 | unsigned int controllerCount = [sortDescriptors count]; 237 | 238 | for(unsigned int index = 0; index < controllerCount; index += 1) 239 | { 240 | NSDictionary* info = [sortDescriptors objectAtIndex:index]; 241 | if([info objectForKey:@"controller"] == projectController) 242 | { 243 | [sortDescriptors removeObject:info]; 244 | return; 245 | } 246 | } 247 | } 248 | 249 | + (NSMutableDictionary*)sortDescriptorForProjectController:(id)projectController; 250 | { 251 | unsigned int controllerCount = [sortDescriptors count]; 252 | 253 | for(unsigned int index = 0; index < controllerCount; index += 1) 254 | { 255 | NSDictionary* info = [sortDescriptors objectAtIndex:index]; 256 | if([info objectForKey:@"controller"] == projectController) 257 | return [info objectForKey:@"sortDescriptor"]; 258 | } 259 | 260 | return nil; 261 | } 262 | @end 263 | -------------------------------------------------------------------------------- /src/TMLabels.mm: -------------------------------------------------------------------------------- 1 | #import 2 | #import "TextMate.h" 3 | #import "ProjectPlus.h" 4 | 5 | @interface TMLabels : NSObject 6 | { 7 | } 8 | + (void)setColour:(int)colourIndex forPath:(NSString*)path; 9 | + (int)colourIndexForPath:(NSString*)path; 10 | + (NSColor*)colourForPath:(NSString*)path; 11 | + (float)labelOpacity; 12 | + (BOOL)useLabels; 13 | + (void)drawLabelIndex:(int)colourIndex inRect:(NSRect)rect; 14 | @end 15 | 16 | void Interpolate (void* info, float const* inData, float *outData) 17 | { 18 | NSColor** cols = (NSColor**)info; 19 | 20 | float from[4], to[4]; 21 | [cols[0] getComponents:&from[0]]; 22 | [cols[1] getComponents:&to[0]]; 23 | 24 | float a = inData[0]; 25 | for(int i = 0; i < 4; i++) 26 | outData[i] = (1.0f-a)*from[i] + a*to[i]; 27 | } 28 | 29 | @interface NSColor (HexColor) 30 | + (NSColor*)colorWithRRGGBB:(uint32_t)value; 31 | @end 32 | 33 | @implementation NSColor (HexColor) 34 | + (NSColor*)colorWithRRGGBB:(uint32_t)value 35 | { 36 | float red = ((value & 0xFF0000) >> 16) / 255.0f; 37 | float green = ((value & 0x00FF00) >> 8) / 255.0f; 38 | float blue = ((value & 0x0000FF) >> 0) / 255.0f; 39 | return [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:1.0f]; 40 | } 41 | @end 42 | 43 | @interface NSBezierPath (RoundedRectangle) 44 | + (NSBezierPath*)bezierPathWithRoundRectInRect:(NSRect)aRect radius:(float)radius; 45 | @end 46 | 47 | @implementation NSBezierPath (RoundedRectangle) 48 | + (NSBezierPath*)bezierPathWithRoundRectInRect:(NSRect)aRect radius:(float)radius 49 | { 50 | NSBezierPath* path = [self bezierPath]; 51 | // radius = MIN(radius, 0.5f * MIN(NSWidth(aRect), NSHeight(aRect))); 52 | NSRect rect = NSInsetRect(aRect, radius, radius); 53 | [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMinY(rect)) radius:radius startAngle:180.0 endAngle:270.0]; 54 | [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMinY(rect)) radius:radius startAngle:270.0 endAngle:360.0]; 55 | [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMaxY(rect)) radius:radius startAngle: 0.0 endAngle: 90.0]; 56 | [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMaxY(rect)) radius:radius startAngle: 90.0 endAngle:180.0]; 57 | [path closePath]; 58 | return path; 59 | } 60 | 61 | - (void)fillWithGradientFrom:(NSColor*)startCol to:(NSColor*)endCol 62 | { 63 | struct CGFunctionCallbacks callbacks = { 0, Interpolate, NULL }; 64 | 65 | NSColor* cols[2] = { startCol, endCol }; 66 | CGFunctionRef function = CGFunctionCreate( 67 | &cols[0], // void *info, 68 | 1, // size_t domainDimension, 69 | NULL, // float const* domain, 70 | 4, // size_t rangeDimension, 71 | NULL, // float const* range, 72 | &callbacks // CGFunctionCallbacks const* callbacks 73 | ); 74 | 75 | CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); 76 | 77 | NSRect bounds = [self bounds]; 78 | float srcX = NSMinX(bounds), srcY = NSMinY(bounds); 79 | float dstX = NSMinX(bounds), dstY = NSMaxY(bounds); 80 | CGShadingRef shading = CGShadingCreateAxial( 81 | cspace, // CGColorSpaceRef colorspace, 82 | CGPointMake(srcX, srcY), // CGPoint start, 83 | CGPointMake(dstX, dstY), // CGPoint end, 84 | function, // CGFunctionRef function, 85 | false, // bool extendStart, 86 | false // bool extendEnd 87 | ); 88 | 89 | [NSGraphicsContext saveGraphicsState]; 90 | [self addClip]; 91 | CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; 92 | CGContextDrawShading(context, shading); 93 | [NSGraphicsContext restoreGraphicsState]; 94 | 95 | CGShadingRelease(shading); 96 | CGColorSpaceRelease(cspace); 97 | CGFunctionRelease(function); 98 | } 99 | @end 100 | 101 | 102 | #define LIST_OFFSET 5 103 | #define ICON_SIZE 15 104 | 105 | #define DEFAULT_COLOUR_OPACITY 0.5 106 | 107 | struct Colour { 108 | NSString *name; 109 | NSColor *colour; 110 | } colours[] = { 111 | {@"None", nil}, 112 | {@"Gray", [NSColor grayColor]}, 113 | {@"Green", [NSColor greenColor]}, 114 | {@"Purple", [NSColor purpleColor]}, 115 | {@"Blue", [NSColor blueColor]}, 116 | {@"Yellow", [NSColor yellowColor]}, 117 | {@"Red", [NSColor redColor]}, 118 | {@"Orange", [NSColor orangeColor]}, 119 | }; 120 | 121 | @implementation NSOutlineView (LabeledOutlineView) 122 | - (void)drawLabelForRow:(int)rowNumber 123 | { 124 | NSDictionary *item = [self itemAtRow:rowNumber]; 125 | if (item) { 126 | NSString *path = [item objectForKey:@"filename"]; 127 | if (!path) path = [item objectForKey:@"sourceDirectory"]; 128 | 129 | int labelColorIndex = [TMLabels colourIndexForPath:path]; 130 | if (labelColorIndex > 0) { 131 | NSRect r = NSIntegralRect(NSInsetRect([self rectOfRow:rowNumber], 2.0f, 0.0f)); 132 | r.origin.y += 0.0f; 133 | r.size.height -= 1.0f; 134 | 135 | if([self isRowSelected:rowNumber]) 136 | r.size.width = 15.0f; 137 | 138 | [TMLabels drawLabelIndex:labelColorIndex inRect:r]; 139 | } 140 | } 141 | } 142 | 143 | - (void)drawSelectedLabelForRow:(int)rowNumber 144 | { 145 | NSDictionary *item = [self itemAtRow:rowNumber]; 146 | if (item) { 147 | NSString *path = [item objectForKey:@"filename"]; 148 | if (!path) path = [item objectForKey:@"sourceDirectory"]; 149 | 150 | int labelColorIndex = [TMLabels colourIndexForPath:path]; 151 | if (labelColorIndex > 0) { 152 | NSRect r = NSIntegralRect(NSInsetRect([self rectOfRow:rowNumber], 2.0f, 0.0f)); 153 | r.origin.y += 1.0f; 154 | r.size.height = 12; 155 | 156 | r.size.width = 15.0f; 157 | 158 | NSRect rect = [self rectOfRow:rowNumber]; 159 | rect.origin.x = LIST_OFFSET + [self levelForRow:rowNumber] * ICON_SIZE + 1; 160 | rect.origin.y += 2; 161 | rect.size.width = 12; 162 | rect.size.height = 12; 163 | 164 | [TMLabels drawLabelIndex:labelColorIndex inRect:rect]; 165 | } 166 | } 167 | } 168 | 169 | - (void)labeledHighlightSelectionInClipRect:(NSRect)clipRect 170 | { 171 | if([TMLabels useLabels]) 172 | { 173 | NSRange rows = [self rowsInRect:clipRect]; 174 | 175 | int rowNumber = rows.location; 176 | while (rowNumber <= rows.location + rows.length) 177 | [self drawLabelForRow:rowNumber++]; 178 | } 179 | 180 | [self labeledHighlightSelectionInClipRect:clipRect]; 181 | 182 | if([TMLabels useLabels]) 183 | { 184 | NSRange visibleRowIndexes = [self rowsInRect:clipRect]; 185 | NSIndexSet *selectedRowIndexes = [self selectedRowIndexes]; 186 | 187 | int row; 188 | int endRow; 189 | 190 | for (row = visibleRowIndexes.location, endRow = row + visibleRowIndexes.length; row < endRow ; row++) { 191 | if ([selectedRowIndexes containsIndex: row]) 192 | [self drawSelectedLabelForRow:row]; 193 | } 194 | } 195 | } 196 | @end 197 | 198 | @implementation NSButton (ProjectContextMenu) 199 | - (NSArray*)selectedItems 200 | { 201 | NSOutlineView *outlineView = [self valueForKey:@"outlineView"]; 202 | NSIndexSet *selectedRows = [outlineView selectedRowIndexes]; 203 | NSMutableArray *items = [NSMutableArray arrayWithCapacity:[selectedRows count]]; 204 | 205 | int rowIndex = [selectedRows firstIndex]; 206 | do { 207 | [items addObject:[outlineView itemAtRow:rowIndex]]; 208 | } while ((rowIndex = [selectedRows indexGreaterThanIndex:rowIndex]) != NSNotFound); 209 | 210 | return items; 211 | } 212 | 213 | - (BOOL)myValidateMenuItem:(id )menuItem 214 | { 215 | if ([menuItem action] == @selector(setColourLabel:)) { 216 | NSArray *items = [self selectedItems]; 217 | unsigned int itemCount = [items count]; 218 | 219 | [menuItem setState:NSOffState]; 220 | 221 | for (unsigned int index = 0; index < itemCount; index += 1) { 222 | NSDictionary *item = [items objectAtIndex:index]; 223 | NSString *path = [item objectForKey:@"filename"]; 224 | if (!path) path = [item objectForKey:@"sourceDirectory"]; 225 | 226 | if ([TMLabels colourIndexForPath:path] == [menuItem tag]) { 227 | [menuItem setState:NSOnState]; 228 | break; 229 | } 230 | } 231 | return YES; 232 | } 233 | 234 | return [self myValidateMenuItem:menuItem]; 235 | } 236 | 237 | - (void)setColourLabel:(id)sender 238 | { 239 | NSArray *items = [self selectedItems]; 240 | unsigned int itemCount = [items count]; 241 | 242 | for (unsigned int index = 0; index < itemCount; index += 1) { 243 | NSDictionary *item = [items objectAtIndex:index]; 244 | NSString *path = [item objectForKey:@"filename"]; 245 | if (!path) path = [item objectForKey:@"sourceDirectory"]; 246 | 247 | if (path) 248 | [TMLabels setColour:[sender tag] forPath:path]; 249 | } 250 | 251 | [[self valueForKey:@"outlineView"] display]; 252 | } 253 | 254 | - (void)labeledAwakeFromNib 255 | { 256 | [self labeledAwakeFromNib]; 257 | 258 | if(not [TMLabels useLabels]) 259 | return; 260 | 261 | if(not [[self window] isKindOfClass:NSClassFromString(@"NSDrawerWindow")]) 262 | return; 263 | 264 | NSMenu *menu = (NSMenu*)[self valueForKey:@"actionMenu"]; 265 | [menu addItem:[NSMenuItem separatorItem]]; 266 | 267 | NSMenuItem *colourMenu = [[NSMenuItem alloc] initWithTitle:@"Colour Label" action:nil keyEquivalent:@""]; 268 | { 269 | NSMenu *colourSubMenu = [[NSMenu alloc] init]; 270 | 271 | int colourCount = sizeof(colours) / sizeof(Colour); 272 | for (int index = 0; index < colourCount; index++) { 273 | Str255 str = { }; 274 | NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:(noErr == GetLabel(index, NULL, str) ? [NSString stringWithCString:(char*)str] : colours[index].name) 275 | action:@selector(setColourLabel:) 276 | keyEquivalent:@""]; 277 | [item setTarget:self]; 278 | [item setTag:index]; 279 | if (colours[index].colour) { 280 | NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(32, 16)]; 281 | { 282 | [image lockFocus]; 283 | [TMLabels drawLabelIndex:index inRect:NSMakeRect(0, 0, 32, 16)]; 284 | [image unlockFocus]; 285 | [item setImage:image]; 286 | } 287 | [image release]; 288 | } 289 | [colourSubMenu addItem:item]; 290 | [item release]; 291 | } 292 | 293 | [colourMenu setSubmenu:colourSubMenu]; 294 | [colourSubMenu release]; 295 | } 296 | [menu addItem:colourMenu]; 297 | [colourMenu release]; 298 | } 299 | @end 300 | 301 | 302 | 303 | 304 | @implementation TMLabels 305 | + (void)load 306 | { 307 | [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: 308 | [NSNumber numberWithFloat:DEFAULT_COLOUR_OPACITY],@"TMLabels Opacity", 309 | [NSNumber numberWithBool:YES],@"ProjectPlus Labels Enabled", 310 | nil]]; 311 | 312 | [OakOutlineView jr_swizzleMethod:@selector(highlightSelectionInClipRect:) withMethod:@selector(labeledHighlightSelectionInClipRect:) error:NULL]; 313 | 314 | [OakMenuButton jr_swizzleMethod:@selector(awakeFromNib) withMethod:@selector(labeledAwakeFromNib) error:NULL]; 315 | [OakMenuButton jr_swizzleMethod:@selector(validateMenuItem:) withMethod:@selector(myValidateMenuItem:) error:NULL]; 316 | 317 | [[ProjectPlus sharedInstance] watchDefaultsKey:@"ProjectPlus Labels Enabled"]; 318 | } 319 | 320 | + (int)colourIndexForPath:(NSString*)path 321 | { 322 | OSStatus ret; 323 | OSErr err; 324 | FSRef ref; 325 | FSCatalogInfo info; 326 | UInt16 flags; 327 | int colour; 328 | 329 | ret = FSPathMakeRef ((UInt8*)[path UTF8String], &ref, NULL); 330 | 331 | if (ret != noErr) 332 | return nil; 333 | 334 | err = FSGetCatalogInfo (&ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &info, NULL, NULL, NULL); 335 | 336 | if (err != noErr) 337 | return nil; 338 | 339 | if (info.nodeFlags & kFSNodeIsDirectoryMask) { 340 | FolderInfo *pinfo = (FolderInfo*)&info.finderInfo; 341 | 342 | flags = pinfo->finderFlags; 343 | } else { 344 | FileInfo *pinfo = (FileInfo*)&info.finderInfo; 345 | 346 | flags = pinfo->finderFlags; 347 | } 348 | 349 | colour = (flags & kColor) >> 1; 350 | 351 | return colour; 352 | } 353 | 354 | + (NSColor*)colourForPath:(NSString*)path 355 | { 356 | return colours[[self colourIndexForPath:path]].colour; 357 | } 358 | 359 | + (void)setColour:(int)colourIndex forPath:(NSString*)path 360 | { 361 | OSStatus ret; 362 | OSErr err; 363 | FSRef ref; 364 | FSCatalogInfo info; 365 | 366 | ret = FSPathMakeRef ((UInt8*)[path UTF8String], &ref, NULL); 367 | 368 | if (ret != noErr) 369 | return; 370 | 371 | err = FSGetCatalogInfo (&ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &info, NULL, NULL, NULL); 372 | 373 | if (err != noErr) 374 | return; 375 | 376 | if (info.nodeFlags & kFSNodeIsDirectoryMask) { 377 | FolderInfo *pinfo = (FolderInfo*)&info.finderInfo; 378 | 379 | pinfo->finderFlags = (pinfo->finderFlags & ~kColor) | (colourIndex << 1); 380 | } else { 381 | FileInfo *pinfo = (FileInfo*)&info.finderInfo; 382 | 383 | pinfo->finderFlags = (pinfo->finderFlags & ~kColor) | (colourIndex << 1); 384 | } 385 | 386 | FSSetCatalogInfo (&ref, kFSCatInfoFinderInfo, &info); 387 | } 388 | 389 | + (float)labelOpacity 390 | { 391 | float opacity = [[NSUserDefaults standardUserDefaults] floatForKey:@"TMLabels Opacity"]; 392 | 393 | if (opacity == 0) 394 | opacity = DEFAULT_COLOUR_OPACITY; 395 | 396 | return opacity; 397 | } 398 | 399 | + (BOOL)useLabels 400 | { 401 | return [[NSUserDefaults standardUserDefaults] boolForKey:@"ProjectPlus Labels Enabled"]; 402 | } 403 | 404 | + (void)drawLabelIndex:(int)colourIndex inRect:(NSRect)rect 405 | { 406 | // color names: Gray, Green, Purple, Blue, Yellow, Red, Orange 407 | // static uint32_t const startCol[] = { 0xCFCFCF, 0xD4EE9C, 0xDDBDEA, 0xACD0FE, 0xF8F79C, 0xB2B2B2, 0xF9D194 }; 408 | static uint32_t const startCol[] = { 0xCFCFCF, 0xD4EE9C, 0xDDBDEA, 0xACD0FE, 0xF8F79C, 0xFFA09B, 0xF9D194 }; 409 | static uint32_t const stopCol[] = { 0xA8A8A8, 0xAFDC49, 0xC186D7, 0x5B9CFE, 0xECDF4A, 0xFC605C, 0xF6AC46 }; 410 | 411 | NSBezierPath* path = [NSBezierPath bezierPathWithRoundRectInRect:rect radius:8.0f]; 412 | NSColor *from = [[NSColor colorWithRRGGBB:startCol[colourIndex-1]] colorWithAlphaComponent:[TMLabels labelOpacity]]; 413 | NSColor *to = [[NSColor colorWithRRGGBB:stopCol[colourIndex-1]] colorWithAlphaComponent:[TMLabels labelOpacity]]; 414 | [path fillWithGradientFrom:from to:to]; 415 | } 416 | @end 417 | -------------------------------------------------------------------------------- /src/etc/JRSwizzle.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | JRSwizzle.h 3 | Copyright (c) 2007 Jonathan 'Wolf' Rentzsch: 4 | Some rights reserved: 5 | 6 | ***************************************************************************/ 7 | 8 | #import 9 | 10 | @interface NSObject (JRSwizzle) 11 | + (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_; 12 | + (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_; 13 | @end 14 | -------------------------------------------------------------------------------- /src/etc/JRSwizzle.m: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | JRSwizzle.m 3 | Copyright (c) 2007 Jonathan 'Wolf' Rentzsch: 4 | Some rights reserved: 5 | 6 | ***************************************************************************/ 7 | 8 | #import "JRSwizzle.h" 9 | #import 10 | 11 | #define SetNSError(ERROR_VAR, FORMAT,...) \ 12 | if (ERROR_VAR) { \ 13 | NSString *errStr = [@"+[NSObject(JRSwizzle) jr_swizzleMethod:withMethod:error:]: " stringByAppendingFormat:FORMAT,##__VA_ARGS__]; \ 14 | *ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \ 15 | code:-1 \ 16 | userInfo:[NSDictionary dictionaryWithObject:errStr forKey:NSLocalizedDescriptionKey]]; \ 17 | } 18 | 19 | @implementation NSObject (JRSwizzle) 20 | 21 | + (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ { 22 | #if OBJC_API_VERSION >= 2 23 | Method origMethod = class_getInstanceMethod(self, origSel_); 24 | if (!origMethod) { 25 | SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); 26 | return NO; 27 | } 28 | 29 | Method altMethod = class_getInstanceMethod(self, altSel_); 30 | if (!altMethod) { 31 | SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); 32 | return NO; 33 | } 34 | 35 | class_addMethod(self, 36 | origSel_, 37 | class_getMethodImplementation(self, origSel_), 38 | method_getTypeEncoding(origMethod)); 39 | class_addMethod(self, 40 | altSel_, 41 | class_getMethodImplementation(self, altSel_), 42 | method_getTypeEncoding(altMethod)); 43 | 44 | method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_)); 45 | return YES; 46 | #else 47 | // Scan for non-inherited methods. 48 | Method directOriginalMethod = NULL, directAlternateMethod = NULL; 49 | 50 | void *iterator = NULL; 51 | struct objc_method_list *mlist = class_nextMethodList(self, &iterator); 52 | while (mlist) { 53 | int method_index = 0; 54 | for (; method_index < mlist->method_count; method_index++) { 55 | if (mlist->method_list[method_index].method_name == origSel_) { 56 | assert(!directOriginalMethod); 57 | directOriginalMethod = &mlist->method_list[method_index]; 58 | } 59 | if (mlist->method_list[method_index].method_name == altSel_) { 60 | assert(!directAlternateMethod); 61 | directAlternateMethod = &mlist->method_list[method_index]; 62 | } 63 | } 64 | mlist = class_nextMethodList(self, &iterator); 65 | } 66 | 67 | // If either method is inherited, copy it up to the target class to make it non-inherited. 68 | if (!directOriginalMethod || !directAlternateMethod) { 69 | Method inheritedOriginalMethod = NULL, inheritedAlternateMethod = NULL; 70 | if (!directOriginalMethod) { 71 | inheritedOriginalMethod = class_getInstanceMethod(self, origSel_); 72 | if (!inheritedOriginalMethod) { 73 | SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); 74 | return NO; 75 | } 76 | } 77 | if (!directAlternateMethod) { 78 | inheritedAlternateMethod = class_getInstanceMethod(self, altSel_); 79 | if (!inheritedAlternateMethod) { 80 | SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); 81 | return NO; 82 | } 83 | } 84 | 85 | int hoisted_method_count = !directOriginalMethod && !directAlternateMethod ? 2 : 1; 86 | struct objc_method_list *hoisted_method_list = malloc(sizeof(struct objc_method_list) + (sizeof(struct objc_method)*(hoisted_method_count-1))); 87 | hoisted_method_list->method_count = hoisted_method_count; 88 | Method hoisted_method = hoisted_method_list->method_list; 89 | 90 | if (!directOriginalMethod) { 91 | bcopy(inheritedOriginalMethod, hoisted_method, sizeof(struct objc_method)); 92 | directOriginalMethod = hoisted_method++; 93 | } 94 | if (!directAlternateMethod) { 95 | bcopy(inheritedAlternateMethod, hoisted_method, sizeof(struct objc_method)); 96 | directAlternateMethod = hoisted_method; 97 | } 98 | class_addMethods(self, hoisted_method_list); 99 | } 100 | 101 | // Swizzle. 102 | IMP temp = directOriginalMethod->method_imp; 103 | directOriginalMethod->method_imp = directAlternateMethod->method_imp; 104 | directAlternateMethod->method_imp = temp; 105 | 106 | return YES; 107 | #endif 108 | } 109 | 110 | + (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ { 111 | assert(0); 112 | return NO; 113 | } 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /src/etc/ProjectPlus_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ProjectPlus' target in the 'ProjectPlus' project. 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /src/etc/TextMate.h: -------------------------------------------------------------------------------- 1 | #import "JRSwizzle.h" 2 | 3 | #define OakProjectController NSClassFromString(@"OakProjectController") 4 | #define OakPreferencesManager NSClassFromString(@"OakPreferencesManager") 5 | #define OakOutlineView NSClassFromString(@"OakOutlineView") 6 | #define OakMenuButton NSClassFromString(@"OakMenuButton") 7 | #define OakWindow NSClassFromString(@"OakWindow") 8 | -------------------------------------------------------------------------------- /src/scm/GitIcons.mm: -------------------------------------------------------------------------------- 1 | #import "SCMIcons.h" 2 | 3 | #define USE_THREADING 4 | 5 | @interface GitIcons : NSObject 6 | { 7 | NSMutableDictionary* projectStatuses; 8 | } 9 | + (GitIcons*)sharedInstance; 10 | @end 11 | 12 | static GitIcons *SharedInstance; 13 | 14 | @implementation GitIcons 15 | // ================== 16 | // = Setup/Teardown = 17 | // ================== 18 | + (GitIcons*)sharedInstance 19 | { 20 | return SharedInstance ?: [[self new] autorelease]; 21 | } 22 | 23 | + (void)load 24 | { 25 | [[SCMIcons sharedInstance] registerSCMDelegate:[self sharedInstance]]; 26 | } 27 | 28 | - (NSString*)scmName; 29 | { 30 | return @"Git"; 31 | } 32 | 33 | - (id)init 34 | { 35 | if(SharedInstance) 36 | { 37 | [self release]; 38 | } 39 | else if(self = SharedInstance = [[super init] retain]) 40 | { 41 | projectStatuses = [NSMutableDictionary new]; 42 | } 43 | return SharedInstance; 44 | } 45 | 46 | - (void)dealloc 47 | { 48 | [projectStatuses release]; 49 | [super dealloc]; 50 | } 51 | 52 | - (NSString*)gitPath; 53 | { 54 | return [[SCMIcons sharedInstance] pathForVariable:@"TM_GIT" paths:[NSArray arrayWithObjects:@"/opt/local/bin/git",@"/usr/local/bin/git",@"/usr/bin/git",nil]]; 55 | } 56 | 57 | - (void)executeLsFilesUnderPath:(NSString*)path inProject:(NSString*)projectPath; 58 | { 59 | NSString* exePath = [self gitPath]; 60 | if(!exePath || ![[NSFileManager defaultManager] fileExistsAtPath:exePath]) 61 | return; 62 | 63 | @try 64 | { 65 | NSTask* task = [[NSTask new] autorelease]; 66 | [task setLaunchPath:exePath]; 67 | [task setCurrentDirectoryPath:projectPath]; 68 | if(path) 69 | [task setArguments:[NSArray arrayWithObjects:@"ls-files", @"--exclude-standard", @"-z", @"-t", @"-m", @"-c", @"-d", nil]]; 70 | else 71 | [task setArguments:[NSArray arrayWithObjects:@"ls-files", @"--exclude-standard", @"-z", @"-t", @"-m", @"-c", @"-d", path, nil]]; 72 | 73 | NSPipe *pipe = [NSPipe pipe]; 74 | [task setStandardOutput: pipe]; 75 | [task setStandardError:[NSPipe pipe]]; // Prevent errors from being printed to the Console 76 | 77 | NSFileHandle *file = [pipe fileHandleForReading]; 78 | 79 | [task launch]; 80 | 81 | NSData *data = [file readDataToEndOfFile]; 82 | 83 | [task waitUntilExit]; 84 | 85 | if([task terminationStatus] != 0) 86 | { 87 | // Prevent repeated calling 88 | [projectStatuses setObject:[NSDictionary dictionary] forKey:projectPath]; 89 | return; 90 | } 91 | 92 | NSString *string = [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] autorelease]; 93 | 94 | NSArray* lines = [string componentsSeparatedByString:@"\0"]; 95 | NSMutableDictionary* project = [[NSMutableDictionary alloc] initWithCapacity:([lines count]>0) ? ([lines count]-1) : 0]; 96 | if([lines count] > 1) 97 | { 98 | for(int index = 0; index < [lines count]; index++) 99 | { 100 | NSString* line = [lines objectAtIndex:index]; 101 | if([line length] > 3) 102 | { 103 | const char* statusChar = [[line substringToIndex:1] UTF8String]; 104 | NSString* filename = [projectPath stringByAppendingPathComponent:[line substringFromIndex:2]]; 105 | SCMIconsStatus status = SCMIconsStatusUnknown; 106 | switch(*statusChar) 107 | { 108 | case 'H': status = SCMIconsStatusVersioned; break; 109 | case 'C': status = SCMIconsStatusModified; break; 110 | } 111 | [project setObject:[NSNumber numberWithInt:status] forKey:filename]; 112 | } 113 | } 114 | } 115 | [projectStatuses setObject:project forKey:projectPath]; 116 | [project release]; 117 | } 118 | @catch(NSException* exception) 119 | { 120 | NSLog(@"%s %@: launch path \"%@\"", _cmd, exception, exePath); 121 | [projectStatuses setObject:[NSDictionary dictionary] forKey:projectPath]; 122 | } 123 | } 124 | 125 | - (void)executeLsFilesForProject:(NSString*)projectPath; 126 | { 127 | NSAutoreleasePool* pool = [NSAutoreleasePool new]; 128 | [self executeLsFilesUnderPath:nil inProject:projectPath]; 129 | [self performSelectorOnMainThread:@selector(redisplayStatuses) withObject:nil waitUntilDone:NO]; 130 | [pool release]; 131 | } 132 | 133 | // SCMIconDelegate 134 | - (SCMIconsStatus)statusForPath:(NSString*)path inProject:(NSString*)projectPath reload:(BOOL)reload; 135 | { 136 | if(reload || ![projectStatuses objectForKey:projectPath]) 137 | [self executeLsFilesUnderPath:path inProject:projectPath]; 138 | 139 | NSNumber* status = [[projectStatuses objectForKey:projectPath] objectForKey:path]; 140 | if(status) 141 | return (SCMIconsStatus)[status intValue]; 142 | else 143 | return SCMIconsStatusUnknown; 144 | } 145 | 146 | - (void)redisplayStatuses; 147 | { 148 | [[SCMIcons sharedInstance] redisplayProjectTrees]; 149 | } 150 | 151 | - (void)reloadStatusesForProject:(NSString*)projectPath; 152 | { 153 | #ifdef USE_THREADING 154 | [NSThread detachNewThreadSelector:@selector(executeLsFilesForProject:) toTarget:self withObject:projectPath]; 155 | #else 156 | [projectStatuses removeObjectForKey:projectPath]; 157 | [self executeLsFilesUnderPath:nil inProject:projectPath]; 158 | #endif 159 | } 160 | @end 161 | -------------------------------------------------------------------------------- /src/scm/SCMIcons.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "TextMate.h" 3 | 4 | enum SCMIconsStatus { 5 | SCMIconsStatusVersioned = 1, 6 | SCMIconsStatusModified, 7 | SCMIconsStatusAdded, 8 | SCMIconsStatusDeleted, 9 | SCMIconsStatusConflicted, 10 | SCMIconsStatusUnversioned, 11 | SCMIconsStatusUnknown, 12 | }; 13 | 14 | @protocol SCMIconDelegate 15 | - (SCMIconsStatus)statusForPath:(NSString*)path inProject:(NSString*)projectPath reload:(BOOL)reload; 16 | - (NSString*)scmName; 17 | @end 18 | // Optional methods: 19 | // - (void)reloadStatusesForProject:(NSString*)projectPath; 20 | 21 | @interface SCMIcons : NSWindowController 22 | { 23 | NSMutableArray* delegates; 24 | NSMutableArray* iconPacks; 25 | IBOutlet NSArrayController* iconPacksController; 26 | } 27 | + (SCMIcons*)sharedInstance; 28 | 29 | - (void)redisplayProjectTrees; 30 | 31 | - (void)registerSCMDelegate:(id )delegate; 32 | 33 | - (void)loadIconPacks; 34 | - (NSDictionary*)iconPack; 35 | - (NSImage*)overlayIcon:(NSString*)name; 36 | 37 | - (void)setSelectedIconPackIndex:(int)index; 38 | 39 | - (NSString*)pathForVariable:(NSString*)shellVariableName paths:(NSArray*)paths; 40 | @end 41 | -------------------------------------------------------------------------------- /src/scm/SCMIcons.mm: -------------------------------------------------------------------------------- 1 | #import "SCMIcons.h" 2 | #import "ProjectPlus.h" 3 | #import "TextMate.h" 4 | 5 | #define LIST_OFFSET 4 6 | #define ICON_SIZE 15 7 | #define BADGE_SIZE 10 8 | 9 | const NSString* overlayImageNames[] = {@"Modified", @"Added", @"Deleted", @"Versioned", @"Conflicted", @"Unversioned"}; 10 | 11 | @interface SCMIcons (Private) 12 | - (void)reloadStatusesForAllProjects; 13 | - (void)reloadStatusesForProject:(NSString*)projectPath; 14 | - (NSImage*)imageForStatusCode:(SCMIconsStatus)status; 15 | - (SCMIconsStatus)statusForPath:(NSString*)path inProject:(NSString*)projectPath reload:(BOOL)reload; 16 | @end 17 | 18 | @interface NSWindowController (SCMAppSwitching) 19 | - (void)scmRefreshApplicationDidBecomeActiveNotification:(id)sender; 20 | @end 21 | 22 | @implementation NSWindowController (SCMAppSwitching) 23 | - (void)scmRefreshApplicationDidBecomeActiveNotification:(id)sender 24 | { 25 | [[SCMIcons sharedInstance] reloadStatusesForAllProjects]; 26 | [self scmRefreshApplicationDidBecomeActiveNotification:sender]; 27 | } 28 | @end 29 | 30 | @implementation NSOutlineView (SCMOutlineView) 31 | - (void)drawOverlayForRow:(int)rowNumber inProject:(NSString*)projectPath; 32 | { 33 | NSDictionary* item = [self itemAtRow:rowNumber]; 34 | 35 | if (item) { 36 | NSString* path = [item objectForKey:@"filename"]; 37 | if (!path) path = [item objectForKey:@"sourceDirectory"]; 38 | SCMIconsStatus status = [[SCMIcons sharedInstance] statusForPath:path inProject:projectPath reload:NO]; 39 | 40 | NSImage* overlay = [[SCMIcons sharedInstance] imageForStatusCode:status]; 41 | if (overlay) 42 | { 43 | // NSAffineTransform* transform = [NSAffineTransform transform]; 44 | // [transform rotateByDegrees:180]; 45 | // [transform concat]; 46 | [overlay setFlipped:YES]; 47 | [overlay drawInRect:NSMakeRect(LIST_OFFSET + ([self levelForRow:rowNumber] + 1) * [self indentationPerLevel], 48 | rowNumber * ([self rowHeight] + [self intercellSpacing].height), 49 | ICON_SIZE, ICON_SIZE) 50 | fromRect:NSZeroRect 51 | operation:NSCompositeSourceOver 52 | fraction:1]; 53 | [overlay setFlipped:NO]; 54 | } 55 | } 56 | } 57 | 58 | - (void)scmDrawRect:(NSRect)rect 59 | { 60 | [self scmDrawRect:rect]; 61 | 62 | NSString* projectPath = [[self delegate] performSelector:@selector(findProjectDirectory)]; 63 | NSRange rows = [self rowsInRect:rect]; 64 | int rowNumber = rows.location; 65 | while (rowNumber <= rows.location + rows.length) 66 | [self drawOverlayForRow:rowNumber++ inProject:projectPath]; 67 | } 68 | @end 69 | 70 | @interface NSWindowPoser : NSWindow 71 | @end 72 | 73 | @implementation NSWindowPoser 74 | // called when the user switches tabs (or load files) 75 | - (void)setRepresentedFilename:(NSString*)path 76 | { 77 | [super setRepresentedFilename:path]; 78 | 79 | if([[self delegate] isKindOfClass:OakProjectController]) 80 | { 81 | NSString* projectPath = [[self delegate] valueForKey:@"projectDirectory"]; 82 | 83 | SCMIconsStatus status = [[SCMIcons sharedInstance] statusForPath:path inProject:projectPath reload:YES]; 84 | NSImage* overlay = [[SCMIcons sharedInstance] imageForStatusCode:status]; 85 | 86 | NSImage* icon = [[[self standardWindowButton:NSWindowDocumentIconButton] image] copy]; 87 | [icon lockFocus]; 88 | 89 | [overlay drawInRect:NSMakeRect(0, 0, [icon size].width, [icon size].height) 90 | fromRect:NSZeroRect 91 | operation:NSCompositeSourceOver 92 | fraction:1]; 93 | 94 | [icon unlockFocus]; 95 | 96 | [[self standardWindowButton:NSWindowDocumentIconButton] setImage:icon]; 97 | [icon release]; 98 | } 99 | } 100 | @end 101 | 102 | static SCMIcons* SharedInstance; 103 | 104 | @implementation SCMIcons 105 | // ================== 106 | // = Setup/Teardown = 107 | // ================== 108 | + (SCMIcons*)sharedInstance 109 | { 110 | return SharedInstance ?: [[self new] autorelease]; 111 | } 112 | 113 | - (id)init 114 | { 115 | if(SharedInstance) 116 | { 117 | [self release]; 118 | } 119 | else if(self = SharedInstance = [[super init] retain]) 120 | { 121 | NSApp = [NSApplication sharedApplication]; 122 | 123 | delegates = [[NSMutableArray alloc] initWithCapacity:1]; 124 | 125 | [NSWindowPoser poseAsClass:[NSWindow class]]; 126 | 127 | [self loadIconPacks]; 128 | 129 | [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:0] forKey:@"SCMMateSelectedIconPack"]]; 130 | 131 | [NSClassFromString(@"OakOutlineView") jr_swizzleMethod:@selector(drawRect:) withMethod:@selector(scmDrawRect:) error:NULL]; 132 | [NSClassFromString(@"OakProjectController") jr_swizzleMethod:@selector(applicationDidBecomeActiveNotification:) withMethod:@selector(scmRefreshApplicationDidBecomeActiveNotification:) error:NULL]; 133 | 134 | // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:nil]; 135 | } 136 | return SharedInstance; 137 | } 138 | 139 | // - (void)applicationDidBecomeActive:(id)sender 140 | // { 141 | // NSLog(@"[%@ applicationDidBecomeActiveNotification:%@]", [self class], sender); 142 | // [[SCMIcons sharedInstance] reloadStatusesForAllProjects]; 143 | // 144 | // NSArray* windows = [NSApp windows]; 145 | // 146 | // for(int index = 0; index < [windows count]; index++) 147 | // { 148 | // NSWindow* window = [windows objectAtIndex:index]; 149 | // if([window delegate] && [[window delegate] isKindOfClass:OakProjectController]) 150 | // [[[window delegate] valueForKey:@"outlineView"] setNeedsDisplay:YES]; 151 | // } 152 | // } 153 | 154 | - (BOOL)scmIsEnabled:(NSString*)scmName; 155 | { 156 | return [[NSUserDefaults standardUserDefaults] boolForKey:[NSString stringWithFormat:@"ProjectPlus %@ Enabled", scmName]]; 157 | } 158 | 159 | - (void)setScm:(NSString*)scmName isEnabled:(BOOL)enabled; 160 | { 161 | [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:[NSString stringWithFormat:@"ProjectPlus %@ Enabled", scmName]]; 162 | } 163 | 164 | - (void)redisplayProjectTrees; 165 | { 166 | [[NSNotificationCenter defaultCenter] postNotificationName:ProjectPlus_redrawRequired object:nil]; 167 | } 168 | 169 | - (void)registerSCMDelegate:(id )delegate; 170 | { 171 | [delegates addObject:delegate]; 172 | [self redisplayProjectTrees]; 173 | } 174 | 175 | - (void)awakeFromNib 176 | { 177 | [self setSelectedIconPackIndex:[[NSUserDefaults standardUserDefaults] integerForKey:@"SCMMateSelectedIconPack"]]; 178 | [iconPacksController addObserver:self forKeyPath:@"selectionIndex" options:NULL context:NULL]; 179 | } 180 | 181 | - (void)dealloc 182 | { 183 | [delegates release]; 184 | [iconPacks release]; 185 | [super dealloc]; 186 | } 187 | 188 | // ========= 189 | // = Icons = 190 | // ========= 191 | - (NSDictionary*)iconPackNamed:(NSString*)iconPackName; 192 | { 193 | size_t imageCount = sizeof(overlayImageNames) / sizeof(NSString*); 194 | NSMutableDictionary* iconPack = [NSMutableDictionary dictionaryWithCapacity:imageCount]; 195 | NSString* path = [@"icons" stringByAppendingPathComponent:iconPackName]; 196 | 197 | for(size_t index = 0; index < imageCount; index += 1) 198 | { 199 | NSString* imageName = overlayImageNames[index]; 200 | size_t imageTypeCount = [[NSImage imageFileTypes] count]; 201 | 202 | for(size_t index = 0; index < imageTypeCount; index += 1) 203 | { 204 | NSString* imageType = [[NSImage imageFileTypes] objectAtIndex:index]; 205 | 206 | if(NSString* imagePath = [[NSBundle bundleForClass:[SCMIcons class]] pathForResource:imageName ofType:imageType inDirectory:path]) 207 | { 208 | if(NSImage* image = [[NSImage alloc] initByReferencingFile:imagePath]) 209 | { 210 | // [image setFlipped:YES]; 211 | [iconPack setObject:image forKey:imageName]; 212 | [image release]; 213 | break; 214 | } 215 | } 216 | } 217 | } 218 | return iconPack; 219 | } 220 | 221 | - (void)loadIconPacks; 222 | { 223 | [iconPacks release]; 224 | iconPacks = [[NSMutableArray alloc] initWithCapacity:5]; 225 | 226 | // NSArray* iconPackNames = [NSArray arrayWithObjects:@"Straight",@"Classic",nil]; 227 | NSString* iconsPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"icons" ofType:nil]; 228 | NSDirectoryEnumerator* dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:iconsPath]; 229 | NSString* iconPackName; 230 | 231 | while(iconPackName = [dirEnum nextObject]) 232 | { 233 | [dirEnum skipDescendents]; 234 | if([[dirEnum fileAttributes] objectForKey:NSFileType] == NSFileTypeDirectory) 235 | { 236 | NSDictionary* icons = [self iconPackNamed:iconPackName]; 237 | if(icons && [icons count]) 238 | { 239 | NSDictionary* iconPack = [NSDictionary dictionaryWithObjectsAndKeys:[self iconPackNamed:iconPackName],@"icons",iconPackName,@"name",nil]; 240 | [iconPacks addObject:iconPack]; 241 | } 242 | } 243 | } 244 | } 245 | 246 | - (NSDictionary*)iconPack; 247 | { 248 | NSDictionary* iconPack = nil; 249 | int selectedIndex = [[NSUserDefaults standardUserDefaults] integerForKey:@"SCMMateSelectedIconPack"]; 250 | 251 | if(selectedIndex < [[iconPacksController arrangedObjects] count]) 252 | iconPack = [[[iconPacksController arrangedObjects] objectAtIndex:selectedIndex] objectForKey:@"icons"]; 253 | 254 | return iconPack; 255 | } 256 | 257 | - (NSImage*)overlayIcon:(NSString*)name 258 | { 259 | return [[self iconPack] objectForKey:name]; 260 | } 261 | 262 | - (NSImage*)imageForStatusCode:(SCMIconsStatus)status 263 | { 264 | switch(status) 265 | { 266 | case 0: 267 | case SCMIconsStatusVersioned: return [self overlayIcon:@"Versioned"]; 268 | case SCMIconsStatusModified: return [self overlayIcon:@"Modified"]; 269 | case SCMIconsStatusAdded: return [self overlayIcon:@"Added"]; 270 | case SCMIconsStatusDeleted: return [self overlayIcon:@"Deleted"]; 271 | case SCMIconsStatusConflicted: return [self overlayIcon:@"Conflicted"]; 272 | case SCMIconsStatusUnversioned: return [self overlayIcon:@"Unversioned"]; 273 | } 274 | 275 | return nil; 276 | } 277 | 278 | - (void)setSelectedIconPackIndex:(int)index 279 | { 280 | [iconPacksController setSelectionIndex:index]; 281 | [[NSUserDefaults standardUserDefaults] setInteger:index forKey:@"SCMMateSelectedIconPack"]; 282 | [self redisplayProjectTrees]; 283 | } 284 | 285 | - (void)observeValueForKeyPath:(NSString*)key ofObject:(id)object change:(NSDictionary*)changes context:(void*)context 286 | { 287 | [self setSelectedIconPackIndex:[iconPacksController selectionIndex]]; 288 | } 289 | 290 | // Delegate notifications/requests 291 | - (void)reloadStatusesForAllProjects; 292 | { 293 | NSArray* windows = [NSApp windows]; 294 | 295 | for(int index = 0; index < [windows count]; index++) 296 | { 297 | NSWindow* window = [windows objectAtIndex:index]; 298 | if([window delegate] && [[window delegate] isKindOfClass:OakProjectController]) 299 | { 300 | [self reloadStatusesForProject:[[window delegate] valueForKey:@"projectDirectory"]]; 301 | } 302 | } 303 | [self redisplayProjectTrees]; 304 | } 305 | 306 | - (void)reloadStatusesForProject:(NSString*)projectPath; 307 | { 308 | for(int delegateIndex = 0; delegateIndex < [delegates count]; delegateIndex++) 309 | { 310 | id delegate = [delegates objectAtIndex:delegateIndex]; 311 | if([self scmIsEnabled:[delegate scmName]] && [delegate respondsToSelector:@selector(reloadStatusesForProject:)]) 312 | [delegate reloadStatusesForProject:projectPath]; 313 | } 314 | } 315 | 316 | - (SCMIconsStatus)statusForPath:(NSString*)path inProject:(NSString*)projectPath reload:(BOOL)reload; 317 | { 318 | if([path length] == 0) 319 | return SCMIconsStatusUnknown; 320 | 321 | for(int index = 0; index < [delegates count]; index++) 322 | { 323 | id delegate = [delegates objectAtIndex:index]; 324 | if([self scmIsEnabled:[delegate scmName]]) 325 | { 326 | SCMIconsStatus status = [delegate statusForPath:path inProject:projectPath reload:reload]; 327 | if(status != SCMIconsStatusUnknown) 328 | return status; 329 | } 330 | } 331 | return SCMIconsStatusUnknown; 332 | } 333 | 334 | - (int)numberOfRowsInTableView:(NSTableView*)tableView 335 | { 336 | return [delegates count]; 337 | } 338 | 339 | - (void)tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(int)rowIndex 340 | { 341 | NSString* name = [[delegates objectAtIndex:rowIndex] scmName]; 342 | [cell setTitle:name]; 343 | [cell setState:[self scmIsEnabled:name] ? NSOnState : NSOffState]; 344 | } 345 | 346 | - (id)tableView:(NSTableView*)tableView objectValueForTableColumn:(NSTableColumn*)tableColumn row:(int)rowIndex 347 | { 348 | return [[delegates objectAtIndex:rowIndex] scmName]; 349 | } 350 | 351 | - (void)tableView:(NSTableView*)tableView setObjectValue:(id)value forTableColumn:(NSTableColumn*)tableColumn row:(int)rowIndex 352 | { 353 | [self setScm:[[delegates objectAtIndex:rowIndex] scmName] isEnabled:[value boolValue]]; 354 | [self reloadStatusesForAllProjects]; 355 | [self redisplayProjectTrees]; 356 | } 357 | 358 | // =========== 359 | // = Utility = 360 | // =========== 361 | - (NSString*)pathForVariable:(NSString*)shellVariableName paths:(NSArray*)paths; 362 | { 363 | NSArray* prefs = [[OakPreferencesManager sharedInstance] performSelector:@selector(shellVariables)]; 364 | for(int index = 0; index < [prefs count]; index++) 365 | { 366 | NSDictionary* pref = [prefs objectAtIndex:index]; 367 | if([[pref objectForKey:@"variable"] isEqualToString:shellVariableName] && [[pref objectForKey:@"enabled"] boolValue]) 368 | return [pref objectForKey:@"value"]; 369 | } 370 | for(int index = 0; index < [paths count]; index++) 371 | { 372 | NSString* path = [paths objectAtIndex:index]; 373 | if([[NSFileManager defaultManager] fileExistsAtPath:path]) 374 | return path; 375 | } 376 | return nil; 377 | } 378 | @end 379 | --------------------------------------------------------------------------------