aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m64
-rw-r--r--Interfaces/English.lproj/ContentPaginationView.xib37
-rw-r--r--Interfaces/English.lproj/DBView.xib925
-rw-r--r--Interfaces/English.lproj/FilterTableWindow.xib337
-rw-r--r--Resources/Images/button_filter.pngbin0 -> 343 bytes
-rw-r--r--Resources/Images/button_filter@2x.pngbin0 -> 450 bytes
-rw-r--r--Resources/Images/button_filter_active.pngbin0 -> 332 bytes
-rw-r--r--Resources/Images/button_filter_active@2x.pngbin0 -> 479 bytes
-rw-r--r--Source/SPAppController.m2
-rw-r--r--Source/SPConnectionController.m19
-rw-r--r--Source/SPContentFilterManager.h4
-rw-r--r--Source/SPContentFilterManager.m62
-rw-r--r--Source/SPDataImport.h47
-rw-r--r--Source/SPDataImport.m61
-rw-r--r--Source/SPDatabaseDocument.m12
-rw-r--r--Source/SPFillView.h47
-rw-r--r--Source/SPFillView.m61
-rw-r--r--Source/SPFilterTableController.h129
-rw-r--r--Source/SPFilterTableController.m706
-rw-r--r--Source/SPHistoryController.m78
-rw-r--r--Source/SPIndexesController.m1
-rw-r--r--Source/SPRuleFilterController.h165
-rw-r--r--Source/SPRuleFilterController.m1579
-rw-r--r--Source/SPTableContent.h160
-rw-r--r--Source/SPTableContent.m1991
-rw-r--r--Source/SPTableContentFilterController.h21
-rw-r--r--Source/SPTableContentFilterController.m5
-rw-r--r--Source/SPTableFilterParser.m3
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj104
29 files changed, 4217 insertions, 2403 deletions
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
index d1de87a1..09988dbe 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
@@ -87,43 +87,45 @@ typedef struct {
*/
- (void)_threadedKeepAlive
{
- @synchronized(self) {
- if(keepAliveThread) {
- NSLog(@"warning: overwriting existing keepAliveThread: %@, results may be unpredictable!",keepAliveThread);
+ @autoreleasepool {
+ @synchronized(self) {
+ if(keepAliveThread) {
+ NSLog(@"warning: overwriting existing keepAliveThread: %@, results may be unpredictable!",keepAliveThread);
+ }
+ keepAliveThread = [NSThread currentThread];
}
- keepAliveThread = [NSThread currentThread];
- }
-
- [keepAliveThread setName:[NSString stringWithFormat:@"SPMySQL connection keepalive monitor thread (id=%p)", self]];
- // If the maximum number of ping failures has been reached, determine whether to reconnect.
- if (keepAliveLastPingBlocked || keepAlivePingFailures >= 3) {
+ [keepAliveThread setName:[NSString stringWithFormat:@"SPMySQL connection keepalive monitor thread (id=%p)", self]];
- // If the connection has been used within the last fifteen minutes,
- // attempt a single reconnection in the background
- if (_elapsedSecondsSinceAbsoluteTime(lastConnectionUsedTime) < 60 * 15) {
- [self _reconnectAfterBackgroundConnectionLoss];
- }
- // Otherwise set the state to connection lost for automatic reconnect on
- // next use.
- else {
- state = SPMySQLConnectionLostInBackground;
- }
+ // If the maximum number of ping failures has been reached, determine whether to reconnect.
+ if (keepAliveLastPingBlocked || keepAlivePingFailures >= 3) {
- // Return as no further ping action required this cycle.
- goto end_cleanup;
- }
+ // If the connection has been used within the last fifteen minutes,
+ // attempt a single reconnection in the background
+ if (_elapsedSecondsSinceAbsoluteTime(lastConnectionUsedTime) < 60 * 15) {
+ [self _reconnectAfterBackgroundConnectionLoss];
+ }
+ // Otherwise set the state to connection lost for automatic reconnect on
+ // next use.
+ else {
+ state = SPMySQLConnectionLostInBackground;
+ }
- // Otherwise, perform a background ping.
- BOOL pingResult = [self _pingConnectionUsingLoopDelay:10000];
- if (pingResult) {
- keepAlivePingFailures = 0;
- } else {
- keepAlivePingFailures++;
- }
+ // Return as no further ping action required this cycle.
+ goto end_cleanup;
+ }
+
+ // Otherwise, perform a background ping.
+ BOOL pingResult = [self _pingConnectionUsingLoopDelay:10000];
+ if (pingResult) {
+ keepAlivePingFailures = 0;
+ } else {
+ keepAlivePingFailures++;
+ }
end_cleanup:
- @synchronized(self) {
- keepAliveThread = nil;
+ @synchronized(self) {
+ keepAliveThread = nil;
+ }
}
}
diff --git a/Interfaces/English.lproj/ContentPaginationView.xib b/Interfaces/English.lproj/ContentPaginationView.xib
index af90ff3e..ce178ae3 100644
--- a/Interfaces/English.lproj/ContentPaginationView.xib
+++ b/Interfaces/English.lproj/ContentPaginationView.xib
@@ -6,39 +6,17 @@
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/>
</dependencies>
<objects>
- <customObject id="-2" userLabel="File's Owner" customClass="SPTableContent">
+ <customObject id="-2" userLabel="File's Owner" customClass="ContentPaginationViewController">
<connections>
- <outlet property="paginationBox" destination="2" id="plQ-Ch-kaG"/>
<outlet property="paginationGoButton" destination="cXy-Rp-Edv" id="tIC-S1-mTE"/>
<outlet property="paginationPageField" destination="Vtc-V2-j0v" id="cwZ-g9-Qbj"/>
- <outlet property="paginationView" destination="1" id="46"/>
- <outlet property="paginationViewController" destination="hrG-95-pXC" id="yiZ-Ax-bVs"/>
+ <outlet property="paginationPageStepper" destination="S7c-s1-KCT" id="c4Q-RA-Dny"/>
+ <outlet property="view" destination="ZlV-bd-j4D" id="zKu-MD-Rhn"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
- <customObject id="-3" userLabel="Application"/>
- <customView id="1">
- <rect key="frame" x="0.0" y="0.0" width="330" height="89"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
- <subviews>
- <box autoresizesSubviews="NO" wantsLayer="YES" boxType="custom" borderType="line" titlePosition="noTitle" id="2">
- <rect key="frame" x="0.0" y="-47" width="330" height="136"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
- <view key="contentView">
- <rect key="frame" x="1" y="1" width="328" height="134"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- </view>
- <color key="borderColor" white="0.0" alpha="0.20000000300000001" colorSpace="calibratedWhite"/>
- <color key="fillColor" white="0.94758063550000005" alpha="1" colorSpace="calibratedWhite"/>
- </box>
- </subviews>
- </customView>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
<userDefaultsController representsSharedInstance="YES" id="24"/>
- <viewController id="hrG-95-pXC" userLabel="Popover View Controller">
- <connections>
- <outlet property="view" destination="ZlV-bd-j4D" id="zor-2S-MNt"/>
- </connections>
- </viewController>
<customView id="ZlV-bd-j4D" userLabel="Popover Content View">
<rect key="frame" x="0.0" y="0.0" width="330" height="89"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
@@ -51,7 +29,7 @@
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<connections>
- <action selector="navigatePaginationFromButton:" target="-2" id="28B-A6-zUZ"/>
+ <action selector="paginationGoAction:" target="-2" id="86K-61-7Jl"/>
<binding destination="24" name="enabled" keyPath="values.LimitResults" id="AkR-JA-LEs"/>
</connections>
</button>
@@ -69,6 +47,8 @@
</textFieldCell>
<connections>
<action selector="performClick:" target="cXy-Rp-Edv" id="4Uu-gW-Xhz"/>
+ <binding destination="-2" name="maxValue" keyPath="maxPage" id="JKa-ju-VyP"/>
+ <binding destination="-2" name="value" keyPath="page" previousBinding="JKa-ju-VyP" id="21h-9k-7Xk"/>
<binding destination="24" name="enabled" keyPath="values.LimitResults" id="rJH-8j-rCm"/>
</connections>
</textField>
@@ -108,7 +88,8 @@
<font key="font" metaFont="smallSystem"/>
</stepperCell>
<connections>
- <action selector="takeIntValueFrom:" target="Vtc-V2-j0v" id="JYr-Pw-YZ2"/>
+ <binding destination="-2" name="maxValue" keyPath="maxPage" id="h3F-DA-oCz"/>
+ <binding destination="-2" name="value" keyPath="page" previousBinding="h3F-DA-oCz" id="W48-Lg-Q7t"/>
<binding destination="24" name="enabled" keyPath="values.LimitResults" id="Nl1-31-oLD"/>
</connections>
</stepper>
diff --git a/Interfaces/English.lproj/DBView.xib b/Interfaces/English.lproj/DBView.xib
index 8742c655..b55710d3 100644
--- a/Interfaces/English.lproj/DBView.xib
+++ b/Interfaces/English.lproj/DBView.xib
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="13F1507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment identifier="macosx"/>
- <development version="5100" identifier="xcode"/>
- <plugIn identifier="com.apple.WebKitIBPlugin" version="11762"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/>
+ <development version="8000" identifier="xcode"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14109"/>
+ <plugIn identifier="com.apple.WebKitIBPlugin" version="14109"/>
+ <capability name="box content view" minToolsVersion="7.0"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="SPDatabaseDocument">
@@ -470,19 +472,19 @@
<tabViewItems>
<tabViewItem label="Structure" identifier="source" id="28">
<view key="view" identifier="StructureTabView" id="29">
- <rect key="frame" x="10" y="7" width="702" height="546"/>
+ <rect key="frame" x="10" y="7" width="706" height="546"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<splitView dividerStyle="thin" id="674">
- <rect key="frame" x="7" y="10" width="691" height="532"/>
+ <rect key="frame" x="7" y="10" width="695" height="532"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<customView identifier="TableStructureColumnsView" id="673">
- <rect key="frame" x="0.0" y="0.0" width="691" height="329"/>
+ <rect key="frame" x="0.0" y="0.0" width="695" height="329"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button toolTip="Edit Table Details (⌘4)" id="6009">
- <rect key="frame" x="628" y="-1" width="32" height="25"/>
+ <rect key="frame" x="632" y="-1" width="32" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_edit" imagePosition="overlaps" alignment="center" enabled="NO" state="on" inset="2" id="6010">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -495,12 +497,12 @@
</connections>
</button>
<imageView id="5129">
- <rect key="frame" x="-1" y="0.0" width="688" height="23"/>
+ <rect key="frame" x="-1" y="0.0" width="692" height="23"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" image="button_bar_spacer" id="5130"/>
</imageView>
<popUpButton id="8025">
- <rect key="frame" x="659" y="0.0" width="36" height="23"/>
+ <rect key="frame" x="663" y="0.0" width="36" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<popUpButtonCell key="cell" type="bevel" bezelStyle="regularSquare" imagePosition="right" alignment="center" lineBreakMode="truncatingTail" state="on" inset="2" pullsDown="YES" arrowPosition="noArrow" selectedItem="8028" id="8026">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -595,14 +597,14 @@
</connections>
</button>
<scrollView focusRingType="none" autohidesScrollers="YES" horizontalLineScroll="18" horizontalPageScroll="10" verticalLineScroll="18" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="231">
- <rect key="frame" x="-1" y="22" width="692" height="306"/>
+ <rect key="frame" x="-1" y="22" width="696" height="306"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" id="07n-i9-O0i">
- <rect key="frame" x="1" y="0.0" width="690" height="305"/>
+ <rect key="frame" x="1" y="0.0" width="694" height="305"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView identifier="TableStructureColumnsTableView" focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" multipleSelection="NO" autosaveName="SPTableStructureSource" rowHeight="16" headerView="3926" id="232" customClass="SPTableView">
- <rect key="frame" x="0.0" y="0.0" width="690" height="283"/>
+ <rect key="frame" x="0.0" y="0.0" width="694" height="288"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -809,7 +811,7 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" id="3926">
- <rect key="frame" x="0.0" y="0.0" width="690" height="17"/>
+ <rect key="frame" x="0.0" y="0.0" width="694" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<connections>
<outlet property="menu" destination="8056" id="Uxa-5O-Q0T"/>
@@ -833,11 +835,11 @@
</subviews>
</customView>
<customView identifier="TableStructureIndexesView" id="672">
- <rect key="frame" x="0.0" y="330" width="691" height="202"/>
+ <rect key="frame" x="0.0" y="330" width="695" height="202"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView id="5148">
- <rect key="frame" x="-1" y="0.0" width="688" height="23"/>
+ <rect key="frame" x="-1" y="0.0" width="692" height="23"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" image="button_bar_spacer" id="5149"/>
</imageView>
@@ -877,20 +879,20 @@
</connections>
</button>
<scrollView identifier="TableStructureIndexesTableScrollView" focusRingType="none" autohidesScrollers="YES" horizontalLineScroll="18" horizontalPageScroll="10" verticalLineScroll="18" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="287">
- <rect key="frame" x="-1" y="22" width="692" height="160"/>
+ <rect key="frame" x="-1" y="22" width="696" height="160"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" id="N3x-Gt-GZH">
- <rect key="frame" x="1" y="0.0" width="690" height="159"/>
+ <rect key="frame" x="1" y="0.0" width="694" height="159"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView identifier="TableStructureIndexesTableView" focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="16" headerView="3923" id="289" customClass="SPTableView">
- <rect key="frame" x="0.0" y="0.0" width="690" height="136"/>
+ <rect key="frame" x="0.0" y="0.0" width="694" height="142"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
- <tableColumn identifier="Non_unique" editable="NO" width="79.5" minWidth="40" maxWidth="1000" id="288">
+ <tableColumn identifier="Non_unique" editable="NO" width="80" minWidth="40" maxWidth="1000" id="288">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Non_unique">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -903,7 +905,7 @@
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
- <tableColumn identifier="Key_name" editable="NO" width="69.5" minWidth="40" maxWidth="1000" id="290">
+ <tableColumn identifier="Key_name" editable="NO" width="70" minWidth="40" maxWidth="1000" id="290">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Key_name">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -916,7 +918,7 @@
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
- <tableColumn identifier="Seq_in_index" editable="NO" width="81.5" minWidth="10" maxWidth="1000" id="291">
+ <tableColumn identifier="Seq_in_index" editable="NO" width="82" minWidth="10" maxWidth="1000" id="291">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Seq_in_index">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -929,7 +931,7 @@
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
- <tableColumn identifier="Column_name" editable="NO" width="88.5" minWidth="10" maxWidth="1000" id="292">
+ <tableColumn identifier="Column_name" editable="NO" width="89" minWidth="10" maxWidth="1000" id="292">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Column_name">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -942,7 +944,7 @@
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
- <tableColumn identifier="Collation" editable="NO" width="59.5" minWidth="10" maxWidth="1000" id="293">
+ <tableColumn identifier="Collation" editable="NO" width="60" minWidth="10" maxWidth="1000" id="293">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Collation">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -955,7 +957,7 @@
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
- <tableColumn identifier="Cardinality" editable="NO" width="69.5" minWidth="10" maxWidth="1000" id="294">
+ <tableColumn identifier="Cardinality" editable="NO" width="70" minWidth="10" maxWidth="1000" id="294">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Cardinality">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -968,7 +970,7 @@
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
- <tableColumn identifier="Sub_part" editable="NO" width="59.5" minWidth="10" maxWidth="1000" id="295">
+ <tableColumn identifier="Sub_part" editable="NO" width="60" minWidth="10" maxWidth="1000" id="295">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Sub_part">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -981,7 +983,7 @@
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
- <tableColumn identifier="Packed" editable="NO" width="46.5" minWidth="10" maxWidth="1000" id="296">
+ <tableColumn identifier="Packed" editable="NO" width="47" minWidth="10" maxWidth="1000" id="296">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Packed">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -1025,12 +1027,12 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" id="3923">
- <rect key="frame" x="0.0" y="0.0" width="690" height="17"/>
+ <rect key="frame" x="0.0" y="0.0" width="694" height="17"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<view id="6024">
- <rect key="frame" x="0.0" y="182" width="691" height="20"/>
+ <rect key="frame" x="0.0" y="182" width="695" height="20"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<textField verticalHuggingPriority="750" id="6027">
@@ -1043,7 +1045,7 @@
</textFieldCell>
</textField>
<imageView id="4503">
- <rect key="frame" x="674" y="3" width="10" height="13"/>
+ <rect key="frame" x="678" y="3" width="10" height="13"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" image="grabber-horizontal" id="4504"/>
</imageView>
@@ -1065,302 +1067,137 @@
</tabViewItem>
<tabViewItem label="Content" identifier="content" id="26">
<view key="view" identifier="ContentTabView" id="27">
- <rect key="frame" x="10" y="7" width="706" height="544"/>
+ <rect key="frame" x="10" y="7" width="706" height="546"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
- <imageView id="5179">
- <rect key="frame" x="6" y="10" width="696" height="23"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
- <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" image="button_bar_spacer" id="5180"/>
- </imageView>
- <button toolTip="Add row (⌥⌘A)" id="5175">
- <rect key="frame" x="6" y="9" width="32" height="25"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_add" imagePosition="only" alignment="center" alternateImage="button_add" enabled="NO" inset="2" id="5184">
- <behavior key="behavior" lightByContents="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent">a</string>
- <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="addRow:" target="67" id="5188"/>
- </connections>
- </button>
- <button toolTip="Refresh table contents (⌘R)" id="5176">
- <rect key="frame" x="100" y="9" width="32" height="25"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_refresh" imagePosition="overlaps" alignment="center" alternateImage="button_refresh" inset="2" id="5183">
- <behavior key="behavior" lightByContents="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent">r</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="reloadTable:" target="67" id="5185"/>
- </connections>
- </button>
- <button toolTip="Delete selected row(s) (⌫)" id="5177">
- <rect key="frame" x="37" y="9" width="32" height="25"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_remove" imagePosition="only" alignment="center" alternateImage="button_remove" enabled="NO" inset="2" id="5182">
- <behavior key="behavior" lightByContents="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent"></string>
- </buttonCell>
- <connections>
- <action selector="removeRow:" target="67" id="5187"/>
- </connections>
- </button>
- <button toolTip="Duplicate selected row (⌘D)" id="5178">
- <rect key="frame" x="68" y="9" width="32" height="25"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_duplicate" imagePosition="overlaps" alignment="center" alternateImage="button_duplicate" enabled="NO" inset="2" id="5181">
- <behavior key="behavior" lightByContents="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent">d</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="duplicateRow:" target="67" id="7838"/>
- </connections>
- </button>
- <button toolTip="Toggle between editing simple text cells as a spreadsheet or in pop-up sheets" id="5201">
- <rect key="frame" x="131" y="9" width="32" height="25"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_edit_mode" imagePosition="overlaps" alignment="center" alternateImage="button_edit_mode_selected" inset="2" id="5202">
- <behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
- <font key="font" metaFont="system"/>
- </buttonCell>
- <connections>
- <binding destination="1907" name="value" keyPath="values.EditInSheetEnabled" id="6351"/>
- </connections>
- </button>
- <textField verticalHuggingPriority="750" id="261">
- <rect key="frame" x="166" y="6" width="421" height="22"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
- <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" id="3759">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <button toolTip="View next page of results" id="6647">
- <rect key="frame" x="654" y="9" width="32" height="25"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_right" imagePosition="only" alignment="center" alternateImage="button_right" inset="2" id="6648">
- <behavior key="behavior" lightByContents="YES"/>
- <font key="font" metaFont="system"/>
- </buttonCell>
- <connections>
- <action selector="navigatePaginationFromButton:" target="67" id="6666"/>
- </connections>
- </button>
- <button toolTip="View previous page of results" id="6650">
- <rect key="frame" x="592" y="9" width="32" height="25"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_left" imagePosition="only" alignment="center" alternateImage="button_left" inset="2" id="6651">
- <behavior key="behavior" lightByContents="YES"/>
- <font key="font" metaFont="system"/>
- </buttonCell>
- <connections>
- <action selector="navigatePaginationFromButton:" target="67" id="6665"/>
- </connections>
- </button>
- <button toolTip="Jump to page (⌘J) or view pagination options" id="6653">
- <rect key="frame" x="623" y="9" width="32" height="25"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_pagination" imagePosition="overlaps" alignment="center" inset="2" id="6654">
- <behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
- <font key="font" metaFont="smallSystem"/>
- <string key="keyEquivalent">j</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="togglePagination:" target="67" id="6662"/>
- </connections>
- </button>
- <splitView autosaveName="" dividerStyle="thin" id="yTv-Lq-Y3H" customClass="SPSplitView">
- <rect key="frame" x="6" y="33" width="696" height="505"/>
+ <customView misplaced="YES" id="GZS-nP-BvF" userLabel="Content Area Runtime Container">
+ <rect key="frame" x="7" y="10" width="695" height="529"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
- <customView id="gSO-0R-Xdc">
- <rect key="frame" x="0.0" y="0.0" width="696" height="25"/>
- <autoresizingMask key="autoresizingMask"/>
+ <customView misplaced="YES" id="vcX-Xr-0cm" userLabel="Table Content Container">
+ <rect key="frame" x="0.0" y="0.0" width="695" height="458"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
- <scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" id="CIQ-tc-1Fn">
- <rect key="frame" x="0.0" y="-1" width="607" height="27"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="kdv-Wp-s5h">
- <rect key="frame" x="0.0" y="0.0" width="607" height="27"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <ruleEditor nestingMode="compound" canRemoveAllRows="YES" rowHeight="25" id="FF9-z2-9od">
- <rect key="frame" x="0.0" y="0.0" width="607" height="27"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
- <connections>
- <outlet property="delegate" destination="ki9-Po-bdr" id="rFd-07-AiC"/>
- </connections>
- </ruleEditor>
- </subviews>
- </clipView>
- <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" controlSize="small" horizontal="YES" id="dIO-Od-K6g">
- <rect key="frame" x="-100" y="-100" width="223" height="11"/>
- <autoresizingMask key="autoresizingMask"/>
- </scroller>
- <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" controlSize="small" horizontal="NO" id="wlW-vM-FUZ">
- <rect key="frame" x="-100" y="-100" width="11" height="133"/>
- <autoresizingMask key="autoresizingMask"/>
- </scroller>
- </scrollView>
- <button verticalHuggingPriority="750" id="gTZ-sd-Ef7">
- <rect key="frame" x="615" y="2" width="54" height="19"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="roundRect" title="Filter" bezelStyle="roundedRect" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="khN-PI-iEA">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="smallSystem"/>
+ <imageView id="5179">
+ <rect key="frame" x="-1" y="0.0" width="696" height="23"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+ <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" image="button_bar_spacer" id="5180"/>
+ </imageView>
+ <button toolTip="Add row (⌥⌘A)" id="5175">
+ <rect key="frame" x="-1" y="-1" width="32" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_add" imagePosition="only" alignment="center" alternateImage="button_add" enabled="NO" inset="2" id="5184">
+ <behavior key="behavior" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent">a</string>
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
</buttonCell>
<connections>
- <action selector="filterTable:" target="67" id="RqZ-Kt-0nW"/>
+ <action selector="addRow:" target="67" id="5188"/>
</connections>
</button>
- </subviews>
- </customView>
- <customView id="AjX-OA-b5u">
- <rect key="frame" x="0.0" y="26" width="696" height="479"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <progressIndicator horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" id="858">
- <rect key="frame" x="674" y="458" width="16" height="16"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
- </progressIndicator>
- <button verticalHuggingPriority="750" id="4676">
- <rect key="frame" x="614" y="455" width="54" height="19"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
- <buttonCell key="cell" type="roundRect" title="Filter" bezelStyle="roundedRect" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="4677">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <button toolTip="Refresh table contents (⌘R)" id="5176">
+ <rect key="frame" x="93" y="-1" width="32" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_refresh" imagePosition="overlaps" alignment="center" alternateImage="button_refresh" inset="2" id="5183">
+ <behavior key="behavior" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent">r</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="reloadTable:" target="67" id="5185"/>
+ </connections>
+ </button>
+ <button toolTip="Delete selected row(s) (⌫)" id="5177">
+ <rect key="frame" x="30" y="-1" width="32" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_remove" imagePosition="only" alignment="center" alternateImage="button_remove" enabled="NO" inset="2" id="5182">
+ <behavior key="behavior" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent"></string>
+ </buttonCell>
+ <connections>
+ <action selector="removeRow:" target="67" id="5187"/>
+ </connections>
+ </button>
+ <button toolTip="Duplicate selected row (⌘D)" id="5178">
+ <rect key="frame" x="61" y="-1" width="32" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_duplicate" imagePosition="overlaps" alignment="center" alternateImage="button_duplicate" enabled="NO" inset="2" id="5181">
+ <behavior key="behavior" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent">d</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="duplicateRow:" target="67" id="7838"/>
+ </connections>
+ </button>
+ <button toolTip="Toggle between editing simple text cells as a spreadsheet or in pop-up sheets" id="5201">
+ <rect key="frame" x="124" y="-1" width="32" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_edit_mode" imagePosition="overlaps" alignment="center" alternateImage="button_edit_mode_selected" inset="2" id="5202">
+ <behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <binding destination="1907" name="value" keyPath="values.EditInSheetEnabled" id="6351"/>
+ </connections>
+ </button>
+ <textField verticalHuggingPriority="750" misplaced="YES" id="261">
+ <rect key="frame" x="193" y="-4" width="362" height="22"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" id="3759">
<font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <button toolTip="View next page of results" id="6647">
+ <rect key="frame" x="647" y="-1" width="32" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_right" imagePosition="only" alignment="center" alternateImage="button_right" inset="2" id="6648">
+ <behavior key="behavior" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
</buttonCell>
<connections>
- <action selector="filterTable:" target="67" id="4678"/>
+ <action selector="navigatePaginationFromButton:" target="67" id="6666"/>
</connections>
</button>
- <splitView vertical="YES" id="7777">
- <rect key="frame" x="-1" y="451" width="606" height="27"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
- <subviews>
- <customView id="7778">
- <rect key="frame" x="0.0" y="0.0" width="314" height="27"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <textField verticalHuggingPriority="750" id="3971">
- <rect key="frame" x="7" y="6" width="67" height="14"/>
- <autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
- <textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Search:" id="3972">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <popUpButton toolTip="Choose a field you want to use for your search" verticalHuggingPriority="750" id="146">
- <rect key="frame" x="80" y="3" width="119" height="20"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
- <popUpButtonCell key="cell" type="roundRect" title="field" bezelStyle="roundedRect" alignment="left" controlSize="small" lineBreakMode="truncatingTail" enabled="NO" state="on" borderStyle="borderAndBezel" inset="2" arrowPosition="arrowAtCenter" preferredEdge="maxY" selectedItem="144" id="3754">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="smallSystem"/>
- <menu key="menu" title="OtherViews" id="147">
- <items>
- <menuItem title="field" state="on" id="144"/>
- </items>
- </menu>
- </popUpButtonCell>
- <connections>
- <action selector="setCompareTypes:" target="67" id="196"/>
- </connections>
- </popUpButton>
- <popUpButton toolTip="Choose a search operator" verticalHuggingPriority="750" id="156">
- <rect key="frame" x="206" y="0.0" width="105" height="25"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
- <popUpButtonCell key="cell" type="roundRect" title="=" bezelStyle="roundedRect" alignment="left" controlSize="small" lineBreakMode="truncatingTail" enabled="NO" state="on" borderStyle="borderAndBezel" inset="2" arrowPosition="arrowAtCenter" preferredEdge="maxY" selectedItem="154" id="3755">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="smallSystem"/>
- <menu key="menu" title="OtherViews" id="157">
- <items>
- <menuItem title="=" state="on" id="154"/>
- </items>
- </menu>
- </popUpButtonCell>
- <connections>
- <action selector="toggleFilterField:" target="67" id="4685"/>
- </connections>
- </popUpButton>
- </subviews>
- </customView>
- <customView id="7779">
- <rect key="frame" x="323" y="0.0" width="283" height="27"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <searchField hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" id="6371">
- <rect key="frame" x="168" y="4" width="110" height="19"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMinY="YES"/>
- <searchFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" bezelStyle="round" sendsSearchStringImmediately="YES" sendsWholeSearchString="YES" id="6372">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
- </searchFieldCell>
- <connections>
- <action selector="filterTable:" target="67" id="6536"/>
- </connections>
- </searchField>
- <searchField hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" id="6369">
- <rect key="frame" x="5" y="4" width="110" height="19"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
- <searchFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" bezelStyle="round" id="6370">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
- </searchFieldCell>
- </searchField>
- <textField hidden="YES" verticalHuggingPriority="750" id="6364">
- <rect key="frame" x="128" y="6" width="28" height="14"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
- <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="AND" id="6365">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <searchField wantsLayer="YES" verticalHuggingPriority="750" id="3963">
- <rect key="frame" x="5" y="4" width="276" height="19"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
- <searchFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" enabled="NO" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" sendsSearchStringImmediately="YES" sendsWholeSearchString="YES" id="3964">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
- </searchFieldCell>
- <connections>
- <action selector="filterTable:" target="67" id="4680"/>
- </connections>
- </searchField>
- </subviews>
- </customView>
- </subviews>
- <holdingPriorities>
- <real value="250"/>
- <real value="250"/>
- </holdingPriorities>
+ <button toolTip="View previous page of results" id="6650">
+ <rect key="frame" x="585" y="-1" width="32" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_left" imagePosition="only" alignment="center" alternateImage="button_left" inset="2" id="6651">
+ <behavior key="behavior" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
<connections>
- <outlet property="delegate" destination="67" id="7780"/>
+ <action selector="navigatePaginationFromButton:" target="67" id="6665"/>
</connections>
- </splitView>
- <scrollView focusRingType="none" autohidesScrollers="YES" horizontalLineScroll="18" horizontalPageScroll="10" verticalLineScroll="18" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="35">
- <rect key="frame" x="-1" y="-2" width="696" height="450"/>
+ </button>
+ <button toolTip="Jump to page (⌘J) or view pagination options" id="6653">
+ <rect key="frame" x="616" y="-1" width="32" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_pagination" imagePosition="overlaps" alignment="center" inset="2" id="6654">
+ <behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
+ <font key="font" metaFont="smallSystem"/>
+ <string key="keyEquivalent">j</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="togglePagination:" target="67" id="6662"/>
+ </connections>
+ </button>
+ <scrollView focusRingType="none" misplaced="YES" autohidesScrollers="YES" horizontalLineScroll="18" horizontalPageScroll="10" verticalLineScroll="18" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="35">
+ <rect key="frame" x="-1" y="22" width="695" height="436"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" id="lwO-LP-RWZ">
- <rect key="frame" x="1" y="0.0" width="694" height="449"/>
- <autoresizingMask key="autoresizingMask"/>
+ <rect key="frame" x="1" y="0.0" width="693" height="435"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView identifier="TableContentTableView" focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" alternatingRowBackgroundColors="YES" autosaveColumns="NO" rowHeight="16" headerView="3920" id="36" customClass="SPCopyTable">
- <rect key="frame" x="0.0" y="0.0" width="694" height="426"/>
+ <rect key="frame" x="0.0" y="0.0" width="693" height="418"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -1397,21 +1234,78 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" id="3920">
- <rect key="frame" x="0.0" y="0.0" width="694" height="17"/>
+ <rect key="frame" x="0.0" y="0.0" width="693" height="17"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
+ <progressIndicator wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" misplaced="YES" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" id="858">
+ <rect key="frame" x="561" y="4" width="16" height="16"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+ </progressIndicator>
+ <button toolTip="Show/Hide table content filters" horizontalHuggingPriority="750" verticalHuggingPriority="750" misplaced="YES" id="LYg-Ux-Lph">
+ <rect key="frame" x="155" y="-1" width="32" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="button_filter" imagePosition="overlaps" alignment="center" alternateImage="button_filter_active" inset="2" id="WNc-7G-qPQ">
+ <behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="toggleRuleEditorVisible:" target="67" id="Os7-vn-sUJ"/>
+ </connections>
+ </button>
+ </subviews>
+ </customView>
+ <customView misplaced="YES" id="9oo-u0-ndW" userLabel="Filter Rule Editor Container">
+ <rect key="frame" x="0.0" y="458" width="695" height="71"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
+ <subviews>
+ <scrollView misplaced="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" id="CIQ-tc-1Fn">
+ <rect key="frame" x="0.0" y="31" width="694" height="40"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="kdv-Wp-s5h">
+ <rect key="frame" x="0.0" y="0.0" width="694" height="40"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <ruleEditor nestingMode="compound" rowHeight="29" id="FF9-z2-9od">
+ <rect key="frame" x="0.0" y="0.0" width="694" height="40"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
+ <connections>
+ <outlet property="delegate" destination="ki9-Po-bdr" id="rFd-07-AiC"/>
+ </connections>
+ </ruleEditor>
+ </subviews>
+ </clipView>
+ <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="dIO-Od-K6g">
+ <rect key="frame" x="-100" y="-100" width="223" height="15"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="wlW-vM-FUZ">
+ <rect key="frame" x="-100" y="-100" width="15" height="133"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ </scrollView>
+ <customView misplaced="YES" id="GJn-1I-e7O" customClass="SPFillView">
+ <rect key="frame" x="0.0" y="30" width="695" height="1"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="string" keyPath="systemColorOfName" value="gridColor"/>
+ </userDefinedRuntimeAttributes>
+ </customView>
+ <button verticalHuggingPriority="750" misplaced="YES" id="4676">
+ <rect key="frame" x="616" y="5" width="54" height="19"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="roundRect" title="Filter" bezelStyle="roundedRect" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="4677">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="smallSystem"/>
+ </buttonCell>
+ <connections>
+ <action selector="filterTable:" target="ki9-Po-bdr" id="eAC-YD-du3"/>
+ </connections>
+ </button>
</subviews>
</customView>
</subviews>
- <holdingPriorities>
- <real value="250"/>
- <real value="250"/>
- </holdingPriorities>
- <connections>
- <outlet property="delegate" destination="ki9-Po-bdr" id="28j-fv-HkI"/>
- </connections>
- </splitView>
+ </customView>
</subviews>
</view>
</tabViewItem>
@@ -1781,7 +1675,7 @@ Gw
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView identifier="CustomQueryResultsTableView" focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" autosaveColumns="NO" rowHeight="16" headerView="7227" id="7224" customClass="SPCopyTable">
- <rect key="frame" x="0.0" y="0.0" width="694" height="190"/>
+ <rect key="frame" x="0.0" y="0.0" width="694" height="196"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -1958,14 +1852,14 @@ Gw
<rect key="frame" x="109" y="0.0" width="554" height="72"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" id="fhZ-nZ-AwI">
- <rect key="frame" x="1" y="1" width="552" height="70"/>
+ <rect key="frame" x="1" y="1" width="537" height="70"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView importsGraphics="NO" richText="NO" findStyle="panel" continuousSpellChecking="YES" smartInsertDelete="YES" id="8236">
- <rect key="frame" x="0.0" y="0.0" width="552" height="70"/>
+ <rect key="frame" x="0.0" y="0.0" width="537" height="70"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
- <size key="minSize" width="552" height="70"/>
+ <size key="minSize" width="537" height="70"/>
<size key="maxSize" width="1097" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<connections>
@@ -2003,14 +1897,14 @@ Gw
<rect key="frame" x="109" y="0.0" width="554" height="199"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" id="nV8-ly-BSi">
- <rect key="frame" x="1" y="1" width="552" height="197"/>
+ <rect key="frame" x="1" y="1" width="537" height="197"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView editable="NO" importsGraphics="NO" richText="NO" id="8242" customClass="SPTextView">
- <rect key="frame" x="0.0" y="0.0" width="552" height="197"/>
+ <rect key="frame" x="0.0" y="0.0" width="537" height="197"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
- <size key="minSize" width="552" height="197"/>
+ <size key="minSize" width="537" height="197"/>
<size key="maxSize" width="1097" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<connections>
@@ -2336,7 +2230,7 @@ Gw
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView identifier="TableRelationsTableView" focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveColumns="NO" rowHeight="16" headerView="5545" id="5548" customClass="SPCopyTable">
- <rect key="frame" x="0.0" y="0.0" width="694" height="450"/>
+ <rect key="frame" x="0.0" y="0.0" width="694" height="456"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -2523,7 +2417,7 @@ Gw
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView identifier="TableTriggersTableView" focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveColumns="NO" rowHeight="16" headerView="6704" id="6701" customClass="SPCopyTable">
- <rect key="frame" x="0.0" y="0.0" width="694" height="450"/>
+ <rect key="frame" x="0.0" y="0.0" width="694" height="456"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -3417,7 +3311,7 @@ Gw
<box autoresizesSubviews="NO" borderType="line" title="Table:" id="5660">
<rect key="frame" x="17" y="231" width="345" height="56"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <view key="contentView" id="G3T-Zw-qVw">
+ <view key="contentView" id="XVR-xN-dOO">
<rect key="frame" x="1" y="1" width="343" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
@@ -3454,7 +3348,7 @@ Gw
<box autoresizesSubviews="NO" borderType="line" title="Name" id="7798">
<rect key="frame" x="17" y="291" width="345" height="58"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <view key="contentView" id="PBs-KU-8Dt">
+ <view key="contentView" id="uQj-ax-2Ux">
<rect key="frame" x="1" y="1" width="343" height="42"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
@@ -3485,7 +3379,7 @@ Gw
<box autoresizesSubviews="NO" borderType="line" title="Action" id="5659">
<rect key="frame" x="17" y="49" width="345" height="87"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <view key="contentView" id="Z9B-3e-PSv">
+ <view key="contentView" id="fmY-UV-qsS">
<rect key="frame" x="1" y="1" width="343" height="71"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
@@ -3555,7 +3449,7 @@ Gw
<box autoresizesSubviews="NO" borderType="line" title="References" id="5652">
<rect key="frame" x="17" y="140" width="345" height="87"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <view key="contentView" id="APb-Qe-OSq">
+ <view key="contentView" id="pjf-Q1-6fe">
<rect key="frame" x="1" y="1" width="343" height="71"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
@@ -3674,7 +3568,7 @@ Gw
<box autoresizesSubviews="NO" borderType="line" title="Trigger:" id="6770">
<rect key="frame" x="17" y="219" width="326" height="109"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
- <view key="contentView" id="Otf-WX-kyk">
+ <view key="contentView" id="vJS-Ph-4at">
<rect key="frame" x="1" y="1" width="324" height="93"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
@@ -3891,14 +3785,14 @@ DQ
<rect key="frame" x="-1" y="35" width="383" height="206"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" id="w4Z-p1-GZA">
- <rect key="frame" x="1" y="1" width="381" height="204"/>
+ <rect key="frame" x="1" y="1" width="366" height="204"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView editable="NO" importsGraphics="NO" id="8219">
- <rect key="frame" x="0.0" y="0.0" width="381" height="204"/>
+ <rect key="frame" x="0.0" y="0.0" width="366" height="204"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
- <size key="minSize" width="381" height="204"/>
+ <size key="minSize" width="366" height="204"/>
<size key="maxSize" width="753" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<connections>
@@ -4071,14 +3965,14 @@ Gw
<rect key="frame" x="20" y="45" width="365" height="177"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" id="aEl-zD-ga2">
- <rect key="frame" x="1" y="1" width="363" height="175"/>
+ <rect key="frame" x="1" y="1" width="348" height="175"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView editable="NO" importsGraphics="NO" richText="NO" id="8202">
- <rect key="frame" x="0.0" y="0.0" width="363" height="175"/>
+ <rect key="frame" x="0.0" y="0.0" width="348" height="175"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
- <size key="minSize" width="363" height="175"/>
+ <size key="minSize" width="348" height="175"/>
<size key="maxSize" width="717" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
</textView>
@@ -4293,310 +4187,6 @@ Gw
<outlet property="initialFirstResponder" destination="5452" id="5525"/>
</connections>
</window>
- <window title="Advanced Filter" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="SPTableFilterPanel" animationBehavior="default" id="8068" userLabel="Advanced Filter Table Window" customClass="NSPanel">
- <windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" nonactivatingPanel="YES"/>
- <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
- <rect key="contentRect" x="162" y="162" width="752" height="317"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
- <value key="minSize" type="size" width="600" height="317"/>
- <view key="contentView" id="8069" userLabel="Filter Table Window">
- <rect key="frame" x="0.0" y="0.0" width="752" height="317"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <splitView dividerStyle="thin" id="8079" customClass="SPSplitView">
- <rect key="frame" x="0.0" y="40" width="752" height="277"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <customView id="8080">
- <rect key="frame" x="0.0" y="0.0" width="752" height="141"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="8141">
- <rect key="frame" x="0.0" y="0.0" width="752" height="142"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <clipView key="contentView" id="e9P-dP-cyb">
- <rect key="frame" x="0.0" y="0.0" width="752" height="142"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <tableView identifier="AdvancedFilterTableView" verticalHuggingPriority="750" allowsExpansionToolTips="YES" selectionHighlightStyle="sourceList" alternatingRowBackgroundColors="YES" emptySelection="NO" autosaveColumns="NO" headerView="8145" id="8144" customClass="SPCopyTable">
- <rect key="frame" x="0.0" y="0.0" width="752" height="119"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <size key="intercellSpacing" width="3" height="2"/>
- <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
- <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
- <tableColumns>
- <tableColumn width="748.921875" minWidth="40" maxWidth="1000" id="8146">
- <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="8149">
- <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"/>
- </tableColumn>
- </tableColumns>
- <connections>
- <outlet property="dataSource" destination="67" id="8157"/>
- <outlet property="delegate" destination="67" id="8158"/>
- </connections>
- </tableView>
- </subviews>
- </clipView>
- <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="8143">
- <rect key="frame" x="1" y="95.851600000000005" width="751.5" height="15"/>
- <autoresizingMask key="autoresizingMask"/>
- </scroller>
- <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="8142">
- <rect key="frame" x="224" y="17" width="15" height="102"/>
- <autoresizingMask key="autoresizingMask"/>
- </scroller>
- <tableHeaderView key="headerView" id="8145">
- <rect key="frame" x="0.0" y="0.0" width="752" height="17"/>
- <autoresizingMask key="autoresizingMask"/>
- </tableHeaderView>
- </scrollView>
- </subviews>
- </customView>
- <customView id="8081">
- <rect key="frame" x="0.0" y="142" width="752" height="135"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <button toolTip="Perform filter while typing (⌘T)" id="8129">
- <rect key="frame" x="537" y="97" width="197" height="18"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
- <buttonCell key="cell" type="check" title="Search while typing" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="8130">
- <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
- <font key="font" metaFont="smallSystem"/>
- <string key="keyEquivalent">t</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- </button>
- <button toolTip="If set negate entire WHERE clause (⌘N)" id="8131">
- <rect key="frame" x="537" y="77" width="197" height="18"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
- <buttonCell key="cell" type="check" title="Negate clause" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="8132">
- <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
- <font key="font" metaFont="smallSystem"/>
- <string key="keyEquivalent">n</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="toggleNegateClause:" target="67" id="8172"/>
- </connections>
- </button>
- <button toolTip="Perform filtering by using SELECT DISTINCT" id="8133">
- <rect key="frame" x="537" y="57" width="197" height="18"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
- <buttonCell key="cell" type="check" title="Select distinct" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="8134">
- <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
- <font key="font" metaFont="smallSystem"/>
- <string key="keyEquivalent">d</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="toggleDistinctSelect:" target="67" id="8170"/>
- </connections>
- </button>
- <textField verticalHuggingPriority="750" id="8135">
- <rect key="frame" x="537" y="37" width="137" height="14"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
- <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Default operator:" id="8136">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <button toolTip="Set default operator which should be used if pattern doen't begin with an operator" verticalHuggingPriority="750" id="8137">
- <rect key="frame" x="678" y="35" width="55" height="16"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
- <buttonCell key="cell" type="push" title="Edit" bezelStyle="rounded" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8138">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="miniSystem"/>
- <string key="keyEquivalent">o</string>
- <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="setDefaultOperator:" target="67" id="8163"/>
- </connections>
- </button>
- <textField verticalHuggingPriority="750" id="8139">
- <rect key="frame" x="554" y="19" width="181" height="14"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
- <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="LIKE '%@%'" id="8140">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="disabledControlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- <connections>
- <binding destination="1907" name="value" keyPath="values.FilterTableDefaultOperator" id="8167"/>
- </connections>
- </textField>
- <textField verticalHuggingPriority="750" id="8180">
- <rect key="frame" x="17" y="115" width="294" height="14"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="WHERE query" id="8181">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <scrollView autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="8204">
- <rect key="frame" x="20" y="21" width="512" height="92"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <clipView key="contentView" copiesOnScroll="NO" id="FLj-pH-h40">
- <rect key="frame" x="1" y="1" width="510" height="90"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <textView editable="NO" importsGraphics="NO" findStyle="panel" allowsNonContiguousLayout="YES" id="8207" customClass="SPTextView">
- <rect key="frame" x="0.0" y="0.0" width="510" height="90"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
- <size key="minSize" width="510" height="90"/>
- <size key="maxSize" width="518" height="10000000"/>
- <color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <connections>
- <outlet property="delegate" destination="67" id="8208"/>
- </connections>
- </textView>
- </subviews>
- </clipView>
- <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="8206">
- <rect key="frame" x="1" y="119" width="232" height="15"/>
- <autoresizingMask key="autoresizingMask"/>
- </scroller>
- <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="8205">
- <rect key="frame" x="224" y="1" width="15" height="127"/>
- <autoresizingMask key="autoresizingMask"/>
- </scroller>
- </scrollView>
- </subviews>
- </customView>
- </subviews>
- <holdingPriorities>
- <real value="250"/>
- <real value="250"/>
- </holdingPriorities>
- </splitView>
- <button toolTip="Apply Filter to current Table (⌘F)" verticalHuggingPriority="750" id="8083">
- <rect key="frame" x="595" y="12" width="143" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="Filter" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8084">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent">f</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="filterTable:" target="67" id="8176"/>
- </connections>
- </button>
- <button toolTip="Create a WHERE clause to search for the last typed pattern in all fields" verticalHuggingPriority="750" id="8127">
- <rect key="frame" x="14" y="12" width="216" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="Search all fields" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8128">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent">a</string>
- <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="toggleLookAllFieldsMode:" target="67" id="8168"/>
- </connections>
- </button>
- <button toolTip="Clear all Filter Criterias (⌘⌫)" verticalHuggingPriority="750" id="8085">
- <rect key="frame" x="452" y="12" width="143" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="Clear" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8086">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent"></string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="tableFilterClear:" target="67" id="8175"/>
- </connections>
- </button>
- </subviews>
- </view>
- <connections>
- <outlet property="initialFirstResponder" destination="8144" id="8159"/>
- </connections>
- </window>
- <window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="7609" userLabel="Filter Table Set Default Operator Sheet" customClass="NSPanel">
- <windowStyleMask key="styleMask" titled="YES"/>
- <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
- <rect key="contentRect" x="235" y="418" width="251" height="102"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
- <value key="minSize" type="size" width="251" height="102"/>
- <value key="maxSize" type="size" width="251" height="102"/>
- <view key="contentView" id="7610">
- <rect key="frame" x="0.0" y="0.0" width="251" height="102"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <button verticalHuggingPriority="750" tag="1" id="7615">
- <rect key="frame" x="140" y="13" width="96" height="28"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="Save" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="7616">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="smallSystem"/>
- <string key="keyEquivalent" base64-UTF8="YES">
-DQ
-</string>
- </buttonCell>
- <connections>
- <action selector="closeSheet:" target="67" id="7627"/>
- </connections>
- </button>
- <button verticalHuggingPriority="750" id="7617">
- <rect key="frame" x="46" y="13" width="96" height="28"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES"/>
- <buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="7618">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="smallSystem"/>
- <string key="keyEquivalent" base64-UTF8="YES">
-Gw
-</string>
- </buttonCell>
- <connections>
- <action selector="closeSheet:" target="67" id="7628"/>
- </connections>
- </button>
- <textField verticalHuggingPriority="750" id="7619">
- <rect key="frame" x="17" y="76" width="208" height="14"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Enter Filter Table's Default Operator:" id="7620">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- </textField>
- <comboBox verticalHuggingPriority="750" id="7629">
- <rect key="frame" x="20" y="51" width="214" height="22"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
- <comboBoxCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" completes="NO" numberOfVisibleItems="5" id="7630">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
- </comboBoxCell>
- </comboBox>
- <button horizontalHuggingPriority="750" verticalHuggingPriority="750" id="7814">
- <rect key="frame" x="17" y="14" width="25" height="25"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="7815">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="smallSystem"/>
- </buttonCell>
- <connections>
- <action selector="showDefaultOperaterHelp:" target="67" id="7816"/>
- </connections>
- </button>
- </subviews>
- </view>
- </window>
<window title="Query Favorite Sheet" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="6405" userLabel="Query Favorite Sheet" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
@@ -4821,7 +4411,7 @@ Gw
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" alternatingRowBackgroundColors="YES" columnReordering="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="14" headerView="6890" id="6889">
- <rect key="frame" x="0.0" y="0.0" width="358" height="133"/>
+ <rect key="frame" x="0.0" y="0.0" width="358" height="139"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -5002,54 +4592,43 @@ Gw
<customObject id="67" userLabel="SPTableContent" customClass="SPTableContent">
<connections>
<outlet property="addButton" destination="5175" id="5189"/>
- <outlet property="argumentField" destination="3963" id="3965"/>
- <outlet property="betweenTextField" destination="6364" id="6368"/>
- <outlet property="compareField" destination="156" id="166"/>
- <outlet property="contentSplitView" destination="yTv-Lq-Y3H" id="BQr-g6-UG3"/>
+ <outlet property="contentAreaContainer" destination="GZS-nP-BvF" id="deL-A4-mpc"/>
<outlet property="contentViewPane" destination="27" id="6661"/>
<outlet property="countText" destination="261" id="262"/>
<outlet property="duplicateButton" destination="5178" id="7837"/>
- <outlet property="fieldField" destination="146" id="164"/>
- <outlet property="filterButton" destination="4676" id="5942"/>
- <outlet property="filterControllerInstance" destination="ki9-Po-bdr" id="5od-0U-9xj"/>
- <outlet property="filterTableDistinctCheckbox" destination="8133" id="8171"/>
- <outlet property="filterTableFilterButton" destination="8083" id="8177"/>
- <outlet property="filterTableLiveSearchCheckbox" destination="8129" id="8174"/>
- <outlet property="filterTableNegateCheckbox" destination="8131" id="8173"/>
- <outlet property="filterTableQueryTitle" destination="8180" id="8184"/>
- <outlet property="filterTableSearchAllFields" destination="8127" id="8169"/>
- <outlet property="filterTableSetDefaultOperatorSheet" destination="7609" id="7632"/>
- <outlet property="filterTableSetDefaultOperatorValue" destination="7629" id="7631"/>
- <outlet property="filterTableSplitView" destination="8079" id="8178"/>
- <outlet property="filterTableView" destination="8144" id="8160"/>
- <outlet property="filterTableWhereClause" destination="8207" id="8209"/>
- <outlet property="filterTableWindow" destination="8068" id="8161"/>
- <outlet property="firstBetweenField" destination="6369" id="6373"/>
+ <outlet property="filterRuleEditorContainer" destination="9oo-u0-ndW" id="OZb-jI-CMy"/>
+ <outlet property="filterTableController" destination="UBS-cN-3Af" id="FV7-by-Gy1"/>
<outlet property="multipleLineEditingButton" destination="5201" id="5203"/>
+ <outlet property="paginationBox" destination="Cen-HW-aJR" id="cE5-7g-Pzn"/>
<outlet property="paginationButton" destination="6653" id="6657"/>
<outlet property="paginationNextButton" destination="6647" id="6664"/>
<outlet property="paginationPreviousButton" destination="6650" id="6663"/>
+ <outlet property="paginationView" destination="EmC-GF-Hfu" id="l4X-ze-hG5"/>
<outlet property="reloadButton" destination="5176" id="6625"/>
<outlet property="removeButton" destination="5177" id="5190"/>
- <outlet property="secondBetweenField" destination="6371" id="6374"/>
+ <outlet property="ruleFilterController" destination="ki9-Po-bdr" id="5od-0U-9xj"/>
<outlet property="spHistoryControllerInstance" destination="6297" id="6316"/>
+ <outlet property="tableContentContainer" destination="vcX-Xr-0cm" id="Xak-w0-y6x"/>
<outlet property="tableContentView" destination="36" id="142"/>
<outlet property="tableDataInstance" destination="4702" id="4712"/>
<outlet property="tableDocumentInstance" destination="-2" id="900"/>
<outlet property="tableInfoInstance" destination="4488" id="6352"/>
<outlet property="tableSourceInstance" destination="69" id="6882"/>
<outlet property="tablesListInstance" destination="68" id="1026"/>
+ <outlet property="toggleRuleFilterButton" destination="LYg-Ux-Lph" id="9eY-dL-AhC"/>
</connections>
</customObject>
- <customObject id="ki9-Po-bdr" userLabel="SPTableContentFilter" customClass="SPTableContentFilterController">
+ <customObject id="ki9-Po-bdr" userLabel="SPRuleFilter" customClass="SPRuleFilterController">
<connections>
- <outlet property="contentSplitView" destination="yTv-Lq-Y3H" id="KB1-gk-Ssi"/>
+ <outlet property="filterButton" destination="4676" id="9tZ-dW-BR3"/>
<outlet property="filterRuleEditor" destination="FF9-z2-9od" id="RW4-XM-XQS"/>
+ <outlet property="tableContentViewBelow" destination="36" id="ZGh-dM-J6C"/>
<outlet property="tableDataInstance" destination="4702" id="e69-W6-UwN"/>
<outlet property="tableDocumentInstance" destination="-2" id="2Xe-WU-zRw"/>
<outlet property="tablesListInstance" destination="68" id="byh-Eh-UAv"/>
</connections>
</customObject>
+ <customObject id="UBS-cN-3Af" customClass="SPFilterTableController"/>
<customObject id="362" userLabel="TableDump" customClass="SPDataImport">
<connections>
<outlet property="customQueryInstance" destination="134" id="953"/>
@@ -5059,7 +4638,6 @@ Gw
<outlet property="singleProgressSheet" destination="6125" id="6139"/>
<outlet property="singleProgressText" destination="6130" id="6140"/>
<outlet property="singleProgressTitle" destination="6132" id="6238"/>
- <outlet property="tableContentInstance" destination="67" id="1025"/>
<outlet property="tableDataInstance" destination="4702" id="4713"/>
<outlet property="tableDocumentInstance" destination="-2" id="534"/>
<outlet property="tableSourceInstance" destination="69" id="1172"/>
@@ -5577,6 +5155,23 @@ Gw
</view>
<point key="canvasLocation" x="98" y="659.5"/>
</window>
+ <customView id="EmC-GF-Hfu">
+ <rect key="frame" x="0.0" y="0.0" width="330" height="89"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+ <subviews>
+ <box autoresizesSubviews="NO" wantsLayer="YES" boxType="custom" borderType="line" titlePosition="noTitle" id="Cen-HW-aJR">
+ <rect key="frame" x="0.0" y="-47" width="330" height="136"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
+ <view key="contentView" id="yNs-cH-TWp">
+ <rect key="frame" x="1" y="1" width="328" height="134"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ </view>
+ <color key="borderColor" white="0.0" alpha="0.20000000300000001" colorSpace="calibratedWhite"/>
+ <color key="fillColor" white="0.94758063550000005" alpha="1" colorSpace="calibratedWhite"/>
+ </box>
+ </subviews>
+ <point key="canvasLocation" x="19" y="-25"/>
+ </customView>
</objects>
<resources>
<image name="NSAdvanced" width="32" height="32"/>
@@ -5594,6 +5189,8 @@ Gw
<image name="button_edit" width="30" height="22"/>
<image name="button_edit_mode" width="30" height="22"/>
<image name="button_edit_mode_selected" width="30" height="22"/>
+ <image name="button_filter" width="30" height="22"/>
+ <image name="button_filter_active" width="30" height="22"/>
<image name="button_info_pane_show" width="30" height="22"/>
<image name="button_left" width="30" height="22"/>
<image name="button_pagination" width="30" height="22"/>
diff --git a/Interfaces/English.lproj/FilterTableWindow.xib b/Interfaces/English.lproj/FilterTableWindow.xib
new file mode 100644
index 00000000..384ec859
--- /dev/null
+++ b/Interfaces/English.lproj/FilterTableWindow.xib
@@ -0,0 +1,337 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <development version="8000" identifier="xcode"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14109"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="SPFilterTableController">
+ <connections>
+ <outlet property="filterTableClearButton" destination="egJ-46-zsK" id="Fq2-LD-h16"/>
+ <outlet property="filterTableDistinctCheckbox" destination="kkn-vg-a1j" id="TDf-pu-HAk"/>
+ <outlet property="filterTableFilterButton" destination="97a-9g-t3j" id="zGX-CX-g2a"/>
+ <outlet property="filterTableLiveSearchCheckbox" destination="eW4-hf-ige" id="PR5-mS-LUC"/>
+ <outlet property="filterTableNegateCheckbox" destination="hdW-5X-UDu" id="NRU-CX-c0O"/>
+ <outlet property="filterTableQueryTitle" destination="WnE-kO-RxG" id="sVM-Sr-Er2"/>
+ <outlet property="filterTableSearchAllFields" destination="dCH-wr-KbJ" id="9xp-Cs-5GB"/>
+ <outlet property="filterTableSetDefaultOperatorSheet" destination="bXg-MQ-c5g" id="Ph2-a6-R2H"/>
+ <outlet property="filterTableSetDefaultOperatorValue" destination="KZQ-WN-rtU" id="QzL-fH-WTe"/>
+ <outlet property="filterTableSplitView" destination="Xk5-iI-QZb" id="Bv6-90-fDH"/>
+ <outlet property="filterTableView" destination="8SC-Fh-D9L" id="nIF-tg-501"/>
+ <outlet property="filterTableWhereClause" destination="NLm-8l-MrV" id="hMK-Jf-ydK"/>
+ <outlet property="window" destination="nzu-CY-UY7" id="EAC-V1-Oqh"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <window title="Advanced Filter" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="SPTableFilterPanel" animationBehavior="default" id="nzu-CY-UY7" userLabel="Advanced Filter Table Window" customClass="NSPanel">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" nonactivatingPanel="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="162" y="162" width="752" height="317"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
+ <value key="minSize" type="size" width="600" height="317"/>
+ <view key="contentView" id="mgs-0s-XcR" userLabel="Filter Table Window">
+ <rect key="frame" x="0.0" y="0.0" width="752" height="317"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <splitView dividerStyle="thin" id="Xk5-iI-QZb" customClass="SPSplitView">
+ <rect key="frame" x="0.0" y="40" width="752" height="277"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <customView id="P9T-cb-A1O">
+ <rect key="frame" x="0.0" y="0.0" width="752" height="141"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="Zmg-mY-I5z">
+ <rect key="frame" x="0.0" y="0.0" width="752" height="142"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <clipView key="contentView" id="PO1-a7-WBE">
+ <rect key="frame" x="0.0" y="23" width="752" height="119"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <tableView identifier="AdvancedFilterTableView" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" selectionHighlightStyle="sourceList" alternatingRowBackgroundColors="YES" emptySelection="NO" autosaveColumns="NO" headerView="eWi-Z3-2Sr" id="8SC-Fh-D9L" customClass="SPCopyTable">
+ <rect key="frame" x="0.0" y="0.0" width="752" height="119"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <size key="intercellSpacing" width="3" height="2"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
+ <tableColumns>
+ <tableColumn identifier="" width="748.921875" minWidth="40" maxWidth="1000" id="VGf-vY-BT6">
+ <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="isR-QM-Zos">
+ <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"/>
+ </tableColumn>
+ </tableColumns>
+ <connections>
+ <outlet property="dataSource" destination="-2" id="dtT-f4-3yI"/>
+ <outlet property="delegate" destination="-2" id="hvw-73-bmw"/>
+ </connections>
+ </tableView>
+ </subviews>
+ </clipView>
+ <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="E65-Wo-BZ0">
+ <rect key="frame" x="1" y="95.851600000000005" width="751.5" height="15"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="Vnq-gG-GNh">
+ <rect key="frame" x="224" y="17" width="15" height="102"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <tableHeaderView key="headerView" id="eWi-Z3-2Sr">
+ <rect key="frame" x="0.0" y="0.0" width="752" height="23"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </tableHeaderView>
+ </scrollView>
+ </subviews>
+ </customView>
+ <customView id="86d-uH-suk">
+ <rect key="frame" x="0.0" y="142" width="752" height="135"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <button toolTip="Perform filter while typing (⌘T)" id="eW4-hf-ige">
+ <rect key="frame" x="537" y="97" width="197" height="18"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="check" title="Search while typing" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="XTP-ut-VQL">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="smallSystem"/>
+ <string key="keyEquivalent">t</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ </button>
+ <button toolTip="If set negate entire WHERE clause (⌘N)" id="hdW-5X-UDu">
+ <rect key="frame" x="537" y="77" width="197" height="18"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="check" title="Negate clause" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="F8k-g6-s93">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="smallSystem"/>
+ <string key="keyEquivalent">n</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="toggleNegateClause:" target="-2" id="leY-aP-oLa"/>
+ </connections>
+ </button>
+ <button toolTip="Perform filtering by using SELECT DISTINCT" id="kkn-vg-a1j">
+ <rect key="frame" x="537" y="57" width="197" height="18"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="check" title="Select distinct" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="n3P-Lk-0tv">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="smallSystem"/>
+ <string key="keyEquivalent">d</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="toggleDistinctSelect:" target="-2" id="8jm-si-8p2"/>
+ </connections>
+ </button>
+ <textField verticalHuggingPriority="750" id="Lir-gD-3FT">
+ <rect key="frame" x="537" y="37" width="137" height="14"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
+ <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Default operator:" id="fOH-Fz-FbJ">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <button toolTip="Set default operator which should be used if pattern doen't begin with an operator" verticalHuggingPriority="750" id="AnE-dT-3rB">
+ <rect key="frame" x="678" y="35" width="55" height="16"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="push" title="Edit" bezelStyle="rounded" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="XWS-uW-aVd">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="miniSystem"/>
+ <string key="keyEquivalent">o</string>
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="setDefaultOperator:" target="-2" id="4EO-rc-z56"/>
+ </connections>
+ </button>
+ <textField verticalHuggingPriority="750" id="eRx-Pl-FwB">
+ <rect key="frame" x="554" y="19" width="181" height="14"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
+ <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="LIKE '%@%'" id="Hag-Ug-A4W">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="disabledControlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <connections>
+ <binding destination="E5e-Af-qLe" name="value" keyPath="values.FilterTableDefaultOperator" id="snR-X9-Dad"/>
+ </connections>
+ </textField>
+ <textField verticalHuggingPriority="750" id="WnE-kO-RxG">
+ <rect key="frame" x="17" y="115" width="294" height="14"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="WHERE query" id="iRJ-gQ-rJy">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <scrollView autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="xjR-JS-f3q">
+ <rect key="frame" x="20" y="21" width="512" height="92"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <clipView key="contentView" copiesOnScroll="NO" id="TBT-Nn-Rpn">
+ <rect key="frame" x="1" y="1" width="510" height="90"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <textView editable="NO" importsGraphics="NO" verticallyResizable="NO" findStyle="panel" allowsNonContiguousLayout="YES" id="NLm-8l-MrV" customClass="SPTextView">
+ <rect key="frame" x="0.0" y="0.0" width="510" height="90"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <size key="minSize" width="510" height="90"/>
+ <size key="maxSize" width="518" height="10000000"/>
+ <color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <connections>
+ <outlet property="delegate" destination="-2" id="nM8-08-2Rj"/>
+ </connections>
+ </textView>
+ </subviews>
+ </clipView>
+ <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="dpu-5p-anM">
+ <rect key="frame" x="1" y="119" width="232" height="15"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="vhd-6q-xMy">
+ <rect key="frame" x="224" y="1" width="15" height="127"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ </scrollView>
+ </subviews>
+ </customView>
+ </subviews>
+ <holdingPriorities>
+ <real value="250"/>
+ <real value="250"/>
+ </holdingPriorities>
+ </splitView>
+ <button toolTip="Apply Filter to current Table (⌘F)" verticalHuggingPriority="750" id="97a-9g-t3j">
+ <rect key="frame" x="595" y="12" width="143" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="Filter" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="aso-eo-Uh0">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent">f</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="filterTable:" target="-2" id="RuC-c5-aMP"/>
+ </connections>
+ </button>
+ <button toolTip="Create a WHERE clause to search for the last typed pattern in all fields" verticalHuggingPriority="750" id="dCH-wr-KbJ">
+ <rect key="frame" x="14" y="12" width="216" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="Search all fields" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ojj-hI-KbP">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent">a</string>
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="toggleLookAllFieldsMode:" target="-2" id="S7l-Cy-VFb"/>
+ </connections>
+ </button>
+ <button toolTip="Clear all Filter Criteria (⌘⌫)" verticalHuggingPriority="750" id="egJ-46-zsK">
+ <rect key="frame" x="452" y="12" width="143" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="Clear" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="0Ec-f2-4N5">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent"></string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="tableFilterClear:" target="-2" id="f00-8N-Gks"/>
+ </connections>
+ </button>
+ </subviews>
+ </view>
+ <connections>
+ <outlet property="initialFirstResponder" destination="8SC-Fh-D9L" id="zBz-CD-uGL"/>
+ </connections>
+ <point key="canvasLocation" x="146" y="493"/>
+ </window>
+ <userDefaultsController representsSharedInstance="YES" id="E5e-Af-qLe"/>
+ <window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="bXg-MQ-c5g" userLabel="Filter Table Set Default Operator Sheet" customClass="NSPanel">
+ <windowStyleMask key="styleMask" titled="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="235" y="418" width="251" height="102"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
+ <value key="minSize" type="size" width="251" height="102"/>
+ <value key="maxSize" type="size" width="251" height="102"/>
+ <view key="contentView" id="nxt-IK-Ukn">
+ <rect key="frame" x="0.0" y="0.0" width="251" height="102"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <button verticalHuggingPriority="750" tag="1" id="dzK-Nw-kVd">
+ <rect key="frame" x="140" y="13" width="96" height="28"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="Save" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="lvs-yx-6iE">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="smallSystem"/>
+ <string key="keyEquivalent" base64-UTF8="YES">
+DQ
+</string>
+ </buttonCell>
+ <connections>
+ <action selector="closeSheet:" target="-2" id="Szi-4E-jOM"/>
+ </connections>
+ </button>
+ <button verticalHuggingPriority="750" id="eKg-Fu-UA7">
+ <rect key="frame" x="46" y="13" width="96" height="28"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES"/>
+ <buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="FJo-lE-2oi">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="smallSystem"/>
+ <string key="keyEquivalent" base64-UTF8="YES">
+Gw
+</string>
+ </buttonCell>
+ <connections>
+ <action selector="closeSheet:" target="-2" id="HtZ-Ia-92w"/>
+ </connections>
+ </button>
+ <textField verticalHuggingPriority="750" id="iLi-Fh-ovo">
+ <rect key="frame" x="17" y="76" width="208" height="14"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Enter Filter Table's Default Operator:" id="Aps-Nn-GvU">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <comboBox verticalHuggingPriority="750" id="KZQ-WN-rtU">
+ <rect key="frame" x="20" y="51" width="214" height="22"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
+ <comboBoxCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" completes="NO" numberOfVisibleItems="5" id="UeY-Ej-YPK">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </comboBoxCell>
+ </comboBox>
+ <button horizontalHuggingPriority="750" verticalHuggingPriority="750" id="6wf-fR-6SB">
+ <rect key="frame" x="17" y="14" width="25" height="25"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="QYZ-U7-zc5">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="smallSystem"/>
+ </buttonCell>
+ <connections>
+ <action selector="showDefaultOperaterHelp:" target="-2" id="hCi-Lg-QN3"/>
+ </connections>
+ </button>
+ </subviews>
+ </view>
+ <point key="canvasLocation" x="75" y="132"/>
+ </window>
+ </objects>
+</document>
diff --git a/Resources/Images/button_filter.png b/Resources/Images/button_filter.png
new file mode 100644
index 00000000..2ae6f60d
--- /dev/null
+++ b/Resources/Images/button_filter.png
Binary files differ
diff --git a/Resources/Images/button_filter@2x.png b/Resources/Images/button_filter@2x.png
new file mode 100644
index 00000000..0e40ee1f
--- /dev/null
+++ b/Resources/Images/button_filter@2x.png
Binary files differ
diff --git a/Resources/Images/button_filter_active.png b/Resources/Images/button_filter_active.png
new file mode 100644
index 00000000..3a39d358
--- /dev/null
+++ b/Resources/Images/button_filter_active.png
Binary files differ
diff --git a/Resources/Images/button_filter_active@2x.png b/Resources/Images/button_filter_active@2x.png
new file mode 100644
index 00000000..67084e78
--- /dev/null
+++ b/Resources/Images/button_filter_active@2x.png
Binary files differ
diff --git a/Source/SPAppController.m b/Source/SPAppController.m
index 2e62725f..c9523bc7 100644
--- a/Source/SPAppController.m
+++ b/Source/SPAppController.m
@@ -2368,7 +2368,7 @@
*/
- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key
{
- NSLog(@"Not yet implemented.");
+ NSLog(@"Not yet implemented: %@", key);
return NO;
}
diff --git a/Source/SPConnectionController.m b/Source/SPConnectionController.m
index 8380f827..b9db12ad 100644
--- a/Source/SPConnectionController.m
+++ b/Source/SPConnectionController.m
@@ -86,6 +86,8 @@ static NSString *SPConnectionViewNibName = @"ConnectionView";
*/
static BOOL FindLinesInFile(NSData *fileData,const void *first,size_t first_len,const void *second,size_t second_len);
+static BOOL isOSAtLeast10_7;
+
@interface SPConnectionController ()
// Privately redeclare as read/write to get the synthesized setter
@@ -178,6 +180,10 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
@synthesize isConnecting;
@synthesize isEditingConnection;
++ (void)initialize {
+ isOSAtLeast10_7 = [SPOSInfo isOSVersionAtLeastMajor:10 minor:7 patch:0];
+}
+
- (NSString *)keychainPassword
{
NSString *kcItemName = [self connectionKeychainItemName];
@@ -2981,10 +2987,15 @@ static NSComparisonResult _compareFavoritesUsingKey(id favorite1, id favorite2,
// the division may lead to values that are not valid for the current screen size (e.g. non-integer values on a
// @1x non-retina screen). The OS works something out when not using layer-backed views, but in the latter
// case the result will look like garbage if we don't fix this.
- // This code is taken from Apple's "BlurryView" example code.
- connectionDetailsFrame = [[connectionDetailsScrollView superview] convertRectToBase:connectionDetailsFrame];
- connectionDetailsFrame.origin.y = round(connectionDetailsFrame.origin.y);
- connectionDetailsFrame = [[connectionDetailsScrollView superview] convertRectFromBase:connectionDetailsFrame];
+ if(isOSAtLeast10_7) {
+ connectionDetailsFrame = [connectionDetailsScrollView backingAlignedRect:connectionDetailsFrame options:NSAlignAllEdgesNearest];
+ }
+ else {
+ // This code is taken from Apple's "BlurryView" example code.
+ connectionDetailsFrame = [[connectionDetailsScrollView superview] convertRectToBase:connectionDetailsFrame];
+ connectionDetailsFrame.origin.y = round(connectionDetailsFrame.origin.y);
+ connectionDetailsFrame = [[connectionDetailsScrollView superview] convertRectFromBase:connectionDetailsFrame];
+ }
[connectionResizeContainer setFrame:connectionDetailsFrame];
scrollDocumentFrame.size.height = scrollViewFrame.size.height;
[[connectionDetailsScrollView documentView] setFrame:scrollDocumentFrame];
diff --git a/Source/SPContentFilterManager.h b/Source/SPContentFilterManager.h
index 3c170bfc..cfc5bc76 100644
--- a/Source/SPContentFilterManager.h
+++ b/Source/SPContentFilterManager.h
@@ -39,7 +39,7 @@
SPDatabaseDocument *tableDocumentInstance;
#ifndef SP_CODA /* ivars */
- NSURL *delegatesFileURL;
+ NSURL *documentFileURL;
#endif
IBOutlet id encodingPopUp;
@@ -65,7 +65,7 @@
NSString *filterType;
}
-- (id)initWithDelegate:(id)managerDelegate forFilterType:(NSString *)compareType;
+- (id)initWithDatabaseDocument:(SPDatabaseDocument *)document forFilterType:(NSString *)compareType;
// Accessors
- (NSMutableArray *)contentFilterForFileURL:(NSURL *)fileURL;
diff --git a/Source/SPContentFilterManager.m b/Source/SPContentFilterManager.m
index d0906c27..6ab50876 100644
--- a/Source/SPContentFilterManager.m
+++ b/Source/SPContentFilterManager.m
@@ -33,7 +33,6 @@
#import "RegexKitLite.h"
#import "SPQueryController.h"
#import "SPDatabaseDocument.h"
-#import "SPTableContent.h"
#import "SPConnectionController.h"
#import "SPSplitView.h"
#import "SPAppController.h"
@@ -48,31 +47,29 @@ static NSString *SPExportFilterAction = @"SPExportFilter";
@implementation SPContentFilterManager
/**
- * Initialize the manager with the supplied delegate
+ * Initialize the manager with the supplied document
*/
-- (id)initWithDelegate:(id)managerDelegate forFilterType:(NSString *)compareType
+- (id)initWithDatabaseDocument:(SPDatabaseDocument *)document forFilterType:(NSString *)compareType
{
- if ((self = [super initWithWindowNibName:@"ContentFilterManager"])) {
+ if (document == nil) {
+ NSBeep();
+ NSLog(@"ContentFilterManager was called without a document.");
+
+ return nil;
+ }
+ if ((self = [super initWithWindowNibName:@"ContentFilterManager"])) {
#ifndef SP_CODA
prefs = [NSUserDefaults standardUserDefaults];
#endif
contentFilters = [[NSMutableArray alloc] init];
-
- if (managerDelegate == nil) {
- NSBeep();
- NSLog(@"ContentFilterManager was called without a delegate.");
-
- return nil;
- }
-
- tableDocumentInstance = [managerDelegate valueForKeyPath:@"tableDocumentInstance"];
+ tableDocumentInstance = document;
#ifndef SP_CODA
- delegatesFileURL = [tableDocumentInstance fileURL];
+ documentFileURL = [[tableDocumentInstance fileURL] copy];
#endif
- filterType = [NSString stringWithString:compareType];
+ filterType = [compareType copy];
}
return self;
@@ -89,10 +86,10 @@ static NSString *SPExportFilterAction = @"SPExportFilter";
// Add global group row to contentFilters
[contentFilters addObject:@{
- @"MenuLabel" : NSLocalizedString(@"Global", @"Content Filter Manager : Filter Entry List: 'Global' Header"),
- @"headerOfFileURL" : @"",
- @"Clause" : @"",
- @"ConjunctionLabel" : @""
+ @"MenuLabel" : NSLocalizedString(@"Global", @"Content Filter Manager : Filter Entry List: 'Global' Header"),
+ @"headerOfFileURL" : @"",
+ @"Clause" : @"",
+ @"ConjunctionLabel" : @""
}];
#ifndef SP_CODA /* prefs access */
@@ -113,13 +110,13 @@ static NSString *SPExportFilterAction = @"SPExportFilter";
// Build doc-based filters
[contentFilters addObject:[NSDictionary dictionaryWithObjectsAndKeys:
- [[[delegatesFileURL absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] lastPathComponent], @"MenuLabel",
- [delegatesFileURL absoluteString], @"headerOfFileURL",
+ [[[documentFileURL absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] lastPathComponent], @"MenuLabel",
+ [documentFileURL absoluteString], @"headerOfFileURL",
@"", @"Clause",
nil]];
- if ([[SPQueryController sharedQueryController] contentFilterForFileURL:delegatesFileURL]) {
- id filters = [[SPQueryController sharedQueryController] contentFilterForFileURL:delegatesFileURL];
+ if ([[SPQueryController sharedQueryController] contentFilterForFileURL:documentFileURL]) {
+ id filters = [[SPQueryController sharedQueryController] contentFilterForFileURL:documentFileURL];
if([filters objectForKey:filterType])
for(id fav in [filters objectForKey:filterType])
[contentFilters addObject:[[fav mutableCopy] autorelease]];
@@ -220,6 +217,7 @@ static NSString *SPExportFilterAction = @"SPExportFilter";
*/
- (id)customQueryInstance
{
+#warning private ivar accessed from outside
return [tableDocumentInstance valueForKey:@"customQueryInstance"];
}
@@ -395,7 +393,7 @@ static NSString *SPExportFilterAction = @"SPExportFilter";
#ifndef SP_CODA
// Update current document's content filters in the SPQueryController
[[SPQueryController sharedQueryController] replaceContentFilterByArray:
- [self contentFilterForFileURL:delegatesFileURL] ofType:filterType forFileURL:delegatesFileURL];
+ [self contentFilterForFileURL:documentFileURL] ofType:filterType forFileURL:documentFileURL];
// Update global preferences' list
id cf = [[prefs objectForKey:SPContentFilters] mutableCopy];
@@ -940,16 +938,16 @@ static NSString *SPExportFilterAction = @"SPExportFilter";
NSError *error = nil;
NSData *plist = [NSPropertyListSerialization dataWithPropertyList:spfdata
- format:NSPropertyListXMLFormat_v1_0
- options:0
- error:&error];
+ format:NSPropertyListXMLFormat_v1_0
+ options:0
+ error:&error];
if(error) {
NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Error while converting content filter data", @"Content filters could not be converted to plist upon export - message title (ContentFilterManager)")
- defaultButton:NSLocalizedString(@"OK", @"OK button")
- alternateButton:nil
- otherButton:nil
- informativeTextWithFormat:@"%@", [error localizedDescription]];
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:@"%@", [error localizedDescription]];
[alert setAlertStyle:NSCriticalAlertStyle];
[alert runModal];
@@ -968,6 +966,8 @@ static NSString *SPExportFilterAction = @"SPExportFilter";
- (void)dealloc
{
SPClear(contentFilters);
+ SPClear(filterType);
+ SPClear(documentFileURL);
[super dealloc];
}
diff --git a/Source/SPDataImport.h b/Source/SPDataImport.h
index 545e02ff..ca1cd7dc 100644
--- a/Source/SPDataImport.h
+++ b/Source/SPDataImport.h
@@ -30,6 +30,13 @@
// More info at <https://github.com/sequelpro/sequelpro>
@class SPMySQLConnection;
+@class SPFieldMapperController;
+@class SPFileHandle;
+@class SPDatabaseDocument;
+@class SPCustomQuery;
+@class SPTableData;
+@class SPTableStructure;
+@class SPTablesList;
typedef enum {
SPFieldMapperInProgress = 1,
@@ -37,16 +44,14 @@ typedef enum {
SPFieldMapperCancelled = 3
} SPFieldMapperSheetStatus;
-@class SPFieldMapperController, SPFileHandle;
-
@interface SPDataImport : NSObject <NSOpenSavePanelDelegate>
{
- IBOutlet id tableDocumentInstance;
- IBOutlet id tablesListInstance;
- IBOutlet id tableSourceInstance;
- IBOutlet id tableContentInstance;
- IBOutlet id tableDataInstance;
- IBOutlet id customQueryInstance;
+#warning Outlets belong to multiple xib files!
+ IBOutlet SPDatabaseDocument *tableDocumentInstance;
+ IBOutlet SPTablesList *tablesListInstance;
+ IBOutlet SPTableStructure *tableSourceInstance;
+ IBOutlet SPTableData *tableDataInstance;
+ IBOutlet SPCustomQuery *customQueryInstance;
IBOutlet id importView;
IBOutlet id importTabView;
@@ -55,7 +60,6 @@ typedef enum {
IBOutlet id importFieldsEnclosedField;
IBOutlet id importFieldsEscapedField;
IBOutlet id importLinesTerminatedField;
- IBOutlet id importFieldMapperSheetWindow;
IBOutlet NSPopUpButton *importFormatPopup;
IBOutlet NSPopUpButton *importEncodingPopup;
@@ -67,21 +71,13 @@ typedef enum {
IBOutlet NSTextView *importFromClipboardTextView;
- IBOutlet id addDropTableSwitch;
- IBOutlet id addCreateTableSwitch;
- IBOutlet id addTableContentSwitch;
- IBOutlet id addErrorsSwitch;
- IBOutlet id sqlFullStreamingSwitch;
- IBOutlet id sqlCompressionSwitch;
- IBOutlet id csvFullStreamingSwitch;
- IBOutlet id multiCSVFullStreamingSwitch;
- IBOutlet id multiXMLFullStreamingSwitch;
- IBOutlet id errorsSheet;
- IBOutlet id errorsView;
- IBOutlet id singleProgressSheet;
- IBOutlet id singleProgressBar;
- IBOutlet id singleProgressTitle;
- IBOutlet id singleProgressText;
+ IBOutlet NSWindow *errorsSheet;
+ IBOutlet NSTextView *errorsView;
+
+ IBOutlet NSPanel *singleProgressSheet;
+ IBOutlet NSProgressIndicator *singleProgressBar;
+ IBOutlet NSTextField *singleProgressTitle;
+ IBOutlet NSTextField *singleProgressText;
SPMySQLConnection *mySQLConnection;
@@ -108,7 +104,6 @@ typedef enum {
BOOL importMethodIsUpdate;
BOOL importIntoNewTable;
- NSUInteger exportMode;
NSUserDefaults *prefs;
BOOL progressCancelled;
BOOL _mainNibLoaded;
@@ -119,8 +114,6 @@ typedef enum {
NSMutableIndexSet *bitFieldsMapIndex;
NSMutableArray *nullableNumericFields;
NSMutableIndexSet *nullableNumericFieldsMapIndex;
-
- NSSavePanel *currentExportPanel;
}
// IBAction methods
diff --git a/Source/SPDataImport.m b/Source/SPDataImport.m
index f350ae0f..f6b865e7 100644
--- a/Source/SPDataImport.m
+++ b/Source/SPDataImport.m
@@ -34,7 +34,6 @@
#import "SPTablesList.h"
#import "SPTableStructure.h"
#import "SPDatabaseStructure.h"
-#import "SPTableContent.h"
#import "SPCustomQuery.h"
#import "SPGrowlController.h"
#import "SPSQLParser.h"
@@ -211,9 +210,9 @@
[NSApp beginSheet:importFromClipboardSheet
modalForWindow:[tableDocumentInstance parentWindow]
- modalDelegate:self
+ modalDelegate:self
didEndSelector:@selector(importFromClipboardSheetDidEnd:returnCode:contextInfo:)
- contextInfo:nil];
+ contextInfo:nil];
}
/**
@@ -408,7 +407,11 @@
[singleProgressBar startAnimation:self];
// Open the progress sheet
- [NSApp beginSheet:singleProgressSheet modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil];
+ [NSApp beginSheet:singleProgressSheet
+ modalForWindow:[tableDocumentInstance parentWindow]
+ modalDelegate:nil
+ didEndSelector:NULL
+ contextInfo:NULL];
[singleProgressSheet makeKeyWindow];
});
@@ -696,12 +699,12 @@
// Re-query the structure of all databases in the background
[[tableDocumentInstance databaseStructureRetrieval] queryDbStructureInBackgroundWithUserInfo:@{@"forceUpdate" : @YES}];
-
- // Import finished Growl notification
- [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Import Finished"
- description:[NSString stringWithFormat:NSLocalizedString(@"Finished importing %@",@"description for finished importing growl notification"), [filename lastPathComponent]]
- document:tableDocumentInstance
- notificationName:@"Import Finished"];
+
+ // Import finished Growl notification
+ [[SPGrowlController sharedGrowlController] notifyWithTitle:@"Import Finished"
+ description:[NSString stringWithFormat:NSLocalizedString(@"Finished importing %@", @"description for finished importing growl notification"), [filename lastPathComponent]]
+ document:tableDocumentInstance
+ notificationName:@"Import Finished"];
}
#pragma mark -
@@ -792,7 +795,11 @@
[singleProgressBar startAnimation:self];
// Open the progress sheet
- [NSApp beginSheet:singleProgressSheet modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self didEndSelector:nil contextInfo:nil];
+ [NSApp beginSheet:singleProgressSheet
+ modalForWindow:[tableDocumentInstance parentWindow]
+ modalDelegate:nil
+ didEndSelector:NULL
+ contextInfo:NULL];
[singleProgressSheet makeKeyWindow];
});
@@ -974,7 +981,11 @@
[singleProgressBar setMaxValue:fileTotalLength];
[singleProgressBar setIndeterminate:NO];
[singleProgressBar startAnimation:self];
- [NSApp beginSheet:singleProgressSheet modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
+ [NSApp beginSheet:singleProgressSheet
+ modalForWindow:[tableDocumentInstance parentWindow]
+ modalDelegate:nil
+ didEndSelector:NULL
+ contextInfo:NULL];
[singleProgressSheet makeKeyWindow];
});
@@ -1211,10 +1222,10 @@
}
// Import finished Growl notification
- [[SPGrowlController sharedGrowlController] notifyWithTitle:NSLocalizedString(@"Import Finished" , @"title for finished importing growl notification")
- description:[NSString stringWithFormat:NSLocalizedString(@"Finished importing %@",@"description for finished importing growl notification"), [filename lastPathComponent]]
- document:tableDocumentInstance
- notificationName:@"Import Finished"];
+ [[SPGrowlController sharedGrowlController] notifyWithTitle:NSLocalizedString(@"Import Finished", @"title for finished importing growl notification")
+ description:[NSString stringWithFormat:NSLocalizedString(@"Finished importing %@", @"description for finished importing growl notification"), [filename lastPathComponent]]
+ document:tableDocumentInstance
+ notificationName:@"Import Finished"];
SPMainQSync(^{
if(importIntoNewTable) {
@@ -1295,10 +1306,10 @@
// Show field mapper sheet and set the focus to it
[NSApp beginSheet:[fieldMapperController window]
- modalForWindow:[tableDocumentInstance parentWindow]
- modalDelegate:self
- didEndSelector:@selector(fieldMapperDidEndSheet:returnCode:contextInfo:)
- contextInfo:nil];
+ modalForWindow:[tableDocumentInstance parentWindow]
+ modalDelegate:self
+ didEndSelector:@selector(fieldMapperDidEndSheet:returnCode:contextInfo:)
+ contextInfo:NULL];
[[fieldMapperController window] makeKeyWindow];
});
@@ -1720,11 +1731,11 @@ cleanup:
}
[errorsView setString:message];
- [NSApp beginSheet:errorsSheet
- modalForWindow:[tableDocumentInstance parentWindow]
- modalDelegate:self
- didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
- contextInfo:nil];
+ [NSApp beginSheet:errorsSheet
+ modalForWindow:[tableDocumentInstance parentWindow]
+ modalDelegate:self
+ didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
+ contextInfo:NULL];
[errorsSheet makeKeyWindow];
}
diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m
index ad7a2614..4bdd0aad 100644
--- a/Source/SPDatabaseDocument.m
+++ b/Source/SPDatabaseDocument.m
@@ -4711,7 +4711,7 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
[sessionState setObject:[NSNumber numberWithInteger:[tableContentInstance pageNumber]] forKey:@"contentPageNumber"];
[sessionState setObject:NSStringFromRect([tableContentInstance viewport]) forKey:@"contentViewport"];
NSDictionary *filterSettings = [tableContentInstance filterSettings];
- if (filterSettings) [sessionState setObject:filterSettings forKey:@"contentFilter"];
+ if (filterSettings) [sessionState setObject:filterSettings forKey:@"contentFilterV2"];
NSDictionary *contentSelectedRows = [tableContentInstance selectionDetailsAllowingIndexSelection:YES];
if (contentSelectedRows) {
@@ -5110,7 +5110,7 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
if([spfSession objectForKey:@"contentSortCol"]) [tableContentInstance setSortColumnNameToRestore:[spfSession objectForKey:@"contentSortCol"] isAscending:[[spfSession objectForKey:@"contentSortColIsAsc"] boolValue]];
if([spfSession objectForKey:@"contentPageNumber"]) [tableContentInstance setPageToRestore:[[spfSession objectForKey:@"pageNumber"] integerValue]];
if([spfSession objectForKey:@"contentViewport"]) [tableContentInstance setViewportToRestore:NSRectFromString([spfSession objectForKey:@"contentViewport"])];
- if([spfSession objectForKey:@"contentFilter"]) [tableContentInstance setFiltersToRestore:[spfSession objectForKey:@"contentFilter"]];
+ if([spfSession objectForKey:@"contentFilterV2"]) [tableContentInstance setFiltersToRestore:[spfSession objectForKey:@"contentFilterV2"]];
// Select table
[tablesListInstance selectTableAtIndex:[NSNumber numberWithInteger:[tables indexOfObject:[spfSession objectForKey:@"table"]]]];
@@ -6395,7 +6395,6 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
// Register observers for when the DisplayTableViewVerticalGridlines preference changes
[prefs addObserver:self forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
[prefs addObserver:tableSourceInstance forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
- [prefs addObserver:tableContentInstance forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
[prefs addObserver:customQueryInstance forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
[prefs addObserver:tableRelationsInstance forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
[prefs addObserver:[SPQueryController sharedQueryController] forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:NULL];
@@ -6404,9 +6403,6 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
[prefs addObserver:tableSourceInstance forKeyPath:SPUseMonospacedFonts options:NSKeyValueObservingOptionNew context:NULL];
[prefs addObserver:[SPQueryController sharedQueryController] forKeyPath:SPUseMonospacedFonts options:NSKeyValueObservingOptionNew context:NULL];
- [prefs addObserver:tableContentInstance forKeyPath:SPGlobalResultTableFont options:NSKeyValueObservingOptionNew context:NULL];
- [prefs addObserver:tableContentInstance forKeyPath:SPDisplayBinaryDataAsHex options:NSKeyValueObservingOptionNew context:NULL];
-
// Register observers for when the logging preference changes
[prefs addObserver:[SPQueryController sharedQueryController] forKeyPath:SPConsoleEnableLogging options:NSKeyValueObservingOptionNew context:NULL];
@@ -6424,13 +6420,9 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
[prefs removeObserver:tableSourceInstance forKeyPath:SPUseMonospacedFonts];
- [prefs removeObserver:tableContentInstance forKeyPath:SPGlobalResultTableFont];
- [prefs removeObserver:tableContentInstance forKeyPath:SPDisplayBinaryDataAsHex];
-
[prefs removeObserver:customQueryInstance forKeyPath:SPDisplayTableViewVerticalGridlines];
[prefs removeObserver:tableRelationsInstance forKeyPath:SPDisplayTableViewVerticalGridlines];
[prefs removeObserver:tableSourceInstance forKeyPath:SPDisplayTableViewVerticalGridlines];
- [prefs removeObserver:tableContentInstance forKeyPath:SPDisplayTableViewVerticalGridlines];
[prefs removeObserver:[SPQueryController sharedQueryController] forKeyPath:SPUseMonospacedFonts];
[prefs removeObserver:[SPQueryController sharedQueryController] forKeyPath:SPConsoleEnableLogging];
diff --git a/Source/SPFillView.h b/Source/SPFillView.h
new file mode 100644
index 00000000..21ea074b
--- /dev/null
+++ b/Source/SPFillView.h
@@ -0,0 +1,47 @@
+//
+// SPFillView.h
+// sequel-pro
+//
+// Created by Max Lohrmann on 09.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+/**
+ * SPFillView is a very simple NSView that will
+ * fill its whole view rect with a solid color.
+ * The color can be set in Interface Builder.
+ */
+@interface SPFillView : NSView
+{
+ NSColor *currentColor;
+}
+
+/**
+ * This method is invoked when unarchiving the View from the xib.
+ * The value is configured in IB under "User Defined Runtime Attributes"
+ */
+- (void)setSystemColorOfName:(NSString *)name;
+
+@end
diff --git a/Source/SPFillView.m b/Source/SPFillView.m
new file mode 100644
index 00000000..b2fae0fb
--- /dev/null
+++ b/Source/SPFillView.m
@@ -0,0 +1,61 @@
+//
+// SPFillView.m
+// sequel-pro
+//
+// Created by Max Lohrmann on 09.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+#import "SPFillView.h"
+
+@implementation SPFillView
+
+- (void)setSystemColorOfName:(NSString *)name
+{
+ //TODO: xibs after 10.6 support storing colors as user defined attributes so we don't need the detour via strings anymore
+ NSColorList *scl = [NSColorList colorListNamed:@"System"];
+ NSColor *color = [scl colorWithKey:name];
+ if(color) {
+ [color retain];
+ [currentColor release];
+ currentColor = color;
+ [self setNeedsDisplay:YES];
+ }
+}
+
+- (void)drawRect:(NSRect)dirtyRect {
+ if(currentColor) {
+ [currentColor set];
+ NSRectFill(dirtyRect);
+ }
+}
+
+- (void)dealloc
+{
+ [currentColor release];
+ [super dealloc];
+}
+
+@end
diff --git a/Source/SPFilterTableController.h b/Source/SPFilterTableController.h
new file mode 100644
index 00000000..7bf02593
--- /dev/null
+++ b/Source/SPFilterTableController.h
@@ -0,0 +1,129 @@
+//
+// SPFilterTableController.h
+// sequel-pro
+//
+// Created by Max Lohrmann on 07.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+// Relocated from existing files. Previous copyright applies.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+@class SPSplitView;
+@class SPCopyTable;
+@class SPTextView;
+
+@interface SPFilterTableController : NSWindowController
+{
+ IBOutlet SPSplitView *filterTableSplitView;
+ IBOutlet NSButton *filterTableFilterButton;
+ IBOutlet NSButton *filterTableClearButton;
+ IBOutlet NSButton *filterTableSearchAllFields;
+
+ IBOutlet SPCopyTable *filterTableView;
+
+ IBOutlet NSButton *filterTableLiveSearchCheckbox;
+ IBOutlet NSButton *filterTableNegateCheckbox;
+ IBOutlet NSButton *filterTableDistinctCheckbox;
+
+ IBOutlet NSTextField *filterTableQueryTitle;
+ IBOutlet SPTextView *filterTableWhereClause;
+
+ IBOutlet NSPanel *filterTableSetDefaultOperatorSheet;
+ IBOutlet NSComboBox* filterTableSetDefaultOperatorValue;
+
+ NSUserDefaults *prefs;
+
+ NSMutableDictionary *filterTableData;
+ BOOL filterTableNegate;
+ BOOL filterTableDistinct;
+ BOOL filterTableIsSwapped;
+ NSString *filterTableDefaultOperator;
+ NSString *lastEditedFilterTableValue;
+
+ id target;
+ SEL action;
+}
+
+/**
+ * Puts the filter table window on screen
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (void)showFilterTableWindow;
+
+/**
+ * Restores filter table content state from serialized data
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (void)setFilterTableData:(NSData *)arcData;
+
+/**
+ * Returns the current contents of the filter table window as serialized data
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (NSData *)filterTableData;
+
+/**
+ * The SQL expression to use as filter.
+ * Can be nil if no filter is set!
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (NSString *)tableFilterString;
+
+/**
+ * Will reconfigure the columns of the filter table view from the given array.
+ * Call with nil to reset the table view to its initial empty state.
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (void)setColumns:(NSArray *)dataColumns;
+
+/**
+ * Will return YES if the SQL expression returned by -tableFilterString should be
+ * used in a "SELECT DISTINCT …" query.
+ *
+ * Results may be inconsistent if not called on the main thread!
+ */
+- (BOOL)isDistinct;
+
+/**
+ * Use this method to make the filter window indicate an error state after executing the filter.
+ * Pass 0 for error ID to indicate an OK state.
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (void)setFilterError:(NSUInteger)errorID message:(NSString *)message sqlstate:(NSString *)sqlstate;
+
+/**
+ * Used when the filter table window wants to trigger filtering
+ *
+ * Results may be inconsistent if not called on the main thread!
+ */
+@property (assign, nonatomic) id target;
+@property (assign, nonatomic) SEL action;
+
+@end
diff --git a/Source/SPFilterTableController.m b/Source/SPFilterTableController.m
new file mode 100644
index 00000000..60e67a04
--- /dev/null
+++ b/Source/SPFilterTableController.m
@@ -0,0 +1,706 @@
+//
+// SPFilterTableController.m
+// sequel-pro
+//
+// Created by Max Lohrmann on 07.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+// Relocated from existing files. Previous copyright applies.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+#import "SPFilterTableController.h"
+#import "SPSplitView.h"
+#import "SPCopyTable.h"
+#import "SPTextView.h"
+#import "RegexKitLite.h"
+#import "SPTextAndLinkCell.h"
+
+static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOperator";
+static void *FilterTableKVOContext = &FilterTableKVOContext;
+
+@interface SPFilterTableController () <NSTableViewDataSource, NSTableViewDelegate, NSControlTextEditingDelegate>
+
+- (IBAction)filterTable:(id)sender;
+- (IBAction)toggleLookAllFieldsMode:(id)sender;
+- (IBAction)tableFilterClear:(id)sender;
+
+- (IBAction)toggleNegateClause:(id)sender;
+- (IBAction)toggleDistinctSelect:(id)sender;
+- (IBAction)setDefaultOperator:(id)sender;
+
+- (IBAction)closeSheet:(id)sender;
+- (IBAction)showDefaultOperaterHelp:(id)sender;
+
+- (void)updateFilterTableClause:(id)currentValue;
+
++ (NSString*)escapeFilterTableDefaultOperator:(NSString*)op;
+
+@end
+
+@implementation SPFilterTableController
+
+#pragma mark Public methods
+
+@synthesize target;
+@synthesize action;
+
+- (instancetype)init {
+ if ((self = [super initWithWindowNibName:@"FilterTableWindow"])) {
+ target = nil;
+ action = NULL;
+
+ prefs = [NSUserDefaults standardUserDefaults];
+ [prefs addObserver:self
+ forKeyPath:SPDisplayTableViewVerticalGridlines
+ options:NSKeyValueObservingOptionNew
+ context:FilterTableKVOContext];
+
+ filterTableData = [[NSMutableDictionary alloc] initWithCapacity:1];
+
+ filterTableNegate = NO;
+ filterTableDistinct = NO;
+ filterTableIsSwapped = NO;
+
+ lastEditedFilterTableValue = nil;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ //TODO this should be changed to the variant with …context: after 10.6 support is removed!
+ [prefs removeObserver:self forKeyPath:SPDisplayTableViewVerticalGridlines];
+
+ SPClear(filterTableData);
+ SPClear(lastEditedFilterTableValue);
+ SPClear(filterTableDefaultOperator);
+ [super dealloc];
+}
+
+- (void)showFilterTableWindow
+{
+ [[self window] makeKeyAndOrderFront:nil];
+ [filterTableWhereClause setContinuousSpellCheckingEnabled:NO];
+ [filterTableWhereClause setAutoindent:NO];
+ [filterTableWhereClause setAutoindentIgnoresEnter:NO];
+ [filterTableWhereClause setAutopair:[prefs boolForKey:SPCustomQueryAutoPairCharacters]];
+ [filterTableWhereClause setAutohelp:NO];
+ [filterTableWhereClause setAutouppercaseKeywords:[prefs boolForKey:SPCustomQueryAutoUppercaseKeywords]];
+ [filterTableWhereClause setCompletionWasReinvokedAutomatically:NO];
+ [filterTableWhereClause insertText:@""];
+ [filterTableWhereClause didChangeText];
+
+ [[filterTableView window] makeFirstResponder:filterTableView];
+}
+
+- (void)setFilterTableData:(NSData*)arcData
+{
+ if(!arcData) return;
+ NSDictionary *filterData = [NSUnarchiver unarchiveObjectWithData:arcData];
+ [filterTableData removeAllObjects];
+ [filterTableData addEntriesFromDictionary:filterData];
+ [[self window] makeKeyAndOrderFront:nil];
+ [filterTableView reloadData];
+}
+
+- (NSData*) filterTableData
+{
+ if(![[self window] isVisible]) return nil;
+
+ [filterTableView deselectAll:nil];
+
+ return [NSArchiver archivedDataWithRootObject:filterTableData];
+}
+
+- (NSString *)tableFilterString
+{
+ if([[[filterTableWhereClause textStorage] string] length]) {
+ if ([filterTableNegateCheckbox state] == NSOnState) {
+ return [NSString stringWithFormat:@"NOT (%@)", [[filterTableWhereClause textStorage] string]];
+ }
+ else {
+ return [[filterTableWhereClause textStorage] string];
+ }
+ }
+ else {
+ return nil;
+ }
+}
+
+- (void)setColumns:(NSArray *)dataColumns
+{
+ [self window]; // make sure window is loaded
+ // Clear filter table
+ [filterTableView abortEditing];
+ while ([[filterTableView tableColumns] count]) {
+ [NSArrayObjectAtIndex([filterTableView tableColumns], 0) setHeaderToolTip:nil]; // prevent crash #2414
+ [filterTableView removeTableColumn:NSArrayObjectAtIndex([filterTableView tableColumns], 0)];
+ }
+ // Clear filter table data
+ [filterTableData removeAllObjects];
+ [filterTableWhereClause setString:@""];
+
+ // Clear error state
+ [self setFilterError:0 message:nil sqlstate:nil];
+
+ if(dataColumns) {
+ CGFloat totalWidth = 0;
+ // Add the new columns to the filterTable
+ for (NSDictionary *columnDefinition in dataColumns ) {
+ // Set up column for filterTable
+ NSTableColumn *filterCol = [[NSTableColumn alloc] initWithIdentifier:[columnDefinition objectForKey:@"datacolumnindex"]];
+ [[filterCol headerCell] setStringValue:[columnDefinition objectForKey:@"name"]];
+ [filterCol setEditable:YES];
+ SPTextAndLinkCell *filterDataCell = [[[SPTextAndLinkCell alloc] initTextCell:@""] autorelease];
+ [filterDataCell setEditable:YES];
+ [filterDataCell setLineBreakMode:NSLineBreakByTruncatingTail]; // add ellipsis for long values (default is to simply hide words)
+ [filterCol setDataCell:filterDataCell];
+ NSSize headerSize = [[[filterCol headerCell] attributedStringValue] size];
+ CGFloat headerInitialWidth = headerSize.width + 5.0;
+ [filterCol setWidth:headerInitialWidth];
+ totalWidth += headerInitialWidth;
+ [filterTableView addTableColumn:filterCol];
+ [filterCol release];
+
+ [filterTableData setObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
+ [columnDefinition objectForKey:@"name"], @"name",
+ [columnDefinition objectForKey:@"typegrouping"], @"typegrouping",
+ [NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil], SPTableContentFilterKey,
+ nil] forKey:[columnDefinition objectForKey:@"datacolumnindex"]];
+ }
+
+ // if the width of all columns is still less than the width of the table view resize them uniformly once to take up all horizontal space
+ if(totalWidth < [[filterTableView enclosingScrollView] contentSize].width) [filterTableView sizeToFit];
+ }
+
+ [filterTableView reloadData];
+}
+
+- (BOOL)isDistinct
+{
+ return filterTableDistinct;
+}
+
+- (void)setFilterError:(NSUInteger)errorID message:(NSString *)message sqlstate:(NSString *)sqlstate
+{
+ if(errorID) {
+ [[self window] setTitle:[NSString stringWithFormat:@"%@ – %@", NSLocalizedString(@"Filter", @"filter label"), NSLocalizedString(@"WHERE clause not valid", @"WHERE clause not valid")]];
+ }
+ else {
+ [[self window] setTitle:NSLocalizedString(@"Filter", @"filter label")];
+ }
+}
+
+#pragma mark - Internal methods
+
+- (void)windowDidLoad
+{
+ [filterTableView setGridStyleMask:([prefs boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
+
+ // Modify the filter table split view sizes
+ [filterTableSplitView setMinSize:135 ofSubviewAtIndex:1];
+
+ // Init Filter Table GUI
+ [filterTableDistinctCheckbox setState:(filterTableDistinct) ? NSOnState : NSOffState];
+ [filterTableNegateCheckbox setState:(filterTableNegate) ? NSOnState : NSOffState];
+ [filterTableLiveSearchCheckbox setState:NSOffState];
+
+ filterTableDefaultOperator = [[[self class] escapeFilterTableDefaultOperator:[prefs objectForKey:SPFilterTableDefaultOperator]] retain];
+}
+
+- (IBAction)filterTable:(id)sender
+{
+ if(target && action) [target performSelector:action withObject:self];
+}
+
+/**
+ * Generate WHERE clause to look for last typed pattern in all fields
+ */
+- (IBAction)toggleLookAllFieldsMode:(id)sender
+{
+ [self updateFilterTableClause:sender];
+
+ // If live search is set perform filtering
+ if ([filterTableLiveSearchCheckbox state] == NSOnState) {
+ [self filterTable:filterTableFilterButton];
+ }
+}
+
+/**
+ * Clear the filter table
+ */
+- (IBAction)tableFilterClear:(id)sender
+{
+ [filterTableView abortEditing];
+
+ if(filterTableData && [filterTableData count]) {
+
+ // Clear filter data
+ for(NSNumber *col in [filterTableData allKeys])
+ {
+ [[filterTableData objectForKey:col] setObject:[NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil] forKey:SPTableContentFilterKey];
+ }
+
+ [filterTableView reloadData];
+ [filterTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
+ [filterTableWhereClause setString:@""];
+
+ // Reload table
+ [self filterTable:nil];
+ }
+}
+
+/**
+ * Set filter table's Negate
+ */
+- (IBAction)toggleNegateClause:(id)sender
+{
+ filterTableNegate = !filterTableNegate;
+
+ if (filterTableNegate) {
+ [filterTableQueryTitle setStringValue:NSLocalizedString(@"WHERE NOT query", @"Title of filter preview area when the query WHERE is negated")];
+ }
+ else {
+ [filterTableQueryTitle setStringValue:NSLocalizedString(@"WHERE query", @"Title of filter preview area when the query WHERE is normal")];
+ }
+
+ // If live search is set perform filtering
+ if ([filterTableLiveSearchCheckbox state] == NSOnState) {
+ [self filterTable:filterTableFilterButton];
+ }
+}
+
+/**
+ * Set filter table's Distinct
+ */
+- (IBAction)toggleDistinctSelect:(id)sender
+{
+ filterTableDistinct = !filterTableDistinct;
+
+ [filterTableDistinctCheckbox setState:(filterTableDistinct) ? NSOnState : NSOffState];
+
+ // If live search is set perform filtering
+ if ([filterTableLiveSearchCheckbox state] == NSOnState) {
+ [self filterTable:filterTableFilterButton];
+ }
+}
+
+/**
+ * Set filter table's default operator
+ */
+- (IBAction)setDefaultOperator:(id)sender
+{
+ [[self window] makeFirstResponder:filterTableView];
+
+ // Load history
+ if([prefs objectForKey:SPFilterTableDefaultOperatorLastItems]) {
+ NSMutableArray *lastItems = [NSMutableArray array];
+
+ [lastItems addObject:@"LIKE '%@%'"];
+
+ for(NSString* item in [prefs objectForKey:SPFilterTableDefaultOperatorLastItems])
+ {
+ [lastItems addObject:item];
+ }
+
+ [filterTableSetDefaultOperatorValue removeAllItems];
+ [filterTableSetDefaultOperatorValue addItemsWithObjectValues:lastItems];
+ }
+
+ [filterTableSetDefaultOperatorValue setStringValue:[prefs objectForKey:SPFilterTableDefaultOperator]];
+
+ [NSApp beginSheet:filterTableSetDefaultOperatorSheet
+ modalForWindow:[self window]
+ modalDelegate:self
+ didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
+ contextInfo:SPTableFilterSetDefaultOperator];
+}
+
+/**
+ * Close an open sheet.
+ */
+- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo
+{
+ [sheet orderOut:self];
+
+ if([contextInfo isEqualToString:SPTableFilterSetDefaultOperator]) {
+ if(returnCode) {
+ if(filterTableDefaultOperator) [filterTableDefaultOperator release];
+ NSString *newOperator = [filterTableSetDefaultOperatorValue stringValue];
+ filterTableDefaultOperator = [[[self class] escapeFilterTableDefaultOperator:newOperator] retain];
+ [prefs setObject:newOperator forKey:SPFilterTableDefaultOperator];
+
+ if(![newOperator isMatchedByRegex:@"(?i)like\\s+['\"]%@%['\"]\\s*"]) {
+ if(![prefs objectForKey:SPFilterTableDefaultOperatorLastItems])
+ [prefs setObject:[NSMutableArray array] forKey:SPFilterTableDefaultOperatorLastItems];
+
+ NSMutableArray *lastItems = [NSMutableArray array];
+ [lastItems setArray:[prefs objectForKey:SPFilterTableDefaultOperatorLastItems]];
+
+ if([lastItems containsObject:newOperator])
+ [lastItems removeObject:newOperator];
+ if([lastItems count] > 0)
+ [lastItems insertObject:newOperator atIndex:0];
+ else
+ [lastItems addObject:newOperator];
+ // Remember only the last 15 items
+ if([lastItems count] > 15)
+ while([lastItems count] > 15)
+ [filterTableSetDefaultOperatorValue removeItemAtIndex:[lastItems count]-1];
+
+ [prefs setObject:lastItems forKey:SPFilterTableDefaultOperatorLastItems];
+ }
+ [self updateFilterTableClause:nil];
+ }
+ }
+}
+
+/**
+ * Closes the current sheet and stops the modal session
+ */
+- (IBAction)closeSheet:(id)sender
+{
+ [NSApp endSheet:[sender window] returnCode:[sender tag]];
+ [[sender window] orderOut:self];
+}
+
+/**
+ * Opens the content filter help page in the default browser.
+ */
+- (IBAction)showDefaultOperaterHelp:(id)sender
+{
+ [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:SPLOCALIZEDURL_CONTENTFILTERHELP]];
+}
+
+/**
+ * Update WHERE clause in filter table window.
+ *
+ * @param currentValue If currentValue == nil take the data from filterTableData, if currentValue == filterTableSearchAllFields
+ * generate a WHERE clause to search in all given fields, if currentValue == a string take this string as table cell data of the
+ * currently edited table cell
+ */
+- (void)updateFilterTableClause:(id)currentValue
+{
+ NSMutableString *clause = [NSMutableString string];
+ NSInteger numberOfRows = [self numberOfRowsInTableView:filterTableView];
+ NSInteger numberOfCols = [[filterTableView tableColumns] count];
+ NSRange opRange, defopRange;
+
+ BOOL lookInAllFields = NO;
+
+ NSString *re1 = @"^\\s*(<[=>]?|>=?|!?=|≠|≤|≥)\\s*(.*?)\\s*$";
+ NSString *re2 = @"^\\s*(.*)\\s+(.*?)\\s*$";
+
+ NSInteger editedRow = [filterTableView editedRow];
+
+ if (currentValue == filterTableSearchAllFields) {
+ numberOfRows = 1;
+ lookInAllFields = YES;
+ }
+
+ [filterTableWhereClause setString:@""];
+
+ for (NSInteger i = 0; i < numberOfRows; i++)
+ {
+ NSInteger numberOfValues = 0;
+
+ for (NSInteger anIndex = 0; anIndex < numberOfCols; anIndex++)
+ {
+ NSString *filterCell = nil;
+ NSDictionary *filterCellData = [NSDictionary dictionaryWithDictionary:[filterTableData objectForKey:[NSString stringWithFormat:@"%ld", (long)anIndex]]];
+
+ // Take filterTableData
+ if (!currentValue) {
+ filterCell = NSArrayObjectAtIndex([filterCellData objectForKey:SPTableContentFilterKey], i);
+ }
+ // Take last edited value to create the OR clause
+ else if (lookInAllFields) {
+ if (lastEditedFilterTableValue && [lastEditedFilterTableValue length]) {
+ filterCell = lastEditedFilterTableValue;
+ }
+ else {
+ [filterTableWhereClause setString:@""];
+ [filterTableWhereClause insertText:@""];
+ [filterTableWhereClause scrollRangeToVisible:NSMakeRange(0, 0)];
+
+ // If live search is set perform filtering
+ if ([filterTableLiveSearchCheckbox state] == NSOnState) {
+ [self filterTable:filterTableFilterButton];
+ }
+ }
+ }
+ // Take value from currently edited table cell
+ else if ([currentValue isKindOfClass:[NSString class]]) {
+ if (i == editedRow && anIndex == [[NSArrayObjectAtIndex([filterTableView tableColumns], [filterTableView editedColumn]) identifier] integerValue]) {
+ filterCell = (NSString*)currentValue;
+ }
+ else {
+ filterCell = NSArrayObjectAtIndex([filterCellData objectForKey:SPTableContentFilterKey], i);
+ }
+ }
+
+ if ([filterCell length]) {
+
+ // Recode special operators
+ filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≠" withString:@"!="];
+ filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≤" withString:@"<="];
+ filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≥" withString:@">="];
+
+ if (numberOfValues) {
+ [clause appendString:(lookInAllFields) ? @" OR " : @" AND "];
+ }
+
+ NSString *fieldName = [[filterCellData objectForKey:@"name"] backtickQuotedString];
+ NSString *filterTableDefaultOperatorWithFieldName = [filterTableDefaultOperator stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName];
+
+ opRange = [filterCell rangeOfString:@"`@`"];
+ defopRange = [filterTableDefaultOperator rangeOfString:@"`@`"];
+
+ // if cell data begins with ' or " treat it as it is
+ // by checking if default operator by itself contains a ' or " - if so
+ // remove first and if given the last ' or "
+ if ([filterCell isMatchedByRegex:@"^\\s*['\"]"]) {
+ if ([filterTableDefaultOperator isMatchedByRegex:@"['\"]"]) {
+ NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:@"^\\s*(['\"])(.*)\\1\\s*$"];
+
+ if ([matches count] && [matches = NSArrayObjectAtIndex(matches, 0) count] == 3) {
+ [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, NSArrayObjectAtIndex(matches, 2)];
+ }
+ else {
+ matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:@"^\\s*(['\"])(.*)\\s*$"];
+
+ if ([matches count] && [matches = NSArrayObjectAtIndex(matches, 0) count] == 3) {
+ [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, NSArrayObjectAtIndex(matches, 2)];
+ }
+ }
+ }
+ else {
+ [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, filterCell];
+ }
+ }
+ // If cell contains the field name placeholder
+ else if (opRange.length || defopRange.length) {
+ filterCell = [filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName];
+
+ if (defopRange.length) {
+ [clause appendFormat:filterTableDefaultOperatorWithFieldName, [filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName]];
+ }
+ else {
+ [clause appendString:[filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName]];
+ }
+ }
+ // If cell is equal to NULL
+ else if ([filterCell isMatchedByRegex:@"(?i)^\\s*null\\s*$"]) {
+ [clause appendFormat:@"%@ IS NULL", fieldName];
+ }
+ // If cell starts with an operator
+ else if ([filterCell isMatchedByRegex:re1]) {
+ NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:re1];
+
+ if ([matches count] && [matches = NSArrayObjectAtIndex(matches, 0) count] == 3) {
+ [clause appendFormat:@"%@ %@ %@", fieldName, NSArrayObjectAtIndex(matches, 1), NSArrayObjectAtIndex(matches, 2)];
+ }
+ }
+ // If cell consists of at least two words treat the first as operator and the rest as argument
+ else if ([filterCell isMatchedByRegex:re2]) {
+ NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:re2];
+
+ if ([matches count] && [matches = NSArrayObjectAtIndex(matches,0) count] == 3) {
+ [clause appendFormat:@"%@ %@ %@", fieldName, [NSArrayObjectAtIndex(matches, 1) uppercaseString], NSArrayObjectAtIndex(matches, 2)];
+ }
+ }
+ // Apply the default operator
+ else {
+ [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, filterCell];
+ }
+
+ numberOfValues++;
+ }
+ }
+
+ if (numberOfValues) {
+ [clause appendString:@"\nOR\n"];
+ }
+ }
+
+ // Remove last " OR " if any
+ [filterTableWhereClause setString:[clause length] > 3 ? [clause substringToIndex:([clause length] - 4)] : @""];
+
+ // Update syntax highlighting and uppercasing
+ [filterTableWhereClause insertText:@""];
+ [filterTableWhereClause scrollRangeToVisible:NSMakeRange(0, 0)];
+
+ // If live search is set perform filtering
+ if ([filterTableLiveSearchCheckbox state] == NSOnState) {
+ [self filterTable:filterTableFilterButton];
+ }
+}
+
+#pragma mark - TableView datasource methods
+
+- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
+{
+ if (filterTableIsSwapped) {
+ [[[filterTableData objectForKey:@(rowIndex)] objectForKey:SPTableContentFilterKey] replaceObjectAtIndex:([[tableColumn identifier] integerValue] - 1) withObject:(NSString *)object];
+ }
+ else {
+ [[[filterTableData objectForKey:[tableColumn identifier]] objectForKey:SPTableContentFilterKey] replaceObjectAtIndex:rowIndex withObject:(NSString *)object];
+ }
+
+ [self updateFilterTableClause:nil];
+}
+
+- (NSInteger)numberOfRowsInTableView:(SPCopyTable *)tableView
+{
+ return filterTableIsSwapped ? [filterTableData count] : [[[filterTableData objectForKey:@"0"] objectForKey:SPTableContentFilterKey] count];
+}
+
+- (id)tableView:(SPCopyTable *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
+{
+ NSUInteger columnIndex = [[tableColumn identifier] integerValue];
+
+ if (filterTableIsSwapped) {
+ // First column shows the field names
+ if (columnIndex == 0) {
+ return [[[NSTableHeaderCell alloc] initTextCell:[[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:@"name"]] autorelease];
+ }
+
+ return NSArrayObjectAtIndex([[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:SPTableContentFilterKey], columnIndex - 1);
+ }
+
+ return NSArrayObjectAtIndex([[filterTableData objectForKey:[tableColumn identifier]] objectForKey:SPTableContentFilterKey], rowIndex);
+}
+
+#pragma mark - TableView delegate methods
+
+- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)rowIndex
+{
+ return YES;
+}
+
+- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
+{
+ //if ([tableDocumentInstance isWorking]) return NO;
+
+ return (filterTableIsSwapped && [[tableColumn identifier] integerValue] == 0) ? NO : YES;
+}
+
+- (void)tableView:(SPCopyTable *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
+{
+ if (filterTableIsSwapped && [[tableColumn identifier] integerValue] == 0) {
+ [cell setDrawsBackground:YES];
+ [cell setBackgroundColor:[NSColor lightGrayColor]];
+ } else {
+ [cell setDrawsBackground:NO];
+ }
+}
+
+- (NSString *)tableView:(NSTableView *)tableView toolTipForCell:(id)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation
+{
+ return nil;
+}
+
+#pragma mark - Control delegate methods
+
+- (void)controlTextDidChange:(NSNotification *)notification
+{
+ if ([notification object] == filterTableView) {
+
+ NSString *string = [[[[notification userInfo] objectForKey:@"NSFieldEditor"] textStorage] string];
+
+ if (string && [string length]) {
+ if (lastEditedFilterTableValue) [lastEditedFilterTableValue release];
+
+ lastEditedFilterTableValue = [[NSString stringWithString:string] retain];
+ }
+
+ [self updateFilterTableClause:string];
+ }
+}
+
+- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)editor
+{
+ return YES;
+}
+
+- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)aFieldEditor
+{
+ return YES;
+}
+
+- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command
+{
+ // Check firstly if SPCopyTable can handle command
+ if ([control control:control textView:textView doCommandBySelector:command]) return YES;
+
+ // Trap the escape key
+ if ([[control window] methodForSelector:command] == [[control window] methodForSelector:@selector(cancelOperation:)]) {
+ // Abort editing
+ [control abortEditing];
+
+ return YES;
+ }
+
+ return NO;
+}
+
+#pragma mark - KVO
+
+/**
+ * This method is called as part of Key Value Observing which is used to watch for prefernce changes which effect the interface.
+ */
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ // a parent class (or cocoa) can also use KVO, so we need to watch out to only catch those KVO messages we requested
+ if(context == FilterTableKVOContext) {
+ // Display table veiew vertical gridlines preference changed
+ if ([keyPath isEqualToString:SPDisplayTableViewVerticalGridlines]) {
+ [filterTableView setGridStyleMask:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
+ }
+ }
+ else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ }
+}
+
+#pragma mark -
+
+/**
+ * Escape passed operator for usage as filterTableDefaultOperator.
+ */
++ (NSString*)escapeFilterTableDefaultOperator:(NSString *)op
+{
+ if (!op) return @"";
+
+ NSMutableString *newOp = [[[NSMutableString alloc] initWithCapacity:[op length]] autorelease];
+
+ [newOp setString:op];
+ [newOp replaceOccurrencesOfRegex:@"%" withString:@"%%"];
+ [newOp replaceOccurrencesOfRegex:@"(?<!`)@(?!=`)" withString:@"%@"];
+
+ return newOp;
+}
+
+@end
+
diff --git a/Source/SPHistoryController.m b/Source/SPHistoryController.m
index aab54d2b..7f1d3ffd 100644
--- a/Source/SPHistoryController.m
+++ b/Source/SPHistoryController.m
@@ -58,6 +58,7 @@
- (void) awakeFromNib
{
+#warning private ivar accessed from outside
tableContentInstance = [theDocument valueForKey:@"tableContentInstance"];
tablesListInstance = [theDocument valueForKey:@"tablesListInstance"];
toolbarItemVisible = NO;
@@ -250,7 +251,8 @@
NSDictionary *contentSelectedRows = [[tableContentInstance onMainThread] selectionDetailsAllowingIndexSelection:YES];
NSRect contentViewport = [[tableContentInstance onMainThread] viewport];
NSDictionary *contentFilter = [[tableContentInstance onMainThread] filterSettings];
- NSData *filterTableData = [tableContentInstance filterTableData];
+ NSData *filterTableData = [[tableContentInstance onMainThread] filterTableData];
+ SPTableContentFilterSource activeFilter = [[tableContentInstance onMainThread] activeFilter];
if (!theDatabase) return;
// If a table is selected, save state information
@@ -261,10 +263,11 @@
[NSNumber numberWithUnsignedInteger:contentPageNumber], @"page",
[NSValue valueWithRect:contentViewport], @"viewport",
[NSNumber numberWithBool:contentSortColIsAsc], @"sortIsAsc",
+ @(activeFilter), @"activeFilter",
nil];
if (contentSortCol) [contentState setObject:contentSortCol forKey:@"sortCol"];
if (contentSelectedRows) [contentState setObject:contentSelectedRows forKey:@"selection"];
- if (contentFilter) [contentState setObject:contentFilter forKey:@"filter"];
+ if (contentFilter) [contentState setObject:contentFilter forKey:@"filterV2"];
if (filterTableData) [contentState setObject:filterTableData forKey:@"filterTable"];
// Update the table content states with this information - used when switching tables to restore last used view.
@@ -278,38 +281,45 @@
} else if (historyPosition != NSNotFound && historyPosition == [history count] - 1) {
NSMutableDictionary *currentHistoryEntry = [history objectAtIndex:historyPosition];
+ BOOL databaseIsTheSame = [[currentHistoryEntry objectForKey:@"database"] isEqualToString:theDatabase];
+ BOOL tableIsTheSame = [[currentHistoryEntry objectForKey:@"table"] isEqualToString:theTable];
+ BOOL viewIsTheSame = ([[currentHistoryEntry objectForKey:@"view"] unsignedIntegerValue] == theView);
// If the table is the same, and the filter settings haven't changed, delete the
// last entry so it can be replaced. This updates navigation within a table, rather than
// creating a new entry every time detail is changed.
- if ([[currentHistoryEntry objectForKey:@"database"] isEqualToString:theDatabase]
- && [[currentHistoryEntry objectForKey:@"table"] isEqualToString:theTable]
- && ([[currentHistoryEntry objectForKey:@"view"] unsignedIntegerValue] != theView
- || ((![currentHistoryEntry objectForKey:@"contentFilter"] && !contentFilter)
- || (![currentHistoryEntry objectForKey:@"contentFilter"]
- && ![(NSString *)[contentFilter objectForKey:@"filterValue"] length]
- && ![[contentFilter objectForKey:@"filterComparison"] isEqualToString:@"IS NULL"]
- && ![[contentFilter objectForKey:@"filterComparison"] isEqualToString:@"IS NOT NULL"])
- || [[currentHistoryEntry objectForKey:@"contentFilter"] isEqualToDictionary:contentFilter])))
- {
+ if (
+ databaseIsTheSame &&
+ tableIsTheSame &&
+ (
+ !viewIsTheSame ||
+ (
+ (![currentHistoryEntry objectForKey:@"contentFilterV2"] && !contentFilter) ||
+ [[currentHistoryEntry objectForKey:@"contentFilterV2"] isEqualToDictionary:contentFilter]
+ )
+ )
+ ) {
[history removeLastObject];
-
+ }
// If the only db/table/view are the same, but the filter settings have changed, also store the
// position details on the *previous* history item
- } else if ([[currentHistoryEntry objectForKey:@"database"] isEqualToString:theDatabase]
- && [[currentHistoryEntry objectForKey:@"table"] isEqualToString:theTable]
- && ([[currentHistoryEntry objectForKey:@"view"] unsignedIntegerValue] == theView
- || ((![currentHistoryEntry objectForKey:@"contentFilter"] && contentFilter)
- || ![[currentHistoryEntry objectForKey:@"contentFilter"] isEqualToDictionary:contentFilter])))
- {
+ else if (
+ databaseIsTheSame &&
+ tableIsTheSame &&
+ (
+ viewIsTheSame ||
+ (
+ (![currentHistoryEntry objectForKey:@"contentFilterV2"] && contentFilter) ||
+ ![[currentHistoryEntry objectForKey:@"contentFilterV2"] isEqualToDictionary:contentFilter]
+ )
+ )
+ ) {
[currentHistoryEntry setObject:[NSValue valueWithRect:contentViewport] forKey:@"contentViewport"];
if (contentSelectedRows) [currentHistoryEntry setObject:contentSelectedRows forKey:@"contentSelection"];
-
+ }
// Special case: if the last history item is currently active, and has no table,
// but the new selection does - delete the last entry, in order to replace it.
// This improves history flow.
- } else if ([[currentHistoryEntry objectForKey:@"database"] isEqualToString:theDatabase]
- && ![currentHistoryEntry objectForKey:@"table"])
- {
+ else if (databaseIsTheSame && ![currentHistoryEntry objectForKey:@"table"]) {
[history removeLastObject];
}
}
@@ -322,10 +332,11 @@
[NSNumber numberWithBool:contentSortColIsAsc], @"contentSortColIsAsc",
[NSNumber numberWithInteger:contentPageNumber], @"contentPageNumber",
[NSValue valueWithRect:contentViewport], @"contentViewport",
+ @(activeFilter), @"activeFilter",
nil];
if (contentSortCol) [newEntry setObject:contentSortCol forKey:@"contentSortCol"];
if (contentSelectedRows) [newEntry setObject:contentSelectedRows forKey:@"contentSelection"];
- if (contentFilter) [newEntry setObject:contentFilter forKey:@"contentFilter"];
+ if (contentFilter) [newEntry setObject:contentFilter forKey:@"contentFilterV2"];
[history addObject:newEntry];
@@ -379,7 +390,8 @@
[tableContentInstance setPageToRestore:[[historyEntry objectForKey:@"contentPageNumber"] integerValue]];
[tableContentInstance setSelectionToRestore:[historyEntry objectForKey:@"contentSelection"]];
[tableContentInstance setViewportToRestore:[[historyEntry objectForKey:@"contentViewport"] rectValue]];
- [tableContentInstance setFiltersToRestore:[historyEntry objectForKey:@"contentFilter"]];
+ [tableContentInstance setFiltersToRestore:[historyEntry objectForKey:@"contentFilterV2"]];
+ [tableContentInstance setActiveFilterToRestore:(SPTableContentFilterSource)[[historyEntry objectForKey:@"activeFilter"] integerValue]];
// If the database, table, and view are the same and content - just trigger a table reload (filters)
if (
@@ -495,7 +507,8 @@ abort_entry_load:
[tableContentInstance setPageToRestore:[[contentState objectForKey:@"page"] unsignedIntegerValue]];
[tableContentInstance setSelectionToRestore:[contentState objectForKey:@"selection"]];
[tableContentInstance setViewportToRestore:[[contentState objectForKey:@"viewport"] rectValue]];
- [tableContentInstance setFiltersToRestore:[contentState objectForKey:@"filter"]];
+ [tableContentInstance setFiltersToRestore:[contentState objectForKey:@"filterV2"]];
+ [tableContentInstance setActiveFilterToRestore:(SPTableContentFilterSource)[[contentState objectForKey:@"activeFilter"] integerValue]];
}
#pragma mark -
@@ -529,21 +542,14 @@ abort_entry_load:
[theName appendFormat:@"/%@", [theEntry objectForKey:@"table"]];
- if ([theEntry objectForKey:@"contentFilter"]) {
- NSDictionary *filterSettings = [theEntry objectForKey:@"contentFilter"];
- if ([filterSettings objectForKey:@"filterField"]) {
- if([filterSettings objectForKey:@"menuLabel"]) {
- theName = [NSMutableString stringWithFormat:NSLocalizedString(@"%@ (Filtered by %@)", @"History item filtered by values label"),
- theName, [filterSettings objectForKey:@"menuLabel"]];
- }
- }
+ if ([theEntry objectForKey:@"contentFilterV2"]) {
+ theName = [NSMutableString stringWithFormat:NSLocalizedString(@"%@ (Filtered)", @"History item filtered by values label"), theName];
}
if ([theEntry objectForKey:@"contentPageNumber"]) {
NSUInteger pageNumber = [[theEntry objectForKey:@"contentPageNumber"] unsignedIntegerValue];
if (pageNumber > 1) {
- theName = [NSMutableString stringWithFormat:NSLocalizedString(@"%@ (Page %lu)", @"History item with page number label"),
- theName, (unsigned long)pageNumber];
+ theName = [NSMutableString stringWithFormat:NSLocalizedString(@"%@ (Page %lu)", @"History item with page number label"), theName, (unsigned long)pageNumber];
}
}
diff --git a/Source/SPIndexesController.m b/Source/SPIndexesController.m
index ace6d882..89dd01b4 100644
--- a/Source/SPIndexesController.m
+++ b/Source/SPIndexesController.m
@@ -31,7 +31,6 @@
#import "SPIndexesController.h"
#import "SPAlertSheets.h"
#import "SPServerSupport.h"
-#import "SPTableContent.h"
#import "SPTableData.h"
#import "SPDatabaseDocument.h"
#import "SPTablesList.h"
diff --git a/Source/SPRuleFilterController.h b/Source/SPRuleFilterController.h
new file mode 100644
index 00000000..5dc4b07f
--- /dev/null
+++ b/Source/SPRuleFilterController.h
@@ -0,0 +1,165 @@
+//
+// SPRuleFilterController.h
+// sequel-pro
+//
+// Created by Max Lohrmann on 04.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+@class SPTableData;
+@class SPDatabaseDocument;
+@class SPTablesList;
+@class SPContentFilterManager;
+
+NSString * const SPRuleFilterHeightChangedNotification;
+
+@interface SPRuleFilterController : NSObject {
+ IBOutlet NSRuleEditor *filterRuleEditor;
+ IBOutlet SPTableData *tableDataInstance;
+ IBOutlet SPDatabaseDocument *tableDocumentInstance;
+ IBOutlet SPTablesList *tablesListInstance;
+ IBOutlet NSView *tableContentViewBelow;
+ IBOutlet NSButton *filterButton;
+
+ NSMutableArray *columns;
+ NSMutableDictionary *contentFilters;
+ NSMutableDictionary *numberOfDefaultFilters;
+
+ id _modelContainer; // private class
+
+ SPContentFilterManager *contentFilterManager;
+
+ CGFloat preferredHeight;
+
+ id target;
+ SEL action;
+
+ BOOL enabled;
+
+ NSUInteger opNodeCacheVersion;
+}
+
+/**
+ * Returns the rule editor view that is managed by this object
+ *
+ * SHOULD be called on the UI thread, or results may be inconsistent!
+ */
+- (NSRuleEditor *)view;
+
+/**
+ * Makes the first NSTextField found in the rule editor the first responder
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (void)focusFirstInputField;
+
+/**
+ * Will reconfigure the columns of the rule editor from the given array.
+ * Call with nil to reset the editor to its initial empty state.
+ * Existing rows will be removed in any case!
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (void)setColumns:(NSArray *)dataColumns;
+
+/**
+ * Converts the current filter expression displayed in the UI into an
+ * SQL string suitable for use in a WHERE clause.
+ *
+ * @param isBINARY Indicates that the filter should use the BINARY qualifier for ignoring
+ * collations during search.
+ * @param err Upon return contains and object describing why the SQL conversion failed,
+ * if it failed or nil, if no errors occured.
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (NSString *)sqlWhereExpressionWithBinary:(BOOL)isBINARY error:(NSError **)err;
+
+/**
+ * Returns the current filter configuration in a serialized form that can be exported and
+ * reapplied later.
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (NSDictionary *)serializedFilter;
+
+/**
+ * Restores the filter rule configuration from a given dictionary.
+ * The current column configuration must match the schema that was used when generating
+ * the serialized data, otherwise the invalid rules will be ignored.
+ *
+ * @param serialized A dictionary previously generated by calling -serializedFilter.
+ * @return A serialized filter
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (void)restoreSerializedFilters:(NSDictionary *)serialized;
+
+/**
+ * Create a serialized filter from a given column, operator and operand.
+ * This is used when navigating foreign key links between tables to create the filter for the target table.
+ *
+ * @param colName Name of the column to filter (left side operand)
+ * @param opName Name of the filter (operator)
+ * @param values The values to filter with (right side operand)
+ * @return A serialized filter
+ *
+ * This method is thread-safe.
+ */
++ (NSDictionary *)makeSerializedFilterForColumn:(NSString *)colName operator:(NSString *)opName values:(NSArray *)values;
+
+/**
+ * The view height the rule editor needs in order to not have to resort to scrollbars
+ *
+ * SHOULD be called on the UI thread, or results may be inconsistent!
+ */
+@property (readonly, assign, nonatomic) CGFloat preferredHeight;
+
+/**
+ * Indicates whether the rule editor has no filter expressions
+ *
+ * SHOULD be called on the UI thread, or results may be inconsistent!
+ */
+- (BOOL)isEmpty;
+
+/**
+ * Adds a new row to the rule editor
+ *
+ * MUST BE CALLED ON THE UI THREAD!
+ */
+- (void)addFilterExpression;
+
+/**
+ * Used when the rule editor wants to trigger filtering
+ *
+ * SHOULD be called on the UI thread, or results may be inconsistent!
+ */
+@property (assign, nonatomic) id target;
+@property (assign, nonatomic) SEL action;
+
+- (BOOL)isEnabled;
+- (void)setEnabled:(BOOL)enabled;
+
+@end
diff --git a/Source/SPRuleFilterController.m b/Source/SPRuleFilterController.m
new file mode 100644
index 00000000..99b7824d
--- /dev/null
+++ b/Source/SPRuleFilterController.m
@@ -0,0 +1,1579 @@
+//
+// SPRuleFilterController.m
+// sequel-pro
+//
+// Created by Max Lohrmann on 04.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// More info at <https://github.com/sequelpro/sequelpro>
+
+#import "SPRuleFilterController.h"
+#import "SPQueryController.h"
+#import "SPDatabaseDocument.h"
+#import "RegexKitLite.h"
+#import "SPContentFilterManager.h"
+#import "SPFunctions.h"
+#import "SPTableFilterParser.h"
+
+typedef NS_ENUM(NSInteger, RuleNodeType) {
+ RuleNodeTypeColumn,
+ RuleNodeTypeString,
+ RuleNodeTypeOperator,
+ RuleNodeTypeArgument,
+ RuleNodeTypeConnector,
+};
+
+NSString * const SPRuleFilterHeightChangedNotification = @"SPRuleFilterHeightChanged";
+
+/**
+ * The type of filter rule that the current item represents.
+ */
+const NSString * const SerFilterClass = @"filterClass";
+/**
+ * The current rule is a group row (an "AND" or "OR" expression with children)
+ */
+const NSString * const SerFilterClassGroup = @"groupNode";
+/**
+ * The current rule is a filter expression
+ */
+const NSString * const SerFilterClassExpression = @"expressionNode";
+/**
+ * Group Nodes only:
+ * Indicates whether the group is a conjunction.
+ * If YES, the children will be combined using "AND", otherwise using "OR".
+ */
+const NSString * const SerFilterGroupIsConjunction = @"isConjunction";
+/**
+ * Group Nodes only:
+ * An array of child filter rules (which again can be group or expression rules)
+ */
+const NSString * const SerFilterGroupChildren = @"children";
+/**
+ * Expression Nodes only:
+ * The name of the column to filter in (left side expression)
+ *
+ * Legacy names:
+ * @"filterField", fieldField
+ */
+const NSString * const SerFilterExprColumn = @"column";
+/**
+ * Expression Nodes only:
+ * The data type grouping of the column for applicable filters
+ */
+const NSString * const SerFilterExprType = @"filterType";
+/**
+ * Expression Nodes only:
+ * The title of the filter operator to apply
+ *
+ * Legacy names:
+ * @"filterComparison", compareField
+ */
+const NSString * const SerFilterExprComparison = @"filterComparison";
+/**
+ * Expression Nodes only:
+ * The values to apply the filter with (an array of 0 or more elements)
+ *
+ * Legacy names:
+ * @"filterValue", argumentField
+ * @"firstBetweenField", @"secondBetweenField", firstBetweenField, secondBetweenField
+ */
+const NSString * const SerFilterExprValues = @"filterValues";
+/**
+ * Expression Nodes only:
+ * the filter definition dictionary (as in ContentFilters.plist)
+ * for the filter represented by SerFilterExprComparison.
+ *
+ * This item is not designed to be serialized to disk
+ */
+const NSString * const SerFilterExprDefinition = @"_filterDefinition";
+
+#pragma mark -
+
+@interface RuleNode : NSObject {
+ RuleNodeType type;
+}
+@property(assign, nonatomic) RuleNodeType type;
+@end
+
+@interface ColumnNode : RuleNode {
+ NSString *name;
+ NSString *typegrouping;
+ NSArray *operatorCache;
+ NSUInteger opCacheVersion;
+}
+@property(copy, nonatomic) NSString *name;
+@property(copy, nonatomic) NSString *typegrouping;
+@property(retain, nonatomic) NSArray *operatorCache;
+@property(assign, nonatomic) NSUInteger opCacheVersion;
+@end
+
+@interface StringNode : RuleNode {
+ NSString *value;
+}
+@property(copy, nonatomic) NSString *value;
+@end
+
+@interface OpNode : RuleNode {
+ // Note: The main purpose of this field is to have @"=" for column A and @"=" for column B to return NO in -isEqual:
+ // because otherwise NSRuleEditor will get confused and blow up.
+ ColumnNode *parentColumn;
+ NSDictionary *settings;
+ NSDictionary *filter;
+}
+@property (assign, nonatomic) ColumnNode *parentColumn;
+@property (retain, nonatomic) NSDictionary *settings;
+@property (retain, nonatomic) NSDictionary *filter;
+@end
+
+@interface ArgNode : RuleNode {
+ NSDictionary *filter;
+ NSUInteger argIndex;
+ NSString *initialValue;
+}
+@property (copy, nonatomic) NSString *initialValue;
+@property (retain, nonatomic) NSDictionary *filter;
+@property (assign, nonatomic) NSUInteger argIndex;
+@end
+
+@interface ConnectorNode : RuleNode {
+ NSDictionary *filter;
+ NSUInteger labelIndex;
+}
+@property (retain, nonatomic) NSDictionary *filter;
+@property (assign, nonatomic) NSUInteger labelIndex;
+@end
+
+#pragma mark -
+
+/**
+ * TODO:
+ * This class shouldn't even exist to begin with.
+ * Its sad story begins with this call in `-[SPRuleFilterController dealloc]`:
+ *
+ * [filterRuleEditor unbind:@"rows"];
+ *
+ * `-dealloc` may not be the best method to undo what we did in `-awakeFromNib`, but it's the only thing we have.
+ * Also we have to unbind this object, or we may receive zombie calls later on because the binding is unretained.
+ * Which brings us to another huge mistake Apple made in the implementation of -unbind. The call looks like this:
+ *
+ * - [NSRulEditor unbind:]
+ * - [NSRuleEditor _rootRowsArray]
+ * - [NSRuleEditor->_boundArrayOwner mutableArrayValueForKeyPath:NSRuleEditor->_boundArrayKeyPath]
+ *
+ * -mutableArrayValueForKeyPath: is the culprit here since it does not return the object itself ("model") but
+ * instead returns an autoreleased proxy object which retains the parent object of the key.
+ *
+ * That explains why we can't put "model" into SPRuleFilterController:
+ * The `-[NSRuleEditor unbind:]` would cause a call to `-[SPRuleFilterController retain]` from within
+ * `-[SPRuleFilterController dealloc]` (which is pointless since there is no way out from -dealloc).
+ * This wouldn't be a problem if the proxy object was released again while dealloc is still on the stack, but
+ * since it is autoreleased we end up with a zombie call again.
+ *
+ * ModelContainer is a dummy intermediate to prevent this, since it is still valid when we enter -dealloc and
+ * trigger -unbind and thus can handle the -retain by the proxy object.
+ */
+@interface ModelContainer : NSObject
+{
+ NSMutableArray *model;
+}
+// This is the binding used by NSRuleEditor for the current state
+@property (retain, nonatomic) NSMutableArray *model;
+@end
+
+#pragma mark -
+
+@interface SPRuleFilterController () <NSRuleEditorDelegate>
+
+@property (readwrite, assign, nonatomic) CGFloat preferredHeight;
+
+- (NSArray *)_compareTypesForColumn:(ColumnNode *)colNode;
+- (IBAction)_textFieldAction:(id)sender;
+- (IBAction)_editFiltersAction:(id)sender;
+- (void)_contentFiltersHaveBeenUpdated:(NSNotification *)notification;
++ (NSDictionary *)_flattenSerializedFilter:(NSDictionary *)in;
+static BOOL SerIsGroup(NSDictionary *dict);
+- (NSDictionary *)_serializedFilterIncludingFilterDefinition:(BOOL)includeDefinition;
++ (void)_writeFilterTree:(NSDictionary *)in toString:(NSMutableString *)out wrapInParenthesis:(BOOL)wrap binary:(BOOL)isBINARY error:(NSError **)err;
+- (NSMutableDictionary *)_restoreSerializedFilter:(NSDictionary *)serialized;
+static void _addIfNotNil(NSMutableArray *array, id toAdd);
+- (ColumnNode *)_columnForName:(NSString *)name;
+- (OpNode *)_operatorNamed:(NSString *)title forColumn:(ColumnNode *)col;
+- (BOOL)_focusOnFieldInSubtree:(NSDictionary *)dict;
+- (void)_resize;
+- (void)openContentFilterManagerForFilterType:(NSString *)filterType;
+- (IBAction)filterTable:(id)sender;
+- (IBAction)_menuItemInRuleEditorClicked:(id)sender;
+- (void)_pretendPlayRuleEditorForCriteria:(NSMutableArray *)criteria displayValues:(NSMutableArray *)displayValues inRow:(NSInteger)row;
+- (void)_ensureValidOperatorCache:(ColumnNode *)col;
+static BOOL _arrayContainsInViewHierarchy(NSArray *haystack, id needle);
+
+@end
+
+@implementation SPRuleFilterController
+
+@synthesize preferredHeight = preferredHeight;
+@synthesize target = target;
+@synthesize action = action;
+
+- (instancetype)init
+{
+ if((self = [super init])) {
+ columns = [[NSMutableArray alloc] init];
+ _modelContainer = [[ModelContainer alloc] init];
+ preferredHeight = 0.0;
+ target = nil;
+ action = NULL;
+ opNodeCacheVersion = 1;
+
+ // Init default filters for Content Browser
+ contentFilters = [[NSMutableDictionary alloc] init];
+ numberOfDefaultFilters = [[NSMutableDictionary alloc] init];
+
+ NSError *readError = nil;
+ NSString *filePath = [NSBundle pathForResource:@"ContentFilters.plist" ofType:nil inDirectory:[[NSBundle mainBundle] bundlePath]];
+ NSData *defaultFilterData = [NSData dataWithContentsOfFile:filePath
+ options:NSMappedRead
+ error:&readError];
+
+ if(defaultFilterData && !readError) {
+ NSDictionary *defaultFilterDict = [NSPropertyListSerialization propertyListWithData:defaultFilterData
+ options:NSPropertyListMutableContainersAndLeaves
+ format:NULL
+ error:&readError];
+
+ if(defaultFilterDict && !readError) {
+ [contentFilters setDictionary:defaultFilterDict];
+ }
+ }
+
+ if (readError) {
+ NSLog(@"Error while reading 'ContentFilters.plist':\n%@", readError);
+ NSBeep();
+ }
+ else {
+ [numberOfDefaultFilters setObject:[NSNumber numberWithInteger:[[contentFilters objectForKey:@"number"] count]] forKey:@"number"];
+ [numberOfDefaultFilters setObject:[NSNumber numberWithInteger:[[contentFilters objectForKey:@"date"] count]] forKey:@"date"];
+ [numberOfDefaultFilters setObject:[NSNumber numberWithInteger:[[contentFilters objectForKey:@"string"] count]] forKey:@"string"];
+ [numberOfDefaultFilters setObject:[NSNumber numberWithInteger:[[contentFilters objectForKey:@"spatial"] count]] forKey:@"spatial"];
+ }
+ }
+ return self;
+}
+
+- (void)awakeFromNib
+{
+ [filterRuleEditor bind:@"rows" toObject:_modelContainer withKeyPath:@"model" options:nil];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(_contentFiltersHaveBeenUpdated:)
+ name:SPContentFiltersHaveBeenUpdatedNotification
+ object:nil];
+}
+
+- (void)focusFirstInputField
+{
+ for(NSDictionary *rootItem in [_modelContainer model]) {
+ if([self _focusOnFieldInSubtree:rootItem]) return;
+ }
+}
+
+- (BOOL)_focusOnFieldInSubtree:(NSDictionary *)dict
+{
+ //if we are a simple row we might have an input field ourself, otherwise search among our children
+ if([[dict objectForKey:@"rowType"] unsignedIntegerValue] == NSRuleEditorRowTypeSimple) {
+ for(id obj in [dict objectForKey:@"displayValues"]) {
+ if([obj isKindOfClass:[NSTextField class]]) {
+ [[(NSTextField *)obj window] makeFirstResponder:obj];
+ return YES;
+ }
+ }
+ }
+ else {
+ for(NSDictionary *child in [dict objectForKey:@"subrows"]) {
+ if([self _focusOnFieldInSubtree:child]) return YES;
+ }
+ }
+ return NO;
+}
+
+- (void)setColumns:(NSArray *)dataColumns;
+{
+ // we have to access the model in the same way the rule editor does for it to realize the changes
+ [[_modelContainer mutableArrayValueForKey:@"model"] removeAllObjects];
+
+ [columns removeAllObjects];
+
+ //without a table there is nothing to filter
+ if(dataColumns) {
+ //sort column names if enabled
+ NSArray *columnDefinitions = dataColumns;
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:SPAlphabeticalTableSorting]) {
+ NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
+ columnDefinitions = [columnDefinitions sortedArrayUsingDescriptors:@[sortDescriptor]];
+ }
+
+ // get the columns
+ for (NSDictionary *colDef in columnDefinitions) {
+ ColumnNode *node = [[ColumnNode alloc] init];
+ [node setName:[colDef objectForKey:@"name"]];
+ [node setTypegrouping:[colDef objectForKey:@"typegrouping"]];
+ [columns addObject:node];
+ [node release];
+ }
+ }
+
+ // make the rule editor reload the criteria
+ [filterRuleEditor reloadCriteria];
+
+ // disable UI if no criteria exist
+ [self setEnabled:([columns count] != 0)];
+}
+
+- (NSInteger)ruleEditor:(NSRuleEditor *)editor numberOfChildrenForCriterion:(nullable id)criterion withRowType:(NSRuleEditorRowType)rowType
+{
+ // nil criterion is always the first element in a row, compound rows are only for "AND"/"OR" groups
+ if(!criterion && rowType == NSRuleEditorRowTypeCompound) {
+ return 2;
+ }
+ else if(!criterion && rowType == NSRuleEditorRowTypeSimple) {
+ return [columns count];
+ }
+ else if(rowType == NSRuleEditorRowTypeSimple) {
+ // the children of the columns are their operators
+ RuleNodeType type = [(RuleNode *)criterion type];
+ if(type == RuleNodeTypeColumn) {
+ ColumnNode *node = (ColumnNode *)criterion;
+ [self _ensureValidOperatorCache:node];
+ return [[node operatorCache] count];
+ }
+ // the first child of an operator is the first argument (if it has one)
+ else if(type == RuleNodeTypeOperator) {
+ OpNode *node = (OpNode *)criterion;
+ NSInteger numOfArgs = [[[node filter] objectForKey:@"NumberOfArguments"] integerValue];
+ return (numOfArgs > 0) ? 1 : 0;
+ }
+ // the child of an argument can only be the conjunction label if more arguments follow
+ else if(type == RuleNodeTypeArgument) {
+ ArgNode *node = (ArgNode *)criterion;
+ NSInteger numOfArgs = [[[node filter] objectForKey:@"NumberOfArguments"] integerValue];
+ return (numOfArgs > [node argIndex]+1) ? 1 : 0;
+ }
+ // the child of a conjunction is the next argument, if we have one
+ else if(type == RuleNodeTypeConnector) {
+ ConnectorNode *node = (ConnectorNode *)criterion;
+ NSInteger numOfArgs = [[[node filter] objectForKey:@"NumberOfArguments"] integerValue];
+ return (numOfArgs > [node labelIndex]+1) ? 1 : 0;
+ }
+ }
+ return 0;
+}
+
+- (id)ruleEditor:(NSRuleEditor *)editor child:(NSInteger)index forCriterion:(nullable id)criterion withRowType:(NSRuleEditorRowType)rowType
+{
+ // nil criterion is always the first element in a row, compound rows are only for "AND"/"OR" groups
+ if(!criterion && rowType == NSRuleEditorRowTypeCompound) {
+ StringNode *node = [[StringNode alloc] init];
+ switch(index) {
+ case 0: [node setValue:@"AND"]; break;
+ case 1: [node setValue:@"OR"]; break;
+ }
+ return [node autorelease];
+ }
+ // this is the column field
+ else if(!criterion && rowType == NSRuleEditorRowTypeSimple) {
+ return [columns objectAtIndex:index];
+ }
+ else if(rowType == NSRuleEditorRowTypeSimple) {
+ // the children of the columns are their operators
+ RuleNodeType type = [(RuleNode *) criterion type];
+ if (type == RuleNodeTypeColumn) {
+ return [[criterion operatorCache] objectAtIndex:index];
+ }
+ // the first child of an operator is the first argument
+ else if(type == RuleNodeTypeOperator) {
+ NSDictionary *filter = [(OpNode *)criterion filter];
+ if([[filter objectForKey:@"NumberOfArguments"] integerValue]) {
+ ArgNode *arg = [[ArgNode alloc] init];
+ [arg setFilter:filter];
+ [arg setArgIndex:0];
+ return [arg autorelease];
+ }
+ }
+ // the child of an argument can only be the conjunction label if more arguments follow
+ else if(type == RuleNodeTypeArgument) {
+ NSDictionary *filter = [(ArgNode *)criterion filter];
+ NSUInteger argIndex = [(ArgNode *)criterion argIndex];
+ if([[filter objectForKey:@"NumberOfArguments"] integerValue] > argIndex +1) {
+ ConnectorNode *node = [[ConnectorNode alloc] init];
+ [node setFilter:filter];
+ [node setLabelIndex:argIndex]; // label 0 follows argument 0
+ return [node autorelease];
+ }
+ }
+ // the child of a conjunction is the next argument, if we have one
+ else if(type == RuleNodeTypeConnector) {
+ ConnectorNode *node = (ConnectorNode *)criterion;
+ NSInteger numOfArgs = [[[node filter] objectForKey:@"NumberOfArguments"] integerValue];
+ if(numOfArgs > [node labelIndex]+1) {
+ ArgNode *arg = [[ArgNode alloc] init];
+ [arg setFilter:[node filter]];
+ [arg setArgIndex:([node labelIndex]+1)];
+ return [arg autorelease];
+ }
+ }
+ }
+ return nil;
+}
+
+- (id)ruleEditor:(NSRuleEditor *)editor displayValueForCriterion:(id)criterion inRow:(NSInteger)row
+{
+ switch([(RuleNode *)criterion type]) {
+ case RuleNodeTypeString: return [(StringNode *)criterion value];
+ case RuleNodeTypeColumn: return [(ColumnNode *)criterion name];
+ case RuleNodeTypeOperator: {
+ OpNode *node = (OpNode *)criterion;
+ NSMenuItem *item;
+ if ([[[node settings] objectForKey:@"isSeparator"] boolValue]) {
+ item = [NSMenuItem separatorItem];
+ }
+ else {
+ /* NOTE:
+ * Apple's doc on NSRuleEditor says that returning NSMenuItems is supported.
+ * However there seems to be a major discrepancy between what Apple considers "supported" and what any
+ * sane person would consider supported.
+ *
+ * Basically one would expect NSMenuItems to be handled in the same way a number of NSString children of a
+ * row's element will be handled, but that was not Apples intention. By supported they actually mean
+ * "Your app won't crash immediately if you return an NSMenuItem here" - but that's about it.
+ * Even selecting such an NSMenuItem will already cause an exception on 10.6 and be treated as a NOOP on
+ * later OSes.
+ * So if we return NSMenuItems we have to implement the full logic of the NSRuleEditor for updating and
+ * displaying the row ourselves, starting with handling the target/action of the NSMenuItems!
+ */
+ item = [[NSMenuItem alloc] initWithTitle:[[node settings] objectForKey:@"title"] action:NULL keyEquivalent:@""];
+ [item setToolTip:[[node settings] objectForKey:@"tooltip"]];
+ [item setTag:[[[node settings] objectForKey:@"tag"] integerValue]];
+ [item setRepresentedObject:@{
+ @"node": node,
+ // this one is needed by the "Edit filters…" item for context
+ @"filterType": SPBoxNil([[node settings] objectForKey:@"filterType"]),
+ }];
+ [item setTarget:self];
+ [item setAction:@selector(_menuItemInRuleEditorClicked:)];
+ [item autorelease];
+ }
+ return item;
+ }
+ case RuleNodeTypeArgument: {
+ //an argument is a textfield
+ ArgNode *node = (ArgNode *)criterion;
+ NSTextField *textField = [[NSTextField alloc] init];
+ [[textField cell] setSendsActionOnEndEditing:YES];
+ [[textField cell] setUsesSingleLineMode:YES];
+ [textField setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
+ [textField sizeToFit];
+ [textField setTarget:self];
+ [textField setAction:@selector(_textFieldAction:)];
+ if([node initialValue]) [textField setStringValue:[node initialValue]];
+ NSRect frame = [textField frame];
+ //adjust width, to make the field wider
+ frame.size.width = 500; //TODO determine a good width (possibly from the field type size) - how to access the rule editors bounds?
+ [textField setFrame:frame];
+ return [textField autorelease];
+ }
+ case RuleNodeTypeConnector: {
+ // a simple string for once
+ ConnectorNode *node = (ConnectorNode *)criterion;
+ NSArray* labels = [[node filter] objectForKey:@"ConjunctionLabels"];
+ return (labels && [labels count] == 1)? [labels objectAtIndex:0] : @"";
+ }
+ }
+
+ return nil;
+}
+
+- (IBAction)_textFieldAction:(id)sender
+{
+ // if the action was caused by pressing return or enter, trigger filtering
+ NSEvent *event = [NSApp currentEvent];
+ if(event && [event type] == NSKeyDown && ([event keyCode] == 36 || [event keyCode] == 76)) {
+ [self filterTable:nil];
+ }
+}
+
+- (IBAction)_menuItemInRuleEditorClicked:(id)sender
+{
+ if(!sender) return; // NSRuleEditor will throw on nil
+
+ NSInteger row = [filterRuleEditor rowForDisplayValue:sender];
+
+ if(row == NSNotFound) return; // unknown display values
+
+ OpNode *node = [[(NSMenuItem *)sender representedObject] objectForKey:@"node"];
+
+ // if the row has an explicit handler, pass on the action and do nothing
+ id _target = [[node settings] objectForKey:@"target"];
+ SEL _action = (SEL)[(NSValue *)[[node settings] objectForKey:@"action"] pointerValue];
+ if(_target && _action) {
+ [_target performSelector:_action withObject:sender];
+ return;
+ }
+
+ /* now comes the painful part, where we'd have to find out where exactly in the row this
+ * displayValue should appear.
+ *
+ * Luckily we know that this method will only be invoked by the displayValues of OpNode
+ * and currently OpNode can only appear as the second node in a row (after the column).
+ *
+ * Annoyingly we can't tell the rule editor to just replace a single element. We actually
+ * have to recalculate the whole row starting with the element we replaced - a task the
+ * rule editor would normally do for us when using NSStrings!
+ */
+ NSMutableArray *criteria = [[filterRuleEditor criteriaForRow:row] mutableCopy];
+ NSMutableArray *displayValues = [[filterRuleEditor displayValuesForRow:row] mutableCopy];
+
+ // find the position of the previous opnode (just for safety)
+ NSUInteger opIndex = NSNotFound;
+ NSUInteger i = 0;
+ for(RuleNode *obj in criteria) {
+ if([obj type] == RuleNodeTypeOperator) {
+ opIndex = i;
+ break;
+ }
+ i++;
+ }
+
+ if(opIndex < [criteria count]) {
+ // yet another uglyness: if one of the displayValues is an input and currently the first responder
+ // we have to manually restore that for the new input we create for UX reasons.
+ // However an NSTextField is seldom a first responder, usually it's an invisible subview of the text field...
+ id firstResponder = [[filterRuleEditor window] firstResponder];
+ BOOL hasFirstResponderInRow = _arrayContainsInViewHierarchy(displayValues, firstResponder);
+
+ //remove previous opnode and everything that follows and append new opnode
+ NSRange stripRange = NSMakeRange(opIndex, ([criteria count] - opIndex));
+ [criteria removeObjectsInRange:stripRange];
+ [criteria addObject:node];
+
+ //remove the display value for the old op node and everything that followed
+ [displayValues removeObjectsInRange:stripRange];
+
+ //now we'll fill in everything again
+ [self _pretendPlayRuleEditorForCriteria:criteria displayValues:displayValues inRow:row];
+
+ //and update the row to its new state
+ [filterRuleEditor setCriteria:criteria andDisplayValues:displayValues forRowAtIndex:row];
+
+ if(hasFirstResponderInRow) {
+ // make the next possible object after the opnode the new next responder (since the previous one is gone now)
+ for (NSUInteger j = stripRange.location + 1; j < [displayValues count]; ++j) {
+ id obj = [displayValues objectAtIndex:j];
+ if([obj respondsToSelector:@selector(acceptsFirstResponder)] && [obj acceptsFirstResponder]) {
+ [[filterRuleEditor window] makeFirstResponder:obj];
+ break;
+ }
+ }
+ }
+ }
+
+ [criteria release];
+ [displayValues release];
+}
+
+BOOL _arrayContainsInViewHierarchy(NSArray *haystack, id needle)
+{
+ //first, try it the easy way
+ if([haystack indexOfObjectIdenticalTo:needle] != NSNotFound) return YES;
+
+ // otherwise, if needle is a view, check if it appears as a desencdant of some other view in haystack
+ Class NSViewClass = [NSView class];
+ if([needle isKindOfClass:NSViewClass]) {
+ for(id obj in haystack) {
+ if([obj isKindOfClass:NSViewClass] && [needle isDescendantOf:obj]) return YES;
+ }
+ }
+
+ return NO;
+}
+
+/**
+ * This method recursively fills up the passed-in criteria and displayValues arrays with objects in the way the
+ * NSRuleEditor would, so they can be used with the -setCriteria:andDisplayValues:forRowAtIndex: call.
+ *
+ * Assumptions made:
+ * - row is a valid row within the bounds of the rule editor
+ * - criteria contains at least one object
+ * - displayValues contains exactly one less object than criteria
+ */
+- (void)_pretendPlayRuleEditorForCriteria:(NSMutableArray *)criteria displayValues:(NSMutableArray *)displayValues inRow:(NSInteger)row
+{
+ id curCriterion = [criteria lastObject];
+
+ //first fill in the display value for the current criterion
+ id display = [self ruleEditor:filterRuleEditor displayValueForCriterion:curCriterion inRow:row];
+ if(!display) return; // abort if unset
+ [displayValues addObject:display];
+
+ // now let's check if we have to go deeper
+ NSRuleEditorRowType rowType = [filterRuleEditor rowTypeForRow:row];
+ if([self ruleEditor:filterRuleEditor numberOfChildrenForCriterion:curCriterion withRowType:rowType]) {
+ // we only care for the first child, though
+ id nextCriterion = [self ruleEditor:filterRuleEditor child:0 forCriterion:curCriterion withRowType:rowType];
+ if(nextCriterion) {
+ [criteria addObject:nextCriterion];
+ [self _pretendPlayRuleEditorForCriteria:criteria displayValues:displayValues inRow:row];
+ }
+ }
+}
+
+- (IBAction)filterTable:(id)sender
+{
+ if(target && action) [target performSelector:action withObject:self];
+}
+
+- (void)_resize
+{
+ // The situation with the sizing is a bit f'ed up:
+ // - When -ruleEditorRowsDidChange: is invoked the NSRuleEditor has not yet updated its required frame size
+ // - We can't use KVO on -frame either, because SPTableContent will update the container size which
+ // ultimately also updates the NSRuleEditor's frame, causing a loop
+ // - Calling -sizeToFit works, but only when the NSRuleEditor is growing. It won't shrink
+ // after removing rows.
+ // - -intrinsicContentSize is what we want, but that method is 10.7+, so on 10.6 let's do the
+ // easiest workaround (note that both -intrinsicContentSize and -sizeToFit internally use -[NSRuleEditor _minimumFrameHeight])
+ CGFloat wantsHeight;
+ if([filterRuleEditor respondsToSelector:@selector(intrinsicContentSize)]) {
+ NSSize sz = [filterRuleEditor intrinsicContentSize];
+ wantsHeight = sz.height;
+ }
+ else {
+ wantsHeight = [filterRuleEditor rowHeight] * [filterRuleEditor numberOfRows];
+ }
+ if(wantsHeight != preferredHeight) {
+ [self setPreferredHeight:wantsHeight];
+ [[NSNotificationCenter defaultCenter] postNotificationName:SPRuleFilterHeightChangedNotification object:self];
+ }
+}
+
+- (void)ruleEditorRowsDidChange:(NSNotification *)notification
+{
+ //TODO find a better way to trigger resize
+ // We can't do this here, because it will cause rows to jump around when removing them (the add case works fine, though)
+ [self performSelector:@selector(_resize) withObject:nil afterDelay:0.2];
+ //[self _resize];
+}
+
+- (void)dealloc
+{
+ [filterRuleEditor unbind:@"rows"];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ // WARNING: THIS MUST COME AFTER -unbind:! See the class comment on ModelContainer for the reasoning
+ SPClear(_modelContainer);
+ SPClear(columns);
+ SPClear(contentFilters);
+ SPClear(numberOfDefaultFilters);
+ [super dealloc];
+}
+
+/**
+ * Sets the compare types for the filter and the appropriate formatter for the textField
+ */
+- (NSArray *)_compareTypesForColumn:(ColumnNode *)colNode
+{
+ if(contentFilters == nil
+ || ![contentFilters objectForKey:@"number"]
+ || ![contentFilters objectForKey:@"string"]
+ || ![contentFilters objectForKey:@"date"]) {
+ NSLog(@"Error while setting filter types.");
+ NSBeep();
+ return @[];
+ }
+
+ NSString *fieldTypeGrouping;
+ if([colNode typegrouping]) {
+ fieldTypeGrouping = [NSString stringWithString:[colNode typegrouping]];
+ }
+ else {
+ return @[];
+ }
+
+ NSMutableArray *compareItems = [NSMutableArray array];
+
+ NSString *compareType;
+
+ if ( [fieldTypeGrouping isEqualToString:@"date"] ) {
+ compareType = @"date";
+
+ /*
+ if ([fieldType isEqualToString:@"timestamp"]) {
+ [argumentField setFormatter:[[NSDateFormatter alloc]
+ initWithDateFormat:@"%Y-%m-%d %H:%M:%S" allowNaturalLanguage:YES]];
+ }
+ if ([fieldType isEqualToString:@"datetime"]) {
+ [argumentField setFormatter:[[NSDateFormatter alloc] initWithDateFormat:@"%Y-%m-%d %H:%M:%S" allowNaturalLanguage:YES]];
+ }
+ if ([fieldType isEqualToString:@"date"]) {
+ [argumentField setFormatter:[[NSDateFormatter alloc] initWithDateFormat:@"%Y-%m-%d" allowNaturalLanguage:YES]];
+ }
+ if ([fieldType isEqualToString:@"time"]) {
+ [argumentField setFormatter:[[NSDateFormatter alloc] initWithDateFormat:@"%H:%M:%S" allowNaturalLanguage:YES]];
+ }
+ if ([fieldType isEqualToString:@"year"]) {
+ [argumentField setFormatter:[[NSDateFormatter alloc] initWithDateFormat:@"%Y" allowNaturalLanguage:YES]];
+ }
+ */
+
+ // TODO: A bug in the framework previously meant enum fields had to be treated as string fields for the purposes
+ // of comparison - this can now be split out to support additional comparison fucntionality if desired.
+ }
+ else if ([fieldTypeGrouping isEqualToString:@"string"] || [fieldTypeGrouping isEqualToString:@"binary"]
+ || [fieldTypeGrouping isEqualToString:@"textdata"] || [fieldTypeGrouping isEqualToString:@"blobdata"]
+ || [fieldTypeGrouping isEqualToString:@"enum"]) {
+
+ compareType = @"string";
+ // [argumentField setFormatter:nil];
+
+ }
+ else if ([fieldTypeGrouping isEqualToString:@"bit"] || [fieldTypeGrouping isEqualToString:@"integer"]
+ || [fieldTypeGrouping isEqualToString:@"float"]) {
+ compareType = @"number";
+ // [argumentField setFormatter:numberFormatter];
+
+ }
+ else if ([fieldTypeGrouping isEqualToString:@"geometry"]) {
+ compareType = @"spatial";
+
+ }
+ else {
+ compareType = @"";
+ NSBeep();
+ NSLog(@"ERROR: unknown type for comparision: in %@", fieldTypeGrouping);
+ }
+
+ // Add IS NULL and IS NOT NULL as they should always be available
+ // [compareField addItemWithTitle:@"IS NULL"];
+ // [compareField addItemWithTitle:@"IS NOT NULL"];
+
+ // Remove user-defined filters first
+ if([numberOfDefaultFilters objectForKey:compareType]) {
+ NSUInteger cycles = [[contentFilters objectForKey:compareType] count] - [[numberOfDefaultFilters objectForKey:compareType] integerValue];
+ while(cycles > 0) {
+ [[contentFilters objectForKey:compareType] removeLastObject];
+ cycles--;
+ }
+ }
+
+ NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
+
+#ifndef SP_CODA /* content filters */
+ // Load global user-defined content filters
+ if([prefs objectForKey:SPContentFilters]
+ && [contentFilters objectForKey:compareType]
+ && [[prefs objectForKey:SPContentFilters] objectForKey:compareType])
+ {
+ [[contentFilters objectForKey:compareType] addObjectsFromArray:[[prefs objectForKey:SPContentFilters] objectForKey:compareType]];
+ }
+
+ // Load doc-based user-defined content filters
+ if([[SPQueryController sharedQueryController] contentFilterForFileURL:[tableDocumentInstance fileURL]]) {
+ id filters = [[SPQueryController sharedQueryController] contentFilterForFileURL:[tableDocumentInstance fileURL]];
+ if([filters objectForKey:compareType])
+ [[contentFilters objectForKey:compareType] addObjectsFromArray:[filters objectForKey:compareType]];
+ }
+#endif
+
+ NSUInteger i = 0;
+ if([contentFilters objectForKey:compareType]) {
+ for (id filter in [contentFilters objectForKey:compareType]) {
+ // Create the tooltip
+ NSString *tooltip;
+ if ([filter objectForKey:@"Tooltip"])
+ tooltip = [filter objectForKey:@"Tooltip"];
+ else {
+ NSMutableString *tip = [[NSMutableString alloc] init];
+ if ([filter objectForKey:@"Clause"] && [(NSString *) [filter objectForKey:@"Clause"] length]) {
+ [tip setString:[[filter objectForKey:@"Clause"] stringByReplacingOccurrencesOfRegex:@"(?<!\\\\)(\\$\\{.*?\\})" withString:@"[arg]"]];
+ if ([tip isMatchedByRegex:@"(?<!\\\\)\\$BINARY"]) {
+ [tip replaceOccurrencesOfRegex:@"(?<!\\\\)\\$BINARY" withString:@""];
+ [tip appendString:NSLocalizedString(@"\n\nPress ⇧ for binary search (case-sensitive).", @"\n\npress shift for binary search tooltip message")];
+ }
+ [tip flushCachedRegexData];
+ [tip replaceOccurrencesOfRegex:@"(?<!\\\\)\\$CURRENT_FIELD" withString:[[colNode name] backtickQuotedString]];
+ [tip flushCachedRegexData];
+ tooltip = [NSString stringWithString:tip];
+ } else {
+ tooltip = @"";
+ }
+ [tip release];
+ }
+
+ OpNode *node = [[OpNode alloc] init];
+ [node setParentColumn:colNode];
+ [node setSettings:@{
+ @"title": ([filter objectForKey:@"MenuLabel"] ? [filter objectForKey:@"MenuLabel"] : @"not specified"),
+ @"tooltip": tooltip,
+ @"tag": @(i),
+ @"filterType": compareType,
+ }];
+ [node setFilter:filter];
+ [compareItems addObject:node];
+ [node release];
+ i++;
+ }
+ }
+
+ {
+ OpNode *node = [[OpNode alloc] init];
+ [node setParentColumn:colNode];
+ [node setSettings:@{
+ @"isSeparator": @YES,
+ }];
+ [compareItems addObject:node];
+ [node release];
+ }
+
+ {
+ OpNode *node = [[OpNode alloc] init];
+ [node setParentColumn:colNode];
+ [node setSettings:@{
+ @"title": NSLocalizedString(@"Edit Filters…", @"edit filter"),
+ @"tooltip": NSLocalizedString(@"Edit user-defined Filters…", @"edit user-defined filter"),
+ @"tag": @(i),
+ @"target": self,
+ @"action": [NSValue valueWithPointer:@selector(_editFiltersAction:)],
+ @"filterType": compareType,
+ }];
+ [compareItems addObject:node];
+ [node release];
+ }
+
+ return compareItems;
+}
+
+- (IBAction)_editFiltersAction:(id)sender
+{
+ if([sender isKindOfClass:[NSMenuItem class]]) {
+ NSMenuItem *menuItem = (NSMenuItem *)sender;
+ NSString *filterType = [(NSDictionary *)[menuItem representedObject] objectForKey:@"filterType"];
+ if([filterType unboxNull]) [self openContentFilterManagerForFilterType:filterType];
+ }
+}
+
+- (void)openContentFilterManagerForFilterType:(NSString *)filterType
+{
+ // init query favorites controller
+#ifndef SP_CODA
+ [[NSUserDefaults standardUserDefaults] synchronize];
+#endif
+ if(contentFilterManager) [contentFilterManager release];
+ contentFilterManager = [[SPContentFilterManager alloc] initWithDatabaseDocument:tableDocumentInstance forFilterType:filterType];
+
+ // Open query favorite manager
+ [NSApp beginSheet:[contentFilterManager window]
+ modalForWindow:[tableDocumentInstance parentWindow]
+ modalDelegate:contentFilterManager
+ didEndSelector:nil
+ contextInfo:nil];
+}
+
+- (void)_contentFiltersHaveBeenUpdated:(NSNotification *)notification
+{
+ // invalidate our OpNode caches
+ opNodeCacheVersion++;
+ //tell the rule editor to reload its criteria
+ [filterRuleEditor reloadCriteria];
+}
+
+- (void)_ensureValidOperatorCache:(ColumnNode *)col
+{
+ if(![col operatorCache] || [col opCacheVersion] != opNodeCacheVersion) {
+ NSArray *ops = [self _compareTypesForColumn:col];
+ [col setOperatorCache:ops];
+ [col setOpCacheVersion:opNodeCacheVersion];
+ }
+}
+
+- (BOOL)isEmpty
+{
+ return ([[_modelContainer model] count] == 0);
+}
+
+- (void)addFilterExpression
+{
+ [filterRuleEditor insertRowAtIndex:0 withType:NSRuleEditorRowTypeSimple asSubrowOfRow:-1 animate:NO];
+}
+
+- (NSRuleEditor *)view
+{
+ return filterRuleEditor;
+}
+
+- (BOOL)isEnabled
+{
+ return enabled;
+}
+
+- (void)setEnabled:(BOOL)_enabled
+{
+ enabled = _enabled;
+ [filterButton setEnabled:_enabled];
+ [filterRuleEditor setEnabled:_enabled];
+}
+
+- (NSString *)sqlWhereExpressionWithBinary:(BOOL)isBINARY error:(NSError **)err
+{
+ NSMutableString *filterString = [[NSMutableString alloc] init];
+ NSError *innerError = nil;
+
+ @autoreleasepool {
+ //get the serialized filter and try to optimise it
+ NSDictionary *filterTree = [[self class] _flattenSerializedFilter:[self _serializedFilterIncludingFilterDefinition:YES]];
+
+ // build it recursively
+ [[self class] _writeFilterTree:filterTree toString:filterString wrapInParenthesis:NO binary:isBINARY error:&innerError];
+
+ [innerError retain]; // carry the error (if any) outside of the scope of the autoreleasepool
+ }
+
+ if(innerError) {
+ [filterString release];
+ if(err) *err = [innerError autorelease];
+ return nil;
+ }
+
+ if(err) *err = nil;
+
+ NSString *out = [filterString copy];
+ [filterString release];
+
+ return [out autorelease];
+}
+
+- (NSDictionary *)serializedFilter
+{
+ return [self _serializedFilterIncludingFilterDefinition:NO];
+}
+
+- (NSDictionary *)_serializedFilterIncludingFilterDefinition:(BOOL)includeDefinition
+{
+ NSMutableArray *rootItems = [NSMutableArray arrayWithCapacity:[[_modelContainer model] count]];
+ for(NSDictionary *item in [_modelContainer model]) {
+ [rootItems addObject:[self _serializeSubtree:item includingDefinition:includeDefinition]];
+ }
+ //the root serialized filter can either be an AND of multiple root items or a single root item
+ if([rootItems count] == 1) {
+ return [rootItems objectAtIndex:0];
+ }
+ else {
+ return @{
+ SerFilterClass: SerFilterClassGroup,
+ SerFilterGroupIsConjunction: @YES,
+ SerFilterGroupChildren: rootItems,
+ };
+ }
+}
+
+- (NSDictionary *)_serializeSubtree:(NSDictionary *)item includingDefinition:(BOOL)includeDefinition
+{
+ NSRuleEditorRowType rowType = (NSRuleEditorRowType)[[item objectForKey:@"rowType"] unsignedIntegerValue];
+ // check if we have an AND or OR compound row
+ if(rowType == NSRuleEditorRowTypeCompound) {
+ // process all children
+ NSArray *subrows = [item objectForKey:@"subrows"];
+ NSMutableArray *children = [[NSMutableArray alloc] initWithCapacity:[subrows count]];
+ for(NSDictionary *subitem in subrows) {
+ [children addObject:[self _serializeSubtree:subitem includingDefinition:includeDefinition]];
+ }
+ StringNode *node = [[item objectForKey:@"criteria"] objectAtIndex:0];
+ BOOL isConjunction = [@"AND" isEqualToString:[node value]];
+ NSDictionary *out = @{
+ SerFilterClass: SerFilterClassGroup,
+ SerFilterGroupIsConjunction: @(isConjunction),
+ SerFilterGroupChildren: children,
+ };
+ [children release];
+ return out;
+ }
+ else {
+ NSArray *criteria = [item objectForKey:@"criteria"];
+ NSArray *displayValues = [item objectForKey:@"displayValues"];
+ ColumnNode *col = [criteria objectAtIndex:0];
+ OpNode *op = [criteria objectAtIndex:1];
+ NSMutableArray *filterValues = [[NSMutableArray alloc] initWithCapacity:2];
+ for (NSUInteger i = 2; i < [criteria count]; ++i) { // the first two must always be column and operator
+ if([(RuleNode *)[criteria objectAtIndex:i] type] != RuleNodeTypeArgument) continue;
+ // if we found an argument, the displayValue will be an NSTextField we can ask for the value
+ NSString *value = [(NSTextField *)[displayValues objectAtIndex:i] stringValue];
+ [filterValues addObject:value];
+ }
+ NSDictionary *out = @{
+ SerFilterClass: SerFilterClassExpression,
+ SerFilterExprColumn: [col name],
+ SerFilterExprType: [[op settings] objectForKey:@"filterType"],
+ SerFilterExprComparison: [[op filter] objectForKey:@"MenuLabel"],
+ SerFilterExprValues: filterValues,
+ };
+ if(includeDefinition) {
+ out = [NSMutableDictionary dictionaryWithDictionary:out];
+ [(NSMutableDictionary *)out setObject:[op filter] forKey:SerFilterExprDefinition];
+ }
+ [filterValues release];
+ return out;
+ }
+}
+
+void _addIfNotNil(NSMutableArray *array, id toAdd)
+{
+ if(toAdd != nil) [array addObject:toAdd];
+}
+
+- (void)restoreSerializedFilters:(NSDictionary *)serialized
+{
+ if(!serialized) return;
+
+ NSMutableArray *newModel = [[NSMutableArray alloc] init];
+ @autoreleasepool {
+ // if the root object is an AND group directly restore its contents, otherwise restore the object
+ if(SerIsGroup(serialized) && [[serialized objectForKey:SerFilterGroupIsConjunction] boolValue]) {
+ for(NSDictionary *child in [serialized objectForKey:SerFilterGroupChildren]) {
+ _addIfNotNil(newModel, [self _restoreSerializedFilter:child]);
+ }
+ }
+ else {
+ _addIfNotNil(newModel, [self _restoreSerializedFilter:serialized]);
+ }
+ }
+
+ // we have to access the model in the same way the rule editor does for it to realize the changes
+ NSMutableArray *proxy = [_modelContainer mutableArrayValueForKey:@"model"];
+ [proxy setArray:newModel];
+
+ [newModel release];
+}
+
+- (NSMutableDictionary *)_restoreSerializedFilter:(NSDictionary *)serialized
+{
+ NSMutableDictionary *obj = [[NSMutableDictionary alloc] initWithCapacity:4];
+
+ if(SerIsGroup(serialized)) {
+ [obj setObject:@(NSRuleEditorRowTypeCompound) forKey:@"rowType"];
+
+ StringNode *sn = [[StringNode alloc] init];
+ [sn setValue:([[serialized objectForKey:SerFilterGroupIsConjunction] boolValue] ? @"AND" : @"OR")];
+ // those have to be mutable arrays for the rule editor to work
+ NSMutableArray *criteria = [NSMutableArray arrayWithObject:sn];
+ [obj setObject:criteria forKey:@"criteria"];
+
+ id displayValue = [self ruleEditor:filterRuleEditor displayValueForCriterion:sn inRow:-1];
+ NSMutableArray *displayValues = [NSMutableArray arrayWithObject:displayValue];
+ [obj setObject:displayValues forKey:@"displayValues"];
+ [sn release];
+
+ NSArray *children = [serialized objectForKey:SerFilterGroupChildren];
+ NSMutableArray *subrows = [[NSMutableArray alloc] initWithCapacity:[children count]];
+ for(NSDictionary *child in children) {
+ _addIfNotNil(subrows, [self _restoreSerializedFilter:child]);
+ }
+ [obj setObject:subrows forKey:@"subrows"];
+ [subrows release];
+ }
+ else {
+ [obj setObject:@(NSRuleEditorRowTypeSimple) forKey:@"rowType"];
+ //simple rows can't have child rows
+ [obj setObject:[NSMutableArray array] forKey:@"subrows"];
+
+ NSMutableArray *criteria = [NSMutableArray arrayWithCapacity:5];
+
+ //first look up the column, bail if it doesn't exist anymore or types changed
+ NSString *columnName = [serialized objectForKey:SerFilterExprColumn];
+ ColumnNode *col = [self _columnForName:columnName];
+ if(!col) {
+ SPLog(@"cannot deserialize unknown column: %@", columnName);
+ goto fail;
+ }
+ [criteria addObject:col];
+
+ //next try to find the given operator
+ NSString *operatorName = [serialized objectForKey:SerFilterExprComparison];
+ OpNode *op = [self _operatorNamed:operatorName forColumn:col];
+ if(!op) {
+ SPLog(@"cannot deserialize unknown operator: %@",operatorName);
+ goto fail;
+ }
+ [criteria addObject:op];
+
+ // we still have to check if the current column type is the same as when we serialized because an operator
+ // with the same name can still act differently for different types
+ NSString *curFilterType = [[op settings] objectForKey:@"filterType"];
+ NSString *serFilterType = [serialized objectForKey:SerFilterExprType]; // this is optional
+ if(serFilterType && ![curFilterType isEqualToString:serFilterType]) {
+ SPLog(@"mismatch in filter types for operator %@: current=%@, serialized=%@",op,curFilterType,serFilterType);
+ goto fail;
+ }
+
+ //now we have to create the argument node(s)
+ NSInteger numOfArgs = [[[op filter] objectForKey:@"NumberOfArguments"] integerValue];
+ //fail if the current op requires more arguments than we have stored values for
+ NSArray *values = [serialized objectForKey:SerFilterExprValues];
+ if(numOfArgs > [values count]) {
+ SPLog(@"filter operator %@ requires %ld arguments, but only have %ld stored values!",op,numOfArgs,[values count]);
+ goto fail;
+ }
+
+ // otherwise add them
+ for (NSUInteger i = 0; i < numOfArgs; ++i) {
+ // insert connector node between args?
+ if(i > 0) {
+ ConnectorNode *node = [[ConnectorNode alloc] init];
+ [node setFilter:[op filter]];
+ [node setLabelIndex:(i-1)]; // label 0 follows argument 0
+ [criteria addObject:node];
+ [node release];
+ }
+ ArgNode *arg = [[ArgNode alloc] init];
+ [arg setArgIndex:i];
+ [arg setFilter:[op filter]];
+ [arg setInitialValue:[values objectAtIndex:i]];
+ [criteria addObject:arg];
+ [arg release];
+ }
+
+ [obj setObject:criteria forKey:@"criteria"];
+
+ //the last thing that remains is creating the displayValues for all criteria
+ NSMutableArray *displayValues = [NSMutableArray arrayWithCapacity:[criteria count]];
+ for(id criterion in criteria) {
+ id dispValue = [self ruleEditor:filterRuleEditor displayValueForCriterion:criterion inRow:-1];
+ if(!dispValue) {
+ SPLog(@"got nil displayValue for criterion %@ on deserialization!",criterion);
+ goto fail;
+ }
+ [displayValues addObject:dispValue];
+ }
+ [obj setObject:displayValues forKey:@"displayValues"];
+ }
+
+ return [obj autorelease];
+
+fail:
+ [obj release];
+ return nil;
+}
+
++ (NSDictionary *)makeSerializedFilterForColumn:(NSString *)colName operator:(NSString *)opName values:(NSArray *)values
+{
+ return @{
+ SerFilterClass: SerFilterClassExpression,
+ SerFilterExprColumn: colName,
+ SerFilterExprComparison: opName,
+ SerFilterExprValues: values,
+ };
+}
+
+- (ColumnNode *)_columnForName:(NSString *)name
+{
+ if([name length]) {
+ for (ColumnNode *col in columns) {
+ if ([name isEqualToString:[col name]]) return col;
+ }
+ }
+ return nil;
+}
+
+- (OpNode *)_operatorNamed:(NSString *)title forColumn:(ColumnNode *)col
+{
+ if([title length]) {
+ // check if we have the operator cache, otherwise build it
+ [self _ensureValidOperatorCache:col];
+ // try to find it in the operator cache
+ for(OpNode *node in [col operatorCache]) {
+ if([[[node filter] objectForKey:@"MenuLabel"] isEqualToString:title]) return node;
+ }
+ }
+ return nil;
+}
+
+BOOL SerIsGroup(NSDictionary *dict)
+{
+ return [SerFilterClassGroup isEqual:[dict objectForKey:SerFilterClass]];
+}
+
+/**
+ * This method looks at the given serialized filter in a recursive manner and
+ * when it encounters
+ * - a group node with only a single child or
+ * - a child that is a group node of the same kind as the parent one
+ * it will pull the child(ren) up
+ *
+ * So for example:
+ * AND(expr1) => expr1
+ * AND(expr1,AND(expr2,expr3)) => AND(expr1,expr2,expr3)
+ *
+ * The input dict is not modified, the returned dict will be equal to the input
+ * dict or have parts of it removed or replaced with new dicts.
+ */
++ (NSDictionary *)_flattenSerializedFilter:(NSDictionary *)in
+{
+ // return non-group-nodes as is
+ if(!SerIsGroup(in)) return in;
+
+ NSNumber *inIsConjunction = [in objectForKey:SerFilterGroupIsConjunction];
+
+ // first give all children the chance to flatten (depth first)
+ NSArray *children = [in objectForKey:SerFilterGroupChildren];
+ NSMutableArray *flatChildren = [NSMutableArray arrayWithCapacity:[children count]];
+ NSUInteger changed = 0;
+ for(NSDictionary *child in children) {
+ NSDictionary *flattened = [self _flattenSerializedFilter:child];
+ //take a closer look at the (possibly changed) child - is it a group node of the same kind as us?
+ if(SerIsGroup(flattened) && [inIsConjunction isEqual:[flattened objectForKey:SerFilterGroupIsConjunction]]) {
+ [flatChildren addObjectsFromArray:[flattened objectForKey:SerFilterGroupChildren]];
+ changed++;
+ }
+ else if(flattened != child) {
+ changed++;
+ }
+ [flatChildren addObject:flattened];
+ }
+ // if there is only a single child, return it (flattening)
+ if([flatChildren count] == 1) return [flatChildren objectAtIndex:0];
+ // if none of the children changed return the original input
+ if(!changed) return in;
+ // last variant: some of our children changed, but we remain
+ return @{
+ SerFilterClass: SerFilterClassGroup,
+ SerFilterGroupIsConjunction: inIsConjunction,
+ SerFilterGroupChildren: flatChildren
+ };
+}
+
++ (void)_writeFilterTree:(NSDictionary *)in toString:(NSMutableString *)out wrapInParenthesis:(BOOL)wrap binary:(BOOL)isBINARY error:(NSError **)err
+{
+ NSError *myErr = nil;
+
+ if(wrap) [out appendString:@"("];
+
+ if(SerIsGroup(in)) {
+ BOOL isConjunction = [[in objectForKey:SerFilterGroupIsConjunction] boolValue];
+ NSString *connector = isConjunction ? @"AND" : @"OR";
+ BOOL first = YES;
+ NSArray *children = [in objectForKey:SerFilterGroupChildren];
+ for(NSDictionary *child in children) {
+ if(!first) [out appendFormat:@" %@ ",connector];
+ else first = NO;
+ // if the child is a group node but of a different kind we want to wrap it in order to prevent operator precedence confusion
+ // expression children will always be wrapped for clarity, except if there is only a single one and we are already wrapped
+ BOOL wrapChild = YES;
+ if(SerIsGroup(child)) {
+ BOOL childIsConjunction = [[child objectForKey:SerFilterGroupIsConjunction] boolValue];
+ if(isConjunction == childIsConjunction) wrapChild = NO;
+ }
+ else {
+ if(wrap && [children count] == 1) wrapChild = NO;
+ }
+ [self _writeFilterTree:child toString:out wrapInParenthesis:wrapChild binary:isBINARY error:&myErr];
+ if(myErr) {
+ if(err) *err = myErr;
+ return;
+ }
+ }
+ }
+ else {
+ // finally - build a SQL filter expression
+ NSDictionary *filter = [in objectForKey:SerFilterExprDefinition];
+ if(!filter) {
+ if(err) *err = [NSError errorWithDomain:SPErrorDomain code:0 userInfo:@{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Fatal error while retrieving content filter. No filter definition found.", @"filter to sql conversion : internal error : 0"),
+ }];
+ return;
+ }
+
+ if(![filter objectForKey:@"NumberOfArguments"]) {
+ if(err) *err = [NSError errorWithDomain:SPErrorDomain code:1 userInfo:@{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Error while retrieving filter clause. No “NumberOfArguments” key found.", @"filter to sql conversion : internal error : invalid filter definition (1)"),
+ }];
+ return;
+ }
+
+ if(![filter objectForKey:@"Clause"] || ![(NSString *)[filter objectForKey:@"Clause"] length]) {
+ if(err) *err = [NSError errorWithDomain:SPErrorDomain code:2 userInfo:@{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Content Filter clause is empty.", @"filter to sql conversion : internal error : invalid filter definition (2)"),
+ }];
+ return;
+ }
+
+ NSArray *values = [in objectForKey:SerFilterExprValues];
+
+ SPTableFilterParser *parser = [[SPTableFilterParser alloc] initWithFilterClause:[filter objectForKey:@"Clause"]
+ numberOfArguments:[[filter objectForKey:@"NumberOfArguments"] integerValue]];
+ [parser setArgument:[values objectOrNilAtIndex:0]];
+ [parser setFirstBetweenArgument:[values objectOrNilAtIndex:0]];
+ [parser setSecondBetweenArgument:[values objectOrNilAtIndex:1]];
+ [parser setSuppressLeadingTablePlaceholder:[[filter objectForKey:@"SuppressLeadingFieldPlaceholder"] boolValue]];
+ [parser setCaseSensitive:isBINARY];
+ [parser setCurrentField:[in objectForKey:SerFilterExprColumn]];
+
+ NSString *sql = [parser filterString];
+ // SPTableFilterParser will return nil if it doesn't like the arguments and NSMutableString doesn't like nil
+ if(!sql) {
+ if(err) *err = [NSError errorWithDomain:SPErrorDomain code:3 userInfo:@{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"No valid SQL expression could be generated. Make sure that you have filled in all required fields.", @"filter to sql conversion : internal error : SPTableFilterParser failed"),
+ }];
+ [parser release];
+ return;
+ }
+ [out appendString:sql];
+
+ [parser release];
+ }
+
+ if(wrap) [out appendString:@")"];
+}
+
+@end
+
+#pragma mark -
+
+@implementation RuleNode
+
+@synthesize type = type;
+
+- (NSUInteger)hash {
+ return type;
+}
+
+- (BOOL)isEqual:(id)other {
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [(RuleNode *)other type] == type) return YES;
+
+ return NO;
+}
+
+@end
+
+@implementation ColumnNode
+
+@synthesize name = name;
+@synthesize typegrouping = typegrouping;
+@synthesize operatorCache = operatorCache;
+@synthesize opCacheVersion = opCacheVersion;
+
+- (instancetype)init
+{
+ if((self = [super init])) {
+ type = RuleNodeTypeColumn;
+ opCacheVersion = 0;
+ }
+ return self;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"ColumnNode<%@@%p>",[self name],self];
+}
+
+- (NSUInteger)hash {
+ return ([name hash] ^ [typegrouping hash] ^ [super hash]);
+}
+
+- (BOOL)isEqual:(id)other {
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [name isEqualToString:[other name]] && [typegrouping isEqualToString:[other typegrouping]]) return YES;
+
+ return NO;
+}
+
+@end
+
+
+@implementation StringNode
+
+@synthesize value = value;
+
+- (instancetype)init
+{
+ if((self = [super init])) {
+ type = RuleNodeTypeString;
+ }
+ return self;
+}
+
+- (NSUInteger)hash {
+ return ([value hash] ^ [super hash]);
+}
+
+- (BOOL)isEqual:(id)other {
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [value isEqualToString:[(StringNode *)other value]]) return YES;
+
+ return NO;
+}
+
+@end
+
+@implementation OpNode
+
+@synthesize parentColumn = parentColumn;
+@synthesize settings = settings;
+@synthesize filter = filter;
+
+- (instancetype)init
+{
+ if((self = [super init])) {
+ type = RuleNodeTypeOperator;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [self setFilter:nil];
+ [self setSettings:nil];
+ [super dealloc];
+}
+
+- (NSUInteger)hash {
+ return (([parentColumn hash] << 16) ^ [settings hash] ^ [super hash]);
+}
+
+- (BOOL)isEqual:(id)other {
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [settings isEqualToDictionary:[(OpNode *)other settings]] && [parentColumn isEqual:[other parentColumn]]) return YES;
+
+ return NO;
+}
+
+@end
+
+@implementation ArgNode
+
+@synthesize filter = filter;
+@synthesize argIndex = argIndex;
+@synthesize initialValue = initialValue;
+
+- (instancetype)init
+{
+ if((self = [super init])) {
+ type = RuleNodeTypeArgument;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [self setInitialValue:nil];
+ [self setFilter:nil];
+ [super dealloc];
+}
+
+- (NSUInteger)hash {
+ // initialValue does not count towards hash because two Args are not different if only the initialValue differs
+ return ((argIndex << 16) ^ [filter hash] ^ [super hash]);
+}
+
+- (BOOL)isEqual:(id)other {
+ // initialValue does not count towards isEqual: because two Args are not different if only the initialValue differs
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [filter isEqualToDictionary:[(ArgNode *)other filter]] && argIndex == [(ArgNode *)other argIndex]) return YES;
+
+ return NO;
+}
+
+@end
+
+@implementation ConnectorNode
+
+@synthesize filter = filter;
+@synthesize labelIndex = labelIndex;
+
+- (instancetype)init
+{
+ if((self = [super init])) {
+ type = RuleNodeTypeConnector;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [self setFilter:nil];
+ [super dealloc];
+}
+
+- (NSUInteger)hash {
+ return ((labelIndex << 16) ^ [filter hash] ^ [super hash]);
+}
+
+- (BOOL)isEqual:(id)other {
+ if (other == self) return YES;
+ if (other && [[other class] isEqual:[self class]] && [filter isEqualToDictionary:[(ConnectorNode *)other filter]] && labelIndex == [(ConnectorNode *)other labelIndex]) return YES;
+
+ return NO;
+}
+
+@end
+
+#pragma mark -
+
+@implementation ModelContainer
+
+@synthesize model = model;
+
+- (instancetype)init
+{
+ if (self = [super init]) {
+ model = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [self setModel:nil];
+ [super dealloc];
+}
+
+// KVO
+
+- (void)insertObject:(id)obj inModelAtIndex:(NSUInteger)idx
+{
+ [model insertObject:obj atIndex:idx];
+}
+
+- (void)removeObjectFromModelAtIndex:(NSUInteger)idx
+{
+ [model removeObjectAtIndex:idx];
+}
+
+- (void)insertModel:(NSArray *)array atIndexes:(NSIndexSet *)indexes
+{
+ [model insertObjects:array atIndexes:indexes];
+}
+
+- (void)removeModelAtIndexes:(NSIndexSet *)indexes
+{
+ [model removeObjectsAtIndexes:indexes];
+}
+
+@end
diff --git a/Source/SPTableContent.h b/Source/SPTableContent.h
index 4c6627de..039fd099 100644
--- a/Source/SPTableContent.h
+++ b/Source/SPTableContent.h
@@ -43,21 +43,26 @@
@class SPDatabaseDocument;
@class SPTablesList;
@class SPTableStructure;
-@class SPTableList;
-@class SPContentFilterManager;
-#ifndef SP_CODA
-@class SPSplitView;
-#endif
-@class SPTableContentFilterController;
+@class SPRuleFilterController;
+@class SPFilterTableController;
+
+@class ContentPaginationViewController; //private
+
+typedef NS_ENUM(NSInteger, SPTableContentFilterSource) {
+ SPTableContentFilterSourceNone = -1,
+ SPTableContentFilterSourceRuleFilter = 0,
+ SPTableContentFilterSourceTableFilter = 1,
+ SPTableContentFilterSourceURLScheme = 2,
+};
#import "SPDatabaseContentViewDelegate.h"
@interface SPTableContent : NSObject <NSTableViewDelegate, NSTableViewDataSource, NSComboBoxDataSource, NSComboBoxDelegate, SPDatabaseContentViewDelegate>
{
IBOutlet SPDatabaseDocument *tableDocumentInstance;
- IBOutlet id tablesListInstance;
+ IBOutlet SPTablesList *tablesListInstance;
IBOutlet SPTableData* tableDataInstance;
- IBOutlet id tableSourceInstance;
+ IBOutlet SPTableStructure *tableSourceInstance;
#ifndef SP_CODA
IBOutlet SPTableInfo *tableInfoInstance;
@@ -65,95 +70,66 @@
#endif
IBOutlet SPCopyTable *tableContentView;
- IBOutlet NSPopUpButton *fieldField;
- IBOutlet id compareField;
- IBOutlet id argumentField;
- IBOutlet id filterButton;
- IBOutlet id addButton;
- IBOutlet id duplicateButton;
- IBOutlet id removeButton;
- IBOutlet id reloadButton;
+
+ IBOutlet NSButton *toggleRuleFilterButton;
+ IBOutlet NSButton *addButton;
+ IBOutlet NSButton *duplicateButton;
+ IBOutlet NSButton *removeButton;
+ IBOutlet NSButton *reloadButton;
#ifndef SP_CODA
IBOutlet NSButton *multipleLineEditingButton;
- IBOutlet id countText;
- IBOutlet id limitRowsField;
- IBOutlet id limitRowsButton;
- IBOutlet id limitRowsStepper;
+ IBOutlet NSTextField *countText;
#endif
- IBOutlet id firstBetweenField;
- IBOutlet id secondBetweenField;
- IBOutlet id betweenTextField;
IBOutlet NSButton *paginationPreviousButton;
#ifndef SP_CODA
IBOutlet NSButton *paginationButton;
- IBOutlet NSButton *paginationGoButton;
#endif
IBOutlet NSButton *paginationNextButton;
#ifndef SP_CODA
IBOutlet NSView *contentViewPane;
- IBOutlet NSViewController *paginationViewController;
+ ContentPaginationViewController *paginationViewController;
+ NSPopover *paginationPopover;
IBOutlet NSView *paginationView;
IBOutlet NSBox *paginationBox;
- NSPopover *paginationPopover;
-#endif
- IBOutlet NSTextField *paginationPageField;
-#ifndef SP_CODA
- IBOutlet NSStepper *paginationPageStepper;
-
- IBOutlet SPCopyTable *filterTableView;
- IBOutlet NSPanel *filterTableWindow;
- IBOutlet SPSplitView *filterTableSplitView;
- IBOutlet NSTextField *filterTableQueryTitle;
- IBOutlet NSButton *filterTableFilterButton;
- IBOutlet NSButton *filterTableClearButton;
- IBOutlet SPTextView *filterTableWhereClause;
- IBOutlet NSButton *filterTableNegateCheckbox;
- IBOutlet NSButton *filterTableDistinctCheckbox;
- IBOutlet NSButton *filterTableLiveSearchCheckbox;
- IBOutlet NSButton *filterTableSearchAllFields;
- IBOutlet NSPanel *filterTableSetDefaultOperatorSheet;
- IBOutlet NSComboBox* filterTableSetDefaultOperatorValue;
-
- // Temporary to avoid nib conflicts during WIP
- IBOutlet SPSplitView *contentSplitView;
-
- IBOutlet SPTableContentFilterController *filterControllerInstance;
+
+ IBOutlet SPRuleFilterController *ruleFilterController;
+ IBOutlet SPFilterTableController *filterTableController;
+ BOOL scrollViewHasRubberbandScrolling;
#endif
SPMySQLConnection *mySQLConnection;
BOOL _mainNibLoaded;
BOOL isWorking;
pthread_mutex_t tableValuesLock;
-#ifndef SP_CODA
- NSMutableArray *nibObjectsToRelease;
-#endif
- NSString *selectedTable, *usedQuery;
+ NSString *selectedTable;
+ NSString *usedQuery;
SPDataStorage *tableValues;
- NSMutableArray *dataColumns, *keys, *oldRow;
- NSUInteger tableRowsCount, previousTableRowsCount;
- NSString *compareType;
+ NSMutableArray *dataColumns;
+ NSMutableArray *keys;
+ NSMutableArray *oldRow;
+ NSUInteger tableRowsCount;
+ NSUInteger previousTableRowsCount;
NSNumber *sortCol;
- BOOL isEditingRow, isEditingNewRow, isSavingRow, isDesc, setLimit;
- BOOL isFiltered, isLimited, isInterruptedLoad, maxNumRowsIsEstimate;
+ BOOL isEditingRow;
+ BOOL isEditingNewRow;
+ BOOL isSavingRow;
+ BOOL isDesc;
+ BOOL setLimit;
+ BOOL isFiltered;
+ BOOL isLimited;
+ BOOL isInterruptedLoad;
+ BOOL maxNumRowsIsEstimate;
NSUserDefaults *prefs;
- NSInteger currentlyEditingRow, maxNumRows;
+ NSInteger currentlyEditingRow;
+ NSInteger maxNumRows;
- NSMutableDictionary *contentFilters;
- NSMutableDictionary *numberOfDefaultFilters;
- NSUInteger lastSelectedContentFilterIndex;
- SPContentFilterManager *contentFilterManager;
NSUInteger contentPage;
#ifndef SP_CODA
- NSMutableDictionary *filterTableData;
- BOOL filterTableNegate;
- BOOL filterTableDistinct;
- BOOL filterTableIsSwapped;
- NSString *filterTableDefaultOperator;
- NSString *lastEditedFilterTableValue;
- NSInteger activeFilter; // 0 = default filter; 1 = filter table; 2 = sequelpro url scheme
+ SPTableContentFilterSource activeFilter;
+ SPTableContentFilterSource activeFilterToRestore;
NSString *schemeFilter;
#endif
@@ -163,17 +139,18 @@
NSUInteger pageToRestore;
NSDictionary *selectionToRestore;
NSRect selectionViewportToRestore;
- NSString *filterFieldToRestore, *filterComparisonToRestore, *filterValueToRestore, *firstBetweenValueToRestore, *secondBetweenValueToRestore;
#ifndef SP_CODA
NSInteger paginationViewHeight;
#endif
NSTimer *tableLoadTimer;
- NSUInteger tableLoadInterfaceUpdateInterval, tableLoadTimerTicksSinceLastUpdate, tableLoadLastRowCount, tableLoadTargetRowCount;
+ NSUInteger tableLoadInterfaceUpdateInterval;
+ NSUInteger tableLoadTimerTicksSinceLastUpdate;
+ NSUInteger tableLoadLastRowCount;
+ NSUInteger tableLoadTargetRowCount;
NSArray *cqColumnDefinition;
- NSString *fieldIDQueryString;
BOOL isFirstChangeInView;
NSString *kCellEditorErrorNoMatch;
@@ -186,24 +163,25 @@
NSColor *whiteColor;
SPFieldEditorController *fieldEditor;
- NSRange fieldEditorSelectedRange;
+
+ // this represents the visible area of the whole content view at runtime.
+ // we use it as a positioning aide for the other two views below
+ IBOutlet NSView *contentAreaContainer;
+ IBOutlet NSView *filterRuleEditorContainer;
+ IBOutlet NSView *tableContentContainer;
+
+ BOOL showFilterRuleEditor;
+
+ NSDictionary *filtersToRestore;
}
#ifdef SP_CODA /* glue */
-@property (assign) id filterButton;
-@property (assign) id fieldField;
-@property (assign) id compareField;
-@property (assign) id betweenTextField;
-@property (assign) id firstBetweenField;
-@property (assign) id secondBetweenField;
-@property (assign) id argumentField;
@property (assign) NSButton* addButton;
@property (assign) NSButton* duplicateButton;
@property (assign) NSButton* removeButton;
@property (assign) NSButton* reloadButton;
@property (assign) NSButton* paginationNextButton;
@property (assign) NSButton* paginationPreviousButton;
-@property (assign) NSTextField* paginationPageField;
@property (assign) SPDatabaseDocument* tableDocumentInstance;
@property (assign) SPTablesList* tablesListInstance;
@property (assign) SPCopyTable* tableContentView;
@@ -229,9 +207,10 @@
- (IBAction)reloadTable:(id)sender;
- (void)reloadTableTask;
- (IBAction)filterTable:(id)sender;
+- (IBAction)toggleRuleEditorVisible:(id)sender;
- (void)filterTableTask;
-- (IBAction)toggleFilterField:(id)sender;
- (void)setUsedQuery:(NSString *)query;
+- (NSString *)selectedTable;
// Pagination
- (IBAction)navigatePaginationFromButton:(id)sender;
@@ -245,17 +224,10 @@
- (IBAction)addRow:(id)sender;
- (IBAction)duplicateRow:(id)sender;
- (IBAction)removeRow:(id)sender;
-- (void)removeRowSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo;
+- (void)removeRowSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
// Filter Table
-- (IBAction)tableFilterClear:(id)sender;
- (IBAction)showFilterTable:(id)sender;
-- (IBAction)toggleNegateClause:(id)sender;
-- (IBAction)toggleDistinctSelect:(id)sender;
-- (IBAction)setDefaultOperator:(id)sender;
-- (IBAction)toggleLookAllFieldsMode:(id)sender;
-- (IBAction)closeSheet:(id)sender;
-- (IBAction)showDefaultOperaterHelp:(id)sender;
// Data accessors
- (NSArray *)currentResult;
@@ -269,7 +241,6 @@
- (void)setConnection:(SPMySQLConnection *)theConnection;
- (void)clickLinkArrow:(SPTextAndLinkCell *)theArrowCell;
- (void)clickLinkArrowTask:(SPTextAndLinkCell *)theArrowCell;
-- (IBAction)setCompareTypes:(id)sender;
- (void)updateResultStore:(SPMySQLStreamingResultStore *)theResultStore approximateRowCount:(NSUInteger)targetRowCount;
- (BOOL)saveRowToTable;
- (void) addRowErrorSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
@@ -301,11 +272,12 @@
- (void)setFiltersToRestore:(NSDictionary *)filterSettings;
- (void)storeCurrentDetailsForRestoration;
- (void)clearDetailsToRestore;
+- (void)setActiveFilterToRestore:(SPTableContentFilterSource)filter;
+- (SPTableContentFilterSource)activeFilter;
- (void)setFilterTableData:(NSData *)arcData;
- (NSData *)filterTableData;
//- (NSString *)escapeFilterArgument:(NSString *)argument againstClause:(NSString *)clause;
-- (void)openContentFilterManager;
- (NSArray *)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSInteger)columnIndex;
@@ -316,7 +288,5 @@
#pragma mark - SPTableContentFilter
- (void)makeContentFilterHaveFocus;
-- (void)updateFilterTableClause:(id)currentValue;
-- (NSString*)escapeFilterTableDefaultOperator:(NSString*)op;
@end
diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m
index b29ebf7a..9ac8fd61 100644
--- a/Source/SPTableContent.m
+++ b/Source/SPTableContent.m
@@ -40,13 +40,9 @@
#import "SPTableData.h"
#import "SPQueryController.h"
#import "SPTextAndLinkCell.h"
-#ifndef SP_CODA
-#import "SPSplitView.h"
-#endif
#import "SPFieldEditorController.h"
#import "SPTooltip.h"
#import "RegexKitLite.h"
-#import "SPContentFilterManager.h"
#import "SPDataStorage.h"
#import "SPAlertSheets.h"
#import "SPHistoryController.h"
@@ -60,20 +56,60 @@
#import "SPThreadAdditions.h"
#import "SPTableFilterParser.h"
#import "SPFunctions.h"
+#import "SPRuleFilterController.h"
+#import "SPFilterTableController.h"
#import <pthread.h>
#import <SPMySQL/SPMySQL.h>
#include <stdlib.h>
-#ifndef SP_CODA
-static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOperator";
-#endif
+/**
+ * This is the unique KVO context of code that resides in THIS class.
+ * Do not try to give it to other classes, ESPECIALLY NOT child classes!
+ */
+static void *TableContentKVOContext = &TableContentKVOContext;
+
+/**
+ * TODO:
+ * This class is a temporary workaround, because before SPTableContent was both a child class in one xib
+ * and an owner class in another xib, which is bad style and causes other complications.
+ */
+@interface ContentPaginationViewController : NSViewController
+{
+ id target;
+ SEL action;
+
+ NSNumber *page;
+ NSNumber *maxPage;
+
+ IBOutlet NSButton *paginationGoButton;
+ IBOutlet NSTextField *paginationPageField;
+ IBOutlet NSStepper *paginationPageStepper;
+}
+- (IBAction)paginationGoAction:(id)sender;
+- (void)makeInputFirstResponder;
+- (BOOL)isFirstResponderInside;
+
+@property (assign, nonatomic) id target;
+@property (assign, nonatomic) SEL action;
+
+// IB Bindings
+@property (copy, nonatomic) NSNumber *page;
+@property (copy, nonatomic) NSNumber *maxPage;
+
+@end
@interface SPTableContent ()
- (BOOL)cancelRowEditing;
- (void)documentWillClose:(NSNotification *)notification;
-- (void)contentFiltersHaveBeenUpdated:(NSNotification *)notification;
+
+- (void)updateFilterRuleEditorSize:(CGFloat)requestedHeight animate:(BOOL)animate;
+- (void)filterRuleEditorPreferredSizeChanged:(NSNotification *)notification;
+- (void)contentViewSizeChanged:(NSNotification *)notification;
+- (void)setRuleEditorVisible:(BOOL)show animate:(BOOL)animate;
+
+- (void)_setViewBlankState;
#pragma mark - SPTableContentDataSource_Private_API
@@ -85,19 +121,11 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
#ifdef SP_CODA
@synthesize addButton;
-@synthesize argumentField;
-@synthesize betweenTextField;
-@synthesize compareField;
@synthesize duplicateButton;
-@synthesize fieldField;
-@synthesize filterButton;
-@synthesize firstBetweenField;
@synthesize paginationNextButton;
-@synthesize paginationPageField;
@synthesize paginationPreviousButton;
@synthesize reloadButton;
@synthesize removeButton;
-@synthesize secondBetweenField;
@synthesize tableContentView;
@synthesize tableDataInstance;
@synthesize tableDocumentInstance;
@@ -114,27 +142,18 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
isWorking = NO;
pthread_mutex_init(&tableValuesLock, NULL);
-#ifndef SP_CODA
- nibObjectsToRelease = [[NSMutableArray alloc] init];
-#endif
tableValues = [[SPDataStorage alloc] init];
dataColumns = [[NSMutableArray alloc] init];
oldRow = [[NSMutableArray alloc] init];
-#ifndef SP_CODA
- filterTableData = [[NSMutableDictionary alloc] initWithCapacity:1];
-#endif
tableRowsCount = 0;
previousTableRowsCount = 0;
#ifndef SP_CODA
- filterTableNegate = NO;
- filterTableDistinct = NO;
- filterTableIsSwapped = NO;
- lastEditedFilterTableValue = nil;
- activeFilter = 0;
+ activeFilter = SPTableContentFilterSourceNone;
schemeFilter = nil;
+ paginationViewController = [[ContentPaginationViewController alloc] init]; // the view itself is lazily loaded
paginationPopover = nil;
#endif
@@ -151,15 +170,13 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
pageToRestore = 1;
selectionToRestore = nil;
selectionViewportToRestore = NSZeroRect;
- filterFieldToRestore = nil;
- filterComparisonToRestore = nil;
- filterValueToRestore = nil;
- firstBetweenValueToRestore = nil;
- secondBetweenValueToRestore = nil;
+ filtersToRestore = nil;
+ activeFilterToRestore = SPTableContentFilterSourceNone;
tableRowsSelectable = YES;
- contentFilterManager = nil;
isFirstChangeInView = YES;
+ showFilterRuleEditor = NO;
+
isFiltered = NO;
isLimited = NO;
isInterruptedLoad = NO;
@@ -175,38 +192,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
blueColor = [NSColor blueColor];
whiteColor = [NSColor whiteColor];
- // Init default filters for Content Browser
- contentFilters = [[NSMutableDictionary alloc] init];
- numberOfDefaultFilters = [[NSMutableDictionary alloc] init];
-
- NSError *readError = nil;
- NSString *filePath = [NSBundle pathForResource:@"ContentFilters.plist" ofType:nil inDirectory:[[NSBundle mainBundle] bundlePath]];
- NSData *defaultFilterData = [NSData dataWithContentsOfFile:filePath
- options:NSMappedRead
- error:&readError];
-
- if(defaultFilterData && !readError) {
- NSDictionary *defaultFilterDict = [NSPropertyListSerialization propertyListWithData:defaultFilterData
- options:NSPropertyListMutableContainersAndLeaves
- format:NULL
- error:&readError];
-
- if(defaultFilterDict && !readError) {
- [contentFilters setDictionary:defaultFilterDict];
- }
- }
-
- if (readError) {
- NSLog(@"Error while reading 'ContentFilters.plist':\n%@", readError);
- NSBeep();
- }
- else {
- [numberOfDefaultFilters setObject:[NSNumber numberWithInteger:[[contentFilters objectForKey:@"number"] count]] forKey:@"number"];
- [numberOfDefaultFilters setObject:[NSNumber numberWithInteger:[[contentFilters objectForKey:@"date"] count]] forKey:@"date"];
- [numberOfDefaultFilters setObject:[NSNumber numberWithInteger:[[contentFilters objectForKey:@"string"] count]] forKey:@"string"];
- [numberOfDefaultFilters setObject:[NSNumber numberWithInteger:[[contentFilters objectForKey:@"spatial"] count]] forKey:@"spatial"];
- }
-
kCellEditorErrorNoMatch = NSLocalizedString(@"Field is not editable. No matching record found.\nReload table, check the encoding, or try to add\na primary key field or more fields\nin the view declaration of '%@' to identify\nfield origin unambiguously.", @"Table Content result editing error - could not identify original row");
kCellEditorErrorNoMultiTabDb = NSLocalizedString(@"Field is not editable. Field has no or multiple table or database origin(s).",@"field is not editable due to no table/database");
kCellEditorErrorTooManyMatches = NSLocalizedString(@"Field is not editable. Couldn't identify field origin unambiguously (%ld matches).", @"Query result editing error - could not match row being edited uniquely");
@@ -221,30 +206,19 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
_mainNibLoaded = YES;
#ifndef SP_CODA /* ui manipulation */
- // Temporary to avoid nib conflicts during WIP
- [contentSplitView setCollapsibleSubviewIndex:0];
- [contentSplitView setCollapsibleSubviewCollapsed:YES animate:NO];
- [contentSplitView setMaxSize:0.f ofSubviewAtIndex:0];
+
+ // initially hide the filter rule editor
+ [self updateFilterRuleEditorSize:0.0 animate:NO];
// Set the table content view's vertical gridlines if required
[tableContentView setGridStyleMask:([prefs boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
- [filterTableView setGridStyleMask:([prefs boolForKey:SPDisplayTableViewVerticalGridlines]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
// Set the double-click action in blank areas of the table to create new rows
[tableContentView setEmptyDoubleClickAction:@selector(addRow:)];
- // Load the pagination view, keeping references to the top-level objects for later release
- NSArray *paginationViewTopLevelObjects = nil;
- NSNib *nibLoader = [[NSNib alloc] initWithNibNamed:@"ContentPaginationView" bundle:[NSBundle mainBundle]];
-
- if (![nibLoader instantiateNibWithOwner:self topLevelObjects:&paginationViewTopLevelObjects]) {
- NSLog(@"Content pagination nib could not be loaded; pagination will not function correctly.");
- }
- else {
- [nibObjectsToRelease addObjectsFromArray:paginationViewTopLevelObjects];
- }
-
- [nibLoader release];
+ [paginationViewController setTarget:self];
+ [paginationViewController setAction:@selector(navigatePaginationFromButton:)];
+ [paginationViewController view]; // make sure the nib is actually loaded
//let's see if we can use the NSPopover (10.7+) or have to make do with our legacy clone.
//this is using reflection right now, as our SDK is 10.8 but our minimum supported version is 10.6
@@ -266,44 +240,47 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
paginationViewFrame.origin.y = paginationButtonFrame.origin.y + paginationButtonFrame.size.height - 2;
paginationViewFrame.size.height = 0;
[paginationView setFrame:paginationViewFrame];
- [contentViewPane addSubview:paginationView];
+ [[paginationButton superview] addSubview:paginationView];
}
-
- // Modify the filter table split view sizes
- [filterTableSplitView setMinSize:135 ofSubviewAtIndex:1];
#endif
[tableContentView setFieldEditorSelectedRange:NSMakeRange(0,0)];
-#ifndef SP_CODA
- // Init Filter Table GUI
- [filterTableDistinctCheckbox setState:(filterTableDistinct) ? NSOnState : NSOffState];
- [filterTableNegateCheckbox setState:(filterTableNegate) ? NSOnState : NSOffState];
- [filterTableLiveSearchCheckbox setState:NSOffState];
-#endif
-#ifndef SP_CODA /* patch */
- filterTableDefaultOperator = [[self escapeFilterTableDefaultOperator:[prefs objectForKey:SPFilterTableDefaultOperator]] retain];
-#else
-// filterTableDefaultOperator = [[self escapeFilterTableDefaultOperator:nil] retain];
-#endif
+ [prefs addObserver:self forKeyPath:SPDisplayTableViewVerticalGridlines options:NSKeyValueObservingOptionNew context:TableContentKVOContext];
+ [prefs addObserver:self forKeyPath:SPGlobalResultTableFont options:NSKeyValueObservingOptionNew context:TableContentKVOContext];
+ [prefs addObserver:self forKeyPath:SPDisplayBinaryDataAsHex options:NSKeyValueObservingOptionNew context:TableContentKVOContext];
+
+ // Add observer to change view sizes with filter rule editor
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(filterRuleEditorPreferredSizeChanged:)
+ name:SPRuleFilterHeightChangedNotification
+ object:ruleFilterController];
+ [contentAreaContainer setPostsFrameChangedNotifications:YES];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(contentViewSizeChanged:)
+ name:NSViewFrameDidChangeNotification
+ object:contentAreaContainer];
+ [ruleFilterController setTarget:self];
+ [ruleFilterController setAction:@selector(filterTable:)];
+
+ [filterTableController setTarget:self];
+ [filterTableController setAction:@selector(filterTable:)];
+ //TODO This is only needed for 10.6 compatibility
+ scrollViewHasRubberbandScrolling = [[[ruleFilterController view] enclosingScrollView] respondsToSelector:@selector(setVerticalScrollElasticity:)];
// Add observers for document task activity
[[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(startDocumentTaskForTab:)
- name:SPDocumentTaskStartNotification
- object:tableDocumentInstance];
+ selector:@selector(startDocumentTaskForTab:)
+ name:SPDocumentTaskStartNotification
+ object:tableDocumentInstance];
[[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(endDocumentTaskForTab:)
- name:SPDocumentTaskEndNotification
- object:tableDocumentInstance];
+ selector:@selector(endDocumentTaskForTab:)
+ name:SPDocumentTaskEndNotification
+ object:tableDocumentInstance];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(documentWillClose:)
name:SPDocumentWillCloseNotification
object:tableDocumentInstance];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(contentFiltersHaveBeenUpdated:)
- name:SPContentFiltersHaveBeenUpdatedNotification
- object:nil];
}
#pragma mark -
@@ -374,6 +351,60 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
}
/**
+ * This configures the table content view in the way it should look like when no valid table is selected
+ */
+- (void)_setViewBlankState
+{
+ // Remove existing columns from the table
+ while ([[tableContentView tableColumns] count]) {
+ [NSArrayObjectAtIndex([tableContentView tableColumns], 0) setHeaderToolTip:nil]; // prevent crash #2414
+ [tableContentView removeTableColumn:NSArrayObjectAtIndex([tableContentView tableColumns], 0)];
+ }
+
+ // Empty the stored data arrays, including emptying the tableValues array
+ // by ressignment for thread safety.
+ previousTableRowsCount = 0;
+ [self clearTableValues];
+ [tableContentView reloadData];
+ isFiltered = NO;
+ isLimited = NO;
+#ifndef SP_CODA
+ [countText setStringValue:@""];
+#endif
+
+ // Reset sort column
+ if (sortCol) SPClear(sortCol);
+ isDesc = NO;
+
+ // Empty and disable filter options
+ [self setRuleEditorVisible:NO animate:NO];
+ [toggleRuleFilterButton setEnabled:NO];
+ [toggleRuleFilterButton setState:NSOffState];
+ [ruleFilterController setColumns:nil];
+
+ // Disable pagination
+ [paginationPreviousButton setEnabled:NO];
+#ifndef SP_CODA
+ [paginationButton setEnabled:NO];
+ [paginationButton setTitle:@""];
+#endif
+ [paginationNextButton setEnabled:NO];
+
+ // Disable table action buttons
+ [addButton setEnabled:NO];
+ [duplicateButton setEnabled:NO];
+ [removeButton setEnabled:NO];
+
+ // Clear restoration settings
+ [self clearDetailsToRestore];
+
+#ifndef SP_CODA
+ [filterTableController setColumns:nil];
+ activeFilter = SPTableContentFilterSourceNone;
+#endif
+}
+
+/**
* Update stored table details and update the interface to match the supplied
* table details.
* Should be called on the main thread.
@@ -387,10 +418,8 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
#endif
NSArray *columnNames;
NSMutableDictionary *preservedColumnWidths = nil;
- NSTableColumn *theCol;
-#ifndef SP_CODA
- NSTableColumn *filterCol;
-#endif
+ NSTableColumn *theCol;
+
BOOL enableInteraction =
#ifndef SP_CODA /* checking toolbar state */
![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:SPMainToolbarTableContent] ||
@@ -432,7 +461,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
if (newTableName) selectedTable = [[NSString alloc] initWithString:newTableName];
previousTableRowsCount = 0;
contentPage = 1;
- [paginationPageField setStringValue:@"1"];
+ [paginationViewController setPage:@1];
// Clear the selection
[tableContentView deselectAll:self];
@@ -453,75 +482,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
// If no table has been supplied, reset the view to a blank table and disabled elements.
if (!newTableName) {
- // Remove existing columns from the table
- while ([[tableContentView tableColumns] count]) {
- [NSArrayObjectAtIndex([tableContentView tableColumns], 0) setHeaderToolTip:nil]; // prevent crash #2414
- [tableContentView removeTableColumn:NSArrayObjectAtIndex([tableContentView tableColumns], 0)];
- }
-
- // Empty the stored data arrays, including emptying the tableValues array
- // by ressignment for thread safety.
- previousTableRowsCount = 0;
- [self clearTableValues];
- [tableContentView reloadData];
- isFiltered = NO;
- isLimited = NO;
-#ifndef SP_CODA
- [countText setStringValue:@""];
-#endif
-
- // Reset sort column
- if (sortCol) SPClear(sortCol);
- isDesc = NO;
-
- // Empty and disable filter options
- [fieldField setEnabled:NO];
- [fieldField removeAllItems];
- [fieldField addItemWithTitle:NSLocalizedString(@"field", @"popup menuitem for field (showing only if disabled)")];
- [compareField setEnabled:NO];
- [compareField removeAllItems];
- [compareField addItemWithTitle:@"="];
- [argumentField setHidden:NO];
- [argumentField setEnabled:NO];
- [firstBetweenField setEnabled:NO];
- [secondBetweenField setEnabled:NO];
- [firstBetweenField setStringValue:@""];
- [secondBetweenField setStringValue:@""];
- [argumentField setStringValue:@""];
- [filterButton setEnabled:NO];
-
- // Hide BETWEEN operator controls
- [firstBetweenField setHidden:YES];
- [secondBetweenField setHidden:YES];
- [betweenTextField setHidden:YES];
-
- // Disable pagination
- [paginationPreviousButton setEnabled:NO];
-#ifndef SP_CODA
- [paginationButton setEnabled:NO];
- [paginationButton setTitle:@""];
-#endif
- [paginationNextButton setEnabled:NO];
-
- // Disable table action buttons
- [addButton setEnabled:NO];
- [duplicateButton setEnabled:NO];
- [removeButton setEnabled:NO];
-
- // Clear restoration settings
- [self clearDetailsToRestore];
-
-#ifndef SP_CODA
- // Clear filter table
- while ([[filterTableView tableColumns] count]) {
- [NSArrayObjectAtIndex([filterTableView tableColumns], 0) setHeaderToolTip:nil]; // prevent crash #2414
- [filterTableView removeTableColumn:NSArrayObjectAtIndex([filterTableView tableColumns], 0)];
- }
- // Clear filter table data
- [filterTableData removeAllObjects];
- [filterTableWhereClause setString:@""];
- activeFilter = 0;
-#endif
+ [self _setViewBlankState];
return;
}
@@ -534,15 +495,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
}
#ifndef SP_CODA
// Remove existing columns from the filter table
- [filterTableView abortEditing];
- while ([[filterTableView tableColumns] count]) {
- [NSArrayObjectAtIndex([filterTableView tableColumns], 0) setHeaderToolTip:nil]; // prevent crash #2414
- [filterTableView removeTableColumn:NSArrayObjectAtIndex([filterTableView tableColumns], 0)];
- }
- // Clear filter table data
- [filterTableData removeAllObjects];
- [filterTableWhereClause setString:@""];
- activeFilter = 0;
+ [filterTableController setColumns:nil];
#endif
// Retrieve the field names and types for this table from the data cache. This is used when requesting all data as part
@@ -578,7 +531,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
#endif
[tableContentView setRowHeight:2.0f+NSSizeToCGSize([@"{ǞṶḹÜ∑zgyf" sizeWithAttributes:@{NSFontAttributeName : tableFont}]).height];
- // Add the new columns to the table and filterTable
+ // Add the new columns to the table
for (NSDictionary *columnDefinition in dataColumns ) {
// Set up the column
@@ -599,24 +552,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
[theCol setEditable:YES];
-#ifndef SP_CODA
- // Set up column for filterTable
- filterCol = [[NSTableColumn alloc] initWithIdentifier:[columnDefinition objectForKey:@"datacolumnindex"]];
- [[filterCol headerCell] setStringValue:[columnDefinition objectForKey:@"name"]];
- [filterCol setEditable:YES];
- SPTextAndLinkCell *filterDataCell = [[[SPTextAndLinkCell alloc] initTextCell:@""] autorelease];
- [filterDataCell setEditable:YES];
- [filterCol setDataCell:filterDataCell];
- [filterTableView addTableColumn:filterCol];
- [filterCol release];
-
- [filterTableData setObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
- [columnDefinition objectForKey:@"name"], @"name",
- [columnDefinition objectForKey:@"typegrouping"], @"typegrouping",
- [NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil], SPTableContentFilterKey,
- nil] forKey:[columnDefinition objectForKey:@"datacolumnindex"]];
-#endif
-
// Set up the data cell depending on the column type
id dataCell;
if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"enum"]) {
@@ -686,12 +621,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
[theCol release];
}
-#ifndef SP_CODA
- [filterTableView setDelegate:self];
- [filterTableView setDataSource:self];
- [filterTableView reloadData];
-#endif
-
// If the table has been reloaded and the previously selected sort column is still present, reselect it.
if (sortColumnNumberToRestore != NSNotFound) {
theCol = [tableContentView tableColumnWithIdentifier:[NSString stringWithFormat:@"%lld", (long long)sortColumnNumberToRestore]];
@@ -721,42 +650,27 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
currentFirstResponder = [currentFirstResponder delegate];
}
+ [filterTableController setColumns:dataColumns];
// Enable and initialize filter fields (with tags for position of menu item and field position)
- [fieldField setEnabled:YES];
- [fieldField removeAllItems];
- NSArray *columnTitles = ([prefs boolForKey:SPAlphabeticalTableSorting])? [columnNames sortedArrayUsingSelector:@selector(compare:)] : columnNames;
- [fieldField addItemsWithTitles:columnTitles];
- [compareField setEnabled:YES];
- [self setCompareTypes:self];
- [argumentField setEnabled:YES];
- [argumentField setStringValue:@""];
- [filterButton setEnabled:enableInteraction];
-
+ [ruleFilterController setColumns:dataColumns];
// Restore preserved filter settings if appropriate and valid
- if (filterFieldToRestore) {
- [fieldField selectItemWithTitle:filterFieldToRestore];
- [self setCompareTypes:self];
-
- if ([fieldField itemWithTitle:filterFieldToRestore]
- && ((!filterComparisonToRestore && filterValueToRestore)
- || (filterComparisonToRestore && [compareField itemWithTitle:filterComparisonToRestore])))
- {
- if (filterComparisonToRestore) [compareField selectItemWithTitle:filterComparisonToRestore];
- if([filterComparisonToRestore isEqualToString:@"BETWEEN"]) {
- [argumentField setHidden:YES];
- if (firstBetweenValueToRestore) [firstBetweenField setStringValue:firstBetweenValueToRestore];
- if (secondBetweenValueToRestore) [secondBetweenField setStringValue:secondBetweenValueToRestore];
- } else {
- if (filterValueToRestore) [argumentField setStringValue:filterValueToRestore];
- }
- [self toggleFilterField:self];
-
- }
+ [ruleFilterController restoreSerializedFilters:filtersToRestore];
+ // hide/show the rule filter editor, based on its previous state (so that it says visible when switching tables, if someone has enabled it and vice versa)
+ if(showFilterRuleEditor) {
+ [self setRuleEditorVisible:YES animate:NO];
+ [toggleRuleFilterButton setState:NSOnState];
+ }
+ else {
+ [self setRuleEditorVisible:NO animate:NO];
+ [toggleRuleFilterButton setState:NSOffState];
}
+ [ruleFilterController setEnabled:enableInteraction];
+ [toggleRuleFilterButton setEnabled:enableInteraction];
+ // restore the filter to the previously choosen one for the table
+ activeFilter = activeFilterToRestore;
// Restore page number if limiting is set
- if ([prefs boolForKey:SPLimitResults])
- contentPage = pageToRestore;
+ if ([prefs boolForKey:SPLimitResults]) contentPage = pageToRestore;
// Restore first responder
[[tableDocumentInstance parentWindow] makeFirstResponder:currentFirstResponder];
@@ -771,10 +685,11 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
if (!previousTableRowsCount) {
[self clearTableValues];
}
-#ifndef SP_CODA
- [filterTableView reloadData];
-#endif
+}
+- (NSString *)selectedTable
+{
+ return selectedTable;
}
/**
@@ -819,18 +734,18 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
// Notify any listeners that a query has started
[[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance];
+ // Add a filter string if appropriate
+ filterString = [[self onMainThread] tableFilterString];
+
// Start construction of the query string
queryString = [NSMutableString stringWithFormat:@"SELECT %@%@ FROM %@",
#ifndef SP_CODA
- (activeFilter == 1 && [self tableFilterString] && filterTableDistinct) ? @"DISTINCT " :
+ (activeFilter == SPTableContentFilterSourceTableFilter && filterString && [filterTableController isDistinct]) ? @"DISTINCT " :
#endif
@"",
[self fieldListForQuery], [selectedTable backtickQuotedString]];
- // Add a filter string if appropriate
- filterString = [[self onMainThread] tableFilterString];
-
- if (filterString) {
+ if ([filterString length]) {
[queryString appendFormat:@" WHERE %@", filterString];
isFiltered = YES;
} else {
@@ -1004,10 +919,10 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
if ([mySQLConnection queryErrored] && ![mySQLConnection lastQueryWasCancelled]) {
#ifndef SP_CODA
- if(activeFilter == 0) {
+ if(activeFilter == SPTableContentFilterSourceRuleFilter || activeFilter == SPTableContentFilterSourceNone) {
#endif
NSString *errorDetail;
- if(filterString)
+ if([filterString length])
errorDetail = [NSString stringWithFormat:NSLocalizedString(@"The table data couldn't be loaded presumably due to used filter clause. \n\nMySQL said: %@", @"message of panel when loading of table failed and presumably due to used filter argument"), [mySQLConnection lastErrorMessage]];
else
errorDetail = [NSString stringWithFormat:NSLocalizedString(@"The table data couldn't be loaded.\n\nMySQL said: %@", @"message of panel when loading of table failed"), [mySQLConnection lastErrorMessage]];
@@ -1016,8 +931,10 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
}
#ifndef SP_CODA
// Filter task came from filter table
- else if(activeFilter == 1){
- [filterTableWindow setTitle:[NSString stringWithFormat:@"%@ – %@", NSLocalizedString(@"Filter", @"filter label"), NSLocalizedString(@"WHERE clause not valid", @"WHERE clause not valid")]];
+ else if(activeFilter == SPTableContentFilterSourceTableFilter) {
+ [[filterTableController onMainThread] setFilterError:[mySQLConnection lastErrorID]
+ message:[mySQLConnection lastErrorMessage]
+ sqlstate:[mySQLConnection lastSqlstate]];
}
}
#endif
@@ -1026,7 +943,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
#ifndef SP_CODA
// Trigger a full reload if required
if (fullTableReloadRequired) [self reloadTable:self];
- [[filterTableWindow onMainThread] setTitle:NSLocalizedString(@"Filter", @"filter label")];
+ [[filterTableController onMainThread] setFilterError:0 message:nil sqlstate:nil];
#endif
}
}
@@ -1051,6 +968,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
[theResultStore startDownload];
#ifndef SP_CODA
+#warning private ivar accessed from outside
NSProgressIndicator *dataLoadingIndicator = [tableDocumentInstance valueForKey:@"queryProgressBar"];
#else
NSProgressIndicator *dataLoadingIndicator = [tableDocumentInstance queryProgressBar];
@@ -1098,70 +1016,35 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
#ifndef SP_CODA
// If filter command was passed by sequelpro url scheme
- if(activeFilter == 2) {
- if(schemeFilter)
- return schemeFilter;
+ if(activeFilter == SPTableContentFilterSourceURLScheme) {
+ if(schemeFilter) return schemeFilter;
}
// Call did come from filter table and is filter table window still open?
- if(activeFilter == 1 && [filterTableWindow isVisible]) {
-
- if([[[filterTableWhereClause textStorage] string] length])
- if([filterTableNegateCheckbox state] == NSOnState)
- return [NSString stringWithFormat:@"NOT (%@)", [[filterTableWhereClause textStorage] string]];
- else
- return [[filterTableWhereClause textStorage] string];
- else
- return nil;
-
+ if(activeFilter == SPTableContentFilterSourceTableFilter && [[filterTableController window] isVisible]) {
+ return [filterTableController tableFilterString];
}
#endif
-
- // If the clause has the placeholder $BINARY that placeholder will be replaced
- // by BINARY if the user pressed ⇧ while invoking 'Filter' otherwise it will
- // replaced by @"".
- BOOL caseSensitive = (([[[NSApp onMainThread] currentEvent] modifierFlags] & NSShiftKeyMask) > 0);
-
- if(contentFilters == nil) {
- NSLog(@"Fatal error while retrieving content filters. No filters found.");
- NSBeep();
- return nil;
- }
-
- // Current selected filter type
- if(![contentFilters objectForKey:compareType]) {
- NSLog(@"Error while retrieving filters. Filter type “%@” unknown.", compareType);
- NSBeep();
- return nil;
- }
- NSDictionary *filter = [[contentFilters objectForKey:compareType] objectAtIndex:[[compareField selectedItem] tag]];
-
- if(![filter objectForKey:@"NumberOfArguments"]) {
- NSLog(@"Error while retrieving filter clause. No “NumberOfArguments” key found.");
- NSBeep();
- return nil;
+ if(activeFilter == SPTableContentFilterSourceRuleFilter && showFilterRuleEditor) {
+ // If the clause has the placeholder $BINARY that placeholder will be replaced
+ // by BINARY if the user pressed ⇧ while invoking 'Filter' otherwise it will
+ // replaced by @"".
+ BOOL caseSensitive = (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) > 0);
+
+ NSError *err = nil;
+ NSString *filter = [ruleFilterController sqlWhereExpressionWithBinary:caseSensitive error:&err];
+ if(err) {
+ SPOnewayAlertSheet(
+ NSLocalizedString(@"Invalid Filter", @"table content : apply filter : invalid filter message title"),
+ [tableDocumentInstance parentWindow],
+ [err localizedDescription]
+ );
+ return nil;
+ }
+ return filter;
}
- if(![filter objectForKey:@"Clause"] || ![(NSString *)[filter objectForKey:@"Clause"] length]) {
-
- SPOnewayAlertSheet(
- NSLocalizedString(@"Warning", @"warning"),
- [tableDocumentInstance parentWindow],
- NSLocalizedString(@"Content Filter clause is empty.", @"content filter clause is empty tooltip.")
- );
-
- return nil;
- }
-
- SPTableFilterParser *parser = [[[SPTableFilterParser alloc] initWithFilterClause:[filter objectForKey:@"Clause"] numberOfArguments:[[filter objectForKey:@"NumberOfArguments"] integerValue]] autorelease];
- [parser setArgument:[argumentField stringValue]];
- [parser setFirstBetweenArgument:[firstBetweenField stringValue]];
- [parser setSecondBetweenArgument:[secondBetweenField stringValue]];
- [parser setSuppressLeadingTablePlaceholder:[[filter objectForKey:@"SuppressLeadingFieldPlaceholder"] boolValue]];
- [parser setCaseSensitive:caseSensitive];
- [parser setCurrentField:[fieldField titleOfSelectedItem]];
-
- return [parser filterString];
+ return nil;
}
/**
@@ -1364,7 +1247,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
{
BOOL senderIsPaginationButton = (sender == paginationPreviousButton || sender == paginationNextButton
#ifndef SP_CODA
- || sender == paginationGoButton
+ || sender == paginationViewController
#endif
);
@@ -1375,22 +1258,25 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
#ifndef SP_CODA
+ BOOL resetPaging = NO; // if filtering was triggered by pressing the "Filter" button, reset to page 1
+
// If the filter table is being used - the advanced filter - switch type
- if(sender == filterTableFilterButton) {
- activeFilter = 1;
+ if(sender == filterTableController) {
+ activeFilter = SPTableContentFilterSourceTableFilter;
+ resetPaging = YES;
}
-
// If a string was supplied, use a custom query from that URL scheme
else if([sender isKindOfClass:[NSString class]] && [(NSString *)sender length]) {
if(schemeFilter) SPClear(schemeFilter);
schemeFilter = [sender retain];
- activeFilter = 2;
+ activeFilter = SPTableContentFilterSourceURLScheme;
+ resetPaging = YES;
}
-
// If a button other than the pagination buttons was used, set the active filter type to
// the standard filter field.
- else if (!senderIsPaginationButton) {
- activeFilter = 0;
+ else if (sender == ruleFilterController) {
+ activeFilter = SPTableContentFilterSourceRuleFilter;
+ resetPaging = YES;
}
#endif
@@ -1416,16 +1302,18 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
// Select the correct pagination value.
// If the filter button was used, or if pagination is disabled, reset to page one
- if (!senderIsPaginationButton && ([sender isKindOfClass:[NSButton class]] || [sender isKindOfClass:[NSTextField class]] || ![prefs boolForKey:SPLimitResults] || [paginationPageField integerValue] <= 0))
+ NSInteger paginationViewPage = [[paginationViewController page] integerValue];
+ if (resetPaging || ![prefs boolForKey:SPLimitResults] || paginationViewPage <= 0) {
contentPage = 1;
-
+ }
// If the current page is out of bounds, move it within bounds
- else if (([paginationPageField integerValue] - 1) * [prefs integerForKey:SPLimitResultsValue] >= maxNumRows)
- contentPage = ceilf((CGFloat)maxNumRows / [prefs floatForKey:SPLimitResultsValue]);
-
+ else if ((paginationViewPage - 1) * [prefs integerForKey:SPLimitResultsValue] >= maxNumRows) {
+ contentPage = ceilf((CGFloat) maxNumRows / [prefs floatForKey:SPLimitResultsValue]);
+ }
// Otherwise, use the pagination value
- else
- contentPage = [paginationPageField integerValue];
+ else {
+ contentPage = paginationViewPage;
+ }
if ([self tableFilterString]) {
taskString = NSLocalizedString(@"Filtering table...", @"Filtering table task description");
@@ -1438,11 +1326,15 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
[tableDocumentInstance startTaskWithDescription:taskString];
if ([NSThread isMainThread]) {
- [NSThread detachNewThreadWithName:SPCtxt(@"SPTableContent filter table task", tableDocumentInstance) target:self selector:@selector(filterTableTask) object:nil];
+ [NSThread detachNewThreadWithName:SPCtxt(@"SPTableContent filter table task", tableDocumentInstance)
+ target:self
+ selector:@selector(filterTableTask)
+ object:nil];
} else {
[self filterTableTask];
}
}
+
- (void)filterTableTask
{
@autoreleasepool {
@@ -1452,7 +1344,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
#endif
// Reset and reload data using the new filter settings
- [self setSelectionToRestore:[self selectionDetailsAllowingIndexSelection:NO]];
+ [self setSelectionToRestore:[[self onMainThread] selectionDetailsAllowingIndexSelection:NO]];
previousTableRowsCount = 0;
[self clearTableValues];
[self loadTableValues];
@@ -1462,61 +1354,32 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
}
}
-/**
- * Enables or disables the filter input field based on the selected filter type.
- */
-- (IBAction)toggleFilterField:(id)sender
+- (IBAction)toggleRuleEditorVisible:(id)sender
{
-
- // Check if user called "Edit Filter…"
- if([[compareField selectedItem] tag] == (NSInteger)[[contentFilters objectForKey:compareType] count]) {
- [self openContentFilterManager];
- return;
+ BOOL shouldShow = !showFilterRuleEditor;
+ [self setRuleEditorVisible:shouldShow animate:YES];
+ // if this was the active filter before, it no longer can be the active filter when it is hidden
+ if(activeFilter == SPTableContentFilterSourceRuleFilter && !shouldShow) {
+ activeFilter = SPTableContentFilterSourceNone;
}
+}
- // Remember last selection for "Edit filter…"
- lastSelectedContentFilterIndex = [[compareField selectedItem] tag];
-
- NSDictionary *filter = [[contentFilters objectForKey:compareType] objectAtIndex:lastSelectedContentFilterIndex];
- NSUInteger numOfArgs = [[filter objectForKey:@"NumberOfArguments"] integerValue];
- if (numOfArgs == 2) {
- [argumentField setHidden:YES];
-
- if([filter objectForKey:@"ConjunctionLabels"] && [[filter objectForKey:@"ConjunctionLabels"] count] == 1)
- [betweenTextField setStringValue:[[filter objectForKey:@"ConjunctionLabels"] objectAtIndex:0]];
- else
- [betweenTextField setStringValue:@""];
-
- [betweenTextField setHidden:NO];
- [firstBetweenField setHidden:NO];
- [secondBetweenField setHidden:NO];
-
- [firstBetweenField setEnabled:YES];
- [secondBetweenField setEnabled:YES];
- [firstBetweenField selectText:self];
- }
- else if (numOfArgs == 1){
- [argumentField setHidden:NO];
- [argumentField setEnabled:YES];
- [argumentField selectText:self];
-
- [betweenTextField setHidden:YES];
- [firstBetweenField setHidden:YES];
- [secondBetweenField setHidden:YES];
+- (void)setRuleEditorVisible:(BOOL)show animate:(BOOL)animate
+{
+ // we can't change the state of the button here, because the mouse click already changed it
+ if(show) {
+ if([ruleFilterController isEmpty]) {
+ [ruleFilterController addFilterExpression];
+ // the sizing will be updated automatically by adding a row
+ }
+ else {
+ [self updateFilterRuleEditorSize:[ruleFilterController preferredHeight] animate:animate];
+ }
}
else {
- [argumentField setHidden:NO];
- [argumentField setEnabled:NO];
-
- [betweenTextField setHidden:YES];
- [firstBetweenField setHidden:YES];
- [secondBetweenField setHidden:YES];
-
- // Start search if no argument is required
- if(numOfArgs == 0)
- [self filterTable:self];
+ [self updateFilterRuleEditorSize:0.0 animate:animate];
}
-
+ showFilterRuleEditor = show;
}
- (void)setUsedQuery:(NSString *)query
@@ -1622,10 +1485,10 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
if (sender == paginationPreviousButton) {
if (contentPage <= 1) return;
- [paginationPageField setIntegerValue:(contentPage - 1)];
+ [paginationViewController setPage:@(contentPage - 1)];
} else if (sender == paginationNextButton) {
if ((NSInteger)contentPage * [prefs integerForKey:SPLimitResultsValue] >= maxNumRows) return;
- [paginationPageField setIntegerValue:(contentPage + 1)];
+ [paginationViewController setPage:@(contentPage + 1)];
}
[self filterTable:sender];
@@ -1659,18 +1522,17 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
if(makeVisible) {
[paginationButton setState:NSOnState];
[paginationButton setImage:[NSImage imageNamed:@"button_action"]];
- [[paginationPageField window] makeFirstResponder:paginationPageField];
+ [paginationViewController makeInputFirstResponder];
}
else {
[paginationButton setState:NSOffState];
[paginationButton setImage:[NSImage imageNamed:@"button_pagination"]];
- if ([[paginationPageField window] firstResponder] == paginationPageField
- || ([[[paginationPageField window] firstResponder] respondsToSelector:@selector(superview)]
- && [(id)[[paginationPageField window] firstResponder] superview]
- && [[(id)[[paginationPageField window] firstResponder] superview] respondsToSelector:@selector(superview)]
- && [[(id)[[paginationPageField window] firstResponder] superview] superview] == paginationPageField))
- {
- [[paginationPageField window] makeFirstResponder:nil];
+ // TODO This is only relevant in 10.6 legacy mode.
+ // When using a modern NSPopover, the view controller's parent window is an _NSPopoverWindow,
+ // not the SP window and we don't care what the first responder in the popover is.
+ // (when it is not being displayed anyway).
+ if (!paginationPopover && [paginationViewController isFirstResponderInside]) {
+ [[[paginationViewController view] window] makeFirstResponder:nil];
}
}
@@ -1710,33 +1572,24 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
}
BOOL enabledMode = ![tableDocumentInstance isWorking];
- NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
- [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
-
+ BOOL limitResults = [prefs boolForKey:SPLimitResults];
// Set up the previous page button
- if ([prefs boolForKey:SPLimitResults] && contentPage > 1)
- [paginationPreviousButton setEnabled:enabledMode];
- else
- [paginationPreviousButton setEnabled:NO];
+ [paginationPreviousButton setEnabled:(limitResults && contentPage > 1 ? enabledMode : NO)];
// Set up the next page button
- if ([prefs boolForKey:SPLimitResults] && contentPage < maxPage)
- [paginationNextButton setEnabled:enabledMode];
- else
- [paginationNextButton setEnabled:NO];
+ [paginationNextButton setEnabled:(limitResults && contentPage < maxPage ? enabledMode : NO)];
#ifndef SP_CODA
// As long as a table is selected (which it will be if this is called), enable pagination detail button
[paginationButton setEnabled:enabledMode];
#endif
+ // "1" is the minimum page, so maxPage must not be less (which it would be for empty tables)
+ if(maxPage < 1) maxPage = 1;
+
// Set the values and maximums for the text field and associated pager
- [paginationPageField setStringValue:[numberFormatter stringFromNumber:[NSNumber numberWithUnsignedInteger:contentPage]]];
- [[paginationPageField formatter] setMaximum:[NSNumber numberWithUnsignedInteger:maxPage]];
-#ifndef SP_CODA
- [paginationPageStepper setIntegerValue:contentPage];
- [paginationPageStepper setMaxValue:maxPage];
-#endif
+ [paginationViewController setPage:@(contentPage)];
+ [paginationViewController setMaxPage:@(maxPage)];
}
#pragma mark -
@@ -1965,10 +1818,10 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
if (![tableContentView numberOfSelectedRows]) return;
NSAlert *alert = [NSAlert alertWithMessageText:@""
- defaultButton:NSLocalizedString(@"Delete", @"delete button")
- alternateButton:NSLocalizedString(@"Cancel", @"cancel button")
- otherButton:nil
- informativeTextWithFormat:@""];
+ defaultButton:NSLocalizedString(@"Delete", @"delete button")
+ alternateButton:NSLocalizedString(@"Cancel", @"cancel button")
+ otherButton:nil
+ informativeTextWithFormat:@""];
[alert setAlertStyle:NSCriticalAlertStyle];
@@ -2025,7 +1878,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
/**
* Perform the requested row deletion action.
*/
-- (void)removeRowSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo
+- (void)removeRowSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
NSMutableIndexSet *selectedRows = [NSMutableIndexSet indexSet];
NSString *wherePart;
@@ -2533,45 +2386,39 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
[spHistoryControllerInstance setModifyingState:YES];
#endif
- NSString *targetFilterValue = [tableValues cellDataAtRow:[theArrowCell getClickedRow] column:dataColumnIndex];
+ id targetFilterValue = [tableValues cellDataAtRow:[theArrowCell getClickedRow] column:dataColumnIndex];
//when navigating binary relations (eg. raw UUID) do so via a hex-encoded value for charset safety
BOOL navigateAsHex = ([targetFilterValue isKindOfClass:[NSData class]] && [[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"binary"]);
if(navigateAsHex) targetFilterValue = [mySQLConnection escapeData:(NSData *)targetFilterValue includingQuotes:NO];
+ NSString *filterComparison = @"=";
+ if([targetFilterValue isNSNull]) filterComparison = @"IS NULL";
+ else if(navigateAsHex) filterComparison = @"= (Hex String)";
+
+ // Store the filter details to use when loading the target table
+ NSDictionary *filterSettings = [SPRuleFilterController makeSerializedFilterForColumn:[refDictionary objectForKey:@"column"]
+ operator:filterComparison
+ values:@[targetFilterValue]];
+
// If the link is within the current table, apply filter settings manually
if ([[refDictionary objectForKey:@"table"] isEqualToString:selectedTable]) {
SPMainQSync(^{
- [fieldField selectItemWithTitle:[refDictionary objectForKey:@"column"]];
- [self setCompareTypes:self];
- if ([targetFilterValue isNSNull]) {
- [compareField selectItemWithTitle:@"IS NULL"];
- }
- else {
- if(navigateAsHex) [compareField selectItemWithTitle:@"= (Hex String)"];
- [argumentField setStringValue:targetFilterValue];
- }
+ [ruleFilterController restoreSerializedFilters:filterSettings];
+ [self setRuleEditorVisible:YES animate:YES];
+ activeFilter = SPTableContentFilterSourceRuleFilter;
});
tableFilterRequired = YES;
}
else {
- NSString *filterComparison = nil;
- if([targetFilterValue isNSNull]) filterComparison = @"IS NULL";
- else if(navigateAsHex) filterComparison = @"= (Hex String)";
-
- // Store the filter details to use when loading the target table
- NSDictionary *filterSettings = @{
- @"filterField": [refDictionary objectForKey:@"column"],
- @"filterValue": targetFilterValue,
- @"filterComparison": SPBoxNil(filterComparison)
- };
SPMainQSync(^{
[self setFiltersToRestore:filterSettings];
-
+ [self setActiveFilterToRestore:SPTableContentFilterSourceRuleFilter];
// Attempt to switch to the target table
if (![tablesListInstance selectItemWithName:[refDictionary objectForKey:@"table"]]) {
NSBeep();
[self setFiltersToRestore:nil];
+ [self setActiveFilterToRestore:SPTableContentFilterSourceNone];
}
});
}
@@ -2592,188 +2439,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
}
}
-- (void)contentFiltersHaveBeenUpdated:(NSNotification *)notification
-{
- [self setCompareTypes:nil];
-}
-
-/**
- * Sets the compare types for the filter and the appropriate formatter for the textField
- */
-- (IBAction)setCompareTypes:(id)sender
-{
-
- if(contentFilters == nil
- || ![contentFilters objectForKey:@"number"]
- || ![contentFilters objectForKey:@"string"]
- || ![contentFilters objectForKey:@"date"]) {
- NSLog(@"Error while setting filter types.");
- NSBeep();
- return;
- }
-
- // Retrieve the current field comparison setting for later restoration if possible
- NSString *titleToRestore = [[compareField selectedItem] title];
-
- // Reset the menu before building it back up
- [compareField removeAllItems];
-
- NSString *fieldTypeGrouping;
- if([[tableDataInstance columnWithName:[fieldField titleOfSelectedItem]] objectForKey:@"typegrouping"])
- fieldTypeGrouping = [NSString stringWithString:[[tableDataInstance columnWithName:[fieldField titleOfSelectedItem]] objectForKey:@"typegrouping"]];
- else
- return;
-
- if ( [fieldTypeGrouping isEqualToString:@"date"] ) {
- compareType = @"date";
-
- /*
- if ([fieldType isEqualToString:@"timestamp"]) {
- [argumentField setFormatter:[[NSDateFormatter alloc]
- initWithDateFormat:@"%Y-%m-%d %H:%M:%S" allowNaturalLanguage:YES]];
- }
- if ([fieldType isEqualToString:@"datetime"]) {
- [argumentField setFormatter:[[NSDateFormatter alloc] initWithDateFormat:@"%Y-%m-%d %H:%M:%S" allowNaturalLanguage:YES]];
- }
- if ([fieldType isEqualToString:@"date"]) {
- [argumentField setFormatter:[[NSDateFormatter alloc] initWithDateFormat:@"%Y-%m-%d" allowNaturalLanguage:YES]];
- }
- if ([fieldType isEqualToString:@"time"]) {
- [argumentField setFormatter:[[NSDateFormatter alloc] initWithDateFormat:@"%H:%M:%S" allowNaturalLanguage:YES]];
- }
- if ([fieldType isEqualToString:@"year"]) {
- [argumentField setFormatter:[[NSDateFormatter alloc] initWithDateFormat:@"%Y" allowNaturalLanguage:YES]];
- }
- */
-
- // TODO: A bug in the framework previously meant enum fields had to be treated as string fields for the purposes
- // of comparison - this can now be split out to support additional comparison fucntionality if desired.
- } else if ([fieldTypeGrouping isEqualToString:@"string"] || [fieldTypeGrouping isEqualToString:@"binary"]
- || [fieldTypeGrouping isEqualToString:@"textdata"] || [fieldTypeGrouping isEqualToString:@"blobdata"]
- || [fieldTypeGrouping isEqualToString:@"enum"]) {
-
- compareType = @"string";
- // [argumentField setFormatter:nil];
-
- } else if ([fieldTypeGrouping isEqualToString:@"bit"] || [fieldTypeGrouping isEqualToString:@"integer"]
- || [fieldTypeGrouping isEqualToString:@"float"]) {
- compareType = @"number";
- // [argumentField setFormatter:numberFormatter];
-
- } else if ([fieldTypeGrouping isEqualToString:@"geometry"]) {
- compareType = @"spatial";
-
- } else {
- compareType = @"";
- NSBeep();
- NSLog(@"ERROR: unknown type for comparision: %@, in %@", [[tableDataInstance columnWithName:[fieldField titleOfSelectedItem]] objectForKey:@"type"], fieldTypeGrouping);
- }
-
- // Add IS NULL and IS NOT NULL as they should always be available
- // [compareField addItemWithTitle:@"IS NULL"];
- // [compareField addItemWithTitle:@"IS NOT NULL"];
-
- // Remove user-defined filters first
- if([numberOfDefaultFilters objectForKey:compareType]) {
- NSUInteger cycles = [[contentFilters objectForKey:compareType] count] - [[numberOfDefaultFilters objectForKey:compareType] integerValue];
- while(cycles > 0) {
- [[contentFilters objectForKey:compareType] removeLastObject];
- cycles--;
- }
- }
-
-#ifndef SP_CODA /* content filters */
- // Load global user-defined content filters
- if([prefs objectForKey:SPContentFilters]
- && [contentFilters objectForKey:compareType]
- && [[prefs objectForKey:SPContentFilters] objectForKey:compareType])
- {
- [[contentFilters objectForKey:compareType] addObjectsFromArray:[[prefs objectForKey:SPContentFilters] objectForKey:compareType]];
- }
-
- // Load doc-based user-defined content filters
- if([[SPQueryController sharedQueryController] contentFilterForFileURL:[tableDocumentInstance fileURL]]) {
- id filters = [[SPQueryController sharedQueryController] contentFilterForFileURL:[tableDocumentInstance fileURL]];
- if([filters objectForKey:compareType])
- [[contentFilters objectForKey:compareType] addObjectsFromArray:[filters objectForKey:compareType]];
- }
-#endif
-
- // Rebuild operator popup menu
- NSUInteger i = 0;
- NSMenu *menu = [compareField menu];
- if([contentFilters objectForKey:compareType])
- for(id filter in [contentFilters objectForKey:compareType]) {
- NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:([filter objectForKey:@"MenuLabel"])?[filter objectForKey:@"MenuLabel"]:@"not specified" action:NULL keyEquivalent:@""];
- // Create the tooltip
- if([filter objectForKey:@"Tooltip"])
- [item setToolTip:[filter objectForKey:@"Tooltip"]];
- else {
- NSMutableString *tip = [[NSMutableString alloc] init];
- if([filter objectForKey:@"Clause"] && [(NSString *)[filter objectForKey:@"Clause"] length]) {
- [tip setString:[[filter objectForKey:@"Clause"] stringByReplacingOccurrencesOfRegex:@"(?<!\\\\)(\\$\\{.*?\\})" withString:@"[arg]"]];
- if([tip isMatchedByRegex:@"(?<!\\\\)\\$BINARY"]) {
- [tip replaceOccurrencesOfRegex:@"(?<!\\\\)\\$BINARY" withString:@""];
- [tip appendString:NSLocalizedString(@"\n\nPress ⇧ for binary search (case-sensitive).", @"\n\npress shift for binary search tooltip message")];
- }
- [tip flushCachedRegexData];
- [tip replaceOccurrencesOfRegex:@"(?<!\\\\)\\$CURRENT_FIELD" withString:[[fieldField titleOfSelectedItem] backtickQuotedString]];
- [tip flushCachedRegexData];
- [item setToolTip:tip];
- } else {
- [item setToolTip:@""];
- }
- [tip release];
- }
- [item setTag:i];
- [menu addItem:item];
- [item release];
- i++;
- }
-
-#ifndef SP_CODA
- [menu addItem:[NSMenuItem separatorItem]];
- NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Edit Filters…", @"edit filter") action:NULL keyEquivalent:@""];
- [item setToolTip:NSLocalizedString(@"Edit user-defined Filters…", @"edit user-defined filter")];
- [item setTag:i];
- [menu addItem:item];
- [item release];
-#endif
-
- // Attempt to reselect the previously selected title, falling back to the first
- // item on failure, as long as there is no filter selection to be restored.
- if (!filterFieldToRestore) {
- [compareField selectItemWithTitle:titleToRestore];
- if (![compareField selectedItem]) [compareField selectItemAtIndex:0];
- }
-
- // Update the argumentField enabled state
- [self performSelectorOnMainThread:@selector(toggleFilterField:) withObject:self waitUntilDone:YES];
-
- // set focus on argumentField
- [argumentField performSelectorOnMainThread:@selector(selectText:) withObject:self waitUntilDone:YES];
-
-}
-
-- (void)openContentFilterManager
-{
- [compareField selectItemWithTag:lastSelectedContentFilterIndex];
-
- // init query favorites controller
-#ifndef SP_CODA
- [prefs synchronize];
-#endif
- if(contentFilterManager) [contentFilterManager release];
- contentFilterManager = [[SPContentFilterManager alloc] initWithDelegate:self forFilterType:compareType];
-
- // Open query favorite manager
- [NSApp beginSheet:[contentFilterManager window]
- modalForWindow:[tableDocumentInstance parentWindow]
- modalDelegate:contentFilterManager
- didEndSelector:nil
- contextInfo:nil];
-}
-
/**
* Tries to write a new row to the table.
* Returns YES if row is written to table, otherwise NO; also returns YES if no row
@@ -2851,8 +2516,8 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
fieldValue = [NSString stringWithFormat:@"b'%@'", ((![desc length] || [desc isEqualToString:@"0"]) ? @"0" : desc)];
} else if ([fieldTypeGroup isEqualToString:@"date"] && [desc isEqualToString:@"NOW()"]) {
fieldValue = @"NOW()";
- } else if ([fieldTypeGroup isEqualToString:@"string"] && [[rowObject description] isEqualToString:@"UUID()"]) {
- fieldValue = @"UUID()";
+ } else if ([fieldTypeGroup isEqualToString:@"string"] && [[rowObject description] isEqualToString:@"UUID()"]) {
+ fieldValue = @"UUID()";
} else {
fieldValue = [mySQLConnection escapeAndQuoteString:desc];
}
@@ -2986,12 +2651,20 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
currentlyEditingRow = -1;
return YES;
-
+ }
// Report errors which have occurred
- }
else {
- SPBeginAlertSheet(NSLocalizedString(@"Unable to write row", @"Unable to write row error"), NSLocalizedString(@"Edit row", @"Edit row button"), NSLocalizedString(@"Discard changes", @"discard changes button"), nil, [tableDocumentInstance parentWindow], self, @selector(addRowErrorSheetDidEnd:returnCode:contextInfo:), NULL,
- [NSString stringWithFormat:NSLocalizedString(@"MySQL said:\n\n%@", @"message of panel when error while adding row to db"), [mySQLConnection lastErrorMessage]]);
+ SPBeginAlertSheet(
+ NSLocalizedString(@"Unable to write row", @"Unable to write row error"),
+ NSLocalizedString(@"Edit row", @"Edit row button"),
+ NSLocalizedString(@"Discard changes", @"discard changes button"),
+ nil,
+ [tableDocumentInstance parentWindow],
+ self,
+ @selector(addRowErrorSheetDidEnd:returnCode:contextInfo:),
+ NULL,
+ [NSString stringWithFormat:NSLocalizedString(@"MySQL said:\n\n%@", @"message of panel when error while adding row to db"), [mySQLConnection lastErrorMessage]]
+ );
return NO;
}
}
@@ -3328,46 +3001,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
}
-/**
- * Close an open sheet.
- */
-- (void)sheetDidEnd:(id)sheet returnCode:(NSInteger)returnCode contextInfo:(NSString *)contextInfo
-{
-#ifndef SP_CODA
- [sheet orderOut:self];
-
- if([contextInfo isEqualToString:SPTableFilterSetDefaultOperator]) {
- if(returnCode) {
- if(filterTableDefaultOperator) [filterTableDefaultOperator release];
- NSString *newOperator = [filterTableSetDefaultOperatorValue stringValue];
- filterTableDefaultOperator = [[self escapeFilterTableDefaultOperator:newOperator] retain];
- [prefs setObject:newOperator forKey:SPFilterTableDefaultOperator];
-
- if(![newOperator isMatchedByRegex:@"(?i)like\\s+['\"]%@%['\"]\\s*"]) {
- if(![prefs objectForKey:SPFilterTableDefaultOperatorLastItems])
- [prefs setObject:[NSMutableArray array] forKey:SPFilterTableDefaultOperatorLastItems];
-
- NSMutableArray *lastItems = [NSMutableArray array];
- [lastItems setArray:[prefs objectForKey:SPFilterTableDefaultOperatorLastItems]];
-
- if([lastItems containsObject:newOperator])
- [lastItems removeObject:newOperator];
- if([lastItems count] > 0)
- [lastItems insertObject:newOperator atIndex:0];
- else
- [lastItems addObject:newOperator];
- // Remember only the last 15 items
- if([lastItems count] > 15)
- while([lastItems count] > 15)
- [filterTableSetDefaultOperatorValue removeItemAtIndex:[lastItems count]-1];
-
- [prefs setObject:lastItems forKey:SPFilterTableDefaultOperatorLastItems];
- }
- [self updateFilterTableClause:nil];
- }
- }
-#endif
-}
+
/**
* Show Error sheet (can be called from inside of a endSheet selector)
@@ -3564,160 +3198,11 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
#pragma mark Filter Table
/**
- * Clear the filter table
- */
-- (IBAction)tableFilterClear:(id)sender
-{
-#ifndef SP_CODA
-
- [filterTableView abortEditing];
-
- if(filterTableData && [filterTableData count]) {
-
- // Clear filter data
- for(NSNumber *col in [filterTableData allKeys])
- {
- [[filterTableData objectForKey:col] setObject:[NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil] forKey:SPTableContentFilterKey];
- }
-
- [filterTableView reloadData];
- [filterTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
- [filterTableWhereClause setString:@""];
-
- // Reload table
- [self filterTable:nil];
- }
-#endif
-}
-
-/**
* Show filter table
*/
- (IBAction)showFilterTable:(id)sender
{
-#ifndef SP_CODA
- [filterTableWindow makeKeyAndOrderFront:nil];
- [filterTableWhereClause setContinuousSpellCheckingEnabled:NO];
- [filterTableWhereClause setAutoindent:NO];
- [filterTableWhereClause setAutoindentIgnoresEnter:NO];
- [filterTableWhereClause setAutopair:[prefs boolForKey:SPCustomQueryAutoPairCharacters]];
- [filterTableWhereClause setAutohelp:NO];
- [filterTableWhereClause setAutouppercaseKeywords:[prefs boolForKey:SPCustomQueryAutoUppercaseKeywords]];
- [filterTableWhereClause setCompletionWasReinvokedAutomatically:NO];
- [filterTableWhereClause insertText:@""];
- [filterTableWhereClause didChangeText];
-
- [[filterTableView window] makeFirstResponder:filterTableView];
-#endif
-}
-
-/**
- * Set filter table's Negate
- */
-- (IBAction)toggleNegateClause:(id)sender
-{
-#ifndef SP_CODA
- filterTableNegate = !filterTableNegate;
-
- if (filterTableNegate) {
- [filterTableQueryTitle setStringValue:NSLocalizedString(@"WHERE NOT query", @"Title of filter preview area when the query WHERE is negated")];
- }
- else {
- [filterTableQueryTitle setStringValue:NSLocalizedString(@"WHERE query", @"Title of filter preview area when the query WHERE is normal")];
- }
-
- // If live search is set perform filtering
- if ([filterTableLiveSearchCheckbox state] == NSOnState) {
- [self filterTable:filterTableFilterButton];
- }
-#endif
-
-}
-
-/**
- * Set filter table's Distinct
- */
-- (IBAction)toggleDistinctSelect:(id)sender
-{
-#ifndef SP_CODA
- filterTableDistinct = !filterTableDistinct;
-
- [filterTableDistinctCheckbox setState:(filterTableDistinct) ? NSOnState : NSOffState];
-
- // If live search is set perform filtering
- if ([filterTableLiveSearchCheckbox state] == NSOnState) {
- [self filterTable:filterTableFilterButton];
- }
-#endif
-
-}
-
-/**
- * Set filter table's default operator
- */
-- (IBAction)setDefaultOperator:(id)sender
-{
-#ifndef SP_CODA
-
- [filterTableWindow makeFirstResponder:filterTableView];
-
- // Load history
- if([prefs objectForKey:SPFilterTableDefaultOperatorLastItems]) {
- NSMutableArray *lastItems = [NSMutableArray array];
-
- [lastItems addObject:@"LIKE '%@%'"];
-
- for(NSString* item in [prefs objectForKey:SPFilterTableDefaultOperatorLastItems])
- {
- [lastItems addObject:item];
- }
-
- [filterTableSetDefaultOperatorValue removeAllItems];
- [filterTableSetDefaultOperatorValue addItemsWithObjectValues:lastItems];
- }
-
- [filterTableSetDefaultOperatorValue setStringValue:[prefs objectForKey:SPFilterTableDefaultOperator]];
-
- [NSApp beginSheet:filterTableSetDefaultOperatorSheet
- modalForWindow:filterTableWindow
- modalDelegate:self
- didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
- contextInfo:SPTableFilterSetDefaultOperator];
-#endif
-
-}
-
-/**
- * Generate WHERE clause to look for last typed pattern in all fields
- */
-- (IBAction)toggleLookAllFieldsMode:(id)sender
-{
- [self updateFilterTableClause:sender];
-
-#ifndef SP_CODA
- // If live search is set perform filtering
- if ([filterTableLiveSearchCheckbox state] == NSOnState) {
- [self filterTable:filterTableFilterButton];
- }
-#endif
-
-}
-
-/**
- * Closes the current sheet and stops the modal session
- */
-- (IBAction)closeSheet:(id)sender
-{
- [NSApp endSheet:[sender window] returnCode:[sender tag]];
- [[sender window] orderOut:self];
-}
-
-/**
- * Opens the content filter help page in the default browser.
- */
-- (IBAction)showDefaultOperaterHelp:(id)sender
-{
- [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:SPLOCALIZEDURL_CONTENTFILTERHELP]];
+ [filterTableController showFilterTableWindow];
}
#pragma mark -
@@ -3850,7 +3335,8 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
*/
- (CGFloat) tablesListWidth
{
- return [[[[tableDocumentInstance valueForKeyPath:@"contentViewSplitter"] subviews] objectAtIndex:0] frame].size.width;
+#warning private ivar accessed from outside
+ return [[[[tableDocumentInstance valueForKey:@"contentViewSplitter"] subviews] objectAtIndex:0] frame].size.width;
}
/**
@@ -3860,21 +3346,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
*/
- (NSDictionary *) filterSettings
{
- NSDictionary *theDictionary;
-
- if (![fieldField isEnabled]) return nil;
-
- theDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
- [self tableFilterString], @"menuLabel",
- [fieldField titleOfSelectedItem], @"filterField",
- [[compareField selectedItem] title], @"filterComparison",
- [NSNumber numberWithInteger:[[compareField selectedItem] tag]], @"filterComparisonTag",
- [argumentField stringValue], @"filterValue",
- [firstBetweenField stringValue], @"firstBetweenField",
- [secondBetweenField stringValue], @"secondBetweenField",
- nil];
-
- return theDictionary;
+ return [ruleFilterController serializedFilter];
}
/**
@@ -3921,44 +3393,9 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
*/
- (void) setFiltersToRestore:(NSDictionary *)filterSettings
{
- if (filterFieldToRestore) SPClear(filterFieldToRestore);
- if (filterComparisonToRestore) SPClear(filterComparisonToRestore);
- if (filterValueToRestore) SPClear(filterValueToRestore);
- if (firstBetweenValueToRestore) SPClear(firstBetweenValueToRestore);
- if (secondBetweenValueToRestore) SPClear(secondBetweenValueToRestore);
-
- if ([filterSettings count]) {
- if ([filterSettings objectForKey:@"filterField"])
- filterFieldToRestore = [[NSString alloc] initWithString:[filterSettings objectForKey:@"filterField"]];
- if ([[filterSettings objectForKey:@"filterComparison"] unboxNull]) {
- // Check if operator is BETWEEN, if so set up input fields
- if([[filterSettings objectForKey:@"filterComparison"] isEqualToString:@"BETWEEN"]) {
- [argumentField setHidden:YES];
- [betweenTextField setHidden:NO];
- [firstBetweenField setHidden:NO];
- [secondBetweenField setHidden:NO];
- [firstBetweenField setEnabled:YES];
- [secondBetweenField setEnabled:YES];
- }
-
- filterComparisonToRestore = [[NSString alloc] initWithString:[filterSettings objectForKey:@"filterComparison"]];
- }
- if([filterComparisonToRestore isEqualToString:@"BETWEEN"]) {
- if ([filterSettings objectForKey:@"firstBetweenField"])
- firstBetweenValueToRestore = [[NSString alloc] initWithString:[filterSettings objectForKey:@"firstBetweenField"]];
- if ([filterSettings objectForKey:@"secondBetweenField"])
- secondBetweenValueToRestore = [[NSString alloc] initWithString:[filterSettings objectForKey:@"secondBetweenField"]];
- } else {
- id filterValue = [filterSettings objectForKey:@"filterValue"];
- if ([filterValue unboxNull]) {
- if ([filterValue isKindOfClass:[NSData class]]) {
- filterValueToRestore = [[NSString alloc] initWithData:(NSData *)filterValue encoding:[mySQLConnection stringEncoding]];
- } else {
- filterValueToRestore = [[NSString alloc] initWithString:(NSString *)filterValue];
- }
- }
- }
- }
+ [filterSettings retain];
+ SPClear(filtersToRestore);
+ filtersToRestore = filterSettings;
}
/**
@@ -3973,6 +3410,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
[self setSelectionToRestore:[self selectionDetailsAllowingIndexSelection:YES]];
[self setViewportToRestore:[self viewport]];
[self setFiltersToRestore:[self filterSettings]];
+ [self setActiveFilterToRestore:activeFilter];
}
/**
@@ -3985,36 +3423,98 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
[self setSelectionToRestore:nil];
[self setViewportToRestore:NSZeroRect];
[self setFiltersToRestore:nil];
+ [self setActiveFilterToRestore:SPTableContentFilterSourceNone];
}
-- (void) setFilterTableData:(NSData*)arcData
+- (NSData*) filterTableData
{
-#ifndef SP_CODA
- if(!arcData) return;
- NSDictionary *filterData = [NSUnarchiver unarchiveObjectWithData:arcData];
- [filterTableData removeAllObjects];
- [filterTableData addEntriesFromDictionary:filterData];
- [filterTableWindow makeKeyAndOrderFront:nil];
- // [filterTableView reloadData];
-#endif
+ return [filterTableController filterTableData];
}
-- (NSData*) filterTableData
+- (void)setFilterTableData:(NSData *)arcData;
{
-#ifndef SP_CODA
- if(![filterTableWindow isVisible]) return nil;
+ [filterTableController setFilterTableData:arcData];
+}
- [filterTableView deselectAll:nil];
+- (SPTableContentFilterSource)activeFilter
+{
+ return activeFilter;
+}
- return [NSArchiver archivedDataWithRootObject:filterTableData];
-#else
- return nil;
-#endif
+- (void)setActiveFilterToRestore:(SPTableContentFilterSource)filter
+{
+ activeFilterToRestore = filter;
}
#pragma mark -
#pragma mark Table drawing and editing
+- (void)updateFilterRuleEditorSize:(CGFloat)requestedHeight animate:(BOOL)animate
+{
+ NSRect contentAreaRect = [contentAreaContainer frame];
+ CGFloat availableHeight = contentAreaRect.size.height;
+
+ NSRect ruleEditorRect = [[[ruleFilterController view] enclosingScrollView] frame];
+
+ //adjust for the UI elements below the rule editor, but only if the view height should not be 0 (ie. hidden)
+ CGFloat containerRequestedHeight = requestedHeight ? requestedHeight + ruleEditorRect.origin.y : 0;
+
+ //the rule editor can ask for about one-third of the available space before we have it use it's scrollbar
+ CGFloat topContainerGivenHeight = MIN(containerRequestedHeight,(availableHeight / 3));
+
+ // abort if the size didn't really change
+ NSRect topContainerRect = [filterRuleEditorContainer frame];
+ if(topContainerGivenHeight == topContainerRect.size.height) return;
+
+ CGFloat newBottomContainerHeight = availableHeight - topContainerGivenHeight;
+
+ NSRect bottomContainerRect = [tableContentContainer frame];
+ bottomContainerRect.size.height = newBottomContainerHeight;
+
+ topContainerRect.origin.y = newBottomContainerHeight;
+ topContainerRect.size.height = topContainerGivenHeight;
+
+ // this one should be inferable from the IB layout IMHO, but the OS gets it wrong
+ ruleEditorRect.size.height = topContainerGivenHeight - ruleEditorRect.origin.y;
+
+ if(animate) {
+ [NSAnimationContext beginGrouping];
+ [[tableContentContainer animator] setFrame:bottomContainerRect];
+ [[filterRuleEditorContainer animator] setFrame:topContainerRect];
+ [[[[ruleFilterController view] enclosingScrollView] animator] setFrame:ruleEditorRect];
+ [NSAnimationContext endGrouping];
+ }
+ else {
+ [tableContentContainer setFrameSize:bottomContainerRect.size];
+ [filterRuleEditorContainer setFrame:topContainerRect];
+ [[[ruleFilterController view] enclosingScrollView] setFrame:ruleEditorRect];
+ }
+
+ //disable rubberband scrolling as long as there is nothing to scroll
+ if(scrollViewHasRubberbandScrolling) {
+ NSScrollView *filterControllerScroller = [[ruleFilterController view] enclosingScrollView];
+ if (ruleEditorRect.size.height >= requestedHeight) {
+ [filterControllerScroller setVerticalScrollElasticity:NSScrollElasticityNone];
+ } else {
+ [filterControllerScroller setVerticalScrollElasticity:NSScrollElasticityAutomatic];
+ }
+ }
+}
+
+- (void)filterRuleEditorPreferredSizeChanged:(NSNotification *)notification
+{
+ if(showFilterRuleEditor) {
+ [self updateFilterRuleEditorSize:[ruleFilterController preferredHeight] animate:YES];
+ }
+}
+
+- (void)contentViewSizeChanged:(NSNotification *)notification
+{
+ if(showFilterRuleEditor) {
+ [self updateFilterRuleEditorSize:[ruleFilterController preferredHeight] animate:NO];
+ }
+}
+
/**
* Updates the number of rows in the selected table.
* Attempts to use the fullResult count if available, also updating the
@@ -4035,6 +3535,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
[tableDataInstance setStatusValue:@"y" forKey:@"RowsCountAccurate"];
#ifndef SP_CODA
[[tableInfoInstance onMainThread] tableChanged:nil];
+#warning private ivar accessed from outside
[[[tableDocumentInstance valueForKey:@"extendedTableInfoInstance"] onMainThread] loadTable:selectedTable];
#endif
@@ -4135,7 +3636,8 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
[removeButton setEnabled:NO];
[duplicateButton setEnabled:NO];
[reloadButton setEnabled:NO];
- [filterButton setEnabled:NO];
+ [ruleFilterController setEnabled:NO];
+ [toggleRuleFilterButton setEnabled:NO];
tableRowsSelectable = NO;
[paginationPreviousButton setEnabled:NO];
[paginationNextButton setEnabled:NO];
@@ -4170,7 +3672,8 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
}
}
- [filterButton setEnabled:[fieldField isEnabled]];
+ [ruleFilterController setEnabled:(!![selectedTable length])];
+ [toggleRuleFilterButton setEnabled:(!![selectedTable length])];
tableRowsSelectable = YES;
}
@@ -4190,22 +3693,27 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
#ifndef SP_CODA /* observe pref changes */
- // Display table veiew vertical gridlines preference changed
- if ([keyPath isEqualToString:SPDisplayTableViewVerticalGridlines]) {
- [tableContentView setGridStyleMask:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
- [filterTableView setGridStyleMask:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
- }
- // Table font preference changed
- else if ([keyPath isEqualToString:SPGlobalResultTableFont]) {
- NSFont *tableFont = [NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]];
+ // a parent class (or cocoa) can also use KVO, so we need to watch out to only catch those KVO messages we requested
+ if(context == TableContentKVOContext) {
+ // Display table veiew vertical gridlines preference changed
+ if ([keyPath isEqualToString:SPDisplayTableViewVerticalGridlines]) {
+ [tableContentView setGridStyleMask:([[change objectForKey:NSKeyValueChangeNewKey] boolValue]) ? NSTableViewSolidVerticalGridLineMask : NSTableViewGridNone];
+ }
+ // Table font preference changed
+ else if ([keyPath isEqualToString:SPGlobalResultTableFont]) {
+ NSFont *tableFont = [NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]];
- [tableContentView setRowHeight:2.0f + NSSizeToCGSize([@"{ǞṶḹÜ∑zgyf" sizeWithAttributes:@{NSFontAttributeName : tableFont}]).height];
- [tableContentView setFont:tableFont];
- [tableContentView reloadData];
+ [tableContentView setRowHeight:2.0f + NSSizeToCGSize([@"{ǞṶḹÜ∑zgyf" sizeWithAttributes:@{NSFontAttributeName : tableFont}]).height];
+ [tableContentView setFont:tableFont];
+ [tableContentView reloadData];
+ }
+ // Display binary data as Hex
+ else if ([keyPath isEqualToString:SPDisplayBinaryDataAsHex] && [tableContentView numberOfRows] > 0) {
+ [tableContentView reloadData];
+ }
}
- // Display binary data as Hex
- else if ([keyPath isEqualToString:SPDisplayBinaryDataAsHex] && [tableContentView numberOfRows] > 0) {
- [tableContentView reloadData];
+ else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
#endif
}
@@ -4255,11 +3763,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
- (NSInteger)numberOfRowsInTableView:(SPCopyTable *)tableView
{
-#ifndef SP_CODA
- if (tableView == filterTableView) {
- return filterTableIsSwapped ? [filterTableData count] : [[[filterTableData objectForKey:@"0"] objectForKey:SPTableContentFilterKey] count];
- }
-#endif
if (tableView == tableContentView) {
return tableRowsCount;
}
@@ -4270,20 +3773,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
- (id)tableView:(SPCopyTable *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
{
NSUInteger columnIndex = [[tableColumn identifier] integerValue];
-#ifndef SP_CODA
- if (tableView == filterTableView) {
- if (filterTableIsSwapped) {
- // First column shows the field names
- if (columnIndex == 0) {
- return [[[NSTableHeaderCell alloc] initTextCell:[[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:@"name"]] autorelease];
- }
-
- return NSArrayObjectAtIndex([[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:SPTableContentFilterKey], columnIndex - 1);
- }
-
- return NSArrayObjectAtIndex([[filterTableData objectForKey:[tableColumn identifier]] objectForKey:SPTableContentFilterKey], rowIndex);
- }
-#endif
if (tableView == tableContentView) {
id value = nil;
@@ -4354,20 +3843,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
{
-#ifndef SP_CODA
- if(tableView == filterTableView) {
- if (filterTableIsSwapped) {
- [[[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:SPTableContentFilterKey] replaceObjectAtIndex:([[tableColumn identifier] integerValue] - 1) withObject:(NSString *)object];
- }
- else {
- [[[filterTableData objectForKey:[tableColumn identifier]] objectForKey:SPTableContentFilterKey] replaceObjectAtIndex:rowIndex withObject:(NSString *)object];
- }
-
- [self updateFilterTableClause:nil];
-
- return;
- }
-#endif
if (tableView == tableContentView) {
NSInteger columnIndex = [[tableColumn identifier] integerValue];
// If the current cell should have been edited in a sheet, do nothing - field closing will have already
@@ -4459,209 +3934,13 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
#ifndef SP_CODA
/**
- * Escape passed operator for usage as filterTableDefaultOperator.
- */
-- (NSString*)escapeFilterTableDefaultOperator:(NSString *)op
-{
- if (!op) return @"";
-
- NSMutableString *newOp = [[[NSMutableString alloc] initWithCapacity:[op length]] autorelease];
-
- [newOp setString:op];
- [newOp replaceOccurrencesOfRegex:@"%" withString:@"%%"];
- [newOp replaceOccurrencesOfRegex:@"(?<!`)@(?!=`)" withString:@"%@"];
-
- return newOp;
-}
-
-/**
- * Update WHERE clause in filter table window.
- *
- * @param currentValue If currentValue == nil take the data from filterTableData, if currentValue == filterTableSearchAllFields
- * generate a WHERE clause to search in all given fields, if currentValue == a string take this string as table cell data of the
- * currently edited table cell
- */
-- (void)updateFilterTableClause:(id)currentValue
-{
- NSMutableString *clause = [NSMutableString string];
- NSInteger numberOfRows = [self numberOfRowsInTableView:filterTableView];
- NSInteger numberOfCols = [[filterTableView tableColumns] count];
- NSInteger numberOfValues = 0;
- NSRange opRange, defopRange;
-
- BOOL lookInAllFields = NO;
-
- NSString *re1 = @"^\\s*(<[=>]?|>=?|!?=|≠|≤|≥)\\s*(.*?)\\s*$";
- NSString *re2 = @"^\\s*(.*)\\s+(.*?)\\s*$";
-
- NSInteger editedRow = [filterTableView editedRow];
-
- if (currentValue == filterTableSearchAllFields) {
- numberOfRows = 1;
- lookInAllFields = YES;
- }
-
- [filterTableWhereClause setString:@""];
-
- for (NSInteger i = 0; i < numberOfRows; i++)
- {
- numberOfValues = 0;
-
- for (NSInteger anIndex = 0; anIndex < numberOfCols; anIndex++)
- {
- NSString *filterCell = nil;
- NSDictionary *filterCellData = [NSDictionary dictionaryWithDictionary:[filterTableData objectForKey:[NSString stringWithFormat:@"%ld", (long)anIndex]]];
-
- // Take filterTableData
- if (!currentValue) {
- filterCell = NSArrayObjectAtIndex([filterCellData objectForKey:SPTableContentFilterKey], i);
- }
- // Take last edited value to create the OR clause
- else if (lookInAllFields) {
- if (lastEditedFilterTableValue && [lastEditedFilterTableValue length]) {
- filterCell = lastEditedFilterTableValue;
- }
- else {
- [filterTableWhereClause setString:@""];
- [filterTableWhereClause insertText:@""];
- [filterTableWhereClause scrollRangeToVisible:NSMakeRange(0, 0)];
-
- // If live search is set perform filtering
- if ([filterTableLiveSearchCheckbox state] == NSOnState) {
- [self filterTable:filterTableFilterButton];
- }
- }
- }
- // Take value from currently edited table cell
- else if ([currentValue isKindOfClass:[NSString class]]) {
- if (i == editedRow && anIndex == [[NSArrayObjectAtIndex([filterTableView tableColumns], [filterTableView editedColumn]) identifier] integerValue]) {
- filterCell = (NSString*)currentValue;
- }
- else {
- filterCell = NSArrayObjectAtIndex([filterCellData objectForKey:SPTableContentFilterKey], i);
- }
- }
-
- if ([filterCell length]) {
-
- // Recode special operators
- filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≠" withString:@"!="];
- filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≤" withString:@"<="];
- filterCell = [filterCell stringByReplacingOccurrencesOfRegex:@"^\\s*≥" withString:@">="];
-
- if (numberOfValues) {
- [clause appendString:(lookInAllFields) ? @" OR " : @" AND "];
- }
-
- NSString *fieldName = [[filterCellData objectForKey:@"name"] backtickQuotedString];
- NSString *filterTableDefaultOperatorWithFieldName = [filterTableDefaultOperator stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName];
-
- opRange = [filterCell rangeOfString:@"`@`"];
- defopRange = [filterTableDefaultOperator rangeOfString:@"`@`"];
-
- // if cell data begins with ' or " treat it as it is
- // by checking if default operator by itself contains a ' or " - if so
- // remove first and if given the last ' or "
- if ([filterCell isMatchedByRegex:@"^\\s*['\"]"]) {
- if ([filterTableDefaultOperator isMatchedByRegex:@"['\"]"]) {
- NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:@"^\\s*(['\"])(.*)\\1\\s*$"];
-
- if ([matches count] && [matches = NSArrayObjectAtIndex(matches, 0) count] == 3) {
- [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, NSArrayObjectAtIndex(matches, 2)];
- }
- else {
- matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:@"^\\s*(['\"])(.*)\\s*$"];
-
- if ([matches count] && [matches = NSArrayObjectAtIndex(matches, 0) count] == 3) {
- [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, NSArrayObjectAtIndex(matches, 2)];
- }
- }
- }
- else {
- [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, filterCell];
- }
- }
- // If cell contains the field name placeholder
- else if (opRange.length || defopRange.length) {
- filterCell = [filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName];
-
- if (defopRange.length) {
- [clause appendFormat:filterTableDefaultOperatorWithFieldName, [filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName]];
- }
- else {
- [clause appendString:[filterCell stringByReplacingOccurrencesOfString:@"`@`" withString:fieldName]];
- }
- }
- // If cell is equal to NULL
- else if ([filterCell isMatchedByRegex:@"(?i)^\\s*null\\s*$"]) {
- [clause appendFormat:@"%@ IS NULL", fieldName];
- }
- // If cell starts with an operator
- else if ([filterCell isMatchedByRegex:re1]) {
- NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:re1];
-
- if ([matches count] && [matches = NSArrayObjectAtIndex(matches, 0) count] == 3) {
- [clause appendFormat:@"%@ %@ %@", fieldName, NSArrayObjectAtIndex(matches, 1), NSArrayObjectAtIndex(matches, 2)];
- }
- }
- // If cell consists of at least two words treat the first as operator and the rest as argument
- else if ([filterCell isMatchedByRegex:re2]) {
- NSArray *matches = [filterCell arrayOfCaptureComponentsMatchedByRegex:re2];
-
- if ([matches count] && [matches = NSArrayObjectAtIndex(matches,0) count] == 3) {
- [clause appendFormat:@"%@ %@ %@", fieldName, [NSArrayObjectAtIndex(matches, 1) uppercaseString], NSArrayObjectAtIndex(matches, 2)];
- }
- }
- // Apply the default operator
- else {
- [clause appendFormat:[NSString stringWithFormat:@"%%@ %@", filterTableDefaultOperatorWithFieldName], fieldName, filterCell];
- }
-
- numberOfValues++;
- }
- }
-
- if (numberOfValues) {
- [clause appendString:@"\nOR\n"];
- }
- }
-
- // Remove last " OR " if any
- [filterTableWhereClause setString:[clause length] > 3 ? [clause substringToIndex:([clause length] - 4)] : @""];
-
- // Update syntax highlighting and uppercasing
- [filterTableWhereClause insertText:@""];
- [filterTableWhereClause scrollRangeToVisible:NSMakeRange(0, 0)];
-
- // If live search is set perform filtering
- if ([filterTableLiveSearchCheckbox state] == NSOnState) {
- [self filterTable:filterTableFilterButton];
- }
-}
-
-/**
* Makes the content filter field have focus by making it the first responder.
*/
- (void)makeContentFilterHaveFocus
{
- NSDictionary *filter = [[contentFilters objectForKey:compareType] objectAtIndex:[[compareField selectedItem] tag]];
-
- if ([filter objectForKey:@"NumberOfArguments"]) {
-
- NSUInteger numOfArgs = [[filter objectForKey:@"NumberOfArguments"] integerValue];
-
- switch (numOfArgs)
- {
- case 2:
- [[firstBetweenField window] makeFirstResponder:firstBetweenField];
- break;
- case 1:
- [[argumentField window] makeFirstResponder:argumentField];
- break;
- default:
- [[compareField window] makeFirstResponder:compareField];
- }
- }
+ [self setRuleEditorVisible:YES animate:YES];
+ [toggleRuleFilterButton setState:NSOnState];
+ [ruleFilterController focusFirstInputField];
}
#endif
@@ -4834,135 +4113,128 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
{
if ([tableDocumentInstance isWorking]) return NO;
-#ifndef SP_CODA
- if (tableView == filterTableView) {
- return (filterTableIsSwapped && [[tableColumn identifier] integerValue] == 0) ? NO : YES;
- }
- else
-#endif
- if (tableView == tableContentView) {
-
- // Nothing is editable while the field editor is running.
- // This guards against a special case where accessibility services might
- // check if a table field is editable while the sheet is running.
- if (fieldEditor) return NO;
-
- // Ensure that row is editable since it could contain "(not loaded)" columns together with
- // issue that the table has no primary key
- NSString *wherePart = [NSString stringWithString:[self argumentForRow:[tableContentView selectedRow]]];
+ if (tableView == tableContentView) {
- if (![wherePart length]) return NO;
+ // Nothing is editable while the field editor is running.
+ // This guards against a special case where accessibility services might
+ // check if a table field is editable while the sheet is running.
+ if (fieldEditor) return NO;
- // If the selected cell hasn't been loaded, load it.
- if ([[tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]] isSPNotLoaded]) {
+ // Ensure that row is editable since it could contain "(not loaded)" columns together with
+ // issue that the table has no primary key
+ NSString *wherePart = [NSString stringWithString:[self argumentForRow:[tableContentView selectedRow]]];
- // Only get the data for the selected column, not all of them
- NSString *query = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@", [[[tableColumn headerCell] stringValue] backtickQuotedString], [selectedTable backtickQuotedString], wherePart];
+ if (![wherePart length]) return NO;
- SPMySQLResult *tempResult = [mySQLConnection queryString:query];
+ // If the selected cell hasn't been loaded, load it.
+ if ([[tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]] isSPNotLoaded]) {
- if (![tempResult numberOfRows]) {
- SPOnewayAlertSheet(
- NSLocalizedString(@"Error", @"error"),
- [tableDocumentInstance parentWindow],
- NSLocalizedString(@"Couldn't load the row. Reload the table to be sure that the row exists and use a primary key for your table.", @"message of panel when loading of row failed")
- );
- return NO;
- }
+ // Only get the data for the selected column, not all of them
+ NSString *query = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@", [[[tableColumn headerCell] stringValue] backtickQuotedString], [selectedTable backtickQuotedString], wherePart];
- NSArray *tempRow = [tempResult getRowAsArray];
+ SPMySQLResult *tempResult = [mySQLConnection queryString:query];
- [tableValues replaceObjectInRow:rowIndex column:[[tableContentView tableColumns] indexOfObject:tableColumn] withObject:[tempRow objectAtIndex:0]];
- [tableContentView reloadData];
+ if (![tempResult numberOfRows]) {
+ SPOnewayAlertSheet(
+ NSLocalizedString(@"Error", @"error"),
+ [tableDocumentInstance parentWindow],
+ NSLocalizedString(@"Couldn't load the row. Reload the table to be sure that the row exists and use a primary key for your table.", @"message of panel when loading of row failed")
+ );
+ return NO;
}
- // Retrieve the column definition
- NSDictionary *columnDefinition = [cqColumnDefinition objectAtIndex:[[tableColumn identifier] integerValue]];
+ NSArray *tempRow = [tempResult getRowAsArray];
- // Open the editing sheet if required
- if ([tableContentView shouldUseFieldEditorForRow:rowIndex column:[[tableColumn identifier] integerValue] checkWithLock:NULL]) {
+ [tableValues replaceObjectInRow:rowIndex column:[[tableContentView tableColumns] indexOfObject:tableColumn] withObject:[tempRow objectAtIndex:0]];
+ [tableContentView reloadData];
+ }
- BOOL isBlob = [tableDataInstance columnIsBlobOrText:[[tableColumn headerCell] stringValue]];
+ // Retrieve the column definition
+ NSDictionary *columnDefinition = [cqColumnDefinition objectAtIndex:[[tableColumn identifier] integerValue]];
- // A table is per definition editable
- BOOL isFieldEditable = YES;
+ // Open the editing sheet if required
+ if ([tableContentView shouldUseFieldEditorForRow:rowIndex column:[[tableColumn identifier] integerValue] checkWithLock:NULL]) {
- // Check for Views if field is editable
- if ([tablesListInstance tableType] == SPTableTypeView) {
- NSArray *editStatus = [self fieldEditStatusForRow:rowIndex andColumn:[[tableColumn identifier] integerValue]];
- isFieldEditable = [[editStatus objectAtIndex:0] integerValue] == 1;
- }
+ BOOL isBlob = [tableDataInstance columnIsBlobOrText:[[tableColumn headerCell] stringValue]];
- NSUInteger fieldLength = 0;
- NSString *fieldEncoding = nil;
- BOOL allowNULL = YES;
+ // A table is per definition editable
+ BOOL isFieldEditable = YES;
- NSString *fieldType = [columnDefinition objectForKey:@"type"];
+ // Check for Views if field is editable
+ if ([tablesListInstance tableType] == SPTableTypeView) {
+ NSArray *editStatus = [self fieldEditStatusForRow:rowIndex andColumn:[[tableColumn identifier] integerValue]];
+ isFieldEditable = [[editStatus objectAtIndex:0] integerValue] == 1;
+ }
- if ([columnDefinition objectForKey:@"char_length"]) {
- fieldLength = [[columnDefinition objectForKey:@"char_length"] integerValue];
- }
+ NSUInteger fieldLength = 0;
+ NSString *fieldEncoding = nil;
+ BOOL allowNULL = YES;
- if ([columnDefinition objectForKey:@"null"]) {
- allowNULL = (![[columnDefinition objectForKey:@"null"] integerValue]);
- }
+ NSString *fieldType = [columnDefinition objectForKey:@"type"];
- if ([columnDefinition objectForKey:@"charset_name"] && ![[columnDefinition objectForKey:@"charset_name"] isEqualToString:@"binary"]) {
- fieldEncoding = [columnDefinition objectForKey:@"charset_name"];
- }
+ if ([columnDefinition objectForKey:@"char_length"]) {
+ fieldLength = [[columnDefinition objectForKey:@"char_length"] integerValue];
+ }
- fieldEditor = [[SPFieldEditorController alloc] init];
+ if ([columnDefinition objectForKey:@"null"]) {
+ allowNULL = (![[columnDefinition objectForKey:@"null"] integerValue]);
+ }
- [fieldEditor setEditedFieldInfo:[NSDictionary dictionaryWithObjectsAndKeys:
- [[tableColumn headerCell] stringValue], @"colName",
- [self usedQuery], @"usedQuery",
- @"content", @"tableSource",
- nil]];
+ if ([columnDefinition objectForKey:@"charset_name"] && ![[columnDefinition objectForKey:@"charset_name"] isEqualToString:@"binary"]) {
+ fieldEncoding = [columnDefinition objectForKey:@"charset_name"];
+ }
- [fieldEditor setTextMaxLength:fieldLength];
- [fieldEditor setFieldType:fieldType == nil ? @"" : fieldType];
- [fieldEditor setFieldEncoding:fieldEncoding == nil ? @"" : fieldEncoding];
- [fieldEditor setAllowNULL:allowNULL];
+ fieldEditor = [[SPFieldEditorController alloc] init];
- id cellValue = [tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]];
+ [fieldEditor setEditedFieldInfo:[NSDictionary dictionaryWithObjectsAndKeys:
+ [[tableColumn headerCell] stringValue], @"colName",
+ [self usedQuery], @"usedQuery",
+ @"content", @"tableSource",
+ nil]];
- if ([cellValue isNSNull]) {
- cellValue = [NSString stringWithString:[prefs objectForKey:SPNullValue]];
- }
+ [fieldEditor setTextMaxLength:fieldLength];
+ [fieldEditor setFieldType:fieldType == nil ? @"" : fieldType];
+ [fieldEditor setFieldEncoding:fieldEncoding == nil ? @"" : fieldEncoding];
+ [fieldEditor setAllowNULL:allowNULL];
- if ([self cellValueIsDisplayedAsHexForColumn:[[tableColumn identifier] integerValue]]) {
- [fieldEditor setTextMaxLength:[[self tableView:tableContentView objectValueForTableColumn:tableColumn row:rowIndex] length]];
- isFieldEditable = NO;
- }
+ id cellValue = [tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]];
- NSInteger editedColumn = 0;
+ if ([cellValue isNSNull]) {
+ cellValue = [NSString stringWithString:[prefs objectForKey:SPNullValue]];
+ }
- for (NSTableColumn* col in [tableContentView tableColumns])
- {
- if ([[col identifier] isEqualToString:[tableColumn identifier]]) break;
+ if ([self cellValueIsDisplayedAsHexForColumn:[[tableColumn identifier] integerValue]]) {
+ [fieldEditor setTextMaxLength:[[self tableView:tableContentView objectValueForTableColumn:tableColumn row:rowIndex] length]];
+ isFieldEditable = NO;
+ }
- editedColumn++;
- }
+ NSInteger editedColumn = 0;
- [fieldEditor editWithObject:cellValue
- fieldName:[[tableColumn headerCell] stringValue]
- usingEncoding:[mySQLConnection stringEncoding]
- isObjectBlob:isBlob
- isEditable:isFieldEditable
- withWindow:[tableDocumentInstance parentWindow]
- sender:self
- contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithInteger:rowIndex], @"rowIndex",
- [NSNumber numberWithInteger:editedColumn], @"columnIndex",
- [NSNumber numberWithBool:isFieldEditable], @"isFieldEditable",
- nil]];
+ for (NSTableColumn* col in [tableContentView tableColumns])
+ {
+ if ([[col identifier] isEqualToString:[tableColumn identifier]]) break;
- return NO;
+ editedColumn++;
}
- return YES;
+ [fieldEditor editWithObject:cellValue
+ fieldName:[[tableColumn headerCell] stringValue]
+ usingEncoding:[mySQLConnection stringEncoding]
+ isObjectBlob:isBlob
+ isEditable:isFieldEditable
+ withWindow:[tableDocumentInstance parentWindow]
+ sender:self
+ contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:rowIndex], @"rowIndex",
+ [NSNumber numberWithInteger:editedColumn], @"columnIndex",
+ [NSNumber numberWithBool:isFieldEditable], @"isFieldEditable",
+ nil]];
+
+ return NO;
}
+ return YES;
+ }
+
return YES;
}
@@ -5002,13 +4274,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
*/
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)rowIndex
{
-#ifndef SP_CODA
- if (tableView == filterTableView) {
- return YES;
- }
- else
-#endif
- return tableView == tableContentView ? tableRowsSelectable : YES;
+ return tableView == tableContentView ? tableRowsSelectable : YES;
}
/**
@@ -5063,76 +4329,62 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
*/
- (void)tableView:(SPCopyTable *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
{
-#ifndef SP_CODA
- if (tableView == filterTableView) {
- if (filterTableIsSwapped && [[tableColumn identifier] integerValue] == 0) {
- [cell setDrawsBackground:YES];
- [cell setBackgroundColor:lightGrayColor];
- }
- else {
- [cell setDrawsBackground:NO];
- }
+ if (tableView == tableContentView) {
- return;
- }
- else
-#endif
- if (tableView == tableContentView) {
+ if (![cell respondsToSelector:@selector(setTextColor:)]) return;
- if (![cell respondsToSelector:@selector(setTextColor:)]) return;
+ BOOL cellIsNullOrUnloaded = NO;
+ BOOL cellIsLinkCell = [cell isMemberOfClass:[SPTextAndLinkCell class]];
- BOOL cellIsNullOrUnloaded = NO;
- BOOL cellIsLinkCell = [cell isMemberOfClass:[SPTextAndLinkCell class]];
+ NSUInteger columnIndex = [[tableColumn identifier] integerValue];
- NSUInteger columnIndex = [[tableColumn identifier] integerValue];
+ // If user wants to edit 'cell' set text color to black and return to avoid
+ // writing in gray if value was NULL
+ if ([tableView editedColumn] != -1
+ && [tableView editedRow] == rowIndex
+ && (NSUInteger)[[NSArrayObjectAtIndex([tableView tableColumns], [tableView editedColumn]) identifier] integerValue] == columnIndex) {
+ [cell setTextColor:blackColor];
+ if (cellIsLinkCell) [cell setLinkActive:NO];
+ return;
+ }
+
+ // While the table is being loaded, additional validation is required - data
+ // locks must be used to avoid crashes, and indexes higher than the available
+ // rows or columns may be requested. Use gray to indicate loading in these cases.
+ if (isWorking) {
+ pthread_mutex_lock(&tableValuesLock);
- // If user wants to edit 'cell' set text color to black and return to avoid
- // writing in gray if value was NULL
- if ([tableView editedColumn] != -1
- && [tableView editedRow] == rowIndex
- && (NSUInteger)[[NSArrayObjectAtIndex([tableView tableColumns], [tableView editedColumn]) identifier] integerValue] == columnIndex) {
- [cell setTextColor:blackColor];
- if (cellIsLinkCell) [cell setLinkActive:NO];
- return;
+ if (rowIndex < (NSInteger)tableRowsCount && columnIndex < [tableValues columnCount]) {
+ cellIsNullOrUnloaded = [tableValues cellIsNullOrUnloadedAtRow:rowIndex column:columnIndex];
}
- // While the table is being loaded, additional validation is required - data
- // locks must be used to avoid crashes, and indexes higher than the available
- // rows or columns may be requested. Use gray to indicate loading in these cases.
- if (isWorking) {
- pthread_mutex_lock(&tableValuesLock);
+ pthread_mutex_unlock(&tableValuesLock);
+ }
+ else {
+ cellIsNullOrUnloaded = [tableValues cellIsNullOrUnloadedAtRow:rowIndex column:columnIndex];
+ }
- if (rowIndex < (NSInteger)tableRowsCount && columnIndex < [tableValues columnCount]) {
- cellIsNullOrUnloaded = [tableValues cellIsNullOrUnloadedAtRow:rowIndex column:columnIndex];
- }
+ if (cellIsNullOrUnloaded) {
+ [cell setTextColor:rowIndex == [tableContentView selectedRow] ? whiteColor : lightGrayColor];
+ }
+ else {
+ [cell setTextColor:blackColor];
- pthread_mutex_unlock(&tableValuesLock);
- }
- else {
- cellIsNullOrUnloaded = [tableValues cellIsNullOrUnloadedAtRow:rowIndex column:columnIndex];
+ if ([self cellValueIsDisplayedAsHexForColumn:[[tableColumn identifier] integerValue]]) {
+ [cell setTextColor:rowIndex == [tableContentView selectedRow] ? whiteColor : blueColor];
}
+ }
- if (cellIsNullOrUnloaded) {
- [cell setTextColor:rowIndex == [tableContentView selectedRow] ? whiteColor : lightGrayColor];
+ // Disable link arrows for the currently editing row and for any NULL or unloaded cells
+ if (cellIsLinkCell) {
+ if (cellIsNullOrUnloaded || [tableView editedRow] == rowIndex) {
+ [cell setLinkActive:NO];
}
else {
- [cell setTextColor:blackColor];
-
- if ([self cellValueIsDisplayedAsHexForColumn:[[tableColumn identifier] integerValue]]) {
- [cell setTextColor:rowIndex == [tableContentView selectedRow] ? whiteColor : blueColor];
- }
- }
-
- // Disable link arrows for the currently editing row and for any NULL or unloaded cells
- if (cellIsLinkCell) {
- if (cellIsNullOrUnloaded || [tableView editedRow] == rowIndex) {
- [cell setLinkActive:NO];
- }
- else {
- [cell setLinkActive:YES];
- }
+ [cell setLinkActive:YES];
}
}
+ }
}
#ifndef SP_CODA
@@ -5145,10 +4397,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
*/
- (NSString *)tableView:(NSTableView *)tableView toolTipForCell:(id)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation
{
- if (tableView == filterTableView) {
- return nil;
- }
- else if (tableView == tableContentView) {
+ if (tableView == tableContentView) {
if ([[aCell stringValue] length] < 2 || [tableDocumentInstance isWorking]) return nil;
@@ -5211,12 +4460,11 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
// Show the cell string value as tooltip (including line breaks and tabs)
// by using the cell's font
[SPTooltip showWithObject:[aCell stringValue]
- atLocation:pos
- ofType:@"text"
- displayOptions:[NSDictionary dictionaryWithObjectsAndKeys:
- [[aCell font] familyName], @"fontname",
- [NSString stringWithFormat:@"%f",[[aCell font] pointSize]], @"fontsize",
- nil]];
+ atLocation:pos
+ ofType:@"text"
+ displayOptions:[NSDictionary dictionaryWithObjectsAndKeys:[[aCell font] familyName], @"fontname",
+ [NSString stringWithFormat:@"%f", [[aCell font] pointSize]], @"fontsize",
+ nil]];
return nil;
}
@@ -5225,64 +4473,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
}
#endif
-#ifndef SP_CODA /* SplitView delegate methods */
-
-#pragma mark -
-#pragma mark SplitView delegate methods
-
-- (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview
-{
- return NO;
-}
-
-/**
- * Set a minimum size for the filter text area.
- */
-- (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset
-{
- return proposedMax - 180;
-}
-
-/**
- * Set a minimum size for the field list and action area.
- */
-- (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)offset
-{
- return proposedMin + 225;
-}
-
-/**
- * Improve default resizing and resize only the filter text area by default.
- */
-- (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize
-{
- NSSize newSize = [sender frame].size;
- NSView *leftView = [[sender subviews] objectAtIndex:0];
- NSView *rightView = [[sender subviews] objectAtIndex:1];
- float dividerThickness = [sender dividerThickness];
- NSRect leftFrame = [leftView frame];
- NSRect rightFrame = [rightView frame];
-
- // Resize height of both views
- leftFrame.size.height = newSize.height;
- rightFrame.size.height = newSize.height;
-
- // Only resize the right view's width - unless the constraint has been reached
- if (rightFrame.size.width > 180 || newSize.width > oldSize.width) {
- rightFrame.size.width = newSize.width - leftFrame.size.width - dividerThickness;
- }
- else {
- leftFrame.size.width = newSize.width - rightFrame.size.width - dividerThickness;
- }
-
- rightFrame.origin.x = leftFrame.size.width + dividerThickness;
-
- [leftView setFrame:leftFrame];
- [rightView setFrame:rightFrame];
-}
-
-#endif
-
#pragma mark -
#pragma mark Control delegate methods
@@ -5314,24 +4504,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
return YES;
}
-- (void)controlTextDidChange:(NSNotification *)notification
-{
-#ifndef SP_CODA
- if ([notification object] == filterTableView) {
-
- NSString *string = [[[[notification userInfo] objectForKey:@"NSFieldEditor"] textStorage] string];
-
- if (string && [string length]) {
- if (lastEditedFilterTableValue) [lastEditedFilterTableValue release];
-
- lastEditedFilterTableValue = [[NSString stringWithString:string] retain];
- }
-
- [self updateFilterTableClause:string];
- }
-#endif
-}
-
/**
* If the user selected a table cell which is a blob field and tried to edit it
* cancel the inline edit, display the field editor sheet instead for editing
@@ -5454,6 +4626,13 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
+ if(_mainNibLoaded) {
+ //TODO this should be changed to the variant with …context: after 10.6 support is removed!
+ [prefs removeObserver:self forKeyPath:SPGlobalResultTableFont];
+ [prefs removeObserver:self forKeyPath:SPDisplayBinaryDataAsHex];
+ [prefs removeObserver:self forKeyPath:SPDisplayTableViewVerticalGridlines];
+ }
+
// Cancel previous performSelector: requests on ourselves and the table view
// to prevent crashes for deferred actions
[NSObject cancelPreviousPerformRequestsWithTarget:self];
@@ -5467,17 +4646,11 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
SPClear(dataColumns);
SPClear(oldRow);
#ifndef SP_CODA
- for (id retainedObject in nibObjectsToRelease) [retainedObject release];
- SPClear(nibObjectsToRelease);
SPClear(paginationPopover);
+ SPClear(paginationViewController);
- SPClear(filterTableData);
- if (lastEditedFilterTableValue) SPClear(lastEditedFilterTableValue);
- if (filterTableDefaultOperator) SPClear(filterTableDefaultOperator);
#endif
if (selectedTable) SPClear(selectedTable);
- if (contentFilters) SPClear(contentFilters);
- if (numberOfDefaultFilters) SPClear(numberOfDefaultFilters);
if (keys) SPClear(keys);
if (sortCol) SPClear(sortCol);
SPClear(usedQuery);
@@ -5485,12 +4658,54 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper
if (selectionToRestore) SPClear(selectionToRestore);
if (cqColumnDefinition) SPClear(cqColumnDefinition);
- if (filterFieldToRestore) filterFieldToRestore = nil;
- if (filterComparisonToRestore) filterComparisonToRestore = nil;
- if (filterValueToRestore) filterValueToRestore = nil;
- if (firstBetweenValueToRestore) firstBetweenValueToRestore = nil;
- if (secondBetweenValueToRestore) secondBetweenValueToRestore = nil;
+ SPClear(filtersToRestore);
+
+ [super dealloc];
+}
+
+@end
+
+#pragma mark -
+@implementation ContentPaginationViewController
+
+@synthesize page = page;
+@synthesize maxPage = maxPage;
+@synthesize target = target;
+@synthesize action = action;
+
+- (instancetype)init
+{
+ if((self = [super initWithNibName:@"ContentPaginationView" bundle:nil])) {
+ [self setPage:@1];
+ [self setMaxPage:@1];
+ }
+ return self;
+}
+
+- (IBAction)paginationGoAction:(id)sender
+{
+ if(target && action) [target performSelector:action withObject:self];
+}
+
+- (void)makeInputFirstResponder
+{
+ [[paginationPageField window] makeFirstResponder:paginationPageField];
+}
+
+- (BOOL)isFirstResponderInside
+{
+ NSResponder *firstResponder = [[paginationPageField window] firstResponder];
+ return (
+ [firstResponder isKindOfClass:[NSView class]] &&
+ [(NSView *)firstResponder isDescendantOf:[self view]]
+ );
+}
+
+- (void)dealloc
+{
+ [self setPage:nil];
+ [self setMaxPage:nil];
[super dealloc];
}
diff --git a/Source/SPTableContentFilterController.h b/Source/SPTableContentFilterController.h
deleted file mode 100644
index 542f4bad..00000000
--- a/Source/SPTableContentFilterController.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// This class is a dummy.
-// It is only present because DBView.xib already references it, but the
-// code itself is still in another branch. This stub is used to avoid a warning
-// from the Nib loader, saying 'this class was not found and replaced with a NSObject'.
-
-#import <Foundation/Foundation.h>
-
-@class SPSplitView;
-@class SPTableData;
-@class SPDatabaseDocument;
-@class SPTablesList;
-
-@interface SPTableContentFilterController : NSObject {
- IBOutlet SPSplitView *contentSplitView;
- IBOutlet NSRuleEditor *filterRuleEditor;
- IBOutlet SPTableData *tableDataInstance;
- IBOutlet SPDatabaseDocument *tableDocumentInstance;
- IBOutlet SPTablesList *tablesListInstance;
-}
-
-@end
diff --git a/Source/SPTableContentFilterController.m b/Source/SPTableContentFilterController.m
deleted file mode 100644
index 39c0f722..00000000
--- a/Source/SPTableContentFilterController.m
+++ /dev/null
@@ -1,5 +0,0 @@
-#import "SPTableContentFilterController.h"
-
-@implementation SPTableContentFilterController
-
-@end
diff --git a/Source/SPTableFilterParser.m b/Source/SPTableFilterParser.m
index 93b976f8..ac408baf 100644
--- a/Source/SPTableFilterParser.m
+++ b/Source/SPTableFilterParser.m
@@ -109,8 +109,7 @@
[clause flushCachedRegexData];
// Escape % sign for format insertion ie if number of arguments is greater than 0
- if(numberOfArguments > 0)
- [clause replaceOccurrencesOfRegex:@"%" withString:@"%%"];
+ if(numberOfArguments > 0) [clause replaceOccurrencesOfRegex:@"%" withString:@"%%"];
[clause flushCachedRegexData];
// Replace placeholder ${} by %@
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index 409f85b4..16a3957d 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -185,7 +185,7 @@
503CDBB21ACDC204004F8A2F /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 503CDBB11ACDC204004F8A2F /* Quartz.framework */; };
505F568F1BCEE485007467DD /* SPFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 507FF1111BBCC57600104523 /* SPFunctions.m */; };
505F56901BCEE491007467DD /* SPOSInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 50EAB5B71A8FBB08008F627A /* SPOSInfo.m */; };
- 506CE9311A311C6C0039F736 /* SPTableContentFilterController.m in Sources */ = {isa = PBXBuildFile; fileRef = 506CE9301A311C6C0039F736 /* SPTableContentFilterController.m */; };
+ 506CE9311A311C6C0039F736 /* SPRuleFilterController.m in Sources */ = {isa = PBXBuildFile; fileRef = 506CE9301A311C6C0039F736 /* SPRuleFilterController.m */; };
507FF1121BBCC57600104523 /* SPFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 507FF1111BBCC57600104523 /* SPFunctions.m */; };
507FF1621BBF0D5000104523 /* SPTableCopyTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 112730551180788A000737FD /* SPTableCopyTest.m */; };
507FF2421BC33BBC00104523 /* SPOSInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 50EAB5B71A8FBB08008F627A /* SPOSInfo.m */; };
@@ -391,6 +391,8 @@
58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF57D0F3B4E9700518E8E /* SPTableData.m */; };
73F70A961E4E547500636550 /* SPJSONFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 73F70A951E4E547500636550 /* SPJSONFormatter.m */; };
8D15AC340486D014006FF6A4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; };
+ 9BE765EBBDFD2F121C13D274 /* SPFillView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE768F3989033CEDDC2027E /* SPFillView.m */; };
+ 9BE76F2886901784E4FD2321 /* SPFilterTableController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE76A3D5C9830E2F7738770 /* SPFilterTableController.m */; };
B51D6B9E114C310C0074704E /* toolbar-switch-to-table-triggers.png in Resources */ = {isa = PBXBuildFile; fileRef = B51D6B9D114C310C0074704E /* toolbar-switch-to-table-triggers.png */; };
B52460D70F8EF92300171639 /* SPArrayAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = B52460D40F8EF92300171639 /* SPArrayAdditions.m */; };
B52460D80F8EF92300171639 /* SPTextViewAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = B52460D60F8EF92300171639 /* SPTextViewAdditions.m */; };
@@ -411,6 +413,11 @@
B5E2C5FA0F2353B5007446E0 /* table-property.png in Resources */ = {isa = PBXBuildFile; fileRef = B5E2C5F90F2353B5007446E0 /* table-property.png */; };
B5E92F190F75B2D100012500 /* ExportDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = B5E92F170F75B2D100012500 /* ExportDialog.xib */; };
B5E92F1C0F75B2E800012500 /* SPExportController.m in Sources */ = {isa = PBXBuildFile; fileRef = B5E92F1B0F75B2E800012500 /* SPExportController.m */; };
+ BA6B043D20A4FB0B00B012E1 /* button_filter_active@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BA6B043920A4FB0A00B012E1 /* button_filter_active@2x.png */; };
+ BA6B043E20A4FB0B00B012E1 /* button_filter@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BA6B043C20A4FB0B00B012E1 /* button_filter@2x.png */; };
+ BA6B044120A4FEDC00B012E1 /* button_filter_active.png in Resources */ = {isa = PBXBuildFile; fileRef = BA6B043F20A4FEDB00B012E1 /* button_filter_active.png */; };
+ BA6B044220A4FEDC00B012E1 /* button_filter.png in Resources */ = {isa = PBXBuildFile; fileRef = BA6B044020A4FEDC00B012E1 /* button_filter.png */; };
+ BAC6BAF920A0D22400247837 /* FilterTableWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = BAC6BAF720A0D22400247837 /* FilterTableWindow.xib */; };
BC01BCCF104024BE006BDEE7 /* SPEncodingPopupAccessory.m in Sources */ = {isa = PBXBuildFile; fileRef = BC01BCCE104024BE006BDEE7 /* SPEncodingPopupAccessory.m */; };
BC05F1C5101241DF008A97F8 /* YRKSpinningProgressIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = BC05F1C4101241DF008A97F8 /* YRKSpinningProgressIndicator.m */; };
BC09D7DE12A786FB0030DB64 /* cancel-clicked-highlighted.png in Resources */ = {isa = PBXBuildFile; fileRef = BC09D7D812A786FB0030DB64 /* cancel-clicked-highlighted.png */; };
@@ -879,8 +886,8 @@
503B02C91AE82C5E0060CAB1 /* SPTableFilterParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTableFilterParser.m; sourceTree = "<group>"; };
503B02CE1AE95C2C0060CAB1 /* SPTableFilterParserTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTableFilterParserTest.m; sourceTree = "<group>"; };
503CDBB11ACDC204004F8A2F /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; };
- 506CE92F1A311C6C0039F736 /* SPTableContentFilterController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTableContentFilterController.h; sourceTree = "<group>"; };
- 506CE9301A311C6C0039F736 /* SPTableContentFilterController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTableContentFilterController.m; sourceTree = "<group>"; };
+ 506CE92F1A311C6C0039F736 /* SPRuleFilterController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPRuleFilterController.h; sourceTree = "<group>"; };
+ 506CE9301A311C6C0039F736 /* SPRuleFilterController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPRuleFilterController.m; sourceTree = "<group>"; };
507FF1101BBCC4C400104523 /* SPFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPFunctions.h; sourceTree = "<group>"; };
507FF1111BBCC57600104523 /* SPFunctions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFunctions.m; sourceTree = "<group>"; };
508022941BF7BA470052A9B2 /* English */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = English; path = English.lproj/SPQLPluginExportSettingsTemplate.html; sourceTree = "<group>"; };
@@ -1127,6 +1134,10 @@
73F70A941E4E547500636550 /* SPJSONFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPJSONFormatter.h; sourceTree = "<group>"; };
73F70A951E4E547500636550 /* SPJSONFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPJSONFormatter.m; sourceTree = "<group>"; };
8D15AC370486D014006FF6A4 /* Sequel Pro.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Sequel Pro.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 9BE768F3989033CEDDC2027E /* SPFillView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFillView.m; sourceTree = "<group>"; };
+ 9BE76A3D5C9830E2F7738770 /* SPFilterTableController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFilterTableController.m; sourceTree = "<group>"; };
+ 9BE76CC0CCE3A4A74E3E8D5E /* SPFillView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPFillView.h; sourceTree = "<group>"; };
+ 9BE76F9BF9BDA2921CDD05AF /* SPFilterTableController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPFilterTableController.h; sourceTree = "<group>"; };
B51D6B9D114C310C0074704E /* toolbar-switch-to-table-triggers.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-table-triggers.png"; sourceTree = "<group>"; };
B52460D30F8EF92300171639 /* SPArrayAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPArrayAdditions.h; sourceTree = "<group>"; };
B52460D40F8EF92300171639 /* SPArrayAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPArrayAdditions.m; sourceTree = "<group>"; };
@@ -1153,6 +1164,11 @@
B5E92F1A0F75B2E800012500 /* SPExportController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPExportController.h; sourceTree = "<group>"; };
B5E92F1B0F75B2E800012500 /* SPExportController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPExportController.m; sourceTree = "<group>"; };
B5EAC0FC0EC87FF900CC579C /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
+ BA6B043920A4FB0A00B012E1 /* button_filter_active@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "button_filter_active@2x.png"; sourceTree = "<group>"; };
+ BA6B043C20A4FB0B00B012E1 /* button_filter@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "button_filter@2x.png"; sourceTree = "<group>"; };
+ BA6B043F20A4FEDB00B012E1 /* button_filter_active.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = button_filter_active.png; sourceTree = "<group>"; };
+ BA6B044020A4FEDC00B012E1 /* button_filter.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = button_filter.png; sourceTree = "<group>"; };
+ BAC6BAF820A0D22400247837 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/FilterTableWindow.xib; sourceTree = "<group>"; };
BC01BCCD104024BE006BDEE7 /* SPEncodingPopupAccessory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEncodingPopupAccessory.h; sourceTree = "<group>"; };
BC01BCCE104024BE006BDEE7 /* SPEncodingPopupAccessory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEncodingPopupAccessory.m; sourceTree = "<group>"; };
BC05F1C3101241DF008A97F8 /* YRKSpinningProgressIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YRKSpinningProgressIndicator.h; sourceTree = "<group>"; };
@@ -1552,11 +1568,13 @@
17A7773311C52D8E001E27B4 /* SPIndexesController.m */,
50A9F8AF19EAD4B90053E571 /* SPGotoDatabaseController.h */,
50A9F8B019EAD4B90053E571 /* SPGotoDatabaseController.m */,
- 506CE92F1A311C6C0039F736 /* SPTableContentFilterController.h */,
- 506CE9301A311C6C0039F736 /* SPTableContentFilterController.m */,
+ 506CE92F1A311C6C0039F736 /* SPRuleFilterController.h */,
+ 506CE9301A311C6C0039F736 /* SPRuleFilterController.m */,
1713C73D140D88D400CFD461 /* Query Controller */,
17381853151FB29C0078FFE2 /* User Manager */,
17846B9D170C95D800414499 /* Process List */,
+ 9BE76A3D5C9830E2F7738770 /* SPFilterTableController.m */,
+ 9BE76F9BF9BDA2921CDD05AF /* SPFilterTableController.h */,
);
name = "Subview Controllers";
sourceTree = "<group>";
@@ -1971,6 +1989,8 @@
17DC8827126B32F300E9AAEC /* Table Views */,
17DF51241163C68600E3F396 /* Outline Views */,
17DC8828126B332F00E9AAEC /* Accessory Views */,
+ 9BE768F3989033CEDDC2027E /* SPFillView.m */,
+ 9BE76CC0CCE3A4A74E3E8D5E /* SPFillView.h */,
);
name = Views;
sourceTree = "<group>";
@@ -2014,18 +2034,6 @@
17E6418B0EF01FF7001BC333 /* Images */ = {
isa = PBXGroup;
children = (
- 17BFC80D10D3FF5200A3B112 /* Icons */,
- 17B548661E82B02100175D5A /* button_bar_handle@2x.png */,
- 17B548671E82B02100175D5A /* button_duplicate@2x.png */,
- 17B548681E82B02100175D5A /* button_edit_mode_selected@2x.png */,
- 17B548691E82B02100175D5A /* button_edit_mode@2x.png */,
- 17B5486A1E82B02100175D5A /* button_edit@2x.png */,
- 17B5486B1E82B02100175D5A /* button_left@2x.png */,
- 17B5486C1E82B02100175D5A /* button_pagination@2x.png */,
- 17B5486D1E82B02100175D5A /* button_remove@2x.png */,
- 17B5486E1E82B02100175D5A /* button_right@2x.png */,
- 17B5486F1E82B02100175D5A /* button_select_all@2x.png */,
- 17B548701E82B02100175D5A /* button_select_none@2x.png */,
C9AD7C8116761B3300234EEE /* button_action.png */,
C9AD7C8216761B3300234EEE /* button_action@2x.png */,
582E93F5168298C6003459FD /* button_add_folder.png */,
@@ -2033,20 +2041,31 @@
582E93FF168299E3003459FD /* button_add.png */,
3876E14F1CC0B26000D85154 /* button_add@2x.png */,
582E940116829B0A003459FD /* button_bar_handle.png */,
+ 17B548661E82B02100175D5A /* button_bar_handle@2x.png */,
C9C9943F1678A439001F5DA8 /* button_bar_spacer.png */,
582E940D1682A2AD003459FD /* button_bar_spacer@2x.png */,
582E94151682A463003459FD /* button_clear.png */,
17AD35F41E79888D000F213E /* button_clear@2x.png */,
582E94171682A4B4003459FD /* button_duplicate.png */,
- 582E942316835EA9003459FD /* button_edit.png */,
- 582E941E16835DD4003459FD /* button_edit_mode.png */,
+ 17B548671E82B02100175D5A /* button_duplicate@2x.png */,
582E941D16835DD4003459FD /* button_edit_mode_selected.png */,
+ 17B548681E82B02100175D5A /* button_edit_mode_selected@2x.png */,
+ 582E941E16835DD4003459FD /* button_edit_mode.png */,
+ 17B548691E82B02100175D5A /* button_edit_mode@2x.png */,
+ 582E942316835EA9003459FD /* button_edit.png */,
+ 17B5486A1E82B02100175D5A /* button_edit@2x.png */,
+ BA6B043F20A4FEDB00B012E1 /* button_filter_active.png */,
+ BA6B043920A4FB0A00B012E1 /* button_filter_active@2x.png */,
+ BA6B044020A4FEDC00B012E1 /* button_filter.png */,
+ BA6B043C20A4FB0B00B012E1 /* button_filter@2x.png */,
C9AD7C851676204300234EEE /* button_info_pane_hide.png */,
C9AD7C861676204300234EEE /* button_info_pane_hide@2x.png */,
C9AD7C871676204300234EEE /* button_info_pane_show.png */,
C9AD7C881676204300234EEE /* button_info_pane_show@2x.png */,
58C3506410B9A56C00D37E14 /* button_left.png */,
+ 17B5486B1E82B02100175D5A /* button_left@2x.png */,
58C3506A10B9AA8B00D37E14 /* button_pagination.png */,
+ 17B5486C1E82B02100175D5A /* button_pagination@2x.png */,
C9C9943B1678A117001F5DA8 /* button_pane_hide.png */,
C9C9943C1678A117001F5DA8 /* button_pane_hide@2x.png */,
C9C994431678A5CD001F5DA8 /* button_pane_show.png */,
@@ -2054,42 +2073,47 @@
C9AD7C7D167619B400234EEE /* button_refresh.png */,
C9AD7C7E167619B400234EEE /* button_refresh@2x.png */,
582E942516835FAE003459FD /* button_remove.png */,
+ 17B5486D1E82B02100175D5A /* button_remove@2x.png */,
58C3506610B9A57300D37E14 /* button_right.png */,
+ 17B5486E1E82B02100175D5A /* button_right@2x.png */,
582E94291683628C003459FD /* button_select_all.png */,
+ 17B5486F1E82B02100175D5A /* button_select_all@2x.png */,
582E942A1683628C003459FD /* button_select_none.png */,
- BC09D7DD12A786FB0030DB64 /* cancel.png */,
- BC09D7D912A786FB0030DB64 /* cancel-clicked.png */,
+ 17B548701E82B02100175D5A /* button_select_none@2x.png */,
BC09D7D812A786FB0030DB64 /* cancel-clicked-highlighted.png */,
+ BC09D7D912A786FB0030DB64 /* cancel-clicked.png */,
BC09D7DA12A786FB0030DB64 /* cancel-highlighted.png */,
- BC09D7DC12A786FB0030DB64 /* cancel-hovered.png */,
BC09D7DB12A786FB0030DB64 /* cancel-hovered-highlighted.png */,
+ BC09D7DC12A786FB0030DB64 /* cancel-hovered.png */,
+ BC09D7DD12A786FB0030DB64 /* cancel.png */,
582E942D1683658A003459FD /* clearconsole.png */,
- B577483A0F7A8B57003B34F9 /* database.png */,
177E792B0FCB54EC00E9E122 /* database-small.png */,
C9AD7C771676138000234EEE /* database-small@2x.png */,
+ B577483A0F7A8B57003B34F9 /* database.png */,
177E792C0FCB54EC00E9E122 /* dummy-small.png */,
582E9449168374C1003459FD /* field-small-square.png */,
384582C30FB95FF800DDACB6 /* func-small.png */,
17E6419D0EF02036001BC333 /* grabber-horizontal.png */,
17E6419E0EF02036001BC333 /* grabber-vertical.png */,
582E944B16837986003459FD /* hideconsole.png */,
+ 17BFC80D10D3FF5200A3B112 /* Icons */,
5843DA68161FA35600EAA6D1 /* key-icon-alternate.png */,
5843DA69161FA35600EAA6D1 /* key-icon-alternate@2x.png */,
5843DA6A161FA35600EAA6D1 /* key-icon.png */,
5843DA6B161FA35600EAA6D1 /* key-icon@2x.png */,
58E205FB1234FE4F00A97059 /* KeyTemplate.pdf */,
- 58D2E22D101222870063EF1D /* link-arrow.png */,
- 582E939C168296F3003459FD /* link-arrow@2x.png */,
58D2E22B101222870063EF1D /* link-arrow-clicked.png */,
582E9399168296F3003459FD /* link-arrow-clicked@2x.png */,
- 581068B51015411B0068C6E2 /* link-arrow-highlighted.png */,
- 582E939B168296F3003459FD /* link-arrow-highlighted@2x.png */,
58D2E22C101222870063EF1D /* link-arrow-highlighted-clicked.png */,
582E939A168296F3003459FD /* link-arrow-highlighted-clicked@2x.png */,
+ 581068B51015411B0068C6E2 /* link-arrow-highlighted.png */,
+ 582E939B168296F3003459FD /* link-arrow-highlighted@2x.png */,
+ 58D2E22D101222870063EF1D /* link-arrow.png */,
+ 582E939C168296F3003459FD /* link-arrow@2x.png */,
582E944F16837AA9003459FD /* network-small.png */,
384582C60FB9603600DDACB6 /* proc-small.png */,
- 58F48AA2161D03C6008536A1 /* quick-connect-icon.pdf */,
58F48B2D161D08C0008536A1 /* quick-connect-icon-white.pdf */,
+ 58F48AA2161D03C6008536A1 /* quick-connect-icon.pdf */,
50F530511ABCF66B002F2C1A /* reset.pdf */,
582E946F16837DB2003459FD /* showconsole.png */,
588B2CC50FE5641E00EC5FC0 /* ssh-connected.png */,
@@ -2102,12 +2126,12 @@
582E9481168380D6003459FD /* sync_arrows_05.png */,
582E9482168380D6003459FD /* sync_arrows_06.png */,
B5E2C5F90F2353B5007446E0 /* table-property.png */,
- C9C9944B1678BCFA001F5DA8 /* table-small.png */,
- C9C9944C1678BCFA001F5DA8 /* table-small@2x.png */,
C9C994471678B3E6001F5DA8 /* table-small-square.png */,
C9C994481678B3E6001F5DA8 /* table-small-square@2x.png */,
- 582E948E168383F0003459FD /* table-view-small.png */,
+ C9C9944B1678BCFA001F5DA8 /* table-small.png */,
+ C9C9944C1678BCFA001F5DA8 /* table-small@2x.png */,
582E948D168383F0003459FD /* table-view-small-square.png */,
+ 582E948E168383F0003459FD /* table-view-small.png */,
58A137CC123ED5E6000B1B75 /* titlebarlock.png */,
582E94A716839AD5003459FD /* toolbar-preferences-autoupdate.png */,
582E94A916839AEF003459FD /* toolbar-preferences-general.png */,
@@ -2158,6 +2182,7 @@
BCCBD7FA104408B000D4C50A /* SaveSPFAccessory.xib */,
B58DA7390FF8BB9E00FDDACD /* SSHQuestionDialog.xib */,
4D90B7A0101E0D1500D116A1 /* UserManagerView.xib */,
+ BAC6BAF720A0D22400247837 /* FilterTableWindow.xib */,
);
path = Interfaces;
sourceTree = "<group>";
@@ -2829,6 +2854,7 @@
17E641F20EF02036001BC333 /* toolbar-switch-to-structure.png in Resources */,
17E641F30EF02036001BC333 /* toolbar-switch-to-table-info.png in Resources */,
17E6420A0EF020CB001BC333 /* DBView.xib in Resources */,
+ BA6B043D20A4FB0B00B012E1 /* button_filter_active@2x.png in Resources */,
17E641FA0EF02088001BC333 /* Growl Registration Ticket.growlRegDict in Resources */,
17E641FC0EF02088001BC333 /* sequel-pro.scriptSuite in Resources */,
3876E1501CC0B26000D85154 /* button_add@2x.png in Resources */,
@@ -2847,6 +2873,7 @@
BC2C8E220FA8C2DB008468C7 /* SPMySQLHelpTemplate.html in Resources */,
384582C40FB95FF800DDACB6 /* func-small.png in Resources */,
384582C70FB9603600DDACB6 /* proc-small.png in Resources */,
+ BAC6BAF920A0D22400247837 /* FilterTableWindow.xib in Resources */,
177E792E0FCB54EC00E9E122 /* database-small.png in Resources */,
177E792F0FCB54EC00E9E122 /* dummy-small.png in Resources */,
B58DA73C0FF8BBA500FDDACD /* PrintAccessory.xib in Resources */,
@@ -2860,6 +2887,7 @@
BCA6F631100FA7D700E80253 /* FieldEditorSheet.xib in Resources */,
58D2E22E101222870063EF1D /* link-arrow-clicked.png in Resources */,
58D2E22F101222870063EF1D /* link-arrow-highlighted-clicked.png in Resources */,
+ BA6B044120A4FEDC00B012E1 /* button_filter_active.png in Resources */,
58D2E230101222870063EF1D /* link-arrow.png in Resources */,
17B548781E82B02100175D5A /* button_remove@2x.png in Resources */,
581068B61015411B0068C6E2 /* link-arrow-highlighted.png in Resources */,
@@ -2913,7 +2941,9 @@
5843DA6E161FA35600EAA6D1 /* key-icon.png in Resources */,
5843DA6F161FA35600EAA6D1 /* key-icon@2x.png in Resources */,
C9F92710162D38D70051CB2E /* toolbar-switch-to-table-info@2x.png in Resources */,
+ BA6B044220A4FEDC00B012E1 /* button_filter.png in Resources */,
C9F92712162D39E60051CB2E /* toolbar-switch-to-browse.png in Resources */,
+ BA6B043E20A4FB0B00B012E1 /* button_filter@2x.png in Resources */,
C9F92714162D39FE0051CB2E /* toolbar-switch-to-browse@2x.png in Resources */,
17B5487B1E82B02100175D5A /* button_select_none@2x.png in Resources */,
C9AD7C781676138000234EEE /* database-small@2x.png in Resources */,
@@ -3233,7 +3263,7 @@
1785EB6A127DD79300F468C8 /* SPEditorPreferencePane.m in Sources */,
17FDB04C1280778B00DBBBC2 /* SPFontPreviewTextField.m in Sources */,
17D3C22212859E070047709F /* SPFavoriteNode.m in Sources */,
- 506CE9311A311C6C0039F736 /* SPTableContentFilterController.m in Sources */,
+ 506CE9311A311C6C0039F736 /* SPRuleFilterController.m in Sources */,
17D3C66E128AD4710047709F /* SPFavoritesController.m in Sources */,
17D3C671128AD8160047709F /* SPSingleton.m in Sources */,
17D3C6D3128B1C900047709F /* SPFavoritesOutlineView.m in Sources */,
@@ -3267,6 +3297,8 @@
50E217B318174246009D3580 /* SPColorSelectorView.m in Sources */,
50E217B618174280009D3580 /* SPFavoriteColorSupport.m in Sources */,
1A564F74237E2E4958CA593A /* SPPillAttachmentCell.m in Sources */,
+ 9BE76F2886901784E4FD2321 /* SPFilterTableController.m in Sources */,
+ 9BE765EBBDFD2F121C13D274 /* SPFillView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3542,6 +3574,14 @@
name = ExportDialog.xib;
sourceTree = "<group>";
};
+ BAC6BAF720A0D22400247837 /* FilterTableWindow.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ BAC6BAF820A0D22400247837 /* English */,
+ );
+ name = FilterTableWindow.xib;
+ sourceTree = "<group>";
+ };
BC30C00F111C98BD002701C9 /* DataMigrationDialog.xib */ = {
isa = PBXVariantGroup;
children = (