Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

- Consolidated the new row spanning table view and the sticky row tab…

…le view into NoodleTableView. Broke out NSTableView category into its own file.

- Added a delegate method to override whether a cell for a specific row/column is to be included in the sticky row header.
- Added iToonz example to demonstrate new row spanning feature. Also makes use of new delegate method.
- Added NSIndexSet category which provides a simple enumerator.
  • Loading branch information...
commit 5010dc5353ba38c545a04c24fb8e799cb36468c8 1 parent c001496
@MrNoodle authored
View
2  Examples/StickyRowTableView Revue/Controller.m
@@ -27,6 +27,7 @@
//
#import "Controller.h"
+#import "NoodleTableView.h"
@implementation Controller
@@ -65,6 +66,7 @@ - (void)awakeFromNib
}
}
+ [_stickyRowTableView setShowsStickyRowHeader:YES];
[_stickyRowTableView reloadData];
[_iPhoneTableView reloadData];
}
View
48 Examples/StickyRowTableView Revue/English.lproj/MainMenu.xib
@@ -3,18 +3,18 @@
<data>
<int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">10B504</string>
- <string key="IBDocument.InterfaceBuilderVersion">732</string>
+ <string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.2</string>
<string key="IBDocument.HIToolboxVersion">437.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="NS.object.0">732</string>
+ <string key="NS.object.0">740</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
- <integer value="57"/>
<integer value="528"/>
<integer value="450"/>
+ <integer value="57"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -3437,7 +3437,7 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string>NoodleStickyRowTableView</string>
+ <string>NoodleTableView</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -3608,6 +3608,18 @@
</object>
</object>
<object class="IBPartialClassDescription">
+ <string key="className">Controller</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">_tableView</string>
+ <string key="NS.object.0">NoodleTableView</string>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">Examples/iToonz/Controller.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
@@ -3616,6 +3628,13 @@
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="732683367">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">NSTableView-NoodleExtensions.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="204379972">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">NoodleStickyRowTableView.h</string>
@@ -3642,6 +3661,18 @@
</object>
</object>
<object class="IBPartialClassDescription">
+ <string key="className">NSTableColumn</string>
+ <reference key="sourceIdentifier" ref="732683367"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSTableView</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">scrollToStickyRow:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <reference key="sourceIdentifier" ref="732683367"/>
+ </object>
+ <object class="IBPartialClassDescription">
<string key="className">NSTableView</string>
<object class="NSMutableDictionary" key="actions">
<string key="NS.key.0">scrollToStickyRow:</string>
@@ -3682,9 +3713,12 @@
</object>
</object>
<object class="IBPartialClassDescription">
- <string key="className">NoodleStickyRowTableView</string>
+ <string key="className">NoodleTableView</string>
<string key="superclassName">NSTableView</string>
- <reference key="sourceIdentifier" ref="204379972"/>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">NoodleTableView.h</string>
+ </object>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
@@ -4239,7 +4273,7 @@
<integer value="3100" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
- <string key="IBDocument.LastKnownRelativeProjectPath">../NoodleStickyRowTableViewTest.xcodeproj</string>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../../../NoodleKit.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
</data>
</archive>
View
11 Examples/StickyRowTableView Revue/Read Me.rtf
@@ -15,9 +15,9 @@
version 0.18369\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\cf0 \
-This is a sample project and test harness for NoodleStickyRowTableView.\
+This is a sample project and test harness for the sticky row header feature of NoodleTableView.\
\
-NoodleStickyRowTableView is an NSTableView subclass where group rows (this can be overridden) will "stick" to the top of the view when they scroll off. This is like how section headers will stick to the top in UITableViews on the iPhone.\
+NoodleTableView is an NSTableView subclass where group rows (this can be overridden) will "stick" to the top of the view when they scroll off. This is like how section headers will stick to the top in UITableViews on the iPhone.\
\
Almost all of it is accomplished via an NSTableView category. The reason it was done this way was so that (a) you could integrate it into your own NSTableView subclass easily and (b) you could gain the functionality in NSOutlineView and its subclasses as well. To get the basic functionality, all you need to do is subclass
\f1 -drawRect:
@@ -25,13 +25,16 @@ Almost all of it is accomplished via an NSTableView category. The reason it was
\f1 -drawStickyRowHeader
\f0 after your call to
\f1 super
-\f0 . Look at the very minimal NoodleStickyRowTableView class for details.\
+\f0 . Look at the NoodleTableView class for details.\
\
This project also includes NoodleIPhoneTableView which overrides a bit more to simulate the look and feel of UITableView.\
\
A blog post on this can be found at: {\field{\*\fldinst{HYPERLINK "http://www.noodlesoft.com/blog/2009/09/25/sticky-section-headers-in-nstableview"}}{\fldrslt http://www.noodlesoft.com/blog/2009/09/25/sticky-section-headers-in-nstableview}}\
\
-Notes:\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+
+\b \cf0 Notes
+\b0 \
\
\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
\ls1\ilvl0\cf0 {\listtext \'95 }As noted in the article linked above, limitations in being able to get a visual cache of a group row forced me to compromise and use a custom look for the sticky rows in the default implementation. As shown in the NoodleIPhoneTableView class, you can override all the drawing but it requires a bit more work.\
View
42 Examples/iToonz/Controller.h
@@ -0,0 +1,42 @@
+//
+// Controller.h
+// NoodleKit
+//
+// Created by Paul Kim on 10/21/09.
+// Copyright 2009 Noodlesoft, LLC. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+#import <Cocoa/Cocoa.h>
+
+@class NoodleTableView;
+
+@interface Controller : NSObject
+{
+ IBOutlet NoodleTableView *_tableView;
+ NSArray *_entries;
+ id _number;
+}
+
+@property (readonly) NSArray *entries;
+
+@end
View
162 Examples/iToonz/Controller.m
@@ -0,0 +1,162 @@
+//
+// Controller.m
+// NoodleKit
+//
+// Created by Paul Kim on 10/21/09.
+// Copyright 2009 Noodlesoft, LLC. All rights reserved.
+//
+// Created by Paul Kim on 10/20/09.
+// Copyright 2009 Noodlesoft, LLC. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+#import "Controller.h"
+#import "NoodleTableView.h"
+
+#define ARTIST_KEY @"artist"
+#define ARTWORK_KEY @"artwork"
+#define ALBUM_KEY @"album"
+#define SONGCOUNT_KEY @"songCount"
+
+@implementation Controller
+
+
+//@synthesize window;
+@synthesize entries = _entries;
+
+- (void)awakeFromNib
+{
+ [_tableView setIntercellSpacing:NSMakeSize(0.0, 0.0)];
+ [_tableView setShowsStickyRowHeader:YES];
+ [_tableView setRowSpanningEnabledForCapableColumns:YES];
+ _number = [NSNumber numberWithInt:5];
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
+{
+ _entries = [[NSArray alloc] initWithObjects:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ @"Mr Disco", ARTIST_KEY,
+ [NSImage imageNamed:NSImageNameFolderBurnable], ARTWORK_KEY,
+ @"Burn Baby Burn", ALBUM_KEY,
+ [NSNumber numberWithInteger:9], SONGCOUNT_KEY, nil],
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ @"Pierre LeMac", ARTIST_KEY,
+ [NSImage imageNamed:NSImageNameBonjour], ARTWORK_KEY,
+ @"Bonjour Ma Cherie", ALBUM_KEY,
+ [NSNumber numberWithInteger:13], SONGCOUNT_KEY, nil],
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ @"M.C. Mac", ARTIST_KEY,
+ [NSImage imageNamed:NSImageNameDotMac], ARTWORK_KEY,
+ @"Dot Mackin'", ALBUM_KEY,
+ [NSNumber numberWithInteger:7], SONGCOUNT_KEY, nil],
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ @"M.C. Mac", ARTIST_KEY,
+ [NSImage imageNamed:NSImageNameFolderSmart], ARTWORK_KEY,
+ @"You Think You're So Smart", ALBUM_KEY,
+ [NSNumber numberWithInteger:14], SONGCOUNT_KEY, nil],
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ @"ComputerHead", ARTIST_KEY,
+ [NSImage imageNamed:NSImageNameComputer], ARTWORK_KEY,
+ @"Cancel Computer", ALBUM_KEY,
+ [NSNumber numberWithInteger:12], SONGCOUNT_KEY, nil],
+ nil];
+
+ [(NoodleTableColumn *)[_tableView tableColumnWithIdentifier:@"Album"] setRowSpanningEnabled:NO];
+ [_tableView reloadData];
+}
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
+{
+ return [[_entries valueForKeyPath:@"@sum.songCount"] integerValue];
+}
+
+- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
+{
+ NSInteger tally, songCount;
+ id identifier;
+
+ tally = 0;
+ identifier = [aTableColumn identifier];
+ for (NSDictionary *dict in _entries)
+ {
+ songCount = [[dict objectForKey:SONGCOUNT_KEY] integerValue];
+
+ if (rowIndex < tally + songCount)
+ {
+ if ([identifier isEqual:@"Artwork"])
+ {
+ return [dict objectForKey:ARTWORK_KEY];
+ }
+ else if ([identifier isEqual:@"Album"])
+ {
+ return [dict objectForKey:ALBUM_KEY];
+ }
+ else if ([identifier isEqual:@"Artist"])
+ {
+ return [dict objectForKey:ARTIST_KEY];
+ }
+ else if ([identifier isEqual:@"Song"])
+ {
+ return [NSString stringWithFormat:@"Song #%d", rowIndex - tally + 1];
+ }
+ }
+ tally += songCount;
+ }
+ return nil;
+}
+
+- (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn
+{
+ if ([[tableColumn identifier] isEqual:@"Album"])
+ {
+ [(NoodleTableColumn *)tableColumn setRowSpanningEnabled:![(NoodleTableColumn *)tableColumn isRowSpanningEnabled]];
+ [_tableView reloadData];
+ }
+}
+
+- (BOOL)tableView:(NSTableView *)tableView isStickyRow:(NSInteger)row
+{
+ id value, newValue;
+ NSTableColumn *column;
+
+ column = [tableView tableColumnWithIdentifier:@"Artist"];
+ value = [self tableView:tableView objectValueForTableColumn:column row:row];
+
+ if (row > 0)
+ {
+ newValue = [self tableView:tableView objectValueForTableColumn:column row:row - 1];
+ if (![value isEqual:newValue])
+ {
+ return YES;
+ }
+ return NO;
+ }
+ return YES;
+}
+
+- (BOOL)tableView:(NSTableView *)tableView shouldDisplayCellInStickyRowHeaderForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
+{
+ return [[tableColumn identifier] isEqual:@"Artist"];
+}
+
+@end
View
1  Examples/iToonz/Credits.rtf
View
4,813 Examples/iToonz/English.lproj/MainMenu.xib
4,813 additions, 0 deletions not shown
View
128 Examples/iToonz/Read Me.rtf
@@ -0,0 +1,128 @@
+{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf110
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Monaco;}
+{\colortbl;\red255\green255\blue255;}
+{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid1}
+{\list\listtemplateid2\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid101\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid2}
+{\list\listtemplateid3\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid201\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid3}
+{\list\listtemplateid4\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid301\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid4}
+{\list\listtemplateid5\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid401\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid5}
+{\list\listtemplateid6\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid501\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid6}
+{\list\listtemplateid7\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid601\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid7}}
+{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}{\listoverride\listid3\listoverridecount0\ls3}{\listoverride\listid4\listoverridecount0\ls4}{\listoverride\listid5\listoverridecount0\ls5}{\listoverride\listid6\listoverridecount0\ls6}{\listoverride\listid7\listoverridecount0\ls7}}
+\margl1440\margr1440\vieww19360\viewh17460\viewkind0
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\b\fs24 \cf0 iToonz
+\b0 \
+version 0.68\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+\cf0 \
+This is a sample project and test harness for the row spanning feature of NoodleTableView.\
+\
+The row spanning feature allows a tableview to have cells span multiple rows. An example of this can be seen in iTune's "Artwork" column.\
+\
+This all started from a blog post by Jesper here: {\field{\*\fldinst{HYPERLINK "http://waffle.wootest.net/2009/10/04/artwork-column-in-cocoa/"}}{\fldrslt http://waffle.wootest.net/2009/10/04/artwork-column-in-cocoa/}}\
+\
+This was followed by Jacob Xiao's post here: {\field{\*\fldinst{HYPERLINK "http://likethought.com/lockfocus/2009/10/another-way-to-mimic-the-artwork-column-in-cocoa/"}}{\fldrslt http://likethought.com/lockfocus/2009/10/another-way-to-mimic-the-artwork-column-in-cocoa/}}\
+\
+These posts inspired me to implement a generalized version that should work with any type of cell. It was Jacob's approach that I ended up using in my version. Thanks to both Jesper and Jacob for the inspiration for this.\
+\
+You can view my post here: {\field{\*\fldinst{HYPERLINK "http://www.noodlesoft.com/blog/2009/10/20/yet-another-way-to-mimic-the-artwork-column-in-cocoa"}}{\fldrslt http://www.noodlesoft.com/blog/2009/10/20/yet-another-way-to-mimic-the-artwork-column-in-cocoa}}\
+\
+Note that the original class in the blog post has been merged with the NoodleStickyRowTableView into NoodleTableView. As a result, both features can be used concurrently. This project also demonstrates how to specify via a delegate method which indicates which cells will be displayed in the sticky row header.\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+
+\b \cf0 Notes
+\b0 \
+\
+\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
+\ls1\ilvl0\cf0 {\listtext \'95 }As mentioned above, you can use any NSCell subclass.\
+{\listtext \'95 }"Spans" are determined by contiguous ranges of rows with the same object value for a particular column. It is assumed that the same object value will result in the same visual output for a particular column cell.\
+{\listtext \'95 }Internally, a special cell is used. It renders the full cell and then draws out each row's slice into the tableview. This also allows for an optimization to only do the full render once instead of for every row slice.\
+{\listtext \'95 }If the first column is set to span rows, horizontal grid lines will only be drawn for the last row in a span.\
+{\listtext \'95 }Span cells will not highlight.\
+{\listtext \'95 }Clicking on span cells will do nothing.\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+\cf0 \
+To integrate this functionality, first off, you need to use NoodleTableView. For any column that you want to have row spanning, make that column be an instance of NoodleTableColumn. You can then either set the rowSpanningEnabled property or call -setRowSpanningEnabledForCapableColumns: which will enable it for every NoodleTableColumn in the tableview.\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+
+\b \cf0 Possible Improvements\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+
+\b0 \cf0 \
+\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
+\ls2\ilvl0\cf0 {\listtext \'95 }Provide delegate hooks for determining the range of a span.\
+{\listtext \'95 }Have mouse clicks on span cells select the first row in the span. This is how iTunes works though it only does it if you click the artwork itself, not the cell in general. Possibly something left up to the application to implement.\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+\cf0 \
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+
+\b \cf0 Contact
+\b0 \
+\
+Just go {\field{\*\fldinst{HYPERLINK "http://www.noodlesoft.com/about.php"}}{\fldrslt www.noodlesoft.com}} and shoot me an email. Or visit the blog article linked above and leave a comment. Bugs, suggestions and other feedback appreciated.\
+\
+\
+
+\b License
+\b0 \
+\
+I am releasing this under the MIT license.\
+\
+____________________________________\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+
+\f1 \cf0 Copyright (c) 2009 Noodlesoft, LLC. All Rights Reserved.\
+\
+Permission is hereby granted, free of charge, to any person\
+obtaining a copy of this software and associated documentation\
+files (the "Software"), to deal in the Software without\
+restriction, including without limitation the rights to use,\
+copy, modify, merge, publish, distribute, sublicense, and/or sell\
+copies of the Software, and to permit persons to whom the\
+Software is furnished to do so, subject to the following\
+conditions:\
+\
+The above copyright notice and this permission notice shall be\
+included in all copies or substantial portions of the Software.\
+\
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\
+OTHER DEALINGS IN THE SOFTWARE.\
+\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
+\ls3\ilvl0
+\f0\b \cf0 \
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+\cf0 \
+Changelog
+\b0 \
+\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
+\ls4\ilvl0\cf0 \
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+\cf0 0.68:\
+\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
+\ls5\ilvl0\cf0 {\listtext \'95 }Fixed crash when clicking on a non-span cell and dragging to a span cell.\
+{\listtext \'95 }Fixed highlighting behavior. Span cells should no longer highlight.\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+\cf0 \
+0.37:\
+\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
+\ls6\ilvl0\cf0 {\listtext \'95 }Fixed drawing loop causing high CPU usage.\
+{\listtext \'95 }Revamped grid code.\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+\cf0 \
+0.04:\
+\pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
+\ls7\ilvl0\cf0 {\listtext \'95 }Initial public release\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
+\cf0 \
+}
View
28 Examples/iToonz/iToonz-Info.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
View
14 Examples/iToonz/main.m
@@ -0,0 +1,14 @@
+//
+// main.m
+// NoodleStickyRowTableViewTest
+//
+// Created by Paul Kim on 8/23/09.
+// Copyright Noodlesoft, LLC 2009. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, char *argv[])
+{
+ return NSApplicationMain(argc, (const char **) argv);
+}
View
49 NSIndexSet-NoodleExtensions.h
@@ -0,0 +1,49 @@
+//
+// NSIndexSet-NoodleExtensions.h
+// NoodleRowSpanningTableViewTest
+//
+// Created by Paul Kim on 10/20/09.
+// Copyright 2009 Noodlesoft, LLC. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+#import <Cocoa/Cocoa.h>
+
+@interface NoodleIndexSetEnumerator : NSObject
+{
+ NSUInteger *_indexes;
+ NSUInteger _count;
+ NSUInteger _currentIndex;
+}
+
+// Returns NSNotFound when there are no more indexes.
+- (NSUInteger)nextIndex;
+
+@end
+
+
+@interface NSIndexSet (NoodleExtensions)
+
+- (NoodleIndexSetEnumerator *)indexEnumerator;
+
+@end
+
View
96 NSIndexSet-NoodleExtensions.m
@@ -0,0 +1,96 @@
+//
+// NSIndexSet-NoodleExtensions.m
+// NoodleRowSpanningTableViewTest
+//
+// Created by Paul Kim on 10/20/09.
+// Copyright 2009 Noodlesoft, LLC. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+#import "NSIndexSet-NoodleExtensions.h"
+
+@interface NoodleIndexSetEnumerator ()
+
++ enumeratorWithIndexSet:(NSIndexSet *)set;
+- initWithIndexSet:(NSIndexSet *)set;
+
+@end
+
+@implementation NoodleIndexSetEnumerator
+
++ enumeratorWithIndexSet:(NSIndexSet *)set
+{
+ return [[[[self class] alloc] initWithIndexSet:set] autorelease];
+}
+
+- initWithIndexSet:(NSIndexSet *)set
+{
+ if ((self = [super init]) != nil)
+ {
+ _currentIndex = 0;
+ _count = [set count];
+ _indexes = (NSUInteger *)malloc(sizeof(NSUInteger) * _count);
+
+ [set getIndexes:_indexes maxCount:_count inIndexRange:nil];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ free(_indexes);
+ _indexes = NULL;
+ [super dealloc];
+}
+
+- (void)finalize
+{
+ free(_indexes);
+ [super finalize];
+}
+
+- (NSUInteger)nextIndex
+{
+ if (_currentIndex < _count)
+ {
+ NSUInteger i;
+
+ i = _indexes[_currentIndex];
+ _currentIndex++;
+
+ return i;
+ }
+ return NSNotFound;
+}
+
+@end
+
+@implementation NSIndexSet (NoodleExtensions)
+
+- (NoodleIndexSetEnumerator *)indexEnumerator
+{
+ return [NoodleIndexSetEnumerator enumeratorWithIndexSet:self];
+}
+
+@end
+
+
View
88 NoodleStickyRowTableView.h → NSTableView-NoodleExtensions.h
@@ -1,40 +1,13 @@
//
-// NoodleStickyRowTableView.h
+// NSTableView-NoodleExtensions.h
// NoodleKit
//
-// Created by Paul Kim on 8/21/09.
+// Created by Paul Kim on 10/22/09.
// Copyright 2009 Noodlesoft, LLC. All rights reserved.
//
-// Permission is hereby granted, free of charge, to any person
-// obtaining a copy of this software and associated documentation
-// files (the "Software"), to deal in the Software without
-// restriction, including without limitation the rights to use,
-// copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following
-// conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-// OTHER DEALINGS IN THE SOFTWARE.
-//
#import <Cocoa/Cocoa.h>
-/*
- Categories and subclasses that provide "sticky" header rows.
-
- For more details, see the related blog post at http://www.noodlesoft.com/blog/2009/09/25/sticky-section-headers-in-nstableview/
- */
-
typedef NSUInteger NoodleStickyRowTransition;
enum
@@ -43,24 +16,11 @@ enum
NoodleStickyRowTransitionFadeIn
};
-@interface NoodleStickyRowTableView : NSTableView
-{
-}
-@end
+@interface NSTableView (NoodleExtensions)
-@interface NoodleStickyRowOutlineView : NSOutlineView
-{
-}
-
-@end
-
-/*
- The bulk of this is implemented in categories. This is so (a) it can be easily
- integrated into your own subclass and (b) it can be easily used in subclasses
- of NSOutlineView without copying and pasting large chunks of code.
- */
-@interface NSTableView (NoodleStickyRowExtensions)
+#pragma mark Sticky Row Header methods
+// Note: see NoodleTableView's -drawRect on how to hook in this functionality in a subclass
/*
Currently set to any groups rows (as dictated by the delegate). The
@@ -101,10 +61,37 @@ enum
*/
- (NoodleStickyRowTransition)stickyRowHeaderTransition;
+#pragma mark Row Spanning methods
+
+/*
+ Returns the range of the span at the given column and row indexes. The span is determined by
+ a range of contiguous rows having the same object value.
+ */
+- (NSRange)rangeOfRowSpanAtColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex;
+
@end
-@interface NSOutlineView (NoodleStickyRowExtensions)
+@class NoodleRowSpanningCell;
+@interface NSTableColumn (NoodleExtensions)
+
+#pragma mark Row Spanning methods
+/*
+ Returns whether this column will try to consolidate rows into spans.
+ */
+- (BOOL)isRowSpanningEnabled;
+
+/*
+ Returns the cell used to draw the spanning regions. Default implementation returns nil.
+ */
+- (NoodleRowSpanningCell *)spanningCell;
+
+@end
+
+
+@interface NSOutlineView (NoodleExtensions)
+
+#pragma mark Sticky Row Header methods
/*
Currently set to any groups rows (or as dictated by the delegate). The
delegate can implement -outlineView:isStickyRow: to override this.
@@ -113,13 +100,19 @@ enum
@end
+
@interface NSObject (NoodleStickyRowDelegate)
/*
Allows the delegate to specify if a row is sticky. By default, group rows
are sticky. The delegate can override that by implementing this method.
*/
-- (BOOL)tableView:(NSTableView *)tableView isStickyRow:(NSInteger)row;
+- (BOOL)tableView:(NSTableView *)tableView isStickyRow:(NSInteger)rowIndex;
+
+/*
+ Allows the delegate to specify whether a certain cell should be drawn in the sticky row header
+ */
+- (BOOL)tableView:(NSTableView *)tableView shouldDisplayCellInStickyRowHeaderForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex;
/*
Same as above but for outline views.
@@ -127,3 +120,4 @@ enum
- (BOOL)outlineView:(NSOutlineView *)outlineView isStickyItem:(id)item;
@end
+
View
164 NoodleStickyRowTableView.m → NSTableView-NoodleExtensions.m
@@ -1,33 +1,12 @@
//
-// NoodlyTableView.m
+// NSTableView-NoodleExtensions.m
// NoodleKit
//
-// Created by Paul Kim on 8/21/09.
+// Created by Paul Kim on 10/22/09.
// Copyright 2009 Noodlesoft, LLC. All rights reserved.
//
-// Permission is hereby granted, free of charge, to any person
-// obtaining a copy of this software and associated documentation
-// files (the "Software"), to deal in the Software without
-// restriction, including without limitation the rights to use,
-// copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following
-// conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-// OTHER DEALINGS IN THE SOFTWARE.
-//
-#import "NoodleStickyRowTableView.h"
+#import "NSTableView-NoodleExtensions.h"
#import "NSImage-NoodleExtensions.h"
#define NOODLE_STICKY_ROW_VIEW_TAG 233931134
@@ -38,7 +17,9 @@ void NoodleClearRect(NSRect rect)
NSRectFill(rect);
}
-@interface NSTableView (NoodlePrivateStickyRowExtensions)
+@interface NSTableView ()
+
+#pragma mark Sticky Row Header methods
// Returns index of the sticky row previous to the first visible row.
- (NSInteger)_previousStickyRow;
@@ -53,36 +34,15 @@ - (id)_stickyRowHeaderView;
@end
-@implementation NoodleStickyRowTableView
-
-// This is the minimum you have to do implement in your subclass.
-- (void)drawRect:(NSRect)rect
-{
- [super drawRect:rect];
-
- [self drawStickyRowHeader];
-}
-
-@end
-
-@implementation NoodleStickyRowOutlineView
-// This is the minimum you have to do implement in your subclass.
-- (void)drawRect:(NSRect)rect
-{
- [super drawRect:rect];
-
- [self drawStickyRowHeader];
-}
+@implementation NSTableView (NoodleExtensions)
-@end
-
-@implementation NSTableView (NoodleStickyRowExtensions)
+#pragma mark Sticky Row Header methods
- (BOOL)isRowSticky:(NSInteger)rowIndex
{
id delegate;
-
+
delegate = [self delegate];
if ([delegate respondsToSelector:@selector(tableView:isStickyRow:)])
@@ -147,7 +107,7 @@ - (id)_stickyRowHeaderView
[view setTarget:self];
[view setAction:@selector(scrollToStickyRow:)];
-
+
[self addSubview:view];
[view release];
}
@@ -159,24 +119,36 @@ - (void)drawStickyRow:(NSInteger)row clipRect:(NSRect)clipRect
NSRect rowRect, cellRect;
NSCell *cell;
NSInteger colIndex, count;
+ id delegate;
+
+ delegate = [self delegate];
+
+ if (![delegate respondsToSelector:@selector(tableView:shouldDisplayCellInStickyRowHeaderForTableColumn:row:)])
+ {
+ delegate = nil;
+ }
rowRect = [self rectOfRow:row];
[[[self backgroundColor] highlightWithLevel:0.5] set];
NSRectFill(rowRect);
-// PENDING: -drawRow:clipRect: is too smart for its own good. If the row is not visible,
-// this method won't draw anything. Useless for row caching.
-// [self drawRow:row clipRect:rowRect];
-
+ // PENDING: -drawRow:clipRect: is too smart for its own good. If the row is not visible,
+ // this method won't draw anything. Useless for row caching.
+ // [self drawRow:row clipRect:rowRect];
+
count = [self numberOfColumns];
for (colIndex = 0; colIndex < count; colIndex++)
{
- cell = [self preparedCellAtColumn:colIndex row:row];
- cellRect = [self frameOfCellAtColumn:colIndex row:row];
- [cell drawWithFrame:cellRect inView:self];
+ if ((delegate == nil) ||
+ [delegate tableView:self shouldDisplayCellInStickyRowHeaderForTableColumn:[[self tableColumns] objectAtIndex:colIndex] row:row])
+ {
+ cell = [self preparedCellAtColumn:colIndex row:row];
+ cellRect = [self frameOfCellAtColumn:colIndex row:row];
+ [cell drawWithFrame:cellRect inView:self];
+ }
}
-
+
[[self gridColor] set];
[NSBezierPath strokeLineFromPoint:NSMakePoint(NSMinX(rowRect), NSMaxY(rowRect)) toPoint:NSMakePoint(NSMaxX(rowRect), NSMaxY(rowRect))];
}
@@ -199,18 +171,18 @@ - (void)_updateStickyRowHeaderImageWithRow:(NSInteger)row
rowRect = [self rectOfRow:row];
imageRect = NSMakeRect(0.0, 0.0, NSWidth(rowRect), NSHeight(rowRect));
stickyView = [self _stickyRowHeaderView];
-
+
isSelected = [self isRowSelected:row];
if (isSelected)
{
[self deselectRow:row];
}
-
+
// Optimization: instead of creating a new image each time (and since we can't
// add ivars in a category), just use the image in the sticky view. We're going
// to put it there in the end anyways, why not reuse it?
image = [stickyView image];
-
+
if ((image == nil) || !NSEqualSizes(rowRect.size, [image size]))
{
image = [[NSImage alloc] initWithSize:rowRect.size];
@@ -218,7 +190,7 @@ - (void)_updateStickyRowHeaderImageWithRow:(NSInteger)row
[stickyView setImage:image];
[image release];
}
-
+
visibleRect = [self visibleRect];
// Calculate a distance between the row header and the actual sticky row and normalize it
@@ -383,10 +355,76 @@ - (NSRect)stickyRowHeaderRect
return NSZeroRect;
}
+#pragma mark Row Spanning methods
+
+- (NSRange)rangeOfRowSpanAtColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex
+{
+ id dataSource, objectValue, originalObjectValue;
+ NSInteger i, start, end, count;
+ NSTableColumn *column;
+
+ dataSource = [self dataSource];
+
+ column = [[self tableColumns] objectAtIndex:columnIndex];
+
+ if ([column isRowSpanningEnabled])
+ {
+ originalObjectValue = [dataSource tableView:self objectValueForTableColumn:column row:rowIndex];
+
+ // Figure out the span of this cell. We determine this by going up and down finding contiguous rows with
+ // the same object value.
+ i = rowIndex;
+ while (i-- > 0)
+ {
+ objectValue = [dataSource tableView:self objectValueForTableColumn:column row:i];
+
+ if (![objectValue isEqual:originalObjectValue])
+ {
+ break;
+ }
+ }
+ start = i + 1;
+
+ count = [self numberOfRows];
+ i = rowIndex + 1;
+ while (i < count)
+ {
+ objectValue = [dataSource tableView:self objectValueForTableColumn:column row:i];
+
+ if (![objectValue isEqual:originalObjectValue])
+ {
+ break;
+ }
+ i++;
+ }
+ end = i - 1;
+
+ return NSMakeRange(start, end - start + 1);
+ }
+ return NSMakeRange(rowIndex, 1);
+}
+
+@end
+
+@implementation NSTableColumn (NoodleExtensions)
+
+#pragma mark Row Spanning methods
+
+- (BOOL)isRowSpanningEnabled
+{
+ return NO;
+}
+
+- (NoodleRowSpanningCell *)spanningCell
+{
+ return nil;
+}
+
@end
+@implementation NSOutlineView (NoodleExtensions)
-@implementation NSOutlineView (NoodleStickyRowExtensions)
+#pragma mark Sticky Row Header methods
- (BOOL)isRowSticky:(NSInteger)rowIndex
{
View
2  NoodleIPhoneTableView.m
@@ -28,7 +28,7 @@
//
#import "NoodleIPhoneTableView.h"
-#import "NoodleStickyRowTableView.h"
+#import "NSTableView-NoodleExtensions.h"
@implementation NoodleIPhoneTableView
View
193 NoodleKit.xcodeproj/project.pbxproj
@@ -14,16 +14,26 @@
322DEBB81072598E00CB7080 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 322DEBB61072598E00CB7080 /* MainMenu.xib */; };
322DECB310727B3C00CB7080 /* NoodleKit.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* NoodleKit.framework */; };
322DECC610727B9300CB7080 /* NoodleKit.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* NoodleKit.framework */; };
+ 3251D221108F7FAE007E281D /* NSIndexSet-NoodleExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3251D21F108F7FAE007E281D /* NSIndexSet-NoodleExtensions.h */; };
+ 3251D222108F7FAE007E281D /* NSIndexSet-NoodleExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3251D220108F7FAE007E281D /* NSIndexSet-NoodleExtensions.m */; };
+ 3251D225108F7FB9007E281D /* NoodleTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3251D223108F7FB9007E281D /* NoodleTableView.h */; };
+ 3251D226108F7FB9007E281D /* NoodleTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3251D224108F7FB9007E281D /* NoodleTableView.m */; };
+ 3251D235108F8017007E281D /* NoodleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* NoodleKit.framework */; };
+ 3251D237108F8021007E281D /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3251D236108F8021007E281D /* Cocoa.framework */; };
+ 3251D241108F8119007E281D /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3251D23F108F8119007E281D /* MainMenu.xib */; };
+ 3251D243108F8125007E281D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3251D242108F8125007E281D /* main.m */; };
+ 3251D245108F8165007E281D /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 3251D244108F8165007E281D /* Credits.rtf */; };
+ 3251D248108F817C007E281D /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = 3251D247108F817B007E281D /* Controller.m */; };
+ 3251D48D1090FFB0007E281D /* NSTableView-NoodleExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3251D48B1090FFB0007E281D /* NSTableView-NoodleExtensions.h */; };
+ 3251D48E1090FFB0007E281D /* NSTableView-NoodleExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3251D48C1090FFB0007E281D /* NSTableView-NoodleExtensions.m */; };
+ 326A35051092486400AF02F8 /* NoodleIPhoneTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32782FD910713DA00041F09C /* NoodleIPhoneTableView.m */; };
+ 326A35061092486800AF02F8 /* NoodleIPhoneTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 32782FD810713DA00041F09C /* NoodleIPhoneTableView.h */; };
32782FB110713A4C0041F09C /* NoodleLineNumberView.h in Headers */ = {isa = PBXBuildFile; fileRef = 32782FAF10713A4C0041F09C /* NoodleLineNumberView.h */; };
32782FB210713A4C0041F09C /* NoodleLineNumberView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32782FB010713A4C0041F09C /* NoodleLineNumberView.m */; };
32782FB610713B640041F09C /* NoodleLineNumberMarker.h in Headers */ = {isa = PBXBuildFile; fileRef = 32782FB410713B640041F09C /* NoodleLineNumberMarker.h */; };
32782FB710713B640041F09C /* NoodleLineNumberMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = 32782FB510713B640041F09C /* NoodleLineNumberMarker.m */; };
32782FD610713C1E0041F09C /* NSResponder-NoodleModalExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 32782FD410713C1E0041F09C /* NSResponder-NoodleModalExtensions.h */; };
32782FD710713C1E0041F09C /* NSResponder-NoodleModalExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 32782FD510713C1E0041F09C /* NSResponder-NoodleModalExtensions.m */; };
- 32782FDE10713DA00041F09C /* NoodleIPhoneTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 32782FD810713DA00041F09C /* NoodleIPhoneTableView.h */; };
- 32782FDF10713DA00041F09C /* NoodleIPhoneTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32782FD910713DA00041F09C /* NoodleIPhoneTableView.m */; };
- 32782FE010713DA00041F09C /* NoodleStickyRowTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 32782FDA10713DA00041F09C /* NoodleStickyRowTableView.h */; };
- 32782FE110713DA00041F09C /* NoodleStickyRowTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32782FDB10713DA00041F09C /* NoodleStickyRowTableView.m */; };
32782FE210713DA00041F09C /* NSImage-NoodleExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 32782FDC10713DA00041F09C /* NSImage-NoodleExtensions.h */; };
32782FE310713DA00041F09C /* NSImage-NoodleExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 32782FDD10713DA00041F09C /* NSImage-NoodleExtensions.m */; };
32782FE610713DD60041F09C /* NSObject-NoodlePerformWhenIdle.h in Headers */ = {isa = PBXBuildFile; fileRef = 32782FE410713DD60041F09C /* NSObject-NoodlePerformWhenIdle.h */; };
@@ -63,6 +73,13 @@
remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
remoteInfo = NoodleKit;
};
+ 3251D238108F802F007E281D /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
+ remoteInfo = NoodleKit;
+ };
3278318710715CEB0041F09C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@@ -144,6 +161,20 @@
322DEBB11072595B00CB7080 /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Controller.m; path = "Window Effects/Controller.m"; sourceTree = "<group>"; };
322DEBB21072595B00CB7080 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = "Window Effects/main.m"; sourceTree = "<group>"; };
322DEBB71072598E00CB7080 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = "Examples/Window Effects/English.lproj/MainMenu.xib"; sourceTree = "<group>"; };
+ 3251D21F108F7FAE007E281D /* NSIndexSet-NoodleExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet-NoodleExtensions.h"; sourceTree = "<group>"; };
+ 3251D220108F7FAE007E281D /* NSIndexSet-NoodleExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet-NoodleExtensions.m"; sourceTree = "<group>"; };
+ 3251D223108F7FB9007E281D /* NoodleTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoodleTableView.h; sourceTree = "<group>"; };
+ 3251D224108F7FB9007E281D /* NoodleTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoodleTableView.m; sourceTree = "<group>"; };
+ 3251D22F108F800C007E281D /* iToonz.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iToonz.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3251D231108F800C007E281D /* iToonz-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "iToonz-Info.plist"; sourceTree = "<group>"; };
+ 3251D236108F8021007E281D /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+ 3251D240108F8119007E281D /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ 3251D242108F8125007E281D /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 3251D244108F8165007E281D /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
+ 3251D246108F817B007E281D /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Controller.h; sourceTree = "<group>"; };
+ 3251D247108F817B007E281D /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Controller.m; sourceTree = "<group>"; };
+ 3251D48B1090FFB0007E281D /* NSTableView-NoodleExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTableView-NoodleExtensions.h"; sourceTree = "<group>"; };
+ 3251D48C1090FFB0007E281D /* NSTableView-NoodleExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTableView-NoodleExtensions.m"; sourceTree = "<group>"; };
32782FAF10713A4C0041F09C /* NoodleLineNumberView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoodleLineNumberView.h; sourceTree = "<group>"; };
32782FB010713A4C0041F09C /* NoodleLineNumberView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoodleLineNumberView.m; sourceTree = "<group>"; };
32782FB410713B640041F09C /* NoodleLineNumberMarker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoodleLineNumberMarker.h; sourceTree = "<group>"; };
@@ -152,8 +183,6 @@
32782FD510713C1E0041F09C /* NSResponder-NoodleModalExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSResponder-NoodleModalExtensions.m"; sourceTree = "<group>"; };
32782FD810713DA00041F09C /* NoodleIPhoneTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoodleIPhoneTableView.h; sourceTree = "<group>"; };
32782FD910713DA00041F09C /* NoodleIPhoneTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoodleIPhoneTableView.m; sourceTree = "<group>"; };
- 32782FDA10713DA00041F09C /* NoodleStickyRowTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoodleStickyRowTableView.h; sourceTree = "<group>"; };
- 32782FDB10713DA00041F09C /* NoodleStickyRowTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoodleStickyRowTableView.m; sourceTree = "<group>"; };
32782FDC10713DA00041F09C /* NSImage-NoodleExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage-NoodleExtensions.h"; sourceTree = "<group>"; };
32782FDD10713DA00041F09C /* NSImage-NoodleExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSImage-NoodleExtensions.m"; sourceTree = "<group>"; };
32782FE410713DD60041F09C /* NSObject-NoodlePerformWhenIdle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject-NoodlePerformWhenIdle.h"; sourceTree = "<group>"; };
@@ -199,6 +228,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 3251D22D108F800C007E281D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3251D235108F8017007E281D /* NoodleKit.framework in Frameworks */,
+ 3251D237108F8021007E281D /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
32783083107151B60041F09C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -245,6 +283,7 @@
3278314F10715AB80041F09C /* StickyRowTableView Revue.app */,
327831A6107160380041F09C /* LineNumberView.app */,
322DEBA61072593A00CB7080 /* Window Effects.app */,
+ 3251D22F108F800C007E281D /* iToonz.app */,
);
name = Products;
sourceTree = "<group>";
@@ -258,6 +297,7 @@
0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */,
3278307F1071513F0041F09C /* Examples */,
034768DFFF38A50411DB9C8B /* Products */,
+ 3251D236108F8021007E281D /* Cocoa.framework */,
);
name = NoodleKit;
sourceTree = "<group>";
@@ -285,6 +325,8 @@
children = (
32782FE410713DD60041F09C /* NSObject-NoodlePerformWhenIdle.h */,
32782FE510713DD60041F09C /* NSObject-NoodlePerformWhenIdle.m */,
+ 3251D21F108F7FAE007E281D /* NSIndexSet-NoodleExtensions.h */,
+ 3251D220108F7FAE007E281D /* NSIndexSet-NoodleExtensions.m */,
32782FD410713C1E0041F09C /* NSResponder-NoodleModalExtensions.h */,
32782FD510713C1E0041F09C /* NSResponder-NoodleModalExtensions.m */,
32782FDC10713DA00041F09C /* NSImage-NoodleExtensions.h */,
@@ -295,10 +337,12 @@
32782FB510713B640041F09C /* NoodleLineNumberMarker.m */,
32782FAF10713A4C0041F09C /* NoodleLineNumberView.h */,
32782FB010713A4C0041F09C /* NoodleLineNumberView.m */,
+ 3251D48B1090FFB0007E281D /* NSTableView-NoodleExtensions.h */,
+ 3251D48C1090FFB0007E281D /* NSTableView-NoodleExtensions.m */,
+ 3251D223108F7FB9007E281D /* NoodleTableView.h */,
+ 3251D224108F7FB9007E281D /* NoodleTableView.m */,
32782FD810713DA00041F09C /* NoodleIPhoneTableView.h */,
32782FD910713DA00041F09C /* NoodleIPhoneTableView.m */,
- 32782FDA10713DA00041F09C /* NoodleStickyRowTableView.h */,
- 32782FDB10713DA00041F09C /* NoodleStickyRowTableView.m */,
);
name = Classes;
sourceTree = "<group>";
@@ -333,9 +377,23 @@
name = "Window Effects";
sourceTree = "<group>";
};
+ 3251D23E108F8063007E281D /* iToonz */ = {
+ isa = PBXGroup;
+ children = (
+ 3251D242108F8125007E281D /* main.m */,
+ 3251D231108F800C007E281D /* iToonz-Info.plist */,
+ 3251D244108F8165007E281D /* Credits.rtf */,
+ 3251D23F108F8119007E281D /* MainMenu.xib */,
+ 3251D246108F817B007E281D /* Controller.h */,
+ 3251D247108F817B007E281D /* Controller.m */,
+ );
+ path = iToonz;
+ sourceTree = "<group>";
+ };
3278307F1071513F0041F09C /* Examples */ = {
isa = PBXGroup;
children = (
+ 3251D23E108F8063007E281D /* iToonz */,
327831A110715FC90041F09C /* LineNumberView */,
327830941071527D0041F09C /* ModalResponder */,
3278314A10715A970041F09C /* StickyRowTableView Revue */,
@@ -403,11 +461,13 @@
32782FB110713A4C0041F09C /* NoodleLineNumberView.h in Headers */,
32782FB610713B640041F09C /* NoodleLineNumberMarker.h in Headers */,
32782FD610713C1E0041F09C /* NSResponder-NoodleModalExtensions.h in Headers */,
- 32782FDE10713DA00041F09C /* NoodleIPhoneTableView.h in Headers */,
- 32782FE010713DA00041F09C /* NoodleStickyRowTableView.h in Headers */,
32782FE210713DA00041F09C /* NSImage-NoodleExtensions.h in Headers */,
32782FE610713DD60041F09C /* NSObject-NoodlePerformWhenIdle.h in Headers */,
3278303A107140600041F09C /* NSWindow-NoodleEffects.h in Headers */,
+ 3251D221108F7FAE007E281D /* NSIndexSet-NoodleExtensions.h in Headers */,
+ 3251D225108F7FB9007E281D /* NoodleTableView.h in Headers */,
+ 3251D48D1090FFB0007E281D /* NSTableView-NoodleExtensions.h in Headers */,
+ 326A35061092486800AF02F8 /* NoodleIPhoneTableView.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -433,6 +493,24 @@
productReference = 322DEBA61072593A00CB7080 /* Window Effects.app */;
productType = "com.apple.product-type.application";
};
+ 3251D22E108F800C007E281D /* iToonz */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 3251D234108F800D007E281D /* Build configuration list for PBXNativeTarget "iToonz" */;
+ buildPhases = (
+ 3251D22B108F800C007E281D /* Resources */,
+ 3251D22C108F800C007E281D /* Sources */,
+ 3251D22D108F800C007E281D /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 3251D239108F802F007E281D /* PBXTargetDependency */,
+ );
+ name = iToonz;
+ productName = iToonz;
+ productReference = 3251D22F108F800C007E281D /* iToonz.app */;
+ productType = "com.apple.product-type.application";
+ };
32783084107151B60041F09C /* ModalResponder */ = {
isa = PBXNativeTarget;
buildConfigurationList = 3278308A107151B70041F09C /* Build configuration list for PBXNativeTarget "ModalResponder" */;
@@ -527,6 +605,7 @@
3278314E10715AB80041F09C /* StickyRowTableView Revue */,
327831A5107160380041F09C /* LineNumberView */,
322DEBA51072593A00CB7080 /* Window Effects */,
+ 3251D22E108F800C007E281D /* iToonz */,
);
};
/* End PBXProject section */
@@ -540,6 +619,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 3251D22B108F800C007E281D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3251D241108F8119007E281D /* MainMenu.xib in Resources */,
+ 3251D245108F8165007E281D /* Credits.rtf in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
32783081107151B60041F09C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -587,6 +675,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 3251D22C108F800C007E281D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3251D243108F8125007E281D /* main.m in Sources */,
+ 3251D248108F817C007E281D /* Controller.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
32783082107151B60041F09C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -622,11 +719,13 @@
32782FB210713A4C0041F09C /* NoodleLineNumberView.m in Sources */,
32782FB710713B640041F09C /* NoodleLineNumberMarker.m in Sources */,
32782FD710713C1E0041F09C /* NSResponder-NoodleModalExtensions.m in Sources */,
- 32782FDF10713DA00041F09C /* NoodleIPhoneTableView.m in Sources */,
- 32782FE110713DA00041F09C /* NoodleStickyRowTableView.m in Sources */,
32782FE310713DA00041F09C /* NSImage-NoodleExtensions.m in Sources */,
32782FE710713DD60041F09C /* NSObject-NoodlePerformWhenIdle.m in Sources */,
3278303B107140600041F09C /* NSWindow-NoodleEffects.m in Sources */,
+ 3251D222108F7FAE007E281D /* NSIndexSet-NoodleExtensions.m in Sources */,
+ 3251D226108F7FB9007E281D /* NoodleTableView.m in Sources */,
+ 3251D48E1090FFB0007E281D /* NSTableView-NoodleExtensions.m in Sources */,
+ 326A35051092486400AF02F8 /* NoodleIPhoneTableView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -638,6 +737,11 @@
target = 8DC2EF4F0486A6940098B216 /* NoodleKit */;
targetProxy = 322DEBAC1072594000CB7080 /* PBXContainerItemProxy */;
};
+ 3251D239108F802F007E281D /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8DC2EF4F0486A6940098B216 /* NoodleKit */;
+ targetProxy = 3251D238108F802F007E281D /* PBXContainerItemProxy */;
+ };
3278318810715CEB0041F09C /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 8DC2EF4F0486A6940098B216 /* NoodleKit */;
@@ -673,6 +777,14 @@
path = ..;
sourceTree = "<group>";
};
+ 3251D23F108F8119007E281D /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 3251D240108F8119007E281D /* English */,
+ );
+ name = MainMenu.xib;
+ sourceTree = "<group>";
+ };
32783091107152680041F09C /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
@@ -837,6 +949,54 @@
};
name = Release;
};
+ 3251D232108F800D007E281D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
+ INFOPLIST_FILE = "Examples/iToonz/iToonz-Info.plist";
+ INSTALL_PATH = "$(HOME)/Applications";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = iToonz;
+ };
+ name = Debug;
+ };
+ 3251D233108F800D007E281D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
+ INFOPLIST_FILE = "Examples/iToonz/iToonz-Info.plist";
+ INSTALL_PATH = "$(HOME)/Applications";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = iToonz;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
32783088107151B70041F09C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1011,6 +1171,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 3251D234108F800D007E281D /* Build configuration list for PBXNativeTarget "iToonz" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3251D232108F800D007E281D /* Debug */,
+ 3251D233108F800D007E281D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
3278308A107151B70041F09C /* Build configuration list for PBXNativeTarget "ModalResponder" */ = {
isa = XCConfigurationList;
buildConfigurations = (
View
94 NoodleTableView.h
@@ -0,0 +1,94 @@
+//
+// NoodleRowSpanningTableView.h
+// NoodleRowSpanningTableViewTest
+//
+// Created by Paul Kim on 10/20/09.
+// Copyright 2009 Noodlesoft, LLC. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+#import <Cocoa/Cocoa.h>
+
+/*
+ This NSTableView subclass provides a couple of neat features.
+
+ Sticky Row Headers
+ ==================
+ This allows you to specify certain rows (it uses group rows by default) to "stick" to the top of the tableview
+ scroll area when it is scrolled out of view. This is similar to how section headers work in UITableView in the
+ iPhone SDK. The bulk of the implementation and delegate API are in the NSTableView-NoodleExtensions category.
+
+ To enable this feature, just set the showsStickyRowHeader property.
+
+ For more details, see the related blog post at http://www.noodlesoft.com/blog/2009/09/25/sticky-section-headers-in-nstableview/
+
+
+ Row Spanning Columns
+ ====================
+
+ Row Spanning Columns are columns whose cells are allow to span across multiple rows. The span is determined by
+ a contiguous section of rows that have the same object value. The cells within such a span are consolidated into
+ a single special cell. An example of this can be seen in the Artwork column in iTunes.
+
+ For any columns where you want to have this take effect, just change the column class to NoodleTableColumn and
+ set the rowSpanningEnabled property on it. You can alternatively call -setRowSpanningEnabledForCapablyColumns
+ on the tableview in your -awakeFromNib to enable all the NoodleTableColumns in your table in one fell swoop.
+
+ For more details, see the related blog post at http://www.noodlesoft.com/blog/2009/10/20/yet-another-way-to-mimic-the-artwork-column-in-cocoa/
+ */
+@interface NoodleTableView : NSTableView
+{
+ BOOL _showsStickyRowHeader;
+
+ BOOL _hasSpanningColumns;
+ BOOL _isDrawingStickyRow;
+}
+
+@property (getter=showsStickyRowHeader, setter=setShowsStickyRowHeader) BOOL showsStickyRowHeader;
+
+#pragma mark Row Spanning methods
+
+/*
+ Enables/disables row spanning for all NoodleTableColumns in the receiver. Note that row spanning is not enabled
+ by default so if you want row spanning, call this from -awakeFromNib is a good idea.
+ */
+- (void)setRowSpanningEnabledForCapableColumns:(BOOL)flag;
+
+@end
+
+
+@class NoodleRowSpanningCell;
+
+/*
+ Special table column that enables row spanning functionality. Just set your columns in IB to use this class and
+ enable it by calling -setRowSpaningEnabled:
+ */
+@interface NoodleTableColumn :NSTableColumn
+{
+ BOOL _spanningEnabled;
+ NoodleRowSpanningCell *_cell;
+
+}
+
+@property (getter=isRowSpanningEnabled, setter=setRowSpanningEnabled:) BOOL rowSpanningEnabled;
+
+@end
View
548 NoodleTableView.m
@@ -0,0 +1,548 @@
+//
+// NoodleRowSpanningTableView.m
+// NoodleRowSpanningTableViewTest
+//
+// Created by Paul Kim on 10/20/09.
+// Copyright 2009 Noodlesoft, LLC. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+#import "NoodleTableView.h"
+#import "NSTableView-NoodleExtensions.h"
+#import "NSImage-NoodleExtensions.h"
+#import "NSIndexSet-NoodleExtensions.h"
+
+/*
+ Internal cell class. Wraps around another cell. Draws the inner cell in its "full frame" but when drawing in
+ the tableview, draws each row sliver from the full image.
+ */
+@interface NoodleRowSpanningCell : NSCell
+{
+ NSRect _fullFrame;
+ NSCell *_cell;
+ NSImage *_cachedImage;
+ NSColor *_backgroundColor;
+ NSInteger _startIndex;
+ NSInteger _lastStartIndex;
+ NSInteger _endIndex;
+ NSInteger _lastEndIndex;
+}
+
+@property NSRect fullFrame;
+@property (assign) NSCell *cell;
+@property (copy) NSColor *backgroundColor;
+@property NSInteger startIndex;
+@property NSInteger endIndex;
+
+@end
+
+@implementation NoodleRowSpanningCell
+
+@synthesize fullFrame = _fullFrame;
+@synthesize cell = _cell;
+@synthesize backgroundColor = _backgroundColor;
+@synthesize startIndex = _startIndex;
+@synthesize endIndex = _endIndex;
+
+- (void)_clearOutCaches
+{
+ _startIndex = -1;
+ _endIndex = -1;
+ _lastStartIndex = -1;
+ _lastEndIndex = -1;
+ _cell = nil;
+}
+
+- (void)dealloc
+{
+ [self _clearOutCaches];
+ [_backgroundColor release];
+ [_cachedImage release];
+
+ [super dealloc];
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ NoodleRowSpanningCell *copy;
+
+ copy = [super copyWithZone:zone];
+
+ // super will copy the pointer across (via NSCopyObject()) but we need to retain or copy the actual instances
+ copy->_cachedImage = [_cachedImage copy];
+ copy->_backgroundColor = [_backgroundColor copy];
+
+ return copy;
+}
+
+- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
+{
+ // Draw the full span of the cell into a cached image and then pull out the correct sliver as needed
+
+ if ((_startIndex != _lastStartIndex) || (_endIndex != _lastEndIndex) || (_cachedImage == nil))
+ {
+ // If the indices have changed, we are dealing with a new span so recache the cell's full image
+ NSAffineTransform *transform;
+ NSColor *color;
+
+ if ((_cachedImage == nil) || !NSEqualSizes(_fullFrame.size, [_cachedImage size]))
+ {
+ [_cachedImage release];
+ _cachedImage = [[NSImage alloc] initWithSize:_fullFrame.size];
+ [_cachedImage setFlipped:[controlView isFlipped]];
+ }
+
+ [_cachedImage lockFocus];
+
+ transform = [NSAffineTransform transform];
+ [transform translateXBy:-NSMinX(_fullFrame) yBy:-NSMinY(_fullFrame)];
+ [transform concat];
+
+ color = _backgroundColor;
+ if (color == nil)
+ {
+ color = [NSColor clearColor];
+ }
+ [color set];
+ NSRectFill(_fullFrame);
+
+ [_cell drawWithFrame:_fullFrame inView:controlView];
+
+ [_cachedImage unlockFocus];
+
+ _lastStartIndex = _startIndex;
+ _lastEndIndex = _endIndex;
+ }
+
+ // Now draw the sliver for the current row in the right spot
+ [_cachedImage drawAdjustedInRect:cellFrame
+ fromRect:NSMakeRect(NSMinX(cellFrame) - NSMinX(_fullFrame),
+ NSMinY(cellFrame) - NSMinY(_fullFrame),
+ NSWidth(cellFrame), NSHeight(cellFrame))
+ operation:NSCompositeSourceOver fraction:1.0];
+}
+
+@end
+
+@implementation NoodleTableColumn : NSTableColumn
+
+@synthesize rowSpanningEnabled = _spanningEnabled;
+
+#define SPANNING_ENABLED_KEY @"spanningEnabled"
+
+- (void)encodeWithCoder:(NSCoder *)encoder
+{
+ [super encodeWithCoder:encoder];
+
+ if ([encoder allowsKeyedCoding])
+ {
+ [encoder encodeObject:[NSNumber numberWithBool:_spanningEnabled] forKey:SPANNING_ENABLED_KEY];
+ }
+ else
+ {
+ [encoder encodeObject:[NSNumber numberWithBool:_spanningEnabled]];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)decoder
+{
+ if ((self = [super initWithCoder:decoder]) != nil)
+ {
+ id value;
+
+ if ([decoder allowsKeyedCoding])
+ {
+ value = [decoder decodeObjectForKey:SPANNING_ENABLED_KEY];
+ }
+ else
+ {
+ value = [decoder decodeObject];
+ }
+
+ if (value != nil)
+ {
+ _spanningEnabled = [value boolValue];
+ }
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [_cell release];
+ [super dealloc];
+}
+
+- (NoodleRowSpanningCell *)spanningCell
+{
+ if (_cell == nil)
+ {
+ _cell = [[NoodleRowSpanningCell alloc] initTextCell:@""];
+ }
+ return _cell;
+}
+
+@end
+
+
+@implementation NoodleTableView
+
+@synthesize showsStickyRowHeader = _showsStickyRowHeader;
+
+#pragma mark NSCoding methods
+
+#define SHOWS_STICKY_ROW_HEADER_KEY @"showsStickyRowHeader"
+
+- (void)encodeWithCoder:(NSCoder *)encoder
+{
+ [super encodeWithCoder:encoder];
+
+ if ([encoder allowsKeyedCoding])
+ {
+ [encoder encodeObject:[NSNumber numberWithBool:_showsStickyRowHeader] forKey:SHOWS_STICKY_ROW_HEADER_KEY];
+ }
+ else
+ {
+ [encoder encodeObject:[NSNumber numberWithBool:_showsStickyRowHeader]];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)decoder
+{
+ if ((self = [super initWithCoder:decoder]) != nil)
+ {
+ id value;
+
+ if ([decoder allowsKeyedCoding])
+ {
+ value = [decoder decodeObjectForKey:SHOWS_STICKY_ROW_HEADER_KEY];
+ }
+ else
+ {
+ value = [decoder decodeObject];
+ }
+ _showsStickyRowHeader = [value boolValue];
+
+ for (NSTableColumn *column in [self tableColumns])
+ {
+ if ([column isKindOfClass:[NoodleTableColumn class]])
+ {
+ _hasSpanningColumns = YES;
+ break;
+ }
+ }
+ }
+ return self;
+}
+
+- (void)addTableColumn:(NSTableColumn *)column
+{
+ [super addTableColumn:column];
+
+ if ([column isKindOfClass:[NoodleTableColumn class]])
+ {
+ _hasSpanningColumns = YES;
+ }
+}
+
+- (void)removeTableColumn:(NSTableColumn *)column
+{
+ [super removeTableColumn:column];
+
+ for (NSTableColumn *column in [self tableColumns])
+ {
+ if ([column isKindOfClass:[NoodleTableColumn class]])
+ {
+ _hasSpanningColumns = YES;
+ break;
+ }
+ }
+}
+
+#pragma mark Row Spanning methods
+
+- (void)setRowSpanningEnabledForCapableColumns:(BOOL)flag
+{
+ for (id column in [self tableColumns])
+ {
+ if ([column respondsToSelector:@selector(setRowSpanningEnabled:)])
+ {
+ [column setRowSpanningEnabled:flag];
+ }
+ }
+}
+
+// Does the actual work of drawing the grid. Originally, was trying to set the grid mask and calling super's
+// -drawGridInClipRect: method on specific regions to get the effect I wanted but all the setting of the masks
+// ended up sucking down CPU cycles as it got into a loop queueing up tons of redraw requests.
+- (void)_drawGrid:(NSUInteger)gridMask inClipRect:(NSRect)aRect
+{
+ NSRect rect;
+
+ [[self gridColor] set];
+
+ if ((gridMask & NSTableViewSolidHorizontalGridLineMask) != 0)
+ {
+ NSRange range;
+ NSInteger i;
+
+ range = [self rowsInRect:aRect];
+ for (i = range.location; i < NSMaxRange(range); i++)
+ {
+ rect = [self rectOfRow:i];
+ if (NSMaxY(rect) <= NSMaxY(aRect))
+ {
+ rect.origin.x = NSMinX(aRect);
+ rect.size.width = NSWidth(aRect);
+ rect.origin.y -= 0.5;
+ [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMinX(rect), NSMaxY(rect)) toPoint:NSMakePoint(NSMaxX(rect), NSMaxY(rect))];
+ }
+ }
+ }
+ if ((gridMask & NSTableViewSolidVerticalGridLineMask) != 0)
+ {
+ NoodleIndexSetEnumerator *enumerator;
+ NSInteger i;
+
+ enumerator = [[self columnIndexesInRect:aRect] indexEnumerator];
+ while ((i = [enumerator nextIndex]) != NSNotFound)
+ {
+ rect = [self rectOfColumn:i];
+ if (NSMaxX(rect) <= NSMaxX(aRect))
+ {
+ rect.origin.y = NSMinY(aRect);
+ rect.size.height = NSHeight(aRect);
+ rect.origin.x -= 0.5;
+ [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX(rect), NSMinY(rect)) toPoint:NSMakePoint(NSMaxX(rect), NSMaxY(rect))];
+ }
+ }
+ }
+}
+
+- (void)drawGridInClipRect:(NSRect)aRect
+{
+ NSUInteger origGridMask;
+
+ origGridMask = [self gridStyleMask];
+
+ if (_hasSpanningColumns && ((origGridMask & NSTableViewSolidHorizontalGridLineMask) != 0))
+ {
+ // Rules:
+ // - No spanning cell should have grid lines within it. Only at the bottom.
+ // - Non-spanning cells inherit their grid lines from the closest spanning cell to the left (or to the right
+ // if there are none to the left.
+
+ NSRange range, spanRange;
+ NSInteger columnIndex, endColumnIndex, startColumnIndex, row;
+ NSMutableIndexSet *columnIndexes;
+ NoodleIndexSetEnumerator *enumerator;
+ NSRect topLeft, bottomRight, rect;
+
+ // Grab the indexes of all the spanning columns.
+ columnIndex = 0;
+ columnIndexes = [NSMutableIndexSet indexSet];
+ for (NSTableColumn *column in [self tableColumns])
+ {
+ if ([column isRowSpanningEnabled])
+ {
+ [columnIndexes addIndex:columnIndex];
+ }
+ columnIndex++;
+ }
+
+ // Hard to explain but we calculate regions going from left to right, defining regions horizontally
+ // from one spanning column to the next and vertically by row spans. We first draw the non-horizontal
+ // grid lines within a span (taking into account precedence rules concerning columns as noted above).
+ // Then we draw the horizontal grid line at the bottom of a span. We are trying to find the maximal
+ // regions to send to the grid drawing routine as doing it cell by cell incurs a bit of overhead.
+ startColumnIndex = 0;
+ enumerator = [columnIndexes indexEnumerator];
+ while ((columnIndex = [enumerator nextIndex]) != NSNotFound)
+ {
+ // This column is the right edge of this region (which is up to the next spanning column)
+ endColumnIndex = [columnIndexes indexGreaterThanIndex:columnIndex];
+ if (endColumnIndex == NSNotFound)
+ {
+ endColumnIndex = [self numberOfColumns] - 1;
+ }
+ else
+ {
+ endColumnIndex--;
+ }
+
+ range = [self rowsInRect:aRect];
+
+ row = range.location;
+ while (row < NSMaxRange(range))
+ {
+ spanRange = [self rangeOfRowSpanAtColumn:columnIndex row:row];
+
+ // Get the rects of the top left of our region (the start column and start row of the span)
+ // to the bottom right (the end column and the row in the span just before the last one).
+ topLeft = [self frameOfCellAtColumn:startColumnIndex row:spanRange.location];
+ bottomRight = [self frameOfCellAtColumn:endColumnIndex row:NSMaxRange(spanRange) - 2];
+
+ rect = NSIntersectionRect(aRect, NSUnionRect(topLeft, bottomRight));
+
+ if (spanRange.length > 1)
+ {
+ // Draw span region without horizontal grid lines
+ [self _drawGrid:origGridMask & ~NSTableViewSolidHorizontalGridLineMask inClipRect:rect];
+
+ // Now, calculate the region at the last row of the span
+ topLeft = [self frameOfCellAtColumn:startColumnIndex row:NSMaxRange(spanRange) - 1];
+ bottomRight = [self frameOfCellAtColumn:endColumnIndex row:NSMaxRange(spanRange) - 1];
+
+ // Draw bottom of span with horizontal grid lines
+ rect = NSIntersectionRect(aRect, NSUnionRect(topLeft, bottomRight) );
+ [self _drawGrid:origGridMask inClipRect:rect];
+ }
+ else
+ {
+ // Not a span row or just a single row. Either way, draw with horizontal grid lines
+ [self _drawGrid:origGridMask inClipRect:rect];
+ }
+ // Advance to the next row span
+ row = NSMaxRange(spanRange);
+ }
+ // Advance to the next span column region
+ startColumnIndex = endColumnIndex + 1;
+ }
+ }
+ else
+ {
+ // We only need the special logic when we have row spanning columns and drawing horizontal lines. Otherwise,
+ // just call super.
+ [super drawGridInClipRect:aRect];
+ }
+}
+
+- (NSCell *)preparedCellAtColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex
+{
+ NSTableColumn *column;
+
+ column = [[self tableColumns] objectAtIndex:columnIndex];
+
+ if (!_isDrawingStickyRow && [column isRowSpanningEnabled])
+ {
+ NSRange range;
+
+ range = [self rangeOfRowSpanAtColumn:columnIndex row:rowIndex];
+
+ if (range.length >= 1)
+ {
+ // Here is where we insert our special cell for row spanning behavior
+ NoodleRowSpanningCell *spanningCell;
+ NSCell *cell;
+ NSInteger start, end;
+ BOOL wasSelected;
+
+ start = range.location;
+ end = NSMaxRange(range) - 1;
+
+ // Want to draw cell in its unhighlighted state since spanning cells aren't selectable. Unfortuantely,
+ // can't just setHighlight:NO on it because NSTableView sets other attributes (like text color).
+ // Instead, we deselect the row, grab the cell, then set it back (if it was selected before).
+ wasSelected = [self isRowSelected:start];
+ [self deselectRow:start];
+
+ cell = [super preparedCellAtColumn:columnIndex row:start];
+
+ if (wasSelected)
+ {
+ [self selectRowIndexes:[NSIndexSet indexSetWithIndex:start] byExtendingSelection:YES];
+ }
+
+ spanningCell = [column spanningCell];
+ [spanningCell setCell:cell];
+
+ // The full frame is the rect encompassing the first and last rows of the span.
+ [spanningCell setFullFrame:NSUnionRect([self frameOfCellAtColumn:columnIndex row:start],
+ [self frameOfCellAtColumn:columnIndex row:end])];
+ [spanningCell setStartIndex:start];
+ [spanningCell setEndIndex:end];
+
+ [spanningCell setBackgroundColor:[self backgroundColor]];
+
+ return spanningCell;
+ }
+ }
+ return [super preparedCellAtColumn:columnIndex row:rowIndex];
+}
+
+- (void)mouseDown:(NSEvent *)event
+{
+ if (_hasSpanningColumns)
+ {
+ // Eat up any clicks on spanning cells. In the future, may want to consider having clicks select the
+ // first row in the span (like when clicking on the artwork in iTunes).
+
+ NSPoint point;
+ NSInteger columnIndex;
+ NSTableColumn *column;
+
+ point = [event locationInWindow];
+ point = [self convertPointFromBase:point];
+
+ columnIndex = [self columnAtPoint:point];
+ column = [[self tableColumns] objectAtIndex:columnIndex];
+ if ([column isRowSpanningEnabled])
+ {
+ return;
+ }
+ }
+ [super mouseDown:event];
+}
+
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+ [super drawRect:dirtyRect];
+
+ // All that needs to be done to enable the sticky row header functionality (bulk of the work
+ // done in the NSTableView category)
+ if ([self showsStickyRowHeader])
+ {
+ [self drawStickyRowHeader];
+ }
+
+ if (_hasSpanningColumns)
+ {
+ // Clean up any cached data. Don't want to keep stale cache data around.
+ for (NSTableColumn *column in [self tableColumns])
+ {
+ if ([column isRowSpanningEnabled])
+ {
+ [[column spanningCell] _clearOutCaches];
+ }
+ }
+ }
+}
+
+- (void)drawStickyRow:(NSInteger)row clipRect:(NSRect)clipRect
+{
+ _isDrawingStickyRow = YES;
+ [super drawStickyRow:row clipRect:clipRect];
+ _isDrawingStickyRow = NO;
+}
+
+@end
View
41 README.md
@@ -7,42 +7,47 @@ The project is primarily structured to build a framework. There are targets for
This framework is meant to be built/used on 10.5 and later and should support 64-bit.
-This code is maintained at <http://github.com/MrNoodle/NoodleKit>
+This code is maintained at <http://github.com/MrNoodle/NoodleKit> . Please post any issues and requests there.
What nifty stuff is in here?
----------------------------
-#### NSObject-NoodlePerformWhenIdle
-NSObject category for calling a method when the user has been idle for the specified amount of time. Useful for putting up non-critical alerts and purging memory caches, among other things.
-
+#### NSObject-NoodlePerformWhenIdle
+NSObject category for calling a method when the user has been idle for the specified amount of time. Useful for putting up non-critical alerts and purging memory caches, among other things.
<http://www.noodlesoft.com/blog/2008/01/08/idle-hands/>
-#### NSResponder-NoodleModalExtensions
-NSResponder category providing methods that will dismiss a dialog and return the proper code for whatever button (OK/Cancel) was clicked. Just hook your dialog buttons up to these methods in IB and you're set. Alleviates having to write that glue code every time.
+#### NSIndexSet-NoodleExtensions
+Provides an enumerator to cycle through the indexes in an NSIndexSet. Not featured directly in any blog article but used for the "Row Spanning Columns" feature (see below).
+#### NSResponder-NoodleModalExtensions
+NSResponder category providing methods that will dismiss a dialog and return the proper code for whatever button (OK/Cancel) was clicked. Just hook your dialog buttons up to these methods in IB and you're set. Alleviates having to write that glue code every time.
<http://www.noodlesoft.com/blog/2008/03/10/modal-glue/>
-#### NSImage-NoodleExtensions
-NSImage category providing methods to draw NSImages with correct orientation and scaling regardless of the flipped status of the image or the context being drawn into.
-
+#### NSImage-NoodleExtensions
+NSImage category providing methods to draw NSImages with correct orientation and scaling regardless of the flipped status of the image or the context being drawn into.
<http://www.noodlesoft.com/blog/2009/02/02/understanding-flipped-coordinate-systems/>
-#### NSWindow-NoodleEffects
-Provides a basic zoom effect for NSWindow.
-
-<http://www.noodlesoft.com/blog/2007/06/30/animation-in-the-time-of-tiger-part-1/>
+#### NSWindow-NoodleEffects
+Provides a basic zoom effect for NSWindow.
+<http://www.noodlesoft.com/blog/2007/06/30/animation-in-the-time-of-tiger-part-1/>
<http://www.noodlesoft.com/blog/2007/09/20/animation-in-the-time-of-tiger-part-3/>
-#### NoodleLineNumberView, NoodleLineNumberMarker:
-Adds line numbers (and markers) to NSTextView.
-
+#### NoodleLineNumberView, NoodleLineNumberMarker
+Adds line numbers (and corresponding markers) to NSTextView.
<http://www.noodlesoft.com/blog/2008/10/05/displaying-line-numbers-with-nstextview/>
-#### NoodleStickyRowTableView, NoodleIPhoneTableView:
-An NSTableView category that does sticky row headers, like with UITableView on the iPhone. NoodleStickyRowTableView is a basic subclass while NoodleIPhoneTableView simulates the look and feel of UITableView.
+#### NSTableView-NoodleExtensions, NoodleTableView, NoodleIPhoneTableView
+The NSTableView category and NoodleTableView are a consolidation of the sticky row header tableview
+and row spanning tableview featured on my blog.
+#####Sticky Row Headers
+An NSTableView category that does sticky row headers, like with UITableView on the iPhone. NoodleTableView implements the basic hooks to enable the feature while NoodleIPhoneTableView simulates the look and feel of UITableView.
<http://www.noodlesoft.com/blog/2009/09/25/sticky-section-headers-in-nstableview/>
+#####Row Spanning Columns
+Certain columns can be made to allow their cells to span across multiple rows. These spans are determined by contiguous sections of rows with the same object value. You can enable this in NoodleTableView by using NoodleTableColumns for any columns you want to exhibit this behavior. Remember to enable the property on each column or call -setRowSpanningEnabledForCapableColumns: to enable it for all NoodleTableColumns in the tableview.
+<http://www.noodlesoft.com/blog/2009/10/20/yet-another-way-to-mimic-the-artwork-column-in-cocoa/>
+
License
-------
Please sign in to comment.
Something went wrong with that request. Please try again.