269 Commits

Author SHA1 Message Date
Alex Rozanski 66d26c7c4a Merge pull request #54 from orta/elcap
Update demo for el cap
2016-06-29 20:43:41 +01:00
Alex Rozanski c939b2597f Merge pull request #58 from dinhviethoa/fix-crash-in-dealloc
Fixed crash in -PXSourceList dealloc
2016-06-29 20:42:47 +01:00
Alex Rozanski 037854c178 Merge pull request #59 from dinhviethoa/sierra-compatibility
Fixed compatibility with 10.12 Sierra
2016-06-29 20:41:38 +01:00
Hoa V. DINH b8e90dec19 Fixed for 10.12 2016-06-26 09:42:33 -07:00
Hoa V. DINH 83a09b299d Fixed potential crash 2016-06-26 08:37:46 -07:00
Orta Therox 978053929f Show transparency in example 2015-11-03 10:28:32 -05:00
Orta Therox f0e7809109 Update demo for el cap 2015-11-03 10:22:20 -05:00
Alex Rozanski bb53253cba Merge pull request #50 from orta/patch-1
Update README.markdown
2015-05-08 14:06:47 +01:00
Orta 54abae7a5a Update README.markdown 2015-05-08 11:30:57 +01:00
Alex Rozanski e0071d0aed Bump project to 2.0.7 2015-05-08 11:19:40 +01:00
Alex Rozanski bc5727fba6 PXSourceList.m: fix whitespace woes.
- Convert all tabs to spaces.
- Remove trailing whitespace.
2015-05-08 11:16:28 +01:00
Alex Rozanski b902e04b9c Fix -setFlipped: deprecation warning.
This removes old, pre-10.6 drawing code (including call to -setFlipped:) as we
no longer support 10.6.
2015-05-08 11:13:08 +01:00
Alex Rozanski 8f7081f4d5 Bump project to 2.0.6 2015-05-08 11:01:58 +01:00
Alex Rozanski 2bf9e6a6f1 Merge pull request #49 from dusek/accessibility-badge-viewbased
Fix PXSourceListBadgeCell accessibility
2015-05-08 10:56:40 +01:00
Boris Dušek ab0408c8fe Fix PXSourceListBadgeCell accessibility
Now in the view-based source list, instead of just "Photos", VoiceOver
reads "Photos, 264"
2015-03-28 12:45:02 +01:00
Alex Rozanski 929b1651af Add badges to README. 2014-09-25 09:42:48 -04:00
Alex Rozanski 2eb6414e6b Bump project to 2.0.5 2014-06-07 14:45:37 -04:00
Alex Rozanski 2550dce3b7 Fix #43: sourceListDeleteKeyPressedOnRows: called twice.
This issue was being caused by the fact that the overridden
-setDelegate: method on PXSourceListDelegateDataSourceProxy doesn't
correctly remove the old delegate as observing the PXSourceList
notifications.

Setting the same delegate twice will cause this issue to
occur.
2014-06-07 14:39:44 -04:00
Alex Rozanski c62b8b2c92 Bump project to 2.0.4. 2014-05-11 13:17:39 -04:00
Alex Rozanski 67772622cf Remove unused badgeMargin constant. 2014-05-11 13:12:42 -04:00
Alex Rozanski a555035042 Merge pull request #41 from CrazyCatcher/master
fix a Zeroing Weak References problem
2014-05-11 12:59:55 -04:00
crazycatcher 0129de341e fix a Zeroing Weak References problem 2014-05-06 20:12:51 +08:00
Alex Rozanski 7a82f63e9b Add Release Notes for 2.0.3. 2014-03-25 21:41:47 +00:00
Alex Rozanski b215e263ef Bump project to 2.0.3. 2014-03-25 21:41:41 +00:00
Alex Rozanski 5ce29c98c9 Fix issue in view-based source list example.
This fixes an issue where items created with the add button couldn't be
dragged.
2014-03-25 21:37:06 +00:00
Alex Rozanski 7df260c6f7 Fix #40: Editing titles on cell based source list causes exception.
The cause of this bug was returning YES in
-[PXSourceListDelegateDataSourceProxy respondsToSelector:] for the
NSControl delegate methods -controlTextDidEndEditing:,
-controlTextDidBeginEditing: and -controlTextDidChange: when they were
called on the proxy because NSOutlineView implements them internally.
However we weren't returning a method signature for them in -methodSignatureForSelector:
which was throwing an exception.

This fix has two components:
- We only allow forwarding of NSOutlineView(Delegate|DataSource) methods
  to the source list PXSourceListDelegateDataSourceProxy (which
  was the original intention). If PXSourceList returns YES for
  -respondsToSelector: we ignore it if the method is not from one of
  these two protocols.
- The NSControl delegate methods have been added to the fast-path
  forwarding delegate methods array in
  PXSourceListDelegateDataSourceProxy (the array which contains method
  names which can be forwarded to the source list's delegate as-is, without
  modifying the selector or arguments).

These fix the underlying cause of the exception and implement
the missing behaviour of allowing invocation of these NSControl methods
on the source list's delegate.
2014-03-25 21:29:31 +00:00
Alex Rozanski 8407bc98bb Remove unnecessary computation in -[PXSourceListDelegateDataSourceProxy methodSignatureForSelector:].
Fast-path delegate and data source methods (those whose method
signature doesn't need modification) don't need to be checked in
-methodSignatureForSelector: because they are handled in
-forwardingTargetForSelector: which bypasses the
-methodSignatureForSelector:/forwardInvocation: path.
2014-03-25 21:08:20 +00:00
Alex Rozanski 29d61656fe Add Release Notes for 2.0.2. 2014-03-06 00:01:22 +00:00
Alex Rozanski 52e21a8ced Bump project to 2.0.2. 2014-03-06 00:00:06 +00:00
Alex Rozanski 60974e14fd Fix #39: Badges not drawn correctly when Source List row is selected.
This fix consists of two parts:
1) We first need to check the backgroundStyle property in
PXSourceListBadgeCell to determine whether to draw the light badge
background or not.

2) Next we need to determine whether the enclosing Source List (or child
view) is focused to decide whether to draw the badge text using the bright
blue or grey-blue. To do this, we walk up the view hierarchy
from the common ancestor view of the controlView and firstResponder
until we hit a PXSourceList instance. If we find one, then the source
list is selected, otherwise not.

Walking up the tree from the common ancestor view catches the highly-
unlikely but possible scenario where there are two PXSourceList
instances on screen and one is focused.
2014-03-05 23:50:11 +00:00
Alex Rozanski 3eadddb784 Bump project to 2.0.1. 2014-01-29 13:25:34 +00:00
Alex Rozanski 20a6e83f86 Add Release Notes for 2.0.1. 2014-01-29 12:55:20 +00:00
Alex Rozanski 7548ffdd1b Update ReleaseNotes for 2.0.0.
The note about -[PXSourceList delegate] and -[PXSourceList dataSource]
being marked as unavailable was missing from the 2.0.0 release notes.
2014-01-29 12:50:24 +00:00
Alex Rozanski 8a647790ab Small fixes to various parts of the documentation. 2014-01-27 17:58:56 +00:00
Alex Rozanski 92b64abde5 Improve class documentation for PXSourceList. 2014-01-27 17:38:26 +00:00
Alex Rozanski 1d82dc7cc6 Fix grammar in README. 2014-01-27 17:32:52 +00:00
Alex Rozanski e54db103c8 Add a list of key features to the 'Overview' section. 2014-01-27 17:31:46 +00:00
Alex Rozanski 9e480433fc Improve 'PXSourceList 2' section. 2014-01-27 17:17:41 +00:00
Alex Rozanski 92e25dfad8 Clarify source list style in README. 2014-01-27 17:07:50 +00:00
Alex Rozanski daac004cd6 Clarify bold sentence in motivation section in README. 2014-01-27 17:06:57 +00:00
Alex Rozanski 0457b85abf Tidy up podspec. 2014-01-27 15:34:15 +00:00
Alex Rozanski 89003e83f6 Fix license attribute in the podspec. 2014-01-27 15:21:04 +00:00
Alex Rozanski 1a42ce1151 Add @param declarations in PXSourceList.h.
These were missing for the parameters for -setDelegate: and
-setDataSource:.
2014-01-27 15:14:01 +00:00
Alex Rozanski 217f046290 Use the new libraryItem and albumsItem methods in the view-based example. 2014-01-27 15:10:16 +00:00
Alex Rozanski 4c9e975bcb Data model changes in the view-based example. 2014-01-27 15:08:39 +00:00
Alex Rozanski ba8878cafa Tidy up model creation code in view-based example. 2014-01-27 15:00:59 +00:00
Alex Rozanski 7449fd8b12 Add a 'type' property to the PhotoCollection class in the view-based example. 2014-01-27 14:58:40 +00:00
Alex Rozanski d1dbe60b4e Make use of a Photo object in the view-based example app. 2014-01-27 14:51:49 +00:00
Alex Rozanski 1187aa3c70 Clarify PXSourceListItem badge value property description in the README. 2014-01-27 14:39:04 +00:00
Alex Rozanski 7342601ea7 Provide a more informative header for PXSourceListItem. 2014-01-27 14:38:12 +00:00
Alex Rozanski 35d1e8992a Add section in the README for PXSourceListItem. 2014-01-27 14:36:34 +00:00
Alex Rozanski 10d6abe252 Remove code styling for heading as it looks terrible. 2014-01-27 14:28:39 +00:00
Alex Rozanski ced5fc1690 Move "Documentation" section close to the top in the README. 2014-01-27 14:25:32 +00:00
Alex Rozanski c5e3ae6f43 Improve "Motivation" section of the README. 2014-01-27 14:23:24 +00:00
Alex Rozanski b03b731601 s/Apple Human Interface Guidelines/OS X Human Interface Guidelines/. 2014-01-27 14:14:40 +00:00
Alex Rozanski 18eca61f0e Add note in the README about Source List styling. 2014-01-27 14:13:13 +00:00
Alex Rozanski 0f6c209972 Update documentation for automatic badge view positioning in PXSourceListTableCellView. 2014-01-27 14:09:36 +00:00
Alex Rozanski 6300fd3844 Change positioning in PXSourceListTableCellView.
Position the badge view in -layout automatically, as NSTableCellView does
for the textField and imageView outlets.
2014-01-27 14:03:08 +00:00
Alex Rozanski 611e6d31cd Fix formatting in README. 2014-01-27 13:40:48 +00:00
Alex Rozanski 22e7df8bb8 Fix small typo in README. 2014-01-27 13:38:42 +00:00
Alex Rozanski 843ec6e8ad Add 'View-based Source Lists' section. 2014-01-27 13:37:38 +00:00
Alex Rozanski 4ee91187ba Move 'PXSourceList 2' section above 'Delegate and Data Source'. 2014-01-27 13:29:04 +00:00
Alex Rozanski f9278ccca3 Add a 'PXSourceList 2' section to the README. 2014-01-27 13:25:57 +00:00
Alex Rozanski 3833e9491c Improve 'Required Methods' section of README.
Add required method declarations for cell-based and view-based
source lists for easy copypasta.
2014-01-27 13:16:51 +00:00
Alex Rozanski 26ef0dcdb0 General README improvements. 2014-01-27 13:14:24 +00:00
Alex Rozanski 5b0de5d015 Tidy up documentation section of README. 2014-01-27 12:52:05 +00:00
Alex Rozanski 5f65870086 Add cell-based and view-based info to the README. 2014-01-27 12:49:11 +00:00
Alex Rozanski 897c93c803 Add additional information to README.
Add information about delegate and data source methods.
2014-01-27 12:44:35 +00:00
Alex Rozanski 719fe1e215 Use "~> 2.0" in the Podfile example in the README. 2014-01-27 12:39:32 +00:00
Alex Rozanski 2fb4bbf2e4 Add Cocoapods installation instructions. 2014-01-23 22:52:01 +00:00
Alex Rozanski 0e6d9044b6 Use relative path for screenshot image in README. 2014-01-23 22:31:22 +00:00
Alex Rozanski 7381b1e96c Add LICENSE file to the repository. 2014-01-23 22:28:50 +00:00
Alex Rozanski 64f9204bf2 Add CocoaPods podspec. 2014-01-23 22:23:17 +00:00
Alex Rozanski 5d641f3c79 Fix the human-readable copyright in PXSourceList.framework's Info plist. 2014-01-23 22:19:23 +00:00
Alex Rozanski b62409e738 Fix header file visibility in the PXSourceList framework.
Some files were marked as Project when they should be Public.
2014-01-23 22:17:04 +00:00
Alex Rozanski 4c7a24ff3b Bump the PXSourceList framework version to 2.0.0. 2014-01-23 22:15:48 +00:00
Alex Rozanski c0cb56bacb Add the correct location for PXSourceList-Info.plist in the Xcode project. 2014-01-23 22:11:20 +00:00
Alex Rozanski 89f0483e3b Add a release notes file. 2014-01-23 22:08:42 +00:00
Alex Rozanski cf1ea5eb60 Fix link to OSI licence page. 2014-01-23 21:55:18 +00:00
Alex Rozanski c82ff20752 Add contributors link to attribution section and fix dates and attribution in the licence. 2014-01-23 21:39:24 +00:00
Alex Rozanski 916abecd67 Grammar :| 2014-01-23 21:34:10 +00:00
Alex Rozanski eb031d173c Remove "Known Issues" section from the README because the issues are either captured in GitHub Issues or elsewhere. 2014-01-23 21:31:43 +00:00
Alex Rozanski 64937fcfc0 Add a note about linking to PXSourceList.framework. 2014-01-23 21:28:07 +00:00
Alex Rozanski 7f1a377845 Move PXSourceList-Info.plist into the project root. 2014-01-23 21:23:49 +00:00
Alex Rozanski bb7e905b4a Add note about copying all of the files from PXSourceList/ into projects in the README. 2014-01-23 21:21:31 +00:00
Alex Rozanski 65b269fef0 Rename "How the control works" to "Delegate and Data Source" and improve this section. 2014-01-23 17:51:03 +00:00
Alex Rozanski b5cd679440 Move the screenshot to the Introduction section and use the new screenshot. 2014-01-23 17:43:07 +00:00
Alex Rozanski 9018d2525b Improve Documentation section of the README. 2014-01-23 17:39:20 +00:00
Alex Rozanski 4c9a0ef270 Improve beginning of README file. 2014-01-23 17:31:52 +00:00
Alex Rozanski f4f6aefb15 Add a view-based Source List screenshot to the repository. 2014-01-23 17:16:05 +00:00
Alex Rozanski a80fdadfd6 Use meaningful names for the window titles for both example projects. 2014-01-23 17:12:41 +00:00
Alex Rozanski f416b43ccf Add a currently selected item label to the view-based Source List example project. 2014-01-23 17:11:39 +00:00
Alex Rozanski d51dcd3e9c Remove explicit FRAMEWORK_SEARCH_PATHS build setting from the cell-based Source List example as it's not needed. 2014-01-23 16:55:28 +00:00
Alex Rozanski a9ef8bfcf0 Implement #35: Give compiler errors for the inherited delegate & datasource. 2014-01-23 16:53:42 +00:00
Alex Rozanski d159f8e218 Tighten up PXSourceListItem class documentation. 2014-01-23 14:30:09 +00:00
Alex Rozanski 3fa7c89a20 Improve class documentation for PXSourceListBadgeView. 2014-01-23 14:24:01 +00:00
Alex Rozanski 63eb6d836c Add @see references to PXSourceListDelegate documentation. 2014-01-23 14:22:09 +00:00
Alex Rozanski 702319aacc Add more @warnings for PXSourceListDelegate methods which are only used when the Source List is operating in cell-based mode. 2014-01-23 14:07:43 +00:00
Alex Rozanski 8fb93c6fcb Add @return information for -sourceList:toolTipForCell:rect:item:mouseLocation: documentation. 2014-01-23 14:02:52 +00:00
Alex Rozanski c9cdcc6ecf Add note that -sourceList:toolTipForCell:rect:item:mouseLocation: is only used when PXSourceList is operating in cell-based mode. 2014-01-23 14:02:06 +00:00
Alex Rozanski 72a5a24c4f Improve documentation in PXSourceListDelegate.h. 2014-01-23 13:57:52 +00:00
Alex Rozanski b48a3022bb Add full-stops to all sentences in the PXSourceListDelegate documentation. 2014-01-23 13:33:07 +00:00
Alex Rozanski 488ba1d18b Move -sourceList:toolTipForCell:rect:item:mouseLocation: to a new 'Providing Tooltips' section in the PXSourceListDelegate documentation. 2014-01-23 13:29:51 +00:00
Alex Rozanski 5618ffbb3a Add minimum OS X SDK version numbers to the PXSourceListDelegate documentation. 2014-01-23 13:28:49 +00:00
Alex Rozanski 58675b10d1 Fix incorrect minimum PXSourceList version number in documentation for -sourceList:isGroupAlwaysExpanded:. 2014-01-23 13:26:18 +00:00
Alex Rozanski cdfca99b11 Add method group names to the PXSourceListDelegate documentation. 2014-01-23 13:25:26 +00:00
Alex Rozanski 59190fa728 Add @warning to second PXSourceListDataSource protocol documentation paragraph. 2014-01-23 13:20:08 +00:00
Alex Rozanski 99e678b430 Minor fixes to the PXSourceListDataSource members. 2014-01-22 17:02:31 +00:00
Alex Rozanski 074c1badfb Add more information to protocol for PXSourceListDataSource. 2014-01-22 16:43:58 +00:00
Alex Rozanski c058bfc0cc Go for the final improvement of the documentation in PXSourceList.h. 2014-01-22 16:38:44 +00:00
Alex Rozanski 67c7ecf1ff Improve PXSourceListDataSource protocol documentation. 2014-01-22 16:15:47 +00:00
Alex Rozanski 676357f878 Improve NSOutlineViewDelegate protocol documentation. 2014-01-22 16:15:18 +00:00
Alex Rozanski a22ad22aaa Document the remainder of PXSourceListDataSource. 2014-01-22 16:10:45 +00:00
Alex Rozanski fa81835e0d Revert "Add PXSourceListDataSource method -sourceList:sortDescriptorsDidChange:."
This method shouldn't have been added because we don't use table headers in PXSourceList.
This reverts commit dc194d3d2d.
2014-01-22 15:51:50 +00:00
Alex Rozanski ac5f7a07e2 Document delegate notification methods and move them into PXSourceListDelegate. 2014-01-22 15:25:01 +00:00
Alex Rozanski d421cf68be Document PXSourceList delegate notifications. 2014-01-22 15:17:22 +00:00
Alex Rozanski c262e3022d Document the remaining methods on PXSourceListDelegate. 2014-01-21 18:09:37 +00:00
Alex Rozanski 08cb8e9f9a Document some of the new PXSourceListDelegate methods added in PXSourceList 2.0. 2014-01-21 17:54:58 +00:00
Alex Rozanski 44cd91369c Add note to the PXSourceListDelegate class documentation about NSTableColumn parameters not being a part of some of the delegate methods. 2014-01-21 17:43:35 +00:00
Alex Rozanski 37b014b986 Document more methods of PXSourceListDelegate. 2014-01-21 15:55:10 +00:00
Alex Rozanski b4babe8596 Change @availability references in PXSourceList{Delegate,DataSource}.h to refer to PXSourceList version. 2014-01-21 15:34:54 +00:00
Alex Rozanski 13bc8e4636 Add @availability parameter to members in PXSourceList.h. 2014-01-21 15:33:49 +00:00
Alex Rozanski b45e06c65e Add missing -sourceList:shouldShowCellExpansionForItem: method to PXSourceListDelegate. 2014-01-21 15:23:32 +00:00
Alex Rozanski da58f03800 Fix incorrect selector used in +[PXSourceListDelegateDataSourceProxy addCustomMethodNameMappings] 2014-01-21 15:22:56 +00:00
Alex Rozanski ea899192dc Upgrade the Xcode project file. 2014-01-21 15:15:40 +00:00
Alex Rozanski cc61dcd094 Move internal files into PXSourceList/Internal. 2014-01-21 15:04:24 +00:00
Alex Rozanski 6c8d09bd20 Make PXSourceListDelegateDataSourceProxy an NSProxy subclass. 2014-01-15 10:37:55 +00:00
Alex Rozanski 9cc6c4a8b5 Tighten up custom NSOutlineViewDelegate method implementations in PXSourceList.m. 2014-01-09 13:03:04 +00:00
Alex Rozanski d9c88b6b0c Remove implementation of -outlineView:willDisplayCell:forTableColumn:item: on PXSourceList as it is no longer needed. 2014-01-09 12:59:06 +00:00
Alex Rozanski f53da27c6c Remove implementation of -outlineView:selectionIndexesForProposedSelection: from PXSourceList as this messes with some of the delegate calls. This behaviour is implemented in -reloadData so is not needed. 2014-01-09 12:55:11 +00:00
Alex Rozanski 6a45539bf5 Remove implementation of -outlineView:dataCellForTableColumn:item: on PXSourceList as it is not needed. 2014-01-09 12:52:21 +00:00
Alex Rozanski 5ab99e01e4 Add information to the PXSourceList docs on using PXSourceListItem. 2014-01-08 20:58:48 +00:00
Alex Rozanski 9d09db0561 Add more detail to the 'cell-based vs. view-based mode' section of the documentation in PXSourceList.h. 2014-01-08 20:44:16 +00:00
Alex Rozanski be0a25457d Tighten up the introduction to the PXSourceList documentation. 2014-01-08 20:32:53 +00:00
Alex Rozanski aa39d26cbd Add view-based vs. cell-based notes to the documentation of PXSourceList. 2014-01-08 20:30:26 +00:00
Alex Rozanski 9811f14708 Document PXSourceListTableCellView. 2014-01-08 20:18:40 +00:00
Alex Rozanski dd38b1a2a0 Add a note about PXSourceListBadgeCell's purpose to the top of its header file. 2014-01-08 17:38:22 +00:00
Alex Rozanski 66e6d45a68 Document PXSourceListBadgeView. 2014-01-08 17:37:14 +00:00
Alex Rozanski 87b65118fd Rename the 'Internal' folder to 'Private' in Xcode. 2014-01-08 17:24:10 +00:00
Alex Rozanski 90656cf6b8 Document the rest of the methods in PXSourceListItem. 2014-01-08 17:22:33 +00:00
Alex Rozanski 0fbee2543c Document the convenience initialisers of PXSourceListItem. 2014-01-08 17:06:19 +00:00
Alex Rozanski 7923c9b8c3 Use <h3 /> for class documentation headers. 2014-01-08 17:01:36 +00:00
Alex Rozanski 57943a4cbe Add class documentation for PXSourceListItem. 2014-01-08 17:00:07 +00:00
Alex Rozanski 95c0bbcc3c Remove SourceListItem.{h,m} from the project. 2014-01-08 16:42:01 +00:00
Alex Rozanski ae87ceac0b Move the cell-based example to using PXSourceListItem. 2014-01-08 16:41:23 +00:00
Alex Rozanski 4a53a00a17 Switch the view-based example to using PXSourceListItem. 2014-01-08 16:38:57 +00:00
Alex Rozanski df39cdcb63 Add a -hasChildren method to PXSourceListItem. 2014-01-08 16:38:07 +00:00
Alex Rozanski 5bc8d42069 Add an identifier parameter to the convenience methods which don't take a represented object. 2014-01-08 16:36:28 +00:00
Alex Rozanski 15a331f1a3 Add an #import for PXSourceListItem in PXSourceList.h. 2014-01-08 16:35:06 +00:00
Alex Rozanski 7038117d4c Add a generic Source List model object, PXSourceListItem. 2014-01-08 16:34:26 +00:00
Alex Rozanski 92733c9c60 Document -sourceList:objectValueForItem: and -sourceList:setObjectValue:forItem:. 2014-01-07 17:51:24 +00:00
Alex Rozanski 4cc050e804 Add a note to -sourceList:viewForItem: that it is a required method when using the Source List in view-based mode. 2014-01-07 17:45:59 +00:00
Alex Rozanski d2018e2104 Document -sourceList:viewForItem:. 2014-01-07 17:44:46 +00:00
Alex Rozanski dd6f403da0 Implement support for custom badge text and background colours.
This was erroneously removed when switching to the reusable badge cell implementation
for cell-based mode in PXSourceList.
2014-01-07 17:29:06 +00:00
Alex Rozanski a09cf6feee Document the badge-related methods of the data source. 2014-01-07 17:22:04 +00:00
Alex Rozanski 61a65ec3fe Return early from -drawRow:clipRect: if operating in view-based mode. 2014-01-07 17:04:57 +00:00
Alex Rozanski f66d5c23ab Implement #33: Add drag and drop example to the view-based example. 2014-01-07 15:04:00 +00:00
Alex Rozanski fa180a4830 Add new methods to SourceListItem for #33.
Specifically, implement -removeChildItems: and -insertChildItems:atIndexes:
which wrap the relevant methods on NSMutableArray. These will be used when
implementing drag and drop and we need to move child items around when the
drop is complete.
2014-01-07 14:51:22 +00:00
Alex Rozanski 263a155f9c Document the required methods of PXSourceListDataSource. 2014-01-07 08:58:53 +00:00
Alex Rozanski d6514ae3b6 Notification messages should come from the Source List, not the delegate/dataSource proxy. 2014-01-06 13:55:39 +00:00
Alex Rozanski a5e577d6df Move the delegate notification unregistration code to PXSourceListDelegateDataSourceProxy. 2014-01-06 13:54:42 +00:00
Alex Rozanski 1fbcb367d5 Don't explicitly use the delegate and dataSource properties of PXSourceListDelegateDataSourceProxy.
Since it's a proxy, we can send messages to the object directly, and they will be correctly
routed to the delegate or data source.
2014-01-06 13:52:44 +00:00
Alex Rozanski 4904d3fabf Fix implementation of -drawBadgeForRow:inRect:.
We should use the -badgeValue property of PXSourceListBadgeCell.
2014-01-06 13:47:14 +00:00
Alex Rozanski b2cda7e069 Update copyright headers in all the files. 2014-01-06 13:45:03 +00:00
Alex Rozanski d1a7324062 Remove unused comments in PXSourceList.h. 2014-01-06 13:36:48 +00:00
Alex Rozanski 7df5afa46f Provide basic documentation for methods and properties in PXSourceList.h. 2014-01-06 13:32:52 +00:00
Alex Rozanski 2349cebb57 Modify -itemHasBadge: and -badgeValueForItem: on PXSourceList.
We can only provide valid implementations when operating in cell-based mode,
as in view-based mode, the Source List's delegate and data source implementation
work with custom views and logic.
2014-01-06 13:31:01 +00:00
Alex Rozanski bc17e06958 Add the --no-repeat-first-par to the documentation script.
This prevents the first paragraph of each documented member being repeated below in the
longer description.
2014-01-06 12:56:15 +00:00
Alex Rozanski b09e6b3fbf Implement an example model object, PhotoCollection.
This is used to encapsulate information about collections used in the view-based
example project. It is an example of using a representedObject property on a
Source List item model to avoid having to synchronise model changes with the
Source List item model.
2014-01-06 12:24:59 +00:00
Alex Rozanski e1f2f3231b Implement -badgeValue and -setBadgeValue: on PXSourceListBadgeView.
These methods just call through to the underlying badge cell.
2014-01-06 12:24:06 +00:00
Alex Rozanski de73c4c316 Add a representedObject property to SourceListItem.
Also add a convenience initialiser method which takes a represented object and item.
2014-01-06 12:02:30 +00:00
Alex Rozanski 440c897fd0 Implement the remove button action method in the view-based example. 2014-01-06 11:56:10 +00:00
Alex Rozanski c7c2155f9b Allow the label for items not in the "Library" group to be edited. 2014-01-06 11:49:00 +00:00
Alex Rozanski 3adc4a731c Implement an -addButtonAction: method for the view-based example. 2014-01-06 11:48:26 +00:00
Alex Rozanski cc7239cb12 Improve SourceListItem API for adding/removing children.
Back the `children` property with a mutable array and implement -addChildItem:
and -removeChildItem: as convenience methods for adding and removing children.
2014-01-06 11:42:31 +00:00
Alex Rozanski 0e3d1678bb Add '+' and '-' buttons to the view-based Source List example. 2014-01-06 11:41:25 +00:00
Alex Rozanski a6dbac872e ...And remove Todo.rtf from the Xcode project. 2014-01-01 10:36:03 +00:00
Alex Rozanski 0f7f5097b6 Remove the Todo file.
The issues in that file are outdated or have been completed. GitHub Issues is
being used for issue tracking/feature ideas.
2014-01-01 10:35:11 +00:00
Alex Rozanski 190568635e Don't call -[PXSourceList frameOfCellAtColumn:row:] if we're view-based.
This doesn't make sense in view-based mode. It also causes a warning message to be
posted to the console by NSOutlineView.
2014-01-01 10:32:47 +00:00
Alex Rozanski edbd4e9274 Implement #32: Add an Appledoc target.
This makes use of an aggregate target which can be run to generate documentation
from the PXSourceList source.
2014-01-01 10:22:34 +00:00
Alex Rozanski 8c5e602aa1 Prevent floating of group rows in the Source List. 2013-12-31 18:02:11 +00:00
Alex Rozanski 69cfa2972d Add extra imports to PXSourceList.h for convenience. 2013-12-31 18:00:44 +00:00
Alex Rozanski d817b0b91e Implement basic view-based example. 2013-12-31 17:59:29 +00:00
Alex Rozanski 5b27e6b5b5 Add image resources for the view-based example. 2013-12-31 17:58:16 +00:00
Alex Rozanski 0f266df8d1 Implement PXSourceListTableCellView.
This is an NSTableCellView subclass which has an outlet for a badge view which
can be connected in a XIB file.
2013-12-31 15:35:25 +00:00
Alex Rozanski 6f3a2fffc3 Rename PXSourceListBadge to PXSourceListBadgeView.
Although it inherits from NSControl, this is a much better name for
this class.
2013-12-31 15:34:30 +00:00
Alex Rozanski da05d3b5c2 Fix #31: Handle the required NSOutlineViewDataSource methods in PXSourceListDelegateDataSourceProxy if not implemented by the PXSourceListDataSource.
The delegate/dataSource proxy is always marked as responding to these
methods required by NSOutlineView, which avoids the "Illegal NSOutlineView
Data Source" console messages which would be confusing otherwise.
In -forwardInvocation: if the Source List's dataSource doesn't implement
the corresponding PXSourceListDataSource methods, then we return early to
avoid an NSInvalidArgumentException.
2013-12-31 13:09:29 +00:00
Alex Rozanski 515ab9de9a Add support for calling PXSourceList(Delegate|DataSource) methods directly on PXSourceListDelegateDataSourceProxy. 2013-12-31 12:48:32 +00:00
Alex Rozanski 7cf81559cf Remove +outlineViewDelegateMethods and +outlineViewDataSourceMethods.
Lazily-loading these is unnecessary.
2013-12-31 12:36:18 +00:00
Alex Rozanski e42415e4ad Implement more sophisticated runtime forwarding.
This commit implements complex method forwarding for cases such as
-outlineView:shouldEditTableColumn:item: to -sourceList:shouldEditItem:
where the source and target selectors have different numbers of
arguments.
2013-12-31 12:27:54 +00:00
Alex Rozanski 6348e30331 Rename px_protocolArgumentTypesKey to make its meaning clearer. 2013-12-30 11:01:01 +00:00
Alex Rozanski 432d65ee41 Add an isRequired flag to each method dictionary returned from px_allProtocolMethods(). 2013-12-30 10:59:42 +00:00
Alex Rozanski 40df5b91ca Set up MainMenu.xib in the view-based example. 2013-12-28 12:40:23 +00:00
Alex Rozanski c864a9f73b Tidy up the example project directory structure. 2013-12-28 12:34:03 +00:00
Alex Rozanski 35c4c1cad1 Share SourceListItem.m between the two example projects. 2013-12-28 12:27:24 +00:00
Alex Rozanski b10fb51d87 Add a skeleton view-based Source List example project. 2013-12-28 12:22:54 +00:00
Alex Rozanski ce6d9a20ab Fix bundle identifiers for both targets. 2013-12-28 12:20:56 +00:00
Alex Rozanski 01f5b9cce2 Rename the existing demo project and link against the PXSourceList framework.
Since we're also going to have a view-based Source List example project,
rename the existing project to CellBasedSourceList. Also link with
PXSourceList.framework so we don't have to add sources to all projects
from now on.
2013-12-28 12:19:26 +00:00
Alex Rozanski ad62a11395 Include left and right padding in the badge's size. 2013-12-28 12:13:54 +00:00
Alex Rozanski 615cb07384 Make PXSourceListBadgeCell a text cell.
NSCell's implementation of -setIntegerValue: does nothing if the cell
is not a text cell.
2013-12-28 12:09:33 +00:00
Alex Rozanski 6792922263 Move px_allProtocolMethods() into a new file.
This function and associated string constants have been moved into
PXSourceListRuntimeAdditions.(h|m).
2013-12-25 13:13:19 +00:00
Alex Rozanski d009613055 Move notification forwarding code into PXSourceListDelegateDataSourceProxy. 2013-12-25 13:10:10 +00:00
Alex Rozanski 937251f515 Move method forwarding implementation to PXSourceListDelegateDataSourceProxy. 2013-12-25 13:04:25 +00:00
Alex Rozanski 4c21ec1595 Make use of the delegate-datasource proxy.
Remove secondaryDelegate and secondaryDataSource from PXSourceList
and read from/write to the delegate/dataSource property instead.
2013-12-25 13:04:12 +00:00
Alex Rozanski 688d8fea2d Implement bare PXSourceListDelegateDataSourceProxy class.
This class is used as the outline view's delegate and data source and
encapsulates all of the information which is used for forwarding outline
view delegate and data source messages to the Source List's delegate and
data source.
2013-12-25 13:04:10 +00:00
Alex Rozanski 5a5cda8845 Implement badge drawing on PXSourceListBadgeCell. 2013-11-15 20:26:33 +00:00
Alex Rozanski f7cb7fc6b7 Use PXSourceListBadgeCell for sizing and drawing. 2013-11-15 20:03:20 +00:00
Alex Rozanski 7479278133 Add PXSourceListBadge and PXSourceListBadgeCell.
These will be used to display badges in the source list.
2013-11-15 19:36:12 +00:00
Alex Rozanski a472f03345 Make PXSourceListDelegate on-par with NSOutlineViewDelegate.
Add missing delegate methods to PXSourceListDelegate which make sense
that are counterparts to those declared on NSOutlineViewDelegate.
2013-11-12 16:35:56 +00:00
Alex Rozanski 74fffe24a3 Add PXSourceListDelegate methods for view-based tables support. 2013-11-12 16:32:29 +00:00
Alex Rozanski dc194d3d2d Add PXSourceListDataSource method -sourceList:sortDescriptorsDidChange:.
This brings the API on-par with that of NSOutlineViewDataSource.
2013-11-12 16:29:57 +00:00
Alex Rozanski 7230b01b30 Make -sourceList:objectValueForItem: an optional method on PXSourceListDataSource.
This is to allow for view-based table support.
2013-11-12 16:27:03 +00:00
Alex Rozanski c5546bbf80 Modify when PXSourceList is the outline view's delegate and data source.
Since we can only forward messages meant for the source list delegate and
data source when we have a delegate and data source, this commit makes sure
that PXSourceList is only the outline view's delegate and data source when
it has a delegate and data source respectively.
2013-11-10 19:53:45 +00:00
Alex Rozanski b99342e507 Purge NSOutlineView's -respondsToSelector: cache when changing the delegate and data source.
When the source list delegate and data source changes we want NSOutlineView to remove any
cached data it has on what methods its delegate and data source respond to. Since PXSourceList
is always the outline view's delegate and data source, we need to call -setDelegate: and -setDataSource:
on super first with nil then back to the PXSourceList instance again.
2013-11-10 19:48:54 +00:00
Alex Rozanski 4e2b1eebfd Remove explicit method forwarding implementations.
This commit removes stubs of NSOutlineViewDelegate and
NSOutlineViewDataSource method from PXSourceList which simply forward
their implementation onto the source list delegate and data source.
2013-11-10 19:45:15 +00:00
Alex Rozanski b6bf83210a Override -forwardInvocation: on PXSourceList.
Uses the data in the method forwarding map to forward the invocation to
the correct object with the mapped selector.
2013-11-10 19:41:07 +00:00
Alex Rozanski 11535a40f5 Use custom method mappings when constructing the method forwarding map. 2013-11-10 19:35:51 +00:00
Alex Rozanski c81aad8b9c Implement +[PXSourceList customMethodNameMappings].
This is an explicit mapping between outline view delegate/data source method
names and source list delegate/data source method names for where the mapping
cannot be automatically generated.
2013-11-10 19:31:59 +00:00
Alex Rozanski d58c68efda Implement +[PXSourceList methodForwardingBlacklist].
This contains the names of methods which we don't want to create mappings
for in the method forwarding map.
2013-11-10 19:17:44 +00:00
Alex Rozanski e4ab518ee5 Blacklist certain outline view delegate and data source methods from being forwarded. 2013-11-10 19:05:19 +00:00
Alex Rozanski 38f30f6b3a Implement +[PXSourceList methodForwardingMap].
This abstracts the underlying dictionary and lazily instantiates it. This
commit also implements +addEntriesToMethodForwardingMap: which abstracts
away adding new mappings.
2013-11-10 18:53:28 +00:00
Alex Rozanski ec34ffb990 Override -respondsToSelector: on PXSourceList.
For the NSOutlineView delegate and data source methods that are in the
forwarding map, this implementation asks the source list delegate and
data source whether they implement the method.
2013-11-10 18:39:24 +00:00
Alex Rozanski 8e0429db2d Add the originating protocol to the forwarding map.
When we need to forward a message, we need to determine whether to send
it to the Source List's delegate or data source. This commit modifies the
forwarding map to add the originating protocol of the method in the map.
2013-11-10 18:36:59 +00:00
Alex Rozanski 9404856979 Create a static forwarding method map.
This will be used to determine which message to send to the Source List
delegate and data source when forwarding outline view delegate and data
source messages.
2013-11-10 18:06:47 +00:00
Alex Rozanski 206870e3d2 Implement +[PXSourceList methodNameMappingsForProtocol:]
This method gets all the method names from the given protocol and creates a dictionary
mapping from these methods to those which will be used by the Source List delegate and
data source. In particular, it replaces occurrences of "outlineView" with "sourceList" in
a case-insensitive way and matches the case of the "o" and the "v" in the replacement.
2013-11-10 18:01:23 +00:00
Alex Rozanski e0934f3fea Implement px_allProtocolMethods() function.
This function uses protocol_copyMethodDescriptionList() from the Objective-C
runtime to get information about all the methods from a particular protocol.
2013-11-10 16:56:53 +00:00
Alex Rozanski dad9689ff2 Remove forward-declarations of internal methods in PXSourceList. 2013-11-09 22:09:12 +00:00
Alex Rozanski 2b9aba5e9a Make -[PXSourceList numberOfGroups] a readonly property. 2013-11-09 22:07:52 +00:00
Alex Rozanski 1c70cce8c9 Replace layout and drawing macros with constants and inline functions. 2013-11-09 22:05:26 +00:00
Alex Rozanski f7ee7a9cec Remove >= 10.6 and >= 10.7 guarded code.
PXSourceList 2.0 will require the 10.7 SDK.
2013-11-09 21:14:22 +00:00
Alex Rozanski 2534a3e444 Make use of property auto-synthesis. 2013-11-09 21:12:43 +00:00
Alex Rozanski 300a993a15 Convert project to ARC. 2013-11-09 19:27:53 +00:00
Alex Rozanski c7e5c7930c Fix issue #27.
Check for MAC_OS_X_VERSION_10_7 for 10.7 drag-and drop code.
2013-06-13 18:58:23 +01:00
Alex Rozanski 632302a672 Actually use the correct anchor in the HIG link in the Readme. 2013-05-20 15:02:55 +02:00
Alex Rozanski 76b53614f3 Fix HIG link in Readme 2013-05-20 14:59:20 +02:00
Alex Rozanski 8ff0daee2e Fix for #25: iconRect.y calculated from iconSize.width not iconSize.height.
This commit fixes an issue where setting a non-square icon size for a
Source List could cause the icons to display incorrectly. This was
because the min-y value used when calculating icon rects was being based
on the width set for the Source List's icon size, rather than the height.
2012-11-02 10:14:03 +00:00
Alex Rozanski 0abec5e66e Merge pull request #24 from Abizern/master
10.7 added some extra methods to outline views to support drag and drop.
2012-09-12 07:41:06 -07:00
Abizer Nasir de1019eb30 Add 10.7 supported drag and drop methods 2012-09-12 15:10:12 +01:00
Christian Klotz 5c616dddbe Merge pull request #2 from Abizern/hidpi
Update project settings for HiDPI images
2012-07-05 14:38:49 -07:00
Abizer Nasir ac8accdbe5 Update project settings for HiDPI images
Recommended by Xcode 4.5.

Can be merged into main line once 4.4 is released with ML.
2012-07-05 15:58:19 +01:00
Alex Rozanski ac8f9db463 Merge pull request #21 from Abizern/master
Correct a cast
2012-06-28 13:18:34 -07:00
Christian Klotz 41a0ecfd9f Merge pull request #1 from Abizern/master
Minor correction to the changes in your last commit
2012-06-28 09:57:51 -07:00
Alex Rozanski 2181ea6ab6 Merge pull request #20 from christianklotz/master
PXSourceList works as an external library
2012-06-28 04:30:31 -07:00
Abizer Nasir 242a38a5eb Add the correct cast to long for formatting NSInteger with %ld 2012-06-28 11:07:18 +01:00
Christian Klotz 9ce4d06331 Resolve formatted string issue 2012-05-27 19:04:50 +01:00
Christian Klotz 127d4a2e2e Adjust library build settings to make it work as static library 2012-05-27 19:04:30 +01:00
Alex Rozanski 0124633b12 Reorganise the project and filesystem structure into more logical groupings 2012-01-08 11:20:11 +00:00
Alex Rozanski 310f7df19f Fix Lion display bugs 2012-01-08 11:01:21 +00:00
Alex Rozanski d0f3eb6259 Update gitignore file 2012-01-08 11:01:11 +00:00
Alex Rozanski 203a5c8b27 Update the README file to include more detailed license information (PXSourceList is still New BSD licensed) 2012-01-08 10:38:49 +00:00
Alex Rozanski ac660ba1f7 Merge pull request #18 from robelkin/DeleteKeySupport
Support the delete key found below 'fn' on a full size apple keyboard
2012-01-06 12:24:04 -08:00
Rob Elkin af5b8b9d35 Support the delete key found below 'fn' on a full size apple keyboard 2012-01-06 19:08:38 +00:00
Brian Chapados 0c87fe4400 Add -[SourceListItem description] as a debugging aid 2011-11-21 17:54:01 -08:00
Brian Chapados 51784809bf Declare iconSize property as (nonatomic, assign) to fix LLVM3 compiler warning 2011-11-18 10:57:32 -08:00
Alex Rozanski 079ae55635 Removed Gestalt for 10.6/10.5 compatibility and used -respondsToSelector: instead 2011-03-17 17:44:38 +00:00
Alex Rozanski 280837b6c7 Updated icon drawing to make it 10.5-compatible 2011-03-17 10:57:00 +00:00
Alex Rozanski 8987876099 Remove -[NSImage setFlipped:] which is deprecated. Replace with -[NSImage drawInRect:fromRect:operation:fraction:respectFlipped:hints:] 2011-02-01 07:25:37 +00:00
Alex Rozanski d5922b1689 Updated GC-compatible code contributed by @byteproject 2011-01-13 17:19:05 +00:00
Alex Rozanski a9a0c60400 Made PXSourceList GC-compatible (thanks to @byteproject) 2011-01-11 15:52:01 +00:00
Alex Rozanski 45d0a1b884 Updated min SDK version to be 10.5 in the build settings 2011-01-11 15:38:58 +00:00
Alex Rozanski ce456c4bdd Updated gitignore 2011-01-11 15:32:53 +00:00
wagerlabs 016be05f1b Changed install path to relative to the executable and made framework GC-capable 2011-01-11 13:24:10 +00:00
wagerlabs 0f9d67e2fc Moved PXSourceList into a framework of its own 2011-01-11 12:42:20 +00:00
Alex Rozanski a435282302 Updated link to Source List docs on Apple's developer site 2010-12-07 16:06:57 +00:00
Simon Wolf 1559687bb6 NSCell subclass sizes are returned properly in frameOfCellAtColumn:row: 2010-11-18 14:05:40 +00:00
Alex Rozanski a9727a34f1 Fixed bug in registering delegate for the PXSLItemDidCollapseNotification notification 2010-05-11 13:47:21 +01:00
Alex Rozanski fb6716633e Fixed issue causing early truncation of cells when the Source List is resized 2010-03-31 14:23:47 +01:00
Alex Rozanski 2fcb584ed5 Missed a download link in the README that needed changing 2010-03-24 20:47:43 +00:00
Alex Rozanski 349b39e1d7 Updated README to link to the new downloads page for documentation 2010-03-24 20:45:59 +00:00
73 changed files with 6221 additions and 5226 deletions
+18 -3
View File
@@ -1,7 +1,22 @@
*.DS_Store
# We can always generate the documentation from source.
docs/
# Mac OS X
.DS_Store
# From http://github.com/github/gitignore
# Xcode
build/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
/Build
/build
!default.perspectivev3
*.xcworkspace
!default.xcworkspace
xcuserdata
profile
*.moved-aside
-24
View File
@@ -1,24 +0,0 @@
//
// AppDelegate.h
// PXSourceList
//
// Created by Alex Rozanski on 08/01/2010.
// Copyright 2010 Alex Rozanski http://perspx.com
//
#import <Cocoa/Cocoa.h>
#import "PXSourceList.h"
#ifndef MAC_OS_X_VERSION_10_6
@protocol NSApplicationDelegate <NSObject> @end
#endif
@interface AppDelegate : NSObject <NSApplicationDelegate, PXSourceListDataSource, PXSourceListDelegate> {
IBOutlet PXSourceList *sourceList;
IBOutlet NSTextField *selectedItemLabel;
NSMutableArray *sourceListItems;
}
@end
-40
View File
@@ -1,40 +0,0 @@
//
// PXSourceList.h
// PXSourceList
//
// Created by Alex Rozanski on 05/09/2009.
// Copyright 2009-10 Alex Rozanski http://perspx.com
//
#import <Cocoa/Cocoa.h>
#import "PXSourceListDelegate.h"
#import "PXSourceListDataSource.h"
#ifndef MAC_OS_X_VERSION_10_6
@protocol NSOutlineViewDelegate <NSObject> @end
@protocol NSOutlineViewDataSource <NSObject> @end
#endif
@interface PXSourceList: NSOutlineView <NSOutlineViewDelegate, NSOutlineViewDataSource>
{
id <PXSourceListDelegate> _secondaryDelegate; //Used to store the publicly visible delegate
id <PXSourceListDataSource> _secondaryDataSource; //Used to store the publicly visible data source
NSSize _iconSize; //The size of icons in the Source List. Defaults to 16x16
}
@property NSSize iconSize;
@property (assign) id<PXSourceListDataSource> dataSource;
@property (assign) id<PXSourceListDelegate> delegate;
- (NSUInteger)numberOfGroups; //Returns the number of groups in the Source List
- (BOOL)isGroupItem:(id)item; //Returns whether `item` is a group
- (BOOL)isGroupAlwaysExpanded:(id)group; //Returns whether `group` is displayed as always expanded
- (BOOL)itemHasBadge:(id)item; //Returns whether `item` has a badge
- (NSInteger)badgeValueForItem:(id)item; //Returns the badge value for `item`
@end
-873
View File
@@ -1,873 +0,0 @@
//
// PXSourceList.m
// PXSourceList
//
// Created by Alex Rozanski on 05/09/2009.
// Copyright 2009-10 Alex Rozanski http://perspx.com
//
#import "PXSourceList.h"
//Layout constants
#define MIN_BADGE_WIDTH 22.0 //The minimum badge width for each item (default 22.0)
#define BADGE_HEIGHT 14.0 //The badge height for each item (default 14.0)
#define BADGE_MARGIN 5.0 //The spacing between the badge and the cell for that row
#define ROW_RIGHT_MARGIN 5.0 //The spacing between the right edge of the badge and the edge of the table column
#define ICON_SPACING 2.0 //The spacing between the icon and it's adjacent cell
#define DISCLOSURE_TRIANGLE_SPACE 18.0 //The indentation reserved for disclosure triangles for non-group items
//Drawing constants
#define BADGE_BACKGROUND_COLOR [NSColor colorWithCalibratedRed:(152/255.0) green:(168/255.0) blue:(202/255.0) alpha:1]
#define BADGE_HIDDEN_BACKGROUND_COLOR [NSColor colorWithDeviceWhite:(180/255.0) alpha:1]
#define BADGE_SELECTED_TEXT_COLOR [NSColor keyboardFocusIndicatorColor]
#define BADGE_SELECTED_UNFOCUSED_TEXT_COLOR [NSColor colorWithCalibratedRed:(153/255.0) green:(169/255.0) blue:(203/255.0) alpha:1]
#define BADGE_SELECTED_HIDDEN_TEXT_COLOR [NSColor colorWithCalibratedWhite:(170/255.0) alpha:1]
#define BADGE_FONT [NSFont boldSystemFontOfSize:11]
//Delegate notification constants
NSString * const PXSLSelectionIsChangingNotification = @"PXSourceListSelectionIsChanging";
NSString * const PXSLSelectionDidChangeNotification = @"PXSourceListSelectionDidChange";
NSString * const PXSLItemWillExpandNotification = @"PXSourceListItemWillExpand";
NSString * const PXSLItemDidExpandNotification = @"PXSourceListItemDidExpand";
NSString * const PXSLItemWillCollapseNotification = @"PXSourceListItemWillCollapse";
NSString * const PXSLItemDidCollapseNotification = @"PXSourceListItemDidCollapse";
NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKeyPressedOnRows";
#pragma mark -
@interface PXSourceList ()
- (NSSize)sizeOfBadgeAtRow:(NSInteger)rowIndex;
- (void)drawBadgeForRow:(NSInteger)rowIndex inRect:(NSRect)badgeFrame;
- (void)registerDelegateToReceiveNotification:(NSString*)notification withSelector:(SEL)selector;
@end
#pragma mark -
@implementation PXSourceList
@synthesize iconSize = _iconSize;
@dynamic dataSource;
@dynamic delegate;
#pragma mark Init/Dealloc
- (id)initWithCoder:(NSCoder*)decoder
{
if(self=[super initWithCoder:decoder])
{
[self setDelegate:(id<PXSourceListDelegate>)[super delegate]];
[super setDelegate:self];
[self setDataSource:(id<PXSourceListDataSource>)[super dataSource]];
[super setDataSource:self];
_iconSize = NSMakeSize(16,16);
}
return self;
}
- (void)dealloc
{
//Remove ourselves as the delegate and data source to be safe
[super setDataSource:nil];
[super setDelegate:nil];
//Unregister the delegate from receiving notifications
[[NSNotificationCenter defaultCenter] removeObserver:_secondaryDelegate name:nil object:self];
[super dealloc];
}
#pragma mark -
#pragma mark Custom Accessors
- (void)setDelegate:(id<PXSourceListDelegate>)aDelegate
{
//Unregister the old delegate from receiving notifications
[[NSNotificationCenter defaultCenter] removeObserver:_secondaryDelegate name:nil object:self];
_secondaryDelegate = aDelegate;
//Register the new delegate to receive notifications
[self registerDelegateToReceiveNotification:PXSLSelectionIsChangingNotification
withSelector:@selector(sourceListSelectionIsChanging:)];
[self registerDelegateToReceiveNotification:PXSLSelectionDidChangeNotification
withSelector:@selector(sourceListSelectionDidChange:)];
[self registerDelegateToReceiveNotification:PXSLItemWillExpandNotification
withSelector:@selector(sourceListItemWillExpand:)];
[self registerDelegateToReceiveNotification:PXSLItemDidExpandNotification
withSelector:@selector(sourceListItemDidExpand:)];
[self registerDelegateToReceiveNotification:PXSLItemWillCollapseNotification
withSelector:@selector(sourceListItemWillCollapse:)];
[self registerDelegateToReceiveNotification:PXSLItemDidCollapseNotification
withSelector:@selector(sourceListItemWillCollapse:)];
[self registerDelegateToReceiveNotification:PXSLDeleteKeyPressedOnRowsNotification
withSelector:@selector(sourceListDeleteKeyPressedOnRows:)];
}
- (void)setDataSource:(id<PXSourceListDataSource>)aDataSource
{
_secondaryDataSource = aDataSource;
[self reloadData];
}
- (void)setIconSize:(NSSize)newIconSize
{
_iconSize = newIconSize;
CGFloat rowHeight = [self rowHeight];
//Make sure icon height does not exceed row height; if so constrain, keeping width and height in proportion
if(_iconSize.height>rowHeight)
{
_iconSize.width = _iconSize.width * (rowHeight/_iconSize.height);
_iconSize.height = rowHeight;
}
}
#pragma mark -
#pragma mark Data Management
- (void)reloadData
{
[super reloadData];
//Expand items that are displayed as always expanded
if([_secondaryDataSource conformsToProtocol:@protocol(PXSourceListDataSource)] &&
[_secondaryDelegate respondsToSelector:@selector(sourceList:isGroupAlwaysExpanded:)])
{
for(NSUInteger i=0;i<[self numberOfGroups];i++)
{
id item = [_secondaryDataSource sourceList:self child:i ofItem:nil];
if([self isGroupAlwaysExpanded:item]) {
[self expandItem:item expandChildren:NO];
}
}
}
//If there are selected rows and the item hierarchy has changed, make sure a Group row isn't
//selected
if([self numberOfSelectedRows]>0) {
NSIndexSet *selectedIndexes = [self selectedRowIndexes];
NSUInteger firstSelectedRow = [selectedIndexes firstIndex];
//Is a group item selected?
if([self isGroupItem:[self itemAtRow:firstSelectedRow]]) {
//Work backwards to find the first non-group row
BOOL foundRow = NO;
for(NSUInteger i=firstSelectedRow;i>0;i--)
{
if(![self isGroupItem:[self itemAtRow:i]]) {
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:i] byExtendingSelection:NO];
foundRow = YES;
break;
}
}
//If there is no non-group row preceding the currently selected group item, remove the selection
//from the Source List
if(!foundRow) {
[self deselectAll:self];
}
}
}
else if(![self allowsEmptySelection]&&[self numberOfSelectedRows]==0)
{
//Select the first non-group row if no rows are selected, and empty selection is disallowed
for(NSUInteger i=0;i<[self numberOfRows];i++)
{
if(![self isGroupItem:[self itemAtRow:i]]) {
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:i] byExtendingSelection:NO];
break;
}
}
}
}
- (NSUInteger)numberOfGroups
{
if([_secondaryDataSource respondsToSelector:@selector(sourceList:numberOfChildrenOfItem:)]) {
return [_secondaryDataSource sourceList:self numberOfChildrenOfItem:nil];
}
return 0;
}
- (BOOL)isGroupItem:(id)item
{
//Groups are defined as root items (at level 0)
return 0==[self levelForItem:item];
}
- (BOOL)isGroupAlwaysExpanded:(id)group
{
//Make sure that the item IS a group to prevent unwanted queries sent to the data source
if([self isGroupItem:group]) {
//Query the data source
if([_secondaryDelegate respondsToSelector:@selector(sourceList:isGroupAlwaysExpanded:)]) {
return [_secondaryDelegate sourceList:self isGroupAlwaysExpanded:group];
}
}
return NO;
}
- (BOOL)itemHasBadge:(id)item
{
if([_secondaryDataSource respondsToSelector:@selector(sourceList:itemHasBadge:)]) {
return [_secondaryDataSource sourceList:self itemHasBadge:item];
}
return NO;
}
- (NSInteger)badgeValueForItem:(id)item
{
//Make sure that the item has a badge
if(![self itemHasBadge:item]) {
return NSNotFound;
}
if([_secondaryDataSource respondsToSelector:@selector(sourceList:badgeValueForItem:)]) {
return [_secondaryDataSource sourceList:self badgeValueForItem:item];
}
return NSNotFound;
}
#pragma mark -
#pragma mark Selection Handling
- (void)selectRowIndexes:(NSIndexSet*)indexes byExtendingSelection:(BOOL)extend
{
NSUInteger numberOfIndexes = [indexes count];
//Prevent empty selection if we don't want it
if(![self allowsEmptySelection]&&0==numberOfIndexes) {
return;
}
//Would use blocks but we're also targeting 10.5...
//Get the selected indexes
NSUInteger *selectedIndexes = malloc(sizeof(NSUInteger)*numberOfIndexes);
[indexes getIndexes:selectedIndexes maxCount:numberOfIndexes inIndexRange:nil];
//Loop through the indexes and only add non-group row indexes
//Allows selection across groups without selecting the group rows
NSMutableIndexSet *newSelectionIndexes = [NSMutableIndexSet indexSet];
for(NSInteger i=0;i<numberOfIndexes;i++)
{
if(![self isGroupItem:[self itemAtRow:selectedIndexes[i]]]) {
[newSelectionIndexes addIndex:selectedIndexes[i]];
}
}
//If there are any non-group rows selected
if([newSelectionIndexes count]>0) {
[super selectRowIndexes:newSelectionIndexes byExtendingSelection:extend];
}
//C memory management... *sigh*
free(selectedIndexes);
}
#pragma mark -
#pragma mark Layout
- (NSRect)frameOfOutlineCellAtRow:(NSInteger)row
{
//Return a zero-rect if the item is always expanded (a disclosure triangle will not be drawn)
if([self isGroupAlwaysExpanded:[self itemAtRow:row]]) {
return NSZeroRect;
}
return [super frameOfOutlineCellAtRow:row];
}
- (NSRect)frameOfCellAtColumn:(NSInteger)column row:(NSInteger)row
{
id item = [self itemAtRow:row];
NSCell *cell = [self preparedCellAtColumn:column row:row];
NSSize cellSize = [cell cellSize];
NSRect cellFrame = [super frameOfCellAtColumn:column row:row];
if([self isGroupItem:item])
{
NSRect rowRect = [self rectOfRow:row];
CGFloat minX = NSMinX(cellFrame);
//Set the origin x-coord; if there are no children of the group at current, there will still be a
//margin to the left of the cell (in cellFrame), which we don't want
if([self isGroupAlwaysExpanded:[self itemAtRow:row]]) {
minX = 7;
}
return NSMakeRect(minX,
NSMidY(cellFrame)-(cellSize.height/2.0),
NSWidth(rowRect)-minX,
cellSize.height);
}
else
{
CGFloat leftIndent = [self levelForRow:row]*[self indentationPerLevel]+DISCLOSURE_TRIANGLE_SPACE;
//Calculate space left for a badge if need be
CGFloat rightIndent = [self sizeOfBadgeAtRow:row].width+ROW_RIGHT_MARGIN;
//Allow space for an icon if need be
if(![self isGroupItem:item]&&[_secondaryDataSource respondsToSelector:@selector(sourceList:itemHasIcon:)])
{
if([_secondaryDataSource sourceList:self itemHasIcon:item]) {
leftIndent += [self iconSize].width+(ICON_SPACING*2);
}
}
return NSMakeRect(leftIndent,
NSMidY(cellFrame)-(cellSize.height/2.0),
NSWidth(cellFrame)-rightIndent-leftIndent,
cellSize.height);
}
}
//This method calculates and returns the size of the badge for the row index passed to the method. If the
//row for the row index passed to the method does not have a badge, then NSZeroSize is returned.
- (NSSize)sizeOfBadgeAtRow:(NSInteger)rowIndex
{
id rowItem = [self itemAtRow:rowIndex];
//Make sure that the item has a badge
if(![self itemHasBadge:rowItem]) {
return NSZeroSize;
}
NSAttributedString *badgeAttrString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%d", [self badgeValueForItem:rowItem]]
attributes:[NSDictionary dictionaryWithObjectsAndKeys:BADGE_FONT, NSFontAttributeName, nil]];
NSSize stringSize = [badgeAttrString size];
//Calculate the width needed to display the text or the minimum width if it's smaller
CGFloat width = stringSize.width+(2*BADGE_MARGIN);
if(width<MIN_BADGE_WIDTH) {
width = MIN_BADGE_WIDTH;
}
[badgeAttrString release];
return NSMakeSize(width, BADGE_HEIGHT);
}
#pragma mark -
#pragma mark Drawing
- (void)drawRow:(NSInteger)rowIndex clipRect:(NSRect)clipRect
{
[super drawRow:rowIndex clipRect:clipRect];
id item = [self itemAtRow:rowIndex];
//Draw an icon if the item has one
if(![self isGroupItem:item]&&[_secondaryDataSource respondsToSelector:@selector(sourceList:itemHasIcon:)])
{
if([_secondaryDataSource sourceList:self itemHasIcon:item])
{
NSRect cellFrame = [self frameOfCellAtColumn:0 row:rowIndex];
NSSize iconSize = [self iconSize];
NSRect iconRect = NSMakeRect(NSMinX(cellFrame)-iconSize.width-ICON_SPACING,
NSMidY(cellFrame)-(iconSize.width/2.0),
iconSize.width,
iconSize.height);
if([_secondaryDataSource respondsToSelector:@selector(sourceList:iconForItem:)])
{
NSImage *icon = [_secondaryDataSource sourceList:self iconForItem:item];
if(icon!=nil)
{
[icon setFlipped:[self isFlipped]];
NSSize actualIconSize = [icon size];
//If the icon is *smaller* than the size retrieved from the -iconSize property, make sure we
//reduce the size of the rectangle to draw the icon in, so that it is not stretched.
if((actualIconSize.width<iconSize.width)||(actualIconSize.height<iconSize.height))
{
iconRect = NSMakeRect(NSMidX(iconRect)-(actualIconSize.width/2.0),
NSMidY(iconRect)-(actualIconSize.height/2.0),
actualIconSize.width,
actualIconSize.height);
}
[icon drawInRect:iconRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1];
}
}
}
}
//Draw the badge if the item has one
if([self itemHasBadge:item])
{
NSRect rowRect = [self rectOfRow:rowIndex];
NSSize badgeSize = [self sizeOfBadgeAtRow:rowIndex];
NSRect badgeFrame = NSMakeRect(NSMaxX(rowRect)-badgeSize.width-ROW_RIGHT_MARGIN,
NSMidY(rowRect)-(badgeSize.height/2.0),
badgeSize.width,
badgeSize.height);
[self drawBadgeForRow:rowIndex inRect:badgeFrame];
}
}
- (void)drawBadgeForRow:(NSInteger)rowIndex inRect:(NSRect)badgeFrame
{
id rowItem = [self itemAtRow:rowIndex];
NSBezierPath *badgePath = [NSBezierPath bezierPathWithRoundedRect:badgeFrame
xRadius:(BADGE_HEIGHT/2.0)
yRadius:(BADGE_HEIGHT/2.0)];
//Get window and control state to determine colours used
BOOL isVisible = [[NSApp mainWindow] isVisible];
BOOL isFocused = [[[self window] firstResponder] isEqual:self];
NSInteger rowBeingEdited = [self editedRow];
//Set the attributes based on the row state
NSDictionary *attributes;
NSColor *backgroundColor;
if([[self selectedRowIndexes] containsIndex:rowIndex])
{
backgroundColor = [NSColor whiteColor];
//Set the text color based on window and control state
NSColor *textColor;
if(isVisible && (isFocused || rowBeingEdited==rowIndex)) {
textColor = BADGE_SELECTED_TEXT_COLOR;
}
else if(isVisible && !isFocused) {
textColor = BADGE_SELECTED_UNFOCUSED_TEXT_COLOR;
}
else {
textColor = BADGE_SELECTED_HIDDEN_TEXT_COLOR;
}
attributes = [[NSDictionary alloc] initWithObjectsAndKeys:BADGE_FONT, NSFontAttributeName,
textColor, NSForegroundColorAttributeName, nil];
}
else
{
//Set the text colour based on window and control state
NSColor *badgeColor = [NSColor whiteColor];
if(isVisible) {
//If the data source returns a custom colour..
if([_secondaryDataSource respondsToSelector:@selector(sourceList:badgeBackgroundColorForItem:)]) {
backgroundColor = [_secondaryDataSource sourceList:self badgeBackgroundColorForItem:rowItem];
if(backgroundColor==nil)
backgroundColor = BADGE_BACKGROUND_COLOR;
}
else { //Otherwise use the default (purple-blue colour)
backgroundColor = BADGE_BACKGROUND_COLOR;
}
//If the delegate wants a custom badge text colour..
if([_secondaryDataSource respondsToSelector:@selector(sourceList:badgeTextColorForItem:)]) {
badgeColor = [_secondaryDataSource sourceList:self badgeTextColorForItem:rowItem];
if(badgeColor==nil)
badgeColor = [NSColor whiteColor];
}
}
else { //Gray colour
backgroundColor = BADGE_HIDDEN_BACKGROUND_COLOR;
}
attributes = [[NSDictionary alloc] initWithObjectsAndKeys:BADGE_FONT, NSFontAttributeName,
badgeColor, NSForegroundColorAttributeName, nil];
}
[backgroundColor set];
[badgePath fill];
//Draw the badge text
NSAttributedString *badgeAttrString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%d", [self badgeValueForItem:rowItem]]
attributes:attributes];
NSSize stringSize = [badgeAttrString size];
NSPoint badgeTextPoint = NSMakePoint(NSMidX(badgeFrame)-(stringSize.width/2.0), //Center in the badge frame
NSMidY(badgeFrame)-(stringSize.height/2.0)); //Center in the badge frame
[badgeAttrString drawAtPoint:badgeTextPoint];
[attributes release];
[badgeAttrString release];
}
#pragma mark -
#pragma mark Keyboard Handling
- (void)keyDown:(NSEvent *)theEvent
{
NSIndexSet *selectedIndexes = [self selectedRowIndexes];
NSString *keyCharacters = [theEvent characters];
//Make sure we have a selection
if([selectedIndexes count]>0)
{
if([keyCharacters length]>0)
{
unichar firstKey = [keyCharacters characterAtIndex:0];
if(firstKey==NSUpArrowFunctionKey||firstKey==NSDownArrowFunctionKey)
{
//Handle keyboard navigation across groups
if([selectedIndexes count]==1&&!([theEvent modifierFlags] & NSShiftKeyMask))
{
int delta = firstKey==NSDownArrowFunctionKey?1:-1; //Search "backwards" if up arrow, "forwards" if down
NSInteger newRow = [selectedIndexes firstIndex];
//Keep incrementing/decrementing the row until a non-header row is reached
do {
newRow+=delta;
//If out of bounds of the number of rows..
if(newRow<0||newRow==[self numberOfRows])
break;
} while([self isGroupItem:[self itemAtRow:newRow]]);
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:newRow] byExtendingSelection:NO];
return;
}
}
else if(firstKey==NSDeleteCharacter||firstKey==NSBackspaceCharacter)
{
//Post the notification
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLDeleteKeyPressedOnRowsNotification
object:self
userInfo:[NSDictionary dictionaryWithObject:selectedIndexes forKey:@"rows"]];
return;
}
}
}
//We don't care about it
[super keyDown:theEvent];
}
#pragma mark -
#pragma mark Menu Handling
- (NSMenu *)menuForEvent:(NSEvent *)theEvent
{
NSMenu * m = nil;
if([_secondaryDelegate respondsToSelector:@selector(sourceList:menuForEvent:item:)]) {
NSPoint clickPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
NSInteger row = [self rowAtPoint:clickPoint];
id clickedItem = [self itemAtRow:row];
m = [_secondaryDelegate sourceList:self menuForEvent:theEvent item:clickedItem];
}
if (m == nil) {
m = [super menuForEvent:theEvent];
}
return m;
}
#pragma mark -
#pragma mark NSOutlineView Data Source methods
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
if([_secondaryDataSource conformsToProtocol:@protocol(PXSourceListDataSource)]) {
return [_secondaryDataSource sourceList:self numberOfChildrenOfItem:item];
}
return 0;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
if([_secondaryDataSource conformsToProtocol:@protocol(PXSourceListDataSource)]) {
return [_secondaryDataSource sourceList:self child:index ofItem:item];
}
return nil;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
if([_secondaryDataSource conformsToProtocol:@protocol(PXSourceListDataSource)]) {
return [_secondaryDataSource sourceList:self isItemExpandable:item];
}
return NO;
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
if([_secondaryDataSource conformsToProtocol:@protocol(PXSourceListDataSource)]) {
return [_secondaryDataSource sourceList:self objectValueForItem:item];
}
return nil;
}
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
if([_secondaryDataSource conformsToProtocol:@protocol(PXSourceListDataSource)]) {
[_secondaryDataSource sourceList:self setObjectValue:object forItem:item];
}
}
- (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object
{
if([_secondaryDataSource respondsToSelector:@selector(sourceList:itemForPersistentObject:)]) {
return [_secondaryDataSource sourceList:self itemForPersistentObject:object];
}
return nil;
}
- (id)outlineView:(NSOutlineView *)outlineView persistentObjectForItem:(id)item
{
if([_secondaryDataSource respondsToSelector:@selector(sourceList:persistentObjectForItem:)]) {
return [_secondaryDataSource sourceList:self persistentObjectForItem:item];
}
return nil;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pasteboard
{
if([_secondaryDataSource respondsToSelector:@selector(sourceList:writeItems:toPasteboard:)]) {
return [_secondaryDataSource sourceList:self writeItems:items toPasteboard:pasteboard];
}
return NO;
}
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
{
if([_secondaryDataSource respondsToSelector:@selector(sourceList:validateDrop:proposedItem:proposedChildIndex:)]) {
return [_secondaryDataSource sourceList:self validateDrop:info proposedItem:item proposedChildIndex:index];
}
return NSDragOperationNone;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
{
if([_secondaryDataSource respondsToSelector:@selector(sourceList:acceptDrop:item:childIndex:)]) {
return [_secondaryDataSource sourceList:self acceptDrop:info item:item childIndex:index];
}
return NO;
}
- (NSArray *)outlineView:(NSOutlineView *)outlineView namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination forDraggedItems:(NSArray *)items
{
if([_secondaryDataSource respondsToSelector:@selector(sourceList:namesOfPromisedFilesDroppedAtDestination:forDraggedItems:)]) {
return [_secondaryDataSource sourceList:self namesOfPromisedFilesDroppedAtDestination:dropDestination forDraggedItems:items];
}
return nil;
}
#pragma mark -
#pragma mark NSOutlineView Delegate methods
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
{
if([_secondaryDelegate respondsToSelector:@selector(sourceList:shouldExpandItem:)]) {
return [_secondaryDelegate sourceList:self shouldExpandItem:item];
}
return YES;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldCollapseItem:(id)item
{
//Make sure the item isn't displayed as always expanded
if([self isGroupItem:item])
{
if([self isGroupAlwaysExpanded:item]) {
return NO;
}
}
if([_secondaryDelegate respondsToSelector:@selector(sourceList:shouldCollapseItem:)]) {
return [_secondaryDelegate sourceList:self shouldCollapseItem:item];
}
return YES;
}
- (NSCell *)outlineView:(NSOutlineView *)outlineView dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
if([_secondaryDelegate respondsToSelector:@selector(sourceList:dataCellForItem:)]) {
return [_secondaryDelegate sourceList:self dataCellForItem:item];
}
NSInteger row = [self rowForItem:item];
//Return the default table column
return [[[self tableColumns] objectAtIndex:0] dataCellForRow:row];
}
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
if([_secondaryDelegate respondsToSelector:@selector(sourceList:willDisplayCell:forItem:)]) {
[_secondaryDelegate sourceList:self willDisplayCell:cell forItem:item];
}
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
{
//Make sure that the item isn't a group as they can't be selected
if(![self isGroupItem:item]) {
if([_secondaryDelegate respondsToSelector:@selector(sourceList:shouldSelectItem:)]) {
return [_secondaryDelegate sourceList:self shouldSelectItem:item];
}
}
else {
return NO;
}
return YES;
}
- (NSIndexSet *)outlineView:(NSOutlineView *)outlineView selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes
{
//The outline view will try to select the first row if -[allowsEmptySelection:] is set to NO if this is a group row
//stop it from doing so and leave it to our implementation of-[reloadData] which will select the first non-group row
//for us.
if([self numberOfSelectedRows]==0) {
if([self isGroupItem:[self itemAtRow:[proposedSelectionIndexes firstIndex]]]) {
return [NSIndexSet indexSet];
}
}
if([_secondaryDelegate respondsToSelector:@selector(sourceList:selectionIndexesForProposedSelection:)]) {
return [_secondaryDelegate sourceList:self selectionIndexesForProposedSelection:proposedSelectionIndexes];
}
//Since we implement this method, something must be returned to the outline view
return proposedSelectionIndexes;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
//Group titles can't be edited
if([self isGroupItem:item])
return NO;
if([_secondaryDelegate respondsToSelector:@selector(sourceList:shouldEditItem:)]) {
return [_secondaryDelegate sourceList:self shouldEditItem:item];
}
return YES;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldTrackCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
if([_secondaryDelegate respondsToSelector:@selector(sourceList:shouldTrackCell:forItem:)]) {
return [_secondaryDelegate sourceList:self shouldTrackCell:cell forItem:item];
}
return NO;
}
- (CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
{
if([_secondaryDelegate respondsToSelector:@selector(sourceList:heightOfRowByItem:)]) {
return [_secondaryDelegate sourceList:self heightOfRowByItem:item];
}
return [self rowHeight];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
{
return [self isGroupItem:item];
}
#pragma mark -
#pragma mark Notification handling
/* Notification wrappers */
- (void)outlineViewSelectionIsChanging:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLSelectionIsChangingNotification object:self];
}
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLSelectionDidChangeNotification object:self];
}
- (void)outlineViewItemWillExpand:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLItemWillExpandNotification
object:self
userInfo:[notification userInfo]];
}
- (void)outlineViewItemDidExpand:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLItemDidExpandNotification
object:self
userInfo:[notification userInfo]];
}
- (void)outlineViewItemWillCollapse:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLItemWillCollapseNotification
object:self
userInfo:[notification userInfo]];
}
- (void)outlineViewItemDidCollapse:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLItemDidCollapseNotification
object:self
userInfo:[notification userInfo]];
}
- (void)registerDelegateToReceiveNotification:(NSString*)notification withSelector:(SEL)selector
{
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
//Set the delegate as a receiver of the notification if it implements the notification method
if([_secondaryDelegate respondsToSelector:selector]) {
[defaultCenter addObserver:_secondaryDelegate
selector:selector
name:notification
object:self];
}
}
@end
-41
View File
@@ -1,41 +0,0 @@
//
// PXSourceListDataSource.h
// PXViewKit
//
// Created by Alex Rozanski on 17/10/2009.
// Copyright 2009-10 Alex Rozanski http://perspx.com
//
#import <Cocoa/Cocoa.h>
@class PXSourceList;
@protocol PXSourceListDataSource <NSObject>
@required
- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item;
- (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item;
- (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item;
- (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item;
@optional
- (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item;
- (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item;
- (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item;
- (NSColor*)sourceList:(PXSourceList*)aSourceList badgeTextColorForItem:(id)item;
- (NSColor*)sourceList:(PXSourceList*)aSourceList badgeBackgroundColorForItem:(id)item;
- (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item;
- (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item;
//The rest of these methods are basically "wrappers" for the NSOutlineViewDataSource methods
- (id)sourceList:(PXSourceList*)aSourceList itemForPersistentObject:(id)object;
- (id)sourceList:(PXSourceList*)aSourceList persistentObjectForItem:(id)item;
- (BOOL)sourceList:(PXSourceList*)aSourceList writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard;
- (NSDragOperation)sourceList:(PXSourceList*)sourceList validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index;
- (BOOL)sourceList:(PXSourceList*)aSourceList acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index;
- (NSArray *)sourceList:(PXSourceList*)aSourceList namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination forDraggedItems:(NSArray *)items;
@end
-63
View File
@@ -1,63 +0,0 @@
//
// PXSourceListDelegate.h
// PXViewKit
//
// Created by Alex Rozanski on 17/10/2009.
// Copyright 2009-10 Alex Rozanski http://perspx.com
//
#import <Cocoa/Cocoa.h>
@class PXSourceList;
@protocol PXSourceListDelegate <NSObject>
@optional
//Extra methods
- (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group;
- (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item;
//Basically NSOutlineViewDelegate wrapper methods
- (BOOL)sourceList:(PXSourceList*)aSourceList shouldSelectItem:(id)item;
- (NSIndexSet*)sourceList:(PXSourceList*)aSourceList selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes;
- (BOOL)sourceList:(PXSourceList*)aSourceList shouldEditItem:(id)item;
- (BOOL)sourceList:(PXSourceList*)aSourceList shouldTrackCell:(NSCell *)cell forItem:(id)item;
- (BOOL)sourceList:(PXSourceList*)aSourceList shouldExpandItem:(id)item;
- (BOOL)sourceList:(PXSourceList*)aSourceList shouldCollapseItem:(id)item;
- (CGFloat)sourceList:(PXSourceList*)aSourceList heightOfRowByItem:(id)item;
- (NSCell*)sourceList:(PXSourceList*)aSourceList willDisplayCell:(id)cell forItem:(id)item;
- (NSCell*)sourceList:(PXSourceList*)aSourceList dataCellForItem:(id)item;
@end
@interface NSObject (PXSourceListNotifications)
//Selection
- (void)sourceListSelectionIsChanging:(NSNotification *)notification;
- (void)sourceListSelectionDidChange:(NSNotification *)notification;
//Item expanding/collapsing
- (void)sourceListItemWillExpand:(NSNotification *)notification;
- (void)sourceListItemDidExpand:(NSNotification *)notification;
- (void)sourceListItemWillCollapse:(NSNotification *)notification;
- (void)sourceListItemDidCollapse:(NSNotification *)notification;
- (void)sourceListDeleteKeyPressedOnRows:(NSNotification *)notification;
@end
//PXSourceList delegate notifications
extern NSString * const PXSLSelectionIsChangingNotification;
extern NSString * const PXSLSelectionDidChangeNotification;
extern NSString * const PXSLItemWillExpandNotification;
extern NSString * const PXSLItemDidExpandNotification;
extern NSString * const PXSLItemWillCollapseNotification;
extern NSString * const PXSLItemDidCollapseNotification;
extern NSString * const PXSLDeleteKeyPressedOnRowsNotification;
-53
View File
@@ -1,53 +0,0 @@
//
// SourceListItem.h
// PXSourceList
//
// Created by Alex Rozanski on 08/01/2010.
// Copyright 2010 Alex Rozanski http://perspx.com
//
#import <Cocoa/Cocoa.h>
/*An example of a class that could be used to represent a Source List Item
Provides a title, an identifier, and an icon to be shown, as well as a badge value and a property to determine
whether the current item has a badge or not (`badgeValue` is set to -1 if no badge is shown)
Used to form a hierarchical model of SourceListItem instances similar to the Source List tree structure
and easily accessible by the data source with the "children" property
SourceListItem *parent
- SourceListItem *child1;
- SourceListItem *child2;
- SourceListItem *childOfChild2;
- SourceListItem *anotherChildOfChild2;
- SourceListItem *child3;
*/
@interface SourceListItem : NSObject {
NSString *title;
NSString *identifier;
NSImage *icon;
NSInteger badgeValue;
NSArray *children;
}
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *identifier;
@property (nonatomic, retain) NSImage *icon;
@property NSInteger badgeValue;
@property (nonatomic, copy) NSArray *children;
//Convenience methods
+ (id)itemWithTitle:(NSString*)aTitle identifier:(NSString*)anIdentifier;
+ (id)itemWithTitle:(NSString*)aTitle identifier:(NSString*)anIdentifier icon:(NSImage*)anIcon;
- (BOOL)hasBadge;
- (BOOL)hasChildren;
- (BOOL)hasIcon;
@end
-82
View File
@@ -1,82 +0,0 @@
//
// SourceListItem.m
// PXSourceList
//
// Created by Alex Rozanski on 08/01/2010.
// Copyright 2010 Alex Rozanski http://perspx.com
//
#import "SourceListItem.h"
@implementation SourceListItem
@synthesize title;
@synthesize identifier;
@synthesize icon;
@synthesize badgeValue;
@synthesize children;
#pragma mark -
#pragma mark Init/Dealloc
- (id)init
{
if(self=[super init])
{
badgeValue = -1; //We don't want a badge value by default
}
return self;
}
+ (id)itemWithTitle:(NSString*)aTitle identifier:(NSString*)anIdentifier
{
SourceListItem *item = [SourceListItem itemWithTitle:aTitle identifier:anIdentifier icon:nil];
return item;
}
+ (id)itemWithTitle:(NSString*)aTitle identifier:(NSString*)anIdentifier icon:(NSImage*)anIcon
{
SourceListItem *item = [[[SourceListItem alloc] init] autorelease];
[item setTitle:aTitle];
[item setIdentifier:anIdentifier];
[item setIcon:anIcon];
return item;
}
- (void)dealloc
{
[title release];
[identifier release];
[icon release];
[children release];
[super dealloc];
}
#pragma mark -
#pragma mark Custom Accessors
- (BOOL)hasBadge
{
return badgeValue!=-1;
}
- (BOOL)hasChildren
{
return [children count]>0;
}
- (BOOL)hasIcon
{
return icon!=nil;
}
@end
@@ -0,0 +1,16 @@
//
// AppDelegate.h
// PXSourceList
//
// Created by Alex Rozanski on 08/01/2010.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Cocoa/Cocoa.h>
#import "PXSourceList.h"
@interface AppDelegate : NSObject <NSApplicationDelegate, PXSourceListDataSource, PXSourceListDelegate>
@end
@@ -3,11 +3,20 @@
// PXSourceList
//
// Created by Alex Rozanski on 08/01/2010.
// Copyright 2010 Alex Rozanski http://perspx.com
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import "AppDelegate.h"
#import "SourceListItem.h"
@interface AppDelegate ()
@property (weak, nonatomic) IBOutlet PXSourceList *sourceList;
@property (weak, nonatomic) IBOutlet NSTextField *selectedItemLabel;
@property (strong, nonatomic) NSMutableArray *sourceListItems;
@end
@implementation AppDelegate
@@ -16,37 +25,37 @@
- (void)awakeFromNib
{
[selectedItemLabel setStringValue:@"(none)"];
[self.selectedItemLabel setStringValue:@"(none)"];
sourceListItems = [[NSMutableArray alloc] init];
self.sourceListItems = [[NSMutableArray alloc] init];
//Set up the "Library" parent item and children
SourceListItem *libraryItem = [SourceListItem itemWithTitle:@"LIBRARY" identifier:@"library"];
SourceListItem *musicItem = [SourceListItem itemWithTitle:@"Music" identifier:@"music"];
PXSourceListItem *libraryItem = [PXSourceListItem itemWithTitle:@"LIBRARY" identifier:@"library"];
PXSourceListItem *musicItem = [PXSourceListItem itemWithTitle:@"Music" identifier:@"music"];
[musicItem setIcon:[NSImage imageNamed:@"music.png"]];
SourceListItem *moviesItem = [SourceListItem itemWithTitle:@"Movies" identifier:@"movies"];
PXSourceListItem *moviesItem = [PXSourceListItem itemWithTitle:@"Movies" identifier:@"movies"];
[moviesItem setIcon:[NSImage imageNamed:@"movies.png"]];
SourceListItem *podcastsItem = [SourceListItem itemWithTitle:@"Podcasts" identifier:@"podcasts"];
PXSourceListItem *podcastsItem = [PXSourceListItem itemWithTitle:@"Podcasts" identifier:@"podcasts"];
[podcastsItem setIcon:[NSImage imageNamed:@"podcasts.png"]];
[podcastsItem setBadgeValue:10];
SourceListItem *audiobooksItem = [SourceListItem itemWithTitle:@"Audiobooks" identifier:@"audiobooks"];
[podcastsItem setBadgeValue:@10];
PXSourceListItem *audiobooksItem = [PXSourceListItem itemWithTitle:@"Audiobooks" identifier:@"audiobooks"];
[audiobooksItem setIcon:[NSImage imageNamed:@"audiobooks.png"]];
[libraryItem setChildren:[NSArray arrayWithObjects:musicItem, moviesItem, podcastsItem,
audiobooksItem, nil]];
//Set up the "Playlists" parent item and children
SourceListItem *playlistsItem = [SourceListItem itemWithTitle:@"PLAYLISTS" identifier:@"playlists"];
SourceListItem *playlist1Item = [SourceListItem itemWithTitle:@"Playlist1" identifier:@"playlist1"];
PXSourceListItem *playlistsItem = [PXSourceListItem itemWithTitle:@"PLAYLISTS" identifier:@"playlists"];
PXSourceListItem *playlist1Item = [PXSourceListItem itemWithTitle:@"Playlist1" identifier:@"playlist1"];
//Create a second-level group to demonstrate
SourceListItem *playlist2Item = [SourceListItem itemWithTitle:@"Playlist2" identifier:@"playlist2"];
SourceListItem *playlist3Item = [SourceListItem itemWithTitle:@"Playlist3" identifier:@"playlist3"];
PXSourceListItem *playlist2Item = [PXSourceListItem itemWithTitle:@"Playlist2" identifier:@"playlist2"];
PXSourceListItem *playlist3Item = [PXSourceListItem itemWithTitle:@"Playlist3" identifier:@"playlist3"];
[playlist1Item setIcon:[NSImage imageNamed:@"playlist.png"]];
[playlist2Item setIcon:[NSImage imageNamed:@"playlist.png"]];
[playlist3Item setIcon:[NSImage imageNamed:@"playlist.png"]];
SourceListItem *playlistGroup = [SourceListItem itemWithTitle:@"Playlist Group" identifier:@"playlistgroup"];
SourceListItem *playlistGroupItem = [SourceListItem itemWithTitle:@"Child Playlist" identifier:@"childplaylist"];
PXSourceListItem *playlistGroup = [PXSourceListItem itemWithTitle:@"Playlist Group" identifier:@"playlistgroup"];
PXSourceListItem *playlistGroupItem = [PXSourceListItem itemWithTitle:@"Child Playlist" identifier:@"childplaylist"];
[playlistGroup setIcon:[NSImage imageNamed:@"playlistFolder.png"]];
[playlistGroupItem setIcon:[NSImage imageNamed:@"playlist.png"]];
[playlistGroup setChildren:[NSArray arrayWithObject:playlistGroupItem]];
@@ -54,18 +63,12 @@
[playlistsItem setChildren:[NSArray arrayWithObjects:playlist1Item, playlistGroup,playlist2Item,
playlist3Item, nil]];
[sourceListItems addObject:libraryItem];
[sourceListItems addObject:playlistsItem];
[self.sourceListItems addObject:libraryItem];
[self.sourceListItems addObject:playlistsItem];
[sourceList reloadData];
[self.sourceList reloadData];
}
- (void)dealloc
{
[sourceListItems release];
[super dealloc];
}
#pragma mark -
#pragma mark Source List Data Source Methods
@@ -74,7 +77,7 @@
{
//Works the same way as the NSOutlineView data source: `nil` means a parent item
if(item==nil) {
return [sourceListItems count];
return [self.sourceListItems count];
}
else {
return [[item children] count];
@@ -86,7 +89,7 @@
{
//Works the same way as the NSOutlineView data source: `nil` means a parent item
if(item==nil) {
return [sourceListItems objectAtIndex:index];
return [self.sourceListItems objectAtIndex:index];
}
else {
return [[item children] objectAtIndex:index];
@@ -114,19 +117,19 @@
- (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
{
return [item hasBadge];
return !![(PXSourceListItem *)item badgeValue];
}
- (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
{
return [item badgeValue];
return [(PXSourceListItem *)item badgeValue].integerValue;
}
- (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
{
return [item hasIcon];
return !![item icon];
}
@@ -144,7 +147,7 @@
} else {
[m addItemWithTitle:@"clicked outside" action:nil keyEquivalent:@""];
}
return [m autorelease];
return m;
}
return nil;
}
@@ -163,18 +166,18 @@
- (void)sourceListSelectionDidChange:(NSNotification *)notification
{
NSIndexSet *selectedIndexes = [sourceList selectedRowIndexes];
NSIndexSet *selectedIndexes = [self.sourceList selectedRowIndexes];
//Set the label text to represent the new selection
if([selectedIndexes count]>1)
[selectedItemLabel setStringValue:@"(multiple)"];
[self.selectedItemLabel setStringValue:@"(multiple)"];
else if([selectedIndexes count]==1) {
NSString *identifier = [[sourceList itemAtRow:[selectedIndexes firstIndex]] identifier];
NSString *identifier = [[self.sourceList itemAtRow:[selectedIndexes firstIndex]] identifier];
[selectedItemLabel setStringValue:identifier];
[self.selectedItemLabel setStringValue:identifier];
}
else {
[selectedItemLabel setStringValue:@"(none)"];
[self.selectedItemLabel setStringValue:@"(none)"];
}
}
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.alexrozanski.CellBased.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

Before

Width:  |  Height:  |  Size: 415 B

After

Width:  |  Height:  |  Size: 415 B

Before

Width:  |  Height:  |  Size: 654 B

After

Width:  |  Height:  |  Size: 654 B

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 721 B

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

Before

Width:  |  Height:  |  Size: 511 B

After

Width:  |  Height:  |  Size: 511 B

Before

Width:  |  Height:  |  Size: 702 B

After

Width:  |  Height:  |  Size: 702 B

+688
View File
@@ -0,0 +1,688 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment version="1050" defaultVersion="1070" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customObject id="373" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="29">
<items>
<menuItem title="New Application" id="56">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="New Application" systemMenu="apple" id="57">
<items>
<menuItem title="About New Application" id="58">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="236"/>
<menuItem title="Preferences…" keyEquivalent="," id="129"/>
<menuItem isSeparatorItem="YES" id="143"/>
<menuItem title="Services" id="131">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="130"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="144"/>
<menuItem title="Hide New Application" keyEquivalent="h" id="134">
<connections>
<action selector="hide:" target="-1" id="369"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="145">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="370"/>
</connections>
</menuItem>
<menuItem title="Show All" id="150">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="372"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="149"/>
<menuItem title="Quit New Application" keyEquivalent="q" id="136">
<connections>
<action selector="terminate:" target="-2" id="448"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="83">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="81">
<items>
<menuItem title="New" keyEquivalent="n" id="82"/>
<menuItem title="Open…" keyEquivalent="o" id="72"/>
<menuItem title="Open Recent" id="124">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
<items>
<menuItem title="Clear Menu" id="126">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="127"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="79"/>
<menuItem title="Close" keyEquivalent="w" id="73">
<connections>
<action selector="performClose:" target="-1" id="193"/>
</connections>
</menuItem>
<menuItem title="Save" keyEquivalent="s" id="75">
<connections>
<action selector="saveDocument:" target="-1" id="362"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="80">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="saveDocumentAs:" target="-1" id="363"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="112">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="364"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="74"/>
<menuItem title="Page Setup..." keyEquivalent="P" id="77">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="87"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="78">
<connections>
<action selector="print:" target="-1" id="86"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="684">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="685">
<items>
<menuItem title="Undo" keyEquivalent="z" id="686">
<connections>
<action selector="undo:" target="-1" id="749"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="687">
<connections>
<action selector="redo:" target="-1" id="745"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="688"/>
<menuItem title="Cut" keyEquivalent="x" id="689">
<connections>
<action selector="cut:" target="-1" id="741"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="690">
<connections>
<action selector="copy:" target="-1" id="755"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="691">
<connections>
<action selector="paste:" target="-1" id="742"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="692">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="754"/>
</connections>
</menuItem>
<menuItem title="Delete" id="693">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="756"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="694">
<connections>
<action selector="selectAll:" target="-1" id="758"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="695"/>
<menuItem title="Find" id="696">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="734">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="735">
<connections>
<action selector="performFindPanelAction:" target="-1" id="772"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="736">
<connections>
<action selector="performFindPanelAction:" target="-1" id="775"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="737">
<connections>
<action selector="performFindPanelAction:" target="-1" id="771"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="738">
<connections>
<action selector="performFindPanelAction:" target="-1" id="773"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="739">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="774"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="697">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="727">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="728">
<connections>
<action selector="showGuessPanel:" target="-1" id="752"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="729">
<connections>
<action selector="checkSpelling:" target="-1" id="753"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="730"/>
<menuItem title="Check Spelling While Typing" id="731">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="747"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="732">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="750"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="733">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="698">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="718">
<items>
<menuItem title="Show Substitutions" id="719">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="720"/>
<menuItem title="Smart Copy/Paste" id="721">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="743"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="722">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="744"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="723">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Smart Links" id="724">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="762"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="725">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Text Replacement" id="726">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="699">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="714">
<items>
<menuItem title="Make Upper Case" id="715">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Make Lower Case" id="716">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Capitalize" id="717">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="700">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="711">
<items>
<menuItem title="Start Speaking" id="712">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="751"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="713">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="759"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="375">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="376">
<items>
<menuItem title="Font" id="377">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="388">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="389">
<connections>
<action selector="orderFrontFontPanel:" target="373" id="423"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="390">
<connections>
<action selector="addFontTrait:" target="373" id="420"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="391">
<connections>
<action selector="addFontTrait:" target="373" id="421"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="392">
<connections>
<action selector="underline:" target="-1" id="431"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="393"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394">
<connections>
<action selector="modifyFont:" target="373" id="424"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395">
<connections>
<action selector="modifyFont:" target="373" id="422"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="396"/>
<menuItem title="Kern" id="397">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="415">
<items>
<menuItem title="Use Default" id="416">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="437"/>
</connections>
</menuItem>
<menuItem title="Use None" id="417">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="440"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="418">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="430"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="419">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="434"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligature" id="398">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligature" id="411">
<items>
<menuItem title="Use Default" id="412">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="438"/>
</connections>
</menuItem>
<menuItem title="Use None" id="413">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="439"/>
</connections>
</menuItem>
<menuItem title="Use All" id="414">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="433"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="399">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="405">
<items>
<menuItem title="Use Default" id="406">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="436"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="407">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="429"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="408">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="428"/>
</connections>
</menuItem>
<menuItem title="Raise" id="409">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="425"/>
</connections>
</menuItem>
<menuItem title="Lower" id="410">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="426"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="400"/>
<menuItem title="Show Colors" keyEquivalent="C" id="401">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="432"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="402"/>
<menuItem title="Copy Style" keyEquivalent="c" id="403">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="427"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="404">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="435"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="776">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="777">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="778">
<connections>
<action selector="alignLeft:" target="-1" id="804"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="779">
<connections>
<action selector="alignCenter:" target="-1" id="800"/>
</connections>
</menuItem>
<menuItem title="Justify" id="780">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="802"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="781">
<connections>
<action selector="alignRight:" target="-1" id="803"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="782"/>
<menuItem title="Writing Direction" id="783">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="788">
<items>
<menuItem title="Paragraph" enabled="NO" id="789">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="790">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="791">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="792">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="793"/>
<menuItem title="Selection" enabled="NO" id="794">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="795">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="796">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="797">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="784"/>
<menuItem title="Show Ruler" id="785">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="798"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="786">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="801"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="787">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="799"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="295">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="296">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="297">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="366"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="298">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="19">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="24">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="23">
<connections>
<action selector="performMiniaturize:" target="-1" id="37"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="239">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="240"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="92"/>
<menuItem title="Bring All to Front" id="5">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="39"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="103">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="106">
<items>
<menuItem title="New Application Help" keyEquivalent="?" id="111">
<connections>
<action selector="showHelp:" target="-1" id="360"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="Cell-Based Source List Example" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" animationBehavior="default" id="367">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="53" y="204" width="853" height="570"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<view key="contentView" id="368">
<rect key="frame" x="0.0" y="0.0" width="853" height="570"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView autohidesScrollers="YES" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="811">
<rect key="frame" x="0.0" y="0.0" width="214" height="570"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" heightSizable="YES"/>
<clipView key="contentView" id="Ne8-cz-zNj">
<rect key="frame" x="1" y="1" width="212" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" autosaveColumns="NO" rowHeight="20" indentationPerLevel="14" outlineTableColumn="816" id="814" customClass="PXSourceList">
<rect key="frame" x="0.0" y="0.0" width="212" height="568"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="0.0"/>
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="209" minWidth="16" maxWidth="1000" id="816">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="819">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="820" id="824"/>
<outlet property="delegate" destination="820" id="823"/>
</connections>
</outlineView>
</subviews>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="813">
<rect key="frame" x="-100" y="-100" width="212" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="812">
<rect key="frame" x="-100" y="-100" width="15" height="539"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField verticalHuggingPriority="750" id="830">
<rect key="frame" x="361" y="277" width="195" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="The currently selected item is:" id="831">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" id="832">
<rect key="frame" x="558" y="277" width="252" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" id="833">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
</window>
<customObject id="820" customClass="AppDelegate">
<connections>
<outlet property="selectedItemLabel" destination="832" id="835"/>
<outlet property="sourceList" destination="814" id="825"/>
</connections>
</customObject>
</objects>
</document>
Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

@@ -0,0 +1,23 @@
//
// AppDelegate.h
// ViewBasedSourceList
//
// Created by Alex Rozanski on 28/12/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Cocoa/Cocoa.h>
#import "PXSourceList.h"
@interface AppDelegate : NSObject <NSApplicationDelegate, PXSourceListDataSource, PXSourceListDelegate>
@property (assign) IBOutlet NSWindow *window;
@property (assign) IBOutlet PXSourceList *sourceList;
@property (assign) IBOutlet NSButton *removeButton;
@property (weak, nonatomic) IBOutlet NSTextField *selectedItemLabel;
- (IBAction)addButtonAction:(id)sender;
- (IBAction)removeButtonAction:(id)sender;
@end
+300
View File
@@ -0,0 +1,300 @@
//
// AppDelegate.m
// ViewBasedSourceList
//
// Created by Alex Rozanski on 28/12/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import "AppDelegate.h"
#import "Photo.h"
#import "PhotoCollection.h"
@interface AppDelegate ()
@property (strong, nonatomic) NSMutableArray *modelObjects;
@property (strong, nonatomic) NSMutableArray *sourceListItems;
@end
static NSString * const draggingType = @"SourceListExampleDraggingType";
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Used to support drag and drop in the source list.
[self.sourceList registerForDraggedTypes:@[draggingType]];
self.sourceListItems = [[NSMutableArray alloc] init];
[self setUpDataModel];
[self.sourceList reloadData];
}
#pragma mark - Data Model
/* We could set an identifier on the PXSourceListItem instances, but it makes more sense to put our
identifying information on the underlying model object in this case.
We also add some dummy Photo objects to each collection to emulate how a real model class would work.
*/
- (void)setUpDataModel
{
PhotoCollection *photosCollection = [PhotoCollection collectionWithTitle:@"Photos" identifier:@"photos" type:PhotoCollectionTypeLibrary];
[self addNumberOfPhotoObjects:264 toCollection:photosCollection];
PhotoCollection *eventsCollection = [PhotoCollection collectionWithTitle:@"Events" identifier:@"events" type:PhotoCollectionTypeLibrary];
[self addNumberOfPhotoObjects:689 toCollection:eventsCollection];
PhotoCollection *peopleCollection = [PhotoCollection collectionWithTitle:@"People" identifier:@"people" type:PhotoCollectionTypeLibrary];
[self addNumberOfPhotoObjects:135 toCollection:peopleCollection];
PhotoCollection *placesCollection = [PhotoCollection collectionWithTitle:@"Places" identifier:@"places" type:PhotoCollectionTypeLibrary];
[self addNumberOfPhotoObjects:28 toCollection:placesCollection];
PhotoCollection *snapsCollection = [PhotoCollection collectionWithTitle:@"Holiday Snaps" identifier:nil type:PhotoCollectionTypeUserCreated];
[self addNumberOfPhotoObjects:40 toCollection:snapsCollection];
PhotoCollection *graduationCollection = [PhotoCollection collectionWithTitle:@"Graduation" identifier: nil type:PhotoCollectionTypeUserCreated];
[self addNumberOfPhotoObjects:1050 toCollection:graduationCollection];
// Store all of the model objects in an array because each source list item only holds a weak reference to them.
self.modelObjects = [@[photosCollection, eventsCollection, peopleCollection, placesCollection, snapsCollection, graduationCollection] mutableCopy];
// Icon images we're going to use in the Source List.
NSImage *photosImage = [NSImage imageNamed:@"photos"];
[photosImage setTemplate:YES];
NSImage *eventsImage = [NSImage imageNamed:@"events"];
[eventsImage setTemplate:YES];
NSImage *peopleImage = [NSImage imageNamed:@"people"];
[peopleImage setTemplate:YES];
NSImage *placesImage = [NSImage imageNamed:@"places"];
[placesImage setTemplate:YES];
NSImage *albumImage = [NSImage imageNamed:@"album"];
[albumImage setTemplate:YES];
// Set up our Source List data model used in the Source List data source methods.
PXSourceListItem *libraryItem = [PXSourceListItem itemWithTitle:@"LIBRARY" identifier:nil];
libraryItem.children = @[[PXSourceListItem itemWithRepresentedObject:photosCollection icon:photosImage],
[PXSourceListItem itemWithRepresentedObject:eventsCollection icon:eventsImage],
[PXSourceListItem itemWithRepresentedObject:peopleCollection icon:peopleImage],
[PXSourceListItem itemWithRepresentedObject:placesCollection icon:placesImage]];
PXSourceListItem *albumsItem = [PXSourceListItem itemWithTitle:@"ALBUMS" identifier:nil];
for (PhotoCollection *collection in @[snapsCollection, graduationCollection]) {
[albumsItem addChildItem:[PXSourceListItem itemWithRepresentedObject:collection icon:albumImage]];
}
[self.sourceListItems addObject:libraryItem];
[self.sourceListItems addObject:albumsItem];
}
/* Convenience method for adding dummy Photo objects to a PhotoCollection instance. */
- (void)addNumberOfPhotoObjects:(NSUInteger)numberOfObjects toCollection:(PhotoCollection *)collection
{
NSMutableArray *photos = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < numberOfObjects; ++i)
[photos addObject:[[Photo alloc] init]];
collection.photos = photos;
}
// Methods to avoid hardcoding subscripts into multiple places in the code.
- (PXSourceListItem *)libraryItem
{
return self.sourceListItems[0];
}
- (PXSourceListItem *)albumsItem
{
return self.sourceListItems[1];
}
#pragma mark - Actions
- (IBAction)addButtonAction:(id)sender
{
NSImage *albumImage = [NSImage imageNamed:@"album"];
[albumImage setTemplate:YES];
PhotoCollection *collection = [PhotoCollection collectionWithTitle:@"New Album" identifier:nil type:PhotoCollectionTypeUserCreated];
[self.modelObjects addObject:collection];
PXSourceListItem *newItem = [PXSourceListItem itemWithRepresentedObject:collection icon:albumImage];
[self.albumsItem addChildItem:newItem];
NSUInteger childIndex = [[self.albumsItem children] indexOfObject:newItem];
[self.sourceList insertItemsAtIndexes:[NSIndexSet indexSetWithIndex:childIndex]
inParent:self.albumsItem
withAnimation:NSTableViewAnimationEffectNone];
[self.sourceList editColumn:0 row:[self.sourceList rowForItem:newItem] withEvent:nil select:YES];
}
- (IBAction)removeButtonAction:(id)sender
{
PXSourceListItem *selectedItem = [self.sourceList itemAtRow:self.sourceList.selectedRow];
PXSourceListItem *parentItem = self.albumsItem;
[self.sourceList removeItemsAtIndexes:[NSIndexSet indexSetWithIndex:[parentItem.children indexOfObject:selectedItem]]
inParent:parentItem
withAnimation:NSTableViewAnimationSlideUp];
[self.modelObjects removeObject:selectedItem.representedObject];
// Only 'album' items can be deleted.
[parentItem removeChildItem:selectedItem];
}
#pragma mark - PXSourceList Data Source
- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
{
if (!item)
return self.sourceListItems.count;
return [[item children] count];
}
- (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
{
if (!item)
return self.sourceListItems[index];
return [[item children] objectAtIndex:index];
}
- (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
{
return [item hasChildren];
}
#pragma mark - PXSourceList Delegate
- (BOOL)sourceList:(PXSourceList *)aSourceList isGroupAlwaysExpanded:(id)group
{
return YES;
}
- (NSView *)sourceList:(PXSourceList *)aSourceList viewForItem:(id)item
{
PXSourceListTableCellView *cellView = nil;
if ([aSourceList levelForItem:item] == 0)
cellView = [aSourceList makeViewWithIdentifier:@"HeaderCell" owner:nil];
else
cellView = [aSourceList makeViewWithIdentifier:@"MainCell" owner:nil];
PXSourceListItem *sourceListItem = item;
PhotoCollection *collection = sourceListItem.representedObject;
// Only allow us to edit the user created photo collection titles.
BOOL isTitleEditable = [collection isKindOfClass:[PhotoCollection class]] && collection.type == PhotoCollectionTypeUserCreated;
cellView.textField.editable = isTitleEditable;
cellView.textField.selectable = isTitleEditable;
cellView.textField.stringValue = sourceListItem.title ? sourceListItem.title : [sourceListItem.representedObject title];
cellView.imageView.image = [item icon];
cellView.badgeView.hidden = collection.photos.count == 0;
cellView.badgeView.badgeValue = collection.photos.count;
return cellView;
}
- (void)sourceListSelectionDidChange:(NSNotification *)notification
{
PXSourceListItem *selectedItem = [self.sourceList itemAtRow:self.sourceList.selectedRow];
BOOL removeButtonEnabled = NO;
NSString *newLabel = @"";
if (selectedItem) {
// Only allow us to remove items in the 'albums' group.
removeButtonEnabled = [[self.albumsItem children] containsObject:selectedItem];
// We can use the underlying model object to do something based on the selection.
PhotoCollection *collection = selectedItem.representedObject;
if (collection.identifier)
newLabel = [NSString stringWithFormat:@"'%@' collection selected.", collection.identifier];
else
newLabel = @"User-created collection selected.";
}
self.selectedItemLabel.stringValue = newLabel;
self.removeButton.enabled = removeButtonEnabled;
}
#pragma mark - Drag and Drop
- (BOOL)sourceList:(PXSourceList*)aSourceList writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
{
// Only allow user-created items (not those in "Library" to be moved around).
for (PXSourceListItem *item in items) {
PhotoCollection *collection = item.representedObject;
if (![collection isKindOfClass:[PhotoCollection class]] || collection.type != PhotoCollectionTypeUserCreated)
return NO;
}
// We're dragging from and to the 'Albums' group.
PXSourceListItem *parentItem = self.albumsItem;
// For simplicity in this example, put the dragged indexes on the pasteboard. Since we use the representedObject
// on SourceListItem, we cannot reliably archive it directly.
NSMutableIndexSet *draggedChildIndexes = [NSMutableIndexSet indexSet];
for (PXSourceListItem *item in items)
[draggedChildIndexes addIndex:[[parentItem children] indexOfObject:item]];
[pboard declareTypes:@[draggingType] owner:self];
[pboard setData:[NSKeyedArchiver archivedDataWithRootObject:draggedChildIndexes] forType:draggingType];
return YES;
}
- (NSDragOperation)sourceList:(PXSourceList*)sourceList validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
{
PXSourceListItem *albumsItem = self.albumsItem;
// Only allow the items in the 'albums' group to be moved around. It can either be dropped on the group header, or inserted between other child items.
// It can't be made the child of another item in this group, so the only valid case is when the proposedItem is the 'Albums' group item.
if (![item isEqual:albumsItem])
return NSDragOperationNone;
return NSDragOperationMove;
}
- (BOOL)sourceList:(PXSourceList*)aSourceList acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
{
NSPasteboard *draggingPasteboard = info.draggingPasteboard;
NSMutableIndexSet *draggedChildIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:[draggingPasteboard dataForType:draggingType]];
PXSourceListItem *parentItem = self.albumsItem;
NSMutableArray *draggedItems = [NSMutableArray array];
[draggedChildIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
[draggedItems addObject:[[parentItem children] objectAtIndex:idx]];
}];
// An index of -1 means it's been dropped on the group header itself, so insert at the end of the group.
if (index == -1)
index = parentItem.children.count;
// Perform the Source List and model updates.
[aSourceList beginUpdates];
[aSourceList removeItemsAtIndexes:draggedChildIndexes
inParent:parentItem
withAnimation:NSTableViewAnimationEffectNone];
[parentItem removeChildItems:draggedItems];
// We have to calculate the new child index which we have to perform the drop at, since we've just removed items from the parent item which
// may have come before the drop index.
NSUInteger adjustedDropIndex = index - [draggedChildIndexes countOfIndexesInRange:NSMakeRange(0, index)];
// The insertion indexes are now simply from the adjusted drop index.
NSIndexSet *insertionIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(adjustedDropIndex, draggedChildIndexes.count)];
[parentItem insertChildItems:draggedItems atIndexes:insertionIndexes];
[aSourceList insertItemsAtIndexes:insertionIndexes
inParent:parentItem
withAnimation:NSTableViewAnimationEffectNone];
[aSourceList endUpdates];
return YES;
}
@end
@@ -0,0 +1,879 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9059" systemVersion="15B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9059"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="494" id="495"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<menu title="AMainMenu" systemMenu="main" id="29">
<items>
<menuItem title="ViewBasedSourceList" id="56">
<menu key="submenu" title="ViewBasedSourceList" systemMenu="apple" id="57">
<items>
<menuItem title="About ViewBasedSourceList" id="58">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="236">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Preferences…" keyEquivalent="," id="129"/>
<menuItem isSeparatorItem="YES" id="143">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Services" id="131">
<menu key="submenu" title="Services" systemMenu="services" id="130"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="144">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Hide ViewBasedSourceList" keyEquivalent="h" id="134">
<connections>
<action selector="hide:" target="-1" id="367"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="145">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="368"/>
</connections>
</menuItem>
<menuItem title="Show All" id="150">
<connections>
<action selector="unhideAllApplications:" target="-1" id="370"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="149">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Quit ViewBasedSourceList" keyEquivalent="q" id="136">
<connections>
<action selector="terminate:" target="-3" id="449"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="83">
<menu key="submenu" title="File" id="81">
<items>
<menuItem title="New" keyEquivalent="n" id="82">
<connections>
<action selector="newDocument:" target="-1" id="373"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="72">
<connections>
<action selector="openDocument:" target="-1" id="374"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="124">
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
<items>
<menuItem title="Clear Menu" id="126">
<connections>
<action selector="clearRecentDocuments:" target="-1" id="127"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="79">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Close" keyEquivalent="w" id="73">
<connections>
<action selector="performClose:" target="-1" id="193"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="75">
<connections>
<action selector="saveDocument:" target="-1" id="362"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="112">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="364"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="74">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Page Setup..." keyEquivalent="P" id="77">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="87"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="78">
<connections>
<action selector="print:" target="-1" id="86"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="217">
<menu key="submenu" title="Edit" id="205">
<items>
<menuItem title="Undo" keyEquivalent="z" id="207">
<connections>
<action selector="undo:" target="-1" id="223"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="215">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="redo:" target="-1" id="231"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="206">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Cut" keyEquivalent="x" id="199">
<connections>
<action selector="cut:" target="-1" id="228"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="197">
<connections>
<action selector="copy:" target="-1" id="224"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="203">
<connections>
<action selector="paste:" target="-1" id="226"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="485">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="486"/>
</connections>
</menuItem>
<menuItem title="Delete" id="202">
<connections>
<action selector="delete:" target="-1" id="235"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="198">
<connections>
<action selector="selectAll:" target="-1" id="232"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="214">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Find" id="218">
<menu key="submenu" title="Find" id="220">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
<connections>
<action selector="performFindPanelAction:" target="-1" id="241"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="534">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="535"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208">
<connections>
<action selector="performFindPanelAction:" target="-1" id="487"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="488"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221">
<connections>
<action selector="performFindPanelAction:" target="-1" id="489"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="210">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="216">
<menu key="submenu" title="Spelling and Grammar" id="200">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="204">
<connections>
<action selector="showGuessPanel:" target="-1" id="230"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="201">
<connections>
<action selector="checkSpelling:" target="-1" id="225"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="453"/>
<menuItem title="Check Spelling While Typing" id="219">
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="346">
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="347"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="454">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="456"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="348">
<menu key="submenu" title="Substitutions" id="349">
<items>
<menuItem title="Show Substitutions" id="457">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="458"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="459"/>
<menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="460">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="461"/>
</connections>
</menuItem>
<menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="462">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="463"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="450">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="451">
<items>
<menuItem title="Make Upper Case" id="452">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="464"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="465">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="468"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="466">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="467"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="211">
<menu key="submenu" title="Speech" id="212">
<items>
<menuItem title="Start Speaking" id="196">
<connections>
<action selector="startSpeaking:" target="-1" id="233"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="195">
<connections>
<action selector="stopSpeaking:" target="-1" id="227"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="375">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="376">
<items>
<menuItem title="Font" id="377">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="388">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="389">
<connections>
<action selector="orderFrontFontPanel:" target="420" id="424"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="390">
<connections>
<action selector="addFontTrait:" target="420" id="421"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="391">
<connections>
<action selector="addFontTrait:" target="420" id="422"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="392">
<connections>
<action selector="underline:" target="-1" id="432"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="393"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394">
<connections>
<action selector="modifyFont:" target="420" id="425"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395">
<connections>
<action selector="modifyFont:" target="420" id="423"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="396"/>
<menuItem title="Kern" id="397">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="415">
<items>
<menuItem title="Use Default" id="416">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="438"/>
</connections>
</menuItem>
<menuItem title="Use None" id="417">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="441"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="418">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="431"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="419">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="435"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="398">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="411">
<items>
<menuItem title="Use Default" id="412">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="439"/>
</connections>
</menuItem>
<menuItem title="Use None" id="413">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="440"/>
</connections>
</menuItem>
<menuItem title="Use All" id="414">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="434"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="399">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="405">
<items>
<menuItem title="Use Default" id="406">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="437"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="407">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="430"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="408">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="429"/>
</connections>
</menuItem>
<menuItem title="Raise" id="409">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="426"/>
</connections>
</menuItem>
<menuItem title="Lower" id="410">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="427"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="400"/>
<menuItem title="Show Colors" keyEquivalent="C" id="401">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="433"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="402"/>
<menuItem title="Copy Style" keyEquivalent="c" id="403">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="428"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="404">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="436"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="496">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="497">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="498">
<connections>
<action selector="alignLeft:" target="-1" id="524"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="499">
<connections>
<action selector="alignCenter:" target="-1" id="518"/>
</connections>
</menuItem>
<menuItem title="Justify" id="500">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="523"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="501">
<connections>
<action selector="alignRight:" target="-1" id="521"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="502"/>
<menuItem title="Writing Direction" id="503">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="508">
<items>
<menuItem title="Paragraph" enabled="NO" id="509">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="510">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="525"/>
</connections>
</menuItem>
<menuItem id="511">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="526"/>
</connections>
</menuItem>
<menuItem id="512">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="527"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="513"/>
<menuItem title="Selection" enabled="NO" id="514">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="515">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="528"/>
</connections>
</menuItem>
<menuItem id="516">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="529"/>
</connections>
</menuItem>
<menuItem id="517">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="530"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="504"/>
<menuItem title="Show Ruler" id="505">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="520"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="506">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="522"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="507">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="519"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="295">
<menu key="submenu" title="View" id="296">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="297">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="366"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="298">
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="19">
<menu key="submenu" title="Window" systemMenu="window" id="24">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="23">
<connections>
<action selector="performMiniaturize:" target="-1" id="37"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="239">
<connections>
<action selector="performZoom:" target="-1" id="240"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="92">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Bring All to Front" id="5">
<connections>
<action selector="arrangeInFront:" target="-1" id="39"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="490">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="491">
<items>
<menuItem title="ViewBasedSourceList Help" keyEquivalent="?" id="492">
<connections>
<action selector="showHelp:" target="-1" id="493"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="View-Based Source List Example" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="371">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="707" height="453"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<view key="contentView" id="372">
<rect key="frame" x="0.0" y="0.0" width="707" height="453"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<splitView dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="shf-dM-jNo">
<rect key="frame" x="0.0" y="0.0" width="707" height="453"/>
<subviews>
<customView id="5z6-z4-Eis">
<rect key="frame" x="0.0" y="0.0" width="192" height="453"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="26" horizontalPageScroll="10" verticalLineScroll="26" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gyf-tN-jug">
<rect key="frame" x="0.0" y="0.0" width="192" height="453"/>
<clipView key="contentView" drawsBackground="NO" id="MeO-Mg-PDX">
<rect key="frame" x="0.0" y="0.0" width="192" height="453"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" rowSizeStyle="systemDefault" viewBased="YES" indentationPerLevel="16" outlineTableColumn="KUf-Sx-qvM" id="6cp-sB-vmV" customClass="PXSourceList">
<rect key="frame" x="0.0" y="0.0" width="192" height="0.0"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="AutomaticTableColumnIdentifier.0" width="189" minWidth="16" maxWidth="1000" id="KUf-Sx-qvM">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.33333298560000002" alpha="1" colorSpace="calibratedWhite"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="III-Z6-nUC">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="HeaderCell" id="Bd4-M5-UeQ" customClass="PXSourceListTableCellView">
<rect key="frame" x="1" y="0.0" width="189" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eai-EG-dRe">
<rect key="frame" x="0.0" y="1" width="189" height="14"/>
<animations/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="HEADER CELL" id="5xY-Iv-ixB">
<font key="font" metaFont="smallSystemBold"/>
<color key="textColor" red="0.43921568630000002" green="0.4941176471" blue="0.54901960780000003" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<animations/>
<connections>
<outlet property="textField" destination="eai-EG-dRe" id="6Az-Vc-kqF"/>
</connections>
</tableCellView>
<tableCellView identifier="MainCell" id="hLb-dC-CwP" customClass="PXSourceListTableCellView">
<rect key="frame" x="1" y="17" width="189" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4ER-hQ-M6f">
<rect key="frame" x="3" y="0.0" width="17" height="17"/>
<animations/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSActionTemplate" id="VxW-Wd-dQJ"/>
</imageView>
<textField verticalHuggingPriority="750" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GsV-wD-pbj">
<rect key="frame" x="25" y="0.0" width="130" height="17"/>
<animations/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="aO4-oB-jj3">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<customView misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Wj-Kn-7YJ" customClass="PXSourceListBadgeView">
<rect key="frame" x="164" y="2" width="22" height="14"/>
<constraints>
<constraint firstAttribute="height" constant="14" id="Lwo-5Q-Zmq"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="22" id="iAz-E3-9nO"/>
</constraints>
<animations/>
</customView>
</subviews>
<constraints>
<constraint firstItem="3Wj-Kn-7YJ" firstAttribute="leading" secondItem="GsV-wD-pbj" secondAttribute="trailing" constant="11" id="1UW-HO-ZSa"/>
<constraint firstItem="4ER-hQ-M6f" firstAttribute="top" secondItem="hLb-dC-CwP" secondAttribute="top" id="3LF-HY-wfc"/>
<constraint firstAttribute="trailing" secondItem="3Wj-Kn-7YJ" secondAttribute="trailing" constant="3" id="4vE-ts-quP"/>
<constraint firstItem="GsV-wD-pbj" firstAttribute="leading" secondItem="4ER-hQ-M6f" secondAttribute="trailing" constant="7" id="M3J-to-AOr"/>
<constraint firstItem="4ER-hQ-M6f" firstAttribute="leading" secondItem="hLb-dC-CwP" secondAttribute="leading" constant="3" id="aCY-7s-IhT"/>
<constraint firstAttribute="centerY" secondItem="3Wj-Kn-7YJ" secondAttribute="centerY" id="vSs-Ne-WPB"/>
</constraints>
<animations/>
<connections>
<outlet property="badgeView" destination="3Wj-Kn-7YJ" id="Tcs-WA-hkr"/>
<outlet property="imageView" destination="4ER-hQ-M6f" id="le3-qL-W27"/>
<outlet property="textField" destination="GsV-wD-pbj" id="yrR-Qw-aD4"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="494" id="F2A-gV-k7c"/>
<outlet property="delegate" destination="494" id="G2w-lX-5Dl"/>
</connections>
</outlineView>
</subviews>
<animations/>
<nil key="backgroundColor"/>
</clipView>
<animations/>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="A8C-Fo-iI3">
<rect key="frame" x="1" y="-15" width="0.0" height="16"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="63c-xj-Ig0">
<rect key="frame" x="-15" y="1" width="16" height="0.0"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</scroller>
</scrollView>
<button translatesAutoresizingMaskIntoConstraints="NO" id="BEV-Wz-OdK">
<rect key="frame" x="1" y="2" width="30" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="30" id="BbI-az-zo0"/>
<constraint firstAttribute="height" constant="21" id="OSH-IM-U1Y"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="30" id="Se2-9v-bwa"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="21" id="ZeV-jh-DKY"/>
</constraints>
<animations/>
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSAddTemplate" imagePosition="overlaps" alignment="center" controlSize="mini" state="on" imageScaling="proportionallyDown" inset="2" id="ZO1-1M-bSI">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="miniSystem"/>
</buttonCell>
<connections>
<action selector="addButtonAction:" target="494" id="Rqx-I4-1lq"/>
</connections>
</button>
<button translatesAutoresizingMaskIntoConstraints="NO" id="iQU-Dr-qPa">
<rect key="frame" x="28" y="2" width="30" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="30" id="52p-Vm-2Nb"/>
<constraint firstAttribute="height" constant="21" id="ar8-y0-zCh"/>
</constraints>
<animations/>
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSRemoveTemplate" imagePosition="overlaps" alignment="center" controlSize="mini" enabled="NO" state="on" imageScaling="proportionallyDown" inset="2" id="xbS-Ax-KfO">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="miniSystem"/>
</buttonCell>
<connections>
<action selector="removeButtonAction:" target="494" id="F9K-WH-sD1"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="Gyf-tN-jug" firstAttribute="leading" secondItem="5z6-z4-Eis" secondAttribute="leading" id="8on-dj-sEF"/>
<constraint firstItem="BEV-Wz-OdK" firstAttribute="leading" secondItem="5z6-z4-Eis" secondAttribute="leading" constant="1" id="BRA-XD-2Sl"/>
<constraint firstItem="iQU-Dr-qPa" firstAttribute="leading" secondItem="5z6-z4-Eis" secondAttribute="leading" constant="28" id="Ie2-h9-MID"/>
<constraint firstAttribute="bottom" secondItem="Gyf-tN-jug" secondAttribute="bottom" id="OVF-uf-K7N"/>
<constraint firstAttribute="bottom" secondItem="BEV-Wz-OdK" secondAttribute="bottom" constant="2" id="VKt-iQ-iyA"/>
<constraint firstAttribute="bottom" secondItem="iQU-Dr-qPa" secondAttribute="bottom" constant="2" id="ZJR-zF-U9n"/>
<constraint firstItem="Gyf-tN-jug" firstAttribute="top" secondItem="5z6-z4-Eis" secondAttribute="top" id="e1F-no-bdo"/>
<constraint firstAttribute="trailing" secondItem="Gyf-tN-jug" secondAttribute="trailing" id="ipE-wL-Vti"/>
</constraints>
<animations/>
</customView>
<customView id="V5U-wh-2o3">
<rect key="frame" x="193" y="0.0" width="514" height="453"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rTW-wK-rrG">
<rect key="frame" x="18" y="212" width="478" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="474" id="7gl-Dw-sAL"/>
<constraint firstAttribute="height" constant="30" id="V8l-Fv-qd6"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="DeD-wn-D0N">
<font key="font" metaFont="system" size="25"/>
<color key="textColor" white="0.33724908759124089" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="centerY" secondItem="rTW-wK-rrG" secondAttribute="centerY" id="H9N-qr-6zT"/>
<constraint firstAttribute="centerX" secondItem="rTW-wK-rrG" secondAttribute="centerX" id="LAH-FJ-z3S"/>
</constraints>
<animations/>
</customView>
</subviews>
<animations/>
<holdingPriorities>
<real value="250"/>
<real value="250"/>
</holdingPriorities>
</splitView>
</subviews>
<constraints>
<constraint firstItem="shf-dM-jNo" firstAttribute="leading" secondItem="372" secondAttribute="leading" id="57g-qe-uA7"/>
<constraint firstAttribute="bottom" secondItem="shf-dM-jNo" secondAttribute="bottom" id="JzU-bz-Fxa"/>
<constraint firstAttribute="trailing" secondItem="shf-dM-jNo" secondAttribute="trailing" id="WD2-Bb-YK5"/>
<constraint firstItem="shf-dM-jNo" firstAttribute="top" secondItem="372" secondAttribute="top" id="zmB-td-zla"/>
</constraints>
<animations/>
</view>
</window>
<customObject id="494" customClass="AppDelegate">
<connections>
<outlet property="removeButton" destination="iQU-Dr-qPa" id="MfB-vx-DLY"/>
<outlet property="selectedItemLabel" destination="rTW-wK-rrG" id="GaD-A5-dDW"/>
<outlet property="sourceList" destination="6cp-sB-vmV" id="0Ec-MJ-aU2"/>
<outlet property="window" destination="371" id="532"/>
</connections>
</customObject>
<customObject id="420" customClass="NSFontManager"/>
</objects>
<resources>
<image name="NSActionTemplate" width="14" height="14"/>
<image name="NSAddTemplate" width="11" height="11"/>
<image name="NSRemoveTemplate" width="11" height="11"/>
</resources>
</document>
@@ -0,0 +1,58 @@
{
"images" : [
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
+14
View File
@@ -0,0 +1,14 @@
//
// Photo.h
// PXSourceList
//
// Created by Alex Rozanski on 27/01/2014.
//
//
#import <Foundation/Foundation.h>
/* A model class for illustration purposes that could be used for storing information about a photo. */
@interface Photo : NSObject
@end
+13
View File
@@ -0,0 +1,13 @@
//
// Photo.m
// PXSourceList
//
// Created by Alex Rozanski on 27/01/2014.
//
//
#import "Photo.h"
@implementation Photo
@end
@@ -0,0 +1,31 @@
//
// PhotoCollection.h
// PXSourceList
//
// Created by Alex Rozanski on 06/01/2014.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, PhotoCollectionType) {
PhotoCollectionTypeLibrary,
PhotoCollectionTypeUserCreated
};
/* A simple example of a model class which is used by this project for storing information
about a particular collection of objects in our sample library scenario. These objects
are used by the SourceListItems to populate the Source List's content without having to
synchronise the data (e.g. title) with each SourceListItem.
*/
@interface PhotoCollection : NSObject
@property (strong, nonatomic) NSString *title;
@property (strong, nonatomic) NSString *identifier;
@property (copy, nonatomic) NSArray *photos;
@property (assign, nonatomic) PhotoCollectionType type;
+ (id)collectionWithTitle:(NSString *)title identifier:(NSString *)identifier type:(PhotoCollectionType)type;
@end
@@ -0,0 +1,25 @@
//
// PhotoCollection.m
// PXSourceList
//
// Created by Alex Rozanski on 06/01/2014.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import "PhotoCollection.h"
@implementation PhotoCollection
+ (id)collectionWithTitle:(NSString *)title identifier:(NSString *)identifier type:(PhotoCollectionType)type
{
PhotoCollection *collection = [[PhotoCollection alloc] init];
collection.title = title;
collection.identifier = identifier;
collection.type = type;
return collection;
}
@end
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,17 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "18.png"
},
{
"idiom" : "universal",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,17 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "63.png"
},
{
"idiom" : "universal",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,17 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "14.png"
},
{
"idiom" : "universal",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,17 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "3.png"
},
{
"idiom" : "universal",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,17 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "20.png"
},
{
"idiom" : "universal",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.alexrozanski.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
@@ -0,0 +1,9 @@
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif
@@ -0,0 +1,29 @@
{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;}
{\colortbl;\red255\green255\blue255;}
\paperw9840\paperh8400
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f0\b\fs24 \cf0 Engineering:
\b0 \
Some people\
\
\b Human Interface Design:
\b0 \
Some other people\
\
\b Testing:
\b0 \
Hopefully not nobody\
\
\b Documentation:
\b0 \
Whoever\
\
\b With special thanks to:
\b0 \
Mom\
}
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */
+14
View File
@@ -0,0 +1,14 @@
//
// main.m
// ViewBasedSourceList
//
// Created by Alex Rozanski on 28/12/2013.
//
//
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[])
{
return NSApplicationMain(argc, argv);
}
+10
View File
@@ -0,0 +1,10 @@
PXSourceList is licensed under the New BSD License, as detailed below (adapted from OSI http://www.opensource.org/licenses/bsd-license.php):
Copyright © 2009-14, Alex Rozanski and other contributors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
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.
Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
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.
+7 -9
View File
@@ -9,24 +9,22 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.perspx.${PRODUCT_NAME:rfc1034identifier}</string>
<string>com.alexrozanski.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>2.0.7</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<string>2.0.7</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2009-15 Alex Rozanski and other contributors. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<string></string>
</dict>
</plist>
+28
View File
@@ -0,0 +1,28 @@
Pod::Spec.new do |s|
s.name = 'PXSourceList'
s.version = '2.0.7'
s.author = { 'Alex Rozanski' => 'alex@rozanski.me' }
s.license = 'BSD'
s.homepage = 'https://github.com/Perspx/PXSourceList'
s.summary = 'A Source List control for OS X.'
s.social_media_url = 'http://twitter.com/alexrozanski'
s.requires_arc = true
s.description = <<-DESC
PXSourceList is an NSOutlineView subclass which provides an easy-to-use
implementation of a sidebar similar to that found in iTunes, iPhoto and
Mail.app.
PXSourceList provides a simple API for displaying *icons* and *badges*
which are often used in Source Lists. The project additionally contains
and NSTableCellView subclass and generic data source model item for quick
and easy setup.
DESC
s.platform = :osx
s.osx.deployment_target = '10.7'
s.public_header_files = 'PXSourceList/*.h'
s.source = { :git => 'https://github.com/Perspx/PXSourceList.git', :tag => '2.0.7' }
s.source_files = 'PXSourceList/**/*.{h,m}'
end
+34 -53
View File
@@ -18,9 +18,9 @@
activeBuildConfigurationName = Release;
activeExecutable = D2EA6EB110D9202800402758 /* PXSourceList */;
activeSDKPreference = macosx10.6;
activeTarget = 8D1107260486CEB800E47090 /* PXSourceList */;
activeTarget = 8D1107260486CEB800E47090 /* PXSourceListDemo */;
addToTargets = (
8D1107260486CEB800E47090 /* PXSourceList */,
8D1107260486CEB800E47090 /* PXSourceListDemo */,
);
breakpoints = (
);
@@ -111,44 +111,45 @@
PBXFileDataSource_Warnings_ColumnID,
);
};
PBXPerProjectTemplateStateSaveDate = 284758363;
PBXWorkspaceStateSaveDate = 284758363;
PBXPerProjectTemplateStateSaveDate = 316452921;
PBXWorkspaceStateSaveDate = 316452921;
};
perUserProjectItems = {
D205EE4010F7A84B004C6545 /* PBXTextBookmark */ = D205EE4010F7A84B004C6545 /* PBXTextBookmark */;
D205EE7810F7AB18004C6545 /* PBXTextBookmark */ = D205EE7810F7AB18004C6545 /* PBXTextBookmark */;
D205EE7910F7AB18004C6545 /* PBXTextBookmark */ = D205EE7910F7AB18004C6545 /* PBXTextBookmark */;
D2456CB110F8F80F00B8045C /* PBXTextBookmark */ = D2456CB110F8F80F00B8045C /* PBXTextBookmark */;
D2456CE210F9059100B8045C /* PBXTextBookmark */ = D2456CE210F9059100B8045C /* PBXTextBookmark */;
D2456CF310F905AD00B8045C /* PBXTextBookmark */ = D2456CF310F905AD00B8045C /* PBXTextBookmark */;
D2456D0710F907AB00B8045C /* PBXTextBookmark */ = D2456D0710F907AB00B8045C /* PBXTextBookmark */;
D2456D0810F907AB00B8045C /* PBXTextBookmark */ = D2456D0810F907AB00B8045C /* PBXTextBookmark */;
D2456D6110F90CE000B8045C /* PBXTextBookmark */ = D2456D6110F90CE000B8045C /* PBXTextBookmark */;
D2456D9210F90D5000B8045C /* PBXTextBookmark */ = D2456D9210F90D5000B8045C /* PBXTextBookmark */;
D2456D9310F90D5000B8045C /* PBXTextBookmark */ = D2456D9310F90D5000B8045C /* PBXTextBookmark */;
D2456DF110F9116000B8045C /* PBXTextBookmark */ = D2456DF110F9116000B8045C /* PBXTextBookmark */;
D2456DF210F9116000B8045C /* PBXTextBookmark */ = D2456DF210F9116000B8045C /* PBXTextBookmark */;
D2456DF310F9116000B8045C /* PBXTextBookmark */ = D2456DF310F9116000B8045C /* PBXTextBookmark */;
D27B7D5B10E11399004D7DBC /* PBXTextBookmark */ = D27B7D5B10E11399004D7DBC /* PBXTextBookmark */;
D27B7D5C10E11399004D7DBC /* PBXTextBookmark */ = D27B7D5C10E11399004D7DBC /* PBXTextBookmark */;
D27B7D5E10E11399004D7DBC /* PBXTextBookmark */ = D27B7D5E10E11399004D7DBC /* PBXTextBookmark */;
D27B7D6010E11399004D7DBC /* PBXTextBookmark */ = D27B7D6010E11399004D7DBC /* PBXTextBookmark */;
D27B7D6210E11399004D7DBC /* PBXTextBookmark */ = D27B7D6210E11399004D7DBC /* PBXTextBookmark */;
D27B7D6410E11399004D7DBC /* PBXTextBookmark */ = D27B7D6410E11399004D7DBC /* PBXTextBookmark */;
D27B7D6610E11399004D7DBC /* PBXTextBookmark */ = D27B7D6610E11399004D7DBC /* PBXTextBookmark */;
D27B7D6810E11399004D7DBC /* PBXTextBookmark */ = D27B7D6810E11399004D7DBC /* PBXTextBookmark */;
D27B7D6C10E11399004D7DBC /* PBXTextBookmark */ = D27B7D6C10E11399004D7DBC /* PBXTextBookmark */;
D27B7D6E10E11399004D7DBC /* PBXTextBookmark */ = D27B7D6E10E11399004D7DBC /* PBXTextBookmark */;
D27B7D7010E11399004D7DBC /* PBXTextBookmark */ = D27B7D7010E11399004D7DBC /* PBXTextBookmark */;
D27B7D7210E11399004D7DBC /* PBXTextBookmark */ = D27B7D7210E11399004D7DBC /* PBXTextBookmark */;
D27B7D7410E11399004D7DBC /* PBXTextBookmark */ = D27B7D7410E11399004D7DBC /* PBXTextBookmark */;
D2E0722E10F89C030056126C /* PBXTextBookmark */ = D2E0722E10F89C030056126C /* PBXTextBookmark */;
D205EE4010F7A84B004C6545 = D205EE4010F7A84B004C6545 /* PBXTextBookmark */;
D205EE7810F7AB18004C6545 = D205EE7810F7AB18004C6545 /* PBXTextBookmark */;
D205EE7910F7AB18004C6545 = D205EE7910F7AB18004C6545 /* PBXTextBookmark */;
D2456CB110F8F80F00B8045C = D2456CB110F8F80F00B8045C /* PBXTextBookmark */;
D2456CE210F9059100B8045C = D2456CE210F9059100B8045C /* PBXTextBookmark */;
D2456CF310F905AD00B8045C = D2456CF310F905AD00B8045C /* PBXTextBookmark */;
D2456D0710F907AB00B8045C = D2456D0710F907AB00B8045C /* PBXTextBookmark */;
D2456D0810F907AB00B8045C = D2456D0810F907AB00B8045C /* PBXTextBookmark */;
D2456D6110F90CE000B8045C = D2456D6110F90CE000B8045C /* PBXTextBookmark */;
D2456D9210F90D5000B8045C = D2456D9210F90D5000B8045C /* PBXTextBookmark */;
D2456D9310F90D5000B8045C = D2456D9310F90D5000B8045C /* PBXTextBookmark */;
D2456DF110F9116000B8045C = D2456DF110F9116000B8045C /* PBXTextBookmark */;
D27B7D5B10E11399004D7DBC = D27B7D5B10E11399004D7DBC /* PBXTextBookmark */;
D27B7D5C10E11399004D7DBC = D27B7D5C10E11399004D7DBC /* PBXTextBookmark */;
D27B7D5E10E11399004D7DBC = D27B7D5E10E11399004D7DBC /* PBXTextBookmark */;
D27B7D6010E11399004D7DBC = D27B7D6010E11399004D7DBC /* PBXTextBookmark */;
D27B7D6210E11399004D7DBC = D27B7D6210E11399004D7DBC /* PBXTextBookmark */;
D27B7D6410E11399004D7DBC = D27B7D6410E11399004D7DBC /* PBXTextBookmark */;
D27B7D6610E11399004D7DBC = D27B7D6610E11399004D7DBC /* PBXTextBookmark */;
D27B7D6810E11399004D7DBC = D27B7D6810E11399004D7DBC /* PBXTextBookmark */;
D27B7D6C10E11399004D7DBC = D27B7D6C10E11399004D7DBC /* PBXTextBookmark */;
D27B7D6E10E11399004D7DBC = D27B7D6E10E11399004D7DBC /* PBXTextBookmark */;
D27B7D7010E11399004D7DBC = D27B7D7010E11399004D7DBC /* PBXTextBookmark */;
D27B7D7210E11399004D7DBC = D27B7D7210E11399004D7DBC /* PBXTextBookmark */;
D27B7D7410E11399004D7DBC = D27B7D7410E11399004D7DBC /* PBXTextBookmark */;
D2E0722E10F89C030056126C = D2E0722E10F89C030056126C /* PBXTextBookmark */;
};
sourceControlManager = D2EA6EBE10D9203500402758 /* Source Control */;
userBuildSettings = {
};
};
8D1107260486CEB800E47090 /* PXSourceList */ = {
4CC5EA2912DC81ED001F282E /* PXSourceList */ = {
activeExec = 0;
};
8D1107260486CEB800E47090 /* PXSourceListDemo */ = {
activeExec = 0;
executables = (
D2EA6EB110D9202800402758 /* PXSourceList */,
@@ -309,26 +310,6 @@
vrLen = 1343;
vrLoc = 1314;
};
D2456DF210F9116000B8045C /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = D205EDDD10F7A1EE004C6545 /* README.markdown */;
name = "README.markdown: 60";
rLen = 0;
rLoc = 4839;
rType = 0;
vrLen = 1858;
vrLoc = 2880;
};
D2456DF310F9116000B8045C /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = D205EDDD10F7A1EE004C6545 /* README.markdown */;
name = "README.markdown: 64";
rLen = 0;
rLoc = 4839;
rType = 0;
vrLen = 1928;
vrLoc = 2911;
};
D265C2D610E3814B00A1E824 /* TODO.rtf */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {818, 620}}";
@@ -231,6 +231,8 @@
<key>Layout</key>
<array>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXBottomSmartGroupGIDs</key>
@@ -279,12 +281,12 @@
<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
<array>
<array>
<integer>2</integer>
<integer>0</integer>
<integer>30</integer>
<integer>28</integer>
</array>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
<string>{{0, 1}, {259, 624}}</string>
<string>{{0, 0}, {259, 624}}</string>
</dict>
<key>PBXTopSmartGroupGIDs</key>
<array/>
@@ -314,8 +316,6 @@
<key>Dock</key>
<array>
<dict>
<key>BecomeActive</key>
<true/>
<key>ContentConfiguration</key>
<dict>
<key>PBXProjectModuleGUID</key>
@@ -330,10 +330,6 @@
<string>D2EA6EDB10D9255200402758</string>
<key>PBXProjectModuleLabel</key>
<string>README.markdown</string>
<key>_historyCapacity</key>
<integer>0</integer>
<key>bookmark</key>
<string>D2456DF310F9116000B8045C</string>
<key>history</key>
<array>
<string>D27B7D5B10E11399004D7DBC</string>
@@ -362,7 +358,6 @@
<string>D2456D9210F90D5000B8045C</string>
<string>D2456D9310F90D5000B8045C</string>
<string>D2456DF110F9116000B8045C</string>
<string>D2456DF210F9116000B8045C</string>
</array>
</dict>
<key>SplitCount</key>
@@ -376,18 +371,18 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {865, 406}}</string>
<string>{{0, 0}, {865, 347}}</string>
<key>RubberWindowFrame</key>
<string>4 74 1146 683 0 0 1280 778 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>406pt</string>
<string>347pt</string>
</dict>
<dict>
<key>Proportion</key>
<string>231pt</string>
<string>290pt</string>
<key>Tabs</key>
<array>
<dict>
@@ -401,7 +396,7 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{10, 27}, {865, 204}}</string>
<string>{{10, 27}, {865, 263}}</string>
<key>RubberWindowFrame</key>
<string>4 74 1146 683 0 0 1280 778 </string>
</dict>
@@ -506,11 +501,11 @@
</array>
<key>TableOfContents</key>
<array>
<string>D2456DF410F9116000B8045C</string>
<string>D25DB35D12DCB03B00C3C707</string>
<string>1CA23ED40692098700951B8B</string>
<string>D2456DF510F9116000B8045C</string>
<string>D25DB35E12DCB03B00C3C707</string>
<string>D2EA6EDA10D9255200402758</string>
<string>D2456DF610F9116000B8045C</string>
<string>D25DB35F12DCB03B00C3C707</string>
<string>1CA23EDF0692099D00951B8B</string>
<string>1CA23EE00692099D00951B8B</string>
<string>1CA23EE10692099D00951B8B</string>
@@ -657,13 +652,13 @@
</array>
<key>TableOfContents</key>
<array>
<string>D2456D9B10F90D5600B8045C</string>
<string>D25DB32312DCAE6200C3C707</string>
<string>1CCC7628064C1048000F2A68</string>
<string>1CCC7629064C1048000F2A68</string>
<string>D2456D9C10F90D5600B8045C</string>
<string>D2456D9D10F90D5600B8045C</string>
<string>D2456D9E10F90D5600B8045C</string>
<string>D2456D9F10F90D5600B8045C</string>
<string>D25DB32412DCAE6200C3C707</string>
<string>D25DB32512DCAE6200C3C707</string>
<string>D25DB32612DCAE6200C3C707</string>
<string>D25DB32712DCAE6200C3C707</string>
<string>D2EA6EDA10D9255200402758</string>
</array>
<key>ToolbarConfigUserDefaultsMinorVersion</key>
+620 -96
View File
@@ -3,77 +3,186 @@
archiveVersion = 1;
classes = {
};
objectVersion = 45;
objectVersion = 46;
objects = {
/* Begin PBXAggregateTarget section */
D251F6FD187342DA00FED7C9 /* Documentation */ = {
isa = PBXAggregateTarget;
buildConfigurationList = D251F6FE187342DB00FED7C9 /* Build configuration list for PBXAggregateTarget "Documentation" */;
buildPhases = (
D251F7011873439600FED7C9 /* ShellScript */,
);
dependencies = (
);
name = Documentation;
productName = Documentation;
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
D205EDF010F7A609004C6545 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D205EDEF10F7A609004C6545 /* MainMenu.xib */; };
D205EDF710F7A693004C6545 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D205EDF610F7A693004C6545 /* AppDelegate.m */; };
D2456CAA10F8F7F500B8045C /* audiobooks.png in Resources */ = {isa = PBXBuildFile; fileRef = D2456CA410F8F7F500B8045C /* audiobooks.png */; };
D2456CAB10F8F7F500B8045C /* movies.png in Resources */ = {isa = PBXBuildFile; fileRef = D2456CA510F8F7F500B8045C /* movies.png */; };
D2456CAC10F8F7F500B8045C /* music.png in Resources */ = {isa = PBXBuildFile; fileRef = D2456CA610F8F7F500B8045C /* music.png */; };
D2456CAD10F8F7F500B8045C /* playlist.png in Resources */ = {isa = PBXBuildFile; fileRef = D2456CA710F8F7F500B8045C /* playlist.png */; };
D2456CAE10F8F7F500B8045C /* playlistFolder.png in Resources */ = {isa = PBXBuildFile; fileRef = D2456CA810F8F7F500B8045C /* playlistFolder.png */; };
D2456CAF10F8F7F500B8045C /* podcasts.png in Resources */ = {isa = PBXBuildFile; fileRef = D2456CA910F8F7F500B8045C /* podcasts.png */; };
D2456D8C10F90D3B00B8045C /* SourceListItem.m in Sources */ = {isa = PBXBuildFile; fileRef = D2456D8B10F90D3B00B8045C /* SourceListItem.m */; };
D2456D8D10F90D4A00B8045C /* PXSourceList.m in Sources */ = {isa = PBXBuildFile; fileRef = D2EA6EC110D9205A00402758 /* PXSourceList.m */; };
D265C2D710E3814B00A1E824 /* TODO.rtf in Resources */ = {isa = PBXBuildFile; fileRef = D265C2D610E3814B00A1E824 /* TODO.rtf */; };
D213027C14B9AFE200AAFF85 /* PXSourceList.h in Headers */ = {isa = PBXBuildFile; fileRef = D213027814B9AFE200AAFF85 /* PXSourceList.h */; settings = {ATTRIBUTES = (Public, ); }; };
D213027E14B9AFE200AAFF85 /* PXSourceList.m in Sources */ = {isa = PBXBuildFile; fileRef = D213027914B9AFE200AAFF85 /* PXSourceList.m */; };
D213027F14B9AFE200AAFF85 /* PXSourceListDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D213027A14B9AFE200AAFF85 /* PXSourceListDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
D213028014B9AFE200AAFF85 /* PXSourceListDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = D213027B14B9AFE200AAFF85 /* PXSourceListDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
D21C59A0186EF8B200A8ECB3 /* PXSourceList.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CC5EA2A12DC81ED001F282E /* PXSourceList.framework */; };
D23A17591836AD790027F0D0 /* PXSourceListBadgeView.h in Headers */ = {isa = PBXBuildFile; fileRef = D23A17571836AD790027F0D0 /* PXSourceListBadgeView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D23A175B1836AD790027F0D0 /* PXSourceListBadgeView.m in Sources */ = {isa = PBXBuildFile; fileRef = D23A17581836AD790027F0D0 /* PXSourceListBadgeView.m */; };
D23A175E1836AE280027F0D0 /* PXSourceListBadgeCell.h in Headers */ = {isa = PBXBuildFile; fileRef = D23A175C1836AE280027F0D0 /* PXSourceListBadgeCell.h */; settings = {ATTRIBUTES = (Public, ); }; };
D23A17601836AE280027F0D0 /* PXSourceListBadgeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D23A175D1836AE280027F0D0 /* PXSourceListBadgeCell.m */; };
D24AE5531896A79700AC8CE0 /* Photo.m in Sources */ = {isa = PBXBuildFile; fileRef = D24AE5521896A79700AC8CE0 /* Photo.m */; };
D24B88F7188EC3B100841497 /* PXSourceListDelegateDataSourceProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D24B88F2188EC3B100841497 /* PXSourceListDelegateDataSourceProxy.h */; };
D24B88F8188EC3B100841497 /* PXSourceListDelegateDataSourceProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D24B88F3188EC3B100841497 /* PXSourceListDelegateDataSourceProxy.m */; };
D24B88F9188EC3B100841497 /* PXSourceListPrivateConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = D24B88F4188EC3B100841497 /* PXSourceListPrivateConstants.h */; };
D24B88FA188EC3B100841497 /* PXSourceListRuntimeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D24B88F5188EC3B100841497 /* PXSourceListRuntimeAdditions.h */; };
D24B88FB188EC3B100841497 /* PXSourceListRuntimeAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D24B88F6188EC3B100841497 /* PXSourceListRuntimeAdditions.m */; };
D24E1F02187DB323004DCBB0 /* PXSourceListItem.h in Headers */ = {isa = PBXBuildFile; fileRef = D24E1F00187DB323004DCBB0 /* PXSourceListItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
D24E1F03187DB323004DCBB0 /* PXSourceListItem.m in Sources */ = {isa = PBXBuildFile; fileRef = D24E1F01187DB323004DCBB0 /* PXSourceListItem.m */; };
D251F6F5187317C000FED7C9 /* PXSourceListTableCellView.h in Headers */ = {isa = PBXBuildFile; fileRef = D251F6F3187317C000FED7C9 /* PXSourceListTableCellView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D251F6F6187317C000FED7C9 /* PXSourceListTableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = D251F6F4187317C000FED7C9 /* PXSourceListTableCellView.m */; };
D251F6FA1873240E00FED7C9 /* SourceListIcons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D251F6F91873240E00FED7C9 /* SourceListIcons.xcassets */; };
D263123D186EF9F00036501C /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D263123C186EF9F00036501C /* Cocoa.framework */; };
D2631272186EFBBF0036501C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D2631271186EFBBF0036501C /* AppDelegate.m */; };
D2631274186EFBC50036501C /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D2631273186EFBC50036501C /* MainMenu.xib */; };
D263127B186EFBD20036501C /* audiobooks.png in Resources */ = {isa = PBXBuildFile; fileRef = D2631275186EFBD20036501C /* audiobooks.png */; };
D263127C186EFBD20036501C /* movies.png in Resources */ = {isa = PBXBuildFile; fileRef = D2631276186EFBD20036501C /* movies.png */; };
D263127D186EFBD20036501C /* music.png in Resources */ = {isa = PBXBuildFile; fileRef = D2631277186EFBD20036501C /* music.png */; };
D263127E186EFBD20036501C /* playlist.png in Resources */ = {isa = PBXBuildFile; fileRef = D2631278186EFBD20036501C /* playlist.png */; };
D263127F186EFBD20036501C /* playlistFolder.png in Resources */ = {isa = PBXBuildFile; fileRef = D2631279186EFBD20036501C /* playlistFolder.png */; };
D2631280186EFBD20036501C /* podcasts.png in Resources */ = {isa = PBXBuildFile; fileRef = D263127A186EFBD20036501C /* podcasts.png */; };
D2631282186EFBDC0036501C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D2631281186EFBDC0036501C /* main.m */; };
D2631289186EFC4E0036501C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D2631288186EFC4E0036501C /* AppDelegate.m */; };
D263128C186EFC5C0036501C /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D263128A186EFC5C0036501C /* MainMenu.xib */; };
D263128E186EFC630036501C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D263128D186EFC630036501C /* Images.xcassets */; };
D2631293186EFC830036501C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D2631292186EFC830036501C /* main.m */; };
D2631299186EFD8E0036501C /* PXSourceList.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CC5EA2A12DC81ED001F282E /* PXSourceList.framework */; };
D2FF2953187AD32000CFEDD1 /* PhotoCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = D2FF2952187AD32000CFEDD1 /* PhotoCollection.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
4CC5EA2F12DC8209001F282E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 4CC5EA2912DC81ED001F282E;
remoteInfo = PXSourceList;
};
D2631297186EFD8B0036501C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 4CC5EA2912DC81ED001F282E;
remoteInfo = PXSourceList;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
089C165DFE840E0CC02AAC07 /* InfoPlist.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = InfoPlist.strings; path = Resources/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
256AC3F00F4B6AF500CF3369 /* PXSourceList_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PXSourceList_Prefix.pch; sourceTree = "<group>"; };
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
8D1107310486CEB800E47090 /* PXSourceList-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "PXSourceList-Info.plist"; sourceTree = "<group>"; };
4CC5EA2A12DC81ED001F282E /* PXSourceList.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PXSourceList.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8D1107320486CEB800E47090 /* PXSourceList.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PXSourceList.app; sourceTree = BUILT_PRODUCTS_DIR; };
D205EDDD10F7A1EE004C6545 /* README.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.markdown; sourceTree = "<group>"; };
D205EDEF10F7A609004C6545 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = Resources/MainMenu.xib; sourceTree = "<group>"; };
D205EDF510F7A693004C6545 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Classes/AppDelegate.h; sourceTree = "<group>"; };
D205EDF610F7A693004C6545 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Classes/AppDelegate.m; sourceTree = "<group>"; };
D205EE7D10F7B5AB004C6545 /* SourceListItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SourceListItem.h; path = Classes/SourceListItem.h; sourceTree = "<group>"; };
D2456CA410F8F7F500B8045C /* audiobooks.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = audiobooks.png; path = Resources/audiobooks.png; sourceTree = "<group>"; };
D2456CA510F8F7F500B8045C /* movies.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = movies.png; path = Resources/movies.png; sourceTree = "<group>"; };
D2456CA610F8F7F500B8045C /* music.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = music.png; path = Resources/music.png; sourceTree = "<group>"; };
D2456CA710F8F7F500B8045C /* playlist.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = playlist.png; path = Resources/playlist.png; sourceTree = "<group>"; };
D2456CA810F8F7F500B8045C /* playlistFolder.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = playlistFolder.png; path = Resources/playlistFolder.png; sourceTree = "<group>"; };
D2456CA910F8F7F500B8045C /* podcasts.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = podcasts.png; path = Resources/podcasts.png; sourceTree = "<group>"; };
D2456D8B10F90D3B00B8045C /* SourceListItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SourceListItem.m; path = Classes/SourceListItem.m; sourceTree = "<group>"; };
D265C2D610E3814B00A1E824 /* TODO.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = TODO.rtf; sourceTree = "<group>"; };
D2EA6EC010D9205A00402758 /* PXSourceList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceList.h; path = Classes/PXSourceList.h; sourceTree = "<group>"; };
D2EA6EC110D9205A00402758 /* PXSourceList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PXSourceList.m; path = Classes/PXSourceList.m; sourceTree = "<group>"; };
D2EA6EFF10D925AA00402758 /* PXSourceListDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListDataSource.h; path = Classes/PXSourceListDataSource.h; sourceTree = "<group>"; };
D2EA6F0010D925AA00402758 /* PXSourceListDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListDelegate.h; path = Classes/PXSourceListDelegate.h; sourceTree = "<group>"; };
D213027814B9AFE200AAFF85 /* PXSourceList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceList.h; path = PXSourceList/PXSourceList.h; sourceTree = "<group>"; };
D213027914B9AFE200AAFF85 /* PXSourceList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PXSourceList.m; path = PXSourceList/PXSourceList.m; sourceTree = "<group>"; };
D213027A14B9AFE200AAFF85 /* PXSourceListDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListDataSource.h; path = PXSourceList/PXSourceListDataSource.h; sourceTree = "<group>"; };
D213027B14B9AFE200AAFF85 /* PXSourceListDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListDelegate.h; path = PXSourceList/PXSourceListDelegate.h; sourceTree = "<group>"; };
D23A17571836AD790027F0D0 /* PXSourceListBadgeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListBadgeView.h; path = PXSourceList/PXSourceListBadgeView.h; sourceTree = "<group>"; };
D23A17581836AD790027F0D0 /* PXSourceListBadgeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PXSourceListBadgeView.m; path = PXSourceList/PXSourceListBadgeView.m; sourceTree = "<group>"; };
D23A175C1836AE280027F0D0 /* PXSourceListBadgeCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListBadgeCell.h; path = PXSourceList/PXSourceListBadgeCell.h; sourceTree = "<group>"; };
D23A175D1836AE280027F0D0 /* PXSourceListBadgeCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PXSourceListBadgeCell.m; path = PXSourceList/PXSourceListBadgeCell.m; sourceTree = "<group>"; };
D24AE5511896A79700AC8CE0 /* Photo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Photo.h; path = Examples/ViewBasedSourceList/Photo.h; sourceTree = "<group>"; };
D24AE5521896A79700AC8CE0 /* Photo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Photo.m; path = Examples/ViewBasedSourceList/Photo.m; sourceTree = "<group>"; };
D24B88F2188EC3B100841497 /* PXSourceListDelegateDataSourceProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListDelegateDataSourceProxy.h; path = PXSourceList/Internal/PXSourceListDelegateDataSourceProxy.h; sourceTree = "<group>"; };
D24B88F3188EC3B100841497 /* PXSourceListDelegateDataSourceProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PXSourceListDelegateDataSourceProxy.m; path = PXSourceList/Internal/PXSourceListDelegateDataSourceProxy.m; sourceTree = "<group>"; };
D24B88F4188EC3B100841497 /* PXSourceListPrivateConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListPrivateConstants.h; path = PXSourceList/Internal/PXSourceListPrivateConstants.h; sourceTree = "<group>"; };
D24B88F5188EC3B100841497 /* PXSourceListRuntimeAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListRuntimeAdditions.h; path = PXSourceList/Internal/PXSourceListRuntimeAdditions.h; sourceTree = "<group>"; };
D24B88F6188EC3B100841497 /* PXSourceListRuntimeAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PXSourceListRuntimeAdditions.m; path = PXSourceList/Internal/PXSourceListRuntimeAdditions.m; sourceTree = "<group>"; };
D24E1F00187DB323004DCBB0 /* PXSourceListItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListItem.h; path = PXSourceList/PXSourceListItem.h; sourceTree = "<group>"; };
D24E1F01187DB323004DCBB0 /* PXSourceListItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PXSourceListItem.m; path = PXSourceList/PXSourceListItem.m; sourceTree = "<group>"; };
D251F6F3187317C000FED7C9 /* PXSourceListTableCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PXSourceListTableCellView.h; path = PXSourceList/PXSourceListTableCellView.h; sourceTree = "<group>"; };
D251F6F4187317C000FED7C9 /* PXSourceListTableCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PXSourceListTableCellView.m; path = PXSourceList/PXSourceListTableCellView.m; sourceTree = "<group>"; };
D251F6F91873240E00FED7C9 /* SourceListIcons.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = SourceListIcons.xcassets; path = Examples/ViewBasedSourceList/SourceListIcons.xcassets; sourceTree = "<group>"; };
D263123B186EF9F00036501C /* ViewBasedSourceList.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ViewBasedSourceList.app; sourceTree = BUILT_PRODUCTS_DIR; };
D263123C186EF9F00036501C /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
D2631257186EF9F10036501C /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
D2631270186EFBBF0036501C /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Examples/CellBasedSourceList/AppDelegate.h; sourceTree = "<group>"; };
D2631271186EFBBF0036501C /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Examples/CellBasedSourceList/AppDelegate.m; sourceTree = "<group>"; };
D2631273186EFBC50036501C /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = MainMenu.xib; path = Examples/CellBasedSourceList/MainMenu.xib; sourceTree = "<group>"; };
D2631275186EFBD20036501C /* audiobooks.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = audiobooks.png; path = Examples/CellBasedSourceList/Images/audiobooks.png; sourceTree = "<group>"; };
D2631276186EFBD20036501C /* movies.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = movies.png; path = Examples/CellBasedSourceList/Images/movies.png; sourceTree = "<group>"; };
D2631277186EFBD20036501C /* music.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = music.png; path = Examples/CellBasedSourceList/Images/music.png; sourceTree = "<group>"; };
D2631278186EFBD20036501C /* playlist.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = playlist.png; path = Examples/CellBasedSourceList/Images/playlist.png; sourceTree = "<group>"; };
D2631279186EFBD20036501C /* playlistFolder.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = playlistFolder.png; path = Examples/CellBasedSourceList/Images/playlistFolder.png; sourceTree = "<group>"; };
D263127A186EFBD20036501C /* podcasts.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = podcasts.png; path = Examples/CellBasedSourceList/Images/podcasts.png; sourceTree = "<group>"; };
D2631281186EFBDC0036501C /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Examples/CellBasedSourceList/main.m; sourceTree = "<group>"; };
D2631283186EFBE50036501C /* PXSourceListDemo_Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PXSourceListDemo_Prefix.pch; path = Examples/CellBasedSourceList/PXSourceListDemo_Prefix.pch; sourceTree = "<group>"; };
D2631284186EFBEB0036501C /* PXSourceListDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PXSourceListDemo-Info.plist"; path = "Examples/CellBasedSourceList/PXSourceListDemo-Info.plist"; sourceTree = "<group>"; };
D2631286186EFBFA0036501C /* English */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = English; path = Examples/CellBasedSourceList/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
D2631287186EFC4E0036501C /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Examples/ViewBasedSourceList/AppDelegate.h; sourceTree = "<group>"; };
D2631288186EFC4E0036501C /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Examples/ViewBasedSourceList/AppDelegate.m; sourceTree = "<group>"; };
D263128B186EFC5C0036501C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Examples/ViewBasedSourceList/Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
D263128D186EFC630036501C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Examples/ViewBasedSourceList/Images.xcassets; sourceTree = "<group>"; };
D263128F186EFC6D0036501C /* ViewBasedSourceList-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "ViewBasedSourceList-Info.plist"; path = "Examples/ViewBasedSourceList/ViewBasedSourceList-Info.plist"; sourceTree = SOURCE_ROOT; };
D2631291186EFC7C0036501C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Examples/ViewBasedSourceList/en.lproj/InfoPlist.strings; sourceTree = SOURCE_ROOT; };
D2631292186EFC830036501C /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Examples/ViewBasedSourceList/main.m; sourceTree = SOURCE_ROOT; };
D2631294186EFC890036501C /* ViewBasedSourceList-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "ViewBasedSourceList-Prefix.pch"; path = "Examples/ViewBasedSourceList/ViewBasedSourceList-Prefix.pch"; sourceTree = SOURCE_ROOT; };
D2631296186EFC910036501C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = Examples/ViewBasedSourceList/en.lproj/Credits.rtf; sourceTree = SOURCE_ROOT; };
D2BA8D1A1891CB0500E4A15A /* PXSourceList-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PXSourceList-Info.plist"; sourceTree = "<group>"; };
D2FF2951187AD32000CFEDD1 /* PhotoCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhotoCollection.h; path = Examples/ViewBasedSourceList/PhotoCollection.h; sourceTree = "<group>"; };
D2FF2952187AD32000CFEDD1 /* PhotoCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PhotoCollection.m; path = Examples/ViewBasedSourceList/PhotoCollection.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
4CC5EA2812DC81ED001F282E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
8D11072E0486CEB800E47090 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D21C59A0186EF8B200A8ECB3 /* PXSourceList.framework in Frameworks */,
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D2631238186EF9F00036501C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D2631299186EFD8E0036501C /* PXSourceList.framework in Frameworks */,
D263123D186EF9F00036501C /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
080E96DDFE201D6D7F000001 /* Classes */ = {
080E96DDFE201D6D7F000001 /* PXSourceList */ = {
isa = PBXGroup;
children = (
D205EDF510F7A693004C6545 /* AppDelegate.h */,
D205EDF610F7A693004C6545 /* AppDelegate.m */,
D205EE7D10F7B5AB004C6545 /* SourceListItem.h */,
D2456D8B10F90D3B00B8045C /* SourceListItem.m */,
D2EA702910D92EB300402758 /* PXSourceList */,
D213027814B9AFE200AAFF85 /* PXSourceList.h */,
D213027914B9AFE200AAFF85 /* PXSourceList.m */,
D213027A14B9AFE200AAFF85 /* PXSourceListDataSource.h */,
D213027B14B9AFE200AAFF85 /* PXSourceListDelegate.h */,
D23A17571836AD790027F0D0 /* PXSourceListBadgeView.h */,
D23A17581836AD790027F0D0 /* PXSourceListBadgeView.m */,
D23A175C1836AE280027F0D0 /* PXSourceListBadgeCell.h */,
D23A175D1836AE280027F0D0 /* PXSourceListBadgeCell.m */,
D251F6F3187317C000FED7C9 /* PXSourceListTableCellView.h */,
D251F6F4187317C000FED7C9 /* PXSourceListTableCellView.m */,
D24E1F00187DB323004DCBB0 /* PXSourceListItem.h */,
D24E1F01187DB323004DCBB0 /* PXSourceListItem.m */,
D2CD7D4A186B02690003E038 /* Internal */,
D21302BA14B9B1DC00AAFF85 /* Supporting Files */,
);
name = Classes;
name = PXSourceList;
sourceTree = "<group>";
};
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
@@ -98,6 +207,8 @@
isa = PBXGroup;
children = (
8D1107320486CEB800E47090 /* PXSourceList.app */,
4CC5EA2A12DC81ED001F282E /* PXSourceList.framework */,
D263123B186EF9F00036501C /* ViewBasedSourceList.app */,
);
name = Products;
sourceTree = "<group>";
@@ -105,33 +216,31 @@
29B97314FDCFA39411CA2CEA /* PXSourceList */ = {
isa = PBXGroup;
children = (
D265C2D610E3814B00A1E824 /* TODO.rtf */,
D205EDDD10F7A1EE004C6545 /* README.markdown */,
080E96DDFE201D6D7F000001 /* Classes */,
29B97315FDCFA39411CA2CEA /* Other Sources */,
29B97317FDCFA39411CA2CEA /* Resources */,
080E96DDFE201D6D7F000001 /* PXSourceList */,
D213027714B9AFCA00AAFF85 /* Example Projects */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
);
name = PXSourceList;
sourceTree = "<group>";
};
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
29B97315FDCFA39411CA2CEA /* Supporting Files */ = {
isa = PBXGroup;
children = (
256AC3F00F4B6AF500CF3369 /* PXSourceList_Prefix.pch */,
29B97316FDCFA39411CA2CEA /* main.m */,
D2631281186EFBDC0036501C /* main.m */,
D2631283186EFBE50036501C /* PXSourceListDemo_Prefix.pch */,
D2631284186EFBEB0036501C /* PXSourceListDemo-Info.plist */,
D2631285186EFBFA0036501C /* InfoPlist.strings */,
);
name = "Other Sources";
name = "Supporting Files";
sourceTree = "<group>";
};
29B97317FDCFA39411CA2CEA /* Resources */ = {
isa = PBXGroup;
children = (
D2631273186EFBC50036501C /* MainMenu.xib */,
D27B7D2710E0C925004D7DBC /* Icons */,
8D1107310486CEB800E47090 /* PXSourceList-Info.plist */,
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
D205EDEF10F7A609004C6545 /* MainMenu.xib */,
);
name = Resources;
sourceTree = "<group>";
@@ -140,41 +249,148 @@
isa = PBXGroup;
children = (
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
D263123C186EF9F00036501C /* Cocoa.framework */,
D2631257186EF9F10036501C /* XCTest.framework */,
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
);
name = Frameworks;
sourceTree = "<group>";
};
D213027714B9AFCA00AAFF85 /* Example Projects */ = {
isa = PBXGroup;
children = (
D2631236186EF9CB0036501C /* View-based Source List */,
D21C599D186EF84900A8ECB3 /* Cell-based Source List */,
);
name = "Example Projects";
sourceTree = "<group>";
};
D21302BA14B9B1DC00AAFF85 /* Supporting Files */ = {
isa = PBXGroup;
children = (
D2BA8D1A1891CB0500E4A15A /* PXSourceList-Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
D21C599D186EF84900A8ECB3 /* Cell-based Source List */ = {
isa = PBXGroup;
children = (
D2631270186EFBBF0036501C /* AppDelegate.h */,
D2631271186EFBBF0036501C /* AppDelegate.m */,
29B97317FDCFA39411CA2CEA /* Resources */,
29B97315FDCFA39411CA2CEA /* Supporting Files */,
);
name = "Cell-based Source List";
sourceTree = "<group>";
};
D2631236186EF9CB0036501C /* View-based Source List */ = {
isa = PBXGroup;
children = (
D2631287186EFC4E0036501C /* AppDelegate.h */,
D2631288186EFC4E0036501C /* AppDelegate.m */,
D263128A186EFC5C0036501C /* MainMenu.xib */,
D263128D186EFC630036501C /* Images.xcassets */,
D251F6F91873240E00FED7C9 /* SourceListIcons.xcassets */,
D2FF2950187AD30200CFEDD1 /* Model */,
D263123F186EF9F00036501C /* Supporting Files */,
);
name = "View-based Source List";
sourceTree = "<group>";
};
D263123F186EF9F00036501C /* Supporting Files */ = {
isa = PBXGroup;
children = (
D263128F186EFC6D0036501C /* ViewBasedSourceList-Info.plist */,
D2631290186EFC7C0036501C /* InfoPlist.strings */,
D2631292186EFC830036501C /* main.m */,
D2631294186EFC890036501C /* ViewBasedSourceList-Prefix.pch */,
D2631295186EFC910036501C /* Credits.rtf */,
);
name = "Supporting Files";
path = ViewBasedSourceList;
sourceTree = "<group>";
};
D27B7D2710E0C925004D7DBC /* Icons */ = {
isa = PBXGroup;
children = (
D2456CA410F8F7F500B8045C /* audiobooks.png */,
D2456CA510F8F7F500B8045C /* movies.png */,
D2456CA610F8F7F500B8045C /* music.png */,
D2456CA710F8F7F500B8045C /* playlist.png */,
D2456CA810F8F7F500B8045C /* playlistFolder.png */,
D2456CA910F8F7F500B8045C /* podcasts.png */,
D2631275186EFBD20036501C /* audiobooks.png */,
D2631276186EFBD20036501C /* movies.png */,
D2631277186EFBD20036501C /* music.png */,
D2631278186EFBD20036501C /* playlist.png */,
D2631279186EFBD20036501C /* playlistFolder.png */,
D263127A186EFBD20036501C /* podcasts.png */,
);
name = Icons;
sourceTree = "<group>";
};
D2EA702910D92EB300402758 /* PXSourceList */ = {
D2CD7D4A186B02690003E038 /* Internal */ = {
isa = PBXGroup;
children = (
D2EA6EC010D9205A00402758 /* PXSourceList.h */,
D2EA6EC110D9205A00402758 /* PXSourceList.m */,
D2EA6EFF10D925AA00402758 /* PXSourceListDataSource.h */,
D2EA6F0010D925AA00402758 /* PXSourceListDelegate.h */,
D24B88F4188EC3B100841497 /* PXSourceListPrivateConstants.h */,
D24B88F2188EC3B100841497 /* PXSourceListDelegateDataSourceProxy.h */,
D24B88F3188EC3B100841497 /* PXSourceListDelegateDataSourceProxy.m */,
D24B88F5188EC3B100841497 /* PXSourceListRuntimeAdditions.h */,
D24B88F6188EC3B100841497 /* PXSourceListRuntimeAdditions.m */,
);
name = PXSourceList;
name = Internal;
sourceTree = "<group>";
};
D2FF2950187AD30200CFEDD1 /* Model */ = {
isa = PBXGroup;
children = (
D24AE5511896A79700AC8CE0 /* Photo.h */,
D24AE5521896A79700AC8CE0 /* Photo.m */,
D2FF2951187AD32000CFEDD1 /* PhotoCollection.h */,
D2FF2952187AD32000CFEDD1 /* PhotoCollection.m */,
);
name = Model;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
4CC5EA2512DC81ED001F282E /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
D24B88F7188EC3B100841497 /* PXSourceListDelegateDataSourceProxy.h in Headers */,
D24E1F02187DB323004DCBB0 /* PXSourceListItem.h in Headers */,
D23A175E1836AE280027F0D0 /* PXSourceListBadgeCell.h in Headers */,
D24B88FA188EC3B100841497 /* PXSourceListRuntimeAdditions.h in Headers */,
D251F6F5187317C000FED7C9 /* PXSourceListTableCellView.h in Headers */,
D24B88F9188EC3B100841497 /* PXSourceListPrivateConstants.h in Headers */,
D23A17591836AD790027F0D0 /* PXSourceListBadgeView.h in Headers */,
D213027C14B9AFE200AAFF85 /* PXSourceList.h in Headers */,
D213027F14B9AFE200AAFF85 /* PXSourceListDataSource.h in Headers */,
D213028014B9AFE200AAFF85 /* PXSourceListDelegate.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
8D1107260486CEB800E47090 /* PXSourceList */ = {
4CC5EA2912DC81ED001F282E /* PXSourceList */ = {
isa = PBXNativeTarget;
buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "PXSourceList" */;
buildConfigurationList = 4CC5EA4B12DC8264001F282E /* Build configuration list for PBXNativeTarget "PXSourceList" */;
buildPhases = (
4CC5EA2512DC81ED001F282E /* Headers */,
4CC5EA2612DC81ED001F282E /* Resources */,
4CC5EA2712DC81ED001F282E /* Sources */,
4CC5EA2812DC81ED001F282E /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = PXSourceList;
productName = PXSourceList;
productReference = 4CC5EA2A12DC81ED001F282E /* PXSourceList.framework */;
productType = "com.apple.product-type.framework";
};
8D1107260486CEB800E47090 /* CellBasedSourceList */ = {
isa = PBXNativeTarget;
buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "CellBasedSourceList" */;
buildPhases = (
8D11072C0486CEB800E47090 /* Sources */,
8D1107290486CEB800E47090 /* Resources */,
@@ -183,89 +399,277 @@
buildRules = (
);
dependencies = (
4CC5EA3012DC8209001F282E /* PBXTargetDependency */,
);
name = PXSourceList;
name = CellBasedSourceList;
productInstallPath = "$(HOME)/Applications";
productName = PXSourceList;
productReference = 8D1107320486CEB800E47090 /* PXSourceList.app */;
productType = "com.apple.product-type.application";
};
D263123A186EF9F00036501C /* ViewBasedSourceList */ = {
isa = PBXNativeTarget;
buildConfigurationList = D2631264186EF9F10036501C /* Build configuration list for PBXNativeTarget "ViewBasedSourceList" */;
buildPhases = (
D2631237186EF9F00036501C /* Sources */,
D2631238186EF9F00036501C /* Frameworks */,
D2631239186EF9F00036501C /* Resources */,
);
buildRules = (
);
dependencies = (
D2631298186EFD8B0036501C /* PBXTargetDependency */,
);
name = ViewBasedSourceList;
productName = ViewBasedSourceList;
productReference = D263123B186EF9F00036501C /* ViewBasedSourceList.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0500;
};
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "PXSourceList" */;
compatibilityVersion = "Xcode 3.1";
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
en,
Base,
);
mainGroup = 29B97314FDCFA39411CA2CEA /* PXSourceList */;
projectDirPath = "";
projectRoot = "";
targets = (
8D1107260486CEB800E47090 /* PXSourceList */,
8D1107260486CEB800E47090 /* CellBasedSourceList */,
D263123A186EF9F00036501C /* ViewBasedSourceList */,
4CC5EA2912DC81ED001F282E /* PXSourceList */,
D251F6FD187342DA00FED7C9 /* Documentation */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
4CC5EA2612DC81ED001F282E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
8D1107290486CEB800E47090 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
D265C2D710E3814B00A1E824 /* TODO.rtf in Resources */,
D205EDF010F7A609004C6545 /* MainMenu.xib in Resources */,
D2456CAA10F8F7F500B8045C /* audiobooks.png in Resources */,
D2456CAB10F8F7F500B8045C /* movies.png in Resources */,
D2456CAC10F8F7F500B8045C /* music.png in Resources */,
D2456CAD10F8F7F500B8045C /* playlist.png in Resources */,
D2456CAE10F8F7F500B8045C /* playlistFolder.png in Resources */,
D2456CAF10F8F7F500B8045C /* podcasts.png in Resources */,
D2631274186EFBC50036501C /* MainMenu.xib in Resources */,
D263127E186EFBD20036501C /* playlist.png in Resources */,
D263127D186EFBD20036501C /* music.png in Resources */,
D263127F186EFBD20036501C /* playlistFolder.png in Resources */,
D2631280186EFBD20036501C /* podcasts.png in Resources */,
D263127B186EFBD20036501C /* audiobooks.png in Resources */,
D263127C186EFBD20036501C /* movies.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D2631239186EF9F00036501C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D263128E186EFC630036501C /* Images.xcassets in Resources */,
D251F6FA1873240E00FED7C9 /* SourceListIcons.xcassets in Resources */,
D263128C186EFC5C0036501C /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
D251F7011873439600FED7C9 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Note: requires appledoc to be installed. See https://github.com/tomaz/appledoc for more information.\n\n# Thanks to http://stackoverflow.com/a/13871762/75245 for help with the parsing.\ninfoPlistLocation=${PROJECT_DIR}/`/usr/bin/xcrun xcodebuild -showBuildSettings -target \"PXSourceList\" 2>/dev/null | grep \"INFOPLIST_FILE\" | sed 's/[ ]*INFOPLIST_FILE = //'`\n\nversion=`/usr/libexec/Plistbuddy -c 'Print :CFBundleShortVersionString' $infoPlistLocation`\noutputPath=\"docs\"\nsourceDir=\"PXSourceList\"\n\n/usr/local/bin/appledoc \\\n--project-name \"${PROJECT_NAME}\" \\\n--project-company \"Alex Rozanski\" \\\n--project-version \"${version}\" \\\n--company-id \"com.alexrozanski\" \\\n--output \"${outputPath}\" \\\n--logformat \"xcode\" \\\n--no-repeat-first-par \\\n--keep-intermediate-files \\\n--no-install-docset \\\n--clean-output \\\n${sourceDir}";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
4CC5EA2712DC81ED001F282E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D23A17601836AE280027F0D0 /* PXSourceListBadgeCell.m in Sources */,
D24E1F03187DB323004DCBB0 /* PXSourceListItem.m in Sources */,
D24B88FB188EC3B100841497 /* PXSourceListRuntimeAdditions.m in Sources */,
D23A175B1836AD790027F0D0 /* PXSourceListBadgeView.m in Sources */,
D213027E14B9AFE200AAFF85 /* PXSourceList.m in Sources */,
D251F6F6187317C000FED7C9 /* PXSourceListTableCellView.m in Sources */,
D24B88F8188EC3B100841497 /* PXSourceListDelegateDataSourceProxy.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
8D11072C0486CEB800E47090 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8D11072D0486CEB800E47090 /* main.m in Sources */,
D2456D8D10F90D4A00B8045C /* PXSourceList.m in Sources */,
D205EDF710F7A693004C6545 /* AppDelegate.m in Sources */,
D2456D8C10F90D3B00B8045C /* SourceListItem.m in Sources */,
D2631282186EFBDC0036501C /* main.m in Sources */,
D2631272186EFBBF0036501C /* AppDelegate.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D2631237186EF9F00036501C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D24AE5531896A79700AC8CE0 /* Photo.m in Sources */,
D2631293186EFC830036501C /* main.m in Sources */,
D2631289186EFC4E0036501C /* AppDelegate.m in Sources */,
D2FF2953187AD32000CFEDD1 /* PhotoCollection.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
4CC5EA3012DC8209001F282E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4CC5EA2912DC81ED001F282E /* PXSourceList */;
targetProxy = 4CC5EA2F12DC8209001F282E /* PBXContainerItemProxy */;
};
D2631298186EFD8B0036501C /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4CC5EA2912DC81ED001F282E /* PXSourceList */;
targetProxy = D2631297186EFD8B0036501C /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
D2631285186EFBFA0036501C /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
089C165DFE840E0CC02AAC07 /* InfoPlist.strings */,
D2631286186EFBFA0036501C /* English */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
D263128A186EFC5C0036501C /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
D263128B186EFC5C0036501C /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
D2631290186EFC7C0036501C /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
D2631291186EFC7C0036501C /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
D2631295186EFC910036501C /* Credits.rtf */ = {
isa = PBXVariantGroup;
children = (
D2631296186EFC910036501C /* en */,
);
name = Credits.rtf;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
4CC5EA2B12DC81ED001F282E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_GC = unsupported;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
INFOPLIST_FILE = "PXSourceList-Info.plist";
INSTALL_PATH = "@loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.7;
OTHER_LDFLAGS = (
"-framework",
Foundation,
"-framework",
AppKit,
);
PRODUCT_NAME = PXSourceList;
SDKROOT = macosx;
};
name = Debug;
};
4CC5EA2C12DC81ED001F282E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_ENABLE_OBJC_GC = unsupported;
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
INFOPLIST_FILE = "PXSourceList-Info.plist";
INSTALL_PATH = "@loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.7;
OTHER_LDFLAGS = (
"-framework",
Foundation,
"-framework",
AppKit,
);
PRODUCT_NAME = PXSourceList;
SDKROOT = macosx;
ZERO_LINK = NO;
};
name = Release;
};
C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = PXSourceList_Prefix.pch;
INFOPLIST_FILE = "PXSourceList-Info.plist";
GCC_PREFIX_HEADER = Examples/CellBasedSourceList/CellBasedSourceList_Prefix.pch;
INFOPLIST_FILE = "Examples/CellBasedSourceList/CellBasedSourceList-Info.plist";
INSTALL_PATH = "$(HOME)/Applications";
MACOSX_DEPLOYMENT_TARGET = 10.7;
PRODUCT_NAME = PXSourceList;
SDKROOT = macosx;
};
name = Debug;
};
@@ -273,46 +677,148 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_ENABLE_OBJC_GC = unsupported;
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = PXSourceList_Prefix.pch;
INFOPLIST_FILE = "PXSourceList-Info.plist";
GCC_PREFIX_HEADER = Examples/CellBasedSourceList/CellBasedSourceList_Prefix.pch;
INFOPLIST_FILE = "Examples/CellBasedSourceList/CellBasedSourceList-Info.plist";
INSTALL_PATH = "$(HOME)/Applications";
MACOSX_DEPLOYMENT_TARGET = 10.7;
PRODUCT_NAME = PXSourceList;
SDKROOT = macosx;
};
name = Release;
};
C01FCF4F08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
PREBINDING = NO;
SDKROOT = macosx10.5;
SDKROOT = macosx;
VALID_ARCHS = "i386 x86_64";
};
name = Debug;
};
C01FCF5008A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
PREBINDING = NO;
SDKROOT = macosx10.5;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
VALID_ARCHS = "i386 x86_64";
};
name = Release;
};
D251F6FF187342DB00FED7C9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
D251F700187342DB00FED7C9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
D2631265186EF9F10036501C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Examples/ViewBasedSourceList/ViewBasedSourceList-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = "Examples/ViewBasedSourceList/ViewBasedSourceList-Info.plist";
MACOSX_DEPLOYMENT_TARGET = 10.9;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Debug;
};
D2631266186EF9F10036501C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Examples/ViewBasedSourceList/ViewBasedSourceList-Prefix.pch";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = "Examples/ViewBasedSourceList/ViewBasedSourceList-Info.plist";
MACOSX_DEPLOYMENT_TARGET = 10.9;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "PXSourceList" */ = {
4CC5EA4B12DC8264001F282E /* Build configuration list for PBXNativeTarget "PXSourceList" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4CC5EA2B12DC81ED001F282E /* Debug */,
4CC5EA2C12DC81ED001F282E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "CellBasedSourceList" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C01FCF4B08A954540054247B /* Debug */,
@@ -330,6 +836,24 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D251F6FE187342DB00FED7C9 /* Build configuration list for PBXAggregateTarget "Documentation" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D251F6FF187342DB00FED7C9 /* Debug */,
D251F700187342DB00FED7C9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D2631264186EF9F10036501C /* Build configuration list for PBXNativeTarget "ViewBasedSourceList" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D2631265186EF9F10036501C /* Debug */,
D2631266186EF9F10036501C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
@@ -0,0 +1,21 @@
//
// PXSourceListDelegateDataSourceProxy.h
// PXSourceList
//
// Created by Alex Rozanski on 25/12/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Foundation/Foundation.h>
#import "PXSourceList.h"
@interface PXSourceListDelegateDataSourceProxy : NSProxy <NSOutlineViewDelegate, NSOutlineViewDataSource, PXSourceListDelegate, PXSourceListDataSource>
@property (weak, nonatomic) PXSourceList *sourceList;
@property (unsafe_unretained, nonatomic) id <PXSourceListDelegate> delegate;
@property (unsafe_unretained, nonatomic) id <PXSourceListDataSource> dataSource;
- (id)initWithSourceList:(PXSourceList *)sourceList;
@end
@@ -0,0 +1,470 @@
//
// PXSourceListDelegateDataSourceProxy.m
// PXSourceList
//
// Created by Alex Rozanski on 25/12/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import "PXSourceListDelegateDataSourceProxy.h"
#import <objc/runtime.h>
#import "PXSourceListPrivateConstants.h"
#import "PXSourceListRuntimeAdditions.h"
// Internal constants.
static NSString * const forwardingMapForwardingMethodNameKey = @"methodName";
static NSString * const forwardingMapForwardedArgumentIndexesKey = @"forwardedArgumentIndexes";
static NSArray * __outlineViewDelegateMethods = nil;
static NSArray * __outlineViewDataSourceMethods = nil;
static NSArray * __requiredOutlineViewDataSourceMethods = nil;
// Cache the PXSourceListDelegate and PXSourceListDataSource method names so that if these methods are invoked on
// us, we can quickly forward them to the delegate and dataSource using -forwardingTargetForSelector: without going
// through -forwardInvocation:.
static NSArray * __fastPathForwardingDelegateMethods = nil;
static NSArray * __fastPathForwardingDataSourceMethods = nil;
// We want to suppress the warnings for protocol methods not being implemented. As a proxy we will forward these
// messages to the actual delegate and data source.
#pragma clang diagnostic ignored "-Wprotocol"
@implementation PXSourceListDelegateDataSourceProxy
+ (void)initialize
{
__outlineViewDelegateMethods = px_methodNamesForProtocol(@protocol(NSOutlineViewDelegate));
__outlineViewDataSourceMethods = px_methodNamesForProtocol(@protocol(NSOutlineViewDataSource));
__fastPathForwardingDelegateMethods = [self fastPathForwardingDelegateMethods];
__fastPathForwardingDataSourceMethods = px_methodNamesForProtocol(@protocol(PXSourceListDataSource));
__requiredOutlineViewDataSourceMethods = @[NSStringFromSelector(@selector(outlineView:numberOfChildrenOfItem:)),
NSStringFromSelector(@selector(outlineView:child:ofItem:)),
NSStringFromSelector(@selector(outlineView:isItemExpandable:)),
NSStringFromSelector(@selector(outlineView:objectValueForTableColumn:byItem:))];
// Add the custom mappings first before we add the 'regular' mappings.
[self addCustomMethodNameMappings];
// Now add the 'regular' mappings.
[self addEntriesToMethodForwardingMap:[self methodNameMappingsForProtocol:@protocol(NSOutlineViewDelegate)]];
[self addEntriesToMethodForwardingMap:[self methodNameMappingsForProtocol:@protocol(NSOutlineViewDataSource)]];
}
- (id)initWithSourceList:(PXSourceList *)sourceList
{
_sourceList = sourceList;
return self;
}
- (void)dealloc
{
//Unregister the delegate from receiving notifications
[[NSNotificationCenter defaultCenter] removeObserver:self.delegate name:nil object:self.sourceList];
}
#pragma mark - Accessors
- (void)setDelegate:(id<PXSourceListDelegate>)delegate
{
if (self.delegate)
[[NSNotificationCenter defaultCenter] removeObserver:self.delegate name:nil object:self.sourceList];
_delegate = delegate;
//Register the new delegate to receive notifications
[self registerDelegateToReceiveNotification:PXSLSelectionIsChangingNotification
withSelector:@selector(sourceListSelectionIsChanging:)];
[self registerDelegateToReceiveNotification:PXSLSelectionDidChangeNotification
withSelector:@selector(sourceListSelectionDidChange:)];
[self registerDelegateToReceiveNotification:PXSLItemWillExpandNotification
withSelector:@selector(sourceListItemWillExpand:)];
[self registerDelegateToReceiveNotification:PXSLItemDidExpandNotification
withSelector:@selector(sourceListItemDidExpand:)];
[self registerDelegateToReceiveNotification:PXSLItemWillCollapseNotification
withSelector:@selector(sourceListItemWillCollapse:)];
[self registerDelegateToReceiveNotification:PXSLItemDidCollapseNotification
withSelector:@selector(sourceListItemDidCollapse:)];
[self registerDelegateToReceiveNotification:PXSLDeleteKeyPressedOnRowsNotification
withSelector:@selector(sourceListDeleteKeyPressedOnRows:)];
}
- (void)setDataSource:(id<PXSourceListDataSource>)dataSource
{
_dataSource = dataSource;
}
#pragma mark - NSObject Overrides
- (BOOL)respondsToSelector:(SEL)aSelector
{
NSString *methodName = NSStringFromSelector(aSelector);
// Only let the source list override NSOutlineView delegate and data source methods.
if ([self.sourceList respondsToSelector:aSelector] && ([__outlineViewDataSourceMethods containsObject:methodName] || [__outlineViewDelegateMethods containsObject:methodName]))
return YES;
if ([__requiredOutlineViewDataSourceMethods containsObject:methodName])
return YES;
if ([__fastPathForwardingDelegateMethods containsObject:methodName])
return [self.delegate respondsToSelector:aSelector];
if ([__fastPathForwardingDataSourceMethods containsObject:methodName])
return [self.dataSource respondsToSelector:aSelector];
id forwardingObject = [self forwardingObjectForSelector:aSelector];
NSDictionary *forwardingInformation = [[self class] forwardingInformationForSelector:aSelector];
if(!forwardingObject || !forwardingInformation)
return NO;
return [forwardingObject respondsToSelector:NSSelectorFromString(forwardingInformation[forwardingMapForwardingMethodNameKey])];
}
- (BOOL)conformsToProtocol:(Protocol *)protocol
{
return class_conformsToProtocol(object_getClass(self), protocol);
}
// Fast-path delegate and data source methods aren't handled here; they are taken care of in -forwardingTargetForSelector:.
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSString *methodName = NSStringFromSelector(aSelector);
struct objc_method_description description = {NULL, NULL};
if ([__outlineViewDelegateMethods containsObject:methodName])
description = px_methodDescriptionForProtocolMethod(@protocol(NSOutlineViewDelegate), aSelector);
else if ([__outlineViewDataSourceMethods containsObject:methodName])
description = px_methodDescriptionForProtocolMethod(@protocol(NSOutlineViewDataSource), aSelector);
if (description.name == NULL && description.types == NULL)
return nil;
return [NSMethodSignature signatureWithObjCTypes:description.types];
}
- (BOOL) isKindOfClass:(Class)aClass
{
return NO;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL sourceSelector = anInvocation.selector;
// Give the Source List a chance to handle the selector first (this is a bit of a hack for the time being
// and should be changed).
if ([self.sourceList respondsToSelector:sourceSelector]) {
[anInvocation invokeWithTarget:self.sourceList];
return;
}
id forwardingObject = [self forwardingObjectForSelector:sourceSelector];
NSDictionary *forwardingInformation = [[self class] forwardingInformationForSelector:sourceSelector];
if(!forwardingObject || !forwardingInformation) {
[super forwardInvocation:anInvocation];
return;
}
SEL forwardingSelector = NSSelectorFromString(forwardingInformation[forwardingMapForwardingMethodNameKey]);
NSArray *forwardedArgumentIndexes = forwardingInformation[forwardingMapForwardedArgumentIndexesKey];
anInvocation.selector = forwardingSelector;
NSMethodSignature *methodSignature = [forwardingObject methodSignatureForSelector:forwardingSelector];
/* Catch the case where we have advertised ourselves as responding to a selector required by NSOutlineView
for a valid dataSource but the corresponding PXSourceListDataSource method isn't implemented by the dataSource.
*/
if ([__requiredOutlineViewDataSourceMethods containsObject:NSStringFromSelector(sourceSelector)]
&& ![self.dataSource respondsToSelector:forwardingSelector]) {
return;
}
/* Modify the arguments in the invocation if the source and target selector arguments are different.
The forwardedArgumentIndexes array contains the indexes of arguments in the original invocation that we want
to use in our modified invocation. E.g. @[@0, @2] means take the first and third arguments and only use them
when forwarding. We want to do this when the forwarded selector has a different number of arguments to the
source selector (see +addCustomMethodNameMappings).
Note that this implementation only works if the arguments in `forwardedArgumentIndexes` are monotonically
increasing (which is good enough for now).
*/
if (forwardedArgumentIndexes) {
// self and _cmd are arguments 0 and 1.
NSUInteger invocationArgumentIndex = 2;
for (NSNumber *newArgumentIndex in forwardedArgumentIndexes) {
NSInteger forwardedArgumentIndex = newArgumentIndex.integerValue;
// Handle the case where we want to use (for example) the third argument from the original invocation
// as the second argument of our modified invocation.
if (invocationArgumentIndex != forwardedArgumentIndex) {
NSUInteger argumentSize = 0;
NSGetSizeAndAlignment([methodSignature getArgumentTypeAtIndex:invocationArgumentIndex], &argumentSize, NULL);
void *argument = malloc(argumentSize);
[anInvocation getArgument:argument atIndex:forwardedArgumentIndex + 2]; // Take self and _cmd into account again.
[anInvocation setArgument:argument atIndex:invocationArgumentIndex];
free(argument);
}
invocationArgumentIndex++;
}
}
[anInvocation invokeWithTarget:forwardingObject];
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSString *methodName = NSStringFromSelector(aSelector);
if ([__fastPathForwardingDelegateMethods containsObject:methodName])
return self.delegate;
if ([__fastPathForwardingDataSourceMethods containsObject:methodName])
return self.dataSource;
return nil;
}
#pragma mark - Method Forwarding
+ (NSMutableDictionary *)methodForwardingMap
{
static NSMutableDictionary *_methodForwardingMap = nil;
if (!_methodForwardingMap)
_methodForwardingMap = [[NSMutableDictionary alloc] init];
return _methodForwardingMap;
}
+ (void)addEntriesToMethodForwardingMap:(NSDictionary *)entries
{
NSArray *methodForwardingBlacklist = [self methodForwardingBlacklist];
NSMutableDictionary *methodForwardingMap = [self methodForwardingMap];
for (NSString *key in entries) {
if (![methodForwardingBlacklist containsObject:key] && !methodForwardingMap[key])
methodForwardingMap[key] = entries[key];
}
}
+ (NSDictionary *)methodNameMappingsForProtocol:(Protocol *)protocol
{
NSMutableDictionary *methodNameMappings = [[NSMutableDictionary alloc] init];
NSArray *protocolMethods = px_allProtocolMethods(protocol);
NSString *protocolName = NSStringFromProtocol(protocol);
for (NSDictionary *methodInfo in protocolMethods) {
NSString *methodName = methodInfo[px_protocolMethodNameKey];
NSString *mappedMethodName = [self mappedMethodNameForMethodName:methodName];
if (!mappedMethodName) {
NSLog(@"PXSourceList: couldn't map method %@ from %@", methodName, protocolName);
continue;
}
[methodNameMappings setObject:@{forwardingMapForwardingMethodNameKey: mappedMethodName}
forKey:methodName];
}
return methodNameMappings;
}
+ (NSString *)mappedMethodNameForMethodName:(NSString *)methodName
{
NSString *outlineViewSearchString = @"outlineView";
NSUInteger letterVOffset = [outlineViewSearchString rangeOfString:@"V"].location;
NSCharacterSet *uppercaseLetterCharacterSet = [NSCharacterSet uppercaseLetterCharacterSet];
NSRange outlineViewStringRange = [methodName rangeOfString:outlineViewSearchString options:NSCaseInsensitiveSearch];
// If for some reason we can't map the method name, try to fail gracefully.
if (outlineViewStringRange.location == NSNotFound)
return nil;
BOOL isOCapitalized = [uppercaseLetterCharacterSet characterIsMember:[methodName characterAtIndex:outlineViewStringRange.location]];
BOOL isVCapitalized = [uppercaseLetterCharacterSet characterIsMember:[methodName characterAtIndex:outlineViewStringRange.location + letterVOffset]];
return [methodName stringByReplacingCharactersInRange:outlineViewStringRange
withString:[NSString stringWithFormat:@"%@ource%@ist", isOCapitalized ? @"S" : @"s", isVCapitalized ? @"L" : @"l"]];
}
- (id)forwardingObjectForSelector:(SEL)selector
{
if ([__outlineViewDataSourceMethods containsObject:NSStringFromSelector(selector)])
return self.dataSource;
if ([__outlineViewDelegateMethods containsObject:NSStringFromSelector(selector)])
return self.delegate;
return nil;
}
+ (NSDictionary *)forwardingInformationForSelector:(SEL)selector
{
return [[self methodForwardingMap] objectForKey:NSStringFromSelector(selector)];
}
// These methods won't have mappings created for them.
+ (NSArray *)methodForwardingBlacklist
{
return @[NSStringFromSelector(@selector(outlineView:shouldSelectTableColumn:)),
NSStringFromSelector(@selector(outlineView:shouldReorderColumn:toColumn:)),
NSStringFromSelector(@selector(outlineView:mouseDownInHeaderOfTableColumn:)),
NSStringFromSelector(@selector(outlineView:didClickTableColumn:)),
NSStringFromSelector(@selector(outlineView:didDragTableColumn:)),
NSStringFromSelector(@selector(outlineView:sizeToFitWidthOfColumn:)),
NSStringFromSelector(@selector(outlineView:shouldReorderColumn:toColumn:)),
NSStringFromSelector(@selector(outlineViewColumnDidMove:)),
NSStringFromSelector(@selector(outlineViewColumnDidResize:)),
NSStringFromSelector(@selector(outlineView:isGroupItem:))];
}
/* Add custom mappings for method names which can't have "outlineView" simply replaced with "sourceList".
For example, -outlineView:objectValueForTableColumn:byItem: should be forwarded to -sourceList:objectValueForItem:. We also only want to
forward the 1st and 3rd arguments when invoking this second selector.
*/
+ (void)addCustomMethodNameMappings
{
[self addCustomMethodNameMappingFromSelector:@selector(outlineView:objectValueForTableColumn:byItem:)
toSelector:@selector(sourceList:objectValueForItem:)
forwardedArgumentIndexes:@[@0, @2]];
[self addCustomMethodNameMappingFromSelector:@selector(outlineView:setObjectValue:forTableColumn:byItem:)
toSelector:@selector(sourceList:setObjectValue:forItem:)
forwardedArgumentIndexes:@[@0, @1, @3]];
[self addCustomMethodNameMappingFromSelector:@selector(outlineView:viewForTableColumn:item:)
toSelector:@selector(sourceList:viewForItem:)
forwardedArgumentIndexes:@[@0, @2]];
[self addCustomMethodNameMappingFromSelector:@selector(outlineView:willDisplayCell:forTableColumn:item:)
toSelector:@selector(sourceList:willDisplayCell:forItem:)
forwardedArgumentIndexes:@[@0, @1, @3]];
[self addCustomMethodNameMappingFromSelector:@selector(outlineView:shouldEditTableColumn:item:)
toSelector:@selector(sourceList:shouldEditItem:)
forwardedArgumentIndexes:@[@0, @2]];
[self addCustomMethodNameMappingFromSelector:@selector(outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:)
toSelector:@selector(sourceList:toolTipForCell:rect:item:mouseLocation:)
forwardedArgumentIndexes:@[@0, @1, @2, @4, @5]];
[self addCustomMethodNameMappingFromSelector:@selector(outlineView:typeSelectStringForTableColumn:item:)
toSelector:@selector(sourceList:typeSelectStringForItem:)
forwardedArgumentIndexes:@[@0, @2]];
[self addCustomMethodNameMappingFromSelector:@selector(outlineView:shouldShowCellExpansionForTableColumn:item:)
toSelector:@selector(sourceList:shouldShowCellExpansionForItem:)
forwardedArgumentIndexes:@[@0, @2]];
[self addCustomMethodNameMappingFromSelector:@selector(outlineView:shouldTrackCell:forTableColumn:item:)
toSelector:@selector(sourceList:shouldTrackCell:forItem:)
forwardedArgumentIndexes:@[@0, @1, @3]];
[self addCustomMethodNameMappingFromSelector:@selector(outlineView:dataCellForTableColumn:item:)
toSelector:@selector(sourceList:dataCellForItem:)
forwardedArgumentIndexes:@[@0, @2]];
}
+ (void)addCustomMethodNameMappingFromSelector:(SEL)fromSelector toSelector:(SEL)toSelector forwardedArgumentIndexes:(NSArray *)argumentIndexes
{
[[self methodForwardingMap] setObject:@{forwardingMapForwardingMethodNameKey: NSStringFromSelector(toSelector),
forwardingMapForwardedArgumentIndexesKey: argumentIndexes}
forKey:NSStringFromSelector(fromSelector)];
}
- (BOOL)getForwardingObject:(id*)outObject andForwardingSelector:(SEL*)outSelector forSelector:(SEL)selector
{
NSDictionary *methodForwardingMap = [[self class] methodForwardingMap];
NSString *originalMethodName = NSStringFromSelector(selector);
NSDictionary *forwardingInfo = methodForwardingMap[originalMethodName];
if (!forwardingInfo)
return NO;
id forwardingObject;
if ([__outlineViewDelegateMethods containsObject:originalMethodName])
forwardingObject = self.delegate;
else if ([__outlineViewDataSourceMethods containsObject:originalMethodName])
forwardingObject = self.dataSource;
if (!forwardingObject)
return NO;
if (outObject)
*outObject = forwardingObject;
if (outSelector)
*outSelector = NSSelectorFromString(forwardingInfo[forwardingMapForwardingMethodNameKey]);
return YES;
}
+ (NSArray *)fastPathForwardingDelegateMethods
{
NSMutableArray *methods = [px_methodNamesForProtocol(@protocol(PXSourceListDelegate)) mutableCopy];
// Add the NSControl delegate methods manually (unfortunately these aren't part of a formal protocol).
[methods addObject:px_methodNameForSelector(@selector(controlTextDidEndEditing:))];
[methods addObject:px_methodNameForSelector(@selector(controlTextDidBeginEditing:))];
[methods addObject:px_methodNameForSelector(@selector(controlTextDidChange:))];
return methods;
}
#pragma mark - Notifications
- (void)registerDelegateToReceiveNotification:(NSString*)notification withSelector:(SEL)selector
{
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
//Set the delegate as a receiver of the notification if it implements the notification method
if([self.delegate respondsToSelector:selector]) {
[defaultCenter addObserver:self.delegate
selector:selector
name:notification
object:self.sourceList];
}
}
/* Notification wrappers */
- (void)outlineViewSelectionIsChanging:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLSelectionIsChangingNotification object:self.sourceList];
}
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLSelectionDidChangeNotification object:self.sourceList];
}
- (void)outlineViewItemWillExpand:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLItemWillExpandNotification
object:self.sourceList
userInfo:[notification userInfo]];
}
- (void)outlineViewItemDidExpand:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLItemDidExpandNotification
object:self.sourceList
userInfo:[notification userInfo]];
}
- (void)outlineViewItemWillCollapse:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLItemWillCollapseNotification
object:self.sourceList
userInfo:[notification userInfo]];
}
- (void)outlineViewItemDidCollapse:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLItemDidCollapseNotification
object:self.sourceList
userInfo:[notification userInfo]];
}
@end
@@ -0,0 +1,18 @@
//
// PXSourceListPrivateConstants.h
// PXSourceList
//
// Created by Alex Rozanski on 25/12/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Foundation/Foundation.h>
extern NSString * const PXSLSelectionIsChangingNotification;
extern NSString * const PXSLSelectionDidChangeNotification;
extern NSString * const PXSLItemWillExpandNotification;
extern NSString * const PXSLItemDidExpandNotification;
extern NSString * const PXSLItemWillCollapseNotification;
extern NSString * const PXSLItemDidCollapseNotification;
extern NSString * const PXSLDeleteKeyPressedOnRowsNotification;
@@ -0,0 +1,21 @@
//
// PXSourceListRuntimeAdditions.h
// PXSourceList
//
// Created by Alex Rozanski on 25/12/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
extern NSString * const px_protocolMethodNameKey;
extern NSString * const px_protocolMethodArgumentTypesKey;
extern NSString * const px_protocolIsRequiredMethodKey;
NSArray *px_allProtocolMethods(Protocol *protocol);
NSArray *px_methodNamesForProtocol(Protocol *protocol);
id px_methodNameForSelector(SEL selector);
struct objc_method_description px_methodDescriptionForProtocolMethod(Protocol *protocol, SEL selector);
@@ -0,0 +1,67 @@
//
// PXSourceListRuntimeAdditions.m
// PXSourceList
//
// Created by Alex Rozanski on 25/12/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import "PXSourceListRuntimeAdditions.h"
NSString * const px_protocolMethodNameKey = @"methodName";
NSString * const px_protocolMethodArgumentTypesKey = @"types";
NSString * const px_protocolIsRequiredMethodKey = @"isRequired";
NSArray *px_allProtocolMethods(Protocol *protocol)
{
NSMutableArray *methodList = [[NSMutableArray alloc] init];
// We have 4 permutations as protocol_copyMethodDescriptionList() takes two BOOL arguments for the types of methods to return.
for (NSUInteger i = 0; i < 4; ++i) {
BOOL isRequiredMethod = (i / 2) % 2;
unsigned int numberOfMethodDescriptions = 0;
struct objc_method_description *methodDescriptions = protocol_copyMethodDescriptionList(protocol, isRequiredMethod, i % 2, &numberOfMethodDescriptions);
for (unsigned int j = 0; j < numberOfMethodDescriptions; ++j) {
struct objc_method_description methodDescription = methodDescriptions[j];
[methodList addObject:@{px_protocolMethodNameKey: px_methodNameForSelector(methodDescription.name),
px_protocolMethodArgumentTypesKey: [NSString stringWithUTF8String:methodDescription.types],
px_protocolIsRequiredMethodKey: @(isRequiredMethod)}];
}
free(methodDescriptions);
}
return methodList;
}
NSArray *px_methodNamesForProtocol(Protocol *protocol)
{
NSMutableArray *methodNames = [[NSMutableArray alloc] init];
for (NSDictionary *methodInfo in px_allProtocolMethods(protocol))
[methodNames addObject:methodInfo[px_protocolMethodNameKey]];
return methodNames;
}
id px_methodNameForSelector(SEL selector)
{
return NSStringFromSelector(selector);
}
struct objc_method_description px_methodDescriptionForProtocolMethod(Protocol *protocol, SEL selector)
{
struct objc_method_description description = {NULL, NULL};
// We have 4 permutations to check for.
for (NSUInteger i = 0; i < 4; ++i) {
description = protocol_getMethodDescription(protocol, selector, (i / 2) % 2, i % 2);
if (description.types != NULL && description.name != NULL)
break;
}
return description;
}
+206
View File
@@ -0,0 +1,206 @@
//
// PXSourceList.h
// PXSourceList
//
// Created by Alex Rozanski on 05/09/2009.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Cocoa/Cocoa.h>
#import "PXSourceListDelegate.h"
#import "PXSourceListDataSource.h"
#import "PXSourceListItem.h"
#import "PXSourceListBadgeView.h"
#import "PXSourceListTableCellView.h"
/**
`PXSourceList` is an `NSOutlineView` subclass that makes using a source list (the sidebar
seen in applications such as iTunes and Mail.app) easier by providing common styling and idiomatic
behaviour of source lists for you through a clean and simple API. Notable features of PXSourceList include:
- All root-level, "group" items are displayed using `NSOutlineView`'s group styling by default and requires no additional setup.
- Source List items often display an *icon* and *badge* (for information such as unread counts). This is built into
the API to make configuring these quick and easy, and a badge `NSControl` subclass is included which can be added to
`NSTableCellView` objects when using `PXSourceList` in *view-based* mode to display badges (see the next section for details).
- `PXSourceList` implements support for showing groups as "always expanded" -- where their child items are always shown
and no 'Show'/'Hide' button is displayed when hovering over the group. This is often useful for listing the main
contexts your application can be in at any given time, which the user can select to change views. As it is paramount to
your application's navigation, these groups are often displayed with their child items always shown.
- `PXSourceList` objects operate with only one column and do not display a header, something
which is reflected in the API and makes the control easier to use.
Like `NSOutlineView` and `NSTableView`, a `PXSourceList` object does not store its own data, but retrieves
values from a weakly-referenced data source (see the `PXSourceListDataSource` protocol). A `PXSourceList`
object can also have a delegate, to which it sends messages when certain events occur (see the
`PXSourceListDelegate` protocol for more information).
### Cell-based vs. view-based mode
Like `NSTableView` and `NSOutlineView`, PXSourceList can operate in both cell-based and view-based mode in
relation to how you provide content to be displayed.
When using PXSourceList in cell-based mode, it can manage drawing of icons and badges for you through custom
drawing and `PXSourceListDataSource` methods. However, when using PXSourceList in view-based mode, it can't
do this directly, because cell views are configured independently in Interface Builder (or programmatically)
and configured in the `PXSourceListDataSource` method, `-sourceList:viewForItem:`.
Instead, in view-based mode, you should set up the icon for each item in `-sourceList:viewForItem:` using the
`imageView` property of `NSTableCellView`, and the `badgeView` property if using `PXSourceListTableCellView`
objects to display your content. Additionally, there are several classes provided alongside `PXSourceList`
which make this set up a lot easier:
- `PXSourceListTableCellView`: an `NSTableCellView` subclass which exposes a `badgeView` outlet that can be
hooked up to a `PXSourceListBadgeView` instance (see below) in Interface Builder. Along with `NSTableCellView`
and its `textField` and `imageView` properties, `PXSourceListTableCellView` is an `NSTableCellView` subclass which
allows you to easily display an icon, title and a badge for each item in the Source List without subclassing.
- `PXSourceListBadgeView`: a view class for displaying badges, which can be used in your table cell views and
configured to display a particular badge number. Additionally, instances can be configured to use custom text and
background colours, although it will use the regular Source List styling of light text on a grey-blue background
by default.
### Creating a data source model with `PXSourceListItem`
Like `NSOutlineView`, PXSourceList queries its data source to build up a tree-like structure of content using
`-sourceList:numberOfChildrenOfItem:` and `-sourceList:child:ofItem:`. Often it is practical to store the structure
of your Source List content in a tree structure which can then be easily returned the the Source List using
these two data source methods.
To help with this, the generic `PXSourceListItem` class has been included with PXSourceList which can be
used to build this tree structure. It declares properties such as `title` and `icon` which are useful in
storing display information which can then be used in `-sourceList:viewForItem:` or `-sourceList:objectValueForItem:`,
as well as a `children` property with convenience methods for mutating its list of children. Take a look at the
`PXSourceListItem` documentation for more information, as well as the cell-based and view-based example
projects included for examples of how to use this class in your own projects.
*/
@interface PXSourceList: NSOutlineView <NSOutlineViewDelegate, NSOutlineViewDataSource>
///---------------------------------------------------------------------------------------
/// @name Delegate and Data Source
///---------------------------------------------------------------------------------------
/** Used to set the Source List's data source.
@warning Unfortunately, due to the way that `PXSourceList` is implemented, sending `-dataSource` to the Source List
will return a proxy object which is used internally. As such you should only use this setter and not invoke `-dataSource`
to retrieve the data source object.
@param dataSource An object to use for the data source.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)setDataSource:(id<PXSourceListDataSource>)dataSource;
/** Used to set the Source List's delegate.
@warning Unfortunately, due to the way that `PXSourceList` is implemented, sending `-delegate` to the Source List
will return a proxy object which is used internally. As such you should only use this setter and not invoke `-delegate`
to retrieve the data source object.
@param delegate An object to use for the delegate.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)setDelegate:(id<PXSourceListDelegate>)delegate;
///---------------------------------------------------------------------------------------
/// @name Setting Display Attributes
///---------------------------------------------------------------------------------------
/** Returns the size of icons in points to display in items in the Source List.
@discussion The default value is 16 x 16.
@warning This property only applies when using `PXSourceList` in cell-based mode. If set on a Source List
operating in view-based mode, this value is not used.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
@property (nonatomic, assign) NSSize iconSize;
///---------------------------------------------------------------------------------------
/// @name Working with Groups
///---------------------------------------------------------------------------------------
@property (readonly) NSUInteger numberOfGroups;
/** Returns a Boolean value that indicates whether a given item in the Source List is a group item.
@param item The item to query about.
@return `YES` if *item* exists in the Source List and is a group item, otherwise `NO`.
@discussion "Group" items are defined as root items in the Source List tree hierarchy.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)isGroupItem:(id)item;
/** Returns a Boolean value that indicates whether a given group item in the Source List is always expanded.
@param group The given group item.
@return `YES` if *group* is a group item in the Source List which is displayed as always expanded, or `NO` otherwise.
@discussion "Group" items are defined as root items in the Source List tree hierarchy. A group item that is displayed
as always expanded doesn't show a 'Show'/'Hide' button on hover as with regular group items. It is automatically expanded
when the Source List's data is reloaded and cannot be collapsed.
This method calls the `-sourceList:isGroupAlwaysExpanded:` method on the Source List's delegate to determine
whether the particular group item is displayed as always expanded or not.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)isGroupAlwaysExpanded:(id)group;
///---------------------------------------------------------------------------------------
/// @name Working with Badges
///---------------------------------------------------------------------------------------
/** Returns a Boolean value that indicates whether a given item in the Source List displays a badge.
@param item The given item.
@return `YES` if the Source List is operating in cell-based mode and *item* displays a badge, or `NO` otherwise.
@discussion This method calls the `-sourceList:itemHasBadge:` method on the Source List's delegate to determine
whether the item displays a badge or not.
@warning This method only applies when using a Source List in cell-based mode. If sent to a Source List in view-based mode, this
method returns `NO`.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)itemHasBadge:(id)item;
/** Returns the integer value of the badge for a given item.
@param item The given item.
@return The integer value of the badge for *item* if the Source List is operating in cell-based mode and *item* displays a badge, or `NSNotFound` otherwise.
@discussion This method calls the `-sourceList:badgeValueForItem:` method on the Source List's data source to determine
the item's badge value.
@warning This method only applies when using a Source List in cell-based mode. If sent to a Source List in view-based mode, this
method returns `NSNotFound`.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (NSInteger)badgeValueForItem:(id)item;
/* === Unavailable methods ===
As a side-effect of PXSourceList's internal implementation, these methods shouldn't be used to query the delegate or data
source. I am *always* looking for a way to remove this limitation. Please file an issue at https://github.com/Perspx/PXSourceList if you
have any ideas!
*/
- (id <NSOutlineViewDelegate>)delegate __attribute__((unavailable("-delegate shouldn't be called on PXSourceList. See the documentation for more information.")));
- (id <NSOutlineViewDataSource>)dataSource __attribute__((unavailable("-dataSource shouldn't be called on PXSourceList. See the documentation for more information.")));
@end
+578
View File
@@ -0,0 +1,578 @@
//
// PXSourceList.m
// PXSourceList
//
// Created by Alex Rozanski on 05/09/2009.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import "PXSourceList.h"
#import "PXSourceListBadgeCell.h"
#import "PXSourceListDelegateDataSourceProxy.h"
#import "PXSourceListPrivateConstants.h"
//Layout constants
static const CGFloat minBadgeWidth = 22.0; // The minimum badge width for each item (default 22.0).
static const CGFloat badgeHeight = 14.0; // The badge height for each item (default 14.0).
static const CGFloat rowRightMargin = 5.0; // The spacing between the right edge of the badge and the edge of the table column.
static const CGFloat iconSpacing = 2.0; // The spacing between the icon and it's adjacent cell.
static const CGFloat disclosureTriangleSpace = 18.0; // The indentation reserved for disclosure triangles for non-group items.
//Delegate notification constants
NSString * const PXSLSelectionIsChangingNotification = @"PXSourceListSelectionIsChanging";
NSString * const PXSLSelectionDidChangeNotification = @"PXSourceListSelectionDidChange";
NSString * const PXSLItemWillExpandNotification = @"PXSourceListItemWillExpand";
NSString * const PXSLItemDidExpandNotification = @"PXSourceListItemDidExpand";
NSString * const PXSLItemWillCollapseNotification = @"PXSourceListItemWillCollapse";
NSString * const PXSLItemDidCollapseNotification = @"PXSourceListItemDidCollapse";
NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKeyPressedOnRows";
#pragma mark -
@interface PXSourceList ()
@property (strong, nonatomic) PXSourceListDelegateDataSourceProxy *delegateDataSourceProxy;
@property (strong, readonly) PXSourceListBadgeCell *reusableBadgeCell;
@end
#pragma mark -
@implementation PXSourceList
@synthesize reusableBadgeCell = _reusableBadgeCell;
#pragma mark - Setup/Teardown
- (id)initWithCoder:(NSCoder*)decoder
{
if(self=[super initWithCoder:decoder]) {
[self PXSL_setup];
}
return self;
}
- (id)initWithFrame:(NSRect)frameRect
{
if((self = [super initWithFrame:frameRect])) {
[self PXSL_setup];
}
return self;
}
- (void)PXSL_setup
{
_iconSize = NSMakeSize(16,16);
_delegateDataSourceProxy = [[PXSourceListDelegateDataSourceProxy alloc] initWithSourceList:self];
}
- (void)dealloc
{
_delegateDataSourceProxy = nil;
//Remove ourselves as the delegate and data source to be safe
[super setDataSource:nil];
[super setDelegate:nil];
}
#pragma mark -
#pragma mark Custom Accessors
- (void)setDelegate:(id<PXSourceListDelegate>)aDelegate
{
self.delegateDataSourceProxy.delegate = aDelegate;
[super setDelegate:nil];
if (aDelegate)
[super setDelegate:self.delegateDataSourceProxy];
}
- (void)setDataSource:(id<PXSourceListDataSource>)aDataSource
{
self.delegateDataSourceProxy.dataSource = aDataSource;
[super setDataSource:nil];
if (aDataSource)
[super setDataSource:self.delegateDataSourceProxy];
[self reloadData];
}
- (void)setIconSize:(NSSize)newIconSize
{
_iconSize = newIconSize;
CGFloat rowHeight = [self rowHeight];
//Make sure icon height does not exceed row height; if so constrain, keeping width and height in proportion
if(_iconSize.height>rowHeight)
{
_iconSize.width = _iconSize.width * (rowHeight/_iconSize.height);
_iconSize.height = rowHeight;
}
}
- (PXSourceListBadgeCell *)reusableBadgeCell
{
if (!_reusableBadgeCell)
_reusableBadgeCell = [[PXSourceListBadgeCell alloc] init];
return _reusableBadgeCell;
}
- (BOOL)floatsGroupRows
{
return NO;
}
- (void)setFloatsGroupRows:(BOOL)value
{
[super setFloatsGroupRows:NO];
}
#pragma mark -
#pragma mark Data Management
- (BOOL)isViewBasedSourceList
{
return [self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:viewForItem:)];
}
- (void)reloadData
{
[super reloadData];
//Expand items that are displayed as always expanded
if([self.delegateDataSourceProxy conformsToProtocol:@protocol(PXSourceListDataSource)] &&
[self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:isGroupAlwaysExpanded:)])
{
for(NSUInteger i=0;i<[self numberOfGroups];i++)
{
id item = [self.delegateDataSourceProxy sourceList:self child:i ofItem:nil];
if([self isGroupAlwaysExpanded:item]) {
[self expandItem:item expandChildren:NO];
}
}
}
//If there are selected rows and the item hierarchy has changed, make sure a Group row isn't
//selected
if([self numberOfSelectedRows]>0) {
NSIndexSet *selectedIndexes = [self selectedRowIndexes];
NSUInteger firstSelectedRow = [selectedIndexes firstIndex];
//Is a group item selected?
if([self isGroupItem:[self itemAtRow:firstSelectedRow]]) {
//Work backwards to find the first non-group row
BOOL foundRow = NO;
for(NSUInteger i=firstSelectedRow;i>0;i--)
{
if(![self isGroupItem:[self itemAtRow:i]]) {
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:i] byExtendingSelection:NO];
foundRow = YES;
break;
}
}
//If there is no non-group row preceding the currently selected group item, remove the selection
//from the Source List
if(!foundRow) {
[self deselectAll:self];
}
}
}
else if(![self allowsEmptySelection]&&[self numberOfSelectedRows]==0)
{
//Select the first non-group row if no rows are selected, and empty selection is disallowed
for(NSUInteger i=0;i<[self numberOfRows];i++)
{
if(![self isGroupItem:[self itemAtRow:i]]) {
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:i] byExtendingSelection:NO];
break;
}
}
}
}
- (NSUInteger)numberOfGroups
{
if([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:numberOfChildrenOfItem:)]) {
return [self.delegateDataSourceProxy sourceList:self numberOfChildrenOfItem:nil];
}
return 0;
}
- (BOOL)isGroupItem:(id)item
{
//Groups are defined as root items (at level 0)
return 0==[self levelForItem:item];
}
- (BOOL)isGroupAlwaysExpanded:(id)group
{
//Make sure that the item IS a group to prevent unwanted queries sent to the data source
if([self isGroupItem:group]) {
//Query the data source
if([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:isGroupAlwaysExpanded:)]) {
return [self.delegateDataSourceProxy sourceList:self isGroupAlwaysExpanded:group];
}
}
return NO;
}
- (BOOL)itemHasBadge:(id)item
{
// Since badges are managed by custom views and logic in view-based mode, we can't determine this.
if (self.isViewBasedSourceList)
return NO;
if([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:itemHasBadge:)]) {
return [self.delegateDataSourceProxy sourceList:self itemHasBadge:item];
}
return NO;
}
- (NSInteger)badgeValueForItem:(id)item
{
// Since badges are managed by custom views and logic in view-based mode, we can't determine this.
if (self.isViewBasedSourceList || ![self itemHasBadge:item])
return NSNotFound;
if([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:badgeValueForItem:)]) {
return [self.delegateDataSourceProxy sourceList:self badgeValueForItem:item];
}
return NSNotFound;
}
#pragma mark -
#pragma mark Selection Handling
- (void)selectRowIndexes:(NSIndexSet*)indexes byExtendingSelection:(BOOL)extend
{
NSUInteger numberOfIndexes = [indexes count];
//Prevent empty selection if we don't want it
if(![self allowsEmptySelection]&&0==numberOfIndexes) {
return;
}
//Would use blocks but we're also targeting 10.5...
//Get the selected indexes
NSUInteger *selectedIndexes = malloc(sizeof(NSUInteger)*numberOfIndexes);
[indexes getIndexes:selectedIndexes maxCount:numberOfIndexes inIndexRange:nil];
//Loop through the indexes and only add non-group row indexes
//Allows selection across groups without selecting the group rows
NSMutableIndexSet *newSelectionIndexes = [NSMutableIndexSet indexSet];
for(NSInteger i=0;i<numberOfIndexes;i++)
{
if(![self isGroupItem:[self itemAtRow:selectedIndexes[i]]]) {
[newSelectionIndexes addIndex:selectedIndexes[i]];
}
}
//If there are any non-group rows selected
if([newSelectionIndexes count]>0) {
[super selectRowIndexes:newSelectionIndexes byExtendingSelection:extend];
}
//C memory management... *sigh*
free(selectedIndexes);
}
#pragma mark -
#pragma mark Layout
- (NSRect)frameOfOutlineCellAtRow:(NSInteger)row
{
//Return a zero-rect if the item is always expanded (a disclosure triangle will not be drawn)
if([self isGroupAlwaysExpanded:[self itemAtRow:row]]) {
return NSZeroRect;
}
NSRect frame = [super frameOfOutlineCellAtRow:row];
if([self levelForRow:row] > 0) {
frame.origin.x = [self levelForRow:row] * [self indentationPerLevel];
}
return frame;
}
- (NSRect)frameOfCellAtColumn:(NSInteger)column row:(NSInteger)row
{
if (self.isViewBasedSourceList)
return [super frameOfCellAtColumn:column row:row];
id item = [self itemAtRow:row];
NSCell *cell = [self preparedCellAtColumn:column row:row];
NSSize cellSize = [cell cellSize];
if (!([cell type] == NSImageCellType) && !([cell type] == NSTextCellType))
cellSize = [cell cellSizeForBounds:[super frameOfCellAtColumn:column row:row]];
NSRect cellFrame = [super frameOfCellAtColumn:column row:row];
NSRect rowRect = [self rectOfRow:row];
if([self isGroupItem:item])
{
CGFloat minX = NSMinX(cellFrame);
//Set the origin x-coord; if there are no children of the group at current, there will still be a
//margin to the left of the cell (in cellFrame), which we don't want
if([self isGroupAlwaysExpanded:[self itemAtRow:row]]) {
minX = 7;
}
return NSMakeRect(minX,
NSMidY(cellFrame)-(cellSize.height/2.0),
NSWidth(rowRect)-minX,
cellSize.height);
}
else
{
CGFloat leftIndent = [self levelForRow:row]*[self indentationPerLevel]+disclosureTriangleSpace;
//Calculate space left for a badge if need be
CGFloat rightIndent = [self sizeOfBadgeAtRow:row].width+rowRightMargin;
//Allow space for an icon if need be
if(![self isGroupItem:item] && [self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:itemHasIcon:)])
{
if([self.delegateDataSourceProxy sourceList:self itemHasIcon:item]) {
leftIndent += [self iconSize].width+(iconSpacing*2);
}
}
return NSMakeRect(leftIndent,
NSMidY(rowRect)-(cellSize.height/2.0),
NSWidth(rowRect)-rightIndent-leftIndent,
cellSize.height);
}
}
//This method calculates and returns the size of the badge for the row index passed to the method. If the
//row for the row index passed to the method does not have a badge, then NSZeroSize is returned.
- (NSSize)sizeOfBadgeAtRow:(NSInteger)rowIndex
{
id rowItem = [self itemAtRow:rowIndex];
if (![self itemHasBadge:rowItem])
return NSZeroSize;
self.reusableBadgeCell.integerValue = [self badgeValueForItem:rowItem];
return NSMakeSize(fmax(self.reusableBadgeCell.cellSize.width, minBadgeWidth), badgeHeight);
}
- (void)viewDidMoveToSuperview
{
//If set to YES, this will cause display issues in Lion where the right part of the outline view is cut off
[self setAutoresizesOutlineColumn:NO];
}
#pragma mark -
#pragma mark Drawing
- (void)drawRow:(NSInteger)rowIndex clipRect:(NSRect)clipRect
{
[super drawRow:rowIndex clipRect:clipRect];
// We only do drawing here if the Source List is cell-based.
if (self.isViewBasedSourceList)
return;
id item = [self itemAtRow:rowIndex];
//Draw an icon if the item has one
if(![self isGroupItem:item] && [self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:itemHasIcon:)])
{
if([self.delegateDataSourceProxy sourceList:self itemHasIcon:item])
{
NSRect cellFrame = [self frameOfCellAtColumn:0 row:rowIndex];
NSSize iconSize = [self iconSize];
NSRect iconRect = NSMakeRect(NSMinX(cellFrame)-iconSize.width-iconSpacing,
NSMidY(cellFrame)-(iconSize.height/2.0f),
iconSize.width,
iconSize.height);
if([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:iconForItem:)])
{
NSImage *icon = [self.delegateDataSourceProxy sourceList:self iconForItem:item];
if(icon!=nil)
{
NSSize actualIconSize = [icon size];
//If the icon is *smaller* than the size retrieved from the -iconSize property, make sure we
//reduce the size of the rectangle to draw the icon in, so that it is not stretched.
if((actualIconSize.width<iconSize.width)||(actualIconSize.height<iconSize.height))
{
iconRect = NSMakeRect(NSMidX(iconRect)-(actualIconSize.width/2.0f),
NSMidY(iconRect)-(actualIconSize.height/2.0f),
actualIconSize.width,
actualIconSize.height);
}
[icon drawInRect:iconRect
fromRect:NSZeroRect
operation:NSCompositeSourceOver
fraction:1
respectFlipped:YES hints:nil];
}
}
}
}
//Draw the badge if the item has one
if([self itemHasBadge:item])
{
NSRect rowRect = [self rectOfRow:rowIndex];
NSSize badgeSize = [self sizeOfBadgeAtRow:rowIndex];
NSRect badgeFrame = NSMakeRect(NSMaxX(rowRect)-badgeSize.width-rowRightMargin,
NSMidY(rowRect)-(badgeSize.height/2.0),
badgeSize.width,
badgeSize.height);
[self drawBadgeForRow:rowIndex inRect:badgeFrame];
}
}
- (void)drawBadgeForRow:(NSInteger)rowIndex inRect:(NSRect)badgeFrame
{
id rowItem = [self itemAtRow:rowIndex];
self.reusableBadgeCell.badgeValue = [self badgeValueForItem:rowItem];
if ([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:badgeTextColorForItem:)])
self.reusableBadgeCell.textColor = [self.delegateDataSourceProxy sourceList:self badgeTextColorForItem:rowItem];
if ([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:badgeBackgroundColorForItem:)])
self.reusableBadgeCell.textColor = [self.delegateDataSourceProxy sourceList:self badgeBackgroundColorForItem:rowItem];
self.reusableBadgeCell.highlighted = [self.selectedRowIndexes containsIndex:rowIndex];
[self.reusableBadgeCell drawWithFrame:badgeFrame inView:self];
}
#pragma mark -
#pragma mark Keyboard Handling
- (void)keyDown:(NSEvent *)theEvent
{
NSIndexSet *selectedIndexes = [self selectedRowIndexes];
NSString *keyCharacters = [theEvent characters];
//Make sure we have a selection
if([selectedIndexes count]>0)
{
if([keyCharacters length]>0)
{
unichar firstKey = [keyCharacters characterAtIndex:0];
if(firstKey==NSUpArrowFunctionKey||firstKey==NSDownArrowFunctionKey)
{
//Handle keyboard navigation across groups
if([selectedIndexes count]==1&&!([theEvent modifierFlags] & NSShiftKeyMask))
{
int delta = firstKey==NSDownArrowFunctionKey?1:-1; //Search "backwards" if up arrow, "forwards" if down
NSInteger newRow = [selectedIndexes firstIndex];
//Keep incrementing/decrementing the row until a non-header row is reached
do {
newRow+=delta;
//If out of bounds of the number of rows..
if(newRow<0||newRow==[self numberOfRows])
break;
} while([self isGroupItem:[self itemAtRow:newRow]]);
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:newRow] byExtendingSelection:NO];
return;
}
}
else if(firstKey==NSDeleteCharacter||firstKey==NSBackspaceCharacter||firstKey==0xf728)
{
//Post the notification
[[NSNotificationCenter defaultCenter] postNotificationName:PXSLDeleteKeyPressedOnRowsNotification
object:self
userInfo:[NSDictionary dictionaryWithObject:selectedIndexes forKey:@"rows"]];
return;
}
}
}
//We don't care about it
[super keyDown:theEvent];
}
#pragma mark -
#pragma mark Menu Handling
- (NSMenu *)menuForEvent:(NSEvent *)theEvent
{
NSMenu * m = nil;
if([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:menuForEvent:item:)]) {
NSPoint clickPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
NSInteger row = [self rowAtPoint:clickPoint];
id clickedItem = [self itemAtRow:row];
m = [self.delegateDataSourceProxy sourceList:self menuForEvent:theEvent item:clickedItem];
}
if (m == nil) {
m = [super menuForEvent:theEvent];
}
return m;
}
#pragma mark - Custom NSOutlineView Delegate Method Implementations
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldCollapseItem:(id)item
{
// Make sure the item isn't displayed as always expanded
if([self isGroupItem:item] && [self isGroupAlwaysExpanded:item])
return NO;
if([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:shouldCollapseItem:)])
return [self.delegateDataSourceProxy sourceList:self shouldCollapseItem:item];
return YES;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
{
// Make sure that the item isn't a group as they can't be selected
if(![self isGroupItem:item] && [self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:shouldSelectItem:)])
return [self.delegateDataSourceProxy sourceList:self shouldSelectItem:item];
return ![self isGroupItem:item];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
if(![self isGroupItem:item] && [self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:shouldEditItem:)])
return [self.delegateDataSourceProxy sourceList:self shouldEditItem:item];
return ![self isGroupItem:item];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
{
return [self isGroupItem:item];
}
@end
+23
View File
@@ -0,0 +1,23 @@
//
// PXSourceListBadgeCell.h
// PXSourceList
//
// Created by Alex Rozanski on 15/11/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Cocoa/Cocoa.h>
/* This is the cell which backs drawing done by PXSourceListBadgeView, and is used internally for
drawing badges when PXSourceList is used in cell-based mode.
You shouldn't need to interact with this class directly.
*/
@interface PXSourceListBadgeCell : NSCell
@property (strong, nonatomic) NSColor *textColor;
@property (strong, nonatomic) NSColor *backgroundColor;
@property (assign, nonatomic) NSUInteger badgeValue;
@end
+122
View File
@@ -0,0 +1,122 @@
//
// PXSourceListBadgeCell.m
// PXSourceList
//
// Created by Alex Rozanski on 15/11/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import "PXSourceListBadgeCell.h"
#import "PXSourceList.h"
//Drawing constants
static inline NSColor *badgeBackgroundColor() { return [NSColor colorWithCalibratedRed:(152/255.0) green:(168/255.0) blue:(202/255.0) alpha:1]; }
static inline NSColor *badgeHiddenBackgroundColor() { return [NSColor colorWithDeviceWhite:(180/255.0) alpha:1]; }
static inline NSColor *badgeSelectedTextColor() { return [NSColor keyboardFocusIndicatorColor]; }
static inline NSColor *badgeSelectedUnfocusedTextColor() { return [NSColor colorWithCalibratedRed:(153/255.0) green:(169/255.0) blue:(203/255.0) alpha:1]; }
static inline NSColor *badgeSelectedHiddenTextColor() { return [NSColor colorWithCalibratedWhite:(170/255.0) alpha:1]; }
static inline NSFont *badgeFont() { return [NSFont boldSystemFontOfSize:11]; }
// Sizing constants.
static const CGFloat badgeLeftAndRightPadding = 5.0;
@implementation PXSourceListBadgeCell
- (id)init
{
if (!(self = [super initTextCell:@""]))
return nil;
return self;
}
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
CGFloat borderRadius = NSHeight(cellFrame)/2.0f;
NSBezierPath *badgePath = [NSBezierPath bezierPathWithRoundedRect:cellFrame xRadius:borderRadius yRadius:borderRadius];
// Get the window and control state to determine the badge colours used.
BOOL isMainWindowVisible = [[NSApp mainWindow] isVisible];
NSDictionary *attributes;
NSColor *backgroundColor;
if(self.isHighlighted || self.backgroundStyle == NSBackgroundStyleDark) {
backgroundColor = [NSColor whiteColor];
NSResponder *firstResponder = controlView.window.firstResponder;
BOOL isFocused = NO;
// Starting with the closest ancestor of the control view and the first responder (to make sure both views are in the
// same subtree of the view hierarchy), keep going up the view hierarchy until we hit a PXSourceList instance to tell
// if the source list is focused.
// This covers both the cell-based and view-based cases as well as if a child view of the NSTableCellView (such as
// a text field) is focused.
if ([firstResponder isKindOfClass:[NSView class]]) {
NSView *view = [(NSView*)firstResponder ancestorSharedWithView:controlView];
do {
if ([view isKindOfClass:[PXSourceList class]]) {
isFocused = YES;
break;
}
} while ((view = [view superview]));
}
NSColor *textColor;
if (isMainWindowVisible && isFocused)
textColor = badgeSelectedTextColor();
else if (isMainWindowVisible && !isFocused)
textColor = badgeSelectedUnfocusedTextColor();
else
textColor = badgeSelectedHiddenTextColor();
attributes = @{NSForegroundColorAttributeName: textColor};
} else {
NSColor *textColor = textColor = self.textColor ? self.textColor : [NSColor whiteColor];;
if(isMainWindowVisible)
backgroundColor = self.backgroundColor ? self.backgroundColor : badgeBackgroundColor();
else
backgroundColor = badgeHiddenBackgroundColor();
attributes = @{NSForegroundColorAttributeName: textColor};
}
[backgroundColor set];
[badgePath fill];
//Draw the badge text
NSMutableAttributedString *badgeString = [self.badgeString mutableCopy];
[badgeString addAttributes:attributes range:NSMakeRange(0, badgeString.length)];
NSSize stringSize = badgeString.size;
NSPoint badgeTextPoint = NSMakePoint(NSMidX(cellFrame) - (stringSize.width/2.0),
NSMidY(cellFrame) - (stringSize.height/2.0));
[badgeString drawAtPoint:badgeTextPoint];
}
- (NSSize)cellSize
{
NSSize size = self.badgeString.size;
size.width += 2 * badgeLeftAndRightPadding;
return size;
}
- (NSAttributedString *)badgeString
{
return [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%ld", self.badgeValue]
attributes:@{NSFontAttributeName: badgeFont()}];
}
- (id)accessibilityAttributeValue:(NSString *)attribute
{
if ([attribute isEqualToString:NSAccessibilityValueAttribute])
return @(_badgeValue).description;
else
return [super accessibilityAttributeValue:attribute];
}
@end
+72
View File
@@ -0,0 +1,72 @@
//
// PXSourceListBadgeView.h
// PXSourceList
//
// Created by Alex Rozanski on 15/11/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Cocoa/Cocoa.h>
/**
`PXSourceListBadgeView` is an `NSControl` subclass which can be used for displaying Source List badges.
@discussion Instances of this class can be used by table cell views which are used to display content when
using `PXSourceList` in view-based mode. These table cell views have to be set up to display badges in the
`PXSourceListDataSource` method `-sourceList:viewForItem:` unlike when using `PXSourceList` in cell-based mode,
where this is done automatically behind-the-scenes.
`PXSourceListTableCellView` has an outlet for a `PXSourceListBadgeView` instance which can be hooked up in Interface
Builder or set in code.
### Display customisation
`PXSourceListBadgeView` displays badges like a 'system default' Source List such as in Mail.app with a grey-blue
background and light text. However, the colours used for the badge value and the background colour of the badge can be
changed by using the `textColor` and `backgroundColor` properties.
@warning Note that the `textColor` and `backgroundColor` properties are only respected when the row displaying the badge
isn't highlighted. When the row is highlighted, the badge is displayed with a white background and a blue text colour.
*/
@interface PXSourceListBadgeView : NSControl
///---------------------------------------------------------------------------------------
/// @name Reading and setting the badge value
///---------------------------------------------------------------------------------------
/**
@brief Returns the numeric value displayed by the receiver.
@since Requires the Mac OS X 10.7 SDK or above.
*/
@property (assign, nonatomic) NSUInteger badgeValue;
///---------------------------------------------------------------------------------------
/// @name Customising the badge appearance
///---------------------------------------------------------------------------------------
/**
@brief Returns the custom text colour used to display the receiver.
@discussion The default value for this property is `nil`. Set this property to `nil` to use the default light badge text colour.
@see backgroundColor
@warning Note that this property is only respected when the row displaying the badge isn't highlighted. When the row is highlighted, the badge is displayed with a blue text colour.
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
@property (strong, nonatomic) NSColor *textColor;
/**
@brief Returns the custom background colour used to display the receiver.
@discussion The default value for this property is `nil`. Set this property to `nil` to use the default grey-blue badge background colour.
@see textColor
@warning Note that this property is only respected when the row displaying the badge isn't highlighted. When the row is highlighted, the badge is displayed with a white background colour.
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
@property (strong, nonatomic) NSColor *backgroundColor;
@end
+52
View File
@@ -0,0 +1,52 @@
//
// PXSourceListBadgeView.m
// PXSourceList
//
// Created by Alex Rozanski on 15/11/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import "PXSourceListBadgeView.h"
#import "PXSourceListBadgeCell.h"
@implementation PXSourceListBadgeView
+ (Class)cellClass
{
return [PXSourceListBadgeCell class];
}
#pragma mark - Custom Accessors
- (void)setBadgeValue:(NSUInteger)badgeValue
{
[self.cell setBadgeValue:badgeValue];
}
- (NSUInteger)badgeValue
{
return [self.cell badgeValue];
}
- (NSColor *)textColor
{
return [self.cell textColor];
}
- (void)setTextColor:(NSColor *)textColor
{
[self.cell setTextColor:textColor];
}
- (NSColor *)backgroundColor
{
return [self.cell backgroundColor];
}
- (void)setBackgroundColor:(NSColor *)backgroundColor
{
[self.cell setBackgroundColor:backgroundColor];
}
@end
+396
View File
@@ -0,0 +1,396 @@
//
// PXSourceListDataSource.h
// PXViewKit
//
// Created by Alex Rozanski on 17/10/2009.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Cocoa/Cocoa.h>
@class PXSourceList;
/**
The `PXSourceListDataSource` protocol defines methods that can be implemented by data sources of `PXSourceList` objects.
Despite many of these methods being optional in their implementation, several methods **must** be implemented by a data source of a `PXSourceList` object. These are:
- `sourceList:numberOfChildrenOfItem:`
- `sourceList:child:ofItem:`
- `sourceList:isItemExpandable:`
- `sourceList:objectValueForItem:` (although this is optional if the Source List is operating in view-based mode).
### PXSourceList in View-based mode
As with `NSOutlineView`, `PXSourceList` can operate in cell-based or view-based mode. Of particular note, the
following `PXSourceListDataSource` methods are *not* used by `PXSourceList` when operating in view-based mode.
- `-sourceList:itemHasBadge:`
- `-sourceList:badgeValueForItem:`
- `-sourceList:badgeTextColorForItem:`
- `-sourceList:badgeBackgroundColorForItem:`
- `-sourceList:itemHasIcon:`
- `-sourceList:iconForItem:`
These properties can be configured in the `PXSourceListDelegate` protocol method, `-sourceList:viewForItem:`.
@warning Most of the methods defined by this protocol are analagous to those declared by `NSOutlineViewDataSource` (and are marked as such in the member's documentation), but are prefixed by "sourceList:" instead of "outlineView:". Only the most basic information about these methods is included here, and you should refer to the `NSOutlineViewDataSource` protocol documentation for more information.
*/
@protocol PXSourceListDataSource <NSObject>
@required
///---------------------------------------------------------------------------------------
/// @name Working with Items in a Source List
///---------------------------------------------------------------------------------------
/**
@brief Returns the number of child items of a given item.
@param sourceList The Source List that sent the message.
@param item An item in the data source.
@return The number of immediate child items of *item*. If *item* is `nil` then you should return the number of top-level items in the Source List item hierarchy.
@since Requires PXSourceList 0.8 and above and the OS X v10.5 SDK or above.
@see sourceList:child:ofItem:
*/
- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item;
/**
@brief Returns the direct child of a given item at the specified index.
@param aSourceList The Source List that sent the message.
@param index The index of the child item of *item* to return.
@param item An item in the data source.
@return The immediate child of *item* at the specified *index*. If *item* is `nil`, then return the top-level item with index of *index*.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
@see sourceList:numberOfChildrenOfItem:
*/
- (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item;
/**
@brief Returns a Boolean value indicating whether a given item in the Source List is expandable.
@discussion An expandable item is one which contains child items, and can be expanded to display these. Additionally, if a group item is always displayed as expanded (denoted by `-sourceList:isGroupAlwaysExpanded:` from the `PXSourceListDelegate` protocol) then you must return `YES` from this method for the given group item.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return `YES` if *item* can be expanded, or `NO` otherwise.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item;
@optional
/**
@brief Returns the data object associated with a given item.
@discussion When using the Source List in cell-based mode, returning the text to be displayed for cells representing Group items, the Source List will *not* transform the titles to uppercase so that they display like in iTunes or iCal, such as "LIBRARY". This is to account for edge cases such as words like "iTunes" which should be capitalized as "iTUNES" and so to do this you must pass uppercase titles yourself. It is strongly recommended that text displayed for group items is uppercased in this way, to fit the conventional style of Source List Group headers.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return The data object associated with `item`.
@warning This is a required method when using the Source List in cell-based mode.
@see sourceList:setObjectValue:forItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item;
/**
@brief Sets the associated object value of a specified item.
@discussion This method must be implemented if the Source List is operating in cell-based mode and any items in the Source List are editable.
@param aSourceList The Source List that sent the message.
@param object The new object value for the given item.
@param item An item in the data source.
@see sourceList:objectValueForItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item;
///---------------------------------------------------------------------------------------
/// @name Working with Badges
///---------------------------------------------------------------------------------------
/**
@brief Returns a Boolean specifying whether a given item shows a badge or not.
@discussion This method can be implemented by the data source to specify whether a given item displays a badge or not. A badge is a rounded rectangle containing a number (the badge value), displayed to the right of a row's cell.
This method must be implemented for the other badge-related data source methods sourceList:badgeValueForItem:, sourceList:badgeTextColorForItem: and sourceList:badgeBackgroundColorForItem: to be called.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return `YES` if *item* should display a badge, or `NO` otherwise.
@warning This method is only used by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, the view for each cell is responsible for managing a badge, if applicable.
@see sourceList:badgeValueForItem:
@see sourceList:badgeTextColorForItem:
@see sourceList:badgeBackgroundColorForItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item;
/**
@brief Returns an integer specifying the badge value for a particular item.
@discussion This method can be implemented by the data source to specify a badge value for any particular item. If you want an item to display a badge, you must also implement sourceList:itemHasBadge: and return `YES` for that item. Returning `NO` for items in sourceList:itemHasBadge: means that this method will not be called for that item.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return The badge value for *item*.
@warning This method is only used by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, the view for each cell is responsible for managing a badge, if applicable.
@see sourceList:itemHasBadge:
@see sourceList:badgeTextColorForItem:
@see sourceList:badgeBackgroundColorForItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item;
/**
@brief Returns a color that is used for the badge text color of an item in the Source List.
@discussion This method can be implemented by the data source to specify a custom badge color for a particular item.
This method is only called for *item* if you return `YES` for *item* in sourceList:itemHasBadge:.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return An `NSColor` object to use for the text color of *item*'s badge or `nil` to use the default badge text color.
@warning This method is only used by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, the view for each cell is responsible for managing a badge, if applicable.
@see sourceList:itemHasBadge:
@see sourceList:badgeValueForItem:
@see sourceList:badgeBackgroundColorForItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (NSColor*)sourceList:(PXSourceList*)aSourceList badgeTextColorForItem:(id)item;
/**
@brief Returns a color that is used for the badge background color of an item in the Source List.
@discussion This method can be implemented by the data source to specify a custom badge background color for a particular item.
This method is only called for *item* if you return `YES` for *item* in sourceList:itemHasBadge:.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return An `NSColor` object to use for the background color of *item*'s badge or `nil` to use the default badge background color.
@warning This method is only used by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, the view for each cell is responsible for managing a badge, if applicable.
@see sourceList:itemHasBadge:
@see sourceList:badgeValueForItem:
@see sourceList:badgeTextColorForItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (NSColor*)sourceList:(PXSourceList*)aSourceList badgeBackgroundColorForItem:(id)item;
///---------------------------------------------------------------------------------------
/// @name Working with Icons
///---------------------------------------------------------------------------------------
/**
@brief Returns a Boolean value that indicates whether a given item shows an icon or not.
@discussion This method can be implemented by the data source to specify whether items contain icons or not. Icons are images which are shown to the left of the row's cell, and provide a visual which accompanies the cell.
This method must be implemented if you want to return an icon with `sourceList:iconForItem:`.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return `YES` if *item* displays an icon, or `NO` otherwise.
@warning This method is only used and invoked by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, the view for each cell is responsible for managing its icon, if applicable.
@see sourceList:iconForItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item;
/**
@brief Returns the icon for a given item in the Source List.
@discussion This method must be implemented by the data source if you return `YES` in `sourceList:itemHasIcon:` for any item in the Source List.
The maximum size of each icon is specified with the Source List's `iconSize` property. If the returned image is larger than the icon size property on the Source List, then it is proportionally resized down to fit this size.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return An `NSImage` that is to be used for the icon for *item*.
@warning This method is only used and invoked by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, the view for each cell is responsible for managing its icon, if applicable.
@see sourceList:itemHasIcon:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item;
//The rest of these methods are basically "wrappers" for the NSOutlineViewDataSource methods
///---------------------------------------------------------------------------------------
/// @name Supporting Object Persistence
///---------------------------------------------------------------------------------------
/**
@brief Invoked by *aSourceList* to return the item for the archived *object*.
@discussion This method is analagous to `-outlineView:itemForPersistentObject:` declared on `NSOutlineViewDataSource`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message
@param object The archived representation of the item in the Source List's data source
@return The unarchived item corresponding to *object*.
@see sourceList:persistentObjectForItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (id)sourceList:(PXSourceList*)aSourceList itemForPersistentObject:(id)object;
/**
@brief Invoked by *aSourceList* to return an archived object for *item*.
@discussion This method is analagous to `-outlineView:persistentObjectForItem:` declared on `NSOutlineViewDataSource`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return The unarchived item corresponding to *object*.
@see sourceList:persistentObjectForItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (id)sourceList:(PXSourceList*)aSourceList persistentObjectForItem:(id)item;
///---------------------------------------------------------------------------------------
/// @name Supporting Drag and Drop
///---------------------------------------------------------------------------------------
/**
@brief Returns a Boolean value indicating whether a drag operation is allowed.
@discussion This method is analagous to `-outlineView:writeItems:toPasteboard:` declared on `NSOutlineViewDataSource`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param items An array of items that are participating in the drag.
@param pboard The pasteboard to which to write the drag data.
@return `YES` if the drag should be allowed and the items were successfully written to the pasteboard, or `NO` otherwise.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard;
/**
@brief Used by a Source List to determine a valid drop target.
@discussion This method is analagous to `-outlineView:validateDrop:proposedItem:proposedChildIndex:` declared on `NSOutlineViewDataSource`. See the documentation for this method for more information.
@param sourceList The Source List that sent the message.
@param info An object which contains more information about the dragging operation.
@param item The proposed parent item.
@param index The proposed child index of the parent.
@return An `NSDragOperation` value that indicates which dragging operation the Source List should perform.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (NSDragOperation)sourceList:(PXSourceList*)sourceList validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index;
/**
@brief Returns a Boolean value specifying whether a drag operation was successful.
@discussion This method is analagous to `-outlineView:acceptDrop:item:childIndex:` declared on `NSOutlineViewDataSource`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param info An object that contains more information about the dragging operation.
@param item The parent of the item which the cursor was over when the mouse button was released.
@param index The index of the child of `item` which the cursor was over when the mouse button was released.
@return `YES` if the drop was successful, or `NO` otherwise.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index;
/**
@brief Returns an array of filenames (not file paths) for the created files that the receiver promises to create.
@discussion This method is analagous to `-outlineView:namesOfPromisedFilesDroppedAtDestination:forDraggedItems:` declared on `NSOutlineViewDataSource`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param dropDestination The drop location where the files are created.
@param items The items that are being dragged.
@return An array of filenames (not file paths) for the created files that the receiver promises to create.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (NSArray *)sourceList:(PXSourceList*)aSourceList namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination forDraggedItems:(NSArray *)items;
///---------------------------------------------------------------------------------------
/// @name Drag and drop methods for 10.7+
///---------------------------------------------------------------------------------------
/**
@brief Invoked to allow the Source List to support multiple item dragging.
@discussion This method is analagous to `-outlineView:pasteboardWriterForItem:` declared on `NSOutlineViewDataSource`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return Returns an instance of `NSPasteboardItem` or a custom object that implements the `NSPasteboardWriting` protocol. Returning `nil` excludes the item from being dragged.
@since Requires PXSourceList 0.8 or above and the OS X v10.7 SDK or above.
*/
- (id <NSPasteboardWriting>)sourceList:(PXSourceList *)aSourceList pasteboardWriterForItem:(id)item;
/**
@brief Implement this method know when the given dragging session is about to begin and potentially modify the dragging session.
@discussion This method is analagous to `-outlineView:draggingSession:willBeginAtPoint:forItems:` declared on `NSOutlineViewDataSource`. See the documentation for this method for more information.
@param aSourceList The Source List in which the drag is about to begin.
@param session The dragging session that is about to begin.
@param screenPoint The point onscreen at which the drag is to begin.
@param draggedItems An array of items to be dragged, excluding items for which `sourceList:pasteboardWriterForItem:` returns `nil`.
@since Requires PXSourceList 0.8 or above and the OS X v10.7 SDK or above.
*/
- (void)sourceList:(PXSourceList *)aSourceList draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint forItems:(NSArray *)draggedItems;
/**
@brief Implement this method to know when the given dragging session has ended.
@discussion This method is analagous to `-outlineView:draggingSession:endedAtPoint:operation:` declared on `NSOutlineViewDataSource`. See the documentation for this method for more information.
@param aSourceList The Source List in which the drag ended.
@param session The dragging session that ended.
@param screenPoint The point onscreen at which the drag ended.
@param operation A mask specifying the types of drag operations permitted by the dragging source.
@since Requires PXSourceList 0.8 or above and the OS X v10.7 SDK or above.
*/
- (void)sourceList:(PXSourceList *)aSourceList draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation;
/**
@brief Implement this method to enable the Source List to update dragging items as they are dragged over the view.
@discussion This method is analagous to `-outlineView:updateDraggingItemsForDrag:` declared on `NSOutlineViewDataSource`. See the documentation for this method for more information.
@param aSourceList The Source List in which the drag occurs.
@param draggingInfo The dragging info object.
@since Requires PXSourceList 0.8 or above and the OS X v10.7 SDK or above.
*/
- (void)sourceList:(PXSourceList *)aSourceList updateDraggingItemsForDrag:(id <NSDraggingInfo>)draggingInfo;
@end
+522
View File
@@ -0,0 +1,522 @@
//
// PXSourceListDelegate.h
// PXViewKit
//
// Created by Alex Rozanski on 17/10/2009.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Cocoa/Cocoa.h>
@class PXSourceList;
/**
The `PXSourceListDelegate` protocol defines methods that can be implemented by delegates of `PXSourceList` objects.
Note that additional documentation for the `PXSourceList` delegate notification constants is included in `PXSourceListDelegate.h`. This includes documentation for:
- `PXSLSelectionIsChangingNotification`
- `PXSLSelectionDidChangeNotification`
- `PXSLItemWillExpandNotification`
- `PXSLItemDidExpandNotification`
- `PXSLItemWillCollapseNotification`
- `PXSLItemDidCollapseNotification`
- `PXSLDeleteKeyPressedOnRowsNotification`
@warning Most of the methods defined by this protocol are analagous to those declared by `NSOutlineViewDelegate` (and are marked as such in the member's documentation), but are prefixed by "sourceList:" instead of "outlineView:". Only the most basic information about these methods is included here, and you should refer to the `NSOutlineViewDelegate` protocol documentation for more information. Several methods differ to those declared on `NSOutlineViewDelegate` in that they don't have an `NSTableColumn` parameter since `PXSourceList` works implicitly with only one table column.
*/
@protocol PXSourceListDelegate <NSObject>
@optional
///---------------------------------------------------------------------------------------
/// @name Working with Groups
///---------------------------------------------------------------------------------------
/**
@brief Returns a Boolean value that indicates whether a particular group item is displayed as always expanded.
@discussion A group that is displayed as *always expanded* displays no 'Show'/'Hide' button to the right on hover, and its direct children are always expanded.
@param aSourceList The Source List that sent the message.
@param group A group item in the data source.
@return `YES` to specify that the group should be displayed as always expanded, or `NO` if not.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group;
///---------------------------------------------------------------------------------------
/// @name Handling Mouse and Keyboard Input
///---------------------------------------------------------------------------------------
/**
@brief Returns a context menu which is to be displayed for a given mouse-down event.
@discussion See `-menuForEvent:` declared on `NSView` for more information.
@param aSourceList The Source List that sent the message.
@param theEvent A mouse event.
@param item An item in the data source.
@return An instantiated `NSMenu` object to be displayed by the Source List for *event*, or `nil` if no menu is to be shown for the given event.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item;
/**
@brief Invoked when *notification* is posted (when a deletion key is pressed and a row in the Source List is selected).
@discussion This method is invoked when the `PXSLDeleteKeyPressedOnRowsNotification` notification is posted. See `PXSourceListDelegate.h` for documentation for this notification constant.
@param notification The posted notification.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)sourceListDeleteKeyPressedOnRows:(NSNotification *)notification;
// The following methods are basically wrappers around NSOutlineViewDelegate methods.
///---------------------------------------------------------------------------------------
/// @name View-based Source List Delegate Methods
///---------------------------------------------------------------------------------------
/**
@brief Returns the view used to display the given item.
@discussion This method is analagous to `-outlineView:viewForTableColumn:item:` declared on `NSOutlineViewDelegate`, although it doesn't pass an `NSTableColumn` parameter as `PXSourceList` implicitly only uses one table column. See the documentation for `-outlineView:viewForTableColumn:item:` for more information.
Unlike when using `PXSourceList` in cell-based mode where the icon and badge value for each item can be set up using `PXSourceListDataSource` methods, it is in this method that you should set up the icon and badge for the view (if applicable) when using `PXSourceList` in view-based mode. You can make use of the `PXSourceListTableCellView` class which exposes an outlet for a `PXSourceListBadgeView` (the class included with the project used to display badges), and the `textField` and `imageView` outlets (which are inherited from its superclass, `NSTableCellView`) for the item's label and icon, respectively.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return The view to display for the specified item, or `nil` if you don't want to display a view for the item.
@warning This is a required method when using the Source List in view-based mode.
@see sourceList:rowViewForItem:
@since Requires PXSourceList 2.0.0 or above and the OS X v10.7 SDK or above.
*/
- (NSView *)sourceList:(PXSourceList *)aSourceList viewForItem:(id)item;
/**
@brief Returns the view used to display the given row.
@discussion This method is analagous to `-outlineView:rowViewForItem:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return An `NSTableRowView` instance, or `nil` if the Source List should create one and use that instead.
@see sourceList:viewForItem:
@since Requires PXSourceList 2.0.0 or above and the OS X v10.7 SDK or above.
*/
- (NSTableRowView *)sourceList:(PXSourceList *)aSourceList rowViewForItem:(id)item;
/**
@brief Sent when a row view has been added to the Source List.
@discussion This method is analagous to `-outlineView:didAddRowView:forRow:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param rowView The view that was added to the Source List.
@param row The row index.
@see sourceList:didRemoveRowView:forRow:
@since Requires PXSourceList 2.0.0 or above and the OS X v10.7 SDK or above.
*/
- (void)sourceList:(PXSourceList *)aSourceList didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row;
/**
@brief Sent when a row view has been removed from the Source List.
@discussion This method is analagous to `-outlineView:didRemoveRowView:forRow:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param rowView The row view that was removed from the Source List.
@param row The row index.
@see sourceList:didAddRowView:forRow:
@since Requires PXSourceList 2.0.0 or above and the OS X v10.7 SDK or above.
*/
- (void)sourceList:(PXSourceList *)aSourceList didRemoveRowView:(NSTableRowView *)rowView forRow:(NSInteger)row;
///---------------------------------------------------------------------------------------
/// @name Handling Selection
///---------------------------------------------------------------------------------------
/**
@brief Returns a Boolean value indicating whether a given item should be selected.
@discussion This method is analagous to `-outlineView:shouldSelectItem:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return `YES` to allow the Source List to select *item*, or `NO` otherwise.
@see sourceList:selectionIndexesForProposedSelection:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList shouldSelectItem:(id)item;
/**
@brief Returns the indexes that should be selected for a user-initiated selection.
@discussion This method is analagous to `-outlineView:selectionIndexesForProposedSelection:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param proposedSelectionIndexes The proposed indexes of rows that should be selected.
@return An `NSIndexSet` object containing the rows that should be selected in the proposed selection.
@see sourceList:shouldSelectItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (NSIndexSet*)sourceList:(PXSourceList*)aSourceList selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes;
/**
@brief Invoked when *notification* is posted (when the Source List's selection changes).
@discussion This method is invoked when the `PXSLSelectionIsChangingNotification` notification is posted. See `PXSourceListDelegate.h` for documentation for this notification constant.
This method is analagous to `-outlineViewSelectionIsChanging:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param notification The posted notification.
@see sourceListSelectionDidChange:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)sourceListSelectionIsChanging:(NSNotification *)notification;
/**
@brief Invoked when *notification* is posted (when the Source List's selection has finished changing).
@discussion This method is invoked when the `PXSLSelectionDidChangeNotification` notification is posted. See `PXSourceListDelegate.h` for documentation for this notification constant.
This method is analagous to `-outlineViewSelectionDidChange:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param notification The posted notification.
@see sourceListSelectionIsChanging:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)sourceListSelectionDidChange:(NSNotification *)notification;
///---------------------------------------------------------------------------------------
/// @name Working with Type Selection
///---------------------------------------------------------------------------------------
/**
@brief Returns the string that is used for type selection for a given item.
@discussion This method is analagous to `-outlineView:typeSelectStringForTableColumn:item:` declared on `NSOutlineViewDelegate`, although it doesn't pass an `NSTableColumn` parameter as `PXSourceList` implicitly only uses one table column. See the documentation for `-outlineView:typeSelectStringForTableColumn:item:` for more information.
@param sourceList The Source List that sent the message.
@param item The item to generate the type selection string for.
@return The string value used for type selection of *item*.
@see sourceList:nextTypeSelectMatchFromItem:toItem:forString:
@see sourceList:shouldTypeSelectForEvent:withCurrentSearchString:
@since Requires PXSourceList 2.0.0 or above and the OS X v10.7 SDK or above.
*/
- (NSString *)sourceList:(PXSourceList *)sourceList typeSelectStringForItem:(id)item;
/**
@brief Returns the first item that matches the given search string from within the given range.
@discussion This method is analagous to `-outlineView:nextTypeSelectMatchFromItem:toItem:forString:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param sourceList The Source List that sent the message.
@param startItem The first item to search.
@param endItem The item before which to stop searching.
@param searchString The string to search.
@return The first item in the *startItem*--*endItem* range which matches *searchString*, or `nil` if there is no match.
@see sourceList:typeSelectStringForItem:
@see sourceList:shouldTypeSelectForEvent:withCurrentSearchString:
@since Requires PXSourceList 2.0.0 or above and the OS X v10.7 SDK or above.
*/
- (id)sourceList:(PXSourceList *)sourceList nextTypeSelectMatchFromItem:(id)startItem toItem:(id)endItem forString:(NSString *)searchString;
/**
@brief Returns a Boolean value which indicates whether type select should proceed for a given event and search string.
@discussion This method is analagous to `-outlineView:shouldTypeSelectForEvent:withCurrentSearchString:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param sourceList The Source List that sent the message.
@param event The event that caused this message to be sent.
@param searchString The search string for which searching is to proceed from.
@return `YES` if type select should proceed, or `NO` otherwise.
@see sourceList:typeSelectStringForItem:
@see sourceList:nextTypeSelectMatchFromItem:toItem:forString:
@since Requires PXSourceList 2.0.0 or above and the OS X v10.7 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList *)sourceList shouldTypeSelectForEvent:(NSEvent *)event withCurrentSearchString:(NSString *)searchString;
/**
@brief Returns a Boolean value which indicates whether a cell expansion tooltip should be displayed for a given item.
@discussion This method is analagous to `-outlineView:shouldShowCellExpansionForTableColumn:item:` declared on `NSOutlineViewDelegate`, although it doesn't pass an `NSTableColumn` parameter as `PXSourceList` implicitly only uses one table column. See the documentation for `-outlineView:shouldShowCellExpansionForItem:` for more information.
@param sourceList The Source List that sent the message.
@param item An item in the data source.
@return `YES` to allow an expansion tooltip to be displayed for *item*, otherwise `NO`.
@warning This method is only used by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, this method is not called.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList *)sourceList shouldShowCellExpansionForItem:(id)item;
///---------------------------------------------------------------------------------------
/// @name Providing Tooltips
///---------------------------------------------------------------------------------------
/**
@brief Returns the tooltip string that should be displayed for a given cell.
@discussion This method is analagous to `-outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:` declared on `NSOutlineViewDelegate`, although it doesn't pass an `NSTableColumn` parameter as `PXSourceList` implicitly only uses one table column. See the documentation for `-outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:` for more information.
@param sourceList The Source List that sent the message.
@param cell The cell to return the tooltip for.
@param rect The proposed active area of the tooltip.
@param item The item in the data source to display the tooltip for.
@param mouseLocation The current mouse location in view coordinates.
@return The tooltip string to be displayed for *cell*, or `nil` if no tooltip is to be shown.
@warning This method is only used by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, this method is not called.
@since Requires PXSourceList 2.0.0 or above and the OS X v10.7 SDK or above.
*/
- (NSString *)sourceList:(PXSourceList *)sourceList toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect item:(id)item mouseLocation:(NSPoint)mouseLocation;
///---------------------------------------------------------------------------------------
/// @name Editing Items
///---------------------------------------------------------------------------------------
/**
@brief Returns a Boolean value which indicates whether the Source List should allow editing of a given item.
@discussion This method is analagous to `-outlineView:shouldEditTableColumn:item:` declared on `NSOutlineViewDelegate`, although it doesn't pass an `NSTableColumn` parameter as `PXSourceList` implicitly only uses one table column. See the documentation for `-outlineView:shouldEditTableColumn:item:` for more information.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return `YES` to allow editing of *item*, or `NO` otherwise.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList shouldEditItem:(id)item;
///---------------------------------------------------------------------------------------
/// @name Customising Tracking Support
///---------------------------------------------------------------------------------------
/**
@brief Returns a Boolean value that indicates whether a given cell should be tracked
@discussion This method is analagous to `-outlineView:shouldTrackCell:forTableColumn:item:` declared on `NSOutlineViewDelegate`, although it doesn't pass an `NSTableColumn` parameter as `PXSourceList` implicitly only uses one table column. See the documentation for `-outlineView:shouldTrackCell:forTableColumn:item:` for more information.
@param aSourceList The Source List that sent the message.
@param cell The cell used to display *item*.
@param item An item in the data source.
@return `YES` if the cell should be tracked for *item*, otherwise `NO`.
@warning This method is only used by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, this method is not called.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList shouldTrackCell:(NSCell *)cell forItem:(id)item;
///---------------------------------------------------------------------------------------
/// @name Expanding and Collapsing the Outline
///---------------------------------------------------------------------------------------
/**
@brief Returns a Boolean value that indicates whether a given item should be expanded.
@discussion This method is analagous to `-outlineView:shouldExpandItem:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return `YES` to allow expansion of *item*, otherwise `NO`.
@see sourceListItemWillExpand:
@see sourceListItemDidExpand:
@see sourceList:shouldCollapseItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList shouldExpandItem:(id)item;
/**
@brief Returns a Boolean value that indicates whether a given item should be collapsed.
@discussion This method is analagous to `-outlineView:shouldCollapseItem:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return `YES` to allow *item* to be collapsed, otherwise `NO`.
@see sourceListItemWillCollapse:
@see sourceListItemDidCollapse:
@see sourceList:shouldExpandItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (BOOL)sourceList:(PXSourceList*)aSourceList shouldCollapseItem:(id)item;
/**
@brief Invoked when *notification* is posted (when an item in the Source List is about to expand in response to user input).
@discussion This method is invoked when the `PXSLItemWillExpandNotification` notification is posted. See `PXSourceListDelegate.h` for documentation for this notification constant.
This method is analagous to `-outlineViewItemWillExpand:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param notification The posted notification.
@see sourceListItemDidExpand:
@see sourceList:shouldExpandItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)sourceListItemWillExpand:(NSNotification *)notification;
/**
@brief Invoked when *notification* is posted (when an item in the Source List was expanded in response to user input).
@discussion This method is invoked when the `PXSLItemDidExpandNotification` notification is posted. See `PXSourceListDelegate.h` for documentation for this notification constant.
This method is analagous to `-outlineViewItemDidExpand:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param notification The posted notification.
@see sourceListItemWillExpand:
@see sourceList:shouldExpandItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)sourceListItemDidExpand:(NSNotification *)notification;
/**
@brief Invoked when *notification* is posted (when an item in the Source List is about to collapse in response to user input).
@discussion This method is invoked when the `PXSLItemWillCollapseNotification` notification is posted. See `PXSourceListDelegate.h` for documentation for this notification constant.
This method is analagous to `-outlineViewItemWillCollapse:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param notification The posted notification.
@see sourceListItemDidCollapse:
@see sourceList:shouldCollapseItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)sourceListItemWillCollapse:(NSNotification *)notification;
/**
@brief Invoked when *notification* is posted (when an item in the Source List was collapsed in response to user input).
@discussion This method is invoked when the `PXSLItemDidCollapseNotification` notification is posted. See `PXSourceListDelegate.h` for documentation for this notification constant.
This method is analagous to `-outlineViewItemDidCollapse:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param notification The posted notification.
@see sourceListItemWillCollapse:
@see sourceList:shouldCollapseItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)sourceListItemDidCollapse:(NSNotification *)notification;
///---------------------------------------------------------------------------------------
/// @name Customising Row Sizes
///---------------------------------------------------------------------------------------
/**
@brief Returns the height in points of the row for the given item.
@discussion This method is analagous to `-outlineView:heightOfRowByItem:` declared on `NSOutlineViewDelegate`. See the documentation for this method for more information.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return The height of the row used to display *item* in points.
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (CGFloat)sourceList:(PXSourceList*)aSourceList heightOfRowByItem:(id)item;
///---------------------------------------------------------------------------------------
/// @name Displaying Cells
///---------------------------------------------------------------------------------------
/**
@brief Informs the delegate that the Source List is about to display the cell associated with the given item.
@discussion This method is analagous to `-outlineView:willDisplayCell:forTableColumn:item:` declared on `NSOutlineViewDelegate`, although it doesn't pass an `NSTableColumn` parameter as `PXSourceList` implicitly only uses one table column. See the documentation for `-outlineView:willDisplayCell:forTableColumn:item:` for more information.
@param aSourceList The Source List that sent the message.
@param cell The cell about to be displayed.
@param item An item in the data source.
@warning This method is only used by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, this method is not called.
@see sourceList:dataCellForItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (void)sourceList:(PXSourceList*)aSourceList willDisplayCell:(id)cell forItem:(id)item;
/**
@brief Returns the cell for use with a given item in the Source List.
@discussion This method is analagous to `-outlineView:dataCellForTableColumn:item:` declared on `NSOutlineViewDelegate`, although it doesn't pass an `NSTableColumn` parameter as `PXSourceList` implicitly only uses one table column. See the documentation for `-outlineView:dataCellForTableColumn:item:` for more information.
@param aSourceList The Source List that sent the message.
@param item An item in the data source.
@return The cell used to display *item*.
@warning This method is only used by the Source List when operating in cell-based mode. When the Source List is operating in view-based mode, this method is not called.
@see sourceList:willDisplayCell:forItem:
@since Requires PXSourceList 0.8 or above and the OS X v10.5 SDK or above.
*/
- (NSCell*)sourceList:(PXSourceList*)aSourceList dataCellForItem:(id)item;
@end
//PXSourceList delegate notifications
/**
@brief This is analagous to the `NSOutlineViewSelectionIsChangingNotification` notification. Take a look at the `NSOutlineView` documentation for more information.
@since Requires PXSourceList 0.8 or above.
*/
extern NSString * const PXSLSelectionIsChangingNotification;
/**
@brief This is analagous to the `NSOutlineViewSelectionDidChangeNotification` notification. Take a look at the `NSOutlineView` documentation for more information.
@since Requires PXSourceList 0.8 or above.
*/
extern NSString * const PXSLSelectionDidChangeNotification;
/**
@brief This is analagous to the `NSOutlineViewItemWillExpandNotification` notification. Take a look at the `NSOutlineView` documentation for more information.
@since Requires PXSourceList 0.8 or above.
*/
extern NSString * const PXSLItemWillExpandNotification;
/**
@brief This is analagous to the `NSOutlineViewItemDidExpandNotification` notification. Take a look at the `NSOutlineView` documentation for more information.
@since Requires PXSourceList 0.8 or above.
*/
extern NSString * const PXSLItemDidExpandNotification;
/**
@brief This is analagous to the `NSOutlineViewItemWillCollapseNotification` notification. Take a look at the `NSOutlineView` documentation for more information.
@since Requires PXSourceList 0.8 or above.
*/
extern NSString * const PXSLItemWillCollapseNotification;
/**
@brief This is analagous to the `NSOutlineViewItemDidCollapseNotification` notification. Take a look at the `NSOutlineView` documentation for more information.
@since Requires PXSourceList 0.8 or above.
*/
extern NSString * const PXSLItemDidCollapseNotification;
/**
@brief Posted whenever a "deletion key" (backspace, cmd-backspace or fn-backspace) is pressed and handled by the Source List and a row is selected.
@discussion The notification *object* is the `PXSourceList` object which the notification was posted by. The *userInfo* dictionary contains a `@"rows"` key which maps to an `NSIndexSet` object which contains the selected row indexes that were selected when the notification was posted.
@since Requires PXSourceList 0.8 or above.
*/
extern NSString * const PXSLDeleteKeyPressedOnRowsNotification;
+228
View File
@@ -0,0 +1,228 @@
//
// PXSourceListItem.h
// PXSourceList
//
// Created by Alex Rozanski on 08/01/2014.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Foundation/Foundation.h>
/**
`PXSourceListItem` is a generic `NSObject` subclass which can be used to build a hierarchical model for use by
a `PXSourceList` data source.
@warning While it is not mandatory to use `PXSourceListItem` objects in a `PXSourceList` data source, this
class is generic enough that it should serve most use cases.
@discussion ### Basic properties
`PXSourceListItem` has been designed to contain properties for the frequently-used information which you need
from a Source List data source item when implementing the `PXSourceListDataSource` (and possibly
`PXSourceListDelegate`) methods, namely:
* The title displayed in the Source List for the given item.
* The icon displayed to the left of the given item in the Source List.
* The badge value displayed to the right of the given item in the Source List.
* Child items of the given item.
The existence of these core properties means that it is unlikely that you should have to create your own
`PXSourceListItem` subclass.
### Identifying objects
`PXSourceListItem`s are often backed by data model objects that are used in other parts of your application, and
the API has been designed to be able to easily identify a given model object from any part of your code
given an arbitrary `PXSourceListItem`. This is useful when you obtain an item using one of `PXSourceList`'s methods
or are given one as an argument to a `PXSourceListDelegate` or `PXSourceListDataSource` protocol method and you
need to find its backing data model object to be able to use in application logic.
There are two (often distinct) patterns used to identify a given backing model object in a `PXSourceListItem`
object:
* Using the `identifier` property. This is probably the easiest way of identifying items, and these identifiers
are best defined as string constants which you can reference from multiple places in your code.
* Using the `representedObject` property. Using `representedObject` can be useful if the underlying model
object has identifying information about it which you can use when determining which object you're
working with given a `PXSourceListItem` instance.
*/
@interface PXSourceListItem : NSObject
@property (strong, nonatomic) NSString *title;
@property (strong, nonatomic) NSImage *icon;
@property (weak, nonatomic) id representedObject;
@property (strong, nonatomic) NSString *identifier;
@property (strong, nonatomic) NSNumber *badgeValue;
///---------------------------------------------------------------------------------------
/// @name Convenience initialisers
///---------------------------------------------------------------------------------------
/** Creates and returns an item with the given parameters.
@param title A title.
@param identifier An identifier.
@return An item initialised with the given parameters.
@see itemWithTitle:identifier:icon:
@see itemWithRepresentedObject:icon:
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
+ (instancetype)itemWithTitle:(NSString *)title identifier:(NSString *)identifier;
/** Creates and returns an item with the given parameters.
@param title A title.
@param identifier An identifier.
@param icon An icon.
@return An item initialised with the given parameters.
@see itemWithTitle:identifier:
@see itemWithRepresentedObject:icon:
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
+ (instancetype)itemWithTitle:(NSString *)title identifier:(NSString *)identifier icon:(NSImage *)icon;
/** Creates and returns an item with the given parameters.
@param object An object.
@param icon An icon.
@return An item initialised with the given parameters.
@see itemWithTitle:identifier:
@see itemWithTitle:identifier:icon:
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
+ (instancetype)itemWithRepresentedObject:(id)object icon:(NSImage *)icon;
///---------------------------------------------------------------------------------------
/// @name Working with child items
///---------------------------------------------------------------------------------------
/**
@brief Returns the receiver's children.
@warning This property is backed by an `NSMutableArray` since an item's children *are* mutable. The getter for
this property returns a copied array for safety, so this getter should not be called excessively.
@see hasChildren
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
@property (strong, nonatomic) NSArray *children;
/**
@brief Returns whether the receiver has any child items.
@discussion This is faster than calling `-children` on the receiver then checking the number of items in the array
because of how this getter is implemented. See `-children` for more information.
@see children
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
- (BOOL)hasChildren;
/**
@brief Adds an item to the receiver's array of child items.
@discussion Adds *item* to the end of the receiver's array of child items.
This is a convenience method rather than having to call `-children` on the receiver, create a mutable copy
and then mutate this array before setting it back on the receiver.
@param childItem An item
@see insertChildItem:atIndex:
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
- (void)addChildItem:(PXSourceListItem *)childItem;
/**
@brief Inserts an item to the receiver's array of child items at a given index.
@discussion Inserts *item* at *index* in the receiver's array of child items.
This is a convenience method rather than having to call `-children` on the receiver, create a mutable copy
and then mutate this array before setting it back on the receiver.
@param childItem An item
@param index An index
@see addChildItem:
@see insertChildItems:atIndexes:
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
- (void)insertChildItem:(PXSourceListItem *)childItem atIndex:(NSUInteger)index;
/**
@brief Removes an item from the receiver's array of child items.
@discussion Removes *item* from the receiver's array of child items.
This is a convenience method rather than having to call `-children` on the receiver, create a mutable copy
and then mutate this array before setting it back on the receiver.
@param childItem An item
@see removeChildItemAtIndex:
@see removeChildItems:
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
- (void)removeChildItem:(PXSourceListItem *)childItem;
/**
@brief Removes the item at the given index from the receiver's array of child items.
@discussion Removes the item at the given *index* from the receiver's array of child items.
This is a convenience method rather than having to call `-children` on the receiver, create a mutable copy
and then mutate this array before setting it back on the receiver.
@param index An integer representing an index in the receiver's array of children
@see removeChildItem:
@see removeChildItems:
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
- (void)removeChildItemAtIndex:(NSUInteger)index;
/**
@brief Removes the items in the given array from the receiver's array of child items.
@discussion Removes all of the items in *items* from the receiver's array of children.
This is a convenience method rather than having to call `-children` on the receiver, create a mutable copy
and then mutate this array before setting it back on the receiver.
@param items An array of `PXSourceListItem` objects
@see removeChildItem:
@see removeChildItemAtIndex:
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
- (void)removeChildItems:(NSArray *)items;
/**
@brief Inserts the given items into the receiver's array of child items at the given indexes.
@discussion Inserts all of the items in *items* to the receiver's array of children at the given *indexes*.
This is a convenience method rather than having to call `-children` on the receiver, create a mutable copy
and then mutate this array before setting it back on the receiver.
@param items An array of `PXSourceListItem` objects
@param indexes The indexes to insert the child items at
@see insertChildItem:atIndex:
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
- (void)insertChildItems:(NSArray *)items atIndexes:(NSIndexSet *)indexes;
@end
+101
View File
@@ -0,0 +1,101 @@
//
// PXSourceListItem.m
// PXSourceList
//
// Created by Alex Rozanski on 08/01/2014.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import "PXSourceListItem.h"
@implementation PXSourceListItem {
NSMutableArray *_children;
}
+ (instancetype)itemWithTitle:(NSString *)title identifier:(NSString *)identifier
{
return [self itemWithTitle:title identifier:identifier icon:nil];
}
+ (instancetype)itemWithTitle:(NSString *)title identifier:(NSString *)identifier icon:(NSImage *)icon
{
PXSourceListItem *item = [[self alloc] init];
item.title = title;
item.identifier = identifier;
item.icon = icon;
return item;
}
+ (instancetype)itemWithRepresentedObject:(id)object icon:(NSImage *)icon
{
PXSourceListItem *item = [[self alloc] init];
item.representedObject = object;
item.icon = icon;
return item;
}
- (id)init
{
if (!(self = [super init]))
return nil;
_children = [[NSMutableArray alloc] init];
return self;
}
#pragma mark - Custom Accessors
- (NSArray *)children
{
return [_children copy];
}
- (void)setChildren:(NSArray *)children
{
_children = [children mutableCopy];
}
#pragma mark - Child Convenience Methods
- (BOOL)hasChildren
{
return _children.count > 0;
}
- (void)addChildItem:(PXSourceListItem *)childItem
{
[_children addObject:childItem];
}
- (void)insertChildItem:(PXSourceListItem *)childItem atIndex:(NSUInteger)index
{
[_children insertObject:childItem atIndex:index];
}
- (void)removeChildItem:(PXSourceListItem *)childItem
{
[_children removeObject:childItem];
}
- (void)removeChildItemAtIndex:(NSUInteger)index
{
[_children removeObjectAtIndex:index];
}
- (void)removeChildItems:(NSArray *)items
{
[_children removeObjectsInArray:items];
}
- (void)insertChildItems:(NSArray *)items atIndexes:(NSIndexSet *)indexes
{
[_children insertObjects:items atIndexes:indexes];
}
@end
+43
View File
@@ -0,0 +1,43 @@
//
// PXSourceListTableCellView.h
// PXSourceList
//
// Created by Alex Rozanski on 31/12/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import <Cocoa/Cocoa.h>
@class PXSourceListBadgeView;
/**
`PXSourceListTableCellView` is an `NSTableCellView` subclass which can be used when using `PXSourceList`
in view-based mode.
Similar to `NSTableCellView` and its `textField` and `imageView` outlets, `PXSourceListTableCellView`
provides a `badgeView` outlet which can be hooked up to a `PXSourceListBadgeView` in Interface Builder
and then configured in `sourceList:viewForItem:`.
`PXSourceListTableCellView` positions its `badgeView` automatically (as `NSTableCellView` does for the `textField`
and `imageView` outlets) to be positioned centred (vertically) and rightmost (horizontally) within the table cell's
bounds. If you want to change this positioning you can do so by creating a `PXSourceListTableCellView` subclass and
overriding `-layout`, but note that idiomatically, source lists display badges to the right of each row.
*/
@interface PXSourceListTableCellView : NSTableCellView
/**
@brief The badge view displayed by the cell.
@discussion When a `PXSourceListTableCellView` instance is created, a `PXSourceListTableCellView` instance
is *not* automatically created and set to this property (just like with `NSTableCellView` and its
`textField` and `imageView` properties). This property is purely declared on this class to make creating
table cell views for a `PXSourceList` in Interface Builder easier without having to declare your own
`NSTableCellView` subclass.
This property is typically configured in the `PXSourceListDelegate` method `sourceList:viewForItem:`.
@since Requires PXSourceList 2.0.0 and above and the Mac OS X 10.7 SDK or above.
*/
@property (weak, nonatomic) IBOutlet PXSourceListBadgeView *badgeView;
@end
+31
View File
@@ -0,0 +1,31 @@
//
// PXSourceListTableCellView.m
// PXSourceList
//
// Created by Alex Rozanski on 31/12/2013.
// Copyright 2009-14 Alex Rozanski http://alexrozanski.com and other contributors.
// This software is licensed under the New BSD License. Full details can be found in the README.
//
#import "PXSourceListTableCellView.h"
#import "PXSourceListBadgeView.h"
@implementation PXSourceListTableCellView
- (void)layout
{
[super layout];
if (!self.badgeView)
return;
[self.badgeView sizeToFit];
NSRect bounds = self.bounds;
NSSize badgeSize = self.badgeView.frame.size;
self.badgeView.frame = NSMakeRect(NSMaxX(bounds) - badgeSize.width,
NSMidY(bounds) - round(badgeSize.height / 2.0f),
badgeSize.width, badgeSize.height);
}
@end
+132 -46
View File
@@ -1,71 +1,157 @@
#PXSourceList
A Source List control for use with the Mac OS X 10.5 SDK or above.
[Download the documentation][1]
[![Pod Version](http://img.shields.io/cocoapods/v/PXSourceList.svg)](http://cocoadocs.org/docsets/PXSourceList/2.0.5/)
[![Platform](http://img.shields.io/cocoapods/p/PXSourceList.svg)](http://cocoadocs.org/docsets/PXSourceList/2.0.5/)
[![Licence](http://img.shields.io/cocoapods/l/PXSourceList.svg)](https://github.com/Perspx/PXSourceList/blob/master/LICENSE)
`PXSourceList` is licensed under the New BSD License.
`PXSourceList` is an `NSOutlineView` subclass used for easily implementing source lists in your applications.
##Intention
[Source Lists][2] are used in a lot of Mac OS X applications, but the support for such controls is quite primitive at best you create an Outline View with Source List highlighting, but none of the features such as badging are built in by default. `PXSourceList` is a reusable control within the context of Source Lists which makes creating applications with Source Lists a much easier process.
PXSourceList requires the OS X 10.7 SDK and above and is licensed under the New BSD License.
##Using the code
There are only a few steps involved:
![PXSourceList in action: The view-based example project included in the repository.](Examples/Screenshots/PXSourceList-ViewBased-Example.png)
1. Download the source, and copy `PXSourceList.h`, `PXSourceList.m`, `PXSourceListDelegate.h` and `PXSourceListDataSource.h` into your Xcode project.
2. To create the control in Interface Builder, drag an `NSOutlineView` object over to a window and in the Identity Inspector for the Outline View, change the class to `PXSourceList`. In the Attributes Inspector, set it to have only 1 column, uncheck "Headers" in the "Columns" section and set "Highlight" to "Source List" there is an NIB in the example project bundled with the source.
3. Make sure to `#import "PXSourceList.h"` for files that require it (the delegate and data source protocol files are imported in this main header), and ensure that your class(es) that are the `delegate` and/or `dataSource` for the Source List conform to the `PXSourceListDelegate` and `PXSourceListDataSource` protocols respectively.
## Overview
Using a [source list](http://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AppleHIGuidelines/Windows/Windows.html#//apple_ref/doc/uid/20000961-CHDDIGDE) for navigation is a common user interface paradigm in OS X applications, but requires a fair amount of manual set up and customisation of standard Cocoa controls.
**Note:** If you intend to use PXSourceList with the 10.5 SDK, you will need to remove some of the protocols that PXSourceList conforms to which do not exist in `PXSourceList.h`, remove the `<NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuDelegate>` from the interface declaration.
**PXSourceList subclasses NSOutlineView and provides much of the common styling and idiomatic behaviour of source lists for you through a clean and simple API.**
There is also an example project bundled with the source to see how the control is used.
PXSourceList has several key features:
##Screenshots
![alt text][3]
- Built-in support for displaying badges — blue-grey pills which display numerical values such as the number of photos in a particular album.
- Always displaying root-level items with group styling — the blue-grey uppercase text seen in the source lists in apps such as Mail.app. This requires no extra configuration.
- Support for displaying specific groups as always expanded through implementation of a single delegate method. These groups will always show their child items and wont show a Show/Hide button on hover.
- Since idiomatic source lists use only a single column and dont display column headers, PXSourceList operates with only a single table column and doesnt display a header. This is reflected in PXSourceLists API and makes the control easier to use.
- The project includes a generic data model class which can be used for building a data source data model without having to roll your own.
##How the control works
I have tried to structure PXSourceList in a way such that it fits common Cocoa design patterns and therefore makes it easier to use.
Note that [in the OS X Human Interface Guidelines](https://developer.apple.com/library/mac/documentation/userexperience/conceptual/applehiguidelines/Windows/Windows.html#//apple_ref/doc/uid/20000961-CHDDIGDE), source lists are broken down into those which provide navigation for the app as a whole (and have a blue-grey background), and those which provide selection functionality for the window (with a white background). PXSourceList implements this *first* style of source list; the second type doesnt require quite as much common customisation so would not be useful as a standalone control.
PXSourceList adapts the delegate and data source design patterns, and extends those of the `NSOutlineViewDelegate` and `NSOutlineViewDataSource`, much in the way that these extend the appropriate `NSTableView` protocols.
## Using PXSourceList
If you want more information have a look at the [Outline View Programming Topics for Cocoa][4] the Source List delegate and data source implementation work in much the same way, but with methods added and removed, as detailed in the documentation.
### Installing with CocoaPods
##Documentation
Documentation is available for PXSourceList, downloadable [here][5]. Provided in the ZIP file is a folder containing HTML documentation, or a docset which can be opened in Xcode and which is then searchable from the Xcode Developer Documentation.
You can install PXSourceList by adding the following line to your `Podfile`:
If you feel that any areas of the documentation are lacking or missing, please feel free to [let me know][6], which will be much appreciated.
pod 'PXSourceList', '~> 2.0'
**Note:** the documentation for the Source List notifications can be found in the `NSObject(PXSourceListNotifications)` reference, which is linked to from the documentation index page.
### Cloning with git
###Documentation Revision History
17th Jan - PXSourceList Documentation [version 0.7.1][7] released
9th Jan - PXSourceList Documentation [version 0.7][8] released
You can also get the source by cloning with git:
##Known Issues
$ git clone https://github.com/Perspx/PXSourceList.git
- Calling `delegate` or `dataSource` on the Source List returns the Source List instance. This is, unfortunately, a side effect of how delegate and data source methods are handled within the Source List I hope to work around it in the future.
- No bindings implementation (yet).
You can then either:
##Attribution
* Copy all of the files from the `PXSourceList` directory (including those in the `Internal` subdirectory) into your project
I was spurred along the way by many sources, but in particular [BWToolkit][9] by Brandon Walkin and Fraser Kuyvenhoven, which gave me the idea of how to handle the Source List delegate and data source methods.
***or***
Also Brian Dunagan's post on [Source List badging][10] and determining state for the various colours was a great help when I came to the drawing code for that.
* Add the Xcode project as a subproject to your own Xcode project or to your workspace and link against the `PXSourceList` framework target.
The documentation was created using [Doxygen][11] and [appledoc][12], thanks of which go to the developers of both.
### Using PXSourceList in your application
The icons used in the demo project bundled with the source code are from the [Fugue icon set][13] by Yusuke Kamiyamane.
1. Drag an `NSOutlineView` object into the window/view that you're displaying the source list in. Often source lists are placed in the leftmost panel of an `NSSplitView`.
2. In the Identity inspector for the outline view, change the class from the `NSOutlineView` placeholder to `PXSourceList`.
3. With the *source list* selected (it helps to use Interface Builders *Document Outline* view for this), select "Source List" for the "Highlight" attribute under the "Table View" section in the Attributes inspector.
4. Control-click on the Source List and drag connectors to the object(s) that you want to be your Source List's delegate and data source, selecting "delegate" or "dataSource" respectively from the popup menu that is shown when you release the mouse button. A Source List *requires* a data source object, but having a delegate is optional.
5. Make sure to `#import "PXSourceList.h"` for files that require it (the delegate and data source protocol files are imported in this main header), and ensure that your source list delegate and data source class(es) conform to the `PXSourceListDelegate` and `PXSourceListDataSource` protocols respectively.
[1]: http://perspx.com/software/PXSourceList/#docs
[2]: http://developer.apple.com/Mac/library/documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGWindows/XHIGWindows.html#//apple_ref/doc/uid/20000961-CHDDIGDE
[3]: http://perspx.com/wp-content/uploads/2010/01/pxsourcelist.jpg
[4]: http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/OutlineView/Articles/UsingOutlineDataSource.html
[5]: http://perspx.com/software/PXSourceList/#docs
[6]: http://perspx.com/contact
[7]: http://perspx.com/wp-content/uploads/2010/01/PXSourceList_0-7-1_Documentation.zip
[8]: http://perspx.com/wp-content/uploads/2010/01/PXSourceList_0-7_Documentation.zip
[9]: http://brandonwalkin.com/bwtoolkit/
[10]: http://www.bdunagan.com/2008/11/10/cocoa-tutorial-source-list-badges-part-2/
[11]: http://www.doxygen.org/
[12]: http://www.gentlebytes.com/freeware/appledoc/
[13]: http://www.pinvoke.com/
There are also two example projects included in the project to see how PXSourceList should be used.
### Cell-based vs. View-based mode
As an `NSOutlineView` subclass, PXSourceList can display its contents using cells (in *cell-based* mode) or views (in *view-based* mode). Some delegate and data source methods (see below) are only applicable when PXSourceList is used in cell-based mode, and is noted as such in the documentation.
## Documentation
`PXSourceList` and its related classes and protocols are documented in the header files included in the repository using [appledoc](http://gentlebytes.com/appledoc/)-style documentation.
Documentation (in HTML and docset formats) can be generated by building the *Documentation* target from the Xcode project. The resulting documentation will be placed in `docs` in the root directory of the project. To generate documentation in this way, appledoc [must be installed](https://github.com/tomaz/appledoc#quick-install) and the script which builds the documentation expects it to be installed under `/usr/local/bin`.
If you notice any mistakes or feel that any areas of the documentation are lacking or missing, please [file a GitHub issue](https://github.com/Perspx/PXSourceList/issues).
## PXSourceList 2
PXSourceList 2 is a great improvement over PXSourceList 0.x and 1.x that adds view-based table support and many other small improvements and bugfixes.
For view-based table support, new delegate and data source methods have been added to bring PXSourceList on-par with `NSOutlineView`s API, and a generic badge view and `NSTableCellView` subclass have been implemented to allow easy setup of `NSTableCellView`s with PXSourceList.
Additionally, a generic `PXSourceListItem` class has been implemented for building a data source model without having to roll your own class. A new internal implementation of PXSourceList fixes problems in prior versions where some source list delegate and data source methods werent being called.
Take a look at the [Release Notes](ReleaseNotes.md) for a comprehensive list of changes as well as API changes in version 2.
## Delegate and Data Source Objects
Like `NSOutlineView`, `PXSourceList` objects obtain their content and other information from their *data source* and *delegate* objects using methods defined in the `PXSourceListDataSource` and `PXSourceListDelegate` protocols respectively.
As well as declaring new delegate and data source methods, since `PXSourceList` subclasses `NSOutlineView`, `PXSourceListDataSource` and `PXSourceListDelegate` include most `NSOutlineViewDelegate` and `NSOutlineViewDataSource` methods but with the "outlineView" prefix replaced with "sourceList". For more information on implementing a data source object, take a look at *[Outline View Programming Topics](https://developer.apple.com/library/mac/documentation/cocoa/conceptual/OutlineView/Articles/UsingOutlineDataSource.html)* — PXSourceLists delegate and data source implementation works in a very similar way.
Note that some of the `NSOutlineView` delegate and data source methods are not relevant to PXSourceList, so they haven't been added to `PXSourceListDelegate` and `PXSourceListDataSource`.
Note also that because of the way PXSourceList works under the hood, `-[PXSourceList delegate]` and `-[PXSourceList dataSource]` have been marked as unavailable (they return an internal proxy object). You should therefore only use `-setDelegate:` and `-setDataSource:`.
### Required Methods
A PXSourceList data source must implement the following methods:
- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
- (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
- (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
If you are using PXSourceList in cell-based mode, you also need to implement:
- (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
If you are using PXSourceList in view-based mode, you should implement:
- (NSView *)sourceList:(PXSourceList *)aSourceList viewForItem:(id)item
in your **delegate** object class.
Take a look at both the view-based and cell-based example projects in the repository for more information about implementing PXSourceList delegate and data source objects.
## View-Based Source Lists
As mentioned above, PXSourceList can be used in either cell-based- or view-based mode to display content using cells or views.
When using PXSourceList in view-based mode, several classes have been included in the project to help with setting up views for each item in the table, which is done in the `PXSourceListDataSource` method, `-sourceList:viewForItem:`.
### PXSourceListBadgeView
This is a view class that draws a badge with a given numeric value. Badges are displayed to the right of rows that show them and are displayed with a grey-blue background when the row containing them is not selected, or a white background otherwise.
### PXSourceListTableCellView
This is an `NSTableCellView` subclass that exposes a `badgeView` outlet that can be hooked up to a `PXSourceListBadgeView` instance in Interface Builder and can then be configured in `-sourceList:viewForItem:`. Like with `NSTableCellView`, `PXSourceListTableCellView` positions its `badgeView` automatically for you.
## Data Source Model Using PXSourceListItem
The generic `PXSourceListItem` class has been added in PXSourceList 2 for creation of a data source model without having to roll your own classes.
Since `PXSourceListDataSource` works by building up a tree structure of model objects (which maps to the tree-like presentation of the content), `PXSourceListItem` allows you to build up a tree structure of model objects using the `children` property and other convenience methods.
Each item can have associated with it:
- **A title**. Useful for setting the `textField` property of an `NSTableCellView` in `-sourceListForItem:`.
- **An icon image**. Useful for setting the image on the `imageView` property of an `NSTableCellView` in `-sourceListForItem:`.
- **An identifier**. Useful for identifying a given item when given one as the return value from `PXSourceList` method or as a parameter to a `PXSourceListDelegate` or `PXSourceListDataSource` method.
- **A badge value**. Useful to store the badge value for a particular item if it doesnt have a backing data model object that you pull this value from.
Additionally, each item has a `representedObject` property associated with it which is useful when you dont want to set the data on a source list item directly, but instead want to pull it from an associated model object. This means that you dont have to keep your data model and properties on `PXSourceListItem` in sync.
## Attribution
Thanks first of all to the [wonderful people](https://github.com/Perspx/PXSourceList/graphs/contributors) who have contributed to the project and helped in improving the project, fixing bugs and adding new features.
In the initial release of PXSourceList, I was spurred along the way by many sources, but in particular [BWToolkit](http://brandonwalkin.com/bwtoolkit/) by Brandon Walkin and Fraser Kuyvenhoven, which gave me the idea of how to handle the source list delegate and data source methods.
Brian Dunagan's post on [Source List badging](http://www.bdunagan.com/2008/11/10/cocoa-tutorial-source-list-badges-part-2/) and determining state for the various colours was also a great help when I came to the drawing code for source list badges.
The *Documentation* target in the Xcode project makes use of the fantastic [appledoc](http://www.gentlebytes.com/appledoc/), which has been an invaluable tool utilised since the very first version of PXSourceList.
The icons used in the example projects bundled with the source code are from the [Fugue icon set](http://p.yusukekamiyamane.com) by Yusuke Kamiyamane (in the cell-based example) and the [Mimi Glyphs set](http://salleedesign.com/resources/mimi-glyphs/) by Jeremy Salée (in the view-based example).
## Licence
PXSourceList is licensed under the New BSD License, as detailed below (adapted from OSI [http://www.opensource.org/licenses/bsd-license.php](http://www.opensource.org/licenses/bsd-license.php)):
Copyright &copy; 2009-14, Alex Rozanski and other contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- 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.
- Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
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.
+95
View File
@@ -0,0 +1,95 @@
# PXSourceList Release Notes
## 2.0.7
- Remove -setFlipped: call which was causing a deprecation warning on OS X 10.10.
- Fix whitespace in PXSourceList.m.
## 2.0.6
- Merge PR #49: Fix PXSourceListBadgeCell accessibility. Adds accessibility for PXSourceListBadgeCell when using PXSourceList in view-based mode.
## 2.0.5
- Fix #43: sourceListDeleteKeyPressedOnRows: called twice. This was caused by an issue where PXSourceList was incorrectly removing the old delegate as an observer of PXSourceList notifications in -setDelegate:.
## 2.0.4
- PR #41: fix a Zeroing Weak References problem. This fixes an issue where using an `NSWindow`, `NSWindowController` or `NSViewController` as a PXSourceList delegate or dataSource would cause problems on 10.7 because prior to 10.8, these classes could not be referenced by zeroing weak references.
- Remove unused `badgeMargin` constant from PXSourceList.m.
## 2.0.3
- Fix #40: Editing titles on cell based source list causes exception.
- Fix issue in view-based source list example where items created with the add button couldn't be dragged.
## 2.0.2
- Fix #39: Badges not drawn correctly when Source List row is selected.
## 2.0.1
- Add missing note to the 2.0.0 release notes about marking `-[PXSourceList delegate]` and `-[PXSourceList dataSource]` as unavailable using \_\_attribute\_\_.
## 2.0.0
### New Features
- Added support for view-based mode to `PXSourceList`, whilst retaining legacy cell-based support. (View-based delegate methods added are detailed below).
- Added a new view-based Source List example target which includes an example of how to use drag-and-drop in the Source List.
- Added `PXSourceListTableCellView`, an `NSTableCellView` subclass which is useful when using `PXSourceList` in view-based mode.
- Added `PXSourceListBadgeView`, which can be used in `PXSourceListTableCellView`s to display badges. This class's drawing is done by the internal `PXSourceListBadgeCell` class.
- Added a generic `PXSourceListItem` data source model class which can be used for easily constructing data source models without having to implement your own class.
### API Changes
- **Incompatible change.* Marked `-[PXSourceList delegate]` and -[PXSourceList dataSource]` as unavailable using the “unavailable” \_\_attribute\_\_. These methods shouldnt be used because of the internal implementation of PXSourceList, and have been documented as such since version 0.8. However, adding this \_\_attribute\_\_ is more robust because a compile-time error will be generated if you use either of these methods.
- Added view-based Source List delegate methods to `PXSourceListDelegate`, namely:
- `-sourceList:viewForItem:`
- `-sourceList:rowViewForItem:`
- `-sourceList:didAddRowView:forRow:`
- `-sourceList:didRemoveRowView:forRow:`
- Added missing `PXSourceListDelegate` methods which map to their `NSOutlineViewDelegate` counterpart methods, namely:
- `-sourceList:toolTipForCell:rect:item:mouseLocation:`
- `-sourceList:typeSelectStringForItem:`
- `-sourceList:nextTypeSelectMatchFromItem:toItem:forString:`
- `-sourceList:shouldTypeSelectForEvent:withCurrentSearchString:`
- Moved the Source List delegate notification methods that were previously part of an `NSObject` category into `PXSourceListDelegate`. The methods affected are:
- `-sourceListSelectionIsChanging:`
- `-sourceListSelectionDidChange:`
- `-sourceListItemWillExpand:`
- `-sourceListItemDidExpand:`
- `-sourceListItemWillCollapse:`
- `-sourceListItemDidCollapse:`
- `-sourceListDeleteKeyPressedOnRows:`
### Bugfixes
- Fixed a *huge* bug where several delegate methods which weren't being called in version 0.x and 1.x of PXSourceList, by removing explicit implementations of `NSOutlineViewDelegate` methods in `PXSourceList` which are now forwarded using a shiny new proxy-based implementation. The stub method implementations removed from `PXSourceList` are:
- `-outlineView:numberOfChildrenOfItem:`
- `-outlineView:child:ofItem:`
- `-outlineView:isItemExpandable:`
- `-outlineView:objectValueForTableColumn:byItem:`
- `-outlineView:setObjectValue:forTableColumn:byItem:`
- `-outlineView:itemForPersistentObject:`
- `-outlineView:persistentObjectForItem:`
- `-outlineView:writeItems:toPasteboard:`
- `-outlineView:writeItems:toPasteboard:`
- `-outlineView:validateDrop:proposedItem:proposedChildIndex:`
- `-outlineView:acceptDrop:item:childIndex:`
- `-outlineView:namesOfPromisedFilesDroppedAtDestination:forDraggedItems:`
- `-outlineView:pasteboardWriterForItem:`
- `-outlineView:draggingSession:willBeginAtPoint:forItems:`
- `-outlineView:draggingSession:endedAtPoint:operation:`
- `-outlineView:updateDraggingItemsForDrag:`
- `-outlineView:shouldExpandItem:`
- `-outlineView:shouldTrackCell:forTableColumn:item:`
- `-outlineView:heightOfRowByItem:`
- `-outlineView:selectionIndexesForProposedSelection:`
- `-outlineView:dataCellForTableColumn:item:`
- `-outlineView:willDisplayCell:forTableColumn:item:`
- Fixed the PXSourceList framework's `CFBundleIdentifier`. It should have been `com.alexrozanski.PXSourceList`.
### Documentation, Documentation, Documentation
- Updated documentation for all public members of `PXSourceList` and its related classes and protocols.
- Added documentation for new classes and `PXSourceListDelegate` methods.
- Added documentation for `PXSourceList` delegate notifications.
- Added a Documentation target to the Xcode project, which can be used to build documentation from source using [appledoc](http://gentlebytes.com/appledoc/).
### Other Changes
- Removed `SourceListItem` from the old example project as it has been superseded by `PXSourceListItem`.
- Removed the TODO.rtf file from the project as all issues are now being tracked through GitHub.
- Upgraded the Xcode project to the Xcode 5 project format. `LastUpgradeCheck` was updated from `0450` to`0500`.
- Added a Release Notes file ;)
-12
View File
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.custom.PXSourceList.docset</string>
<key>CFBundleName</key>
<string>PXSourceList</string>
<key>DocSetFeedName</key>
<string>Custom documentation</string>
</dict>
</plist>
File diff suppressed because it is too large Load Diff
-23
View File
@@ -1,23 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf250
{\fonttbl\f0\fnil\fcharset0 Monaco;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\f0\fs20 \cf0 (Note: Parts that are styled with a strikeout have been completed.)\strike \strikec0 \
\
1. Source List badge colours for Graphite theme set in system preferences\strike0\striked0 \
\strike \strikec0 2. Implement custom drawing for drag and drop\
\strike0\striked0 - Implemented by default by the Outline View\
\strike \strikec0 3. Sort out way that Source List returns badge values \'96 use NSNumber?\strike0\striked0 \
- No, return NSNotFound for -badgeValueForItem: instead if item doesn't have badge \
4. Provide a bindings interface\
5. Cross-group keyboard implementation?\
6. Provide an isGroupItem: delegate method?\
7. Change the way the Source List handles drag and drop to something more like iTunes/Finder?\
8. Make GC-compatible\
\
Fix @nmarisi's bug:\
- If you uncheck "Show Vertical Scroller" in IB for the NSScrollView that the Source List is contained in, the bounds of the Source List are extended so that the badges are cut off.\
\
\strike \strikec0 Fix @davedelong's Bug:\
- Prevent images larger than that specified in the `iconSize` property being passed in to the sourceList:iconForItem: dataSource method.}