13 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
8 changed files with 373 additions and 359 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 411 KiB

@@ -1,7 +1,8 @@
<?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" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9059" systemVersion="15B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9059"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -10,7 +11,7 @@
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<menu title="AMainMenu" systemMenu="main" id="29">
<items>
<menuItem title="ViewBasedSourceList" id="56">
@@ -650,30 +651,29 @@
<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="1440" height="878"/>
<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"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<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="24" horizontalPageScroll="10" verticalLineScroll="24" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gyf-tN-jug">
<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"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" id="MeO-Mg-PDX">
<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="453"/>
<rect key="frame" x="0.0" y="0.0" width="192" height="0.0"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="0.0"/>
<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>
@@ -696,7 +696,7 @@
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eai-EG-dRe">
<rect key="frame" x="0.0" y="1" width="189" height="14"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<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"/>
@@ -704,6 +704,7 @@
</textFieldCell>
</textField>
</subviews>
<animations/>
<connections>
<outlet property="textField" destination="eai-EG-dRe" id="6Az-Vc-kqF"/>
</connections>
@@ -712,33 +713,38 @@
<rect key="frame" x="1" y="17" width="189" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4ER-hQ-M6f">
<imageView misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4ER-hQ-M6f">
<rect key="frame" x="3" y="0.0" width="17" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<animations/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSActionTemplate" id="VxW-Wd-dQJ"/>
</imageView>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GsV-wD-pbj">
<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"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<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 ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Wj-Kn-7YJ" customClass="PXSourceListBadgeView">
<customView misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Wj-Kn-7YJ" customClass="PXSourceListBadgeView">
<rect key="frame" x="164" y="2" width="22" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<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"/>
@@ -754,26 +760,30 @@
</connections>
</outlineView>
</subviews>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<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"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<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"/>
@@ -784,11 +794,11 @@
</button>
<button translatesAutoresizingMaskIntoConstraints="NO" id="iQU-Dr-qPa">
<rect key="frame" x="28" y="2" width="30" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<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"/>
@@ -808,6 +818,7 @@
<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"/>
@@ -815,11 +826,11 @@
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rTW-wK-rrG">
<rect key="frame" x="18" y="212" width="478" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<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"/>
@@ -831,8 +842,10 @@
<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"/>
@@ -845,6 +858,7 @@
<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">
@@ -859,7 +873,7 @@
</objects>
<resources>
<image name="NSActionTemplate" width="14" height="14"/>
<image name="NSAddTemplate" width="8" height="8"/>
<image name="NSRemoveTemplate" width="8" height="8"/>
<image name="NSAddTemplate" width="11" height="11"/>
<image name="NSRemoveTemplate" width="11" height="11"/>
</resources>
</document>
</document>
+2 -2
View File
@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.0.6</string>
<string>2.0.7</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>2.0.6</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>
+2 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'PXSourceList'
s.version = '2.0.6'
s.version = '2.0.7'
s.author = { 'Alex Rozanski' => 'alex@rozanski.me' }
s.license = 'BSD'
s.homepage = 'https://github.com/Perspx/PXSourceList'
@@ -23,6 +23,6 @@ Pod::Spec.new do |s|
s.osx.deployment_target = '10.7'
s.public_header_files = 'PXSourceList/*.h'
s.source = { :git => 'https://github.com/Perspx/PXSourceList.git', :tag => '2.0.6' }
s.source = { :git => 'https://github.com/Perspx/PXSourceList.git', :tag => '2.0.7' }
s.source_files = 'PXSourceList/**/*.{h,m}'
end
@@ -146,6 +146,11 @@ static NSArray * __fastPathForwardingDataSourceMethods = nil;
return [NSMethodSignature signatureWithObjCTypes:description.types];
}
- (BOOL) isKindOfClass:(Class)aClass
{
return NO;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL sourceSelector = anInvocation.selector;
+321 -330
View File
@@ -44,12 +44,12 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
#pragma mark - Setup/Teardown
- (id)initWithCoder:(NSCoder*)decoder
{
if(self=[super initWithCoder:decoder]) {
{
if(self=[super initWithCoder:decoder]) {
[self PXSL_setup];
}
return self;
}
return self;
}
- (id)initWithFrame:(NSRect)frameRect
@@ -57,7 +57,7 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
if((self = [super initWithFrame:frameRect])) {
[self PXSL_setup];
}
return self;
}
@@ -69,9 +69,10 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
- (void)dealloc
{
//Remove ourselves as the delegate and data source to be safe
[super setDataSource:nil];
[super setDelegate:nil];
_delegateDataSourceProxy = nil;
//Remove ourselves as the delegate and data source to be safe
[super setDataSource:nil];
[super setDelegate:nil];
}
@@ -80,7 +81,7 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
- (void)setDelegate:(id<PXSourceListDelegate>)aDelegate
{
self.delegateDataSourceProxy.delegate = aDelegate;
self.delegateDataSourceProxy.delegate = aDelegate;
[super setDelegate:nil];
if (aDelegate)
@@ -90,27 +91,27 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
- (void)setDataSource:(id<PXSourceListDataSource>)aDataSource
{
self.delegateDataSourceProxy.dataSource = aDataSource;
self.delegateDataSourceProxy.dataSource = aDataSource;
[super setDataSource:nil];
if (aDataSource)
[super setDataSource:self.delegateDataSourceProxy];
[self reloadData];
[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;
}
_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
@@ -141,90 +142,90 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
- (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;
}
}
}
[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;
{
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];
//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;
//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;
}
@@ -234,11 +235,11 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
if (self.isViewBasedSourceList)
return NO;
if([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:itemHasBadge:)]) {
return [self.delegateDataSourceProxy sourceList:self itemHasBadge:item];
}
return NO;
if([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:itemHasBadge:)]) {
return [self.delegateDataSourceProxy sourceList:self itemHasBadge:item];
}
return NO;
}
- (NSInteger)badgeValueForItem:(id)item
@@ -246,12 +247,12 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
// 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;
if([self.delegateDataSourceProxy respondsToSelector:@selector(sourceList:badgeValueForItem:)]) {
return [self.delegateDataSourceProxy sourceList:self badgeValueForItem:item];
}
return NSNotFound;
}
#pragma mark -
@@ -259,53 +260,53 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
- (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);
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 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;
}
@@ -315,51 +316,51 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
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);
}
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);
}
}
@@ -367,14 +368,14 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
//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];
id rowItem = [self itemAtRow:rowIndex];
if (![self itemHasBadge:rowItem])
return NSZeroSize;
if (![self itemHasBadge:rowItem])
return NSZeroSize;
self.reusableBadgeCell.integerValue = [self badgeValueForItem:rowItem];
return NSMakeSize(fmax(self.reusableBadgeCell.cellSize.width, minBadgeWidth), badgeHeight);
return NSMakeSize(fmax(self.reusableBadgeCell.cellSize.width, minBadgeWidth), badgeHeight);
}
- (void)viewDidMoveToSuperview
@@ -387,78 +388,68 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
#pragma mark Drawing
- (void)drawRow:(NSInteger)rowIndex clipRect:(NSRect)clipRect
{
[super drawRow:rowIndex clipRect: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);
}
//Use 10.6 NSImage drawing if we can
if([icon respondsToSelector:@selector(drawInRect:fromRect:operation:fraction:respectFlipped:hints:)]) {
[icon drawInRect:iconRect
fromRect:NSZeroRect
operation:NSCompositeSourceOver
fraction:1
respectFlipped:YES hints:nil];
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);
}
else {
[icon setFlipped:[self isFlipped]];
[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-rowRightMargin,
NSMidY(rowRect)-(badgeSize.height/2.0),
badgeSize.width,
badgeSize.height);
[self drawBadgeForRow:rowIndex inRect:badgeFrame];
}
[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
@@ -480,53 +471,53 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
- (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];
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 -
@@ -535,53 +526,53 @@ NSString * const PXSLDeleteKeyPressedOnRowsNotification = @"PXSourceListDeleteKe
- (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;
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])
// 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;
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:)])
// 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];
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];
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];
return [self isGroupItem:item];
}
@end
+1 -1
View File
@@ -28,7 +28,7 @@ Note that [in the OS X Human Interface Guidelines](https://developer.apple.com/l
## Using PXSourceList
### Installing with Cocoapods
### Installing with CocoaPods
You can install PXSourceList by adding the following line to your `Podfile`:
+4
View File
@@ -1,5 +1,9 @@
# 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.