├── Demo-Info.plist
├── DemoSectionView.h
├── DemoSectionView.m
├── DemoSectionView.xib
├── DemoView.h
├── DemoView.m
├── DemoView.xib
├── English.lproj
├── InfoPlist.strings
└── MainMenu.xib
├── JAEdgeInsets.h
├── JAListView-Info.plist
├── JAListView.h
├── JAListView.m
├── JAListView.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── JAListViewAppDelegate.h
├── JAListViewAppDelegate.m
├── JAListViewItem.h
├── JAListViewItem.m
├── JAListView_Prefix.pch
├── JAObjectListView.h
├── JAObjectListView.m
├── JAObjectListViewItem.h
├── JAObjectListViewItem.m
├── JASectionedListView.h
├── JASectionedListView.m
├── NSIndexPath+JAListViewExtensions.h
├── NSIndexPath+JAListViewExtensions.m
├── README
└── main.m
/Demo-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 |
--------------------------------------------------------------------------------
/DemoSectionView.h:
--------------------------------------------------------------------------------
1 | //
2 | // DemoSectionView.h
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 11/26/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "JAListViewItem.h"
11 |
12 |
13 | @interface DemoSectionView : JAListViewItem {}
14 |
15 | + (DemoSectionView *)demoSectionView;
16 |
17 | @property (assign) IBOutlet NSTextField *textField;
18 | @property (assign) IBOutlet NSTextField *shadowTextField;
19 | @property (nonatomic, copy) NSString *text;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/DemoSectionView.m:
--------------------------------------------------------------------------------
1 | //
2 | // DemoSectionView.m
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 11/26/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import "DemoSectionView.h"
10 |
11 |
12 | @implementation DemoSectionView
13 |
14 | + (DemoSectionView *)demoSectionView {
15 | static NSNib *nib = nil;
16 | if(nib == nil) {
17 | nib = [[NSNib alloc] initWithNibNamed:NSStringFromClass(self) bundle:nil];
18 | }
19 |
20 | NSArray *objects = nil;
21 | [nib instantiateNibWithOwner:nil topLevelObjects:&objects];
22 | for(id object in objects) {
23 | if([object isKindOfClass:self]) {
24 | return object;
25 | }
26 | }
27 |
28 | NSAssert1(NO, @"No view of class %@ found.", NSStringFromClass(self));
29 | return nil;
30 | }
31 |
32 |
33 | #pragma mark NSView
34 |
35 | - (void)drawRect:(NSRect)dirtyRect {
36 | [super drawRect:dirtyRect];
37 |
38 | [[NSColor lightGrayColor] set];
39 | NSRectFill(dirtyRect);
40 | }
41 |
42 |
43 | #pragma mark API
44 |
45 | @synthesize textField;
46 | @synthesize shadowTextField;
47 |
48 | - (void)setText:(NSString *)text {
49 | [self.textField setStringValue:[[text copy] autorelease]];
50 | [self.shadowTextField setStringValue:[[text copy] autorelease]];
51 | }
52 |
53 | - (NSString *)text {
54 | return [self.textField stringValue];
55 | }
56 |
57 | @end
58 |
--------------------------------------------------------------------------------
/DemoSectionView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1060
5 | 10H574
6 | 823
7 | 1038.35
8 | 461.00
9 |
13 |
17 |
21 |
30 |
31 | YES
32 |
33 | NSObject
34 |
35 |
36 | FirstResponder
37 |
38 |
39 | NSApplication
40 |
41 |
42 |
43 | 268
44 |
45 | YES
46 |
47 |
48 | 298
49 | {{-3, 16}, {486, 17}}
50 |
51 | YES
52 |
53 | 68288064
54 | 138413056
55 | Section!
56 |
57 | Helvetica-Bold
58 | 13
59 | 16
60 |
61 |
62 |
63 | 6
64 | System
65 | controlColor
66 |
67 | 3
68 | MC42NjY2NjY2NjY3AA
69 |
70 |
71 |
72 | 6
73 | System
74 | controlTextColor
75 |
76 | 3
77 | MAA
78 |
79 |
80 |
81 |
82 |
83 |
84 | 298
85 | {{-3, 15}, {486, 17}}
86 |
87 | YES
88 |
89 | 68288064
90 | 138413056
91 | Section!
92 |
93 |
94 |
95 |
96 | 4
97 | MSAwLjcAA
98 |
99 |
100 |
101 |
102 | {480, 50}
103 |
104 | DemoSectionView
105 |
106 |
107 |
108 |
109 | YES
110 |
111 |
112 | textField
113 |
114 |
115 |
116 | 6
117 |
118 |
119 |
120 | shadowTextField
121 |
122 |
123 |
124 | 7
125 |
126 |
127 |
128 |
129 | YES
130 |
131 | 0
132 |
133 |
134 |
135 |
136 |
137 | -2
138 |
139 |
140 | File's Owner
141 |
142 |
143 | -1
144 |
145 |
146 | First Responder
147 |
148 |
149 | -3
150 |
151 |
152 | Application
153 |
154 |
155 | 1
156 |
157 |
158 | YES
159 |
160 |
161 |
162 |
163 |
164 |
165 | 2
166 |
167 |
168 | YES
169 |
170 |
171 |
172 |
173 |
174 | 3
175 |
176 |
177 |
178 |
179 | 4
180 |
181 |
182 | YES
183 |
184 |
185 |
186 |
187 |
188 | 5
189 |
190 |
191 |
192 |
193 |
194 |
195 | YES
196 |
197 | YES
198 | 1.IBEditorWindowLastContentRect
199 | 1.IBPluginDependency
200 | 1.WindowOrigin
201 | 1.editorWindowContentRectSynchronizationRect
202 | 2.IBPluginDependency
203 | 2.IBViewBoundsToFrameTransform
204 | 3.IBPluginDependency
205 | 4.IBPluginDependency
206 | 4.IBViewBoundsToFrameTransform
207 | 5.IBPluginDependency
208 |
209 |
210 | YES
211 | {{322, 806}, {480, 50}}
212 | com.apple.InterfaceBuilder.CocoaPlugin
213 | {628, 654}
214 | {{217, 442}, {480, 272}}
215 | com.apple.InterfaceBuilder.CocoaPlugin
216 |
217 | P4AAAL+AAADAQAAAwfgAAA
218 |
219 | com.apple.InterfaceBuilder.CocoaPlugin
220 | com.apple.InterfaceBuilder.CocoaPlugin
221 |
222 | P4AAAL+AAABDJAAAwVAAAA
223 |
224 | com.apple.InterfaceBuilder.CocoaPlugin
225 |
226 |
227 |
228 | YES
229 |
230 |
231 | YES
232 |
233 |
234 |
235 |
236 | YES
237 |
238 |
239 | YES
240 |
241 |
242 |
243 | 7
244 |
245 |
246 |
247 | YES
248 |
249 | DemoSectionView
250 | JAListViewItemView
251 |
252 | YES
253 |
254 | YES
255 | shadowTextField
256 | textField
257 |
258 |
259 | YES
260 | NSTextField
261 | NSTextField
262 |
263 |
264 |
265 | YES
266 |
267 | YES
268 | shadowTextField
269 | textField
270 |
271 |
272 | YES
273 |
274 | shadowTextField
275 | NSTextField
276 |
277 |
278 | textField
279 | NSTextField
280 |
281 |
282 |
283 |
284 | IBProjectSource
285 | DemoSectionView.h
286 |
287 |
288 |
289 | JAListViewItemView
290 | NSView
291 |
292 | IBProjectSource
293 | JAListViewItemView.h
294 |
295 |
296 |
297 |
298 | YES
299 |
300 | NSActionCell
301 | NSCell
302 |
303 | IBFrameworkSource
304 | AppKit.framework/Headers/NSActionCell.h
305 |
306 |
307 |
308 | NSApplication
309 | NSResponder
310 |
311 | IBFrameworkSource
312 | AppKit.framework/Headers/NSApplication.h
313 |
314 |
315 |
316 | NSApplication
317 |
318 | IBFrameworkSource
319 | AppKit.framework/Headers/NSApplicationScripting.h
320 |
321 |
322 |
323 | NSApplication
324 |
325 | IBFrameworkSource
326 | AppKit.framework/Headers/NSColorPanel.h
327 |
328 |
329 |
330 | NSApplication
331 |
332 | IBFrameworkSource
333 | AppKit.framework/Headers/NSHelpManager.h
334 |
335 |
336 |
337 | NSApplication
338 |
339 | IBFrameworkSource
340 | AppKit.framework/Headers/NSPageLayout.h
341 |
342 |
343 |
344 | NSApplication
345 |
346 | IBFrameworkSource
347 | AppKit.framework/Headers/NSUserInterfaceItemSearching.h
348 |
349 |
350 |
351 | NSCell
352 | NSObject
353 |
354 | IBFrameworkSource
355 | AppKit.framework/Headers/NSCell.h
356 |
357 |
358 |
359 | NSControl
360 | NSView
361 |
362 | IBFrameworkSource
363 | AppKit.framework/Headers/NSControl.h
364 |
365 |
366 |
367 | NSFormatter
368 | NSObject
369 |
370 | IBFrameworkSource
371 | Foundation.framework/Headers/NSFormatter.h
372 |
373 |
374 |
375 | NSMenu
376 | NSObject
377 |
378 | IBFrameworkSource
379 | AppKit.framework/Headers/NSMenu.h
380 |
381 |
382 |
383 | NSObject
384 |
385 | IBFrameworkSource
386 | AppKit.framework/Headers/NSAccessibility.h
387 |
388 |
389 |
390 | NSObject
391 |
392 |
393 |
394 | NSObject
395 |
396 |
397 |
398 | NSObject
399 |
400 |
401 |
402 | NSObject
403 |
404 |
405 |
406 | NSObject
407 |
408 | IBFrameworkSource
409 | AppKit.framework/Headers/NSDictionaryController.h
410 |
411 |
412 |
413 | NSObject
414 |
415 | IBFrameworkSource
416 | AppKit.framework/Headers/NSDragging.h
417 |
418 |
419 |
420 | NSObject
421 |
422 | IBFrameworkSource
423 | AppKit.framework/Headers/NSFontManager.h
424 |
425 |
426 |
427 | NSObject
428 |
429 | IBFrameworkSource
430 | AppKit.framework/Headers/NSFontPanel.h
431 |
432 |
433 |
434 | NSObject
435 |
436 | IBFrameworkSource
437 | AppKit.framework/Headers/NSKeyValueBinding.h
438 |
439 |
440 |
441 | NSObject
442 |
443 |
444 |
445 | NSObject
446 |
447 | IBFrameworkSource
448 | AppKit.framework/Headers/NSNibLoading.h
449 |
450 |
451 |
452 | NSObject
453 |
454 | IBFrameworkSource
455 | AppKit.framework/Headers/NSOutlineView.h
456 |
457 |
458 |
459 | NSObject
460 |
461 | IBFrameworkSource
462 | AppKit.framework/Headers/NSPasteboard.h
463 |
464 |
465 |
466 | NSObject
467 |
468 | IBFrameworkSource
469 | AppKit.framework/Headers/NSSavePanel.h
470 |
471 |
472 |
473 | NSObject
474 |
475 | IBFrameworkSource
476 | AppKit.framework/Headers/NSTableView.h
477 |
478 |
479 |
480 | NSObject
481 |
482 | IBFrameworkSource
483 | AppKit.framework/Headers/NSToolbarItem.h
484 |
485 |
486 |
487 | NSObject
488 |
489 | IBFrameworkSource
490 | AppKit.framework/Headers/NSView.h
491 |
492 |
493 |
494 | NSObject
495 |
496 | IBFrameworkSource
497 | Foundation.framework/Headers/NSArchiver.h
498 |
499 |
500 |
501 | NSObject
502 |
503 | IBFrameworkSource
504 | Foundation.framework/Headers/NSClassDescription.h
505 |
506 |
507 |
508 | NSObject
509 |
510 | IBFrameworkSource
511 | Foundation.framework/Headers/NSError.h
512 |
513 |
514 |
515 | NSObject
516 |
517 | IBFrameworkSource
518 | Foundation.framework/Headers/NSFileManager.h
519 |
520 |
521 |
522 | NSObject
523 |
524 | IBFrameworkSource
525 | Foundation.framework/Headers/NSKeyValueCoding.h
526 |
527 |
528 |
529 | NSObject
530 |
531 | IBFrameworkSource
532 | Foundation.framework/Headers/NSKeyValueObserving.h
533 |
534 |
535 |
536 | NSObject
537 |
538 | IBFrameworkSource
539 | Foundation.framework/Headers/NSKeyedArchiver.h
540 |
541 |
542 |
543 | NSObject
544 |
545 | IBFrameworkSource
546 | Foundation.framework/Headers/NSObject.h
547 |
548 |
549 |
550 | NSObject
551 |
552 | IBFrameworkSource
553 | Foundation.framework/Headers/NSObjectScripting.h
554 |
555 |
556 |
557 | NSObject
558 |
559 | IBFrameworkSource
560 | Foundation.framework/Headers/NSPortCoder.h
561 |
562 |
563 |
564 | NSObject
565 |
566 | IBFrameworkSource
567 | Foundation.framework/Headers/NSRunLoop.h
568 |
569 |
570 |
571 | NSObject
572 |
573 | IBFrameworkSource
574 | Foundation.framework/Headers/NSScriptClassDescription.h
575 |
576 |
577 |
578 | NSObject
579 |
580 | IBFrameworkSource
581 | Foundation.framework/Headers/NSScriptKeyValueCoding.h
582 |
583 |
584 |
585 | NSObject
586 |
587 | IBFrameworkSource
588 | Foundation.framework/Headers/NSScriptObjectSpecifiers.h
589 |
590 |
591 |
592 | NSObject
593 |
594 | IBFrameworkSource
595 | Foundation.framework/Headers/NSScriptWhoseTests.h
596 |
597 |
598 |
599 | NSObject
600 |
601 | IBFrameworkSource
602 | Foundation.framework/Headers/NSThread.h
603 |
604 |
605 |
606 | NSObject
607 |
608 | IBFrameworkSource
609 | Foundation.framework/Headers/NSURL.h
610 |
611 |
612 |
613 | NSObject
614 |
615 | IBFrameworkSource
616 | Foundation.framework/Headers/NSURLConnection.h
617 |
618 |
619 |
620 | NSObject
621 |
622 | IBFrameworkSource
623 | Foundation.framework/Headers/NSURLDownload.h
624 |
625 |
626 |
627 | NSResponder
628 |
629 | IBFrameworkSource
630 | AppKit.framework/Headers/NSInterfaceStyle.h
631 |
632 |
633 |
634 | NSResponder
635 | NSObject
636 |
637 | IBFrameworkSource
638 | AppKit.framework/Headers/NSResponder.h
639 |
640 |
641 |
642 | NSTextField
643 | NSControl
644 |
645 | IBFrameworkSource
646 | AppKit.framework/Headers/NSTextField.h
647 |
648 |
649 |
650 | NSTextFieldCell
651 | NSActionCell
652 |
653 | IBFrameworkSource
654 | AppKit.framework/Headers/NSTextFieldCell.h
655 |
656 |
657 |
658 | NSView
659 |
660 | IBFrameworkSource
661 | AppKit.framework/Headers/NSClipView.h
662 |
663 |
664 |
665 | NSView
666 |
667 | IBFrameworkSource
668 | AppKit.framework/Headers/NSMenuItem.h
669 |
670 |
671 |
672 | NSView
673 |
674 | IBFrameworkSource
675 | AppKit.framework/Headers/NSRulerView.h
676 |
677 |
678 |
679 | NSView
680 | NSResponder
681 |
682 |
683 |
684 |
685 | 0
686 | IBCocoaFramework
687 |
688 | com.apple.InterfaceBuilder.CocoaPlugin.macosx
689 |
690 |
691 |
692 | com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3
693 |
694 |
695 | YES
696 | JAListView.xcodeproj
697 | 3
698 |
699 |
700 |
--------------------------------------------------------------------------------
/DemoView.h:
--------------------------------------------------------------------------------
1 | //
2 | // DemoView.h
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 9/29/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "JAListViewItem.h"
11 |
12 |
13 | @interface DemoView : JAListViewItem {
14 | NSGradient *gradient;
15 | BOOL selected;
16 | NSTextField *textField;
17 | NSTextField *shadowTextField;
18 | }
19 |
20 | + (DemoView *)demoView;
21 |
22 | @property (nonatomic, copy) NSString *text;
23 | @property (retain) IBOutlet NSTextField *textField;
24 | @property (retain) IBOutlet NSTextField *shadowTextField;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/DemoView.m:
--------------------------------------------------------------------------------
1 | //
2 | // DemoView.m
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 9/29/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import "DemoView.h"
10 |
11 | @interface DemoView ()
12 | - (void)drawBackground;
13 |
14 | @property (nonatomic, readonly) NSGradient *gradient;
15 | @end
16 |
17 |
18 | @implementation DemoView
19 |
20 | + (DemoView *)demoView {
21 | static NSNib *nib = nil;
22 | if(nib == nil) {
23 | nib = [[NSNib alloc] initWithNibNamed:NSStringFromClass(self) bundle:nil];
24 | }
25 |
26 | NSArray *objects = nil;
27 | [nib instantiateNibWithOwner:nil topLevelObjects:&objects];
28 | for(id object in objects) {
29 | if([object isKindOfClass:self]) {
30 | return object;
31 | }
32 | }
33 |
34 | NSAssert1(NO, @"No view of class %@ found.", NSStringFromClass(self));
35 | return nil;
36 | }
37 |
38 |
39 | #pragma mark NSView
40 |
41 | - (void)drawRect:(NSRect)rect {
42 | [super drawRect:rect];
43 | [self drawBackground];
44 | }
45 |
46 |
47 | #pragma mark API
48 |
49 | - (void)drawBackground {
50 | [self.gradient drawInRect:self.bounds angle:self.selected ? 270.0f : 90.0f];
51 |
52 | [[NSColor colorWithDeviceWhite:0.5f alpha:1.0f] set];
53 | NSRectFill(NSMakeRect(0.0f, 0.0f, self.bounds.size.width, 1.0f));
54 |
55 | [[NSColor colorWithDeviceWhite:0.93f alpha:1.0f] set];
56 | NSRectFill(NSMakeRect(0.0f, self.bounds.size.height - 1.0f, self.bounds.size.width, 1.0f));
57 | }
58 |
59 | - (NSGradient *)gradient {
60 | if(gradient == nil) {
61 | gradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceWhite:0.8f alpha:1.0f] endingColor:[NSColor colorWithDeviceWhite:0.85f alpha:1.0f]];
62 | }
63 |
64 | return gradient;
65 | }
66 |
67 | - (void)setText:(NSString *)newText {
68 | NSString *newValue = [[newText copy] autorelease];
69 | [self.textField setStringValue:newValue];
70 | [self.shadowTextField setStringValue:newValue];
71 | }
72 |
73 | - (NSString *)text {
74 | return [self.textField stringValue];
75 | }
76 |
77 | - (void)setSelected:(BOOL)isSelected {
78 | selected = isSelected;
79 |
80 | [self setNeedsDisplay:YES];
81 | }
82 |
83 | @synthesize selected;
84 | @synthesize textField;
85 | @synthesize shadowTextField;
86 |
87 | @end
88 |
--------------------------------------------------------------------------------
/DemoView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1060
5 | 10H574
6 | 823
7 | 1038.35
8 | 461.00
9 |
10 | com.apple.InterfaceBuilder.CocoaPlugin
11 | 823
12 |
13 |
14 | YES
15 |
16 |
17 |
18 | YES
19 | com.apple.InterfaceBuilder.CocoaPlugin
20 |
21 |
22 | YES
23 |
24 | YES
25 |
26 |
27 | YES
28 |
29 |
30 |
31 | YES
32 |
33 | NSObject
34 |
35 |
36 | FirstResponder
37 |
38 |
39 | NSApplication
40 |
41 |
42 |
43 | 266
44 |
45 | YES
46 |
47 |
48 | 298
49 | {{17, 18}, {353, 17}}
50 |
51 | 2
52 | YES
53 |
54 | 67239488
55 | 272631808
56 | Label
57 |
58 | HelveticaNeue-Bold
59 | 14
60 | 16
61 |
62 |
63 |
64 | 6
65 | System
66 | controlColor
67 |
68 | 3
69 | MC42NjY2NjY2NjY3AA
70 |
71 |
72 |
73 | 6
74 | System
75 | controlTextColor
76 |
77 | 3
78 | MAA
79 |
80 |
81 |
82 |
83 |
84 |
85 | 298
86 | {{17, 13}, {353, 21}}
87 |
88 | 2
89 | YES
90 |
91 | 67239488
92 | 272631808
93 | Label
94 |
95 |
96 |
97 |
98 | 1
99 | MSAxIDEgMC43AA
100 |
101 |
102 |
103 |
104 | {387, 50}
105 |
106 | 2
107 | DemoView
108 |
109 |
110 |
111 |
112 | YES
113 |
114 |
115 | textField
116 |
117 |
118 |
119 | 7
120 |
121 |
122 |
123 | shadowTextField
124 |
125 |
126 |
127 | 10
128 |
129 |
130 |
131 |
132 | YES
133 |
134 | 0
135 |
136 |
137 |
138 |
139 |
140 | -2
141 |
142 |
143 | File's Owner
144 |
145 |
146 | -1
147 |
148 |
149 | First Responder
150 |
151 |
152 | -3
153 |
154 |
155 | Application
156 |
157 |
158 | 1
159 |
160 |
161 | YES
162 |
163 |
164 |
165 |
166 |
167 |
168 | 2
169 |
170 |
171 | YES
172 |
173 |
174 |
175 |
176 |
177 | 3
178 |
179 |
180 |
181 |
182 | 5
183 |
184 |
185 | YES
186 |
187 |
188 |
189 |
190 |
191 | 6
192 |
193 |
194 |
195 |
196 |
197 |
198 | YES
199 |
200 | YES
201 | 1.IBEditorWindowLastContentRect
202 | 1.IBPluginDependency
203 | 1.WindowOrigin
204 | 1.editorWindowContentRectSynchronizationRect
205 | 2.IBPluginDependency
206 | 2.IBViewBoundsToFrameTransform
207 | 3.IBPluginDependency
208 | 5.IBPluginDependency
209 | 6.IBPluginDependency
210 |
211 |
212 | YES
213 | {{322, 706}, {387, 50}}
214 | com.apple.InterfaceBuilder.CocoaPlugin
215 | {628, 654}
216 | {{217, 442}, {480, 272}}
217 | com.apple.InterfaceBuilder.CocoaPlugin
218 |
219 | P4AAAL+AAABCFAAAwoYAAA
220 |
221 | com.apple.InterfaceBuilder.CocoaPlugin
222 | com.apple.InterfaceBuilder.CocoaPlugin
223 | com.apple.InterfaceBuilder.CocoaPlugin
224 |
225 |
226 |
227 | YES
228 |
229 |
230 | YES
231 |
232 |
233 |
234 |
235 | YES
236 |
237 |
238 | YES
239 |
240 |
241 |
242 | 10
243 |
244 |
245 |
246 | YES
247 |
248 | DemoView
249 | JAListViewItemView
250 |
251 | YES
252 |
253 | YES
254 | shadowTextField
255 | textField
256 |
257 |
258 | YES
259 | NSTextField
260 | NSTextField
261 |
262 |
263 |
264 | YES
265 |
266 | YES
267 | shadowTextField
268 | textField
269 |
270 |
271 | YES
272 |
273 | shadowTextField
274 | NSTextField
275 |
276 |
277 | textField
278 | NSTextField
279 |
280 |
281 |
282 |
283 | IBProjectSource
284 | DemoView.h
285 |
286 |
287 |
288 | JAListViewItemView
289 | NSView
290 |
291 | IBProjectSource
292 | JAListViewItemView.h
293 |
294 |
295 |
296 |
297 | YES
298 |
299 | NSActionCell
300 | NSCell
301 |
302 | IBFrameworkSource
303 | AppKit.framework/Headers/NSActionCell.h
304 |
305 |
306 |
307 | NSApplication
308 | NSResponder
309 |
310 | IBFrameworkSource
311 | AppKit.framework/Headers/NSApplication.h
312 |
313 |
314 |
315 | NSApplication
316 |
317 | IBFrameworkSource
318 | AppKit.framework/Headers/NSApplicationScripting.h
319 |
320 |
321 |
322 | NSApplication
323 |
324 | IBFrameworkSource
325 | AppKit.framework/Headers/NSColorPanel.h
326 |
327 |
328 |
329 | NSApplication
330 |
331 | IBFrameworkSource
332 | AppKit.framework/Headers/NSHelpManager.h
333 |
334 |
335 |
336 | NSApplication
337 |
338 | IBFrameworkSource
339 | AppKit.framework/Headers/NSPageLayout.h
340 |
341 |
342 |
343 | NSApplication
344 |
345 | IBFrameworkSource
346 | AppKit.framework/Headers/NSUserInterfaceItemSearching.h
347 |
348 |
349 |
350 | NSCell
351 | NSObject
352 |
353 | IBFrameworkSource
354 | AppKit.framework/Headers/NSCell.h
355 |
356 |
357 |
358 | NSControl
359 | NSView
360 |
361 | IBFrameworkSource
362 | AppKit.framework/Headers/NSControl.h
363 |
364 |
365 |
366 | NSFormatter
367 | NSObject
368 |
369 | IBFrameworkSource
370 | Foundation.framework/Headers/NSFormatter.h
371 |
372 |
373 |
374 | NSMenu
375 | NSObject
376 |
377 | IBFrameworkSource
378 | AppKit.framework/Headers/NSMenu.h
379 |
380 |
381 |
382 | NSObject
383 |
384 | IBFrameworkSource
385 | AppKit.framework/Headers/NSAccessibility.h
386 |
387 |
388 |
389 | NSObject
390 |
391 |
392 |
393 | NSObject
394 |
395 |
396 |
397 | NSObject
398 |
399 |
400 |
401 | NSObject
402 |
403 |
404 |
405 | NSObject
406 |
407 | IBFrameworkSource
408 | AppKit.framework/Headers/NSDictionaryController.h
409 |
410 |
411 |
412 | NSObject
413 |
414 | IBFrameworkSource
415 | AppKit.framework/Headers/NSDragging.h
416 |
417 |
418 |
419 | NSObject
420 |
421 | IBFrameworkSource
422 | AppKit.framework/Headers/NSFontManager.h
423 |
424 |
425 |
426 | NSObject
427 |
428 | IBFrameworkSource
429 | AppKit.framework/Headers/NSFontPanel.h
430 |
431 |
432 |
433 | NSObject
434 |
435 | IBFrameworkSource
436 | AppKit.framework/Headers/NSKeyValueBinding.h
437 |
438 |
439 |
440 | NSObject
441 |
442 |
443 |
444 | NSObject
445 |
446 | IBFrameworkSource
447 | AppKit.framework/Headers/NSNibLoading.h
448 |
449 |
450 |
451 | NSObject
452 |
453 | IBFrameworkSource
454 | AppKit.framework/Headers/NSOutlineView.h
455 |
456 |
457 |
458 | NSObject
459 |
460 | IBFrameworkSource
461 | AppKit.framework/Headers/NSPasteboard.h
462 |
463 |
464 |
465 | NSObject
466 |
467 | IBFrameworkSource
468 | AppKit.framework/Headers/NSSavePanel.h
469 |
470 |
471 |
472 | NSObject
473 |
474 | IBFrameworkSource
475 | AppKit.framework/Headers/NSTableView.h
476 |
477 |
478 |
479 | NSObject
480 |
481 | IBFrameworkSource
482 | AppKit.framework/Headers/NSToolbarItem.h
483 |
484 |
485 |
486 | NSObject
487 |
488 | IBFrameworkSource
489 | AppKit.framework/Headers/NSView.h
490 |
491 |
492 |
493 | NSObject
494 |
495 | IBFrameworkSource
496 | Foundation.framework/Headers/NSArchiver.h
497 |
498 |
499 |
500 | NSObject
501 |
502 | IBFrameworkSource
503 | Foundation.framework/Headers/NSClassDescription.h
504 |
505 |
506 |
507 | NSObject
508 |
509 | IBFrameworkSource
510 | Foundation.framework/Headers/NSError.h
511 |
512 |
513 |
514 | NSObject
515 |
516 | IBFrameworkSource
517 | Foundation.framework/Headers/NSFileManager.h
518 |
519 |
520 |
521 | NSObject
522 |
523 | IBFrameworkSource
524 | Foundation.framework/Headers/NSKeyValueCoding.h
525 |
526 |
527 |
528 | NSObject
529 |
530 | IBFrameworkSource
531 | Foundation.framework/Headers/NSKeyValueObserving.h
532 |
533 |
534 |
535 | NSObject
536 |
537 | IBFrameworkSource
538 | Foundation.framework/Headers/NSKeyedArchiver.h
539 |
540 |
541 |
542 | NSObject
543 |
544 | IBFrameworkSource
545 | Foundation.framework/Headers/NSObject.h
546 |
547 |
548 |
549 | NSObject
550 |
551 | IBFrameworkSource
552 | Foundation.framework/Headers/NSObjectScripting.h
553 |
554 |
555 |
556 | NSObject
557 |
558 | IBFrameworkSource
559 | Foundation.framework/Headers/NSPortCoder.h
560 |
561 |
562 |
563 | NSObject
564 |
565 | IBFrameworkSource
566 | Foundation.framework/Headers/NSRunLoop.h
567 |
568 |
569 |
570 | NSObject
571 |
572 | IBFrameworkSource
573 | Foundation.framework/Headers/NSScriptClassDescription.h
574 |
575 |
576 |
577 | NSObject
578 |
579 | IBFrameworkSource
580 | Foundation.framework/Headers/NSScriptKeyValueCoding.h
581 |
582 |
583 |
584 | NSObject
585 |
586 | IBFrameworkSource
587 | Foundation.framework/Headers/NSScriptObjectSpecifiers.h
588 |
589 |
590 |
591 | NSObject
592 |
593 | IBFrameworkSource
594 | Foundation.framework/Headers/NSScriptWhoseTests.h
595 |
596 |
597 |
598 | NSObject
599 |
600 | IBFrameworkSource
601 | Foundation.framework/Headers/NSThread.h
602 |
603 |
604 |
605 | NSObject
606 |
607 | IBFrameworkSource
608 | Foundation.framework/Headers/NSURL.h
609 |
610 |
611 |
612 | NSObject
613 |
614 | IBFrameworkSource
615 | Foundation.framework/Headers/NSURLConnection.h
616 |
617 |
618 |
619 | NSObject
620 |
621 | IBFrameworkSource
622 | Foundation.framework/Headers/NSURLDownload.h
623 |
624 |
625 |
626 | NSResponder
627 |
628 | IBFrameworkSource
629 | AppKit.framework/Headers/NSInterfaceStyle.h
630 |
631 |
632 |
633 | NSResponder
634 | NSObject
635 |
636 | IBFrameworkSource
637 | AppKit.framework/Headers/NSResponder.h
638 |
639 |
640 |
641 | NSTextField
642 | NSControl
643 |
644 | IBFrameworkSource
645 | AppKit.framework/Headers/NSTextField.h
646 |
647 |
648 |
649 | NSTextFieldCell
650 | NSActionCell
651 |
652 | IBFrameworkSource
653 | AppKit.framework/Headers/NSTextFieldCell.h
654 |
655 |
656 |
657 | NSView
658 |
659 | IBFrameworkSource
660 | AppKit.framework/Headers/NSClipView.h
661 |
662 |
663 |
664 | NSView
665 |
666 | IBFrameworkSource
667 | AppKit.framework/Headers/NSMenuItem.h
668 |
669 |
670 |
671 | NSView
672 |
673 | IBFrameworkSource
674 | AppKit.framework/Headers/NSRulerView.h
675 |
676 |
677 |
678 | NSView
679 | NSResponder
680 |
681 |
682 |
683 |
684 | 0
685 | IBCocoaFramework
686 |
687 | com.apple.InterfaceBuilder.CocoaPlugin.macosx
688 |
689 |
690 |
691 | com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3
692 |
693 |
694 | YES
695 | JAListView.xcodeproj
696 | 3
697 |
698 |
699 |
--------------------------------------------------------------------------------
/English.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/JAEdgeInsets.h:
--------------------------------------------------------------------------------
1 | /*
2 | * JAEdgeInsets.h
3 | * GitHub
4 | *
5 | * Created by Josh Abernathy on 1/26/11.
6 | * Copyright 2011 GitHub. All rights reserved.
7 | *
8 | */
9 |
10 | typedef struct {
11 | CGFloat top, left, bottom, right;
12 | } JAEdgeInsets;
13 |
14 | static inline JAEdgeInsets JAEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right) {
15 | return (JAEdgeInsets) { .top = top, .left = left, .bottom = bottom, .right = right };
16 | }
17 |
18 | static const JAEdgeInsets JAEdgeInsetsZero = { .top = 0.0f, .left = 0.0f, .bottom = 0.0f, .right = 0.0f };
19 |
--------------------------------------------------------------------------------
/JAListView-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier}
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | ${MACOSX_DEPLOYMENT_TARGET}
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/JAListView.h:
--------------------------------------------------------------------------------
1 | //
2 | // JAListView.h
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 9/29/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "JAEdgeInsets.h"
11 |
12 | @class JAListView;
13 | @class JAListViewItem;
14 |
15 | extern NSString * const JAListViewDraggingPasteboardType;
16 |
17 | @protocol JAListViewDataSource
18 | - (NSUInteger)numberOfItemsInListView:(JAListView *)listView;
19 | - (JAListViewItem *)listView:(JAListView *)listView viewAtIndex:(NSUInteger)index;
20 | @end
21 |
22 | @protocol JAListViewDelegate
23 | @optional
24 | /**
25 | * Mouse down/up doesn't necessarily mean the view will be selected. These methods will always get called regardless of the return value of -listView:shouldSelectView:.
26 | */
27 | - (void)listView:(JAListView *)listView mouseDownOnView:(JAListViewItem *)view withEvent:(NSEvent *)event;
28 | - (void)listView:(JAListView *)listView mouseUpOnView:(JAListViewItem *)view withEvent:(NSEvent *)event;
29 |
30 | - (void)listView:(JAListView *)listView rightMouseDownOnView:(JAListViewItem *)view withEvent:(NSEvent *)event;
31 |
32 | /**
33 | * A view being selected doesn't necessarily mean it was selected by a click. For instance, keyboard selection will also trigger these events. If the selection is coming from a click, then -listView:willSelectView: gets called on mouse down and -listView:didSelectView: will get called on mouse up while still inside the view.
34 | */
35 | - (BOOL)listView:(JAListView *)listView shouldSelectView:(JAListViewItem *)view;
36 | - (void)listView:(JAListView *)listView willSelectView:(JAListViewItem *)view;
37 | - (void)listView:(JAListView *)listView didSelectView:(JAListViewItem *)view;
38 | - (void)listView:(JAListView *)listView didDeselectView:(JAListViewItem *)view;
39 |
40 | - (void)listView:(JAListView *)listView didRemoveView:(JAListViewItem *)view;
41 | @end
42 |
43 | @protocol JAListViewDraggingSourceDelegate
44 | - (BOOL)listView:(JAListView *)listView shouldDragView:(JAListViewItem *)view;
45 | - (void)listView:(JAListView *)listView didStartDraggingView:(JAListViewItem *)view;
46 | - (void)listView:(JAListView *)listView didEndDraggingView:(JAListViewItem *)view;
47 | @end
48 |
49 | @protocol JAListViewDraggingDestinationDelegate
50 | - (BOOL)listView:(JAListView *)listView shouldAcceptViews:(NSArray *)draggedViews location:(NSPoint)location;
51 | - (void)listView:(JAListView *)listView dragEntered:(NSArray *)draggedViews location:(NSPoint)location;
52 | - (void)listView:(JAListView *)listView dragUpdated:(NSArray *)draggedViews location:(NSPoint)location;
53 | - (void)listView:(JAListView *)listView dragEnded:(NSArray *)draggedViews location:(NSPoint)location;
54 | - (void)listView:(JAListView *)listView dragExited:(NSArray *)draggedViews location:(NSPoint)location;
55 | @end
56 |
57 |
58 | @interface JAListView : NSView {
59 | NSMutableArray *cachedViews;
60 | NSArray *cachedVisibleViews;
61 | CGFloat heightForAllContent;
62 | id dataSource;
63 | id delegate;
64 | id draggingSourceDelegate;
65 | id draggingDestinationDelegate;
66 | BOOL canCallDataSourceInParallel;
67 | NSPoint margin;
68 | CGFloat *cachedLocations;
69 | __weak JAListViewItem *viewBeingSelected;
70 | NSColor *backgroundColor;
71 | BOOL isResizingManually;
72 | BOOL conditionallyUseLayerBacking;
73 | }
74 |
75 | - (void)setup;
76 |
77 | - (void)reloadData;
78 | - (void)reloadDataAnimated:(BOOL)animated;
79 | - (void)reloadDataWithAnimation:(void (^)(NSView *newSuperview, NSArray *viewsToAdd, NSArray *viewsToRemove, NSArray *viewsToMove))animationBlock;
80 |
81 | - (void)reloadLayout;
82 | - (void)reloadLayoutAnimated:(BOOL)animated;
83 | - (void)reloadLayoutWithAnimation:(void (^)(NSView *newSuperview, NSArray *viewsToAdd, NSArray *viewsToRemove, NSArray *viewsToMove))animationBlock;
84 |
85 | - (NSUInteger)numberOfViews;
86 | - (JAListViewItem *)viewAtIndex:(NSUInteger)index;
87 | - (NSInteger)indexForView:(JAListViewItem *)view;
88 | - (BOOL)isViewVisible:(JAListViewItem *)view;
89 |
90 | - (BOOL)containsViewItem:(JAListViewItem *)viewItem;
91 |
92 | - (JAListViewItem *)viewAtPoint:(NSPoint)point;
93 |
94 | /*
95 | * Default implementation returns proposedY. Subclasses could implement this to adjust the Y location for the view.
96 | */
97 | - (CGFloat)yForView:(JAListViewItem *)view proposedY:(CGFloat)proposedY;
98 |
99 | /**
100 | * Default implementation returns proposedHeight. Subclasses could implement this to adjust the height of the view.
101 | */
102 | - (CGFloat)heightForView:(JAListViewItem *)view proposedHeight:(CGFloat)proposedHeight;
103 |
104 | - (CGFloat)cachedYLocationForView:(JAListViewItem *)view;
105 |
106 | /**
107 | * Will call the delegate's listView:didSelectView: but not listView:shouldSelectView: or listView:willSelectView:.
108 | */
109 | - (void)selectView:(JAListViewItem *)view;
110 |
111 | /**
112 | * Will call the delegate's listView:didDeselectView:.
113 | */
114 | - (void)deselectView:(JAListViewItem *)view;
115 |
116 | /**
117 | * Will call the delegate's listView:didDeselectView: for each of the selected views.
118 | */
119 | - (void)deselectAllViews;
120 |
121 | - (void)markViewBeingUsedForInertialScrolling:(JAListViewItem *)newView;
122 | - (void)unmarkViewBeingUsedForInertialScrolling:(JAListViewItem *)view;
123 | - (void)clearViewsBeingUsedForInertialScrolling;
124 |
125 | @property (nonatomic, readonly) NSScrollView *scrollView;
126 | @property (nonatomic, assign) IBOutlet id dataSource;
127 | @property (nonatomic, assign) IBOutlet id delegate;
128 | @property (nonatomic, assign) IBOutlet id draggingSourceDelegate;
129 | @property (nonatomic, assign) IBOutlet id draggingDestinationDelegate;
130 | @property (nonatomic, assign) BOOL canCallDataSourceInParallel;
131 | @property (nonatomic, readonly) NSArray *visibleViews;
132 | @property (nonatomic, assign) NSPoint margin;
133 | @property (nonatomic, assign) JAEdgeInsets padding;
134 | @property (nonatomic, retain) NSColor *backgroundColor;
135 | @property (nonatomic, readonly) CGFloat heightForAllContent;
136 | @property (nonatomic, assign) BOOL conditionallyUseLayerBacking;
137 | @property (nonatomic, readonly) NSArray *selectedViews;
138 | @property (nonatomic, assign) BOOL allowNoSelection;
139 |
140 | @end
141 |
--------------------------------------------------------------------------------
/JAListView.m:
--------------------------------------------------------------------------------
1 | //
2 | // JAListView.m
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 9/29/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import "JAListView.h"
10 | #import "JAListViewItem.h"
11 | #import
12 |
13 | static NSArray *viewsCurrentlyBeingDraggedGlobal = nil;
14 |
15 | static const NSTimeInterval defaultAnimationDuration = 0.25f;
16 |
17 | NSString * const JAListViewDraggingPasteboardType = @"JAListViewDraggingPasteboardType";
18 |
19 | @interface JAListView ()
20 | - (void)viewBoundsDidChange:(NSNotification *)notification;
21 | - (void)viewFrameDidChange:(NSNotification *)notification;
22 | - (void)sizeToFitIfNeeded;
23 | - (void)reloadAllViews;
24 | - (void)recacheAllLocations;
25 | - (void)showVisibleViewsAnimated:(BOOL)animated;
26 | - (void)updateCachedVisibleViews;
27 | - (void)clearCachedLocations;
28 | - (void)seriouslyShowVisibleViewsWithAnimation;
29 | - (void)getSelectionMinimumIndex:(NSUInteger *)minIndex maximumIndex:(NSUInteger *)maxIndex;
30 | - (JAListViewItem *)nextSelectableView;
31 | - (JAListViewItem *)nextSelectableViewFromIndex:(NSUInteger)startingIndex;
32 | - (JAListViewItem *)previousSelectableView;
33 | - (JAListViewItem *)previousSelectableViewFromIndex:(NSUInteger)startingIndex;
34 | - (void)standardLayoutRemoveViews:(NSArray *)viewsToRemove addViews:(NSArray *)viewsToAdd moveViews:(NSArray *)viewsToMove;
35 | - (void)standardLayoutAnimated:(BOOL)animated removeViews:(NSArray *)viewsToRemove addViews:(NSArray *)viewsToAdd moveViews:(NSArray *)viewsToMove;
36 |
37 | @property (readonly) NSMutableArray *cachedViews;
38 | @property (nonatomic, readonly) CGFloat *cachedLocations;
39 | @property (nonatomic, retain) NSArray *cachedVisibleViews;
40 | @property (nonatomic, assign) __weak JAListViewItem *viewBeingSelected;
41 | @property (nonatomic, retain) NSArray *viewsCurrentlyBeingDragged;
42 | @property (nonatomic, retain) NSMutableArray *currentlySelectedViews;
43 | @property (nonatomic, retain) NSTrackingArea *currentTrackingArea;
44 | @property (nonatomic, copy) void (^currentAnimationBlock)(NSView *newSuperview, NSArray *viewsToAdd, NSArray *viewsToRemove, NSArray *viewsToMove);
45 | @property (nonatomic, retain) NSMutableArray *viewsBeingUsedForInertialScrolling;
46 | @property (nonatomic, assign) NSUInteger minIndexForReLayout;
47 | @end
48 |
49 |
50 | @implementation JAListView
51 |
52 | - (void)finalize {
53 | if(cachedLocations != NULL) {
54 | free(cachedLocations);
55 | cachedLocations = NULL;
56 | }
57 |
58 | [super finalize];
59 | }
60 |
61 | - (void)dealloc {
62 | if(cachedLocations != NULL) {
63 | free(cachedLocations);
64 | cachedLocations = NULL;
65 | }
66 |
67 | self.backgroundColor = nil;
68 | self.cachedVisibleViews = nil;
69 | self.currentlySelectedViews = nil;
70 | self.currentTrackingArea = nil;
71 | self.currentAnimationBlock = nil;
72 | self.viewsBeingUsedForInertialScrolling = nil;
73 |
74 | [super dealloc];
75 | }
76 |
77 |
78 | #pragma mark NSView
79 |
80 | - (id)initWithFrame:(NSRect)frameRect {
81 | self = [super initWithFrame:frameRect];
82 | if(self == nil) return nil;
83 |
84 | [self setup];
85 |
86 | return self;
87 | }
88 |
89 | - (id)initWithCoder:(NSCoder *)decoder {
90 | self = [super initWithCoder:decoder];
91 | if(self == nil) return nil;
92 |
93 | [self setup];
94 |
95 | return self;
96 | }
97 |
98 | - (void)viewWillMoveToSuperview:(NSView *)newSuperview {
99 | if(self.superview != nil) {
100 | [[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewBoundsDidChangeNotification object:self.superview];
101 | [[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewFrameDidChangeNotification object:self.superview];
102 | }
103 | }
104 |
105 | - (void)viewDidMoveToSuperview {
106 | if(self.superview == nil) return;
107 |
108 | if(self.scrollView == nil) {
109 | NSLog(@"%@ is not in a scroll view. Unless you know what you're doing, this is a mistake.", self);
110 | }
111 |
112 | [self.scrollView.contentView setPostsBoundsChangedNotifications:YES];
113 | [self.scrollView.contentView setPostsFrameChangedNotifications:YES];
114 |
115 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewBoundsDidChange:) name:NSViewBoundsDidChangeNotification object:self.scrollView.contentView];
116 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewFrameDidChange:) name:NSViewFrameDidChangeNotification object:self.scrollView.contentView];
117 |
118 | [self sizeToFitIfNeeded];
119 |
120 | [self.scrollView setBackgroundColor:self.backgroundColor];
121 | }
122 |
123 | - (BOOL)isFlipped {
124 | return YES;
125 | }
126 |
127 | - (void)drawRect:(NSRect)dirtyRect {
128 | [super drawRect:dirtyRect];
129 |
130 | [self.backgroundColor set];
131 | NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
132 | }
133 |
134 |
135 | #pragma mark NSResponder
136 |
137 | - (void)rightMouseDown:(NSEvent *)event {
138 | NSPoint location = [self convertPoint:[event locationInWindow] fromView:nil];
139 | JAListViewItem *view = [self viewAtPoint:location];
140 |
141 | if([self.delegate respondsToSelector:@selector(listView:rightMouseDownOnView:withEvent:)]) {
142 | [self.delegate listView:self rightMouseDownOnView:view withEvent:event];
143 | }
144 | }
145 |
146 | - (void)rightMouseUp:(NSEvent *)event {
147 | [self rightMouseDown:event];
148 | }
149 |
150 | - (void)mouseDown:(NSEvent *)event {
151 | if(([event modifierFlags] & NSControlKeyMask) != 0 && [event clickCount] == 1) {
152 | [self rightMouseDown:event];
153 | return;
154 | }
155 |
156 | NSPoint location = [self convertPoint:[event locationInWindow] fromView:nil];
157 | JAListViewItem *view = [self viewAtPoint:location];
158 | if(view == nil) return;
159 |
160 | if([self.delegate respondsToSelector:@selector(listView:mouseDownOnView:withEvent:)]) {
161 | [self.delegate listView:self mouseDownOnView:view withEvent:event];
162 | }
163 |
164 | BOOL shouldSelect = YES;
165 | if([self.delegate respondsToSelector:@selector(listView:shouldSelectView:)]) {
166 | shouldSelect = [self.delegate listView:self shouldSelectView:view];
167 | }
168 |
169 | if(!shouldSelect) {
170 | return;
171 | }
172 |
173 | if(self.currentTrackingArea != nil) {
174 | [self removeTrackingArea:self.currentTrackingArea];
175 | }
176 |
177 | self.currentTrackingArea = [[[NSTrackingArea alloc] initWithRect:view.frame options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingEnabledDuringMouseDrag owner:self userInfo:nil] autorelease];
178 | [self addTrackingArea:self.currentTrackingArea];
179 |
180 | self.viewBeingSelected = view;
181 | self.viewBeingSelected.highlighted = YES;
182 |
183 | if([self.delegate respondsToSelector:@selector(listView:willSelectView:)]) {
184 | [self.delegate listView:self willSelectView:view];
185 | }
186 | }
187 |
188 | - (void)mouseUp:(NSEvent *)event {
189 | // this is to fix a nasty AppKit bug where the event after a contexual menu gets routed oddly and we end up with a mouseUp event that doesn't really belong to us
190 | if(!NSPointInRect([self convertPoint:[event locationInWindow] fromView:nil], self.frame)) {
191 | return;
192 | }
193 |
194 | [self removeTrackingArea:self.currentTrackingArea];
195 | self.currentTrackingArea = nil;
196 |
197 | NSPoint location = [self convertPoint:[event locationInWindow] fromView:nil];
198 | JAListViewItem *view = [self viewAtPoint:location];
199 | BOOL respondsToUnSelect = [self.delegate respondsToSelector:@selector(listView:didDeselectView:)];
200 | if(view == nil) {
201 | if(self.viewBeingSelected != nil) {
202 | if(respondsToUnSelect) {
203 | [self.delegate listView:self didDeselectView:self.viewBeingSelected];
204 | }
205 |
206 | self.viewBeingSelected.highlighted = NO;
207 | self.viewBeingSelected = nil;
208 | } else {
209 | if(self.allowNoSelection) {
210 | for(JAListViewItem *selectedView in [[self.currentlySelectedViews copy] autorelease]) {
211 | selectedView.selected = NO;
212 | [self.currentlySelectedViews removeObject:selectedView];
213 |
214 | if(respondsToUnSelect) {
215 | [self.delegate listView:self didDeselectView:selectedView];
216 | }
217 | }
218 | }
219 | }
220 | } else {
221 | if([self.delegate respondsToSelector:@selector(listView:mouseUpOnView:withEvent:)]) {
222 | [self.delegate listView:self mouseUpOnView:view withEvent:event];
223 | }
224 |
225 | self.viewBeingSelected.highlighted = NO;
226 |
227 | BOOL shouldSelect = YES;
228 | if([self.delegate respondsToSelector:@selector(listView:shouldSelectView:)]) {
229 | shouldSelect = [self.delegate listView:self shouldSelectView:view];
230 | }
231 |
232 | if(!shouldSelect) {
233 | return;
234 | }
235 |
236 | if([event modifierFlags] & NSCommandKeyMask) {
237 | if([self.currentlySelectedViews containsObject:view]) {
238 | if(!self.allowNoSelection && self.currentlySelectedViews.count < 2) return;
239 |
240 | [[view retain] autorelease];
241 | view.selected = NO;
242 | [self.currentlySelectedViews removeObject:view];
243 |
244 | if(respondsToUnSelect) {
245 | [self.delegate listView:self didDeselectView:view];
246 | }
247 | } else {
248 | view.selected = YES;
249 | [self.currentlySelectedViews addObject:view];
250 |
251 | if([self.delegate respondsToSelector:@selector(listView:didSelectView:)]) {
252 | [self.delegate listView:self didSelectView:view];
253 | }
254 | }
255 | } else if([event modifierFlags] & NSShiftKeyMask) {
256 | JAListViewItem *lastSelectedView = [self.currentlySelectedViews lastObject];
257 | NSInteger lastIndex = [self indexForView:lastSelectedView];
258 | NSInteger indexOfView = [self indexForView:view];
259 |
260 | if(lastIndex == NSNotFound || indexOfView == NSNotFound) return;
261 |
262 | if(indexOfView < lastIndex) {
263 | JAListViewItem *previousView = [self previousSelectableViewFromIndex:(NSUInteger) lastIndex];
264 | NSInteger currentIndex = [self indexForView:previousView];
265 | while(previousView != nil && currentIndex > indexOfView) {
266 | previousView.selected = YES;
267 | [self.currentlySelectedViews addObject:previousView];
268 |
269 | if([self.delegate respondsToSelector:@selector(listView:didSelectView:)]) {
270 | [self.delegate listView:self didSelectView:previousView];
271 | }
272 |
273 | previousView = [self previousSelectableView];
274 | currentIndex = [self indexForView:previousView];
275 | }
276 | } else {
277 | JAListViewItem *nextView = [self nextSelectableViewFromIndex:(NSUInteger) lastIndex];
278 | NSInteger currentIndex = [self indexForView:nextView];
279 | while(nextView != nil && currentIndex < indexOfView) {
280 | nextView.selected = YES;
281 | [self.currentlySelectedViews addObject:nextView];
282 |
283 | if([self.delegate respondsToSelector:@selector(listView:didSelectView:)]) {
284 | [self.delegate listView:self didSelectView:nextView];
285 | }
286 |
287 | nextView = [self nextSelectableView];
288 | currentIndex = [self indexForView:nextView];
289 | }
290 | }
291 |
292 | view.selected = YES;
293 | [self.currentlySelectedViews addObject:view];
294 |
295 | if([self.delegate respondsToSelector:@selector(listView:didSelectView:)]) {
296 | [self.delegate listView:self didSelectView:view];
297 | }
298 | } else {
299 | for(JAListViewItem *selectedView in [[self.currentlySelectedViews copy] autorelease]) {
300 | selectedView.selected = NO;
301 | [self.currentlySelectedViews removeObject:selectedView];
302 |
303 | if(respondsToUnSelect) {
304 | [self.delegate listView:self didDeselectView:selectedView];
305 | }
306 | }
307 |
308 | view.selected = YES;
309 | [self.currentlySelectedViews addObject:view];
310 | [self.window makeFirstResponder:view];
311 |
312 | if([self.delegate respondsToSelector:@selector(listView:didSelectView:)]) {
313 | [self.delegate listView:self didSelectView:view];
314 | }
315 | }
316 |
317 | self.viewBeingSelected = nil;
318 | }
319 | }
320 |
321 | - (void)mouseExited:(NSEvent *)event {
322 | if(self.viewBeingSelected != nil && [self.delegate respondsToSelector:@selector(listView:didDeselectView:)]) {
323 | [self.delegate listView:self didDeselectView:self.viewBeingSelected];
324 | self.viewBeingSelected = nil;
325 |
326 | [self removeTrackingArea:self.currentTrackingArea];
327 | self.currentTrackingArea = nil;
328 | }
329 | }
330 |
331 | - (void)swipeWithEvent:(NSEvent *)event {
332 | JAListViewItem *newView = nil;
333 | if([event deltaY] < 0.0f && [self nextSelectableView] != nil) {
334 | newView = [self nextSelectableView];
335 | } else if([event deltaY] > 0.0f && [self previousSelectableView] != nil) {
336 | newView = [self previousSelectableView];
337 | } else {
338 | [super swipeWithEvent:event];
339 | }
340 |
341 | if(newView != nil) {
342 | for(JAListViewItem *view in [[self.currentlySelectedViews copy] autorelease]) {
343 | [[view retain] autorelease];
344 | view.selected = NO;
345 | [self.currentlySelectedViews removeObject:view];
346 |
347 | if([self.delegate respondsToSelector:@selector(listView:didDeselectView:)]) {
348 | [self.delegate listView:self didDeselectView:view];
349 | }
350 | }
351 |
352 | newView.selected = YES;
353 | [self.window makeFirstResponder:newView];
354 | [self.currentlySelectedViews addObject:newView];
355 |
356 | if([self.delegate respondsToSelector:@selector(listView:didSelectView:)]) {
357 | [self.delegate listView:self didSelectView:newView];
358 | }
359 |
360 | NSRect viewFrame = newView.frame;
361 | viewFrame.origin.y = self.cachedLocations[[self indexForView:newView]];
362 | [(NSView *) self.scrollView.documentView scrollRectToVisible:viewFrame];
363 | }
364 | }
365 |
366 | - (void)mouseDragged:(NSEvent *)event {
367 | NSPoint location = [self convertPoint:[event locationInWindow] fromView:nil];
368 | NSView *viewUnderMouse = [self viewAtPoint:location];
369 | if(viewUnderMouse == nil || ![viewUnderMouse isKindOfClass:[JAListViewItem class]]) {
370 | return;
371 | }
372 |
373 | JAListViewItem *listItemView = (JAListViewItem *) viewUnderMouse;
374 | if(![self.currentlySelectedViews containsObject:listItemView]) {
375 | BOOL shouldSelect = YES;
376 | if([self.delegate respondsToSelector:@selector(listView:shouldSelectView:)]) {
377 | shouldSelect = [self.delegate listView:self shouldSelectView:listItemView];
378 | }
379 |
380 | if(!shouldSelect) return;
381 |
382 | [self deselectAllViews];
383 | [self selectView:listItemView];
384 | }
385 |
386 | NSMutableArray *viewsToDrag = [NSMutableArray array];
387 |
388 | for(JAListViewItem *view in self.currentlySelectedViews) {
389 | BOOL shouldDrag = NO;
390 | if([self.draggingSourceDelegate respondsToSelector:@selector(listView:shouldDragView:)]) {
391 | shouldDrag = [self.draggingSourceDelegate listView:self shouldDragView:view];
392 | }
393 |
394 | if(shouldDrag) {
395 | [viewsToDrag addObject:view];
396 | }
397 | }
398 |
399 | if(viewsToDrag.count < 1) {
400 | return;
401 | }
402 |
403 | [[NSPasteboard pasteboardWithName:NSDragPboard] declareTypes:[NSArray arrayWithObject:JAListViewDraggingPasteboardType] owner:self];
404 | [[NSPasteboard pasteboardWithName:NSDragPboard] setString:JAListViewDraggingPasteboardType forType:JAListViewDraggingPasteboardType];
405 |
406 | self.viewsCurrentlyBeingDragged = viewsToDrag;
407 |
408 | NSMutableArray *images = [NSMutableArray array];
409 | CGFloat height = 0.0f;
410 | CGFloat width = 0.0f;
411 | for(JAListViewItem *view in self.viewsCurrentlyBeingDragged) {
412 | NSImage *image = [view draggingImage];
413 | height += image.size.height;
414 | width = MAX(width, image.size.width);
415 | [images addObject:image];
416 | }
417 |
418 | NSImage *masterImage = [[[NSImage alloc] initWithSize:NSMakeSize(width, height)] autorelease];
419 | [masterImage lockFocus];
420 | NSUInteger index = 0;
421 | for(NSImage *image in images) {
422 | JAListViewItem *view = [self.viewsCurrentlyBeingDragged objectAtIndex:index];
423 | CGFloat y = height - ((index + 1) * view.frame.size.height);
424 | [image drawAtPoint:NSMakePoint(0.0f, y) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0f];
425 |
426 | index++;
427 | }
428 | [masterImage unlockFocus];
429 |
430 | NSPoint dragPoint = viewUnderMouse.frame.origin;
431 | dragPoint.y += viewUnderMouse.bounds.size.height;
432 | [self dragImage:masterImage at:dragPoint offset:NSZeroSize event:event pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard] source:self slideBack:YES];
433 | }
434 |
435 | - (void)keyDown:(NSEvent *)event {
436 | JAListViewItem *newView = nil;
437 | BOOL isShiftDown = ([event modifierFlags] & NSShiftKeyMask) != 0;
438 | BOOL areUnhandledKeys = NO;
439 | NSString *characters = [event characters];
440 | for(NSUInteger characterIndex = 0; characterIndex < characters.length; characterIndex++) {
441 | unichar character = [characters characterAtIndex:characterIndex];
442 | if(character == NSUpArrowFunctionKey) {
443 | newView = [self previousSelectableView];
444 |
445 | if(!isShiftDown && newView != nil) {
446 | for(JAListViewItem *view in [[self.currentlySelectedViews copy] autorelease]) {
447 | [[view retain] autorelease];
448 | view.selected = NO;
449 | [self.currentlySelectedViews removeObject:view];
450 |
451 | if([self.delegate respondsToSelector:@selector(listView:didDeselectView:)]) {
452 | [self.delegate listView:self didDeselectView:view];
453 | }
454 | }
455 | }
456 | } else if(character == NSDownArrowFunctionKey) {
457 | newView = [self nextSelectableView];
458 |
459 | if(!isShiftDown && newView != nil) {
460 | for(JAListViewItem *view in [[self.currentlySelectedViews copy] autorelease]) {
461 | [[view retain] autorelease];
462 | view.selected = NO;
463 | [self.currentlySelectedViews removeObject:view];
464 |
465 | if([self.delegate respondsToSelector:@selector(listView:didDeselectView:)]) {
466 | [self.delegate listView:self didDeselectView:view];
467 | }
468 | }
469 | }
470 | } else {
471 | areUnhandledKeys = YES;
472 | }
473 | }
474 |
475 | if(newView != nil) {
476 | newView.selected = YES;
477 | [self.window makeFirstResponder:newView];
478 | [self.currentlySelectedViews addObject:newView];
479 |
480 | if([self.delegate respondsToSelector:@selector(listView:didSelectView:)]) {
481 | [self.delegate listView:self didSelectView:newView];
482 | }
483 |
484 | NSRect viewFrame = newView.frame;
485 | viewFrame.origin.y = self.cachedLocations[[self indexForView:newView]];
486 | [(NSView *) self.scrollView.documentView scrollRectToVisible:viewFrame];
487 | }
488 |
489 | if(areUnhandledKeys) {
490 | [super keyDown:event];
491 | }
492 | }
493 |
494 | - (BOOL)acceptsFirstResponder {
495 | return YES;
496 | }
497 |
498 | - (BOOL)becomeFirstResponder {
499 | return YES;
500 | }
501 |
502 | - (BOOL)resignFirstResponder {
503 | return YES;
504 | }
505 |
506 |
507 | #pragma mark NSDraggingSource
508 |
509 | - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
510 | return isLocal ? NSDragOperationMove : NSDragOperationNone;
511 | }
512 |
513 | - (void)draggedImage:(NSImage *)image movedTo:(NSPoint)screenPoint {
514 |
515 | }
516 |
517 | - (void)draggedImage:(NSImage *)image endedAt:(NSPoint)point operation:(NSDragOperation)operation {
518 |
519 | }
520 |
521 |
522 | #pragma mark NSDraggingDestination
523 |
524 | - (NSDragOperation)draggingEntered:(id)sender {
525 | NSPasteboard *pasteboard = [sender draggingPasteboard];
526 | if([pasteboard.types containsObject:JAListViewDraggingPasteboardType]) {
527 | NSPoint location = [self convertPoint:[sender draggingLocation] fromView:nil];
528 | [self.draggingDestinationDelegate listView:self dragEntered:self.viewsCurrentlyBeingDragged location:location];
529 |
530 | return NSDragOperationMove;
531 | }
532 |
533 | return NSDragOperationNone;
534 | }
535 |
536 | - (NSDragOperation)draggingUpdated:(id)sender {
537 | NSPasteboard *pasteboard = [sender draggingPasteboard];
538 | if([pasteboard.types containsObject:JAListViewDraggingPasteboardType]) {
539 | NSPoint location = [self convertPoint:[sender draggingLocation] fromView:nil];
540 | [self.draggingDestinationDelegate listView:self dragUpdated:self.viewsCurrentlyBeingDragged location:location];
541 |
542 | return NSDragOperationMove;
543 | }
544 |
545 | return NSDragOperationNone;
546 | }
547 |
548 | - (BOOL)performDragOperation:(id)sender {
549 | NSPasteboard *pasteboard = [sender draggingPasteboard];
550 | if([pasteboard.types containsObject:JAListViewDraggingPasteboardType]) {
551 | NSPoint location = [self convertPoint:[sender draggingLocation] fromView:nil];
552 | [self.draggingDestinationDelegate listView:self dragEnded:self.viewsCurrentlyBeingDragged location:location];
553 |
554 | self.viewsCurrentlyBeingDragged = nil;
555 |
556 | return YES;
557 | }
558 |
559 | return NO;
560 | }
561 |
562 | - (void)draggingExited:(id)sender {
563 | NSPoint location = [self convertPoint:[sender draggingLocation] fromView:nil];
564 | [self.draggingDestinationDelegate listView:self dragExited:self.viewsCurrentlyBeingDragged location:location];
565 |
566 | self.viewsCurrentlyBeingDragged = nil;
567 | }
568 |
569 |
570 | #pragma mark API
571 |
572 | - (void)setup {
573 | canCallDataSourceInParallel = NO;
574 | margin = NSZeroPoint;
575 | self.backgroundColor = [NSColor darkGrayColor];
576 | [self registerForDraggedTypes:[NSArray arrayWithObject:JAListViewDraggingPasteboardType]];
577 | self.currentlySelectedViews = [NSMutableArray array];
578 | self.viewsBeingUsedForInertialScrolling = [NSMutableArray array];
579 | self.allowNoSelection = YES;
580 | }
581 |
582 | - (void)viewBoundsDidChange:(NSNotification *)notification {
583 | if(isResizingManually) return;
584 |
585 | [self reloadLayout];
586 | }
587 |
588 | - (void)viewFrameDidChange:(NSNotification *)notification {
589 | if(isResizingManually) return;
590 |
591 | [self reloadLayout];
592 | }
593 |
594 | - (JAListViewItem *)viewAtPoint:(NSPoint)point {
595 | for(JAListViewItem *view in self.cachedVisibleViews) {
596 | if(NSPointInRect(point, view.frame)) {
597 | return view;
598 | }
599 | }
600 |
601 | return nil;
602 | }
603 |
604 | - (void)sizeToFitIfNeeded {
605 | CGFloat currentScrollOffset = self.scrollView.contentView.bounds.origin.y;
606 |
607 | CGFloat previousHeight = self.frame.size.height;
608 | CGFloat newHeight = self.heightForAllContent;
609 | if(previousHeight != newHeight) {
610 | [self setFrameSize:NSMakeSize(self.superview.bounds.size.width, MAX(newHeight, self.superview.bounds.size.height))];
611 |
612 | NSPoint newScrollPoint = self.scrollView.contentView.bounds.origin;
613 | newScrollPoint.y = currentScrollOffset;
614 | [self.scrollView.documentView scrollPoint:newScrollPoint];
615 | }
616 | }
617 |
618 | - (void)reloadData {
619 | [self reloadDataAnimated:NO];
620 | }
621 |
622 | - (void)reloadDataAnimated:(BOOL)animated {
623 | [self reloadDataWithAnimation:animated ? ^(NSView *newSuperview, NSArray *viewsToAdd, NSArray *viewsToRemove, NSArray *viewsToMove) {
624 | [self standardLayoutAnimated:YES removeViews:viewsToRemove addViews:viewsToAdd moveViews:viewsToMove];
625 | } : nil];
626 | }
627 |
628 | - (void)reloadDataWithAnimation:(void (^)(NSView *newSuperview, NSArray *viewsToAdd, NSArray *viewsToRemove, NSArray *viewsToMove))animationBlock {
629 | self.currentAnimationBlock = animationBlock;
630 |
631 | if(self.currentAnimationBlock != nil && self.conditionallyUseLayerBacking) {
632 | [self setWantsLayer:YES];
633 | }
634 |
635 | [self reloadAllViews];
636 | [self reloadLayoutWithAnimation:animationBlock];
637 | }
638 |
639 | - (void)reloadAllViews {
640 | [self.cachedViews removeAllObjects];
641 |
642 | NSUInteger numberOfViews = [self numberOfViews];
643 | NSMutableArray *views = [NSMutableArray arrayWithCapacity:numberOfViews];
644 |
645 | // fill the array with dummy values so that the data source can be called in parallel
646 | for(NSUInteger index = 0; index < numberOfViews; index++) {
647 | [views addObject:[NSNull null]];
648 | }
649 |
650 | if(self.canCallDataSourceInParallel) {
651 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
652 | dispatch_apply(numberOfViews, queue, ^(size_t index) {
653 | [views replaceObjectAtIndex:index withObject:[self viewAtIndex:index]];
654 | });
655 | } else {
656 | for(NSUInteger index = 0; index < numberOfViews; index++) {
657 | [views replaceObjectAtIndex:index withObject:[self viewAtIndex:index]];
658 | }
659 | }
660 |
661 | for(JAListViewItem *viewItem in views) {
662 | viewItem.listView = self;
663 | }
664 |
665 | [self.cachedViews addObjectsFromArray:views];
666 | [self clearCachedLocations];
667 | }
668 |
669 | - (void)showVisibleViewsAnimated:(BOOL)animated {
670 | [self updateCachedVisibleViews];
671 |
672 | NSArray *newViews = self.cachedVisibleViews;
673 | NSArray *existingViews = [[self.subviews copy] autorelease];
674 | NSMutableArray *viewsToAdd = [NSMutableArray arrayWithArray:newViews];
675 | NSMutableArray *viewsToRemove = [NSMutableArray arrayWithArray:existingViews];
676 | // don't do any unnecessary adding/removing otherwise layer-backed views feel the pain
677 | for(JAListViewItem *view in newViews) {
678 | if([existingViews containsObject:view]) {
679 | [viewsToAdd removeObject:view];
680 | }
681 | }
682 |
683 | for(NSView *view in existingViews) {
684 | if([newViews containsObject:view]) {
685 | [viewsToRemove removeObject:view];
686 | }
687 | }
688 |
689 | [viewsToAdd removeObjectsInArray:self.viewsBeingUsedForInertialScrolling];
690 | [viewsToRemove removeObjectsInArray:self.viewsBeingUsedForInertialScrolling];
691 |
692 | if(animated) {
693 | CFTimeInterval duration = ([self.window currentEvent].modifierFlags & NSShiftKeyMask) ? 10.0f : defaultAnimationDuration;
694 |
695 | [CATransaction begin];
696 | [CATransaction setAnimationDuration:duration];
697 | [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
698 |
699 | if(self.conditionallyUseLayerBacking) {
700 | [CATransaction setCompletionBlock:^{
701 | [self setWantsLayer:NO];
702 | }];
703 | }
704 | }
705 |
706 | CGFloat minY = CGFLOAT_MAX;
707 | for(NSView *view in [[viewsToRemove copy] autorelease]) {
708 | if([view isKindOfClass:[JAListViewItem class]]) {
709 | minY = MIN(view.frame.origin.y, minY);
710 | } else {
711 | [viewsToRemove removeObject:view];
712 | }
713 | }
714 |
715 | for(JAListViewItem *view in [[viewsToAdd copy] autorelease]) {
716 | if(view.ignoreInListViewLayout) {
717 | [viewsToAdd removeObject:view];
718 | continue;
719 | }
720 |
721 | view.autoresizingMask = NSViewWidthSizable | NSViewMaxYMargin;
722 |
723 | CGFloat y = self.cachedLocations[[self.cachedViews indexOfObject:view]];
724 | minY = MIN(y, minY);
725 | }
726 |
727 | NSMutableArray *viewsToMove = [NSMutableArray array];
728 | NSUInteger index = 0;
729 | for(NSView *view in existingViews) {
730 | if([view isKindOfClass:[JAListViewItem class]]) {
731 | JAListViewItem *listItemView = (JAListViewItem *) view;
732 | // only layout views after the first new view
733 | if((listItemView.frame.origin.y >= minY || index >= self.minIndexForReLayout) && !listItemView.ignoreInListViewLayout) {
734 | [viewsToMove addObject:listItemView];
735 | }
736 | }
737 |
738 | index++;
739 | }
740 |
741 | if(self.currentAnimationBlock != nil) {
742 | self.currentAnimationBlock(self, viewsToAdd, viewsToRemove, viewsToMove);
743 | } else {
744 | [self standardLayoutRemoveViews:viewsToRemove addViews:viewsToAdd moveViews:viewsToMove];
745 | }
746 |
747 | if([self.delegate respondsToSelector:@selector(listView:didRemoveView:)]) {
748 | for(JAListViewItem *viewItem in viewsToRemove) {
749 | [self.delegate listView:self didRemoveView:viewItem];
750 | }
751 | }
752 |
753 | self.minIndexForReLayout = NSUIntegerMax;
754 |
755 | self.currentAnimationBlock = nil;
756 |
757 | if(animated) {
758 | [CATransaction commit];
759 | }
760 | }
761 |
762 | - (CGFloat)cachedYLocationForView:(JAListViewItem *)view {
763 | NSUInteger index = [self.cachedViews indexOfObject:view];
764 | if(index == NSNotFound) {
765 | return 0.0f;
766 | }
767 |
768 | return self.cachedLocations[index];
769 | }
770 |
771 | - (void)standardLayoutRemoveViews:(NSArray *)viewsToRemove addViews:(NSArray *)viewsToAdd moveViews:(NSArray *)viewsToMove {
772 | [self standardLayoutAnimated:NO removeViews:viewsToRemove addViews:viewsToAdd moveViews:viewsToMove];
773 | }
774 |
775 | - (void)standardLayoutAnimated:(BOOL)animated removeViews:(NSArray *)viewsToRemove addViews:(NSArray *)viewsToAdd moveViews:(NSArray *)viewsToMove {
776 | for(JAListViewItem *view in viewsToRemove) {
777 | id viewOrProxy = animated ? [view animator] : view;
778 | [viewOrProxy removeFromSuperview];
779 | }
780 |
781 | for(JAListViewItem *view in viewsToAdd) {
782 | CGFloat y = self.cachedLocations[[self.cachedViews indexOfObject:view]];
783 | CGFloat height = [self heightForView:view proposedHeight:view.bounds.size.height];
784 | NSRect viewFrame = NSMakeRect(view.ignoresListViewPadding ? 0.0f : self.padding.left, y, view.ignoresListViewPadding ? self.bounds.size.width : self.bounds.size.width - (self.padding.left + self.padding.right), height);
785 | view.frame = NSIntegralRect(viewFrame);
786 |
787 | id viewOrProxy = animated ? [self animator] : self;
788 | [viewOrProxy addSubview:view];
789 | }
790 |
791 | for(JAListViewItem *view in viewsToMove) {
792 | NSUInteger indexOfView = [self.cachedViews indexOfObject:view];
793 | if (indexOfView == NSNotFound) {
794 | continue;
795 | }
796 | CGFloat y = self.cachedLocations[indexOfView]; //!!!: boom - bad access
797 | id viewOrProxy = animated ? [view animator] : view;
798 | [viewOrProxy setFrame:NSMakeRect(view.ignoresListViewPadding ? 0.0f : self.padding.left, y, view.bounds.size.width, view.bounds.size.height)];
799 | }
800 | }
801 |
802 | - (void)reloadLayout {
803 | [self reloadLayoutAnimated:NO];
804 | }
805 |
806 | - (void)seriouslyShowVisibleViewsWithAnimation {
807 | [self showVisibleViewsAnimated:YES];
808 | }
809 |
810 | - (void)reloadLayoutAnimated:(BOOL)animated {
811 | [self reloadLayoutWithAnimation:animated ? ^(NSView *newSuperview, NSArray *viewsToAdd, NSArray *viewsToRemove, NSArray *viewsToMove) {
812 | [self standardLayoutAnimated:YES removeViews:viewsToRemove addViews:viewsToAdd moveViews:viewsToMove];
813 | } : nil];
814 | }
815 |
816 | - (void)reloadLayoutWithAnimation:(void (^)(NSView *newSuperview, NSArray *viewsToAdd, NSArray *viewsToRemove, NSArray *viewsToMove))animationBlock {
817 | self.currentAnimationBlock = animationBlock;
818 |
819 | [self recacheAllLocations];
820 | isResizingManually = YES;
821 | [self sizeToFitIfNeeded];
822 | if(self.currentAnimationBlock != nil) {
823 | [self performSelector:@selector(seriouslyShowVisibleViewsWithAnimation) withObject:nil afterDelay:0];
824 | } else {
825 | [self showVisibleViewsAnimated:NO];
826 | }
827 | isResizingManually = NO;
828 | }
829 |
830 | - (void)recacheAllLocations {
831 | self.minIndexForReLayout = NSUIntegerMax;
832 |
833 | CGFloat currentY = self.margin.y + self.padding.top;
834 | NSUInteger index = 0;
835 | for(JAListViewItem *view in self.cachedViews) {
836 | CGFloat newY = [self yForView:view proposedY:currentY];
837 | if(newY != self.cachedLocations[index]) {
838 | self.minIndexForReLayout = MIN(self.minIndexForReLayout, index);
839 | }
840 | self.cachedLocations[index] = newY;
841 | currentY += [self heightForView:view proposedHeight:view.bounds.size.height + self.margin.y];
842 | index++;
843 | }
844 |
845 | heightForAllContent = currentY + self.padding.bottom;
846 |
847 | self.minIndexForReLayout = 0;
848 | }
849 |
850 | - (CGFloat)yForView:(JAListViewItem *)view proposedY:(CGFloat)proposedY {
851 | return proposedY;
852 | }
853 |
854 | - (CGFloat)heightForView:(JAListViewItem *)view proposedHeight:(CGFloat)proposedHeight {
855 | return proposedHeight;
856 | }
857 |
858 | - (NSUInteger)numberOfViews {
859 | return [self.dataSource numberOfItemsInListView:self];
860 | }
861 |
862 | - (JAListViewItem *)viewAtIndex:(NSUInteger)index {
863 | JAListViewItem *view = [self.dataSource listView:self viewAtIndex:index];
864 | NSAssert2([view isKindOfClass:[JAListViewItem class]], @"View must be a %@, instead: %@", NSStringFromClass([JAListViewItem class]), view);
865 | return view;
866 | }
867 |
868 | - (NSInteger)indexForView:(JAListViewItem *)view {
869 | NSInteger index = 0;
870 | for(JAListViewItem *viewAtIndex in self.cachedViews) {
871 | if(viewAtIndex == view) {
872 | return index;
873 | }
874 |
875 | index++;
876 | }
877 |
878 | return NSNotFound;
879 | }
880 |
881 | - (BOOL)isViewVisible:(JAListViewItem *)view {
882 | return [self.visibleViews containsObject:view];
883 | }
884 |
885 | - (BOOL)containsViewItem:(JAListViewItem *)viewItem {
886 | return [self.cachedViews containsObject:viewItem];
887 | }
888 |
889 | - (NSArray *)visibleViews {
890 | return self.cachedVisibleViews;
891 | }
892 |
893 | - (void)updateCachedVisibleViews {
894 | NSRect visibleRect = [self.scrollView documentVisibleRect];
895 | NSMutableArray *newVisibleViews = [NSMutableArray array];
896 | NSUInteger index = 0;
897 | for(JAListViewItem *view in self.cachedViews) {
898 | if(NSIntersectsRect(visibleRect, NSMakeRect(view.frame.origin.x, self.cachedLocations[index], MAX(view.frame.size.width, 1.0f), MAX(view.frame.size.height, 1.0f)))) {
899 | [newVisibleViews addObject:view];
900 | }
901 |
902 | index++;
903 | }
904 |
905 | self.cachedVisibleViews = newVisibleViews;
906 | }
907 |
908 | - (NSScrollView *)scrollView {
909 | return [self enclosingScrollView];
910 | }
911 |
912 | - (void)clearCachedLocations {
913 | if(cachedLocations != NULL) {
914 | free(cachedLocations);
915 | cachedLocations = NULL;
916 | }
917 | }
918 |
919 | - (NSMutableArray *)cachedViews {
920 | if(cachedViews == nil) {
921 | cachedViews = [[NSMutableArray alloc] init];
922 | }
923 |
924 | return cachedViews;
925 | }
926 |
927 | - (CGFloat *)cachedLocations {
928 | if(cachedLocations == NULL) {
929 | cachedLocations = (CGFloat *) calloc(self.cachedViews.count, sizeof(CGFloat));
930 | }
931 |
932 | return cachedLocations;
933 | }
934 |
935 | - (void)setDataSource:(id)newDataSource {
936 | dataSource = newDataSource;
937 |
938 | [self reloadData];
939 | }
940 |
941 | - (NSArray *)viewsCurrentlyBeingDragged {
942 | return viewsCurrentlyBeingDraggedGlobal;
943 | }
944 |
945 | - (void)setViewsCurrentlyBeingDragged:(NSArray *)views {
946 | [views retain];
947 | [viewsCurrentlyBeingDraggedGlobal release];
948 | viewsCurrentlyBeingDraggedGlobal = views;
949 | }
950 |
951 | - (void)setBackgroundColor:(NSColor *)newColor {
952 | [newColor retain];
953 | [backgroundColor release];
954 | backgroundColor = newColor;
955 |
956 | [self.scrollView setBackgroundColor:self.backgroundColor];
957 |
958 | [self setNeedsDisplay:YES];
959 | }
960 |
961 | - (NSArray *)selectedViews {
962 | return self.currentlySelectedViews;
963 | }
964 |
965 | - (void)getSelectionMinimumIndex:(NSUInteger *)minIndex maximumIndex:(NSUInteger *)maxIndex {
966 | if(minIndex != NULL) *minIndex = NSUIntegerMax;
967 | if(maxIndex != NULL) *maxIndex = 0;
968 |
969 | for(JAListViewItem *view in self.currentlySelectedViews) {
970 | NSUInteger index = (NSUInteger) [self indexForView:view];
971 | if(minIndex != NULL) *minIndex = MIN(index, *minIndex);
972 | if(maxIndex != NULL) *maxIndex = MAX(index, *maxIndex);
973 | }
974 | }
975 |
976 | - (JAListViewItem *)nextSelectableView {
977 | NSUInteger maxIndex = 0;
978 | [self getSelectionMinimumIndex:NULL maximumIndex:&maxIndex];
979 |
980 | return [self nextSelectableViewFromIndex:maxIndex];
981 | }
982 |
983 | - (JAListViewItem *)previousSelectableView {
984 | NSUInteger minIndex = 0;
985 | [self getSelectionMinimumIndex:&minIndex maximumIndex:NULL];
986 |
987 | return [self previousSelectableViewFromIndex:minIndex];
988 | }
989 |
990 | - (JAListViewItem *)nextSelectableViewFromIndex:(NSUInteger)startingIndex {
991 | BOOL respondsToShouldSelect = [self.delegate respondsToSelector:@selector(listView:shouldSelectView:)];
992 | NSUInteger numberOfViews = [self numberOfViews];
993 | JAListViewItem *nextView = nil;
994 | NSUInteger index = startingIndex + 1;
995 | while(nextView == nil) {
996 | if(index >= numberOfViews) break;
997 |
998 | JAListViewItem *potentialView = [self viewAtIndex:index];
999 | if(respondsToShouldSelect) {
1000 | if([self.delegate listView:self shouldSelectView:potentialView]) {
1001 | nextView = potentialView;
1002 | }
1003 | } else {
1004 | nextView = potentialView;
1005 | }
1006 |
1007 | index++;
1008 |
1009 | }
1010 |
1011 | return nextView;
1012 | }
1013 |
1014 | - (JAListViewItem *)previousSelectableViewFromIndex:(NSUInteger)startingIndex {
1015 | BOOL respondsToShouldSelect = [self.delegate respondsToSelector:@selector(listView:shouldSelectView:)];
1016 | JAListViewItem *previousView = nil;
1017 | NSInteger index = (NSInteger) startingIndex - 1;
1018 | while(previousView == nil) {
1019 | if(index < 0) break;
1020 |
1021 | JAListViewItem *potentialView = [self viewAtIndex:(NSUInteger) index];
1022 | if(respondsToShouldSelect) {
1023 | if([self.delegate listView:self shouldSelectView:potentialView]) {
1024 | previousView = potentialView;
1025 | }
1026 | } else {
1027 | previousView = potentialView;
1028 | }
1029 |
1030 | index--;
1031 |
1032 | }
1033 |
1034 | return previousView;
1035 | }
1036 |
1037 | - (void)selectView:(JAListViewItem *)view {
1038 | [self.window makeFirstResponder:view];
1039 | view.selected = YES;
1040 | [self.currentlySelectedViews addObject:view];
1041 |
1042 | if([self.delegate respondsToSelector:@selector(listView:didSelectView:)]) {
1043 | [self.delegate listView:self didSelectView:view];
1044 | }
1045 | }
1046 |
1047 | - (void)deselectView:(JAListViewItem *)view {
1048 | if(![self.currentlySelectedViews containsObject:view]) return;
1049 |
1050 | [[view retain] autorelease];
1051 | view.selected = NO;
1052 | [self.currentlySelectedViews removeObject:view];
1053 |
1054 | if([self.delegate respondsToSelector:@selector(listView:didDeselectView:)]) {
1055 | [self.delegate listView:self didDeselectView:view];
1056 | }
1057 | }
1058 |
1059 | - (void)deselectAllViews {
1060 | for(JAListViewItem *view in [[self.currentlySelectedViews copy] autorelease]) {
1061 | [self deselectView:view];
1062 | }
1063 | }
1064 |
1065 | - (void)markViewBeingUsedForInertialScrolling:(JAListViewItem *)newView {
1066 | // This value is fairly arbitrary. I've seen it use 2 different views so we'll go one over that for now.
1067 | static const NSUInteger maxNumberOfViewsForInertialScrolling = 3;
1068 |
1069 | NSAssert(newView != nil, @"Cannot mark a nil view as being used for inertial scrolling.");
1070 |
1071 | [self.viewsBeingUsedForInertialScrolling addObject:newView];
1072 | while(self.viewsBeingUsedForInertialScrolling.count > maxNumberOfViewsForInertialScrolling) {
1073 | [self.viewsBeingUsedForInertialScrolling removeObjectAtIndex:0];
1074 | }
1075 | }
1076 |
1077 | - (void)unmarkViewBeingUsedForInertialScrolling:(JAListViewItem *)view {
1078 | [self.viewsBeingUsedForInertialScrolling removeObject:view];
1079 | }
1080 |
1081 | - (void)clearViewsBeingUsedForInertialScrolling {
1082 | [self.viewsBeingUsedForInertialScrolling removeAllObjects];
1083 | }
1084 |
1085 | @synthesize dataSource;
1086 | @synthesize delegate;
1087 | @synthesize draggingSourceDelegate;
1088 | @synthesize draggingDestinationDelegate;
1089 | @synthesize cachedVisibleViews;
1090 | @synthesize canCallDataSourceInParallel;
1091 | @synthesize margin;
1092 | @synthesize padding;
1093 | @synthesize viewBeingSelected;
1094 | @synthesize backgroundColor;
1095 | @synthesize heightForAllContent;
1096 | @synthesize conditionallyUseLayerBacking;
1097 | @synthesize currentlySelectedViews;
1098 | @synthesize currentTrackingArea;
1099 | @synthesize currentAnimationBlock;
1100 | @synthesize viewsBeingUsedForInertialScrolling;
1101 | @synthesize minIndexForReLayout;
1102 | @synthesize allowNoSelection;
1103 |
1104 | @end
1105 |
--------------------------------------------------------------------------------
/JAListView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 45;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; };
11 | 256AC3DA0F4B6AC300CF3369 /* JAListViewAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* JAListViewAppDelegate.m */; };
12 | 8842256A12AD686600A4B4D9 /* JAObjectListView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8842256912AD686600A4B4D9 /* JAObjectListView.m */; };
13 | 885CF18B12A044CC0069C4CF /* JASectionedListView.m in Sources */ = {isa = PBXBuildFile; fileRef = 885CF18A12A044CC0069C4CF /* JASectionedListView.m */; };
14 | 885CF19F12A045750069C4CF /* DemoSectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 885CF19E12A045750069C4CF /* DemoSectionView.m */; };
15 | 885CF1A112A045A70069C4CF /* DemoSectionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 885CF1A012A045A70069C4CF /* DemoSectionView.xib */; };
16 | 885CF38512A051AD0069C4CF /* NSIndexPath+JAListViewExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 885CF38412A051AD0069C4CF /* NSIndexPath+JAListViewExtensions.m */; };
17 | 88DA161A1254594E00A1D5AA /* JAListView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88DA16191254594E00A1D5AA /* JAListView.m */; };
18 | 88DA167A125461A200A1D5AA /* DemoView.m in Sources */ = {isa = PBXBuildFile; fileRef = 88DA1679125461A200A1D5AA /* DemoView.m */; };
19 | 88DA167E1254624A00A1D5AA /* DemoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 88DA167D1254624A00A1D5AA /* DemoView.xib */; };
20 | 88DC4E7312AEDCC300F37F1C /* JAListViewItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 88DC4E7212AEDCC300F37F1C /* JAListViewItem.m */; };
21 | 88DC4E8912AEDD0000F37F1C /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88DC4E8812AEDD0000F37F1C /* QuartzCore.framework */; };
22 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
23 | 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
24 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXFileReference section */
28 | 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; };
29 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; };
30 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; };
31 | 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; };
32 | 256AC3D80F4B6AC300CF3369 /* JAListViewAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JAListViewAppDelegate.h; sourceTree = ""; };
33 | 256AC3D90F4B6AC300CF3369 /* JAListViewAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JAListViewAppDelegate.m; sourceTree = ""; };
34 | 256AC3F00F4B6AF500CF3369 /* JAListView_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JAListView_Prefix.pch; sourceTree = ""; };
35 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
36 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; };
37 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; };
38 | 8842256812AD686600A4B4D9 /* JAObjectListView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JAObjectListView.h; sourceTree = ""; };
39 | 8842256912AD686600A4B4D9 /* JAObjectListView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JAObjectListView.m; sourceTree = ""; };
40 | 885CF05412A0369F0069C4CF /* JAListViewItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JAListViewItem.h; sourceTree = ""; };
41 | 885CF18912A044CC0069C4CF /* JASectionedListView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JASectionedListView.h; sourceTree = ""; };
42 | 885CF18A12A044CC0069C4CF /* JASectionedListView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JASectionedListView.m; sourceTree = ""; };
43 | 885CF19D12A045750069C4CF /* DemoSectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoSectionView.h; sourceTree = ""; };
44 | 885CF19E12A045750069C4CF /* DemoSectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoSectionView.m; sourceTree = ""; };
45 | 885CF1A012A045A70069C4CF /* DemoSectionView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DemoSectionView.xib; sourceTree = ""; };
46 | 885CF38312A051AD0069C4CF /* NSIndexPath+JAListViewExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexPath+JAListViewExtensions.h"; sourceTree = ""; };
47 | 885CF38412A051AD0069C4CF /* NSIndexPath+JAListViewExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexPath+JAListViewExtensions.m"; sourceTree = ""; };
48 | 888799BB12B2EB89006A9135 /* Demo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Demo-Info.plist"; sourceTree = ""; };
49 | 88D30AC413249FEC00F32DB2 /* JAEdgeInsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JAEdgeInsets.h; sourceTree = ""; };
50 | 88DA16181254594E00A1D5AA /* JAListView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JAListView.h; sourceTree = ""; };
51 | 88DA16191254594E00A1D5AA /* JAListView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JAListView.m; sourceTree = ""; };
52 | 88DA1678125461A200A1D5AA /* DemoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoView.h; sourceTree = ""; };
53 | 88DA1679125461A200A1D5AA /* DemoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoView.m; sourceTree = ""; };
54 | 88DA167D1254624A00A1D5AA /* DemoView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DemoView.xib; sourceTree = ""; };
55 | 88DC4E7212AEDCC300F37F1C /* JAListViewItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JAListViewItem.m; sourceTree = ""; };
56 | 88DC4E8812AEDD0000F37F1C /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
57 | 8D1107310486CEB800E47090 /* JAListView-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "JAListView-Info.plist"; sourceTree = ""; };
58 | 8D1107320486CEB800E47090 /* JAListView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JAListView.app; sourceTree = BUILT_PRODUCTS_DIR; };
59 | /* End PBXFileReference section */
60 |
61 | /* Begin PBXFrameworksBuildPhase section */
62 | 8D11072E0486CEB800E47090 /* Frameworks */ = {
63 | isa = PBXFrameworksBuildPhase;
64 | buildActionMask = 2147483647;
65 | files = (
66 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
67 | 88DC4E8912AEDD0000F37F1C /* QuartzCore.framework in Frameworks */,
68 | );
69 | runOnlyForDeploymentPostprocessing = 0;
70 | };
71 | /* End PBXFrameworksBuildPhase section */
72 |
73 | /* Begin PBXGroup section */
74 | 080E96DDFE201D6D7F000001 /* Classes */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 888799BF12B2EBA3006A9135 /* JAListView */,
78 | 256AC3D80F4B6AC300CF3369 /* JAListViewAppDelegate.h */,
79 | 256AC3D90F4B6AC300CF3369 /* JAListViewAppDelegate.m */,
80 | 1DDD58140DA1D0A300B32029 /* MainMenu.xib */,
81 | 88DA1678125461A200A1D5AA /* DemoView.h */,
82 | 88DA1679125461A200A1D5AA /* DemoView.m */,
83 | 88DA167D1254624A00A1D5AA /* DemoView.xib */,
84 | 885CF19D12A045750069C4CF /* DemoSectionView.h */,
85 | 885CF19E12A045750069C4CF /* DemoSectionView.m */,
86 | 885CF1A012A045A70069C4CF /* DemoSectionView.xib */,
87 | );
88 | name = Classes;
89 | sourceTree = "";
90 | };
91 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
92 | isa = PBXGroup;
93 | children = (
94 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
95 | 88DC4E8812AEDD0000F37F1C /* QuartzCore.framework */,
96 | );
97 | name = "Linked Frameworks";
98 | sourceTree = "";
99 | };
100 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
104 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
105 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
106 | );
107 | name = "Other Frameworks";
108 | sourceTree = "";
109 | };
110 | 19C28FACFE9D520D11CA2CBB /* Products */ = {
111 | isa = PBXGroup;
112 | children = (
113 | 8D1107320486CEB800E47090 /* JAListView.app */,
114 | );
115 | name = Products;
116 | sourceTree = "";
117 | };
118 | 29B97314FDCFA39411CA2CEA /* JAListView */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 080E96DDFE201D6D7F000001 /* Classes */,
122 | 29B97315FDCFA39411CA2CEA /* Other Sources */,
123 | 29B97317FDCFA39411CA2CEA /* Resources */,
124 | 29B97323FDCFA39411CA2CEA /* Frameworks */,
125 | 19C28FACFE9D520D11CA2CBB /* Products */,
126 | 888799BB12B2EB89006A9135 /* Demo-Info.plist */,
127 | );
128 | name = JAListView;
129 | sourceTree = "";
130 | };
131 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = {
132 | isa = PBXGroup;
133 | children = (
134 | 256AC3F00F4B6AF500CF3369 /* JAListView_Prefix.pch */,
135 | 29B97316FDCFA39411CA2CEA /* main.m */,
136 | );
137 | name = "Other Sources";
138 | sourceTree = "";
139 | };
140 | 29B97317FDCFA39411CA2CEA /* Resources */ = {
141 | isa = PBXGroup;
142 | children = (
143 | 8D1107310486CEB800E47090 /* JAListView-Info.plist */,
144 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
145 | );
146 | name = Resources;
147 | sourceTree = "";
148 | };
149 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
150 | isa = PBXGroup;
151 | children = (
152 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
153 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
154 | );
155 | name = Frameworks;
156 | sourceTree = "";
157 | };
158 | 888799BF12B2EBA3006A9135 /* JAListView */ = {
159 | isa = PBXGroup;
160 | children = (
161 | 88DA16181254594E00A1D5AA /* JAListView.h */,
162 | 88DA16191254594E00A1D5AA /* JAListView.m */,
163 | 88D30AC413249FEC00F32DB2 /* JAEdgeInsets.h */,
164 | 885CF18912A044CC0069C4CF /* JASectionedListView.h */,
165 | 885CF18A12A044CC0069C4CF /* JASectionedListView.m */,
166 | 8842256812AD686600A4B4D9 /* JAObjectListView.h */,
167 | 8842256912AD686600A4B4D9 /* JAObjectListView.m */,
168 | 885CF05412A0369F0069C4CF /* JAListViewItem.h */,
169 | 88DC4E7212AEDCC300F37F1C /* JAListViewItem.m */,
170 | 885CF38312A051AD0069C4CF /* NSIndexPath+JAListViewExtensions.h */,
171 | 885CF38412A051AD0069C4CF /* NSIndexPath+JAListViewExtensions.m */,
172 | );
173 | name = JAListView;
174 | sourceTree = "";
175 | };
176 | /* End PBXGroup section */
177 |
178 | /* Begin PBXNativeTarget section */
179 | 8D1107260486CEB800E47090 /* JAListView */ = {
180 | isa = PBXNativeTarget;
181 | buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "JAListView" */;
182 | buildPhases = (
183 | 8D1107290486CEB800E47090 /* Resources */,
184 | 8D11072C0486CEB800E47090 /* Sources */,
185 | 8D11072E0486CEB800E47090 /* Frameworks */,
186 | );
187 | buildRules = (
188 | );
189 | dependencies = (
190 | );
191 | name = JAListView;
192 | productInstallPath = "$(HOME)/Applications";
193 | productName = JAListView;
194 | productReference = 8D1107320486CEB800E47090 /* JAListView.app */;
195 | productType = "com.apple.product-type.application";
196 | };
197 | /* End PBXNativeTarget section */
198 |
199 | /* Begin PBXProject section */
200 | 29B97313FDCFA39411CA2CEA /* Project object */ = {
201 | isa = PBXProject;
202 | attributes = {
203 | ORGANIZATIONNAME = "Maybe Apps";
204 | };
205 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "JAListView" */;
206 | compatibilityVersion = "Xcode 3.1";
207 | developmentRegion = English;
208 | hasScannedForEncodings = 1;
209 | knownRegions = (
210 | English,
211 | Japanese,
212 | French,
213 | German,
214 | );
215 | mainGroup = 29B97314FDCFA39411CA2CEA /* JAListView */;
216 | projectDirPath = "";
217 | projectRoot = "";
218 | targets = (
219 | 8D1107260486CEB800E47090 /* JAListView */,
220 | );
221 | };
222 | /* End PBXProject section */
223 |
224 | /* Begin PBXResourcesBuildPhase section */
225 | 8D1107290486CEB800E47090 /* Resources */ = {
226 | isa = PBXResourcesBuildPhase;
227 | buildActionMask = 2147483647;
228 | files = (
229 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
230 | 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */,
231 | 88DA167E1254624A00A1D5AA /* DemoView.xib in Resources */,
232 | 885CF1A112A045A70069C4CF /* DemoSectionView.xib in Resources */,
233 | );
234 | runOnlyForDeploymentPostprocessing = 0;
235 | };
236 | /* End PBXResourcesBuildPhase section */
237 |
238 | /* Begin PBXSourcesBuildPhase section */
239 | 8D11072C0486CEB800E47090 /* Sources */ = {
240 | isa = PBXSourcesBuildPhase;
241 | buildActionMask = 2147483647;
242 | files = (
243 | 8D11072D0486CEB800E47090 /* main.m in Sources */,
244 | 256AC3DA0F4B6AC300CF3369 /* JAListViewAppDelegate.m in Sources */,
245 | 88DA161A1254594E00A1D5AA /* JAListView.m in Sources */,
246 | 88DA167A125461A200A1D5AA /* DemoView.m in Sources */,
247 | 885CF18B12A044CC0069C4CF /* JASectionedListView.m in Sources */,
248 | 885CF19F12A045750069C4CF /* DemoSectionView.m in Sources */,
249 | 885CF38512A051AD0069C4CF /* NSIndexPath+JAListViewExtensions.m in Sources */,
250 | 8842256A12AD686600A4B4D9 /* JAObjectListView.m in Sources */,
251 | 88DC4E7312AEDCC300F37F1C /* JAListViewItem.m in Sources */,
252 | );
253 | runOnlyForDeploymentPostprocessing = 0;
254 | };
255 | /* End PBXSourcesBuildPhase section */
256 |
257 | /* Begin PBXVariantGroup section */
258 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
259 | isa = PBXVariantGroup;
260 | children = (
261 | 089C165DFE840E0CC02AAC07 /* English */,
262 | );
263 | name = InfoPlist.strings;
264 | sourceTree = "";
265 | };
266 | 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = {
267 | isa = PBXVariantGroup;
268 | children = (
269 | 1DDD58150DA1D0A300B32029 /* English */,
270 | );
271 | name = MainMenu.xib;
272 | sourceTree = "";
273 | };
274 | /* End PBXVariantGroup section */
275 |
276 | /* Begin XCBuildConfiguration section */
277 | C01FCF4B08A954540054247B /* Debug */ = {
278 | isa = XCBuildConfiguration;
279 | buildSettings = {
280 | ALWAYS_SEARCH_USER_PATHS = NO;
281 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
282 | COPY_PHASE_STRIP = NO;
283 | GCC_DYNAMIC_NO_PIC = NO;
284 | GCC_ENABLE_FIX_AND_CONTINUE = YES;
285 | GCC_MODEL_TUNING = G5;
286 | GCC_OPTIMIZATION_LEVEL = 0;
287 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
288 | GCC_PREFIX_HEADER = JAListView_Prefix.pch;
289 | INFOPLIST_FILE = "JAListView-Info.plist";
290 | INSTALL_PATH = "$(HOME)/Applications";
291 | PRODUCT_NAME = JAListView;
292 | };
293 | name = Debug;
294 | };
295 | C01FCF4C08A954540054247B /* Release */ = {
296 | isa = XCBuildConfiguration;
297 | buildSettings = {
298 | ALWAYS_SEARCH_USER_PATHS = NO;
299 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
300 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
301 | GCC_MODEL_TUNING = G5;
302 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
303 | GCC_PREFIX_HEADER = JAListView_Prefix.pch;
304 | INFOPLIST_FILE = "JAListView-Info.plist";
305 | INSTALL_PATH = "$(HOME)/Applications";
306 | PRODUCT_NAME = JAListView;
307 | };
308 | name = Release;
309 | };
310 | C01FCF4F08A954540054247B /* Debug */ = {
311 | isa = XCBuildConfiguration;
312 | buildSettings = {
313 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
314 | GCC_C_LANGUAGE_STANDARD = gnu99;
315 | GCC_ENABLE_OBJC_GC = required;
316 | GCC_OPTIMIZATION_LEVEL = 0;
317 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
318 | GCC_TREAT_WARNINGS_AS_ERRORS = YES;
319 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
320 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
321 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
322 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
323 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
324 | GCC_WARN_MISSING_PARENTHESES = YES;
325 | GCC_WARN_SHADOW = YES;
326 | GCC_WARN_SIGN_COMPARE = YES;
327 | GCC_WARN_STRICT_SELECTOR_MATCH = NO;
328 | GCC_WARN_UNDECLARED_SELECTOR = YES;
329 | GCC_WARN_UNKNOWN_PRAGMAS = YES;
330 | GCC_WARN_UNUSED_VALUE = YES;
331 | GCC_WARN_UNUSED_VARIABLE = YES;
332 | ONLY_ACTIVE_ARCH = YES;
333 | PREBINDING = NO;
334 | SDKROOT = macosx10.6;
335 | WARNING_CFLAGS = (
336 | "-Werror",
337 | "-Wall",
338 | );
339 | };
340 | name = Debug;
341 | };
342 | C01FCF5008A954540054247B /* Release */ = {
343 | isa = XCBuildConfiguration;
344 | buildSettings = {
345 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
346 | GCC_C_LANGUAGE_STANDARD = gnu99;
347 | GCC_ENABLE_OBJC_GC = required;
348 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
349 | GCC_TREAT_WARNINGS_AS_ERRORS = YES;
350 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
351 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
352 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
353 | GCC_WARN_SHADOW = YES;
354 | GCC_WARN_SIGN_COMPARE = YES;
355 | GCC_WARN_STRICT_SELECTOR_MATCH = NO;
356 | GCC_WARN_UNDECLARED_SELECTOR = YES;
357 | GCC_WARN_UNKNOWN_PRAGMAS = YES;
358 | GCC_WARN_UNUSED_VALUE = YES;
359 | GCC_WARN_UNUSED_VARIABLE = YES;
360 | PREBINDING = NO;
361 | SDKROOT = macosx10.6;
362 | WARNING_CFLAGS = (
363 | "-Werror",
364 | "-Wall",
365 | );
366 | };
367 | name = Release;
368 | };
369 | /* End XCBuildConfiguration section */
370 |
371 | /* Begin XCConfigurationList section */
372 | C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "JAListView" */ = {
373 | isa = XCConfigurationList;
374 | buildConfigurations = (
375 | C01FCF4B08A954540054247B /* Debug */,
376 | C01FCF4C08A954540054247B /* Release */,
377 | );
378 | defaultConfigurationIsVisible = 0;
379 | defaultConfigurationName = Release;
380 | };
381 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "JAListView" */ = {
382 | isa = XCConfigurationList;
383 | buildConfigurations = (
384 | C01FCF4F08A954540054247B /* Debug */,
385 | C01FCF5008A954540054247B /* Release */,
386 | );
387 | defaultConfigurationIsVisible = 0;
388 | defaultConfigurationName = Release;
389 | };
390 | /* End XCConfigurationList section */
391 | };
392 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
393 | }
394 |
--------------------------------------------------------------------------------
/JAListView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/JAListViewAppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // JAListViewAppDelegate.h
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 9/29/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "JAListView.h"
11 | #import "JASectionedListView.h"
12 |
13 |
14 | @interface JAListViewAppDelegate : NSObject {
15 | NSWindow *window;
16 | JAListView *listView;
17 | JASectionedListView *sectionedListView;
18 | }
19 |
20 | @property (assign) IBOutlet NSWindow *window;
21 | @property (assign) IBOutlet JAListView *listView;
22 | @property (assign) IBOutlet JASectionedListView *sectionedListView;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/JAListViewAppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // JAListViewAppDelegate.m
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 9/29/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import "JAListViewAppDelegate.h"
10 | #import "DemoView.h"
11 | #import "DemoSectionView.h"
12 |
13 |
14 | @implementation JAListViewAppDelegate
15 |
16 |
17 | #pragma mark NSApplicationDelegate
18 |
19 | - (void)applicationDidFinishLaunching:(NSNotification *)notification {
20 | self.listView.canCallDataSourceInParallel = YES;
21 | self.sectionedListView.canCallDataSourceInParallel = YES;
22 |
23 | [self.listView reloadData];
24 | [self.sectionedListView reloadData];
25 |
26 | [self.window makeKeyAndOrderFront:nil];
27 | }
28 |
29 |
30 | #pragma mark JAListViewDelegate
31 |
32 | - (void)listView:(JAListView *)list willSelectView:(JAListViewItem *)view {
33 | if(list == self.listView) {
34 | DemoView *demoView = (DemoView *) view;
35 | demoView.selected = YES;
36 | }
37 | }
38 |
39 | - (void)listView:(JAListView *)list didSelectView:(JAListViewItem *)view {
40 | if(list == self.listView) {
41 | DemoView *demoView = (DemoView *) view;
42 | demoView.selected = NO;
43 | }
44 | }
45 |
46 | - (void)listView:(JAListView *)list didUnSelectView:(JAListViewItem *)view {
47 | if(list == self.listView) {
48 | DemoView *demoView = (DemoView *) view;
49 | demoView.selected = NO;
50 | }
51 | }
52 |
53 |
54 | #pragma mark JAListViewDataSource
55 |
56 | - (NSUInteger)numberOfItemsInListView:(JAListView *)listView {
57 | return 100;
58 | }
59 |
60 | - (JAListViewItem *)listView:(JAListView *)listView viewAtIndex:(NSUInteger)index {
61 | DemoView *view = [DemoView demoView];
62 | view.text = [NSString stringWithFormat:@"Row %d", index + 1];
63 | return view;
64 | }
65 |
66 |
67 | #pragma mark JASectionedListViewDataSource
68 |
69 | - (NSUInteger)numberOfSectionsInListView:(JASectionedListView *)listView {
70 | return 3;
71 | }
72 |
73 | - (NSUInteger)listView:(JASectionedListView *)listView numberOfViewsInSection:(NSUInteger)section {
74 | if(section == 0) {
75 | return 10;
76 | } else if(section == 1) {
77 | return 2;
78 | } else if(section == 2) {
79 | return 7;
80 | }
81 |
82 | return 0;
83 | }
84 |
85 | - (JAListViewItem *)listView:(JAListView *)listView sectionHeaderViewForSection:(NSUInteger)section {
86 | DemoSectionView *view = [DemoSectionView demoSectionView];
87 | view.text = [NSString stringWithFormat:@"Section %d", section + 1];
88 | return view;
89 | }
90 |
91 | - (JAListViewItem *)listView:(JAListView *)listView viewForSection:(NSUInteger)section index:(NSUInteger)index {
92 | DemoView *view = [DemoView demoView];
93 | view.text = [NSString stringWithFormat:@"Section %d: Row %d", section + 1, index + 1];
94 | return view;
95 | }
96 |
97 |
98 | #pragma mark NSSplitViewDelegate
99 |
100 | - (void)splitViewWillResizeSubviews:(NSNotification *)aNotification {
101 | if(listView.frame.size.width < 1) {
102 | [listView reloadData];
103 | } else if(sectionedListView.frame.size.width < 1) {
104 | [sectionedListView reloadData];
105 | }
106 | }
107 |
108 |
109 | #pragma mark API
110 |
111 | @synthesize window;
112 | @synthesize listView;
113 | @synthesize sectionedListView;
114 |
115 | @end
116 |
--------------------------------------------------------------------------------
/JAListViewItem.h:
--------------------------------------------------------------------------------
1 | //
2 | // JAListViewItem.h
3 | //
4 | // Created by Josh Abernathy on 10/27/10.
5 | // Copyright 2010 Maybe Apps. All rights reserved.
6 | //
7 |
8 | #import
9 |
10 | typedef enum {
11 | JAListViewPositionTop = 1 << 1,
12 | JAListViewPositionMiddle = 1 << 2,
13 | JAListViewPositionBottom = 1 << 3,
14 | JAListViewPositionNone = 0,
15 | } JAListViewPosition;
16 |
17 | @class JAListView;
18 |
19 |
20 | @interface JAListViewItem : NSView {}
21 |
22 | - (NSImage *)draggingImage;
23 |
24 | @property (nonatomic, assign) JAListView *listView;
25 | @property (nonatomic, assign) BOOL ignoreInListViewLayout;
26 | @property (nonatomic, assign, getter=isSelected) BOOL selected;
27 | @property (nonatomic, assign, getter=isHighlighted) BOOL highlighted;
28 | @property (nonatomic, readonly) JAListViewPosition listViewPosition;
29 | @property (nonatomic, assign) BOOL ignoresListViewPadding;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/JAListViewItem.m:
--------------------------------------------------------------------------------
1 | //
2 | // JAListViewItem.m
3 | //
4 | // Created by Josh Abernathy on 10/27/10.
5 | // Copyright 2010 Maybe Apps. All rights reserved.
6 | //
7 |
8 | #import "JAListViewItem.h"
9 | #import "JAListView.h"
10 | #import "JASectionedListView.h"
11 |
12 |
13 | @implementation JAListViewItem
14 |
15 |
16 | #pragma mark NSView
17 |
18 | - (void)scrollWheel:(NSEvent *)event {
19 | [self.listView markViewBeingUsedForInertialScrolling:self];
20 | [super scrollWheel:event];
21 | }
22 |
23 |
24 | #pragma mark NSResponder
25 |
26 | - (BOOL)acceptsFirstResponder {
27 | return YES;
28 | }
29 |
30 |
31 | #pragma mark API
32 |
33 | @synthesize ignoreInListViewLayout;
34 | @synthesize listView;
35 | @synthesize selected;
36 | @synthesize highlighted;
37 | @synthesize listViewPosition;
38 | @synthesize ignoresListViewPadding;
39 |
40 | - (NSImage *)draggingImage {
41 | NSBitmapImageRep *bitmap = [self bitmapImageRepForCachingDisplayInRect:self.bounds];
42 | [self cacheDisplayInRect:self.bounds toBitmapImageRep:bitmap];
43 |
44 | NSSize imageSize = [bitmap size];
45 | NSImage *image = [[[NSImage alloc] initWithSize:imageSize] autorelease];
46 | [image addRepresentation:bitmap];
47 |
48 | NSImage *result = [[[NSImage alloc] initWithSize:imageSize] autorelease];
49 | [result lockFocus];
50 | NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
51 | NSImageInterpolation savedInterpolation = [currentContext imageInterpolation];
52 | [currentContext setImageInterpolation:NSImageInterpolationHigh];
53 | [image drawInRect:NSMakeRect(0, 0, imageSize.width, imageSize.height) fromRect:NSMakeRect(0, 0, imageSize.width, imageSize.height) operation:NSCompositeSourceOver fraction:.5];
54 | [currentContext setImageInterpolation:savedInterpolation];
55 | [result unlockFocus];
56 |
57 | return result;
58 | }
59 |
60 | - (JAListViewPosition)listViewPosition {
61 | JAListViewPosition position = JAListViewPositionNone;
62 | NSUInteger index = (NSUInteger) [self.listView indexForView:self];
63 | NSUInteger numberOfViews = [self.listView numberOfViews];
64 |
65 | if([self.listView isKindOfClass:[JASectionedListView class]]) {
66 | JASectionedListView *sectionedListView = (JASectionedListView *) self.listView;
67 | NSUInteger section = 0;
68 | NSUInteger newIndex = index;
69 | [sectionedListView getSection:§ion andIndex:&newIndex fromAbsoluteIndex:index];
70 | index = newIndex;
71 | numberOfViews = [sectionedListView numberOfViewsInSection:section];
72 | }
73 |
74 | if(index == numberOfViews - 1) {
75 | position |= JAListViewPositionBottom;
76 | }
77 |
78 | if(index == 0) {
79 | position |= JAListViewPositionTop;
80 | }
81 |
82 | if(position == JAListViewPositionNone) {
83 | position = JAListViewPositionMiddle;
84 | }
85 |
86 | return position;
87 | }
88 |
89 | - (void)setSelected:(BOOL)newValue {
90 | selected = newValue;
91 |
92 | [self setNeedsDisplay:YES];
93 | }
94 |
95 | - (void)setHighlighted:(BOOL)newValue {
96 | highlighted = newValue;
97 |
98 | [self setNeedsDisplay:YES];
99 | }
100 |
101 | @end
102 |
--------------------------------------------------------------------------------
/JAListView_Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'JAListView' target in the 'JAListView' project
3 | //
4 |
5 | #ifdef __OBJC__
6 | #import
7 | #endif
8 |
--------------------------------------------------------------------------------
/JAObjectListView.h:
--------------------------------------------------------------------------------
1 | //
2 | // JAObjectListView.h
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 12/6/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "JASectionedListView.h"
11 |
12 | @class JAObjectListViewItem;
13 |
14 |
15 | @interface JAObjectListView : JASectionedListView {}
16 |
17 | - (void)addListViewItem:(JAObjectListViewItem *)view inSection:(NSUInteger)section atIndex:(NSUInteger)index;
18 | - (void)addListViewItem:(JAObjectListViewItem *)view inSection:(NSUInteger)section;
19 | - (void)removeListViewItemInSection:(NSUInteger)section atIndex:(NSUInteger)index;
20 |
21 | - (void)addListViewItem:(JAObjectListViewItem *)view forHeaderForSection:(NSUInteger)section;
22 | - (void)removeListViewItemForHeaderForSection:(NSUInteger)section;
23 |
24 | - (void)removeListViewItem:(JAListViewItem *)view;
25 | - (void)removeAllListViewItems;
26 | - (NSArray *)viewsInSection:(NSUInteger)section;
27 | - (NSUInteger)numberOfSections;
28 |
29 | - (JAListViewItem *)headerForSection:(NSUInteger)section;
30 | - (JAObjectListViewItem *)viewItemForObject:(id)object;
31 | - (NSIndexPath *)indexPathForObject:(id)object;
32 | - (void)removeListViewItemForObject:(id)object;
33 | - (NSArray *)allObjects;
34 |
35 | - (NSString *)debugString;
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/JAObjectListView.m:
--------------------------------------------------------------------------------
1 | //
2 | // JAObjectListView.m
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 12/6/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import "JAObjectListView.h"
10 | #import "JAObjectListViewItem.h"
11 |
12 | @interface JAListView (Private)
13 | - (NSArray *)cachedViews;
14 | @end
15 |
16 | @interface JAObjectListView ()
17 | - (void)reallyAddListViewItem:(JAObjectListViewItem *)view inSection:(NSUInteger)section atIndex:(NSUInteger)index;
18 |
19 | @property (nonatomic, retain) NSMutableArray *sectionRowViews;
20 | @end
21 |
22 |
23 | @implementation JAObjectListView
24 |
25 | - (void)dealloc {
26 | self.sectionRowViews = nil;
27 |
28 | [super dealloc];
29 | }
30 |
31 |
32 | #pragma mark JAListView
33 |
34 | - (void)setup {
35 | [super setup];
36 |
37 | self.sectionDataSource = self;
38 | self.sectionRowViews = [NSMutableArray array];
39 | }
40 |
41 |
42 | #pragma mark JASectionedListView
43 |
44 | - (void)setSectionDataSource:(id)newDataSource {
45 | NSAssert([newDataSource isKindOfClass:[self class]], @"Do not set the sectionedDataSource manually.");
46 |
47 | [super setSectionDataSource:newDataSource];
48 | }
49 |
50 |
51 | #pragma mark JASectionedListViewDataSource
52 |
53 | - (NSUInteger)numberOfSectionsInListView:(JASectionedListView *)listView {
54 | return [self numberOfSections];
55 | }
56 |
57 | - (NSUInteger)listView:(JASectionedListView *)listView numberOfViewsInSection:(NSUInteger)section {
58 | return [self viewsInSection:section].count;
59 | }
60 |
61 | - (JAListViewItem *)listView:(JAListView *)listView sectionHeaderViewForSection:(NSUInteger)section {
62 | return [[self.sectionRowViews objectAtIndex:section] objectAtIndex:0];
63 | }
64 |
65 | - (JAListViewItem *)listView:(JAListView *)listView viewForSection:(NSUInteger)section index:(NSUInteger)index {
66 | return [[self viewsInSection:section] objectAtIndex:index];
67 | }
68 |
69 |
70 | #pragma mark API
71 |
72 | @synthesize sectionRowViews;
73 |
74 | - (void)addListViewItem:(JAObjectListViewItem *)view inSection:(NSUInteger)section atIndex:(NSUInteger)index {
75 | [self reallyAddListViewItem:view inSection:section atIndex:index + 1];
76 | }
77 |
78 | - (void)reallyAddListViewItem:(JAObjectListViewItem *)view inSection:(NSUInteger)section atIndex:(NSUInteger)index {
79 | NSMutableArray *sectionViews = nil;
80 | if(section < self.sectionRowViews.count) {
81 | sectionViews = [self.sectionRowViews objectAtIndex:section];
82 | } else if(section != NSNotFound) {
83 | sectionViews = [NSMutableArray array];
84 | [self.sectionRowViews insertObject:sectionViews atIndex:section];
85 | } else {
86 | NSAssert1(NO, @"Tried to insert view into a non-existent section: %@", view);
87 | }
88 |
89 | if(index != NSNotFound) {
90 | [sectionViews insertObject:view atIndex:index];
91 | } else {
92 | NSAssert1(NO, @"Tried to insert view into a non-existent row: %@", view);
93 | }
94 | }
95 |
96 | - (void)addListViewItem:(JAObjectListViewItem *)view inSection:(NSUInteger)section {
97 | [self addListViewItem:view inSection:section atIndex:[self viewsInSection:section].count];
98 | }
99 |
100 | - (void)removeListViewItemInSection:(NSUInteger)section atIndex:(NSUInteger)index {
101 | NSMutableArray *sectionViews = [self.sectionRowViews objectAtIndex:section];
102 |
103 | NSUInteger trueIndex = index + 1;
104 | JAObjectListViewItem *view = [sectionViews objectAtIndex:trueIndex];
105 | [self unmarkViewBeingUsedForInertialScrolling:view];
106 |
107 | [self deselectView:view];
108 |
109 | [sectionViews removeObjectAtIndex:trueIndex];
110 | }
111 |
112 | - (void)addListViewItem:(JAObjectListViewItem *)view forHeaderForSection:(NSUInteger)section {
113 | NSMutableArray *sectionViews = [NSMutableArray array];
114 | [self.sectionRowViews insertObject:sectionViews atIndex:section];
115 | [sectionViews insertObject:view atIndex:0];
116 | }
117 |
118 | - (void)removeListViewItemForHeaderForSection:(NSUInteger)section {
119 | NSArray *views = [self.sectionRowViews objectAtIndex:section];
120 | for(JAObjectListViewItem *item in views) {
121 | [self unmarkViewBeingUsedForInertialScrolling:item];
122 |
123 | [self deselectView:item];
124 | }
125 |
126 | [self.sectionRowViews removeObjectAtIndex:section];
127 | }
128 |
129 | - (void)removeListViewItem:(JAListViewItem *)view {
130 | [self unmarkViewBeingUsedForInertialScrolling:view];
131 |
132 | [self deselectView:view];
133 |
134 | for(NSUInteger sectionIndex = 0; sectionIndex < self.sectionRowViews.count; sectionIndex++) {
135 | NSMutableArray *sectionViews = [self.sectionRowViews objectAtIndex:sectionIndex];
136 | NSUInteger index = [sectionViews indexOfObject:view];
137 | if(index != NSNotFound) {
138 | if(index == 0) {
139 | [self removeListViewItemForHeaderForSection:sectionIndex];
140 | } else {
141 | [sectionViews removeObjectAtIndex:index];
142 | }
143 | }
144 | }
145 | }
146 |
147 | - (void)removeAllListViewItems {
148 | [self clearViewsBeingUsedForInertialScrolling];
149 | [self deselectAllViews];
150 | [self.sectionRowViews removeAllObjects];
151 | }
152 |
153 | - (NSArray *)viewsInSection:(NSUInteger)section {
154 | NSParameterAssert(section < self.sectionRowViews.count);
155 |
156 | NSMutableArray *views = [[[self.sectionRowViews objectAtIndex:section] mutableCopy] autorelease];
157 | if(views.count < 1) return nil;
158 |
159 | [views removeObjectAtIndex:0];
160 | return views;
161 | }
162 |
163 | - (NSUInteger)numberOfSections {
164 | return self.sectionRowViews.count;
165 | }
166 |
167 | - (JAObjectListViewItem *)viewItemForObject:(id)object {
168 | for(NSArray *views in self.sectionRowViews) {
169 | for(JAObjectListViewItem *item in views) {
170 | if([item.object isEqual:object]) {
171 | return item;
172 | }
173 | }
174 | }
175 |
176 | return nil;
177 | }
178 |
179 | - (NSIndexPath *)indexPathForObject:(id)object {
180 | NSAssert(object != nil, @"Invalid to look for the index path of a nil object.");
181 |
182 | NSUInteger section = 0;
183 | for(NSArray *views in self.sectionRowViews) {
184 |
185 | NSUInteger index = 0;
186 | for(JAObjectListViewItem *item in views) {
187 | if([item.object isEqual:object]) {
188 | if(index == 0) {
189 | return [NSIndexPath indexPathForIndex:JASectionedListViewHeaderIndex inSection:section];
190 | } else {
191 | return [NSIndexPath indexPathForIndex:index - 1 inSection:section];
192 | }
193 | }
194 |
195 | index++;
196 | }
197 |
198 | section++;
199 | }
200 |
201 | return nil;
202 | }
203 |
204 | - (JAListViewItem *)headerForSection:(NSUInteger)section {
205 | return [[self.sectionRowViews objectAtIndex:section] objectAtIndex:0];
206 | }
207 |
208 | - (void)removeListViewItemForObject:(id)object {
209 | [self removeListViewItem:[self viewItemForObject:object]];
210 | }
211 |
212 | - (NSArray *)allObjects {
213 | NSMutableArray *objects = [NSMutableArray array];
214 | for(NSArray *views in self.sectionRowViews) {
215 | for(JAObjectListViewItem *item in views) {
216 | if(item.object != nil) [objects addObject:item.object];
217 | }
218 | }
219 |
220 | return objects;
221 | }
222 |
223 | - (NSString *)debugString {
224 | return [NSString stringWithFormat:@"%@", self.sectionRowViews];
225 | }
226 |
227 | @end
228 |
--------------------------------------------------------------------------------
/JAObjectListViewItem.h:
--------------------------------------------------------------------------------
1 | //
2 | // JAObjectListViewItem.h
3 | // GitHub
4 | //
5 | // Created by Josh Abernathy on 12/29/10.
6 | // Copyright 2010 GitHub. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "JAListViewItem.h"
11 |
12 |
13 | @interface JAObjectListViewItem : JAListViewItem {}
14 |
15 | @property (nonatomic, retain) id object;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/JAObjectListViewItem.m:
--------------------------------------------------------------------------------
1 | //
2 | // JAObjectListViewItem.m
3 | // GitHub
4 | //
5 | // Created by Josh Abernathy on 12/29/10.
6 | // Copyright 2010 GitHub. All rights reserved.
7 | //
8 |
9 | #import "JAObjectListViewItem.h"
10 |
11 |
12 | @implementation JAObjectListViewItem
13 |
14 |
15 | #pragma mark API
16 |
17 | @synthesize object;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/JASectionedListView.h:
--------------------------------------------------------------------------------
1 | //
2 | // JASectionedListView.h
3 | // DemoApp
4 | //
5 | // Created by Josh Abernathy on 11/26/10.
6 | // Copyright 2010 Maybe Apps, LLC. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "JAListView.h"
11 | #import "NSIndexPath+JAListViewExtensions.h"
12 |
13 | enum { JASectionedListViewHeaderIndex = NSUIntegerMax };
14 |
15 | @class JASectionedListView;
16 |
17 | @protocol JASectionedListViewDataSource
18 | - (NSUInteger)numberOfSectionsInListView:(JASectionedListView *)listView;
19 | - (NSUInteger)listView:(JASectionedListView *)listView numberOfViewsInSection:(NSUInteger)section;
20 | - (JAListViewItem *)listView:(JAListView *)listView sectionHeaderViewForSection:(NSUInteger)section;
21 | - (JAListViewItem *)listView:(JAListView *)listView viewForSection:(NSUInteger)section index:(NSUInteger)index;
22 | @end
23 |
24 |
25 | @interface JASectionedListView : JAListView {}
26 |
27 | - (NSUInteger)numberOfSections;
28 | - (NSUInteger)numberOfViewsInSection:(NSUInteger)section;
29 |
30 | - (JAListViewItem *)viewInSection:(NSUInteger)section atIndex:(NSUInteger)index;
31 | - (JAListViewItem *)viewAtIndexPath:(NSIndexPath *)indexPath;
32 |
33 | - (NSUInteger)absoluteIndexFromSection:(NSUInteger)section index:(NSUInteger)index;
34 | - (NSUInteger)absoluteIndexFromIndexPath:(NSIndexPath *)indexPath;
35 |
36 | - (NSIndexPath *)indexPathForView:(JAListViewItem *)view;
37 |
38 | - (BOOL)isViewSectionHeaderView:(JAListViewItem *)view;
39 |
40 | /*
41 | * If the absolute index is a header view, index will be set to JASectionedListViewHeaderIndex.
42 | */
43 | - (void)getSection:(NSUInteger *)section andIndex:(NSUInteger *)index fromAbsoluteIndex:(NSUInteger)absoluteIndex;
44 | - (NSIndexPath *)indexPathFromAbsoluteIndex:(NSUInteger)absoluteIndex;
45 |
46 | @property (nonatomic, assign) IBOutlet id sectionDataSource;
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/JASectionedListView.m:
--------------------------------------------------------------------------------
1 | //
2 | // JASectionedListView.m
3 | // DemoApp
4 | //
5 | // Created by Josh Abernathy on 11/26/10.
6 | // Copyright 2010 Maybe Apps, LLC. All rights reserved.
7 | //
8 |
9 | #import "JASectionedListView.h"
10 | #import "JAListViewItem.h"
11 |
12 | @interface JASectionedListView ()
13 | - (NSUInteger)numberOfTotalViews;
14 | @end
15 |
16 |
17 | @implementation JASectionedListView
18 |
19 |
20 | #pragma mark JAListView
21 |
22 | - (void)setup {
23 | [super setup];
24 |
25 | self.dataSource = self;
26 | }
27 |
28 | - (void)setDataSource:(id)newDataSource {
29 | NSAssert1([newDataSource isKindOfClass:[self class]], @"Please use sectionDataSource for %@.", NSStringFromClass([self class]));
30 |
31 | [super setDataSource:newDataSource];
32 | }
33 |
34 |
35 | #pragma mark JAListViewDataSource
36 |
37 | - (NSUInteger)numberOfItemsInListView:(JAListView *)listView {
38 | return [self numberOfTotalViews];
39 | }
40 |
41 | - (JAListViewItem *)listView:(JAListView *)listView viewAtIndex:(NSUInteger)index {
42 | NSUInteger relativeIndex;
43 | NSUInteger section;
44 |
45 | [self getSection:§ion andIndex:&relativeIndex fromAbsoluteIndex:index];
46 |
47 | if(relativeIndex == JASectionedListViewHeaderIndex) {
48 | return [self.sectionDataSource listView:self sectionHeaderViewForSection:section];
49 | }
50 |
51 | return [self.sectionDataSource listView:self viewForSection:section index:relativeIndex];
52 | }
53 |
54 |
55 | #pragma mark API
56 |
57 | @synthesize sectionDataSource;
58 |
59 | - (void)getSection:(NSUInteger *)section andIndex:(NSUInteger *)index fromAbsoluteIndex:(NSUInteger)absoluteIndex {
60 | *index = absoluteIndex;
61 | for(NSUInteger sectionIndex = 0; sectionIndex < [self numberOfSections]; sectionIndex++) {
62 | NSUInteger numberOfViews = [self numberOfViewsInSection:sectionIndex] + 1;
63 | if(*index < numberOfViews) {
64 | *section = sectionIndex;
65 | break;
66 | } else {
67 | *index -= numberOfViews;
68 | }
69 | }
70 |
71 | // We want index 0 to be the first row, not the header view, so we reinterpret that as JASectionedListViewHeaderIndex.
72 | if(*index == 0) {
73 | *index = JASectionedListViewHeaderIndex;
74 | } else {
75 | *index -= 1;
76 | }
77 | }
78 |
79 | - (NSIndexPath *)indexPathFromAbsoluteIndex:(NSUInteger)absoluteIndex {
80 | NSUInteger section;
81 | NSUInteger index;
82 | [self getSection:§ion andIndex:&index fromAbsoluteIndex:absoluteIndex];
83 | return [NSIndexPath indexPathForIndex:index inSection:section];
84 | }
85 |
86 | - (NSUInteger)absoluteIndexFromSection:(NSUInteger)section index:(NSUInteger)index {
87 | NSUInteger absoluteIndex = 0;
88 | for(NSUInteger sectionIndex = 0; sectionIndex < section; sectionIndex++) {
89 | NSUInteger numberOfViews = [self numberOfViewsInSection:sectionIndex] + 1;
90 | absoluteIndex += numberOfViews;
91 | }
92 |
93 | absoluteIndex += index + 1;
94 | return absoluteIndex;
95 | }
96 |
97 | - (NSUInteger)absoluteIndexFromIndexPath:(NSIndexPath *)indexPath {
98 | return [self absoluteIndexFromSection:indexPath.section index:indexPath.index];
99 | }
100 |
101 | - (NSIndexPath *)indexPathForView:(JAListViewItem *)view {
102 | return [self indexPathFromAbsoluteIndex:(NSUInteger) [self indexForView:view]];
103 | }
104 |
105 | - (BOOL)isViewSectionHeaderView:(JAListViewItem *)view {
106 | return [self indexPathForView:view].index == JASectionedListViewHeaderIndex;
107 | }
108 |
109 | - (JAListViewItem *)viewInSection:(NSUInteger)section atIndex:(NSUInteger)index {
110 | return [self viewAtIndex:[self absoluteIndexFromSection:section index:index]];
111 | }
112 |
113 | - (JAListViewItem *)viewAtIndexPath:(NSIndexPath *)indexPath {
114 | return [self viewInSection:indexPath.section atIndex:indexPath.index];
115 | }
116 |
117 | - (NSUInteger)numberOfSections {
118 | return [self.sectionDataSource numberOfSectionsInListView:self];
119 | }
120 |
121 | - (NSUInteger)numberOfViewsInSection:(NSUInteger)section {
122 | return [self.sectionDataSource listView:self numberOfViewsInSection:section];
123 | }
124 |
125 | - (NSUInteger)numberOfTotalViews {
126 | // one section header view per section
127 | NSUInteger totalCount = [self numberOfSections];
128 | for(NSUInteger sectionIndex = 0; sectionIndex < [self numberOfSections]; sectionIndex++) {
129 | totalCount += [self numberOfViewsInSection:sectionIndex];
130 | }
131 |
132 | return totalCount;
133 | }
134 |
135 | - (void)setSectionDataSource:(id )newDataSource {
136 | sectionDataSource = newDataSource;
137 |
138 | [self reloadData];
139 | }
140 |
141 | @end
142 |
--------------------------------------------------------------------------------
/NSIndexPath+JAListViewExtensions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSIndexPath+JAListViewExtensions.h
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 11/26/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 |
12 | @interface NSIndexPath (JAListViewExtensions)
13 | + (NSIndexPath *)indexPathForIndex:(NSUInteger)index inSection:(NSUInteger)section;
14 | + (NSIndexPath *)indexPathForSection:(NSUInteger)section;
15 |
16 | @property (nonatomic, readonly) NSUInteger index;
17 | @property (nonatomic, readonly) NSUInteger section;
18 | @end
19 |
--------------------------------------------------------------------------------
/NSIndexPath+JAListViewExtensions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSIndexPath+JAListViewExtensions.m
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 11/26/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import "NSIndexPath+JAListViewExtensions.h"
10 |
11 |
12 | @implementation NSIndexPath (JAListViewExtensions)
13 |
14 | + (NSIndexPath *)indexPathForIndex:(NSUInteger)index inSection:(NSUInteger)section {
15 | NSUInteger indices[2];
16 | indices[0] = section;
17 | indices[1] = index;
18 | return [NSIndexPath indexPathWithIndexes:indices length:2];
19 | }
20 |
21 | + (NSIndexPath *)indexPathForSection:(NSUInteger)section {
22 | return [NSIndexPath indexPathWithIndex:section];
23 | }
24 |
25 | - (NSUInteger)index {
26 | return [self indexAtPosition:1];
27 | }
28 |
29 | - (NSUInteger)section {
30 | return [self indexAtPosition:0];
31 | }
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Stock New BSD License:
2 |
3 | Copyright (c) 2010, Josh Abernathy
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10 | * Neither the name of the Josh Abernathy nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11 |
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13 |
14 |
--------------------------------------------------------------------------------
/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // JAListView
4 | //
5 | // Created by Josh Abernathy on 9/29/10.
6 | // Copyright 2010 Maybe Apps. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | int main(int argc, char *argv[])
12 | {
13 | srandom((unsigned int) time(NULL));
14 |
15 | return NSApplicationMain(argc, (const char **) argv);
16 | }
17 |
--------------------------------------------------------------------------------