├── English.lproj
└── InfoPlist.strings
├── Examples
├── ImageLab
│ ├── Credits.rtf
│ ├── English.lproj
│ │ └── MainMenu.xib
│ ├── ImageLab-Info.plist
│ ├── ImageLabAppDelegate.h
│ ├── ImageLabAppDelegate.m
│ ├── Read Me.rtf
│ ├── TimedImageView.h
│ ├── TimedImageView.m
│ ├── main.m
│ └── test.tiff
├── LineNumberView
│ ├── Controller.h
│ ├── Controller.m
│ ├── Credits.rtf
│ ├── English.lproj
│ │ ├── InfoPlist.strings
│ │ └── MainMenu.xib
│ ├── LineNumberView-Info.plist
│ ├── MarkerLineNumberView.h
│ ├── MarkerLineNumberView.m
│ ├── Read Me.rtf
│ └── main.m
├── ModalResponder
│ ├── Controller.h
│ ├── Controller.m
│ ├── Credits.rtf
│ ├── English.lproj
│ │ ├── InfoPlist.strings
│ │ └── MainMenu.xib
│ ├── ModalResponder-Info.plist
│ ├── Read Me.rtf
│ └── main.m
├── StickyRowTableView Revue
│ ├── Controller.h
│ ├── Controller.m
│ ├── Credits.rtf
│ ├── English.lproj
│ │ ├── InfoPlist.strings
│ │ └── MainMenu.xib
│ ├── Read Me.rtf
│ ├── StickyRowTableView Revue-Info.plist
│ └── main.m
├── TimerLab
│ ├── Credits.rtf
│ ├── English.lproj
│ │ └── MainMenu.xib
│ ├── Read Me.rtf
│ ├── TimerLab-Info.plist
│ ├── TimerLabAppDelegate.h
│ ├── TimerLabAppDelegate.m
│ └── main.m
├── Window Effects
│ ├── Controller.h
│ ├── Controller.m
│ ├── English.lproj
│ │ ├── InfoPlist.strings
│ │ └── MainMenu.xib
│ ├── Window Effects-Info.plist
│ └── main.m
└── iToonz
│ ├── Controller.h
│ ├── Controller.m
│ ├── Credits.rtf
│ ├── English.lproj
│ └── MainMenu.xib
│ ├── Read Me.rtf
│ ├── iToonz-Info.plist
│ └── main.m
├── Info.plist
├── NSImage-NoodleExtensions.h
├── NSImage-NoodleExtensions.m
├── NSIndexSet-NoodleExtensions.h
├── NSIndexSet-NoodleExtensions.m
├── NSObject-NoodlePerformWhenIdle.h
├── NSObject-NoodlePerformWhenIdle.m
├── NSResponder-NoodleModalExtensions.h
├── NSResponder-NoodleModalExtensions.m
├── NSTableView-NoodleExtensions.h
├── NSTableView-NoodleExtensions.m
├── NSTimer-NoodleExtensions.h
├── NSTimer-NoodleExtensions.m
├── NSWindow-NoodleEffects.h
├── NSWindow-NoodleEffects.m
├── NoodleCustomImageRep.h
├── NoodleCustomImageRep.m
├── NoodleGlue.h
├── NoodleGlue.m
├── NoodleIPhoneTableView.h
├── NoodleIPhoneTableView.m
├── NoodleKit.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── NoodleKit_Prefix.pch
├── NoodleLineNumberMarker.h
├── NoodleLineNumberMarker.m
├── NoodleLineNumberView.h
├── NoodleLineNumberView.m
├── NoodleTableView.h
├── NoodleTableView.m
├── README.md
└── version.plist
/English.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/Examples/ImageLab/Credits.rtf:
--------------------------------------------------------------------------------
1 | Read Me.rtf
--------------------------------------------------------------------------------
/Examples/ImageLab/ImageLab-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | APPL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 | LSMinimumSystemVersion
22 | ${MACOSX_DEPLOYMENT_TARGET}
23 | NSMainNibFile
24 | MainMenu
25 | NSPrincipalClass
26 | NSApplication
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Examples/ImageLab/ImageLabAppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // ImageLabAppDelegate.h
3 | // ImageLab
4 | //
5 | // Created by Paul Kim on 3/20/11.
6 | // Copyright 2011 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import
30 |
31 | @interface ImageLabAppDelegate : NSObject
32 | {
33 | IBOutlet NSImageView *imageView;
34 | IBOutlet NSTextField *timeLabel;
35 | IBOutlet NSButton *recacheIndicator;
36 | NSImage *testImage;
37 | NSWindow *window;
38 | }
39 |
40 | @property (assign) IBOutlet NSWindow *window;
41 |
42 | - (IBAction)switchImage:sender;
43 | - (IBAction)redraw:sender;
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/Examples/ImageLab/ImageLabAppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // ImageLabAppDelegate.m
3 | // ImageLab
4 | //
5 | // Created by Paul Kim on 3/20/11.
6 | // Copyright 2011-2012 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import "ImageLabAppDelegate.h"
30 | #import "NoodleCustomImageRep.h"
31 | #import
32 |
33 | @implementation ImageLabAppDelegate
34 |
35 | @synthesize window;
36 |
37 | - (NSImage *)lockFocusImage
38 | {
39 | NSImage *image;
40 | NSSize size;
41 | CGFloat diameter;
42 |
43 | image = [[testImage copy] autorelease];
44 |
45 | size = [image size];
46 | diameter = size.width / 2.0;
47 |
48 | [image lockFocus];
49 |
50 | [[NSColor blackColor] set];
51 | [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(diameter / 2.0, diameter / 2.0, diameter, diameter)] fill];
52 |
53 | [image unlockFocus];
54 |
55 | return image;
56 | }
57 |
58 | - (NSImage *)customRepImage
59 | {
60 | NoodleCustomImageRep *rep;
61 | NSSize size;
62 | NSImage *image;
63 |
64 | size = [testImage size];
65 |
66 | rep = [NoodleCustomImageRep imageRepWithDrawBlock:
67 | ^(NoodleCustomImageRep *blockRep)
68 | {
69 | NSSize repSize;
70 | CGFloat diameter;
71 |
72 | repSize = [blockRep size];
73 | diameter = repSize.width / 2.0;
74 |
75 | [testImage drawInRect:NSMakeRect(0.0, 0.0, size.width, size.height) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
76 |
77 | [[NSColor blackColor] set];
78 | [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(diameter / 2.0, diameter / 2.0, diameter, diameter)] fill];
79 |
80 | [recacheIndicator setImage:[[[NSImage imageNamed:NSImageNameStatusAvailable] copy] autorelease]];
81 | }];
82 | [rep setSize:size];
83 | image = [[[NSImage alloc] initWithSize:size] autorelease];
84 | [image addRepresentation:rep];
85 |
86 | return image;
87 | }
88 |
89 | - (NSImage *)lockFocusDrawnImage
90 | {
91 | NSImage *image;
92 | NSSize size;
93 |
94 | size = NSMakeSize(10.0, 10.0);
95 |
96 | image = [[[NSImage alloc] initWithSize:size] autorelease];
97 |
98 | [image lockFocus];
99 |
100 | [[NSGraphicsContext currentContext] setShouldAntialias:NO];
101 |
102 | [[NSColor blueColor] set];
103 | NSRectFill(NSMakeRect(1.0, 1.0, 8.0, 8.0));
104 |
105 | [image unlockFocus];
106 | return image;
107 | }
108 |
109 | - (NSImage *)customRepDrawnImage
110 | {
111 | NoodleCustomImageRep *rep;
112 | NSSize size;
113 | NSImage *image;
114 |
115 | size = NSMakeSize(10.0, 10.0);
116 |
117 | rep = [NoodleCustomImageRep imageRepWithDrawBlock:
118 | ^(NoodleCustomImageRep *blockRep)
119 | {
120 | [[NSColor blueColor] set];
121 | NSRectFill(NSMakeRect(1.0, 1.0, 8.0, 8.0));
122 |
123 | [recacheIndicator setImage:[[[NSImage imageNamed:NSImageNameStatusAvailable] copy] autorelease]];
124 | }];
125 | [rep setSize:size];
126 | image = [[[NSImage alloc] initWithSize:size] autorelease];
127 | [image addRepresentation:rep];
128 |
129 | return image;
130 | }
131 |
132 | - (NSImage *)coreImageTIFFRep
133 | {
134 | NSImage *image;
135 | NSSize size;
136 | CIImage *input, *output;
137 | CIFilter *filter;
138 | NSCIImageRep *rep;
139 | CGRect extent;
140 | CGAffineTransform transform;
141 |
142 | size = [testImage size];
143 |
144 | input = [CIImage imageWithData:[testImage TIFFRepresentation]];
145 | filter = [CIFilter filterWithName:@"CIPointillize" keysAndValues:
146 | @"inputImage", input,
147 | @"inputRadius", [NSNumber numberWithFloat:(float)(size.width / 10.0)],
148 | @"inputCenter", [CIVector vectorWithX:size.width / 2.0 Y:size.height / 2.0],
149 | nil];
150 | output = [filter valueForKey:@"outputImage"];
151 |
152 | extent = [output extent];
153 | transform = CGAffineTransformMakeScale(size.width / extent.size.width, size.height / extent.size.height);
154 | transform = CGAffineTransformTranslate(transform, -extent.origin.x, -extent.origin.y);
155 | output = [output imageByApplyingTransform:transform];
156 |
157 | image = [[[NSImage alloc] initWithSize:size] autorelease];
158 | rep = [NSCIImageRep imageRepWithCIImage:output];
159 | [rep setSize:size];
160 | [image addRepresentation:rep];
161 |
162 | return image;
163 | }
164 |
165 | - (NSImage *)coreImageCustomImageRep
166 | {
167 | NoodleCustomImageRep *rep;
168 | NSSize size;
169 | NSImage *image;
170 | __block id label;
171 |
172 | label = timeLabel;
173 |
174 | size = [testImage size];
175 |
176 | rep = [NoodleCustomImageRep imageRepWithDrawBlock:
177 | ^(NoodleCustomImageRep *blockRep)
178 | {
179 | CGImageRef cgImage;
180 | CIImage *input, *output;
181 | CIFilter *filter;
182 | NSRect rect;
183 |
184 | rect.origin = NSMakePoint(0.0, 0.0);
185 | rect.size = [blockRep size];
186 |
187 | cgImage = [testImage CGImageForProposedRect:&rect
188 | context:[NSGraphicsContext currentContext]
189 | hints:nil];
190 | input = [CIImage imageWithCGImage:cgImage];
191 | filter = [CIFilter filterWithName:@"CIPointillize" keysAndValues:
192 | @"inputImage", input,
193 | @"inputRadius", [NSNumber numberWithFloat:(float)(NSWidth(rect) / 10.0)],
194 | @"inputCenter", [CIVector vectorWithX:NSWidth(rect) / 2.0 Y:NSHeight(rect) / 2.0],
195 | nil];
196 | output = [filter valueForKey:@"outputImage"];
197 |
198 | [output drawInRect:rect fromRect:NSRectFromCGRect([output extent]) operation:NSCompositeCopy fraction:1.0];
199 |
200 | [recacheIndicator setImage:[[[NSImage imageNamed:NSImageNameStatusAvailable] copy] autorelease]];
201 | }];
202 |
203 | [rep setSize:size];
204 | image = [[[NSImage alloc] initWithSize:size] autorelease];
205 | [image addRepresentation:rep];
206 |
207 | return image;
208 | }
209 |
210 |
211 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
212 | {
213 | testImage = [[NSImage imageNamed:@"test"] copy];
214 | [recacheIndicator setImage:[[[NSImage imageNamed:NSImageNameStatusNone] copy] autorelease]];
215 | }
216 |
217 | - (IBAction)switchImage:(id)sender
218 | {
219 | NSInteger tag;
220 | NSImage *image;
221 |
222 | tag = [sender selectedTag];
223 |
224 | image = nil;
225 | switch (tag)
226 | {
227 | case 0:
228 | image = testImage;
229 | break;
230 | case 1:
231 | image = [self lockFocusImage];
232 | break;
233 | case 2:
234 | image = [self customRepImage];
235 | break;
236 | case 3:
237 | image = [self lockFocusDrawnImage];
238 | break;
239 | case 4:
240 | image = [self customRepDrawnImage];
241 | break;
242 | case 5:
243 | image = [self coreImageTIFFRep];
244 | break;
245 | case 6:
246 | image = [self coreImageCustomImageRep];
247 | break;
248 | }
249 | [imageView setObjectValue:image];
250 | }
251 |
252 | - (IBAction)redraw:sender
253 | {
254 | [imageView display];
255 | }
256 |
257 | @end
258 |
--------------------------------------------------------------------------------
/Examples/ImageLab/Read Me.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Monaco;}
3 | {\colortbl;\red255\green255\blue255;}
4 | {\*\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}
5 | {\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}}
6 | {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}}
7 | \margl1440\margr1440\vieww19360\viewh15600\viewkind0
8 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
9 |
10 | \f0\b\fs24 \cf0 ImageLab
11 | \b0 \
12 | \
13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
14 | \cf0 \
15 | This is a sample program that is a companion piece to my blog article on NSImage here: {\field{\*\fldinst{HYPERLINK "http://www.noodlesoft.com/blog/2011/04/15/the-proper-care-and-feeding-of-nsimage"}}{\fldrslt http://www.noodlesoft.com/blog/2011/04/15/the-proper-care-and-feeding-of-nsimage}}\
16 | \
17 | This program shows different ways, both good and bad, of using NSImage. Please see the companion article as it goes into much more detail. This project also demonstrates use of NoodleCustomImageRep.\
18 | \
19 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
20 |
21 | \b \cf0 Notes
22 | \b0 \
23 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
24 | \ls1\ilvl0\cf0 \
25 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
26 | \cf0 \
27 | \
28 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
29 |
30 | \b \cf0 Possible Improvements\
31 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
32 |
33 | \b0 \cf0 \
34 | \
35 | \
36 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
37 |
38 | \b \cf0 Contact
39 | \b0 \
40 | \
41 | 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.\
42 | \
43 | \
44 |
45 | \b License
46 | \b0 \
47 | \
48 | I am releasing this under the MIT license.\
49 | \
50 | ____________________________________\
51 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
52 |
53 | \f1 \cf0 Copyright (c) 2011 Noodlesoft, LLC. All Rights Reserved.\
54 | \
55 | Permission is hereby granted, free of charge, to any person\
56 | obtaining a copy of this software and associated documentation\
57 | files (the "Software"), to deal in the Software without\
58 | restriction, including without limitation the rights to use,\
59 | copy, modify, merge, publish, distribute, sublicense, and/or sell\
60 | copies of the Software, and to permit persons to whom the\
61 | Software is furnished to do so, subject to the following\
62 | conditions:\
63 | \
64 | The above copyright notice and this permission notice shall be\
65 | included in all copies or substantial portions of the Software.\
66 | \
67 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\
68 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\
69 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\
70 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\
71 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\
72 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\
73 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\
74 | OTHER DEALINGS IN THE SOFTWARE.\
75 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
76 | \ls2\ilvl0
77 | \f0\b \cf0 \
78 | }
--------------------------------------------------------------------------------
/Examples/ImageLab/TimedImageView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TimedImageView.h
3 | // ImageLab
4 | //
5 | // Created by Paul Kim on 3/20/11.
6 | // Copyright 2011 Noodlesoft, LLC. All rights reserved.
7 | //
8 | //
9 | // Permission is hereby granted, free of charge, to any person
10 | // obtaining a copy of this software and associated documentation
11 | // files (the "Software"), to deal in the Software without
12 | // restriction, including without limitation the rights to use,
13 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the
15 | // Software is furnished to do so, subject to the following
16 | // conditions:
17 | //
18 | // The above copyright notice and this permission notice shall be
19 | // included in all copies or substantial portions of the Software.
20 | //
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 | // OTHER DEALINGS IN THE SOFTWARE.
29 |
30 | #import
31 |
32 |
33 | @interface TimedImageView : NSImageView
34 | {
35 | IBOutlet id label;
36 | IBOutlet id redrawIndicator;
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/Examples/ImageLab/TimedImageView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TimedImageView.m
3 | // ImageLab
4 | //
5 | // Created by Paul Kim on 3/20/11.
6 | // Copyright 2011 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import "TimedImageView.h"
30 |
31 |
32 | @implementation TimedImageView
33 |
34 | - (void)updateLabel:(NSTimeInterval)time
35 | {
36 | [label setStringValue:[NSString stringWithFormat:@"%.2f ms", time]];
37 | }
38 |
39 | - (void)drawRect:(NSRect)rect
40 | {
41 | NSTimeInterval time, diff;
42 |
43 | [redrawIndicator setImage:[[[NSImage imageNamed:NSImageNameStatusNone] copy] autorelease]];
44 |
45 | time = [NSDate timeIntervalSinceReferenceDate];
46 |
47 | [super drawRect:rect];
48 |
49 | diff = [NSDate timeIntervalSinceReferenceDate] - time;
50 |
51 | [self updateLabel:diff * 1000];
52 | }
53 |
54 | @end
55 |
--------------------------------------------------------------------------------
/Examples/ImageLab/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // ImageLab
4 | //
5 | // Created by Paul Kim on 3/20/11.
6 | // Copyright 2011 Noodlesoft, LLC. 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 |
--------------------------------------------------------------------------------
/Examples/ImageLab/test.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrNoodle/NoodleKit/b124f51a9e89778a4c9c59b900f4a7660dcfa650/Examples/ImageLab/test.tiff
--------------------------------------------------------------------------------
/Examples/LineNumberView/Controller.h:
--------------------------------------------------------------------------------
1 | //
2 | // Controller.h
3 | // Line View Test
4 | //
5 | // Created by Paul Kim on 9/28/08.
6 | // Copyright (c) 2008 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 | //
29 |
30 | #import
31 |
32 | @class NoodleLineNumberView;
33 |
34 | @interface Controller : NSObject
35 | {
36 | IBOutlet NSScrollView *scrollView;
37 | IBOutlet NSTextView *scriptView;
38 | NoodleLineNumberView *lineNumberView;
39 | }
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/Examples/LineNumberView/Controller.m:
--------------------------------------------------------------------------------
1 | //
2 | // Controller.m
3 | // Line View Test
4 | //
5 | // Created by Paul Kim on 9/28/08.
6 | // Copyright (c) 2008 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 | //
29 |
30 | #import "Controller.h"
31 | #import "NoodleLineNumberView.h"
32 | #import "NoodleLineNumberMarker.h"
33 | #import "MarkerLineNumberView.h"
34 |
35 | @implementation Controller
36 |
37 |
38 | - (void)awakeFromNib
39 | {
40 | lineNumberView = [[MarkerLineNumberView alloc] initWithScrollView:scrollView];
41 | [scrollView setVerticalRulerView:lineNumberView];
42 | [scrollView setHasHorizontalRuler:NO];
43 | [scrollView setHasVerticalRuler:YES];
44 | [scrollView setRulersVisible:YES];
45 |
46 | [scriptView setFont:[NSFont userFixedPitchFontOfSize:[NSFont smallSystemFontSize]]];
47 | }
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/Examples/LineNumberView/Credits.rtf:
--------------------------------------------------------------------------------
1 | Read Me.rtf
--------------------------------------------------------------------------------
/Examples/LineNumberView/English.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrNoodle/NoodleKit/b124f51a9e89778a4c9c59b900f4a7660dcfa650/Examples/LineNumberView/English.lproj/InfoPlist.strings
--------------------------------------------------------------------------------
/Examples/LineNumberView/LineNumberView-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.noodlesoft.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | APPL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 | LSMinimumSystemVersion
22 | ${MACOSX_DEPLOYMENT_TARGET}
23 | NSMainNibFile
24 | MainMenu
25 | NSPrincipalClass
26 | NSApplication
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Examples/LineNumberView/MarkerLineNumberView.h:
--------------------------------------------------------------------------------
1 | //
2 | // MarkerTextView.h
3 | // Line View Test
4 | //
5 | // Created by Paul Kim on 10/4/08.
6 | // Copyright (c) 2008 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 | //
29 |
30 | #import
31 | #import "NoodleLineNumberView.h"
32 |
33 | @interface MarkerLineNumberView : NoodleLineNumberView
34 | {
35 | NSImage *markerImage;
36 | }
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/Examples/LineNumberView/MarkerLineNumberView.m:
--------------------------------------------------------------------------------
1 | //
2 | // MarkerTextView.m
3 | // Line View Test
4 | //
5 | // Created by Paul Kim on 10/4/08.
6 | // Copyright (c) 2008 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 | //
29 |
30 | #import "MarkerLineNumberView.h"
31 | #import "NoodleLineNumberMarker.h"
32 |
33 | #define CORNER_RADIUS 3.0
34 | #define MARKER_HEIGHT 13.0
35 |
36 | @implementation MarkerLineNumberView
37 |
38 | - (void)dealloc
39 | {
40 | [markerImage release];
41 |
42 | [super dealloc];
43 | }
44 |
45 | - (void)setRuleThickness:(CGFloat)thickness
46 | {
47 | [super setRuleThickness:thickness];
48 |
49 | // Overridden to reset the size of the marker image forcing it to redraw with the new width.
50 | // If doing this in a non-subclass of NoodleLineNumberView, you can set it to post frame
51 | // notifications and listen for them.
52 | [markerImage setSize:NSMakeSize(thickness, MARKER_HEIGHT)];
53 | }
54 |
55 | - (void)drawMarkerImageIntoRep:(id)rep
56 | {
57 | NSBezierPath *path;
58 | NSRect rect;
59 |
60 | rect = NSMakeRect(1.0, 2.0, [rep size].width - 2.0, [rep size].height - 3.0);
61 |
62 | path = [NSBezierPath bezierPath];
63 | [path moveToPoint:NSMakePoint(NSMaxX(rect), NSMinY(rect) + NSHeight(rect) / 2)];
64 | [path lineToPoint:NSMakePoint(NSMaxX(rect) - 5.0, NSMaxY(rect))];
65 |
66 | [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect) + CORNER_RADIUS, NSMaxY(rect) - CORNER_RADIUS) radius:CORNER_RADIUS startAngle:90 endAngle:180];
67 |
68 | [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect) + CORNER_RADIUS, NSMinY(rect) + CORNER_RADIUS) radius:CORNER_RADIUS startAngle:180 endAngle:270];
69 | [path lineToPoint:NSMakePoint(NSMaxX(rect) - 5.0, NSMinY(rect))];
70 | [path closePath];
71 |
72 | [[NSColor colorWithCalibratedRed:0.003 green:0.56 blue:0.85 alpha:1.0] set];
73 | [path fill];
74 |
75 | [[NSColor colorWithCalibratedRed:0 green:0.44 blue:0.8 alpha:1.0] set];
76 |
77 | [path setLineWidth:2.0];
78 | [path stroke];
79 | }
80 |
81 | - (NSImage *)markerImageWithSize:(NSSize)size
82 | {
83 | if (markerImage == nil)
84 | {
85 | NSCustomImageRep *rep;
86 |
87 | markerImage = [[NSImage alloc] initWithSize:size];
88 | rep = [[NSCustomImageRep alloc] initWithDrawSelector:@selector(drawMarkerImageIntoRep:) delegate:self];
89 | [rep setSize:size];
90 | [markerImage addRepresentation:rep];
91 | [rep release];
92 | }
93 | return markerImage;
94 | }
95 |
96 | - (void)mouseDown:(NSEvent *)theEvent
97 | {
98 | NSPoint location;
99 | NSUInteger line;
100 |
101 | location = [self convertPoint:[theEvent locationInWindow] fromView:nil];
102 | line = [self lineNumberForLocation:location.y];
103 |
104 | if (line != NSNotFound)
105 | {
106 | NoodleLineNumberMarker *marker;
107 |
108 | marker = [self markerAtLine:line];
109 |
110 | if (marker != nil)
111 | {
112 | [self removeMarker:marker];
113 | }
114 | else
115 | {
116 | marker = [[NoodleLineNumberMarker alloc] initWithRulerView:self
117 | lineNumber:line
118 | image:[self markerImageWithSize:NSMakeSize([self ruleThickness], MARKER_HEIGHT)]
119 | imageOrigin:NSMakePoint(0, MARKER_HEIGHT / 2)];
120 | [self addMarker:marker];
121 | [marker release];
122 | }
123 | [self setNeedsDisplay:YES];
124 | }
125 | }
126 |
127 | @end
128 |
--------------------------------------------------------------------------------
/Examples/LineNumberView/Read Me.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf470
2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Monaco;}
3 | {\colortbl;\red255\green255\blue255;}
4 | {\*\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}
5 | {\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}
6 | {\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}
7 | {\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}
8 | {\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}
9 | {\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}
10 | {\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}}
11 | {\*\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}}
12 | \vieww16040\viewh15820\viewkind0
13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
14 |
15 | \f0\b\fs24 \cf0 Line View Test
16 | \b0 \
17 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
18 | \cf0 \
19 | This is a sample project and test harness for NoodleLineNumberView.\
20 | \
21 | NoodleLineNumberView is an NSRulerView subclass that will show line numbers when the document view of the scrollview is an NSTextView. It numbers logical lines, not visual ones. NoodleLineNumberMarker works in tandem with NoodleLineNumberView to display markers at specific lines.\
22 | \
23 | A discussion of this project can be found at: {\field{\*\fldinst{HYPERLINK "http://www.noodlesoft.com/blog/2008/10/05/displaying-line-numbers-with-nstextview/"}}{\fldrslt http://www.noodlesoft.com/blog/2008/10/05/displaying-line-numbers-with-nstextview/}}\
24 | \
25 | Notes:\
26 | \
27 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural
28 | \ls1\ilvl0\cf0 {\listtext \'95 }The view will expand it's width to accommodate the widths of the labels as needed.\
29 | {\listtext \'95 }The included subclass (MarkerLineNumberView) shows how to deal with markers. It also shows how to use an NSCustomImageRep to do the drawing. This allows you to reset the size of the image and have the drawing adjust as needed (this happens if the line number view changes width because the line numbers gained or lost a digit). If you decide to implement most of this stuff in an external class (not a subclass), you can set the line number view to post frame changed notifications and listen for them.\
30 | {\listtext \'95 }Note that markers are tied to numerical lines, not semantic ones. So, if you have a marker at line 50 and insert a new line at line 49, the marker will not shift to line 51 to point at the same line of text but will stay at line 50 pointing at whatever text is there now. Contrast with XCode where the markers move with insertions and deletions of lines (at least as best as it can). This is logic that you'll have to supply yourself.\
31 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
32 | \cf0 \
33 | To integrate NoodleLineNumberView, just create one and set it as the vertical ruler of the scrollview. Depending on the order of operations, you may need to set the client view of the NoodleLineNumberView to the NSTextView manually.\
34 | \
35 | This project actually uses a subclass of NoodleLineNumberView called MarkerLineNumberView. This class shows how one can integrate adding markers. Just click in the line number view to toggle a marker. It's more of an example than a reusable class since your markers may look different and you may have a different UI for adding them. Nonetheless, it shows the basics of how to do it.\
36 | \
37 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
38 |
39 | \b \cf0 Possible Performance Improvements\
40 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
41 |
42 | \b0 \cf0 \
43 | For performance, NoodleLineView keeps an array of the character indices for the start of each line. This gets recalculated whenever the text changes but at least it's cached for redisplays (such as scrolling around). It also only redraw the labels for the lines that are showing.\
44 | \
45 | It seems peppy enough for me, testing on a Powerbook 12" (G4) on Leopard. If you feel the need to optimize it further (I advise you Shark it first instead of making assumptions, though), here are some areas that could be improved:\
46 | \
47 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural
48 | \ls2\ilvl0\cf0 {\listtext \'95 }\strike \strikec0 Right now, the character indices are recalculated every time the text changes. I only listen for a
49 | \f1 NSTextDidChangeNotification
50 | \f0 which is pretty coarse as there's no notion of what changed. You need to hook in deeper to get finer grained details of what characters were affected and see if the line indices need to be recalculated at all (check for whether a line ending was added or removed) or what particular lines should be recalculated (if a line was changed, only recalculate the lines after it).\strike0\striked0 (4/12/2012) This is now implemented. Checks the edited range and only recalculates the lines from that point onwards.\
51 | {\listtext \'95 }The layout coordinates of each line can also be cached. If you tie into NSLayoutManager's delegate methods, you can find out when the layout has been invalidated. This would be helpful for cases where the view is scrolled or otherwise redisplayed without having the layout changed. Invalidations would happen in cases like when the view is resized in which case, you recalculate and recache. I have my doubts about whether this will save much but Shark it and find out.\
52 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
53 | \cf0 \
54 | The first two were not done because I wanted this subclass to be self-contained and not interfere with any delegates that may already be in place.\
55 | \
56 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
57 |
58 | \b \cf0 Contact
59 | \b0 \
60 | \
61 | 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.\
62 | \
63 | \
64 |
65 | \b License
66 | \b0 \
67 | \
68 | I am releasing this under the MIT license.\
69 | \
70 | ____________________________________\
71 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
72 |
73 | \f1 \cf0 Copyright (c) 2008-2009 Noodlesoft, LLC. All Rights Reserved.\
74 | \
75 | Permission is hereby granted, free of charge, to any person\
76 | obtaining a copy of this software and associated documentation\
77 | files (the "Software"), to deal in the Software without\
78 | restriction, including without limitation the rights to use,\
79 | copy, modify, merge, publish, distribute, sublicense, and/or sell\
80 | copies of the Software, and to permit persons to whom the\
81 | Software is furnished to do so, subject to the following\
82 | conditions:\
83 | \
84 | The above copyright notice and this permission notice shall be\
85 | included in all copies or substantial portions of the Software.\
86 | \
87 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\
88 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\
89 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\
90 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\
91 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\
92 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\
93 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\
94 | OTHER DEALINGS IN THE SOFTWARE.\
95 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural
96 | \ls3\ilvl0
97 | \f0\b \cf0 \
98 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
99 | \cf0 \
100 | Changelog
101 | \b0 \
102 | \
103 | 0.4.1:\
104 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural
105 | \ls4\ilvl0\cf0 {\listtext \'95 }Fixed display glitch when scrolling after the line number view resizes when linked against/running on 10.4. Apparently, NSRulerView's
106 | \f1 setRuleThickness:
107 | \f0 does not like non-integral values.\
108 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
109 | \cf0 \
110 | 0.4:\
111 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural
112 | \ls5\ilvl0\cf0 {\listtext \'95 }Changed default font to use label font. Not sure if semantically, that's what XCode uses but it matches for the time being.\
113 | {\listtext \'95 }Fixed display bugs introduced in 0.3 when line number view resizes itself.\
114 | {\listtext \'95 }Added methods for setting various colors. The alternate text color is the color used by the line number label when a marker is drawn under it.\
115 | {\listtext \'95 }Miscellaneous tweaks.\
116 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
117 | \cf0 \
118 | 0.3:\
119 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural
120 | \ls6\ilvl0\cf0 {\listtext \'95 }Now listens for NSTextStorage's NSTextStorageDidProcessEditingNotification instead of NSTextDidChangeNotification. The former includes programmatic changes to the text.\
121 | {\listtext \'95 }Was not taking text storage's inset into account when lining up labels. OSAScriptView, for one, had a non-zero inset. Should be fixed now.\
122 | {\listtext \'95 }Calculates lines lazily allowing multiple programmatic text changes to be batched up.\
123 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
124 | \cf0 \
125 | 0.2:\
126 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\pardirnatural
127 | \ls7\ilvl0\cf0 {\listtext \'95 }Initial public release\
128 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
129 | \cf0 \
130 | }
--------------------------------------------------------------------------------
/Examples/LineNumberView/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // Line View Test
4 | //
5 | // Created by Paul Kim on 10/5/08.
6 | // Copyright Noodlesoft, LLC 2008. 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 |
--------------------------------------------------------------------------------
/Examples/ModalResponder/Controller.h:
--------------------------------------------------------------------------------
1 | //
2 | // Controller.h
3 | // ModalResponderTest
4 | //
5 | // Created by Paul Kim on 3/6/08.
6 | // Copyright 2008-2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 |
12 | @interface Controller : NSObject
13 | {
14 | IBOutlet id window;
15 | IBOutlet id alert;
16 | IBOutlet id field;
17 | }
18 |
19 | - (IBAction)doModal:sender;
20 | - (IBAction)doSheet:sender;
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/Examples/ModalResponder/Controller.m:
--------------------------------------------------------------------------------
1 | //
2 | // Controller.m
3 | // ModalResponderTest
4 | //
5 | // Created by Paul Kim on 3/6/08.
6 | // Copyright 2008-2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 |
9 | #import "Controller.h"
10 |
11 |
12 | @implementation Controller
13 |
14 | - (IBAction)doModal:sender
15 | {
16 | NSInteger returnCode;
17 |
18 | [field setStringValue:@""];
19 |
20 | returnCode = [NSApp runModalForWindow:alert];
21 |
22 | if (returnCode == NSOKButton)
23 | {
24 | [field setStringValue:@"OK!"];
25 | }
26 | else
27 | {
28 | [field setStringValue:@"Cancel"];
29 | }
30 | }
31 |
32 |
33 | - (IBAction)doSheet:sender
34 | {
35 | [field setStringValue:@""];
36 |
37 | [NSApp beginSheet:alert modalForWindow:window modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:nil];
38 | //PENDING
39 | /*
40 | NSLog(@"ALERT: %@", alert);
41 | NSLog(@"PARENT: %@", [alert parentWindow]);
42 | NSLog(@"SHEET: %@", [[alert parentWindow] attachedSheet]);
43 | NSLog(@"WINDOW: %@", window);
44 | NSLog(@"CHILD: %@", [window childWindows]);
45 | NSLog(@"ALERT CHILD: %@", [alert childWindows]);
46 | NSLog(@"ATTACHED: %@", [window attachedSheet]);
47 | */
48 | }
49 |
50 | - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
51 | {
52 | if (returnCode == NSOKButton)
53 | {
54 | [field setStringValue:@"OK!"];
55 | }
56 | else
57 | {
58 | [field setStringValue:@"Cancel"];
59 | }
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/Examples/ModalResponder/Credits.rtf:
--------------------------------------------------------------------------------
1 | Read Me.rtf
--------------------------------------------------------------------------------
/Examples/ModalResponder/English.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrNoodle/NoodleKit/b124f51a9e89778a4c9c59b900f4a7660dcfa650/Examples/ModalResponder/English.lproj/InfoPlist.strings
--------------------------------------------------------------------------------
/Examples/ModalResponder/ModalResponder-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.noodlesoft.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | APPL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 | LSMinimumSystemVersion
22 | ${MACOSX_DEPLOYMENT_TARGET}
23 | NSMainNibFile
24 | MainMenu
25 | NSPrincipalClass
26 | NSApplication
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Examples/ModalResponder/Read Me.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf110
2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Monaco;\f2\fmodern\fcharset0 Courier;
3 | }
4 | {\colortbl;\red255\green255\blue255;}
5 | \margl1440\margr1440\vieww15820\viewh15260\viewkind0
6 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural\pardirnatural
7 |
8 | \f0\b\fs24 \cf0 ModalResponder
9 | \b0 \
10 | \
11 | This is a simple demo of an NSResponder category which deals with modal windows and sheets. If you are creating a modal window or sheet that has "OK" and "Cancel" buttons, you most likely end up writing some glue method that just orders the window out and returns a code. This category makes it so that all you have to do is hook up the buttons to either the
12 | \f1\fs22 -confirmModal:
13 | \f0\fs24 or
14 | \f1\fs22 -cancelModal:
15 | \f0\fs24 actions on the First Responder in Interface Builder. The methods will set a return code of NSOKButton and NSCancelButton respectively. No extra glue though you may have to add the methods to NSResponder in IB.\
16 | \
17 | For more details check out the related blog post at http://www.noodlesoft.com/blog/2008/03/10/modal-glue/\
18 | \
19 | Enjoy.\
20 | \
21 | Paul Kim\
22 | http://www.noodlesoft.com\
23 | \
24 | ========================================\
25 | \
26 | This code is provided for free under the terms of the MIT license:\
27 | \
28 | \pard\pardeftab720\sl360\sa180\ql\qnatural
29 |
30 | \f2 \cf0 Copyright (c) 2008-2009 Noodlesoft, LLC. All Rights Reserved\
31 | Permission is hereby granted, free of charge, to any person obtaining a copy\uc0\u8232 of this software and associated documentation files (the "Software"), to deal\u8232 in the Software without restriction, including without limitation the rights\u8232 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\u8232 copies of the Software, and to permit persons to whom the Software is\u8232 furnished to do so, subject to the following conditions:\
32 | The above copyright notice and this permission notice shall be included in\uc0\u8232 all copies or substantial portions of the Software.\
33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\uc0\u8232 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\u8232 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\u8232 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\u8232 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\u8232 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\u8232 THE SOFTWARE.\
34 | }
--------------------------------------------------------------------------------
/Examples/ModalResponder/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // ModalResponderTest
4 | //
5 | // Created by Paul Kim on 3/6/08.
6 | // Copyright __MyCompanyName__ 2008. 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 |
--------------------------------------------------------------------------------
/Examples/StickyRowTableView Revue/Controller.h:
--------------------------------------------------------------------------------
1 | //
2 | // Controller.h
3 | //
4 | // Created by Paul Kim on 8/21/09.
5 | // Copyright 2009-2012 Noodlesoft, LLC. All rights reserved.
6 | //
7 | // Permission is hereby granted, free of charge, to any person
8 | // obtaining a copy of this software and associated documentation
9 | // files (the "Software"), to deal in the Software without
10 | // restriction, including without limitation the rights to use,
11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the
13 | // Software is furnished to do so, subject to the following
14 | // conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be
17 | // included in all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 | // OTHER DEALINGS IN THE SOFTWARE.
27 | //
28 |
29 | #import
30 |
31 | @class NoodleTableView;
32 |
33 | @interface Controller : NSObject
34 | {
35 | IBOutlet NoodleTableView *_stickyRowTableView;
36 | IBOutlet NSTableView *_iPhoneTableView;
37 | NSMutableArray *_names;
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/Examples/StickyRowTableView Revue/Controller.m:
--------------------------------------------------------------------------------
1 | //
2 | // Controller.m
3 | //
4 | // Created by Paul Kim on 8/21/09.
5 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
6 | //
7 | // Permission is hereby granted, free of charge, to any person
8 | // obtaining a copy of this software and associated documentation
9 | // files (the "Software"), to deal in the Software without
10 | // restriction, including without limitation the rights to use,
11 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the
13 | // Software is furnished to do so, subject to the following
14 | // conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be
17 | // included in all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 | // OTHER DEALINGS IN THE SOFTWARE.
27 | //
28 |
29 | #import "Controller.h"
30 | #import "NoodleTableView.h"
31 |
32 | @implementation Controller
33 |
34 | - (void)awakeFromNib
35 | {
36 | NSString *fileContents;
37 | NSUInteger i, count;
38 | NSString *temp, *prefix, *currentPrefix;
39 | NSArray *words;
40 |
41 | fileContents = [NSString stringWithContentsOfFile:@"/usr/share/dict/propernames" usedEncoding:NULL error:NULL];
42 | words = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
43 |
44 | [self willChangeValueForKey:@"names"];
45 |
46 | words = [words sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
47 | _names = [[NSMutableArray alloc] init];
48 |
49 | count = [words count];
50 | currentPrefix = nil;
51 | for (i = 0; i < count; i++)
52 | {
53 | temp = [words objectAtIndex:i];
54 |
55 | if ([temp length] > 0)
56 | {
57 | prefix = [temp substringToIndex:1];
58 |
59 | if ((currentPrefix == nil) ||
60 | ([currentPrefix caseInsensitiveCompare:prefix] != NSOrderedSame))
61 | {
62 | currentPrefix = [prefix uppercaseString];
63 | [_names addObject:currentPrefix];
64 | }
65 | [_names addObject:temp];
66 | }
67 | }
68 |
69 | [_stickyRowTableView setShowsStickyRowHeader:YES];
70 | [_stickyRowTableView reloadData];
71 | [_iPhoneTableView reloadData];
72 | }
73 |
74 | - (BOOL)_isHeader:(NSInteger)rowIndex
75 | {
76 | return ((rowIndex == 0) ||
77 | [[[_names objectAtIndex:rowIndex] substringToIndex:1] caseInsensitiveCompare:[[_names objectAtIndex:rowIndex - 1] substringToIndex:1]] != NSOrderedSame);
78 | }
79 |
80 | #pragma mark NSTableDataSource methods
81 |
82 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
83 | {
84 | return [_names count];
85 | }
86 |
87 | - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
88 | {
89 | return [_names objectAtIndex:rowIndex];
90 | }
91 |
92 | - (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row
93 | {
94 | return [self _isHeader:row];
95 | }
96 |
97 | @end
98 |
--------------------------------------------------------------------------------
/Examples/StickyRowTableView Revue/Credits.rtf:
--------------------------------------------------------------------------------
1 | Read Me.rtf
--------------------------------------------------------------------------------
/Examples/StickyRowTableView Revue/English.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrNoodle/NoodleKit/b124f51a9e89778a4c9c59b900f4a7660dcfa650/Examples/StickyRowTableView Revue/English.lproj/InfoPlist.strings
--------------------------------------------------------------------------------
/Examples/StickyRowTableView Revue/Read Me.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf110
2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Monaco;}
3 | {\colortbl;\red255\green255\blue255;}
4 | {\*\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}
5 | {\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}
6 | {\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}
7 | {\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}
8 | {\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}}
9 | {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}{\listoverride\listid3\listoverridecount0\ls3}{\listoverride\listid4\listoverridecount0\ls4}{\listoverride\listid5\listoverridecount0\ls5}}
10 | \margl1440\margr1440\vieww19360\viewh17460\viewkind0
11 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
12 |
13 | \f0\b\fs24 \cf0 NoodleStickyRowTableView Test
14 | \b0 \
15 | version 0.18369\
16 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
17 | \cf0 \
18 | This is a sample project and test harness for the sticky row header feature of NoodleTableView.\
19 | \
20 | 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.\
21 | \
22 | 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
23 | \f1 -drawRect:
24 | \f0 and call
25 | \f1 -drawStickyRowHeader
26 | \f0 after your call to
27 | \f1 super
28 | \f0 . Look at the NoodleTableView class for details.\
29 | \
30 | This project also includes NoodleIPhoneTableView which overrides a bit more to simulate the look and feel of UITableView.\
31 | \
32 | 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}}\
33 | \
34 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
35 |
36 | \b \cf0 Notes
37 | \b0 \
38 | \
39 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
40 | \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.\
41 | {\listtext \'95 }The sticky row header is implemented using an NSButton. The row is drawn into an image that is set on the button. The button is adjusted as needed, including whether it is enabled and therefore able to accept mouse clicks.\
42 | {\listtext \'95 }To keep things in a category, the auxiliary button is put in the view hierarchy. It is tagged so that it can be retrieved later. This is further exploited where, for optimization purposes, the alternate image property of NSButton, which would otherwise go unused, is used as a cache.\
43 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
44 | \cf0 \
45 | \
46 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
47 |
48 | \b \cf0 Possible Improvements\
49 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
50 |
51 | \b0 \cf0 \
52 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
53 | \ls2\ilvl0\cf0 {\listtext \'95 }You can keep track of the last sticky row that was cached and reuse its image if it hasn't changed. I had actually implemented this but the performance gains were actually very minimal. For purposes of clarity, I took the code out but you can implement this if you need to eek out the last bit of performance.\
54 | {\listtext \'95 }The fade-in transition is position based (it fades in as you scroll down). It might be better to make it time-based.\
55 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
56 | \cf0 \
57 | \
58 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
59 |
60 | \b \cf0 Contact
61 | \b0 \
62 | \
63 | 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.\
64 | \
65 | \
66 |
67 | \b License
68 | \b0 \
69 | \
70 | I am releasing this under the MIT license.\
71 | \
72 | ____________________________________\
73 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
74 |
75 | \f1 \cf0 Copyright (c) 2009 Noodlesoft, LLC. All Rights Reserved.\
76 | \
77 | Permission is hereby granted, free of charge, to any person\
78 | obtaining a copy of this software and associated documentation\
79 | files (the "Software"), to deal in the Software without\
80 | restriction, including without limitation the rights to use,\
81 | copy, modify, merge, publish, distribute, sublicense, and/or sell\
82 | copies of the Software, and to permit persons to whom the\
83 | Software is furnished to do so, subject to the following\
84 | conditions:\
85 | \
86 | The above copyright notice and this permission notice shall be\
87 | included in all copies or substantial portions of the Software.\
88 | \
89 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\
90 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\
91 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\
92 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\
93 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\
94 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\
95 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\
96 | OTHER DEALINGS IN THE SOFTWARE.\
97 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
98 | \ls3\ilvl0
99 | \f0\b \cf0 \
100 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
101 | \cf0 \
102 | Changelog
103 | \b0 \
104 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
105 | \ls4\ilvl0\cf0 \
106 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
107 | \cf0 \
108 | 0.18369:\
109 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
110 | \ls5\ilvl0\cf0 {\listtext \'95 }Initial public release\
111 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
112 | \cf0 \
113 | }
--------------------------------------------------------------------------------
/Examples/StickyRowTableView Revue/StickyRowTableView Revue-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.noodlesoft.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | APPL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 | LSMinimumSystemVersion
22 | ${MACOSX_DEPLOYMENT_TARGET}
23 | NSMainNibFile
24 | MainMenu
25 | NSPrincipalClass
26 | NSApplication
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Examples/StickyRowTableView Revue/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // NoodleStickyRowTableViewTest
4 | //
5 | // Created by Paul Kim on 8/23/09.
6 | // Copyright Noodlesoft, LLC 2009. 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 |
--------------------------------------------------------------------------------
/Examples/TimerLab/Credits.rtf:
--------------------------------------------------------------------------------
1 | Read Me.rtf
--------------------------------------------------------------------------------
/Examples/TimerLab/Read Me.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf320
2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Monaco;}
3 | {\colortbl;\red255\green255\blue255;}
4 | {\*\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}
5 | {\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}
6 | {\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}}
7 | {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}{\listoverride\listid3\listoverridecount0\ls3}}
8 | \margl1440\margr1440\vieww19360\viewh17460\viewkind0
9 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
10 |
11 | \f0\b\fs24 \cf0 TimerLab
12 | \b0 \
13 | \
14 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
15 | \cf0 \
16 | This is a sample project and test harness for the my NSTimer category.\
17 | \
18 | Normally, if you create an NSTimer and time is suspended (putting the machine to sleep) or changed (setting the system clock), NSTimer will try and compensate. In a sense, the time given to NSTimer is relative. The problem with this is that sometimes when you tell a timer to fire on a specific date at a specific time, you want it to fire on that specific date at that specific time. Thus, this category was created.\
19 | \
20 | The basic approach is to store the original fire date of the timer and re-set it whenever the machine wakes or the system clock is changed. Since at least one new instance variable is needed, it makes sense to do an NSTimer subclass. Unfortunately, NSRunLoop doesn't seem to work with anything but NSTimer directly. I suspect it's calling some private methods on NSTimer that I'm not privy to.\
21 | \
22 | So instead, it's done as a category. By using Snow Leopard's "associative references" ({\field{\*\fldinst{HYPERLINK "http://developer.apple.com/mac/library/documentation/cocoa/conceptual/objectivec/articles/ocAssociativeReferences.html"}}{\fldrslt http://developer.apple.com/mac/library/documentation/cocoa/conceptual/objectivec/articles/ocAssociativeReferences.html}}), I can add the equivalent of instance variables to NSTimer. And since, I was mucking with NSTimer, I also went ahead and added support for blocks.\
23 | \
24 | The test program allows you to test the regular behavior and the newer "absolute" behavior side by side. Set a time and click "Start timer". Now, put your machine to sleep or mess with the system clock. You'll see the regular timer adjust while the "absolute" timer will stay put.\
25 | \
26 | A blog post on this can be found at: {\field{\*\fldinst{HYPERLINK "http://www.noodlesoft.com/blog/2010/07/01/playing-with-nstimer/"}}{\fldrslt http://www.noodlesoft.com/blog/2010/07/01/playing-with-nstimer/}}\
27 | \
28 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
29 |
30 | \b \cf0 Notes
31 | \b0 \
32 | \
33 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
34 | \ls1\ilvl0\cf0 {\listtext \'95 }Because of technical limitations of being a category, once you create an "absolute" timer, calling -setFireDate: may not work like how you'd want. If you need to change the fire date, create a new timer.\
35 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
36 | \cf0 \
37 | \
38 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
39 |
40 | \b \cf0 Possible Improvements\
41 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
42 |
43 | \b0 \cf0 \
44 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
45 | \ls2\ilvl0\cf0 {\listtext \'95 }Add support for repeating timers.\
46 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
47 | \cf0 \
48 | \
49 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
50 |
51 | \b \cf0 Contact
52 | \b0 \
53 | \
54 | 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.\
55 | \
56 | \
57 |
58 | \b License
59 | \b0 \
60 | \
61 | I am releasing this under the MIT license.\
62 | \
63 | ____________________________________\
64 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
65 |
66 | \f1 \cf0 Copyright (c) 2010 Noodlesoft, LLC. All Rights Reserved.\
67 | \
68 | Permission is hereby granted, free of charge, to any person\
69 | obtaining a copy of this software and associated documentation\
70 | files (the "Software"), to deal in the Software without\
71 | restriction, including without limitation the rights to use,\
72 | copy, modify, merge, publish, distribute, sublicense, and/or sell\
73 | copies of the Software, and to permit persons to whom the\
74 | Software is furnished to do so, subject to the following\
75 | conditions:\
76 | \
77 | The above copyright notice and this permission notice shall be\
78 | included in all copies or substantial portions of the Software.\
79 | \
80 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\
81 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\
82 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\
83 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\
84 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\
85 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\
86 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\
87 | OTHER DEALINGS IN THE SOFTWARE.\
88 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
89 | \ls3\ilvl0
90 | \f0\b \cf0 \
91 | }
--------------------------------------------------------------------------------
/Examples/TimerLab/TimerLab-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | APPL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 | LSMinimumSystemVersion
22 | ${MACOSX_DEPLOYMENT_TARGET}
23 | NSMainNibFile
24 | MainMenu
25 | NSPrincipalClass
26 | NSApplication
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Examples/TimerLab/TimerLabAppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // TimeLabAppDelegate.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 6/30/10.
6 | // Copyright 2010 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import
30 |
31 | @interface TimerLabAppDelegate : NSObject
32 | {
33 | IBOutlet NSWindow *_window;
34 | IBOutlet NSDatePicker *_picker;
35 | IBOutlet NSDatePicker *_datePicker;
36 | IBOutlet NSDatePicker *_timePicker;
37 | IBOutlet NSTextField *_initialDateField;
38 | IBOutlet NSTextField *_initialFireDateField;
39 | IBOutlet NSTextField *_currentFireDateField;
40 | IBOutlet NSTextField *_fireDateField;
41 |
42 | IBOutlet NSTextField *_regularInitialDateField;
43 | IBOutlet NSTextField *_regularInitialFireDateField;
44 | IBOutlet NSTextField *_regularCurrentFireDateField;
45 | IBOutlet NSTextField *_regularFireDateField;
46 |
47 |
48 | NSTimer *_absoluteTimer;
49 | NSTimer *_regularTimer;
50 | }
51 |
52 | @property (assign) IBOutlet NSWindow *window;
53 |
54 | - (IBAction)startTimer:(id)sender;
55 |
56 | - (IBAction)syncPickers:(id)sender;
57 |
58 | - (IBAction)refresh:(id)sender;
59 |
60 | @end
61 |
--------------------------------------------------------------------------------
/Examples/TimerLab/TimerLabAppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // TimeLabAppDelegate.m
3 | // TimerLab
4 | //
5 | // Created by Paul Kim on 6/30/10.
6 | // Copyright 2010 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import "TimerLabAppDelegate.h"
30 | #import
31 |
32 | @implementation TimerLabAppDelegate
33 |
34 | @synthesize window = _window;
35 |
36 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
37 | {
38 | [_picker setObjectValue:[NSDate date]];
39 | [self syncPickers:_picker];
40 |
41 | // We use -delayedRefresh: here instead of refresh: because NSTimer will be watching for these same notifications
42 | // and we can't guarantee the order in which the observers are notified (we want the refresh to happen after
43 | // the timer adjusts).
44 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(delayedRefresh:) name:NSSystemTimeZoneDidChangeNotification object:nil];
45 | [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(delayedRefresh:) name:NSWorkspaceDidWakeNotification object:nil];
46 | }
47 |
48 |
49 | - (IBAction)startTimer:(id)sender
50 | {
51 | NSDate *date;
52 |
53 | date = [NSDate date];
54 | [_initialDateField setObjectValue:date];
55 | [_regularInitialDateField setObjectValue:date];
56 |
57 | date = [_picker objectValue];
58 |
59 | [_initialFireDateField setObjectValue:date];
60 | [_regularInitialFireDateField setObjectValue:date];
61 |
62 | [_absoluteTimer invalidate];
63 | [_absoluteTimer release];
64 | _absoluteTimer = [[NSTimer alloc] initWithAbsoluteFireDate:date block:
65 | ^ (NSTimer *timer)
66 | {
67 | [_fireDateField setObjectValue:[NSDate date]];
68 | }];
69 |
70 | [_fireDateField setObjectValue:nil];
71 | [_currentFireDateField setObjectValue:[_absoluteTimer fireDate]];
72 |
73 | [[NSRunLoop currentRunLoop] addTimer:_absoluteTimer forMode:NSDefaultRunLoopMode];
74 |
75 | [_regularTimer invalidate];
76 | [_regularTimer release];
77 | _regularTimer = [[NSTimer alloc] initWithFireDate:date interval:0 repeats:NO block:
78 | ^ (NSTimer *timer)
79 | {
80 | [_regularFireDateField setObjectValue:[NSDate date]];
81 | }];
82 |
83 | [_regularFireDateField setObjectValue:nil];
84 | [_regularCurrentFireDateField setObjectValue:[_regularTimer fireDate]];
85 |
86 | [[NSRunLoop currentRunLoop] addTimer:_regularTimer forMode:NSDefaultRunLoopMode];
87 | }
88 |
89 | - (IBAction)syncPickers:(id)sender
90 | {
91 | NSDate *date;
92 |
93 | date = [sender objectValue];
94 | [_picker setObjectValue:date];
95 | [_datePicker setObjectValue:date];
96 | [_timePicker setObjectValue:date];
97 | }
98 |
99 | - (IBAction)refresh:(id)sender
100 | {
101 | [_currentFireDateField setObjectValue:[_absoluteTimer fireDate]];
102 | [_regularCurrentFireDateField setObjectValue:[_regularTimer fireDate]];
103 | }
104 |
105 | - (IBAction)delayedRefresh:(id)sender
106 | {
107 | [self performSelector:@selector(refresh:) withObject:nil afterDelay:1.0];
108 | }
109 |
110 |
111 | @end
112 |
--------------------------------------------------------------------------------
/Examples/TimerLab/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // TimerLab
4 | //
5 | // Created by Paul Kim on 6/30/10.
6 | // Copyright 2010 Noodlesoft, LLC. 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 |
--------------------------------------------------------------------------------
/Examples/Window Effects/Controller.h:
--------------------------------------------------------------------------------
1 | /* Controller */
2 |
3 | #import
4 |
5 | @interface Controller : NSObject
6 | {
7 | IBOutlet id _window;
8 | }
9 |
10 | - (IBAction)toggleWindow:sender;
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/Examples/Window Effects/Controller.m:
--------------------------------------------------------------------------------
1 | #import "Controller.h"
2 | #import "NSWindow-NoodleEffects.h"
3 |
4 | @implementation Controller
5 |
6 | - (IBAction)toggleWindow:sender
7 | {
8 | NSRect rect;
9 |
10 | rect = [sender convertRect:[sender bounds] toView:nil];
11 | rect.origin = [[sender window] convertBaseToScreen:rect.origin];
12 |
13 | if ([_window isVisible])
14 | {
15 | [_window zoomOffToRect:rect];
16 | }
17 | else
18 | {
19 | [_window zoomOnFromRect:rect];
20 | }
21 | }
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/Examples/Window Effects/English.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MrNoodle/NoodleKit/b124f51a9e89778a4c9c59b900f4a7660dcfa650/Examples/Window Effects/English.lproj/InfoPlist.strings
--------------------------------------------------------------------------------
/Examples/Window Effects/Window Effects-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.noodlesoft.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | APPL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 | LSMinimumSystemVersion
22 | ${MACOSX_DEPLOYMENT_TARGET}
23 | NSMainNibFile
24 | MainMenu
25 | NSPrincipalClass
26 | NSApplication
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Examples/Window Effects/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // WindowZoom
4 | //
5 | // Created by Paul Kim on 6/18/07.
6 | // Copyright __MyCompanyName__ 2007. 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 |
--------------------------------------------------------------------------------
/Examples/iToonz/Controller.h:
--------------------------------------------------------------------------------
1 | //
2 | // Controller.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 10/21/09.
6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import
30 |
31 | @class NoodleTableView;
32 |
33 | @interface Controller : NSObject
34 | {
35 | IBOutlet NoodleTableView *_tableView;
36 | NSArray *_entries;
37 | id _number;
38 | }
39 |
40 | @property (readonly) NSArray *entries;
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/Examples/iToonz/Controller.m:
--------------------------------------------------------------------------------
1 | //
2 | // Controller.m
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 10/21/09.
6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Created by Paul Kim on 10/20/09.
9 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
10 | //
11 | // Permission is hereby granted, free of charge, to any person
12 | // obtaining a copy of this software and associated documentation
13 | // files (the "Software"), to deal in the Software without
14 | // restriction, including without limitation the rights to use,
15 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | // copies of the Software, and to permit persons to whom the
17 | // Software is furnished to do so, subject to the following
18 | // conditions:
19 | //
20 | // The above copyright notice and this permission notice shall be
21 | // included in all copies or substantial portions of the Software.
22 | //
23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
25 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
27 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
28 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
30 | // OTHER DEALINGS IN THE SOFTWARE.
31 |
32 | #import "Controller.h"
33 | #import "NoodleTableView.h"
34 |
35 | #define ARTIST_KEY @"artist"
36 | #define ARTWORK_KEY @"artwork"
37 | #define ALBUM_KEY @"album"
38 | #define SONGCOUNT_KEY @"songCount"
39 |
40 | @implementation Controller
41 |
42 |
43 | //@synthesize window;
44 | @synthesize entries = _entries;
45 |
46 | - (void)awakeFromNib
47 | {
48 | [_tableView setIntercellSpacing:NSMakeSize(0.0, 0.0)];
49 | [_tableView setShowsStickyRowHeader:YES];
50 | [_tableView setRowSpanningEnabledForCapableColumns:YES];
51 | _number = [NSNumber numberWithInt:5];
52 | }
53 |
54 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
55 | {
56 | _entries = [[NSArray alloc] initWithObjects:
57 | [NSDictionary dictionaryWithObjectsAndKeys:
58 | @"Mr Disco", ARTIST_KEY,
59 | [NSImage imageNamed:NSImageNameFolderBurnable], ARTWORK_KEY,
60 | @"Burn Baby Burn", ALBUM_KEY,
61 | [NSNumber numberWithInteger:9], SONGCOUNT_KEY, nil],
62 | [NSDictionary dictionaryWithObjectsAndKeys:
63 | @"Pierre LeMac", ARTIST_KEY,
64 | [NSImage imageNamed:NSImageNameBonjour], ARTWORK_KEY,
65 | @"Bonjour Ma Cherie", ALBUM_KEY,
66 | [NSNumber numberWithInteger:13], SONGCOUNT_KEY, nil],
67 | [NSDictionary dictionaryWithObjectsAndKeys:
68 | @"M.C. Mac", ARTIST_KEY,
69 | [NSImage imageNamed:NSImageNameDotMac], ARTWORK_KEY,
70 | @"Dot Mackin'", ALBUM_KEY,
71 | [NSNumber numberWithInteger:7], SONGCOUNT_KEY, nil],
72 | [NSDictionary dictionaryWithObjectsAndKeys:
73 | @"M.C. Mac", ARTIST_KEY,
74 | [NSImage imageNamed:NSImageNameFolderSmart], ARTWORK_KEY,
75 | @"You Think You're So Smart", ALBUM_KEY,
76 | [NSNumber numberWithInteger:14], SONGCOUNT_KEY, nil],
77 | [NSDictionary dictionaryWithObjectsAndKeys:
78 | @"ComputerHead", ARTIST_KEY,
79 | [NSImage imageNamed:NSImageNameComputer], ARTWORK_KEY,
80 | @"Cancel Computer", ALBUM_KEY,
81 | [NSNumber numberWithInteger:12], SONGCOUNT_KEY, nil],
82 | nil];
83 |
84 | [(NoodleTableColumn *)[_tableView tableColumnWithIdentifier:@"Album"] setRowSpanningEnabled:NO];
85 | [_tableView reloadData];
86 | }
87 |
88 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
89 | {
90 | return [[_entries valueForKeyPath:@"@sum.songCount"] integerValue];
91 | }
92 |
93 | - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
94 | {
95 | NSInteger tally, songCount;
96 | id identifier;
97 |
98 | tally = 0;
99 | identifier = [aTableColumn identifier];
100 | for (NSDictionary *dict in _entries)
101 | {
102 | songCount = [[dict objectForKey:SONGCOUNT_KEY] integerValue];
103 |
104 | if (rowIndex < tally + songCount)
105 | {
106 | if ([identifier isEqual:@"Artwork"])
107 | {
108 | return [dict objectForKey:ARTWORK_KEY];
109 | }
110 | else if ([identifier isEqual:@"Album"])
111 | {
112 | return [dict objectForKey:ALBUM_KEY];
113 | }
114 | else if ([identifier isEqual:@"Artist"])
115 | {
116 | return [dict objectForKey:ARTIST_KEY];
117 | }
118 | else if ([identifier isEqual:@"Song"])
119 | {
120 | return [NSString stringWithFormat:@"Song #%d", rowIndex - tally + 1];
121 | }
122 | }
123 | tally += songCount;
124 | }
125 | return nil;
126 | }
127 |
128 | - (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn
129 | {
130 | if ([[tableColumn identifier] isEqual:@"Album"])
131 | {
132 | [(NoodleTableColumn *)tableColumn setRowSpanningEnabled:![(NoodleTableColumn *)tableColumn isRowSpanningEnabled]];
133 | [_tableView reloadData];
134 | }
135 | }
136 |
137 | - (BOOL)tableView:(NSTableView *)tableView isStickyRow:(NSInteger)row
138 | {
139 | id value, newValue;
140 | NSTableColumn *column;
141 |
142 | column = [tableView tableColumnWithIdentifier:@"Artist"];
143 | value = [self tableView:tableView objectValueForTableColumn:column row:row];
144 |
145 | if (row > 0)
146 | {
147 | newValue = [self tableView:tableView objectValueForTableColumn:column row:row - 1];
148 | if (![value isEqual:newValue])
149 | {
150 | return YES;
151 | }
152 | return NO;
153 | }
154 | return YES;
155 | }
156 |
157 | - (BOOL)tableView:(NSTableView *)tableView shouldDisplayCellInStickyRowHeaderForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
158 | {
159 | return [[tableColumn identifier] isEqual:@"Artist"];
160 | }
161 |
162 | @end
163 |
--------------------------------------------------------------------------------
/Examples/iToonz/Credits.rtf:
--------------------------------------------------------------------------------
1 | Read Me.rtf
--------------------------------------------------------------------------------
/Examples/iToonz/Read Me.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf110
2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Monaco;}
3 | {\colortbl;\red255\green255\blue255;}
4 | {\*\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}
5 | {\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}
6 | {\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}
7 | {\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}
8 | {\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}
9 | {\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}
10 | {\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}}
11 | {\*\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}}
12 | \margl1440\margr1440\vieww19360\viewh17460\viewkind0
13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
14 |
15 | \f0\b\fs24 \cf0 iToonz
16 | \b0 \
17 | version 0.68\
18 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
19 | \cf0 \
20 | This is a sample project and test harness for the row spanning feature of NoodleTableView.\
21 | \
22 | 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.\
23 | \
24 | 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/}}\
25 | \
26 | 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/}}\
27 | \
28 | 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.\
29 | \
30 | 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}}\
31 | \
32 | 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.\
33 | \
34 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
35 |
36 | \b \cf0 Notes
37 | \b0 \
38 | \
39 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
40 | \ls1\ilvl0\cf0 {\listtext \'95 }As mentioned above, you can use any NSCell subclass.\
41 | {\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.\
42 | {\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.\
43 | {\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.\
44 | {\listtext \'95 }Span cells will not highlight.\
45 | {\listtext \'95 }Clicking on span cells will do nothing.\
46 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
47 | \cf0 \
48 | 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.\
49 | \
50 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
51 |
52 | \b \cf0 Possible Improvements\
53 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
54 |
55 | \b0 \cf0 \
56 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
57 | \ls2\ilvl0\cf0 {\listtext \'95 }Provide delegate hooks for determining the range of a span.\
58 | {\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.\
59 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
60 | \cf0 \
61 | \
62 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
63 |
64 | \b \cf0 Contact
65 | \b0 \
66 | \
67 | 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.\
68 | \
69 | \
70 |
71 | \b License
72 | \b0 \
73 | \
74 | I am releasing this under the MIT license.\
75 | \
76 | ____________________________________\
77 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
78 |
79 | \f1 \cf0 Copyright (c) 2009 Noodlesoft, LLC. All Rights Reserved.\
80 | \
81 | Permission is hereby granted, free of charge, to any person\
82 | obtaining a copy of this software and associated documentation\
83 | files (the "Software"), to deal in the Software without\
84 | restriction, including without limitation the rights to use,\
85 | copy, modify, merge, publish, distribute, sublicense, and/or sell\
86 | copies of the Software, and to permit persons to whom the\
87 | Software is furnished to do so, subject to the following\
88 | conditions:\
89 | \
90 | The above copyright notice and this permission notice shall be\
91 | included in all copies or substantial portions of the Software.\
92 | \
93 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\
94 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\
95 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\
96 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\
97 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\
98 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\
99 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\
100 | OTHER DEALINGS IN THE SOFTWARE.\
101 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
102 | \ls3\ilvl0
103 | \f0\b \cf0 \
104 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
105 | \cf0 \
106 | Changelog
107 | \b0 \
108 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
109 | \ls4\ilvl0\cf0 \
110 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
111 | \cf0 0.68:\
112 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
113 | \ls5\ilvl0\cf0 {\listtext \'95 }Fixed crash when clicking on a non-span cell and dragging to a span cell.\
114 | {\listtext \'95 }Fixed highlighting behavior. Span cells should no longer highlight.\
115 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
116 | \cf0 \
117 | 0.37:\
118 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
119 | \ls6\ilvl0\cf0 {\listtext \'95 }Fixed drawing loop causing high CPU usage.\
120 | {\listtext \'95 }Revamped grid code.\
121 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
122 | \cf0 \
123 | 0.04:\
124 | \pard\tx220\tx720\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li720\fi-720\ql\qnatural\pardirnatural
125 | \ls7\ilvl0\cf0 {\listtext \'95 }Initial public release\
126 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
127 | \cf0 \
128 | }
--------------------------------------------------------------------------------
/Examples/iToonz/iToonz-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | APPL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 | LSMinimumSystemVersion
22 | ${MACOSX_DEPLOYMENT_TARGET}
23 | NSMainNibFile
24 | MainMenu
25 | NSPrincipalClass
26 | NSApplication
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Examples/iToonz/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // NoodleStickyRowTableViewTest
4 | //
5 | // Created by Paul Kim on 8/23/09.
6 | // Copyright Noodlesoft, LLC 2009. 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 |
--------------------------------------------------------------------------------
/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleName
10 | ${PRODUCT_NAME}
11 | CFBundleIconFile
12 |
13 | CFBundleIdentifier
14 | com.noodlesoft.${PRODUCT_NAME:rfc1034Identifier}
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | CFBundleShortVersionString
24 | 1.0
25 | NSPrincipalClass
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/NSImage-NoodleExtensions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSImage-NoodleExtensions.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 3/24/07.
6 | // Copyright 2007-2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import
30 |
31 |
32 | /*
33 | This category provides methods for dealing with flipped images. These should draw images correctly regardless of
34 | whether the current context or the current image are flipped. Unless you know what you are doing, these should be used
35 | in lieu of the normal NSImage drawing/compositing methods.
36 |
37 | For more details, check out the related blog post at http://www.noodlesoft.com/blog/2009/02/02/understanding-flipped-coordinate-systems/
38 | */
39 |
40 | @interface NSImage (NoodleExtensions)
41 |
42 | /*!
43 | @method drawAdjustedAtPoint:fromRect:operation:fraction:
44 | @abstract Draws all or part of the image at the specified point in the current coordinate system. Unlike other methods in NSImage, this will orient the image properly in flipped coordinate systems.
45 | @param point The location in the current coordinate system at which to draw the image.
46 | @param srcRect The source rectangle specifying the portion of the image you want to draw. The coordinates of this rectangle are specified in the image's own coordinate system. If you pass in NSZeroRect, the entire image is drawn.
47 | @param op The compositing operation to use when drawing the image. See the NSCompositingOperation constants.
48 | @param delta The opacity of the image, specified as a value from 0.0 to 1.0. Specifying a value of 0.0 draws the image as fully transparent while a value of 1.0 draws the image as fully opaque. Values greater than 1.0 are interpreted as 1.0.
49 | @discussion The image content is drawn at its current resolution and is not scaled unless the CTM of the current coordinate system itself contains a scaling factor. The image is otherwise positioned and oriented using the current coordinate system, except that it takes the flipped status into account, drawing right-side-up in a such a case.
50 |
51 | Unlike the compositeToPoint:fromRect:operation: and compositeToPoint:fromRect:operation:fraction: methods, this method checks the rectangle you pass to the srcRect parameter and makes sure it does not lie outside the image bounds.
52 | */
53 | - (void)drawAdjustedAtPoint:(NSPoint)aPoint fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)delta;
54 |
55 | /*!
56 | @method drawAdjustedInRect:fromRect:operation:fraction:
57 | @abstract Draws all or part of the image in the specified rectangle in the current coordinate system. Unlike other methods in NSImage, this will orient the image properly in flipped coordinate systems.
58 | @param dstRect The rectangle in which to draw the image, specified in the current coordinate system.
59 | @param srcRect The source rectangle specifying the portion of the image you want to draw. The coordinates of this rectangle must be specified using the image's own coordinate system. If you pass in NSZeroRect, the entire image is drawn.
60 | @param op The compositing operation to use when drawing the image. See the NSCompositingOperation constants.
61 | @param delta The opacity of the image, specified as a value from 0.0 to 1.0. Specifying a value of 0.0 draws the image as fully transparent while a value of 1.0 draws the image as fully opaque. Values greater than 1.0 are interpreted as 1.0.
62 | @discussion If the srcRect and dstRect rectangles have different sizes, the source portion of the image is scaled to fit the specified destination rectangle. The image is otherwise positioned and oriented using the current coordinate system, except that it takes the flipped status into account, drawing right-side-up in a such a case.
63 |
64 | Unlike the compositeToPoint:fromRect:operation: and compositeToPoint:fromRect:operation:fraction: methods, this method checks the rectangle you pass to the srcRect parameter and makes sure it does not lie outside the image bounds.
65 | */
66 | - (void)drawAdjustedInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)delta;
67 |
68 | /*!
69 | @method unflippedImage
70 | @abstract Returns a version of the receiver but unflipped.
71 | @discussion This does not actually flip the image but returns an image with the same orientation but with an unflipped coordinate system internally (isFlipped returns NO). If the image is already unflipped, this method returns self.
72 | */
73 | - (NSImage *)unflippedImage;
74 |
75 |
76 | @end
77 |
--------------------------------------------------------------------------------
/NSImage-NoodleExtensions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSImage-NoodleExtensions.m
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 3/24/07.
6 | // Copyright 2007-2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import "NSImage-NoodleExtensions.h"
30 |
31 |
32 | @implementation NSImage (NoodleExtensions)
33 |
34 | - (void)drawAdjustedAtPoint:(NSPoint)aPoint fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)delta
35 | {
36 | NSSize size = [self size];
37 |
38 | [self drawAdjustedInRect:NSMakeRect(aPoint.x, aPoint.y, size.width, size.height) fromRect:srcRect operation:op fraction:delta];
39 | }
40 |
41 | - (void)drawAdjustedInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)delta
42 | {
43 | NSGraphicsContext *context;
44 | BOOL contextIsFlipped;
45 |
46 | context = [NSGraphicsContext currentContext];
47 | contextIsFlipped = [context isFlipped];
48 |
49 | if (contextIsFlipped)
50 | {
51 | NSAffineTransform *transform;
52 |
53 | [context saveGraphicsState];
54 |
55 | // Flip the coordinate system back.
56 | transform = [NSAffineTransform transform];
57 | [transform translateXBy:0 yBy:NSMaxY(dstRect)];
58 | [transform scaleXBy:1 yBy:-1];
59 | [transform concat];
60 |
61 | // The transform above places the y-origin right where the image should be drawn.
62 | dstRect.origin.y = 0.0;
63 | }
64 |
65 | [self drawInRect:dstRect fromRect:srcRect operation:op fraction:delta];
66 |
67 | if (contextIsFlipped)
68 | {
69 | [context restoreGraphicsState];
70 | }
71 | }
72 |
73 | - (NSImage *)unflippedImage
74 | {
75 | if ([self isFlipped])
76 | {
77 | NSImage *newImage;
78 |
79 | newImage = [[[NSImage alloc] initWithSize:[self size]] autorelease];
80 | [newImage lockFocus];
81 | [self drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
82 | [newImage unlockFocus];
83 |
84 | return newImage;
85 | }
86 | return self;
87 | }
88 |
89 | @end
90 |
--------------------------------------------------------------------------------
/NSIndexSet-NoodleExtensions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSIndexSet-NoodleExtensions.h
3 | // NoodleRowSpanningTableViewTest
4 | //
5 | // Created by Paul Kim on 10/20/09.
6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import
30 |
31 | @interface NoodleIndexSetEnumerator : NSObject
32 | {
33 | NSUInteger *_indexes;
34 | NSUInteger _count;
35 | NSUInteger _currentIndex;
36 | }
37 |
38 | // Returns NSNotFound when there are no more indexes.
39 | - (NSUInteger)nextIndex;
40 |
41 | @end
42 |
43 |
44 | @interface NSIndexSet (NoodleExtensions)
45 |
46 | - (NoodleIndexSetEnumerator *)indexEnumerator;
47 |
48 | @end
49 |
50 |
--------------------------------------------------------------------------------
/NSIndexSet-NoodleExtensions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSIndexSet-NoodleExtensions.m
3 | // NoodleRowSpanningTableViewTest
4 | //
5 | // Created by Paul Kim on 10/20/09.
6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import "NSIndexSet-NoodleExtensions.h"
30 |
31 | @interface NoodleIndexSetEnumerator ()
32 |
33 | + enumeratorWithIndexSet:(NSIndexSet *)set;
34 | - initWithIndexSet:(NSIndexSet *)set;
35 |
36 | @end
37 |
38 | @implementation NoodleIndexSetEnumerator
39 |
40 | + enumeratorWithIndexSet:(NSIndexSet *)set
41 | {
42 | return [[[[self class] alloc] initWithIndexSet:set] autorelease];
43 | }
44 |
45 | - initWithIndexSet:(NSIndexSet *)set
46 | {
47 | if ((self = [super init]) != nil)
48 | {
49 | _currentIndex = 0;
50 | _count = [set count];
51 | _indexes = (NSUInteger *)malloc(sizeof(NSUInteger) * _count);
52 |
53 | [set getIndexes:_indexes maxCount:_count inIndexRange:nil];
54 | }
55 | return self;
56 | }
57 |
58 | - (void)dealloc
59 | {
60 | free(_indexes);
61 | _indexes = NULL;
62 | [super dealloc];
63 | }
64 |
65 | - (void)finalize
66 | {
67 | free(_indexes);
68 | [super finalize];
69 | }
70 |
71 | - (NSUInteger)nextIndex
72 | {
73 | if (_currentIndex < _count)
74 | {
75 | NSUInteger i;
76 |
77 | i = _indexes[_currentIndex];
78 | _currentIndex++;
79 |
80 | return i;
81 | }
82 | return NSNotFound;
83 | }
84 |
85 | @end
86 |
87 | @implementation NSIndexSet (NoodleExtensions)
88 |
89 | - (NoodleIndexSetEnumerator *)indexEnumerator
90 | {
91 | return [NoodleIndexSetEnumerator enumeratorWithIndexSet:self];
92 | }
93 |
94 | @end
95 |
96 |
97 |
--------------------------------------------------------------------------------
/NSObject-NoodlePerformWhenIdle.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject-IdleExtensions.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 12/30/07.
6 | // Copyright 2007 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 |
28 | @interface NSObject (NoodlePerformWhenIdle)
29 |
30 | - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterSystemIdleTime:(NSTimeInterval)delay;
31 |
32 | - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterSystemIdleTime:(NSTimeInterval)delay withinTimeLimit:(NSTimeInterval)maxTime;
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/NSObject-NoodlePerformWhenIdle.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject-IdleExtensions.m
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 12/30/07.
6 | // Copyright 2007-2012 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "NSObject-NoodlePerformWhenIdle.h"
27 | #import
28 |
29 | @implementation NSObject (NoodlePerformWhenIdle)
30 |
31 | // Heard somewhere that this prototype may be missing in some cases so adding it here just in case.
32 | CG_EXTERN CFTimeInterval CGEventSourceSecondsSinceLastEventType( CGEventSourceStateID source, CGEventType eventType ) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER;
33 |
34 |
35 | // Semi-private method. Used by the public methods
36 | - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterSystemIdleTime:(NSTimeInterval)delay withinTimeLimit:(NSTimeInterval)maxTime startTime:(NSTimeInterval)startTime
37 | {
38 | CFTimeInterval idleTime;
39 | NSTimeInterval timeSinceInitialCall;
40 |
41 | timeSinceInitialCall = [NSDate timeIntervalSinceReferenceDate] - startTime;
42 |
43 | if (maxTime > 0)
44 | {
45 | if (timeSinceInitialCall >= maxTime)
46 | {
47 | [self performSelector:aSelector withObject:anArgument];
48 | return;
49 | }
50 | }
51 |
52 | idleTime = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateHIDSystemState, kCGAnyInputEventType);
53 | if (idleTime < delay)
54 | {
55 | NSTimeInterval fireTime;
56 | NSMethodSignature *signature;
57 | NSInvocation *invocation;
58 |
59 | signature = [self methodSignatureForSelector:@selector(performSelector:withObject:afterSystemIdleTime:withinTimeLimit:startTime:)];
60 | invocation = [NSInvocation invocationWithMethodSignature:signature];
61 | [invocation setSelector:@selector(performSelector:withObject:afterSystemIdleTime:withinTimeLimit:startTime:)];
62 | [invocation setTarget:self];
63 | [invocation setArgument:&aSelector atIndex:2];
64 | [invocation setArgument:&anArgument atIndex:3];
65 | [invocation setArgument:&delay atIndex:4];
66 | [invocation setArgument:&maxTime atIndex:5];
67 | [invocation setArgument:&startTime atIndex:6];
68 |
69 | fireTime = delay - idleTime;
70 | if (maxTime > 0)
71 | {
72 | fireTime = MIN(fireTime, maxTime - timeSinceInitialCall);
73 | }
74 |
75 | // Not idle for long enough. Set a timer and check back later
76 | [NSTimer scheduledTimerWithTimeInterval:fireTime invocation:invocation repeats:NO];
77 | }
78 | else
79 | {
80 | [self performSelector:aSelector withObject:anArgument];
81 | }
82 | }
83 |
84 |
85 | - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterSystemIdleTime:(NSTimeInterval)delay
86 | {
87 | [self performSelector:aSelector withObject:anArgument afterSystemIdleTime:delay withinTimeLimit:-1];
88 | }
89 |
90 |
91 | - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterSystemIdleTime:(NSTimeInterval)delay withinTimeLimit:(NSTimeInterval)maxTime
92 | {
93 | SInt32 version;
94 |
95 | // NOTE: Even though CGEventSourceSecondsSinceLastEventType exists on Tiger,
96 | // it appears to hang on some Tiger systems. For now, only enabling for Leopard or later.
97 | if ((Gestalt(gestaltSystemVersion, &version) == noErr) && (version >= 0x1050))
98 | {
99 | NSTimeInterval startTime;
100 |
101 | startTime = [NSDate timeIntervalSinceReferenceDate];
102 |
103 | [self performSelector:aSelector withObject:anArgument afterSystemIdleTime:delay withinTimeLimit:maxTime startTime:startTime];
104 | }
105 | else
106 | {
107 | // For pre-10.5, just call it after a delay. Change this if you want to throw an exception
108 | // instead.
109 | [self performSelector:aSelector withObject:anArgument afterDelay:delay];
110 | }
111 | }
112 |
113 |
114 | @end
115 |
--------------------------------------------------------------------------------
/NSResponder-NoodleModalExtensions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSResponder-ModalExtensions.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 3/6/08.
6 | // Copyright 2008-2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 |
28 | /*
29 | These categories provide simple methods to handle alert dialogs (window or sheet form). Hook your "OK" and "Cancel"
30 | buttons to these methods to the first responder in IB and you're done. The modal session will return NSOKButton or
31 | NSCancelButton. I'll leave it to you to figure out which one does what.
32 |
33 | For more details, check out the related blog post at http://www.noodlesoft.com/blog/2008/03/10/modal-glue/
34 | */
35 |
36 |
37 | @interface NSResponder (NoodleModalExtensions)
38 |
39 | - (void)confirmModal:(id)sender;
40 | - (void)cancelModal:(id)sender;
41 |
42 | @end
43 |
44 | @interface NSWindow (NoodleModalExtensions)
45 |
46 | - (void)confirmModal:(id)sender;
47 | - (void)cancelModal:(id)sender;
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/NSResponder-NoodleModalExtensions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSResponder-ModalExtensions.m
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 3/6/08.
6 | // Copyright 2008-2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "NSResponder-NoodleModalExtensions.h"
27 |
28 |
29 | @implementation NSResponder (NoodleModalExtensions)
30 |
31 |
32 | - (void)confirmModal:(id)sender
33 | {
34 | [[self nextResponder] confirmModal:sender];
35 | }
36 |
37 | - (void)cancelModal:(id)sender
38 | {
39 | [[self nextResponder] cancelModal:sender];
40 | }
41 |
42 | @end
43 |
44 | @implementation NSWindow (NoodleModalExtensions)
45 |
46 | - (BOOL)stopModalWindowOrSheetWithCode:(NSInteger)returnCode sender:(id)sender
47 | {
48 | if ([NSApp modalWindow] == self)
49 | {
50 | [self orderOut:sender];
51 | [NSApp stopModalWithCode:returnCode];
52 | return YES;
53 | }
54 | else if ([self isSheet])
55 | {
56 | [self orderOut:sender];
57 | [NSApp endSheet:self returnCode:returnCode];
58 | return YES;
59 | }
60 | else if ([self attachedSheet] != nil)
61 | {
62 | NSWindow *sheet;
63 |
64 | sheet = [self attachedSheet];
65 | [sheet orderOut:sender];
66 | [NSApp endSheet:sheet returnCode:returnCode];
67 | }
68 | return NO;
69 | }
70 |
71 | - (void)confirmModal:(id)sender
72 | {
73 | if (![self stopModalWindowOrSheetWithCode:NSOKButton sender:sender])
74 | {
75 | [[self nextResponder] confirmModal:sender];
76 | }
77 | }
78 |
79 | - (void)cancelModal:(id)sender
80 | {
81 | if (![self stopModalWindowOrSheetWithCode:NSCancelButton sender:sender])
82 | {
83 | [[self nextResponder] cancelModal:self];
84 | }
85 | }
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/NSTableView-NoodleExtensions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableView-NoodleExtensions.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 10/22/09.
6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import
30 |
31 | typedef NSUInteger NoodleStickyRowTransition;
32 |
33 | enum
34 | {
35 | NoodleStickyRowTransitionNone,
36 | NoodleStickyRowTransitionFadeIn
37 | };
38 |
39 |
40 | @interface NSTableView (NoodleExtensions)
41 |
42 | #pragma mark Sticky Row Header methods
43 | // Note: see NoodleTableView's -drawRect on how to hook in this functionality in a subclass
44 |
45 | /*
46 | Currently set to any groups rows (as dictated by the delegate). The
47 | delegate can implement -tableView:isStickyRow: to override this.
48 | */
49 | - (BOOL)isRowSticky:(NSInteger)rowIndex;
50 |
51 | /*
52 | Does the actual drawing of the sticky row. Override if you want a custom look.
53 | You shouldn't invoke this directly. See -drawStickyRowHeader.
54 | */
55 | - (void)drawStickyRow:(NSInteger)row clipRect:(NSRect)clipRect;
56 |
57 | /*
58 | Draws the sticky row at the top of the table. You have to override -drawRect
59 | and call this method, that being all you need to get the sticky row stuff
60 | to work in your subclass. Look at NoodleStickyRowTableView.
61 | Note that you shouldn't need to override this. To modify the look of the row,
62 | override -drawStickyRow: instead.
63 | */
64 | - (void)drawStickyRowHeader;
65 |
66 | /*
67 | Returns the rect of the sticky view header. Will return NSZeroRect if there is no current
68 | sticky row.
69 | */
70 | - (NSRect)stickyRowHeaderRect;
71 |
72 | /*
73 | Does an animated scroll to the current sticky row. Clicking on the sticky
74 | row header will trigger this.
75 | */
76 | - (IBAction)scrollToStickyRow:(id)sender;
77 |
78 | /*
79 | Returns what kind of transition you want when the row becomes sticky. Fade-in
80 | is the default.
81 | */
82 | - (NoodleStickyRowTransition)stickyRowHeaderTransition;
83 |
84 | #pragma mark Row Spanning methods
85 |
86 | /*
87 | Returns the range of the span at the given column and row indexes. The span is determined by
88 | a range of contiguous rows having the same object value.
89 | */
90 | - (NSRange)rangeOfRowSpanAtColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex;
91 |
92 | @end
93 |
94 | @class NoodleRowSpanningCell;
95 |
96 | @interface NSTableColumn (NoodleExtensions)
97 |
98 | #pragma mark Row Spanning methods
99 | /*
100 | Returns whether this column will try to consolidate rows into spans.
101 | */
102 | - (BOOL)isRowSpanningEnabled;
103 |
104 | /*
105 | Returns the cell used to draw the spanning regions. Default implementation returns nil.
106 | */
107 | - (NoodleRowSpanningCell *)spanningCell;
108 |
109 | @end
110 |
111 |
112 | @interface NSOutlineView (NoodleExtensions)
113 |
114 | #pragma mark Sticky Row Header methods
115 | /*
116 | Currently set to any groups rows (or as dictated by the delegate). The
117 | delegate can implement -outlineView:isStickyRow: to override this.
118 | */
119 | - (BOOL)isRowSticky:(NSInteger)rowIndex;
120 |
121 | @end
122 |
123 |
124 | @interface NSObject (NoodleStickyRowDelegate)
125 |
126 | /*
127 | Allows the delegate to specify if a row is sticky. By default, group rows
128 | are sticky. The delegate can override that by implementing this method.
129 | */
130 | - (BOOL)tableView:(NSTableView *)tableView isStickyRow:(NSInteger)rowIndex;
131 |
132 | /*
133 | Allows the delegate to specify whether a certain cell should be drawn in the sticky row header
134 | */
135 | - (BOOL)tableView:(NSTableView *)tableView shouldDisplayCellInStickyRowHeaderForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex;
136 |
137 | /*
138 | Same as above but for outline views.
139 | */
140 | - (BOOL)outlineView:(NSOutlineView *)outlineView isStickyItem:(id)item;
141 |
142 | @end
143 |
144 |
--------------------------------------------------------------------------------
/NSTableView-NoodleExtensions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableView-NoodleExtensions.m
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 10/22/09.
6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import "NSTableView-NoodleExtensions.h"
30 | #import "NSImage-NoodleExtensions.h"
31 |
32 | #define NOODLE_STICKY_ROW_VIEW_TAG 233931134
33 |
34 | void NoodleClearRect(NSRect rect)
35 | {
36 | [[NSColor clearColor] set];
37 | NSRectFill(rect);
38 | }
39 |
40 | @interface NSTableView (NoodlePrivate)
41 |
42 | #pragma mark Sticky Row Header methods
43 |
44 | // Returns index of the sticky row previous to the first visible row.
45 | - (NSInteger)_previousStickyRow;
46 |
47 | // Returns index of the sticky row after the first visible row.
48 | - (NSInteger)_nextStickyRow;
49 |
50 | - (void)_updateStickyRowHeaderImageWithRow:(NSInteger)row;
51 |
52 | // Returns the view used for the sticky row header
53 | - (id)_stickyRowHeaderView;
54 |
55 | @end
56 |
57 |
58 | @implementation NSTableView (NoodleExtensions)
59 |
60 | #pragma mark Sticky Row Header methods
61 |
62 | - (BOOL)isRowSticky:(NSInteger)rowIndex
63 | {
64 | id delegate;
65 |
66 | delegate = [self delegate];
67 |
68 | if ([delegate respondsToSelector:@selector(tableView:isStickyRow:)])
69 | {
70 | return [delegate tableView:self isStickyRow:rowIndex];
71 | }
72 | else if ([delegate respondsToSelector:@selector(tableView:isGroupRow:)])
73 | {
74 | return [delegate tableView:self isGroupRow:rowIndex];
75 | }
76 | return NO;
77 | }
78 |
79 | - (void)drawStickyRowHeader
80 | {
81 | id stickyView;
82 | NSInteger row;
83 |
84 | stickyView = [self _stickyRowHeaderView];
85 | row = [self _previousStickyRow];
86 | if (row != -1)
87 | {
88 | [stickyView setFrame:[self stickyRowHeaderRect]];
89 | [self _updateStickyRowHeaderImageWithRow:row];
90 | }
91 | else
92 | {
93 | [stickyView setFrame:NSZeroRect];
94 | }
95 | }
96 |
97 | - (IBAction)scrollToStickyRow:(id)sender
98 | {
99 | NSInteger row;
100 |
101 | row = [self _previousStickyRow];
102 | if (row != -1)
103 | {
104 | [self scrollRowToVisible:row];
105 | }
106 | }
107 |
108 | - (id)_stickyRowHeaderView
109 | {
110 | NSButton *view;
111 |
112 | view = [self viewWithTag:NOODLE_STICKY_ROW_VIEW_TAG];
113 |
114 | if (view == nil)
115 | {
116 | view = [[NSButton alloc] initWithFrame:NSZeroRect];
117 | [view setEnabled:YES];
118 | [view setBordered:NO];
119 | [view setImagePosition:NSImageOnly];
120 | [view setTitle:nil];
121 | [[view cell] setHighlightsBy:NSNoCellMask];
122 | [[view cell] setShowsStateBy:NSNoCellMask];
123 | [[view cell] setImageScaling:NSImageScaleNone];
124 | [[view cell] setImageDimsWhenDisabled:NO];
125 |
126 | [view setTag:NOODLE_STICKY_ROW_VIEW_TAG];
127 |
128 | [view setTarget:self];
129 | [view setAction:@selector(scrollToStickyRow:)];
130 |
131 | [self addSubview:view];
132 | [view release];
133 | }
134 | return view;
135 | }
136 |
137 | - (void)drawStickyRow:(NSInteger)row clipRect:(NSRect)clipRect
138 | {
139 | NSRect rowRect, cellRect;
140 | NSCell *cell;
141 | NSInteger colIndex, count;
142 | id delegate;
143 |
144 | delegate = [self delegate];
145 |
146 | if (![delegate respondsToSelector:@selector(tableView:shouldDisplayCellInStickyRowHeaderForTableColumn:row:)])
147 | {
148 | delegate = nil;
149 | }
150 |
151 | rowRect = [self rectOfRow:row];
152 |
153 | [[[self backgroundColor] highlightWithLevel:0.5] set];
154 | NSRectFill(rowRect);
155 |
156 | // PENDING: -drawRow:clipRect: is too smart for its own good. If the row is not visible,
157 | // this method won't draw anything. Useless for row caching.
158 | // [self drawRow:row clipRect:rowRect];
159 |
160 | count = [self numberOfColumns];
161 | for (colIndex = 0; colIndex < count; colIndex++)
162 | {
163 | if ((delegate == nil) ||
164 | [delegate tableView:self shouldDisplayCellInStickyRowHeaderForTableColumn:[[self tableColumns] objectAtIndex:colIndex] row:row])
165 | {
166 | cell = [self preparedCellAtColumn:colIndex row:row];
167 | cellRect = [self frameOfCellAtColumn:colIndex row:row];
168 | [cell drawWithFrame:cellRect inView:self];
169 | }
170 | }
171 |
172 | [[self gridColor] set];
173 | [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMinX(rowRect), NSMaxY(rowRect)) toPoint:NSMakePoint(NSMaxX(rowRect), NSMaxY(rowRect))];
174 | }
175 |
176 | - (NoodleStickyRowTransition)stickyRowHeaderTransition
177 | {
178 | return NoodleStickyRowTransitionFadeIn;
179 | }
180 |
181 | - (void)_updateStickyRowHeaderImageWithRow:(NSInteger)row
182 | {
183 | NSImage *image;
184 | NSRect rowRect, visibleRect, imageRect;
185 | CGFloat offset, alpha;
186 | NSAffineTransform *transform;
187 | id stickyView;
188 | NoodleStickyRowTransition transition;
189 | BOOL isSelected;
190 |
191 | rowRect = [self rectOfRow:row];
192 | imageRect = NSMakeRect(0.0, 0.0, NSWidth(rowRect), NSHeight(rowRect));
193 | stickyView = [self _stickyRowHeaderView];
194 |
195 | isSelected = [self isRowSelected:row];
196 | if (isSelected)
197 | {
198 | [self deselectRow:row];
199 | }
200 |
201 | // Optimization: instead of creating a new image each time (and since we can't
202 | // add ivars in a category), just use the image in the sticky view. We're going
203 | // to put it there in the end anyways, why not reuse it?
204 | image = [stickyView image];
205 |
206 | if ((image == nil) || !NSEqualSizes(rowRect.size, [image size]))
207 | {
208 | image = [[NSImage alloc] initWithSize:rowRect.size];
209 | [image setFlipped:[self isFlipped]];
210 | [stickyView setImage:image];
211 | [image release];
212 | }
213 |
214 | visibleRect = [self visibleRect];
215 |
216 | // Calculate a distance between the row header and the actual sticky row and normalize it
217 | // over the row height (plus some extra). We use this to do the fade in effect as you
218 | // scroll away from the sticky row.
219 | offset = (NSMinY(visibleRect) - NSMinY(rowRect)) / (NSHeight(rowRect) * 1.25);
220 |
221 | // When the button is disabled, it passes through to the view underneath. So, until the
222 | // original header view is mostly out of view, allow mouse events to pass through. After
223 | // that, the header is clickable.
224 | if (offset < 0.5)
225 | {
226 | [stickyView setEnabled:NO];
227 | }
228 | else
229 | {
230 | [stickyView setEnabled:YES];
231 | }
232 |
233 | // Row is drawn in tableview coord space.
234 | transform = [NSAffineTransform transform];
235 | [transform translateXBy:-NSMinX(rowRect) yBy:-NSMinY(rowRect)];
236 |
237 | transition = [self stickyRowHeaderTransition];
238 | if (transition == NoodleStickyRowTransitionFadeIn)
239 | {
240 | // Since we want to adjust the transparency based on position, we draw the row into an
241 | // image which we then draw with alpha into the final image.
242 | NSImage *rowImage;
243 |
244 | // Optimization: Since this is a category and we can't add any ivars, we instead use
245 | // the unused alt image of the sticky view (which is a button) as a cache so we don't
246 | // have to keep creating images. Yes, a little hackish.
247 | rowImage = [stickyView alternateImage];
248 | if ((rowImage == nil) || !NSEqualSizes(rowRect.size, [rowImage size]))
249 | {
250 | rowImage = [[NSImage alloc] initWithSize:rowRect.size];
251 | [rowImage setFlipped:[self isFlipped]];
252 |
253 | [stickyView setAlternateImage:rowImage];
254 | [rowImage release];
255 | }
256 |
257 | // Draw the original image
258 | [rowImage lockFocus];
259 | NoodleClearRect(imageRect);
260 |
261 | [transform concat];
262 | [self drawStickyRow:row clipRect:rowRect];
263 |
264 | [rowImage unlockFocus];
265 |
266 | alpha = MIN(offset, 0.9);
267 |
268 | // Draw it with transparency in the final image
269 | [image lockFocus];
270 |
271 | NoodleClearRect(imageRect);
272 | [rowImage drawAdjustedAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:alpha];
273 |
274 | [image unlockFocus];
275 | }
276 | else if (transition == NoodleStickyRowTransitionNone)
277 | {
278 | [image lockFocus];
279 | NoodleClearRect(imageRect);
280 |
281 | [transform concat];
282 | [self drawStickyRow:row clipRect:rowRect];
283 |
284 | [image unlockFocus];
285 | }
286 | else
287 | {
288 | [image lockFocus];
289 | NoodleClearRect(imageRect);
290 |
291 | [@"You returned a bad NoodleStickyRowTransition value. Tsk. Tsk." drawInRect:imageRect withAttributes:nil];
292 |
293 | [image unlockFocus];
294 | }
295 |
296 | if (isSelected)
297 | {
298 | [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:YES];
299 | }
300 |
301 | }
302 |
303 | - (NSInteger)_previousStickyRow
304 | {
305 | NSRect visibleRect;
306 | NSInteger row;
307 |
308 | visibleRect = [self visibleRect];
309 | row = [self rowAtPoint:visibleRect.origin];
310 |
311 | while (row >= 0)
312 | {
313 | if ([self isRowSticky:row])
314 | {
315 | return row;
316 | }
317 | row--;
318 | }
319 | return -1;
320 | }
321 |
322 | - (NSInteger)_nextStickyRow
323 | {
324 | NSRect visibleRect;
325 | NSInteger row;
326 | NSInteger numberOfRows;
327 |
328 | visibleRect = [self visibleRect];
329 | row = [self rowAtPoint:visibleRect.origin];
330 | if (row != -1)
331 | {
332 | numberOfRows = [self numberOfRows];
333 | while (++row < numberOfRows)
334 | {
335 | if ([self isRowSticky:row])
336 | {
337 | return row;
338 | }
339 | }
340 | }
341 | return -1;
342 | }
343 |
344 | - (NSRect)stickyRowHeaderRect
345 | {
346 | NSInteger row;
347 |
348 | row = [self _previousStickyRow];
349 |
350 | if (row != -1)
351 | {
352 | NSInteger nextGroupRow;
353 | NSRect visibleRect, rowRect;
354 |
355 | rowRect = [self rectOfRow:row];
356 | visibleRect = [self visibleRect];
357 |
358 | // Move it to the top of the visible area
359 | rowRect.origin.y = NSMinY(visibleRect);
360 |
361 | nextGroupRow = [self _nextStickyRow];
362 | if (nextGroupRow != -1)
363 | {
364 | NSRect nextRect;
365 |
366 | // "Push" the row up if it's butting up against the next sticky row
367 | nextRect = [self rectOfRow:nextGroupRow];
368 | if (NSMinY(nextRect) < NSMaxY(rowRect))
369 | {
370 | rowRect.origin.y = NSMinY(nextRect) - NSHeight(rowRect);
371 | }
372 | }
373 | return rowRect;
374 | }
375 | return NSZeroRect;
376 | }
377 |
378 | #pragma mark Row Spanning methods
379 |
380 | - (NSRange)rangeOfRowSpanAtColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex
381 | {
382 | id dataSource, objectValue, originalObjectValue;
383 | NSInteger i, start, end, count;
384 | NSTableColumn *column;
385 |
386 | dataSource = [self dataSource];
387 |
388 | column = [[self tableColumns] objectAtIndex:columnIndex];
389 |
390 | if ([column isRowSpanningEnabled])
391 | {
392 | originalObjectValue = [dataSource tableView:self objectValueForTableColumn:column row:rowIndex];
393 |
394 | // Figure out the span of this cell. We determine this by going up and down finding contiguous rows with
395 | // the same object value.
396 | i = rowIndex;
397 | while (i-- > 0)
398 | {
399 | objectValue = [dataSource tableView:self objectValueForTableColumn:column row:i];
400 |
401 | if (![objectValue isEqual:originalObjectValue])
402 | {
403 | break;
404 | }
405 | }
406 | start = i + 1;
407 |
408 | count = [self numberOfRows];
409 | i = rowIndex + 1;
410 | while (i < count)
411 | {
412 | objectValue = [dataSource tableView:self objectValueForTableColumn:column row:i];
413 |
414 | if (![objectValue isEqual:originalObjectValue])
415 | {
416 | break;
417 | }
418 | i++;
419 | }
420 | end = i - 1;
421 |
422 | return NSMakeRange(start, end - start + 1);
423 | }
424 | return NSMakeRange(rowIndex, 1);
425 | }
426 |
427 | @end
428 |
429 | @implementation NSTableColumn (NoodleExtensions)
430 |
431 | #pragma mark Row Spanning methods
432 |
433 | - (BOOL)isRowSpanningEnabled
434 | {
435 | return NO;
436 | }
437 |
438 | - (NoodleRowSpanningCell *)spanningCell
439 | {
440 | return nil;
441 | }
442 |
443 | @end
444 |
445 | @implementation NSOutlineView (NoodleExtensions)
446 |
447 | #pragma mark Sticky Row Header methods
448 |
449 | - (BOOL)isRowSticky:(NSInteger)rowIndex
450 | {
451 | id delegate;
452 |
453 | delegate = [self delegate];
454 |
455 | if ([delegate respondsToSelector:@selector(outlineView:isStickyItem:)])
456 | {
457 | return [delegate outlineView:self isStickyItem:[self itemAtRow:rowIndex]];
458 | }
459 | else if ([delegate respondsToSelector:@selector(outlineView:isGroupItem:)])
460 | {
461 | return [delegate outlineView:self isGroupItem:[self itemAtRow:rowIndex]];
462 | }
463 | return NO;
464 | }
465 |
466 | @end
467 |
468 |
--------------------------------------------------------------------------------
/NSTimer-NoodleExtensions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSTimer-NoodleExtensions.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 6/29/10.
6 | // Copyright 2010 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 |
28 | #if defined(NS_BLOCKS_AVAILABLE) && (NS_BLOCKS_AVAILABLE == 1)
29 |
30 | typedef void (^NoodleTimerBlock)(NSTimer *timer);
31 |
32 |
33 | @interface NSTimer (NoodleExtensions)
34 |
35 | /*
36 | Creates and schedules a timer which will *not* adjust its fire date when the machine is put to sleep or if the clock
37 | is changed. It will fire on the given date to the best of its abilities. If the time has somehow passed (the fire date
38 | occurred when the machine was asleep or the clock was suddenly set to a time past the fire time), the timer will fire
39 | immediately upon wake/clock change.
40 |
41 | Note that calling -setFireTime: may not work properly on this timer. A new timer should be created if you wish to have
42 | it fire at a different time after initial creation.
43 |
44 | For more details, check out the related blog post at http://www.noodlesoft.com/blog/2010/07/01/playing-with-nstimer/
45 | */
46 | + (NSTimer *)scheduledTimerWithAbsoluteFireDate:(NSDate *)fireDate target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo;
47 | + (NSTimer *)scheduledTimerWithAbsoluteFireDate:(NSDate *)fireDate block:(NoodleTimerBlock)block;
48 |
49 |
50 | + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds repeats:(BOOL)repeats block:(NoodleTimerBlock)block;
51 |
52 | + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds repeats:(BOOL)repeats block:(NoodleTimerBlock)block;
53 |
54 | - (id)initWithAbsoluteFireDate:(NSDate *)date target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo;
55 |
56 | - (id)initWithAbsoluteFireDate:(NSDate *)date block:(NoodleTimerBlock)block;
57 |
58 | - (id)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)seconds repeats:(BOOL)repeats block:(NoodleTimerBlock)block;
59 |
60 | @end
61 |
62 | #endif
63 |
--------------------------------------------------------------------------------
/NSTimer-NoodleExtensions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSTimer-NoodleExtensions.m
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 6/29/10.
6 | // Copyright 2010 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "NSTimer-NoodleExtensions.h"
27 | #import
28 | #import "NoodleGlue.h"
29 |
30 | #if defined(NS_BLOCKS_AVAILABLE) && (NS_BLOCKS_AVAILABLE == 1)
31 |
32 | static char originalDateKey;
33 | static char observerKey;
34 |
35 | @implementation NSTimer (NoodleExtensions)
36 |
37 | + (NSTimer *)scheduledTimerWithAbsoluteFireDate:(NSDate *)fireDate target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo
38 | {
39 | __block NSTimer *timer;
40 |
41 | timer = [[[NSTimer alloc] initWithAbsoluteFireDate:fireDate target:target selector:aSelector userInfo:userInfo] autorelease];
42 |
43 | if (timer != nil)
44 | {
45 | [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
46 | }
47 | return timer;
48 | }
49 |
50 | + (NSTimer *)scheduledTimerWithAbsoluteFireDate:(NSDate *)fireDate block:(NoodleTimerBlock)block
51 | {
52 | NoodleGlue *glue;
53 |
54 | glue = [NoodleGlue glueWithBlock:
55 | ^(NoodleGlue *blockGlue, id object)
56 | {
57 | block(object);
58 | }];
59 |
60 | return [self scheduledTimerWithAbsoluteFireDate:fireDate target:glue selector:@selector(invoke:) userInfo:nil];
61 | }
62 |
63 |
64 | + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds repeats:(BOOL)repeats block:(NoodleTimerBlock)block
65 | {
66 | NoodleGlue *glue;
67 |
68 | glue = [NoodleGlue glueWithBlock:
69 | ^(NoodleGlue *blockGlue, id object)
70 | {
71 | block(object);
72 | }];
73 |
74 | return [self scheduledTimerWithTimeInterval:seconds target:glue selector:@selector(invoke:) userInfo:nil repeats:repeats];
75 | }
76 |
77 | + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds repeats:(BOOL)repeats block:(NoodleTimerBlock)block
78 | {
79 | NoodleGlue *glue;
80 |
81 | glue = [NoodleGlue glueWithBlock:
82 | ^(NoodleGlue *blockGlue, id object)
83 | {
84 | block(object);
85 | }];
86 |
87 | return [self timerWithTimeInterval:seconds target:glue selector:@selector(invoke:) userInfo:nil repeats:repeats];
88 | }
89 |
90 | - (id)initWithAbsoluteFireDate:(NSDate *)date target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo
91 | {
92 | self = [self initWithFireDate:date interval:0 target:target selector:aSelector userInfo:userInfo repeats:NO];
93 |
94 | if (self != nil)
95 | {
96 | __block NSTimer *blockSelf;
97 | id observer;
98 |
99 | blockSelf = self;
100 | objc_setAssociatedObject(self, &originalDateKey, date, OBJC_ASSOCIATION_RETAIN);
101 |
102 | // We create a special observer object instead of using self. Since we are doing a category here, we can't
103 | // override -invalidate or -dealloc and do proper unregistering from notifications. Instead, we create an
104 | // intermediary observer that handles the notifications and unregisters itself when it is dealloced. We set
105 | // this observer as an associated object which is the only place where it is retained.
106 | // Note that the timer variable used is declared __block so that it is not retained by the block which would
107 | // result in a retain cycle.
108 | observer = [NoodleGlue glueWithBlock:
109 | ^(NoodleGlue *glue, id object) {
110 | [blockSelf setFireDate:(NSDate *)objc_getAssociatedObject(blockSelf, &originalDateKey)];
111 | }
112 | cleanupBlock:
113 | ^(NoodleGlue *glue) {
114 | [[NSNotificationCenter defaultCenter] removeObserver:glue];
115 | [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:glue];
116 | }];
117 |
118 | objc_setAssociatedObject(self, &observerKey, observer, OBJC_ASSOCIATION_RETAIN);
119 |
120 | [[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(invoke:) name:NSSystemTimeZoneDidChangeNotification object:nil];
121 | [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:observer selector:@selector(invoke:) name:NSWorkspaceDidWakeNotification object:nil];
122 | }
123 | return self;
124 | }
125 |
126 | - (id)initWithAbsoluteFireDate:(NSDate *)date block:(NoodleTimerBlock)block
127 | {
128 | NoodleGlue *glue;
129 |
130 | glue = [NoodleGlue glueWithBlock:
131 | ^(NoodleGlue *blockGlue, id object)
132 | {
133 | block(object);
134 | }];
135 | return [self initWithAbsoluteFireDate:date target:glue selector:@selector(invoke:) userInfo:nil];
136 | }
137 |
138 | - (id)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)seconds repeats:(BOOL)repeats block:(NoodleTimerBlock)block
139 | {
140 | NoodleGlue *glue;
141 |
142 | glue = [NoodleGlue glueWithBlock:
143 | ^(NoodleGlue *blockGlue, id object)
144 | {
145 | block(object);
146 | }];
147 | return [self initWithFireDate:date interval:seconds target:glue selector:@selector(invoke:) userInfo:nil repeats:repeats];
148 | }
149 |
150 |
151 | @end
152 |
153 | #endif
154 |
--------------------------------------------------------------------------------
/NSWindow-NoodleEffects.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSWindow-Zoom.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 6/18/07.
6 | // Copyright 2007-2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 | //
29 |
30 | #import
31 |
32 | /*
33 | Provides a "zoom" animation for windows when ordering on and off screen.
34 |
35 | For more details, check out the related blog posts at http://www.noodlesoft.com/blog/2007/06/30/animation-in-the-time-of-tiger-part-1/ and http://www.noodlesoft.com/blog/2007/09/20/animation-in-the-time-of-tiger-part-3/
36 | */
37 |
38 | @interface NSWindow (NoodleEffects)
39 |
40 | - (void)animateToFrame:(NSRect)frameRect duration:(NSTimeInterval)duration;
41 |
42 | - (void)zoomOnFromRect:(NSRect)startRect;
43 | - (void)zoomOffToRect:(NSRect)endRect;
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/NSWindow-NoodleEffects.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSWindow-Zoom.m
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 6/18/07.
6 | // Copyright 2007-2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 | //
29 |
30 | #import "NSWindow-NoodleEffects.h"
31 | #import
32 |
33 |
34 | @implementation NSWindow (NoodleEffects)
35 |
36 | - (void)animateToFrame:(NSRect)frameRect duration:(NSTimeInterval)duration
37 | {
38 | NSViewAnimation *animation;
39 |
40 | animation = [[NSViewAnimation alloc] initWithViewAnimations:
41 | [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:
42 | self, NSViewAnimationTargetKey,
43 | [NSValue valueWithRect:frameRect], NSViewAnimationEndFrameKey, nil]]];
44 |
45 | [animation setDuration:duration];
46 | [animation setAnimationBlockingMode:NSAnimationBlocking];
47 | [animation setAnimationCurve:NSAnimationLinear];
48 | [animation startAnimation];
49 |
50 | [animation release];
51 | }
52 |
53 | - (NSWindow *)_createZoomWindowWithRect:(NSRect)rect
54 | {
55 | NSWindow *zoomWindow;
56 | NSImageView *imageView;
57 | NSImage *image;
58 | NSRect frame;
59 | BOOL isOneShot;
60 |
61 | frame = [self frame];
62 |
63 | isOneShot = [self isOneShot];
64 | if (isOneShot)
65 | {
66 | [self setOneShot:NO];
67 | }
68 |
69 | if ([self windowNumber] <= 0)
70 | {
71 | CGFloat alpha;
72 |
73 | // Force creation of window device by putting it on-screen. We make it transparent to minimize the chance of
74 | // visible flicker.
75 | alpha = [self alphaValue];
76 | [self setAlphaValue:0.0];
77 | [self orderBack:self];
78 | [self orderOut:self];
79 | [self setAlphaValue:alpha];
80 | }
81 |
82 | image = [[NSImage alloc] initWithSize:frame.size];
83 | [image lockFocus];
84 | // Grab the window's pixels
85 | NSCopyBits([self gState], NSMakeRect(0.0, 0.0, frame.size.width, frame.size.height), NSZeroPoint);
86 | [image unlockFocus];
87 | [image setDataRetained:YES];
88 | [image setCacheMode:NSImageCacheNever];
89 |
90 | zoomWindow = [[NSWindow alloc] initWithContentRect:rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
91 | [zoomWindow setBackgroundColor:[NSColor colorWithDeviceWhite:0.0 alpha:0.0]];
92 | [zoomWindow setHasShadow:[self hasShadow]];
93 | [zoomWindow setLevel:[self level]];
94 | [zoomWindow setOpaque:NO];
95 | [zoomWindow setReleasedWhenClosed:YES];
96 | [zoomWindow useOptimizedDrawing:YES];
97 |
98 | imageView = [[NSImageView alloc] initWithFrame:[zoomWindow contentRectForFrameRect:frame]];
99 | [imageView setImage:image];
100 | [imageView setImageFrameStyle:NSImageFrameNone];
101 | [imageView setImageScaling:NSScaleToFit];
102 | [imageView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
103 |
104 | [zoomWindow setContentView:imageView];
105 | [image release];
106 | [imageView release];
107 |
108 | // Reset one shot flag
109 | [self setOneShot:isOneShot];
110 |
111 | return zoomWindow;
112 | }
113 |
114 | - (void)zoomOnFromRect:(NSRect)startRect
115 | {
116 | NSRect frame;
117 | NSWindow *zoomWindow;
118 |
119 | if ([self isVisible])
120 | {
121 | return;
122 | }
123 |
124 | frame = [self frame];
125 |
126 | zoomWindow = [self _createZoomWindowWithRect:startRect];
127 |
128 | [zoomWindow orderFront:self];
129 |
130 | [zoomWindow animateToFrame:frame duration:[zoomWindow animationResizeTime:frame] * 0.4];
131 |
132 | [self makeKeyAndOrderFront:self];
133 | [zoomWindow close];
134 | }
135 |
136 | - (void)zoomOffToRect:(NSRect)endRect
137 | {
138 | NSRect frame;
139 | NSWindow *zoomWindow;
140 |
141 | frame = [self frame];
142 |
143 | if (![self isVisible])
144 | {
145 | return;
146 | }
147 |
148 | zoomWindow = [self _createZoomWindowWithRect:frame];
149 |
150 | [zoomWindow orderFront:self];
151 | [self orderOut:self];
152 |
153 | [zoomWindow animateToFrame:endRect duration:[zoomWindow animationResizeTime:endRect] * 0.4];
154 |
155 | [zoomWindow close];
156 | }
157 |
158 | @end
159 |
--------------------------------------------------------------------------------
/NoodleCustomImageRep.h:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleCustomImageRep.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 3/16/11.
6 | // Copyright 2011 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import
30 |
31 | @class NoodleCustomImageRep;
32 |
33 | /*
34 | This image rep just provides a way to specify the image drawing via a block.
35 |
36 | For more details, check out the related blog post at http://www.noodlesoft.com/blog/2011/04/15/the-proper-care-and-feeding-of-nsimage
37 | */
38 | @interface NoodleCustomImageRep : NSImageRep
39 | {
40 | void (^_drawBlock)(NoodleCustomImageRep *);
41 | }
42 |
43 | @property (readwrite, copy) void (^drawBlock)(NoodleCustomImageRep *rep);
44 |
45 | + (id)imageRepWithDrawBlock:(void (^)(NoodleCustomImageRep *))block;
46 |
47 | - (id)initWithDrawBlock:(void (^)(NoodleCustomImageRep *))block;
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/NoodleCustomImageRep.m:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleCustomImageRep.m
3 | // ImageCacheTest
4 | //
5 | // Created by Paul Kim on 3/16/11.
6 | // Copyright 2011 Noodlesoft, LLC. All rights reserved.
7 | //
8 |
9 | #import "NoodleCustomImageRep.h"
10 |
11 |
12 | @implementation NoodleCustomImageRep
13 |
14 | @synthesize drawBlock = _drawBlock;
15 |
16 | + (id)imageRepWithDrawBlock:(void (^)(NoodleCustomImageRep *))block
17 | {
18 | return [[[[self class] alloc] initWithDrawBlock:block] autorelease];
19 | }
20 |
21 | - (id)initWithDrawBlock:(void (^)(NoodleCustomImageRep *))block
22 | {
23 | if ((self = [super init]) != nil)
24 | {
25 | [self setDrawBlock:block];
26 | }
27 | return self;
28 | }
29 |
30 | #pragma mark NSCopying method
31 |
32 | - (id)copyWithZone:(NSZone *)zone
33 | {
34 | NoodleCustomImageRep *copy;
35 |
36 | copy = [super copyWithZone:zone];
37 |
38 | // NSImageRep uses NSCopyObject so we have to force a copy here
39 | copy->_drawBlock = [_drawBlock copy];
40 |
41 | return copy;
42 | }
43 |
44 | - (void)dealloc
45 | {
46 | [self setDrawBlock:nil];
47 |
48 | [super dealloc];
49 | }
50 |
51 | #pragma mark NSImageRep methods
52 |
53 | - (BOOL)draw
54 | {
55 | if (_drawBlock != NULL)
56 | {
57 | _drawBlock(self);
58 |
59 | return YES;
60 | }
61 | return NO;
62 | }
63 |
64 | @end
65 |
--------------------------------------------------------------------------------
/NoodleGlue.h:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleBlockAction.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 6/30/10.
6 | // Copyright 2010 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 |
28 | #if defined(NS_BLOCKS_AVAILABLE) && (NS_BLOCKS_AVAILABLE == 1)
29 |
30 | @class NoodleGlue;
31 |
32 | typedef void (^NoodleGlueBlock)(NoodleGlue *glue, id object);
33 | typedef void (^NoodleGlueCleanupBlock)(NoodleGlue *glue);
34 |
35 | /*
36 | In those cases where you need to pass some target object that will get some method called on it, instead of defining a
37 | new method or class to handle it, just use a block and stuff it into one of these objects and pass this object along
38 | instead.
39 |
40 | Common cases are for notifications (though blocks can be used there directly, you can provide a cleanup block here
41 | such that the object automatically unregisters itself from notifications when it is dealloc'ed/finalized). Can
42 | also be used with timers or other places that take a target/action.
43 |
44 | Things to be aware of:
45 | - Most of the time, you probably don't want this object retaining any objects it references (think about how much
46 | of the glue code you write operates). Use "__block" on any objects you don't want to be retained.
47 | - You still need to memory manage this object yourself. There's no magic about it. If you set it as a notification
48 | observer, you need to retain it somewhere because the notification center won't (or if using GC, keep a strong
49 | reference somewhere).
50 |
51 | For more details, check out the related blog post at http://www.noodlesoft.com/blog/2010/07/01/playing-with-nstimer/
52 | */
53 | @interface NoodleGlue : NSObject
54 | {
55 | NoodleGlueBlock _glueBlock;
56 | NoodleGlueCleanupBlock _cleanupBlock;
57 | }
58 |
59 | @property (readwrite, copy) NoodleGlueBlock glueBlock;
60 | @property (readwrite, copy) NoodleGlueCleanupBlock cleanupBlock;
61 |
62 | + (NoodleGlue *)glueWithBlock:(NoodleGlueBlock)glueBlock;
63 | + (NoodleGlue *)glueWithBlock:(NoodleGlueBlock)glueBlock cleanupBlock:(NoodleGlueCleanupBlock)cleanupBlock;
64 |
65 | // Initializes a glue object. glueBlock will be invoked when this object's -invoke: method is called with the argument
66 | // to -invoke: passed on as a parameter. cleanupBlock is invoked when this object is dealloc'ed/finalized with the
67 | // glue object being dealloc'ed sent in as a parameter.
68 | - (id)initWithBlock:(NoodleGlueBlock)glueBlock cleanupBlock:(NoodleGlueCleanupBlock)cleanupBlock;
69 |
70 | // Invokes the main block. When using this in a target/selector situation, use this as the selector.
71 | - (void)invoke:(id)object;
72 |
73 | @end
74 |
75 | /*
76 | NSObject category which, through the use of NoodleGlue and associative references, allows you to assign a block
77 | to be invoked when the object is deallocated.
78 |
79 | This code is more proof of concept than anything you'd want to use in production. For one, it's not threadsafe.
80 |
81 | For more details, check out the related blog post at http://www.noodlesoft.com/blog/2010/07/05/fun-with-glue/
82 | */
83 | @interface NSObject (NoodleCleanupGlue)
84 |
85 | // Sets a block to be invoked when the object is deallocated/collected. Will return an identifier that you can
86 | // use to remove the block later. Note that you need to retain this identifier if you intend to use it later.
87 | // Also, treat the identifier as an opaque object. Its actual type/formatting/structure may change in future
88 | // versions.
89 | - (id)addCleanupBlock:(void (^)(id object))block;
90 |
91 | // Removes the cleanup block using the identifier returned from a previous call to -addCleanupBlock:
92 | - (void)removeCleanupBlock:(id)identifier;
93 |
94 | @end
95 |
96 |
97 | #endif
--------------------------------------------------------------------------------
/NoodleGlue.m:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleBlockAction.m
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 6/30/10.
6 | // Copyright 2010 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "NoodleGlue.h"
27 | #import
28 |
29 | #if defined(NS_BLOCKS_AVAILABLE) && (NS_BLOCKS_AVAILABLE == 1)
30 |
31 |
32 | @implementation NoodleGlue
33 |
34 | @synthesize glueBlock = _glueBlock;
35 | @synthesize cleanupBlock = _cleanupBlock;
36 |
37 | + (NoodleGlue *)glueWithBlock:(NoodleGlueBlock)glueBlock
38 | {
39 | return [self glueWithBlock:glueBlock cleanupBlock:nil];
40 | }
41 |
42 | + (NoodleGlue *)glueWithBlock:(NoodleGlueBlock)glueBlock cleanupBlock:(NoodleGlueCleanupBlock)cleanupBlock
43 | {
44 | return [[[NoodleGlue alloc] initWithBlock:glueBlock cleanupBlock:cleanupBlock] autorelease];
45 | }
46 |
47 | - (id)initWithBlock:(NoodleGlueBlock)glueBlock cleanupBlock:(NoodleGlueCleanupBlock)cleanupBlock
48 | {
49 | if ((self = [super init]) != nil)
50 | {
51 | _glueBlock = [glueBlock copy];
52 | _cleanupBlock = [cleanupBlock copy];
53 | }
54 | return self;
55 | }
56 |
57 | - (void)dealloc
58 | {
59 | if (_cleanupBlock != NULL)
60 | {
61 | _cleanupBlock(self);
62 | }
63 |
64 | [_glueBlock release];
65 | [_cleanupBlock release];
66 |
67 | [super dealloc];
68 | }
69 |
70 | - (void)finalize
71 | {
72 | if (_cleanupBlock != NULL)
73 | {
74 | _cleanupBlock(self);
75 | }
76 |
77 | [super finalize];
78 | }
79 |
80 | - (void)invoke:(id)object
81 | {
82 | _glueBlock(self, object);
83 | }
84 |
85 | @end
86 |
87 |
88 | @implementation NSObject (NoodleCleanupGlue)
89 |
90 | static char cleanupGlueKey;
91 |
92 | - (id)addCleanupBlock:(void (^)(id object))block
93 | {
94 | NSMutableDictionary *glueTable;
95 | NoodleGlue *glue;
96 | __block __weak id blockSelf;
97 | id key;
98 |
99 | blockSelf = self;
100 | glue = [[NoodleGlue alloc] initWithBlock:nil
101 | cleanupBlock:
102 | ^(NoodleGlue *glue)
103 | {
104 | block(blockSelf);
105 | }];
106 |
107 |
108 | glueTable = objc_getAssociatedObject(self, &cleanupGlueKey);
109 |
110 | if (glueTable == nil)
111 | {
112 | glueTable = [NSMutableDictionary dictionary];
113 | objc_setAssociatedObject(self, &cleanupGlueKey, glueTable, OBJC_ASSOCIATION_RETAIN);
114 | }
115 |
116 | key = [NSString stringWithFormat:@"%p", glue];
117 | [glueTable setObject:glue forKey:key];
118 |
119 | [glue release];
120 |
121 | return key;
122 | }
123 |
124 | - (void)removeCleanupBlock:(id)identifier
125 | {
126 | NSMutableDictionary *glueTable;
127 |
128 | glueTable = objc_getAssociatedObject(self, &cleanupGlueKey);
129 |
130 | if (glueTable != nil)
131 | {
132 | NoodleGlue *glue;
133 |
134 | glue = [glueTable objectForKey:identifier];
135 |
136 | // Clear the cleanup block since we don't want it to be invoked when it gets released when it's removed
137 | // from the table
138 | [glue setCleanupBlock:nil];
139 | [glueTable removeObjectForKey:identifier];
140 |
141 | if ([glueTable count] == 0)
142 | {
143 | objc_setAssociatedObject(self, &cleanupGlueKey, nil, OBJC_ASSOCIATION_RETAIN);
144 | }
145 | }
146 | }
147 |
148 | @end
149 |
150 |
151 | #endif
152 |
--------------------------------------------------------------------------------
/NoodleIPhoneTableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleIPhoneTableView.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 9/22/09.
6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import
30 |
31 | /*
32 | Simulates UITableView's look and feel.
33 |
34 | For more details, see the related blog post at http://www.noodlesoft.com/blog/2009/09/25/sticky-section-headers-in-nstableview/
35 | */
36 |
37 | @interface NoodleIPhoneTableView : NSTableView
38 | {
39 | BOOL _isDrawingStickyRow;
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/NoodleIPhoneTableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleIPhoneTableView.m
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 9/22/09.
6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import "NoodleIPhoneTableView.h"
30 | #import "NSTableView-NoodleExtensions.h"
31 |
32 | @implementation NoodleIPhoneTableView
33 |
34 | - (id)initWithFrame:(NSRect)frameRect
35 | {
36 | if ((self = [super initWithFrame:frameRect]) != nil)
37 | {
38 | [self setGridColor:[NSColor colorWithCalibratedWhite:0.849 alpha:1.0]];
39 | [self setGridStyleMask:NSTableViewSolidHorizontalGridLineMask];
40 | }
41 | return self;
42 | }
43 |
44 | - (id)initWithCoder:(NSCoder *)decoder
45 | {
46 | if ((self = [super initWithCoder:decoder]) != nil)
47 | {
48 | [self setGridColor:[NSColor colorWithCalibratedWhite:0.849 alpha:1.0]];
49 | [self setGridStyleMask:NSTableViewSolidHorizontalGridLineMask];
50 | }
51 | return self;
52 | }
53 |
54 | - (void)drawRect:(NSRect)rect
55 | {
56 | [super drawRect:rect];
57 |
58 | [self drawStickyRowHeader];
59 | }
60 |
61 | // Since we are going to ensure that the regular and sticky versions of a row
62 | // look the same, no transition is needed here.
63 | - (NoodleStickyRowTransition)stickyRowHeaderTransition
64 | {
65 | return NoodleStickyRowTransitionNone;
66 | }
67 |
68 | - (void)drawRow:(NSInteger)rowIndex clipRect:(NSRect)clipRect
69 | {
70 | if ([self isRowSticky:rowIndex])
71 | {
72 | NSRect rowRect, cellRect;
73 | NSUInteger colIndex, count;
74 | NSCell *cell;
75 | NSGradient *gradient;
76 | NSAttributedString *attrString;
77 | NSShadow *textShadow;
78 | NSBezierPath *path;
79 | NSDictionary *attributes;
80 |
81 | rowRect = [self rectOfRow:rowIndex];
82 |
83 | if (!_isDrawingStickyRow)
84 | {
85 | // Note that NSTableView will still draw the special background that it does
86 | // for group row so we re-draw the background over it.
87 | [self drawBackgroundInClipRect:rowRect];
88 |
89 | if (NSIntersectsRect(rowRect, [self stickyRowHeaderRect]))
90 | {
91 | // You can barely notice it but if the sticky view is showing, the actual
92 | // row it represents is still seen underneath. We check for this and don't
93 | // draw the row in such a case.
94 | return;
95 | }
96 | }
97 |
98 | gradient = [[NSGradient alloc] initWithStartingColor:
99 | [NSColor colorWithCalibratedRed:0.490 green:0.556 blue:0.600 alpha:0.9]
100 | endingColor:[NSColor colorWithCalibratedRed:0.665 green:0.706 blue:0.738 alpha:0.9]];
101 |
102 | [gradient drawInRect:rowRect angle:90];
103 | [gradient release];
104 |
105 | textShadow = [[NSShadow alloc] init];
106 | [textShadow setShadowOffset:NSMakeSize(1.0, -1.0)];
107 | [textShadow setShadowColor:[NSColor colorWithCalibratedWhite:0.5 alpha:1.0]];
108 | [textShadow setShadowBlurRadius:0.0];
109 |
110 | attributes = [NSDictionary dictionaryWithObjectsAndKeys:
111 | [NSFont fontWithName:@"Helvetica-Bold" size:16.0],
112 | NSFontAttributeName,
113 | textShadow,
114 | NSShadowAttributeName,
115 | [NSColor whiteColor],
116 | NSForegroundColorAttributeName,
117 | nil];
118 | [textShadow release];
119 |
120 | count = [self numberOfColumns];
121 | for (colIndex = 0; colIndex < count; colIndex++)
122 | {
123 | cell = [self preparedCellAtColumn:colIndex row:rowIndex];
124 |
125 | attrString = [[NSAttributedString alloc] initWithString:[cell stringValue] attributes:attributes];
126 |
127 | cellRect = [self frameOfCellAtColumn:colIndex row:rowIndex];
128 |
129 | [cell setAttributedStringValue:attrString];
130 | [cell drawWithFrame:cellRect inView:self];
131 |
132 | [attrString release];
133 | }
134 |
135 | [[NSColor colorWithCalibratedWhite:0.5 alpha:1.0] set];
136 | path = [NSBezierPath bezierPath];
137 | [path moveToPoint:NSMakePoint(NSMinX(rowRect), NSMinY(rowRect))];
138 | [path lineToPoint:NSMakePoint(NSMaxX(rowRect), NSMinY(rowRect))];
139 | [path moveToPoint:NSMakePoint(NSMinX(rowRect), NSMaxY(rowRect))];
140 | [path lineToPoint:NSMakePoint(NSMaxX(rowRect), NSMaxY(rowRect))];
141 | [path stroke];
142 | }
143 | else
144 | {
145 | [super drawRow:rowIndex clipRect:clipRect];
146 | }
147 | }
148 |
149 | - (void)drawStickyRow:(NSInteger)row clipRect:(NSRect)clipRect
150 | {
151 | _isDrawingStickyRow = YES;
152 | [self drawRow:row clipRect:clipRect];
153 | _isDrawingStickyRow = NO;
154 | }
155 |
156 |
157 | @end
158 |
--------------------------------------------------------------------------------
/NoodleKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/NoodleKit_Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'NoodleKit' target in the 'NoodleKit' project.
3 | //
4 |
5 | #ifdef __OBJC__
6 | #import
7 | #endif
8 |
--------------------------------------------------------------------------------
/NoodleLineNumberMarker.h:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleLineNumberMarker.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 9/30/08.
6 | // Copyright (c) 2008 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 | //
29 |
30 | #import
31 |
32 | /*
33 | Marker for NoodleLineNumberView.
34 |
35 | For more details, see the related blog post at: http://www.noodlesoft.com/blog/2008/10/05/displaying-line-numbers-with-nstextview/
36 | */
37 |
38 | @interface NoodleLineNumberMarker : NSRulerMarker
39 | {
40 | NSUInteger _lineNumber;
41 | }
42 |
43 | - (id)initWithRulerView:(NSRulerView *)aRulerView lineNumber:(CGFloat)line image:(NSImage *)anImage imageOrigin:(NSPoint)imageOrigin;
44 |
45 | - (void)setLineNumber:(NSUInteger)line;
46 | - (NSUInteger)lineNumber;
47 |
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/NoodleLineNumberMarker.m:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleLineNumberMarker.m
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 9/30/08.
6 | // Copyright (c) 2008 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 | //
29 |
30 | #import "NoodleLineNumberMarker.h"
31 |
32 |
33 | @implementation NoodleLineNumberMarker
34 |
35 | - (id)initWithRulerView:(NSRulerView *)aRulerView lineNumber:(CGFloat)line image:(NSImage *)anImage imageOrigin:(NSPoint)imageOrigin
36 | {
37 | if ((self = [super initWithRulerView:aRulerView markerLocation:0.0 image:anImage imageOrigin:imageOrigin]) != nil)
38 | {
39 | _lineNumber = line;
40 | }
41 | return self;
42 | }
43 |
44 | - (void)setLineNumber:(NSUInteger)line
45 | {
46 | _lineNumber = line;
47 | }
48 |
49 | - (NSUInteger)lineNumber
50 | {
51 | return _lineNumber;
52 | }
53 |
54 | #pragma mark NSCoding methods
55 |
56 | #define NOODLE_LINE_CODING_KEY @"line"
57 |
58 | - (id)initWithCoder:(NSCoder *)decoder
59 | {
60 | if ((self = [super initWithCoder:decoder]) != nil)
61 | {
62 | if ([decoder allowsKeyedCoding])
63 | {
64 | _lineNumber = [[decoder decodeObjectForKey:NOODLE_LINE_CODING_KEY] unsignedIntegerValue];
65 | }
66 | else
67 | {
68 | _lineNumber = [[decoder decodeObject] unsignedIntegerValue];
69 | }
70 | }
71 | return self;
72 | }
73 |
74 | - (void)encodeWithCoder:(NSCoder *)encoder
75 | {
76 | [super encodeWithCoder:encoder];
77 |
78 | if ([encoder allowsKeyedCoding])
79 | {
80 | [encoder encodeObject:[NSNumber numberWithUnsignedInteger:_lineNumber] forKey:NOODLE_LINE_CODING_KEY];
81 | }
82 | else
83 | {
84 | [encoder encodeObject:[NSNumber numberWithUnsignedInteger:_lineNumber]];
85 | }
86 | }
87 |
88 |
89 | #pragma mark NSCopying methods
90 |
91 | - (id)copyWithZone:(NSZone *)zone
92 | {
93 | id copy;
94 |
95 | copy = [super copyWithZone:zone];
96 | [copy setLineNumber:_lineNumber];
97 |
98 | return copy;
99 | }
100 |
101 |
102 | @end
103 |
--------------------------------------------------------------------------------
/NoodleLineNumberView.h:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleLineNumberView.h
3 | // NoodleKit
4 | //
5 | // Created by Paul Kim on 9/28/08.
6 | // Copyright (c) 2008-2012 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 | //
29 |
30 | #import
31 |
32 | /*
33 | Displays line numbers for an NSTextView.
34 |
35 | For more details, see the related blog post at: http://www.noodlesoft.com/blog/2008/10/05/displaying-line-numbers-with-nstextview/
36 | */
37 |
38 | @class NoodleLineNumberMarker;
39 |
40 | @interface NoodleLineNumberView : NSRulerView
41 | {
42 | // Array of character indices for the beginning of each line
43 | NSMutableArray *_lineIndices;
44 | // When text is edited, this is the start of the editing region. All line calculations after this point are invalid
45 | // and need to be recalculated.
46 | NSUInteger _invalidCharacterIndex;
47 |
48 | // Maps line numbers to markers
49 | NSMutableDictionary *_linesToMarkers;
50 |
51 | NSFont *_font;
52 | NSColor *_textColor;
53 | NSColor *_alternateTextColor;
54 | NSColor *_backgroundColor;
55 | }
56 |
57 | @property (readwrite, retain) NSFont *font;
58 | @property (readwrite, retain) NSColor *textColor;
59 | @property (readwrite, retain) NSColor *alternateTextColor;
60 | @property (readwrite, retain) NSColor *backgroundColor;
61 |
62 | - (id)initWithScrollView:(NSScrollView *)aScrollView;
63 |
64 | - (NSUInteger)lineNumberForLocation:(CGFloat)location;
65 | - (NoodleLineNumberMarker *)markerAtLine:(NSUInteger)line;
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/NoodleTableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleRowSpanningTableView.h
3 | // NoodleRowSpanningTableViewTest
4 | //
5 | // Created by Paul Kim on 10/20/09.
6 | // Copyright 2009-2012 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import
30 |
31 | /*
32 | This NSTableView subclass provides a couple of neat features.
33 |
34 | Sticky Row Headers
35 | ==================
36 | This allows you to specify certain rows (it uses group rows by default) to "stick" to the top of the tableview
37 | scroll area when it is scrolled out of view. This is similar to how section headers work in UITableView in the
38 | iPhone SDK. The bulk of the implementation and delegate API are in the NSTableView-NoodleExtensions category.
39 |
40 | To enable this feature, just set the showsStickyRowHeader property.
41 |
42 | For more details, see the related blog post at http://www.noodlesoft.com/blog/2009/09/25/sticky-section-headers-in-nstableview/
43 |
44 |
45 | Row Spanning Columns
46 | ====================
47 |
48 | Row Spanning Columns are columns whose cells are allow to span across multiple rows. The span is determined by
49 | a contiguous section of rows that have the same object value. The cells within such a span are consolidated into
50 | a single special cell. An example of this can be seen in the Artwork column in iTunes.
51 |
52 | For any columns where you want to have this take effect, just change the column class to NoodleTableColumn and
53 | set the rowSpanningEnabled property on it. You can alternatively call -setRowSpanningEnabledForCapablyColumns
54 | on the tableview in your -awakeFromNib to enable all the NoodleTableColumns in your table in one fell swoop.
55 |
56 | 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/
57 | */
58 | @interface NoodleTableView : NSTableView
59 | {
60 | BOOL _showsStickyRowHeader;
61 |
62 | BOOL _hasSpanningColumns;
63 | BOOL _isDrawingStickyRow;
64 | }
65 |
66 | @property (readwrite, assign) BOOL showsStickyRowHeader;
67 |
68 | #pragma mark Row Spanning methods
69 |
70 | /*
71 | Enables/disables row spanning for all NoodleTableColumns in the receiver. Note that row spanning is not enabled
72 | by default so if you want row spanning, call this from -awakeFromNib is a good idea.
73 | */
74 | - (void)setRowSpanningEnabledForCapableColumns:(BOOL)flag;
75 |
76 | @end
77 |
78 |
79 | @class NoodleRowSpanningCell;
80 |
81 | /*
82 | Special table column that enables row spanning functionality. Just set your columns in IB to use this class and
83 | enable it by calling -setRowSpaningEnabled:
84 | */
85 | @interface NoodleTableColumn :NSTableColumn
86 | {
87 | BOOL _spanningEnabled;
88 | NoodleRowSpanningCell *_cell;
89 |
90 | }
91 |
92 | @property (getter=isRowSpanningEnabled, setter=setRowSpanningEnabled:) BOOL rowSpanningEnabled;
93 |
94 | @end
95 |
--------------------------------------------------------------------------------
/NoodleTableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // NoodleRowSpanningTableView.m
3 | // NoodleRowSpanningTableViewTest
4 | //
5 | // Created by Paul Kim on 10/20/09.
6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | #import "NoodleTableView.h"
30 | #import "NSTableView-NoodleExtensions.h"
31 | #import "NSImage-NoodleExtensions.h"
32 | #import "NSIndexSet-NoodleExtensions.h"
33 |
34 | /*
35 | Internal cell class. Wraps around another cell. Draws the inner cell in its "full frame" but when drawing in
36 | the tableview, draws each row sliver from the full image.
37 | */
38 | @interface NoodleRowSpanningCell : NSCell
39 | {
40 | NSRect _fullFrame;
41 | NSCell *_cell;
42 | NSImage *_cachedImage;
43 | NSColor *_backgroundColor;
44 | NSInteger _startIndex;
45 | NSInteger _lastStartIndex;
46 | NSInteger _endIndex;
47 | NSInteger _lastEndIndex;
48 | }
49 |
50 | @property NSRect fullFrame;
51 | @property (assign) NSCell *cell;
52 | @property (copy) NSColor *backgroundColor;
53 | @property NSInteger startIndex;
54 | @property NSInteger endIndex;
55 |
56 | @end
57 |
58 | @implementation NoodleRowSpanningCell
59 |
60 | @synthesize fullFrame = _fullFrame;
61 | @synthesize cell = _cell;
62 | @synthesize backgroundColor = _backgroundColor;
63 | @synthesize startIndex = _startIndex;
64 | @synthesize endIndex = _endIndex;
65 |
66 | - (void)_clearOutCaches
67 | {
68 | _startIndex = -1;
69 | _endIndex = -1;
70 | _lastStartIndex = -1;
71 | _lastEndIndex = -1;
72 | _cell = nil;
73 | }
74 |
75 | - (void)dealloc
76 | {
77 | [self _clearOutCaches];
78 | [_backgroundColor release];
79 | [_cachedImage release];
80 |
81 | [super dealloc];
82 | }
83 |
84 | - (id)copyWithZone:(NSZone *)zone
85 | {
86 | NoodleRowSpanningCell *copy;
87 |
88 | copy = [super copyWithZone:zone];
89 |
90 | // super will copy the pointer across (via NSCopyObject()) but we need to retain or copy the actual instances
91 | copy->_cachedImage = [_cachedImage copy];
92 | copy->_backgroundColor = [_backgroundColor copy];
93 |
94 | return copy;
95 | }
96 |
97 | - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
98 | {
99 | // Draw the full span of the cell into a cached image and then pull out the correct sliver as needed
100 |
101 | if ((_startIndex != _lastStartIndex) || (_endIndex != _lastEndIndex) || (_cachedImage == nil))
102 | {
103 | // If the indices have changed, we are dealing with a new span so recache the cell's full image
104 | NSAffineTransform *transform;
105 | NSColor *color;
106 |
107 | if ((_cachedImage == nil) || !NSEqualSizes(_fullFrame.size, [_cachedImage size]))
108 | {
109 | [_cachedImage release];
110 | _cachedImage = [[NSImage alloc] initWithSize:_fullFrame.size];
111 | [_cachedImage setFlipped:[controlView isFlipped]];
112 | }
113 |
114 | [_cachedImage lockFocus];
115 |
116 | transform = [NSAffineTransform transform];
117 | [transform translateXBy:-NSMinX(_fullFrame) yBy:-NSMinY(_fullFrame)];
118 | [transform concat];
119 |
120 | color = _backgroundColor;
121 | if (color == nil)
122 | {
123 | color = [NSColor clearColor];
124 | }
125 | [color set];
126 | NSRectFill(_fullFrame);
127 |
128 | [_cell drawWithFrame:_fullFrame inView:controlView];
129 |
130 | [_cachedImage unlockFocus];
131 |
132 | _lastStartIndex = _startIndex;
133 | _lastEndIndex = _endIndex;
134 | }
135 |
136 | // Now draw the sliver for the current row in the right spot
137 | [_cachedImage drawAdjustedInRect:cellFrame
138 | fromRect:NSMakeRect(NSMinX(cellFrame) - NSMinX(_fullFrame),
139 | NSMinY(cellFrame) - NSMinY(_fullFrame),
140 | NSWidth(cellFrame), NSHeight(cellFrame))
141 | operation:NSCompositeSourceOver fraction:1.0];
142 | }
143 |
144 | @end
145 |
146 | @implementation NoodleTableColumn : NSTableColumn
147 |
148 | @synthesize rowSpanningEnabled = _spanningEnabled;
149 |
150 | #define SPANNING_ENABLED_KEY @"spanningEnabled"
151 |
152 | - (void)encodeWithCoder:(NSCoder *)encoder
153 | {
154 | [super encodeWithCoder:encoder];
155 |
156 | if ([encoder allowsKeyedCoding])
157 | {
158 | [encoder encodeObject:[NSNumber numberWithBool:_spanningEnabled] forKey:SPANNING_ENABLED_KEY];
159 | }
160 | else
161 | {
162 | [encoder encodeObject:[NSNumber numberWithBool:_spanningEnabled]];
163 | }
164 | }
165 |
166 | - (id)initWithCoder:(NSCoder *)decoder
167 | {
168 | if ((self = [super initWithCoder:decoder]) != nil)
169 | {
170 | id value;
171 |
172 | if ([decoder allowsKeyedCoding])
173 | {
174 | value = [decoder decodeObjectForKey:SPANNING_ENABLED_KEY];
175 | }
176 | else
177 | {
178 | value = [decoder decodeObject];
179 | }
180 |
181 | if (value != nil)
182 | {
183 | _spanningEnabled = [value boolValue];
184 | }
185 | }
186 | return self;
187 | }
188 |
189 | - (void)dealloc
190 | {
191 | [_cell release];
192 | [super dealloc];
193 | }
194 |
195 | - (NoodleRowSpanningCell *)spanningCell
196 | {
197 | if (_cell == nil)
198 | {
199 | _cell = [[NoodleRowSpanningCell alloc] initTextCell:@""];
200 | }
201 | return _cell;
202 | }
203 |
204 | @end
205 |
206 |
207 | @implementation NoodleTableView
208 |
209 | @synthesize showsStickyRowHeader = _showsStickyRowHeader;
210 |
211 | #pragma mark NSCoding methods
212 |
213 | #define SHOWS_STICKY_ROW_HEADER_KEY @"showsStickyRowHeader"
214 |
215 | - (void)encodeWithCoder:(NSCoder *)encoder
216 | {
217 | [super encodeWithCoder:encoder];
218 |
219 | if ([encoder allowsKeyedCoding])
220 | {
221 | [encoder encodeObject:[NSNumber numberWithBool:_showsStickyRowHeader] forKey:SHOWS_STICKY_ROW_HEADER_KEY];
222 | }
223 | else
224 | {
225 | [encoder encodeObject:[NSNumber numberWithBool:_showsStickyRowHeader]];
226 | }
227 | }
228 |
229 | - (id)initWithCoder:(NSCoder *)decoder
230 | {
231 | if ((self = [super initWithCoder:decoder]) != nil)
232 | {
233 | id value;
234 |
235 | if ([decoder allowsKeyedCoding])
236 | {
237 | value = [decoder decodeObjectForKey:SHOWS_STICKY_ROW_HEADER_KEY];
238 | }
239 | else
240 | {
241 | value = [decoder decodeObject];
242 | }
243 | _showsStickyRowHeader = [value boolValue];
244 |
245 | for (NSTableColumn *column in [self tableColumns])
246 | {
247 | if ([column isKindOfClass:[NoodleTableColumn class]])
248 | {
249 | _hasSpanningColumns = YES;
250 | break;
251 | }
252 | }
253 | }
254 | return self;
255 | }
256 |
257 | - (void)addTableColumn:(NSTableColumn *)column
258 | {
259 | [super addTableColumn:column];
260 |
261 | if ([column isKindOfClass:[NoodleTableColumn class]])
262 | {
263 | _hasSpanningColumns = YES;
264 | }
265 | }
266 |
267 | - (void)removeTableColumn:(NSTableColumn *)column
268 | {
269 | [super removeTableColumn:column];
270 |
271 | for (NSTableColumn *column in [self tableColumns])
272 | {
273 | if ([column isKindOfClass:[NoodleTableColumn class]])
274 | {
275 | _hasSpanningColumns = YES;
276 | break;
277 | }
278 | }
279 | }
280 |
281 | #pragma mark Row Spanning methods
282 |
283 | - (void)setRowSpanningEnabledForCapableColumns:(BOOL)flag
284 | {
285 | for (id column in [self tableColumns])
286 | {
287 | if ([column respondsToSelector:@selector(setRowSpanningEnabled:)])
288 | {
289 | [column setRowSpanningEnabled:flag];
290 | }
291 | }
292 | }
293 |
294 | // Does the actual work of drawing the grid. Originally, was trying to set the grid mask and calling super's
295 | // -drawGridInClipRect: method on specific regions to get the effect I wanted but all the setting of the masks
296 | // ended up sucking down CPU cycles as it got into a loop queueing up tons of redraw requests.
297 | - (void)_drawGrid:(NSUInteger)gridMask inClipRect:(NSRect)aRect
298 | {
299 | NSRect rect;
300 |
301 | [[self gridColor] set];
302 |
303 | if ((gridMask & NSTableViewSolidHorizontalGridLineMask) != 0)
304 | {
305 | NSRange range;
306 | NSInteger i;
307 |
308 | range = [self rowsInRect:aRect];
309 | for (i = range.location; i < NSMaxRange(range); i++)
310 | {
311 | rect = [self rectOfRow:i];
312 | if (NSMaxY(rect) <= NSMaxY(aRect))
313 | {
314 | rect.origin.x = NSMinX(aRect);
315 | rect.size.width = NSWidth(aRect);
316 | rect.origin.y -= 0.5;
317 | [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMinX(rect), NSMaxY(rect)) toPoint:NSMakePoint(NSMaxX(rect), NSMaxY(rect))];
318 | }
319 | }
320 | }
321 | if ((gridMask & NSTableViewSolidVerticalGridLineMask) != 0)
322 | {
323 | NoodleIndexSetEnumerator *enumerator;
324 | NSInteger i;
325 |
326 | enumerator = [[self columnIndexesInRect:aRect] indexEnumerator];
327 | while ((i = [enumerator nextIndex]) != NSNotFound)
328 | {
329 | rect = [self rectOfColumn:i];
330 | if (NSMaxX(rect) <= NSMaxX(aRect))
331 | {
332 | rect.origin.y = NSMinY(aRect);
333 | rect.size.height = NSHeight(aRect);
334 | rect.origin.x -= 0.5;
335 | [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX(rect), NSMinY(rect)) toPoint:NSMakePoint(NSMaxX(rect), NSMaxY(rect))];
336 | }
337 | }
338 | }
339 | }
340 |
341 | - (void)drawGridInClipRect:(NSRect)aRect
342 | {
343 | NSUInteger origGridMask;
344 |
345 | origGridMask = [self gridStyleMask];
346 |
347 | if (_hasSpanningColumns && ((origGridMask & NSTableViewSolidHorizontalGridLineMask) != 0))
348 | {
349 | // Rules:
350 | // - No spanning cell should have grid lines within it. Only at the bottom.
351 | // - Non-spanning cells inherit their grid lines from the closest spanning cell to the left (or to the right
352 | // if there are none to the left.
353 |
354 | NSRange range, spanRange;
355 | NSInteger columnIndex, endColumnIndex, startColumnIndex, row;
356 | NSMutableIndexSet *columnIndexes;
357 | NoodleIndexSetEnumerator *enumerator;
358 | NSRect topLeft, bottomRight, rect;
359 |
360 | // Grab the indexes of all the spanning columns.
361 | columnIndex = 0;
362 | columnIndexes = [NSMutableIndexSet indexSet];
363 | for (NSTableColumn *column in [self tableColumns])
364 | {
365 | if ([column isRowSpanningEnabled])
366 | {
367 | [columnIndexes addIndex:columnIndex];
368 | }
369 | columnIndex++;
370 | }
371 |
372 | // Hard to explain but we calculate regions going from left to right, defining regions horizontally
373 | // from one spanning column to the next and vertically by row spans. We first draw the non-horizontal
374 | // grid lines within a span (taking into account precedence rules concerning columns as noted above).
375 | // Then we draw the horizontal grid line at the bottom of a span. We are trying to find the maximal
376 | // regions to send to the grid drawing routine as doing it cell by cell incurs a bit of overhead.
377 | startColumnIndex = 0;
378 | enumerator = [columnIndexes indexEnumerator];
379 | while ((columnIndex = [enumerator nextIndex]) != NSNotFound)
380 | {
381 | // This column is the right edge of this region (which is up to the next spanning column)
382 | endColumnIndex = [columnIndexes indexGreaterThanIndex:columnIndex];
383 | if (endColumnIndex == NSNotFound)
384 | {
385 | endColumnIndex = [self numberOfColumns] - 1;
386 | }
387 | else
388 | {
389 | endColumnIndex--;
390 | }
391 |
392 | range = [self rowsInRect:aRect];
393 |
394 | row = range.location;
395 | while (row < NSMaxRange(range))
396 | {
397 | spanRange = [self rangeOfRowSpanAtColumn:columnIndex row:row];
398 |
399 | // Get the rects of the top left of our region (the start column and start row of the span)
400 | // to the bottom right (the end column and the row in the span just before the last one).
401 | topLeft = [self frameOfCellAtColumn:startColumnIndex row:spanRange.location];
402 | bottomRight = [self frameOfCellAtColumn:endColumnIndex row:NSMaxRange(spanRange) - 2];
403 |
404 | rect = NSIntersectionRect(aRect, NSUnionRect(topLeft, bottomRight));
405 |
406 | if (spanRange.length > 1)
407 | {
408 | // Draw span region without horizontal grid lines
409 | [self _drawGrid:origGridMask & ~NSTableViewSolidHorizontalGridLineMask inClipRect:rect];
410 |
411 | // Now, calculate the region at the last row of the span
412 | topLeft = [self frameOfCellAtColumn:startColumnIndex row:NSMaxRange(spanRange) - 1];
413 | bottomRight = [self frameOfCellAtColumn:endColumnIndex row:NSMaxRange(spanRange) - 1];
414 |
415 | // Draw bottom of span with horizontal grid lines
416 | rect = NSIntersectionRect(aRect, NSUnionRect(topLeft, bottomRight) );
417 | [self _drawGrid:origGridMask inClipRect:rect];
418 | }
419 | else
420 | {
421 | // Not a span row or just a single row. Either way, draw with horizontal grid lines
422 | [self _drawGrid:origGridMask inClipRect:rect];
423 | }
424 | // Advance to the next row span
425 | row = NSMaxRange(spanRange);
426 | }
427 | // Advance to the next span column region
428 | startColumnIndex = endColumnIndex + 1;
429 | }
430 | }
431 | else
432 | {
433 | // We only need the special logic when we have row spanning columns and drawing horizontal lines. Otherwise,
434 | // just call super.
435 | [super drawGridInClipRect:aRect];
436 | }
437 | }
438 |
439 | - (NSCell *)preparedCellAtColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex
440 | {
441 | NSTableColumn *column;
442 |
443 | column = [[self tableColumns] objectAtIndex:columnIndex];
444 |
445 | if (!_isDrawingStickyRow && [column isRowSpanningEnabled])
446 | {
447 | NSRange range;
448 |
449 | range = [self rangeOfRowSpanAtColumn:columnIndex row:rowIndex];
450 |
451 | if (range.length >= 1)
452 | {
453 | // Here is where we insert our special cell for row spanning behavior
454 | NoodleRowSpanningCell *spanningCell;
455 | NSCell *cell;
456 | NSInteger start, end;
457 | BOOL wasSelected;
458 |
459 | start = range.location;
460 | end = NSMaxRange(range) - 1;
461 |
462 | // Want to draw cell in its unhighlighted state since spanning cells aren't selectable. Unfortuantely,
463 | // can't just setHighlight:NO on it because NSTableView sets other attributes (like text color).
464 | // Instead, we deselect the row, grab the cell, then set it back (if it was selected before).
465 | wasSelected = [self isRowSelected:start];
466 | [self deselectRow:start];
467 |
468 | cell = [super preparedCellAtColumn:columnIndex row:start];
469 |
470 | if (wasSelected)
471 | {
472 | [self selectRowIndexes:[NSIndexSet indexSetWithIndex:start] byExtendingSelection:YES];
473 | }
474 |
475 | spanningCell = [column spanningCell];
476 | [spanningCell setCell:cell];
477 |
478 | // The full frame is the rect encompassing the first and last rows of the span.
479 | [spanningCell setFullFrame:NSUnionRect([self frameOfCellAtColumn:columnIndex row:start],
480 | [self frameOfCellAtColumn:columnIndex row:end])];
481 | [spanningCell setStartIndex:start];
482 | [spanningCell setEndIndex:end];
483 |
484 | [spanningCell setBackgroundColor:[self backgroundColor]];
485 |
486 | return spanningCell;
487 | }
488 | }
489 | return [super preparedCellAtColumn:columnIndex row:rowIndex];
490 | }
491 |
492 | - (void)mouseDown:(NSEvent *)event
493 | {
494 | if (_hasSpanningColumns)
495 | {
496 | // Eat up any clicks on spanning cells. In the future, may want to consider having clicks select the
497 | // first row in the span (like when clicking on the artwork in iTunes).
498 |
499 | NSPoint point;
500 | NSInteger columnIndex;
501 | NSTableColumn *column;
502 |
503 | point = [event locationInWindow];
504 | point = [self convertPointFromBase:point];
505 |
506 | columnIndex = [self columnAtPoint:point];
507 | column = [[self tableColumns] objectAtIndex:columnIndex];
508 | if ([column isRowSpanningEnabled])
509 | {
510 | return;
511 | }
512 | }
513 | [super mouseDown:event];
514 | }
515 |
516 |
517 | - (void)drawRect:(NSRect)dirtyRect
518 | {
519 | [super drawRect:dirtyRect];
520 |
521 | // All that needs to be done to enable the sticky row header functionality (bulk of the work
522 | // done in the NSTableView category)
523 | if ([self showsStickyRowHeader])
524 | {
525 | [self drawStickyRowHeader];
526 | }
527 |
528 | if (_hasSpanningColumns)
529 | {
530 | // Clean up any cached data. Don't want to keep stale cache data around.
531 | for (NSTableColumn *column in [self tableColumns])
532 | {
533 | if ([column isRowSpanningEnabled])
534 | {
535 | [[column spanningCell] _clearOutCaches];
536 | }
537 | }
538 | }
539 | }
540 |
541 | - (void)drawStickyRow:(NSInteger)row clipRect:(NSRect)clipRect
542 | {
543 | _isDrawingStickyRow = YES;
544 | [super drawStickyRow:row clipRect:clipRect];
545 | _isDrawingStickyRow = NO;
546 | }
547 |
548 | @end
549 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | NoodleKit
2 | =========
3 |
4 | This is a random collection of classes and categories that I am making public. Most of this code has been posted on my blog:
5 |
6 | The project is primarily structured to build a framework. There are targets for various examples showing how the different classes are used. Some of the examples also contain a Read Me file so check those out for more details on the specific classes.
7 |
8 | This framework is meant to be built/used on 10.6 and later and should support 64-bit.
9 |
10 | This code is maintained at . Please post any issues and requests there.
11 |
12 | What nifty stuff is in here?
13 | ----------------------------
14 |
15 | #### NSObject-NoodlePerformWhenIdle
16 | 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.
17 |
18 |
19 | #### NSIndexSet-NoodleExtensions
20 | 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).
21 |
22 | #### NSTimer-NoodleExtensions
23 | Allows you to create timers that treat the fire date as absolute. Normally, NSTimer will adjust the time if you put the machine to sleep. This category makes it such that it will fire on the date you told it to originally.
24 |
25 |
26 | #### NoodleGlue
27 | Little class that allows you to plug a block into some code that requires a target/selector. Check the NSTimer category too see how it can be used.
28 |
29 |
30 | #### NSObject-NoodleCleanupGlue
31 | A category on NSObject that allows you to add a block that will be executed when the object is deallocated. It is based on NoodleGlue and it is lumped into the same source file with it.
32 |
33 |
34 | #### NSResponder-NoodleModalExtensions
35 | 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.
36 |
37 |
38 | #### NSImage-NoodleExtensions
39 | 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.
40 |
41 |
42 | #### NoodleCustomImageRep
43 | NSImageRep subclass that allows you to specify the drawing via a block. Handy for drawing images without having to create a new subclass of NSImageRep.
44 |
45 |
46 | #### NSWindow-NoodleEffects
47 | Provides a basic zoom effect for NSWindow.
48 |
49 |
50 |
51 | #### NoodleLineNumberView, NoodleLineNumberMarker
52 | Adds line numbers (and corresponding markers) to NSTextView.
53 |
54 |
55 | #### NSTableView-NoodleExtensions, NoodleTableView, NoodleIPhoneTableView
56 | The NSTableView category and NoodleTableView are a consolidation of the sticky row header tableview
57 | and row spanning tableview featured on my blog.
58 |
59 | #####Sticky Row Headers
60 | 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.
61 |
62 |
63 | #####Row Spanning Columns
64 | 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.
65 |
66 |
67 |
68 | License
69 | -------
70 |
71 | Copyright (c) 2007-2012 Noodlesoft, LLC. All Rights Reserved.
72 |
73 | Permission is hereby granted, free of charge, to any person
74 | obtaining a copy of this software and associated documentation
75 | files (the "Software"), to deal in the Software without
76 | restriction, including without limitation the rights to use,
77 | copy, modify, merge, publish, distribute, sublicense, and/or sell
78 | copies of the Software, and to permit persons to whom the
79 | Software is furnished to do so, subject to the following
80 | conditions:
81 |
82 | The above copyright notice and this permission notice shall be
83 | included in all copies or substantial portions of the Software.
84 |
85 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
87 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
88 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
89 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
91 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
92 | OTHER DEALINGS IN THE SOFTWARE.
93 |
--------------------------------------------------------------------------------
/version.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildVersion
6 | 2
7 | CFBundleShortVersionString
8 | 1.0
9 | CFBundleVersion
10 | 1
11 | ProjectName
12 | DevToolsWizardTemplates
13 | SourceVersion
14 | 15920000
15 |
16 |
17 |
--------------------------------------------------------------------------------