├── .gitignore ├── SMTimelineEditor.xcodeproj └── project.pbxproj └── SMTimelineEditor ├── Images ├── TimeMarker.png ├── seq-btn-play.png ├── seq-btn-restart.png ├── seq-btn-stepback.png ├── seq-btn-stepforward.png ├── seq-btn-stop.png └── seq-next-seq.png ├── SMAppDelegate.h ├── SMAppDelegate.m ├── SMDebugTableView.h ├── SMDebugTableView.m ├── SMElement.h ├── SMElement.m ├── SMGuideView.h ├── SMGuideView.m ├── SMTestView.h ├── SMTestView.m ├── SMTimeline.h ├── SMTimeline.m ├── SMTimelineEditor-Info.plist ├── SMTimelineEditor-Prefix.pch ├── SMTimelineSegment.h ├── SMTimelineSegment.m ├── SMTimelineTableView.h ├── SMTimelineTableView.m ├── SMTimelineTrackView.h ├── SMTimelineTrackView.m ├── SMTimelineView.h ├── SMTimelineView.m ├── en.lproj ├── Credits.rtf ├── InfoPlist.strings └── MainMenu.xib └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | SMTimelineEditor.xcodeproj/project.xcworkspace/xcuserdata/stephanmichels.xcuserdatad/UserInterfaceState.xcuserstate 2 | SMTimelineEditor.xcodeproj/xcuserdata/stephanmichels.xcuserdatad/xcschemes/SMTimelineEditor.xcscheme 3 | SMTimelineEditor.xcodeproj/xcuserdata/stephanmichels.xcuserdatad/xcschemes/xcschememanagement.plist 4 | SMTimelineEditor.xcodeproj/project.xcworkspace/contents.xcworkspacedata 5 | SMTimelineEditor.xcodeproj/xcuserdata/stephanmichels.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist -------------------------------------------------------------------------------- /SMTimelineEditor.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 766451081613304D005FBF58 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766451071613304D005FBF58 /* Cocoa.framework */; }; 11 | 766451121613304D005FBF58 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 766451101613304D005FBF58 /* InfoPlist.strings */; }; 12 | 766451141613304D005FBF58 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 766451131613304D005FBF58 /* main.m */; }; 13 | 766451181613304D005FBF58 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 766451161613304D005FBF58 /* Credits.rtf */; }; 14 | 7664511B1613304D005FBF58 /* SMAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7664511A1613304D005FBF58 /* SMAppDelegate.m */; }; 15 | 7664511E1613304E005FBF58 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7664511C1613304E005FBF58 /* MainMenu.xib */; }; 16 | 76645126161332E2005FBF58 /* SMElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 76645125161332E2005FBF58 /* SMElement.m */; }; 17 | 7699EB6E16197AF900673C9C /* SMGuideView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7699EB6D16197AF900673C9C /* SMGuideView.m */; }; 18 | 769AB77416144ABF009398D8 /* SMTimelineTrackView.m in Sources */ = {isa = PBXBuildFile; fileRef = 769AB77316144ABF009398D8 /* SMTimelineTrackView.m */; }; 19 | 769AB79E16148F3C009398D8 /* SMTimeline.m in Sources */ = {isa = PBXBuildFile; fileRef = 769AB79D16148F3C009398D8 /* SMTimeline.m */; }; 20 | 769AB7A11614904D009398D8 /* SMTimelineSegment.m in Sources */ = {isa = PBXBuildFile; fileRef = 769AB7A01614904D009398D8 /* SMTimelineSegment.m */; }; 21 | 769AB7A91614A456009398D8 /* SMTestView.m in Sources */ = {isa = PBXBuildFile; fileRef = 769AB7A81614A455009398D8 /* SMTestView.m */; }; 22 | 769AB7AD1614A81B009398D8 /* SMDebugTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 769AB7AC1614A81B009398D8 /* SMDebugTableView.m */; }; 23 | 769AB7B01614AAE4009398D8 /* SMTimelineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 769AB7AF1614AAE4009398D8 /* SMTimelineView.m */; }; 24 | 769AB7D81615BE39009398D8 /* TimeMarker.png in Resources */ = {isa = PBXBuildFile; fileRef = 769AB7D71615BE39009398D8 /* TimeMarker.png */; }; 25 | 769AB7DB1615C019009398D8 /* SMTimelineTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 769AB7DA1615C018009398D8 /* SMTimelineTableView.m */; }; 26 | 76E3DC22161B2AEB00B04E20 /* seq-btn-play.png in Resources */ = {isa = PBXBuildFile; fileRef = 76E3DC1C161B2AEB00B04E20 /* seq-btn-play.png */; }; 27 | 76E3DC23161B2AEB00B04E20 /* seq-btn-restart.png in Resources */ = {isa = PBXBuildFile; fileRef = 76E3DC1D161B2AEB00B04E20 /* seq-btn-restart.png */; }; 28 | 76E3DC24161B2AEB00B04E20 /* seq-btn-stepback.png in Resources */ = {isa = PBXBuildFile; fileRef = 76E3DC1E161B2AEB00B04E20 /* seq-btn-stepback.png */; }; 29 | 76E3DC25161B2AEB00B04E20 /* seq-btn-stepforward.png in Resources */ = {isa = PBXBuildFile; fileRef = 76E3DC1F161B2AEB00B04E20 /* seq-btn-stepforward.png */; }; 30 | 76E3DC26161B2AEB00B04E20 /* seq-btn-stop.png in Resources */ = {isa = PBXBuildFile; fileRef = 76E3DC20161B2AEB00B04E20 /* seq-btn-stop.png */; }; 31 | 76E3DC27161B2AEB00B04E20 /* seq-next-seq.png in Resources */ = {isa = PBXBuildFile; fileRef = 76E3DC21161B2AEB00B04E20 /* seq-next-seq.png */; }; 32 | /* End PBXBuildFile section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 766451031613304D005FBF58 /* SMTimelineEditor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SMTimelineEditor.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 766451071613304D005FBF58 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 37 | 7664510A1613304D005FBF58 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 38 | 7664510B1613304D005FBF58 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 39 | 7664510C1613304D005FBF58 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 40 | 7664510F1613304D005FBF58 /* SMTimelineEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SMTimelineEditor-Info.plist"; sourceTree = ""; }; 41 | 766451111613304D005FBF58 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 42 | 766451131613304D005FBF58 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 43 | 766451151613304D005FBF58 /* SMTimelineEditor-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SMTimelineEditor-Prefix.pch"; sourceTree = ""; }; 44 | 766451171613304D005FBF58 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 45 | 766451191613304D005FBF58 /* SMAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SMAppDelegate.h; sourceTree = ""; }; 46 | 7664511A1613304D005FBF58 /* SMAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SMAppDelegate.m; sourceTree = ""; }; 47 | 7664511D1613304E005FBF58 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; 48 | 76645124161332E2005FBF58 /* SMElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMElement.h; sourceTree = ""; }; 49 | 76645125161332E2005FBF58 /* SMElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMElement.m; sourceTree = ""; }; 50 | 7699EB6C16197AF900673C9C /* SMGuideView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMGuideView.h; sourceTree = ""; }; 51 | 7699EB6D16197AF900673C9C /* SMGuideView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMGuideView.m; sourceTree = ""; }; 52 | 769AB77216144ABF009398D8 /* SMTimelineTrackView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMTimelineTrackView.h; sourceTree = ""; }; 53 | 769AB77316144ABF009398D8 /* SMTimelineTrackView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMTimelineTrackView.m; sourceTree = ""; }; 54 | 769AB79C16148F3C009398D8 /* SMTimeline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMTimeline.h; sourceTree = ""; }; 55 | 769AB79D16148F3C009398D8 /* SMTimeline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMTimeline.m; sourceTree = ""; }; 56 | 769AB79F1614904D009398D8 /* SMTimelineSegment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMTimelineSegment.h; sourceTree = ""; }; 57 | 769AB7A01614904D009398D8 /* SMTimelineSegment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMTimelineSegment.m; sourceTree = ""; }; 58 | 769AB7A71614A455009398D8 /* SMTestView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMTestView.h; sourceTree = ""; }; 59 | 769AB7A81614A455009398D8 /* SMTestView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMTestView.m; sourceTree = ""; }; 60 | 769AB7AB1614A81B009398D8 /* SMDebugTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMDebugTableView.h; sourceTree = ""; }; 61 | 769AB7AC1614A81B009398D8 /* SMDebugTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMDebugTableView.m; sourceTree = ""; }; 62 | 769AB7AE1614AAE3009398D8 /* SMTimelineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMTimelineView.h; sourceTree = ""; }; 63 | 769AB7AF1614AAE4009398D8 /* SMTimelineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMTimelineView.m; sourceTree = ""; }; 64 | 769AB7D71615BE39009398D8 /* TimeMarker.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = TimeMarker.png; sourceTree = ""; }; 65 | 769AB7D91615C018009398D8 /* SMTimelineTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMTimelineTableView.h; sourceTree = ""; }; 66 | 769AB7DA1615C018009398D8 /* SMTimelineTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMTimelineTableView.m; sourceTree = ""; }; 67 | 76E3DC1C161B2AEB00B04E20 /* seq-btn-play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "seq-btn-play.png"; sourceTree = ""; }; 68 | 76E3DC1D161B2AEB00B04E20 /* seq-btn-restart.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "seq-btn-restart.png"; sourceTree = ""; }; 69 | 76E3DC1E161B2AEB00B04E20 /* seq-btn-stepback.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "seq-btn-stepback.png"; sourceTree = ""; }; 70 | 76E3DC1F161B2AEB00B04E20 /* seq-btn-stepforward.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "seq-btn-stepforward.png"; sourceTree = ""; }; 71 | 76E3DC20161B2AEB00B04E20 /* seq-btn-stop.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "seq-btn-stop.png"; sourceTree = ""; }; 72 | 76E3DC21161B2AEB00B04E20 /* seq-next-seq.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "seq-next-seq.png"; sourceTree = ""; }; 73 | /* End PBXFileReference section */ 74 | 75 | /* Begin PBXFrameworksBuildPhase section */ 76 | 766451001613304D005FBF58 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | 766451081613304D005FBF58 /* Cocoa.framework in Frameworks */, 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | /* End PBXFrameworksBuildPhase section */ 85 | 86 | /* Begin PBXGroup section */ 87 | 766450F81613304D005FBF58 = { 88 | isa = PBXGroup; 89 | children = ( 90 | 7664510D1613304D005FBF58 /* SMTimelineEditor */, 91 | 766451061613304D005FBF58 /* Frameworks */, 92 | 766451041613304D005FBF58 /* Products */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 766451041613304D005FBF58 /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 766451031613304D005FBF58 /* SMTimelineEditor.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 766451061613304D005FBF58 /* Frameworks */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 766451071613304D005FBF58 /* Cocoa.framework */, 108 | 766451091613304D005FBF58 /* Other Frameworks */, 109 | ); 110 | name = Frameworks; 111 | sourceTree = ""; 112 | }; 113 | 766451091613304D005FBF58 /* Other Frameworks */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 7664510A1613304D005FBF58 /* AppKit.framework */, 117 | 7664510B1613304D005FBF58 /* CoreData.framework */, 118 | 7664510C1613304D005FBF58 /* Foundation.framework */, 119 | ); 120 | name = "Other Frameworks"; 121 | sourceTree = ""; 122 | }; 123 | 7664510D1613304D005FBF58 /* SMTimelineEditor */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 766451191613304D005FBF58 /* SMAppDelegate.h */, 127 | 7664511A1613304D005FBF58 /* SMAppDelegate.m */, 128 | 769AB7AE1614AAE3009398D8 /* SMTimelineView.h */, 129 | 769AB7AF1614AAE4009398D8 /* SMTimelineView.m */, 130 | 769AB77216144ABF009398D8 /* SMTimelineTrackView.h */, 131 | 769AB77316144ABF009398D8 /* SMTimelineTrackView.m */, 132 | 769AB7A71614A455009398D8 /* SMTestView.h */, 133 | 769AB7A81614A455009398D8 /* SMTestView.m */, 134 | 76645124161332E2005FBF58 /* SMElement.h */, 135 | 76645125161332E2005FBF58 /* SMElement.m */, 136 | 769AB79C16148F3C009398D8 /* SMTimeline.h */, 137 | 769AB79D16148F3C009398D8 /* SMTimeline.m */, 138 | 769AB79F1614904D009398D8 /* SMTimelineSegment.h */, 139 | 769AB7A01614904D009398D8 /* SMTimelineSegment.m */, 140 | 7664511C1613304E005FBF58 /* MainMenu.xib */, 141 | 769AB7AB1614A81B009398D8 /* SMDebugTableView.h */, 142 | 769AB7AC1614A81B009398D8 /* SMDebugTableView.m */, 143 | 769AB7D91615C018009398D8 /* SMTimelineTableView.h */, 144 | 769AB7DA1615C018009398D8 /* SMTimelineTableView.m */, 145 | 7699EB6C16197AF900673C9C /* SMGuideView.h */, 146 | 7699EB6D16197AF900673C9C /* SMGuideView.m */, 147 | 769AB7D61615BE38009398D8 /* Images */, 148 | 7664510E1613304D005FBF58 /* Supporting Files */, 149 | ); 150 | path = SMTimelineEditor; 151 | sourceTree = ""; 152 | }; 153 | 7664510E1613304D005FBF58 /* Supporting Files */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 7664510F1613304D005FBF58 /* SMTimelineEditor-Info.plist */, 157 | 766451101613304D005FBF58 /* InfoPlist.strings */, 158 | 766451131613304D005FBF58 /* main.m */, 159 | 766451151613304D005FBF58 /* SMTimelineEditor-Prefix.pch */, 160 | 766451161613304D005FBF58 /* Credits.rtf */, 161 | ); 162 | name = "Supporting Files"; 163 | sourceTree = ""; 164 | }; 165 | 769AB7D61615BE38009398D8 /* Images */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 76E3DC1C161B2AEB00B04E20 /* seq-btn-play.png */, 169 | 76E3DC1D161B2AEB00B04E20 /* seq-btn-restart.png */, 170 | 76E3DC1E161B2AEB00B04E20 /* seq-btn-stepback.png */, 171 | 76E3DC1F161B2AEB00B04E20 /* seq-btn-stepforward.png */, 172 | 76E3DC20161B2AEB00B04E20 /* seq-btn-stop.png */, 173 | 76E3DC21161B2AEB00B04E20 /* seq-next-seq.png */, 174 | 769AB7D71615BE39009398D8 /* TimeMarker.png */, 175 | ); 176 | path = Images; 177 | sourceTree = ""; 178 | }; 179 | /* End PBXGroup section */ 180 | 181 | /* Begin PBXNativeTarget section */ 182 | 766451021613304D005FBF58 /* SMTimelineEditor */ = { 183 | isa = PBXNativeTarget; 184 | buildConfigurationList = 766451211613304E005FBF58 /* Build configuration list for PBXNativeTarget "SMTimelineEditor" */; 185 | buildPhases = ( 186 | 766450FF1613304D005FBF58 /* Sources */, 187 | 766451001613304D005FBF58 /* Frameworks */, 188 | 766451011613304D005FBF58 /* Resources */, 189 | ); 190 | buildRules = ( 191 | ); 192 | dependencies = ( 193 | ); 194 | name = SMTimelineEditor; 195 | productName = SMTimelineEditor; 196 | productReference = 766451031613304D005FBF58 /* SMTimelineEditor.app */; 197 | productType = "com.apple.product-type.application"; 198 | }; 199 | /* End PBXNativeTarget section */ 200 | 201 | /* Begin PBXProject section */ 202 | 766450FA1613304D005FBF58 /* Project object */ = { 203 | isa = PBXProject; 204 | attributes = { 205 | CLASSPREFIX = SM; 206 | LastUpgradeCheck = 0450; 207 | ORGANIZATIONNAME = "Stephan Michels"; 208 | }; 209 | buildConfigurationList = 766450FD1613304D005FBF58 /* Build configuration list for PBXProject "SMTimelineEditor" */; 210 | compatibilityVersion = "Xcode 3.2"; 211 | developmentRegion = English; 212 | hasScannedForEncodings = 0; 213 | knownRegions = ( 214 | en, 215 | ); 216 | mainGroup = 766450F81613304D005FBF58; 217 | productRefGroup = 766451041613304D005FBF58 /* Products */; 218 | projectDirPath = ""; 219 | projectRoot = ""; 220 | targets = ( 221 | 766451021613304D005FBF58 /* SMTimelineEditor */, 222 | ); 223 | }; 224 | /* End PBXProject section */ 225 | 226 | /* Begin PBXResourcesBuildPhase section */ 227 | 766451011613304D005FBF58 /* Resources */ = { 228 | isa = PBXResourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | 766451121613304D005FBF58 /* InfoPlist.strings in Resources */, 232 | 766451181613304D005FBF58 /* Credits.rtf in Resources */, 233 | 7664511E1613304E005FBF58 /* MainMenu.xib in Resources */, 234 | 769AB7D81615BE39009398D8 /* TimeMarker.png in Resources */, 235 | 76E3DC22161B2AEB00B04E20 /* seq-btn-play.png in Resources */, 236 | 76E3DC23161B2AEB00B04E20 /* seq-btn-restart.png in Resources */, 237 | 76E3DC24161B2AEB00B04E20 /* seq-btn-stepback.png in Resources */, 238 | 76E3DC25161B2AEB00B04E20 /* seq-btn-stepforward.png in Resources */, 239 | 76E3DC26161B2AEB00B04E20 /* seq-btn-stop.png in Resources */, 240 | 76E3DC27161B2AEB00B04E20 /* seq-next-seq.png in Resources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXResourcesBuildPhase section */ 245 | 246 | /* Begin PBXSourcesBuildPhase section */ 247 | 766450FF1613304D005FBF58 /* Sources */ = { 248 | isa = PBXSourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | 766451141613304D005FBF58 /* main.m in Sources */, 252 | 7664511B1613304D005FBF58 /* SMAppDelegate.m in Sources */, 253 | 76645126161332E2005FBF58 /* SMElement.m in Sources */, 254 | 769AB77416144ABF009398D8 /* SMTimelineTrackView.m in Sources */, 255 | 769AB79E16148F3C009398D8 /* SMTimeline.m in Sources */, 256 | 769AB7A11614904D009398D8 /* SMTimelineSegment.m in Sources */, 257 | 769AB7A91614A456009398D8 /* SMTestView.m in Sources */, 258 | 769AB7AD1614A81B009398D8 /* SMDebugTableView.m in Sources */, 259 | 769AB7B01614AAE4009398D8 /* SMTimelineView.m in Sources */, 260 | 769AB7DB1615C019009398D8 /* SMTimelineTableView.m in Sources */, 261 | 7699EB6E16197AF900673C9C /* SMGuideView.m in Sources */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | /* End PBXSourcesBuildPhase section */ 266 | 267 | /* Begin PBXVariantGroup section */ 268 | 766451101613304D005FBF58 /* InfoPlist.strings */ = { 269 | isa = PBXVariantGroup; 270 | children = ( 271 | 766451111613304D005FBF58 /* en */, 272 | ); 273 | name = InfoPlist.strings; 274 | sourceTree = ""; 275 | }; 276 | 766451161613304D005FBF58 /* Credits.rtf */ = { 277 | isa = PBXVariantGroup; 278 | children = ( 279 | 766451171613304D005FBF58 /* en */, 280 | ); 281 | name = Credits.rtf; 282 | sourceTree = ""; 283 | }; 284 | 7664511C1613304E005FBF58 /* MainMenu.xib */ = { 285 | isa = PBXVariantGroup; 286 | children = ( 287 | 7664511D1613304E005FBF58 /* en */, 288 | ); 289 | name = MainMenu.xib; 290 | sourceTree = ""; 291 | }; 292 | /* End PBXVariantGroup section */ 293 | 294 | /* Begin XCBuildConfiguration section */ 295 | 7664511F1613304E005FBF58 /* Debug */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | ALWAYS_SEARCH_USER_PATHS = NO; 299 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 300 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 301 | CLANG_CXX_LIBRARY = "libc++"; 302 | CLANG_ENABLE_OBJC_ARC = YES; 303 | CLANG_WARN_EMPTY_BODY = YES; 304 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 305 | COPY_PHASE_STRIP = NO; 306 | GCC_C_LANGUAGE_STANDARD = gnu99; 307 | GCC_DYNAMIC_NO_PIC = NO; 308 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 309 | GCC_OPTIMIZATION_LEVEL = 0; 310 | GCC_PREPROCESSOR_DEFINITIONS = ( 311 | "DEBUG=1", 312 | "$(inherited)", 313 | ); 314 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 315 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 316 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 317 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 318 | GCC_WARN_UNUSED_VARIABLE = YES; 319 | MACOSX_DEPLOYMENT_TARGET = 10.8; 320 | ONLY_ACTIVE_ARCH = YES; 321 | SDKROOT = macosx; 322 | }; 323 | name = Debug; 324 | }; 325 | 766451201613304E005FBF58 /* Release */ = { 326 | isa = XCBuildConfiguration; 327 | buildSettings = { 328 | ALWAYS_SEARCH_USER_PATHS = NO; 329 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 330 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 331 | CLANG_CXX_LIBRARY = "libc++"; 332 | CLANG_ENABLE_OBJC_ARC = YES; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 | COPY_PHASE_STRIP = YES; 336 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 337 | GCC_C_LANGUAGE_STANDARD = gnu99; 338 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 339 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 340 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 341 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 342 | GCC_WARN_UNUSED_VARIABLE = YES; 343 | MACOSX_DEPLOYMENT_TARGET = 10.8; 344 | SDKROOT = macosx; 345 | }; 346 | name = Release; 347 | }; 348 | 766451221613304E005FBF58 /* Debug */ = { 349 | isa = XCBuildConfiguration; 350 | buildSettings = { 351 | COMBINE_HIDPI_IMAGES = YES; 352 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 353 | GCC_PREFIX_HEADER = "SMTimelineEditor/SMTimelineEditor-Prefix.pch"; 354 | INFOPLIST_FILE = "SMTimelineEditor/SMTimelineEditor-Info.plist"; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | WRAPPER_EXTENSION = app; 357 | }; 358 | name = Debug; 359 | }; 360 | 766451231613304E005FBF58 /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | COMBINE_HIDPI_IMAGES = YES; 364 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 365 | GCC_PREFIX_HEADER = "SMTimelineEditor/SMTimelineEditor-Prefix.pch"; 366 | INFOPLIST_FILE = "SMTimelineEditor/SMTimelineEditor-Info.plist"; 367 | PRODUCT_NAME = "$(TARGET_NAME)"; 368 | WRAPPER_EXTENSION = app; 369 | }; 370 | name = Release; 371 | }; 372 | /* End XCBuildConfiguration section */ 373 | 374 | /* Begin XCConfigurationList section */ 375 | 766450FD1613304D005FBF58 /* Build configuration list for PBXProject "SMTimelineEditor" */ = { 376 | isa = XCConfigurationList; 377 | buildConfigurations = ( 378 | 7664511F1613304E005FBF58 /* Debug */, 379 | 766451201613304E005FBF58 /* Release */, 380 | ); 381 | defaultConfigurationIsVisible = 0; 382 | defaultConfigurationName = Release; 383 | }; 384 | 766451211613304E005FBF58 /* Build configuration list for PBXNativeTarget "SMTimelineEditor" */ = { 385 | isa = XCConfigurationList; 386 | buildConfigurations = ( 387 | 766451221613304E005FBF58 /* Debug */, 388 | 766451231613304E005FBF58 /* Release */, 389 | ); 390 | defaultConfigurationIsVisible = 0; 391 | defaultConfigurationName = Release; 392 | }; 393 | /* End XCConfigurationList section */ 394 | }; 395 | rootObject = 766450FA1613304D005FBF58 /* Project object */; 396 | } 397 | -------------------------------------------------------------------------------- /SMTimelineEditor/Images/TimeMarker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smic/SMTimelineEditor/bdf3cea96062664dfe70c5d7d235c3cb6709cbf8/SMTimelineEditor/Images/TimeMarker.png -------------------------------------------------------------------------------- /SMTimelineEditor/Images/seq-btn-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smic/SMTimelineEditor/bdf3cea96062664dfe70c5d7d235c3cb6709cbf8/SMTimelineEditor/Images/seq-btn-play.png -------------------------------------------------------------------------------- /SMTimelineEditor/Images/seq-btn-restart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smic/SMTimelineEditor/bdf3cea96062664dfe70c5d7d235c3cb6709cbf8/SMTimelineEditor/Images/seq-btn-restart.png -------------------------------------------------------------------------------- /SMTimelineEditor/Images/seq-btn-stepback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smic/SMTimelineEditor/bdf3cea96062664dfe70c5d7d235c3cb6709cbf8/SMTimelineEditor/Images/seq-btn-stepback.png -------------------------------------------------------------------------------- /SMTimelineEditor/Images/seq-btn-stepforward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smic/SMTimelineEditor/bdf3cea96062664dfe70c5d7d235c3cb6709cbf8/SMTimelineEditor/Images/seq-btn-stepforward.png -------------------------------------------------------------------------------- /SMTimelineEditor/Images/seq-btn-stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smic/SMTimelineEditor/bdf3cea96062664dfe70c5d7d235c3cb6709cbf8/SMTimelineEditor/Images/seq-btn-stop.png -------------------------------------------------------------------------------- /SMTimelineEditor/Images/seq-next-seq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smic/SMTimelineEditor/bdf3cea96062664dfe70c5d7d235c3cb6709cbf8/SMTimelineEditor/Images/seq-next-seq.png -------------------------------------------------------------------------------- /SMTimelineEditor/SMAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMAppDelegate.h 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 26.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMAppDelegate : NSObject 12 | 13 | @property (assign) IBOutlet NSWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMAppDelegate.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 26.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import "SMAppDelegate.h" 10 | #import "SMElement.h" 11 | #import "SMTimeline.h" 12 | #import "SMTimelineSegment.h" 13 | #import "SMTimelineTableView.h" 14 | #import "SMGuideView.h" 15 | 16 | 17 | static NSString * const SMElementPropertyX = @"SMElementPropertyX"; 18 | static NSString * const SMElementPropertyY = @"SMElementPropertyY"; 19 | static NSString * const SMElementPropertyWidth = @"SMElementPropertyWidth"; 20 | static NSString * const SMElementPropertyHeight = @"SMElementPropertyHeight"; 21 | 22 | @interface SMAppDelegate () 23 | 24 | @property (weak) IBOutlet NSScrollView *outlineScrollView; 25 | @property (weak) IBOutlet NSOutlineView *outline; 26 | @property (weak) IBOutlet NSScrollView *tableScrollView; 27 | @property (weak) IBOutlet NSTableView *table; 28 | @property (weak) IBOutlet SMGuideView *guideView; 29 | 30 | @property (nonatomic) NSTimeInterval time; 31 | @property (strong, nonatomic) NSArray *elements; 32 | @property (strong, nonatomic) NSMutableIndexSet *expandedElements; 33 | 34 | @end 35 | 36 | @implementation SMAppDelegate 37 | 38 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 39 | { 40 | // Insert code here to initialize your application 41 | [self startSynchronizing]; 42 | 43 | self.time = 100.0; 44 | 45 | [NSRulerView registerUnitWithName:@"seconds" abbreviation:@"s" unitToPointsConversionFactor:100 stepUpCycle:@[@10.0f] stepDownCycle:@[@0.5f, @0.1f]]; 46 | 47 | self.tableScrollView.hasHorizontalRuler = YES; 48 | self.tableScrollView.rulersVisible = YES; 49 | 50 | NSRulerView *rulerView = self.tableScrollView.horizontalRulerView; 51 | // rulerView.reservedThicknessForAccessoryView = 0.0f; 52 | rulerView.reservedThicknessForMarkers = 12.0f; 53 | rulerView.ruleThickness = 17.0f; 54 | 55 | NSLog(@"height=%f", rulerView.frame.size.height); 56 | 57 | rulerView.measurementUnits = @"seconds"; 58 | rulerView.originOffset = 2.0f + 5.0f; 59 | 60 | [rulerView setClientView:self.tableScrollView.documentView]; 61 | NSRulerMarker *marker = [[NSRulerMarker alloc] initWithRulerView:rulerView markerLocation:self.time image:[NSImage imageNamed:@"TimeMarker.png"] imageOrigin:NSMakePoint(5.0f, 0.0f)]; 62 | marker.movable = YES; 63 | [rulerView addMarker:marker]; 64 | 65 | SMGuideView *guideView = [[SMGuideView alloc] initWithFrame:self.tableScrollView.contentView.bounds]; 66 | guideView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; 67 | [guideView bind:@"time" toObject:self withKeyPath:@"time" options:nil]; 68 | [self.tableScrollView.contentView addSubview:guideView]; 69 | self.guideView = guideView; 70 | 71 | [self.tableScrollView.contentView setPostsBoundsChangedNotifications:YES]; 72 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 73 | [center addObserver:self selector:@selector(boundsDidChange:) name:NSViewBoundsDidChangeNotification object:self.tableScrollView.contentView]; 74 | 75 | 76 | NSLog(@"Outline Scroll View: %p", self.outlineScrollView); 77 | NSLog(@"Table Scroll View: %p", self.tableScrollView); 78 | 79 | // [self.table sizeToFit]; 80 | 81 | NSMutableArray *elements = [NSMutableArray arrayWithCapacity:3]; 82 | 83 | SMElement *element1 = [[SMElement alloc] init]; 84 | element1.name = @"Element 1"; 85 | [elements addObject:element1]; 86 | 87 | SMTimeline *timeline1 = [[SMTimeline alloc] init]; 88 | timeline1.propertyName = @"X"; 89 | 90 | SMTimelineSegment *segment1 = [[SMTimelineSegment alloc] init]; 91 | segment1.position = 100; 92 | segment1.duration = 100; 93 | 94 | SMTimelineSegment *segment2 = [[SMTimelineSegment alloc] init]; 95 | segment2.position = 250; 96 | segment2.duration = 100; 97 | 98 | timeline1.segments = @[segment1, segment2]; 99 | 100 | SMTimeline *timeline2 = [[SMTimeline alloc] init]; 101 | timeline2.propertyName = @"Y"; 102 | 103 | SMTimelineSegment *segment3 = [[SMTimelineSegment alloc] init]; 104 | segment3.position = 100; 105 | segment3.duration = 100; 106 | 107 | SMTimelineSegment *segment4 = [[SMTimelineSegment alloc] init]; 108 | segment4.position = 250; 109 | segment4.duration = 100; 110 | 111 | timeline2.segments = @[segment3, segment4]; 112 | 113 | element1.timelines = @[timeline1, timeline2]; 114 | 115 | SMElement *element2 = [[SMElement alloc] init]; 116 | element2.name = @"Element 2"; 117 | [elements addObject:element2]; 118 | 119 | SMElement *element3 = [[SMElement alloc] init]; 120 | element3.name = @"Element 3"; 121 | [elements addObject:element3]; 122 | 123 | self.elements = elements; 124 | 125 | self.expandedElements = [NSMutableIndexSet indexSet]; 126 | 127 | [self.outline reloadData]; 128 | [self.table reloadData]; 129 | } 130 | 131 | #pragma mark - Private methods 132 | 133 | - (NSUInteger)numberOfRows { 134 | NSUInteger numberOfRows = 0; 135 | for (NSInteger elementIndex = [self.elements count] - 1; elementIndex >= 0; elementIndex--) { 136 | numberOfRows++; 137 | if ([self.expandedElements containsIndex:elementIndex]) { 138 | SMElement *element = [self.elements objectAtIndex:elementIndex]; 139 | numberOfRows += [element.timelines count]; 140 | } 141 | } 142 | return numberOfRows; 143 | } 144 | 145 | - (NSIndexPath *)indexPathForRow:(NSUInteger)rowIndex { 146 | NSUInteger elementIndex = 0; 147 | do { 148 | NSUInteger numberOfRows = 1; 149 | if ([self.expandedElements containsIndex:elementIndex]) { 150 | SMElement *element = [self.elements objectAtIndex:elementIndex]; 151 | numberOfRows += [element.timelines count]; 152 | } 153 | 154 | if (rowIndex < numberOfRows) { 155 | if (rowIndex == 0) { 156 | return [NSIndexPath indexPathWithIndex:elementIndex]; 157 | } else { 158 | NSUInteger indexes[] = {elementIndex, rowIndex - 1}; 159 | return [NSIndexPath indexPathWithIndexes:indexes length:2]; 160 | } 161 | } 162 | 163 | rowIndex -= numberOfRows; 164 | elementIndex ++; 165 | } while (YES); 166 | return nil; 167 | } 168 | 169 | - (NSUInteger)rowForIndexPath:(NSIndexPath *)indexPath { 170 | NSParameterAssert(indexPath.length == 1 || indexPath.length == 2); 171 | NSUInteger elementIndex = [indexPath indexAtPosition:0]; 172 | NSUInteger rowIndex = 0; 173 | for (NSUInteger actualElementIndex = 0; actualElementIndex < [self.elements count]; actualElementIndex++) { 174 | if (actualElementIndex == elementIndex) { 175 | if ([indexPath length] == 2) { 176 | return rowIndex + [indexPath indexAtPosition:1]; 177 | } else { 178 | return rowIndex; 179 | } 180 | } 181 | rowIndex++; 182 | if ([self.expandedElements containsIndex:actualElementIndex]) { 183 | SMElement *element = [self.elements objectAtIndex:actualElementIndex]; 184 | rowIndex += [element.timelines count]; 185 | } 186 | } 187 | return NSNotFound; 188 | } 189 | 190 | #pragma mark - Outline data source 191 | 192 | - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { 193 | if (!item) { 194 | return [self.elements count]; 195 | } 196 | if ([item isKindOfClass:[SMElement class]]) { 197 | SMElement *element = (SMElement *)item; 198 | return [element.timelines count]; 199 | } 200 | return 0; 201 | } 202 | 203 | - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item { 204 | if (!item) { 205 | return YES; 206 | } 207 | if ([item isKindOfClass:[SMElement class]]) { 208 | SMElement *element = (SMElement *)item; 209 | return [element.timelines count] > 0; 210 | } 211 | return NO; 212 | } 213 | 214 | 215 | - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item { 216 | if (!item) { 217 | return [self.elements objectAtIndex:index]; 218 | } 219 | if ([item isKindOfClass:[SMElement class]]) { 220 | SMElement *element = (SMElement *)item; 221 | return [element.timelines objectAtIndex:index]; 222 | // switch (index) { 223 | // case 0: 224 | // return SMElementPropertyX; 225 | // break; 226 | // case 1: 227 | // return SMElementPropertyY; 228 | // break; 229 | // case 2: 230 | // return SMElementPropertyWidth; 231 | // break; 232 | // case 3: 233 | // return SMElementPropertyHeight; 234 | // break; 235 | // 236 | // default: 237 | // break; 238 | // } 239 | } 240 | return nil; 241 | } 242 | 243 | - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { 244 | if ([item isKindOfClass:[SMElement class]]) { 245 | SMElement *element = item; 246 | return element.name; 247 | } 248 | if ([item isKindOfClass:[SMTimeline class]]) { 249 | SMTimeline *timeline = item; 250 | return timeline.propertyName; 251 | } 252 | // 253 | // if (item == SMElementPropertyX) { 254 | // return @"Property X"; 255 | // } 256 | // if (item == SMElementPropertyY) { 257 | // return @"Property Y"; 258 | // } 259 | // if (item == SMElementPropertyWidth) { 260 | // return @"Property Width"; 261 | // } 262 | // if (item == SMElementPropertyHeight) { 263 | // return @"Property Height"; 264 | // } 265 | return nil; 266 | } 267 | 268 | - (void)outlineViewItemDidExpand:(NSNotification *)notification { 269 | NSLog(@"Did expand: %@", notification); 270 | // [self.table reloadData]; 271 | SMElement *element = [notification.userInfo objectForKey:@"NSObject"]; 272 | NSUInteger elementIndex = [self.elements indexOfObject:element]; 273 | NSUInteger rowIndex = [self rowForIndexPath:[NSIndexPath indexPathWithIndex:elementIndex]]; 274 | 275 | [self.table beginUpdates]; 276 | 277 | [self.expandedElements addIndex:elementIndex]; 278 | 279 | [self.table insertRowsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(rowIndex + 1, 4)] withAnimation:NSTableViewAnimationSlideDown]; 280 | [self.table endUpdates]; 281 | } 282 | 283 | - (void)outlineViewItemDidCollapse:(NSNotification *)notification { 284 | NSLog(@"Did collapse: %@", notification); 285 | // [self.table reloadData]; 286 | SMElement *element = [notification.userInfo objectForKey:@"NSObject"]; 287 | NSUInteger elementIndex = [self.elements indexOfObject:element]; 288 | NSUInteger rowIndex = [self rowForIndexPath:[NSIndexPath indexPathWithIndex:elementIndex]]; 289 | 290 | [self.table beginUpdates]; 291 | 292 | [self.expandedElements removeIndex:elementIndex]; 293 | 294 | [self.table removeRowsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(rowIndex + 1, 4)] withAnimation:NSTableViewAnimationSlideUp]; 295 | [self.table endUpdates]; 296 | } 297 | 298 | - (void)outlineViewSelectionIsChanging:(NSNotification *)notification { 299 | NSLog(@"Selection Did change: %@", notification); 300 | NSIndexSet *selectedRowIndexes = self.outline.selectedRowIndexes; 301 | [self.table selectRowIndexes:selectedRowIndexes byExtendingSelection:NO]; 302 | } 303 | 304 | #pragma mark - Table data source 305 | 306 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { 307 | return [self numberOfRows]; 308 | } 309 | 310 | - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex { 311 | // // The column identifier string is the easiest way to identify a table column. Much easier 312 | // // than keeping a reference to the table column object. 313 | // NSString *columnIdentifer = [aTableColumn identifier]; 314 | // 315 | // // Get the name at the specified row in the namesArray 316 | // NSString *theName = [namesArray objectAtIndex:rowIndex]; 317 | // 318 | // 319 | // // Compare each column identifier and set the return value to 320 | // // the Person field value appropriate for the column. 321 | // if ([columnIdentifer isEqualToString:@"name"]) { 322 | // returnValue = theName; 323 | // } 324 | 325 | NSIndexPath *indexPath = [self indexPathForRow:rowIndex]; 326 | if ([indexPath length] == 1) { 327 | NSUInteger elementIndex = [indexPath indexAtPosition:0]; 328 | // SMElement *element = [self.elements objectAtIndex:elementIndex]; 329 | // return element.name; 330 | return nil; 331 | } 332 | if ([indexPath length] == 2) { 333 | NSUInteger elementIndex = [indexPath indexAtPosition:0]; 334 | NSUInteger propertyIndex = [indexPath indexAtPosition:1]; 335 | SMElement *element = [self.elements objectAtIndex:elementIndex]; 336 | return [element.timelines objectAtIndex:propertyIndex]; 337 | // switch (propertyIndex) { 338 | // case 0: 339 | // return @"X=1"; 340 | // break; 341 | // case 1: 342 | // return @"Y=2"; 343 | // break; 344 | // case 2: 345 | // return @"Width=3"; 346 | // break; 347 | // case 3: 348 | // return @"Height=4"; 349 | // break; 350 | // 351 | // default: 352 | // break; 353 | // } 354 | } 355 | 356 | return nil; 357 | } 358 | 359 | - (CGFloat)tableView:(NSTableView *)tableView sizeToFitWidthOfColumn:(NSInteger)column { 360 | NSLog(@"sizeToFitWidthOfColumn: %li", column); 361 | return 2000.0f; 362 | } 363 | 364 | - (void)tableViewSelectionIsChanging:(NSNotification *)notification { 365 | NSLog(@"Selection Did change: %@", notification); 366 | NSIndexSet *selectedRowIndexes = self.table.selectedRowIndexes; 367 | [self.outline selectRowIndexes:selectedRowIndexes byExtendingSelection:NO]; 368 | } 369 | 370 | #pragma mark - Scroll view 371 | 372 | - (void)startSynchronizing { 373 | { 374 | // don't retain the watched view, because we assume that it will 375 | // be retained by the view hierarchy for as long as we're around. 376 | NSScrollView *synchronizedScrollView = self.outlineScrollView; 377 | 378 | // get the content view of the 379 | NSClipView *synchronizedContentView = [synchronizedScrollView contentView]; 380 | 381 | // Make sure the watched view is sending bounds changed 382 | // notifications (which is probably does anyway, but calling 383 | // this again won't hurt). 384 | [synchronizedContentView setPostsBoundsChangedNotifications:YES]; 385 | 386 | // a register for those notifications on the synchronized content view. 387 | [[NSNotificationCenter defaultCenter] addObserver:self 388 | selector:@selector(synchronizedViewContentBoundsDidChange:) 389 | name:NSViewBoundsDidChangeNotification 390 | object:synchronizedContentView]; 391 | } 392 | { 393 | // don't retain the watched view, because we assume that it will 394 | // be retained by the view hierarchy for as long as we're around. 395 | NSScrollView *synchronizedScrollView = self.tableScrollView; 396 | 397 | // get the content view of the 398 | NSClipView *synchronizedContentView = [synchronizedScrollView contentView]; 399 | 400 | // Make sure the watched view is sending bounds changed 401 | // notifications (which is probably does anyway, but calling 402 | // this again won't hurt). 403 | [synchronizedContentView setPostsBoundsChangedNotifications:YES]; 404 | 405 | // a register for those notifications on the synchronized content view. 406 | [[NSNotificationCenter defaultCenter] addObserver:self 407 | selector:@selector(synchronizedViewContentBoundsDidChange:) 408 | name:NSViewBoundsDidChangeNotification 409 | object:synchronizedContentView]; 410 | } 411 | } 412 | 413 | - (void)synchronizedViewContentBoundsDidChange:(NSNotification *)notification 414 | { 415 | // get the changed content view from the notification 416 | NSClipView *changedContentView = [notification object]; 417 | NSScrollView *changedScrollView = [changedContentView.documentView enclosingScrollView]; 418 | 419 | NSScrollView *scrollView = nil; 420 | if (changedScrollView == self.outlineScrollView) { 421 | scrollView = self.tableScrollView; 422 | } else { 423 | scrollView = self.outlineScrollView; 424 | } 425 | NSClipView *contentView = [scrollView contentView]; 426 | 427 | // get the origin of the NSClipView of the scroll view that 428 | // we're watching 429 | NSPoint changedBoundsOrigin = [changedContentView documentVisibleRect].origin;; 430 | 431 | // get our current origin 432 | NSPoint curOffset = [contentView bounds].origin; 433 | NSPoint newOffset = curOffset; 434 | 435 | // scrolling is synchronized in the vertical plane 436 | // so only modify the y component of the offset 437 | newOffset.y = changedBoundsOrigin.y; 438 | 439 | // if our synced position is different from our current 440 | // position, reposition our content view 441 | if (!NSEqualPoints(curOffset, newOffset)) 442 | { 443 | // note that a scroll view watching this one will 444 | // get notified here 445 | [contentView scrollToPoint:newOffset]; 446 | // we have to tell the NSScrollView to update its 447 | // scrollers 448 | [scrollView reflectScrolledClipView:contentView]; 449 | } 450 | } 451 | 452 | - (void)stopSynchronizing { 453 | { 454 | NSClipView* synchronizedContentView = [self.outlineScrollView contentView]; 455 | 456 | // remove any existing notification registration 457 | [[NSNotificationCenter defaultCenter] removeObserver:self 458 | name:NSViewBoundsDidChangeNotification 459 | object:synchronizedContentView]; 460 | } 461 | { 462 | NSClipView* synchronizedContentView = [self.tableScrollView contentView]; 463 | 464 | // remove any existing notification registration 465 | [[NSNotificationCenter defaultCenter] removeObserver:self 466 | name:NSViewBoundsDidChangeNotification 467 | object:synchronizedContentView]; 468 | } 469 | } 470 | 471 | - (void)boundsDidChange:(NSNotification *)notification { 472 | if (notification.object != self.tableScrollView.contentView) { 473 | return; 474 | } 475 | // NSLog(@"bounds=%@", NSStringFromRect(self.tableScrollView.contentView.bounds)); 476 | self.guideView.frame = self.tableScrollView.contentView.bounds; 477 | } 478 | 479 | - (void)timelineTableView:(SMTimelineTableView *)view didChangeTime:(NSTimeInterval)time { 480 | self.time = time; 481 | NSLog(@"Change time to %f", self.time); 482 | } 483 | 484 | @end 485 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMDebugTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMDebugTableView.h 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMDebugTableView : NSTableView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMDebugTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMDebugTableView.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import "SMDebugTableView.h" 10 | 11 | @implementation SMDebugTableView 12 | 13 | - (void)mouseDown:(NSEvent *)event { 14 | NSLog(@"Stack: %@", [NSThread callStackSymbols]); 15 | [super mouseDown:event]; 16 | } 17 | 18 | - (void)selectRowIndexes:(NSIndexSet *)indexes byExtendingSelection:(BOOL)extend { 19 | NSLog(@"Stack: %@", [NSThread callStackSymbols]); 20 | [super selectRowIndexes:indexes byExtendingSelection:extend]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMElement.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMElement.h 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 26.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMElement : NSObject 12 | 13 | @property (copy, nonatomic) NSString *name; 14 | @property (strong, nonatomic) NSArray *timelines; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMElement.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMElement.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 26.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import "SMElement.h" 10 | 11 | @implementation SMElement 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMGuideView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMGuideView.h 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 01.10.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMGuideView : NSView 12 | 13 | @property (nonatomic) NSTimeInterval time; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMGuideView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMGuideView.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 01.10.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import "SMGuideView.h" 10 | 11 | @implementation SMGuideView 12 | 13 | static char SMObservationContext; 14 | 15 | - (id)initWithFrame:(NSRect)frame 16 | { 17 | self = [super initWithFrame:frame]; 18 | if (self) { 19 | // Initialization code here. 20 | [self addObserver:self forKeyPath:@"time" options:0 context:&SMObservationContext]; 21 | } 22 | 23 | return self; 24 | } 25 | 26 | - (void)drawRect:(NSRect)dirtyRect { 27 | // Drawing code here. 28 | // static NSColor *color = nil; 29 | // if (!color) { 30 | // color = [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:0.0f alpha:0.4f]; 31 | // } 32 | // [color set]; 33 | // NSRectFill(dirtyRect); 34 | 35 | CGFloat x = /*5.0f + 2.0f +*/ roundf(self.time - NSMinX(self.frame)); 36 | 37 | [[NSColor blackColor] set]; 38 | [NSBezierPath setDefaultLineWidth:0]; 39 | [NSBezierPath strokeLineFromPoint:NSMakePoint(x + 0.5f, NSMinY(self.bounds)) 40 | toPoint:NSMakePoint(x + 0.5f, NSMaxY(self.bounds))]; 41 | } 42 | 43 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 44 | if (context != &SMObservationContext) { 45 | return; 46 | } 47 | 48 | [self setNeedsDisplay:YES]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTestView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMTestView.h 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMTestView : NSView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTestView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMTestView.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import "SMTestView.h" 10 | 11 | @implementation SMTestView 12 | 13 | - (id)initWithFrame:(NSRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | // Initialization code here. 18 | } 19 | 20 | return self; 21 | } 22 | 23 | - (void)drawRect:(NSRect)dirtyRect 24 | { 25 | // Drawing code here. 26 | } 27 | 28 | - (void)mouseDown:(NSEvent *)event { 29 | NSLog(@"Mouse down"); 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimeline.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMTimeline.h 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMTimeline : NSObject 12 | 13 | @property (copy, nonatomic) NSString *propertyName; 14 | @property (strong, nonatomic) NSArray *segments; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimeline.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMTimeline.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import "SMTimeline.h" 10 | 11 | @implementation SMTimeline 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimelineEditor-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | de.stephanmichels.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2012 Stephan Michels. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimelineEditor-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SMTimelineEditor' target in the 'SMTimelineEditor' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimelineSegment.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMTimelineSegment.h 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMTimelineSegment : NSObject 12 | 13 | @property (nonatomic) NSTimeInterval position; 14 | @property (nonatomic) NSTimeInterval duration; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimelineSegment.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMTimelineSegment.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import "SMTimelineSegment.h" 10 | 11 | @implementation SMTimelineSegment 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimelineTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMTimelineTableView.h 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 28.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMTimelineTableView : NSTableView 12 | 13 | @end 14 | 15 | @interface NSObject (SMTimelineTableViewDelegate) 16 | 17 | - (void)timelineTableView:(SMTimelineTableView *)view didChangeTime:(NSTimeInterval)time; 18 | 19 | @end -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimelineTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMTimelineTableView.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 28.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import "SMTimelineTableView.h" 10 | #import "SMGuideView.h" 11 | 12 | @implementation SMTimelineTableView 13 | 14 | - (CGFloat)rulerView:(NSRulerView *)rulerView willMoveMarker:(NSRulerMarker *)marker toLocation:(CGFloat)location { 15 | NSLog(@"will move marker:%@ toLocation:%f", marker, location); 16 | 17 | location = MAX(location, rulerView.originOffset); 18 | 19 | NSTimeInterval time = location; 20 | [(id)self.delegate timelineTableView:self didChangeTime:time]; 21 | 22 | return location; 23 | } 24 | 25 | //- (void)rulerView:(NSRulerView *)rulerView didMoveMarker:(NSRulerMarker *)marker { 26 | // 27 | //} 28 | 29 | //- (void)awakeFromNib { 30 | // SMGuideView *guideView = [[SMGuideView alloc] initWithFrame:self.bounds]; 31 | // guideView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; 32 | // [self addSubview:guideView]; 33 | //} 34 | 35 | //- (void)drawRect:(NSRect)dirtyRect { 36 | // [super drawRect:dirtyRect]; 37 | // 38 | // // Drawing code here. 39 | // static NSColor *color = nil; 40 | // if (!color) { 41 | // color = [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:0.0f alpha:0.4f]; 42 | // } 43 | // [color set]; 44 | // NSRectFill(dirtyRect); 45 | //} 46 | 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimelineTrackView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMTimelineTrackView.h 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMTimelineTrackView : NSTableCellView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimelineTrackView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMTimelineTrackView.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import "SMTimelineTrackView.h" 10 | #import "SMTimeline.h" 11 | #import "SMTimelineSegment.h" 12 | #import "SMTimelineView.h" 13 | 14 | 15 | @interface SMTimelineTrackView () 16 | 17 | //@property (readonly) SMTimeline *timeline; 18 | @end 19 | 20 | @implementation SMTimelineTrackView 21 | 22 | //- (SMTimeline *)timeline { 23 | // return self.objectValue; 24 | //} 25 | // 26 | //+ (NSSet *)keyPathsForValuesAffectingTimeline { 27 | // return [NSSet setWithObjects:@"objectValue", nil]; 28 | //} 29 | 30 | - (id)initWithFrame:(NSRect)frame 31 | { 32 | self = [super initWithFrame:frame]; 33 | if (self) { 34 | // Initialization code here. 35 | } 36 | 37 | return self; 38 | } 39 | 40 | - (void)awakeFromNib { 41 | SMTimelineView *timelineView = [self.subviews objectAtIndex:0]; 42 | [timelineView bind:@"timeline" toObject:self withKeyPath:@"objectValue" options:nil]; 43 | } 44 | 45 | //- (void)drawRect:(NSRect)dirtyRect { 46 | // // Drawing code here. 47 | // NSLog(@"bounds: %@", NSStringFromRect(self.frame)); 48 | // 49 | // NSBezierPath *path = [NSBezierPath bezierPath]; 50 | // [path moveToPoint:NSMakePoint(0.0f, -5.0f)]; 51 | // [path lineToPoint:NSMakePoint(5.0f, 0.0f)]; 52 | // [path lineToPoint:NSMakePoint(0.0f, 5.0f)]; 53 | // [path lineToPoint:NSMakePoint(-5.0f, 0.0f)]; 54 | // [path closePath]; 55 | // path.lineWidth = 0; 56 | // 57 | // for (SMTimelineSegment *segment in self.timeline.segments) { 58 | // NSRect rect = NSInsetRect(self.bounds, 5.0f + 2.0f, 2.0f); 59 | // rect.origin.x += segment.position; 60 | // rect.size.width = segment.duration; 61 | // 62 | // { 63 | // [[NSColor redColor] setFill]; 64 | // NSRectFill(rect); 65 | // [[NSColor blackColor] set]; 66 | // NSFrameRect(rect); 67 | // } 68 | // 69 | // { 70 | // NSPoint point = NSMakePoint(NSMinX(rect), NSMidY(rect)); 71 | // [NSGraphicsContext saveGraphicsState]; 72 | // NSAffineTransform* transform = [NSAffineTransform transform]; 73 | // [transform translateXBy:point.x yBy:point.y]; 74 | // // [transform translateXBy:point.x + 0.5f yBy:point.y + 0.5f]; 75 | // [transform concat]; 76 | // 77 | // [[NSColor whiteColor] setFill]; 78 | // [path fill]; 79 | // [[NSColor blackColor] set]; 80 | // [path stroke]; 81 | // [NSGraphicsContext restoreGraphicsState]; 82 | // } 83 | // { 84 | // NSPoint point = NSMakePoint(NSMaxX(rect), NSMidY(rect)); 85 | // [NSGraphicsContext saveGraphicsState]; 86 | // NSAffineTransform* transform = [NSAffineTransform transform]; 87 | // [transform translateXBy:point.x yBy:point.y]; 88 | // // [transform translateXBy:point.x + 0.5f yBy:point.y + 0.5f]; 89 | // [transform concat]; 90 | // 91 | // [[NSColor whiteColor] setFill]; 92 | // [path fill]; 93 | // [[NSColor blackColor] set]; 94 | // [path stroke]; 95 | // [NSGraphicsContext restoreGraphicsState]; 96 | // } 97 | // } 98 | //} 99 | // 100 | //- (NSView *)hitTest:(NSPoint)point { 101 | // NSLog(@"hitTest: %@", NSStringFromPoint(point)); 102 | // NSView *result = [super hitTest:point]; 103 | // NSLog(@"Result: %@", result); 104 | // return result; 105 | //} 106 | // 107 | //- (void)mouseDown:(NSEvent *)event { 108 | // NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 109 | // SMTimelineSegment *selectedSegment = nil; 110 | // BOOL selectedPosition = YES; 111 | // NSRect bounds = self.bounds; 112 | // for (SMTimelineSegment *segment in self.timeline.segments) { 113 | // NSRect rect = NSMakeRect(2.0f + segment.position, 114 | // NSMidY(bounds) - 5.0f, 115 | // 10.0f, 116 | // 10.0f); 117 | // if (NSPointInRect(point, rect)) { 118 | // selectedSegment = segment; 119 | // selectedPosition = YES; 120 | // } 121 | // 122 | // rect = NSMakeRect(2.0f + segment.position + segment.duration, 123 | // NSMidY(bounds) - 5.0f, 124 | // 10.0f, 125 | // 10.0f); 126 | // if (NSPointInRect(point, rect)) { 127 | // selectedSegment = segment; 128 | // selectedPosition = NO; 129 | // } 130 | // } 131 | // //if (selectedBoxIndex == NSNotFound) return; 132 | // if (!selectedSegment) { 133 | // return; 134 | // } 135 | // 136 | // [[NSCursor closedHandCursor] set]; 137 | // while ([event type]!=NSLeftMouseUp) { 138 | // event = [[self window] nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)]; 139 | // NSPoint currentPoint = [self convertPoint:[event locationInWindow] fromView:nil]; 140 | // currentPoint.x = fminf(fmaxf(currentPoint.x, bounds.origin.x), bounds.size.width); 141 | // currentPoint.y = fminf(fmaxf(currentPoint.y, bounds.origin.y), bounds.size.height); 142 | // 143 | // CGFloat dx = currentPoint.x-point.x; 144 | // CGFloat dy = currentPoint.y-point.y; 145 | // 146 | // if (dx == 0.0f && dy == 0.0f) { 147 | // continue; 148 | // } 149 | // 150 | // if (selectedPosition) { 151 | // if (selectedSegment.position + dx >= 0 && selectedSegment.duration - dx >= 0) { 152 | // selectedSegment.position += dx; 153 | // selectedSegment.duration -= dy; 154 | // } 155 | // } else { 156 | // if (selectedSegment.duration + dx >= 0 && selectedSegment.position + selectedSegment.duration + dx < 2000) { 157 | // selectedSegment.duration += dy; 158 | // } 159 | // } 160 | // 161 | // point = currentPoint; 162 | // 163 | // [self setNeedsDisplay:YES]; 164 | // } 165 | // 166 | // [[NSCursor openHandCursor] set]; 167 | //} 168 | 169 | @end 170 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimelineView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMTimelineView.h 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class SMTimeline; 12 | 13 | @interface SMTimelineView : NSView 14 | 15 | @property (strong, nonatomic) SMTimeline *timeline; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /SMTimelineEditor/SMTimelineView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMTimelineView.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 27.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import "SMTimelineView.h" 10 | #import "SMTimeline.h" 11 | #import "SMTimelineSegment.h" 12 | 13 | 14 | @implementation SMTimelineView 15 | 16 | - (id)initWithFrame:(NSRect)frame 17 | { 18 | self = [super initWithFrame:frame]; 19 | if (self) { 20 | // Initialization code here. 21 | } 22 | 23 | return self; 24 | } 25 | 26 | - (void)drawRect:(NSRect)dirtyRect { 27 | // Drawing code here. 28 | // NSLog(@"bounds: %@", NSStringFromRect(self.frame)); 29 | 30 | NSBezierPath *path = [NSBezierPath bezierPath]; 31 | [path moveToPoint:NSMakePoint(0.0f, -5.5f)]; 32 | [path lineToPoint:NSMakePoint(5.5f, 0.0f)]; 33 | [path lineToPoint:NSMakePoint(0.0f, 5.5f)]; 34 | [path lineToPoint:NSMakePoint(-5.5f, 0.0f)]; 35 | [path closePath]; 36 | path.lineWidth = 0; 37 | 38 | for (SMTimelineSegment *segment in self.timeline.segments) { 39 | NSRect rect = NSInsetRect(self.bounds, 5.0f + 2.0f, 2.0f); 40 | rect.origin.x += segment.position; 41 | rect.size.width = segment.duration; 42 | 43 | if (segment.duration > 0) { 44 | [[NSColor redColor] setFill]; 45 | NSRectFill(rect); 46 | [[NSColor blackColor] set]; 47 | NSFrameRect(rect); 48 | } 49 | 50 | { 51 | NSPoint point = NSMakePoint(NSMinX(rect), NSMidY(rect)); 52 | [NSGraphicsContext saveGraphicsState]; 53 | NSAffineTransform* transform = [NSAffineTransform transform]; 54 | // [transform translateXBy:point.x yBy:point.y]; 55 | [transform translateXBy:point.x - 0.5f yBy:point.y/* + 0.5f*/]; 56 | [transform concat]; 57 | 58 | [[NSColor whiteColor] setFill]; 59 | [path fill]; 60 | [[NSColor blackColor] set]; 61 | [path stroke]; 62 | [NSGraphicsContext restoreGraphicsState]; 63 | } 64 | if (segment.duration > 0) { 65 | NSPoint point = NSMakePoint(NSMaxX(rect), NSMidY(rect)); 66 | [NSGraphicsContext saveGraphicsState]; 67 | NSAffineTransform* transform = [NSAffineTransform transform]; 68 | // [transform translateXBy:point.x yBy:point.y]; 69 | [transform translateXBy:point.x - 0.5f yBy:point.y/* + 0.5f*/]; 70 | [transform concat]; 71 | 72 | [[NSColor whiteColor] setFill]; 73 | [path fill]; 74 | [[NSColor blackColor] set]; 75 | [path stroke]; 76 | [NSGraphicsContext restoreGraphicsState]; 77 | } 78 | } 79 | 80 | // [[NSColor redColor] set]; 81 | // [NSBezierPath strokeLineFromPoint:NSMakePoint(100.0f, NSMinY(self.bounds)) 82 | // toPoint:NSMakePoint(100.0f, NSMaxY(self.bounds))]; 83 | // NSLog(@"frame=%@ superView=%@", NSStringFromRect(self.frame), NSStringFromRect(self.superview.frame)); 84 | } 85 | 86 | typedef enum { 87 | SMHandleSegment = 0, 88 | SMHandleSegmentStart, 89 | SMHandleSegmentEnd, 90 | } SMHandle; 91 | 92 | - (void)mouseDown:(NSEvent *)event { 93 | NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 94 | SMTimelineSegment *selectedSegment = nil; 95 | NSUInteger selectedSegmentIndex = NSNotFound; 96 | SMHandle selectedHandle = SMHandleSegment; 97 | NSRect bounds = self.bounds; 98 | NSPoint handleOffset = NSZeroPoint; 99 | 100 | NSArray *segments = self.timeline.segments; 101 | 102 | NSUInteger segmentIndex = 0; 103 | for (SMTimelineSegment *segment in segments) { 104 | NSRect rect = NSMakeRect(2.0f + segment.position, 105 | NSMidY(bounds) - 5.0f, 106 | 10.0f, 107 | 10.0f); 108 | if (NSPointInRect(point, rect)) { 109 | selectedSegment = segment; 110 | selectedSegmentIndex = segmentIndex; 111 | selectedHandle = SMHandleSegmentStart; 112 | handleOffset = NSMakePoint(point.x - (segment.position + 2.0f + 5.0f), NSMidY(bounds)); 113 | break; 114 | } 115 | 116 | rect = NSMakeRect(2.0f + segment.position + segment.duration, 117 | NSMidY(bounds) - 5.0f, 118 | 10.0f, 119 | 10.0f); 120 | if (NSPointInRect(point, rect)) { 121 | selectedSegment = segment; 122 | selectedSegmentIndex = segmentIndex; 123 | selectedHandle = SMHandleSegmentEnd; 124 | handleOffset = NSMakePoint(point.x - (segment.position + segment.duration + 2.0f + 5.0f), NSMidY(bounds)); 125 | break; 126 | } 127 | 128 | rect = NSMakeRect(2.0f + 5.0f + segment.position, 129 | NSMidY(bounds) - 5.0f, 130 | segment.duration, 131 | 10.0f); 132 | if (NSPointInRect(point, rect)) { 133 | selectedSegment = segment; 134 | selectedSegmentIndex = segmentIndex; 135 | selectedHandle = SMHandleSegment; 136 | handleOffset = NSMakePoint(point.x - (segment.position + 2.0f + 5.0f), NSMidY(bounds)); 137 | break; 138 | } 139 | 140 | segmentIndex++; 141 | } 142 | //if (selectedBoxIndex == NSNotFound) return; 143 | if (!selectedSegment) { 144 | return; 145 | } 146 | 147 | NSLog(@"selectedHandle=%i handleOffset=%@", selectedHandle, NSStringFromPoint(handleOffset)); 148 | 149 | SMTimelineSegment *previousSegment = selectedSegmentIndex > 0 ? [segments objectAtIndex:selectedSegmentIndex - 1] : nil; 150 | SMTimelineSegment *nextSegment = selectedSegmentIndex + 1 < [segments count] ? [segments objectAtIndex:selectedSegmentIndex + 1] : nil; 151 | 152 | [[NSCursor closedHandCursor] set]; 153 | while ([event type]!=NSLeftMouseUp) { 154 | event = [[self window] nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)]; 155 | NSPoint currentPoint = [self convertPoint:[event locationInWindow] fromView:nil]; 156 | currentPoint.x = fminf(fmaxf(currentPoint.x, bounds.origin.x), bounds.size.width); 157 | currentPoint.y = fminf(fmaxf(currentPoint.y, bounds.origin.y), bounds.size.height); 158 | 159 | CGFloat dx = currentPoint.x - point.x; 160 | CGFloat dy = currentPoint.y - point.y; 161 | 162 | if (dx == 0.0f && dy == 0.0f) { 163 | continue; 164 | } 165 | 166 | 167 | CGFloat location = currentPoint.x - 2.0f - 5.0f - handleOffset.x; 168 | if (selectedHandle == SMHandleSegmentStart) { 169 | // greater or equal the zero 170 | location = MAX(0, location); 171 | // lesser or equal than the end of the segment 172 | location = MIN(selectedSegment.position + selectedSegment.duration, location); 173 | // greater or equal than the end of the previous segment 174 | if (previousSegment) { 175 | location = MAX(previousSegment.position + previousSegment.duration, location); 176 | } 177 | 178 | selectedSegment.duration = selectedSegment.position + selectedSegment.duration - location; 179 | selectedSegment.position = location; 180 | } else if (selectedHandle == SMHandleSegmentEnd) { 181 | // greather or equal than the segment start 182 | location = MAX(selectedSegment.position, location); 183 | // lesser or egaul than time end 184 | location = MIN(2000, location); 185 | // lesser than the start of the next segment 186 | if (nextSegment) { 187 | location = MIN(nextSegment.position, location); 188 | } 189 | 190 | selectedSegment.duration = location - selectedSegment.position; 191 | } else if (selectedHandle == SMHandleSegment) { 192 | // greater or equal the zero 193 | location = MAX(0, location); 194 | // lesser or egaul than time end 195 | location = MIN(2000 - selectedSegment.duration, location); 196 | if (previousSegment) { 197 | location = MAX(previousSegment.position + previousSegment.duration, location); 198 | } 199 | if (nextSegment) { 200 | location = MIN(nextSegment.position - selectedSegment.duration, location); 201 | } 202 | 203 | selectedSegment.position = location; 204 | } 205 | 206 | point = currentPoint; 207 | 208 | [self setNeedsDisplay:YES]; 209 | } 210 | 211 | [[NSCursor openHandCursor] set]; 212 | } 213 | 214 | @end 215 | -------------------------------------------------------------------------------- /SMTimelineEditor/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /SMTimelineEditor/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /SMTimelineEditor/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SMTimelineEditor 4 | // 5 | // Created by Stephan Michels on 26.09.12. 6 | // Copyright (c) 2012 Stephan Michels. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **)argv); 14 | } 15 | --------------------------------------------------------------------------------