diff options
-rw-r--r-- | Interfaces/English.lproj/DBView.xib | 318 | ||||
-rw-r--r-- | Interfaces/English.lproj/FilterTableWindow.xib | 337 | ||||
-rw-r--r-- | Source/SPFilterTableController.h | 129 | ||||
-rw-r--r-- | Source/SPFilterTableController.m | 698 | ||||
-rw-r--r-- | Source/SPHistoryController.m | 2 | ||||
-rw-r--r-- | Source/SPTableContent.h | 34 | ||||
-rw-r--r-- | Source/SPTableContent.m | 877 | ||||
-rw-r--r-- | sequel-pro.xcodeproj/project.pbxproj | 18 |
8 files changed, 1352 insertions, 1061 deletions
diff --git a/Interfaces/English.lproj/DBView.xib b/Interfaces/English.lproj/DBView.xib index 042c1381..a725d186 100644 --- a/Interfaces/English.lproj/DBView.xib +++ b/Interfaces/English.lproj/DBView.xib @@ -4187,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="125"/> - <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"/> @@ -4903,18 +4599,7 @@ Gw <outlet property="filterButton" destination="4676" id="5942"/> <outlet property="filterControllerInstance" destination="ki9-Po-bdr" id="5od-0U-9xj"/> <outlet property="filterRuleEditorContainer" destination="9oo-u0-ndW" id="OZb-jI-CMy"/> - <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="filterTableController" destination="UBS-cN-3Af" id="FV7-by-Gy1"/> <outlet property="multipleLineEditingButton" destination="5201" id="5203"/> <outlet property="paginationButton" destination="6653" id="6657"/> <outlet property="paginationNextButton" destination="6647" id="6664"/> @@ -4941,6 +4626,7 @@ Gw <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"/> diff --git a/Interfaces/English.lproj/FilterTableWindow.xib b/Interfaces/English.lproj/FilterTableWindow.xib new file mode 100644 index 00000000..79da4153 --- /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" 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 Criterias (⌘⌫)" 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/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..dc1bf98a --- /dev/null +++ b/Source/SPFilterTableController.m @@ -0,0 +1,698 @@ +// +// 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) { + // 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]; + [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"]]; + } + } + + [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 c39421a9..8a0554fe 100644 --- a/Source/SPHistoryController.m +++ b/Source/SPHistoryController.m @@ -250,7 +250,7 @@ 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]; if (!theDatabase) return; // If a table is selected, save state information diff --git a/Source/SPTableContent.h b/Source/SPTableContent.h index d47dd467..c8f7100a 100644 --- a/Source/SPTableContent.h +++ b/Source/SPTableContent.h @@ -43,10 +43,8 @@ @class SPDatabaseDocument; @class SPTablesList; @class SPTableStructure; -#ifndef SP_CODA -@class SPSplitView; -#endif @class SPTableContentFilterController; +@class SPFilterTableController; typedef NS_ENUM(NSInteger, SPTableContentFilterSource) { SPTableContentFilterSourceNone = -1, @@ -102,21 +100,8 @@ typedef NS_ENUM(NSInteger, SPTableContentFilterSource) { #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; - IBOutlet SPTableContentFilterController *filterControllerInstance; + IBOutlet SPFilterTableController *filterTableController; #endif SPMySQLConnection *mySQLConnection; @@ -140,12 +125,6 @@ typedef NS_ENUM(NSInteger, SPTableContentFilterSource) { NSUInteger contentPage; #ifndef SP_CODA - NSMutableDictionary *filterTableData; - BOOL filterTableNegate; - BOOL filterTableDistinct; - BOOL filterTableIsSwapped; - NSString *filterTableDefaultOperator; - NSString *lastEditedFilterTableValue; SPTableContentFilterSource activeFilter; NSString *schemeFilter; #endif @@ -243,14 +222,7 @@ typedef NS_ENUM(NSInteger, SPTableContentFilterSource) { - (void)removeRowSheetDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(NSString *)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; @@ -309,7 +281,5 @@ typedef NS_ENUM(NSInteger, SPTableContentFilterSource) { #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 934745f6..8d1a19a3 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -40,9 +40,6 @@ #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" @@ -60,6 +57,7 @@ #import "SPTableFilterParser.h" #import "SPFunctions.h" #import "SPTableContentFilterController.h" +#import "SPFilterTableController.h" #import <pthread.h> #import <SPMySQL/SPMySQL.h> @@ -71,10 +69,6 @@ */ static void *TableContentKVOContext = &TableContentKVOContext; -#ifndef SP_CODA -static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOperator"; -#endif - @interface SPTableContent () - (BOOL)cancelRowEditing; @@ -127,18 +121,11 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper 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 = SPTableContentFilterSourceNone; schemeFilter = nil; paginationPopover = nil; @@ -198,7 +185,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper // 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:)]; @@ -238,25 +224,10 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper [paginationView setFrame:paginationViewFrame]; [contentViewPane 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]; @@ -273,6 +244,9 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper object:contentAreaContainer]; [filterControllerInstance setTarget:self]; [filterControllerInstance setAction:@selector(filterTable:)]; + + [filterTableController setTarget:self]; + [filterTableController setAction:@selector(filterTable:)]; // Add observers for document task activity [[NSNotificationCenter defaultCenter] addObserver:self @@ -406,14 +380,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper [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:@""]; + [filterTableController setColumns:nil]; activeFilter = SPTableContentFilterSourceNone; #endif } @@ -432,10 +399,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] || @@ -511,14 +476,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:@""]; + [filterTableController setColumns:nil]; // TODO code smell //...but keep it, if the rule filter is the active one if(activeFilter != SPTableContentFilterSourceRuleFilter) activeFilter = SPTableContentFilterSourceNone; @@ -557,7 +515,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 @@ -578,24 +536,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"]) { @@ -665,12 +605,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]]; @@ -700,6 +634,7 @@ 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) [filterControllerInstance updateFiltersFrom:self]; // Restore preserved filter settings if appropriate and valid @@ -732,10 +667,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper if (!previousTableRowsCount) { [self clearTableValues]; } -#ifndef SP_CODA - [filterTableView reloadData]; -#endif - } - (NSString *)selectedTable @@ -785,17 +716,17 @@ 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 == SPTableContentFilterSourceTableFilter && [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 length]) { [queryString appendFormat:@" WHERE %@", filterString]; isFiltered = YES; @@ -983,7 +914,9 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper #ifndef SP_CODA // Filter task came from filter table else if(activeFilter == SPTableContentFilterSourceTableFilter) { - [filterTableWindow setTitle:[NSString stringWithFormat:@"%@ – %@", NSLocalizedString(@"Filter", @"filter label"), NSLocalizedString(@"WHERE clause not valid", @"WHERE clause not valid")]]; + [[filterTableController onMainThread] setFilterError:[mySQLConnection lastErrorID] + message:[mySQLConnection lastErrorMessage] + sqlstate:[mySQLConnection lastSqlstate]]; } } #endif @@ -992,7 +925,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 } } @@ -1069,18 +1002,8 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper } // Call did come from filter table and is filter table window still open? - if(activeFilter == SPTableContentFilterSourceTableFilter && [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(activeFilter == SPTableContentFilterSourceRuleFilter && showFilterRuleEditor) { @@ -1317,7 +1240,7 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper #ifndef SP_CODA // If the filter table is being used - the advanced filter - switch type - if(sender == filterTableFilterButton) { + if(sender == filterTableController) { activeFilter = SPTableContentFilterSourceTableFilter; } @@ -3054,46 +2977,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) @@ -3290,160 +3174,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 - @@ -3664,29 +3399,14 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper [self setFiltersToRestore:nil]; } -- (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; - - [filterTableView deselectAll:nil]; - - return [NSArchiver archivedDataWithRootObject:filterTableData]; -#else - return nil; -#endif + [filterTableController setFilterTableData:arcData]; } #pragma mark - @@ -3916,7 +3636,6 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper // 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]) { @@ -3982,11 +3701,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; } @@ -3997,20 +3711,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; @@ -4081,20 +3781,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 @@ -4186,187 +3872,6 @@ 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 @@ -4546,135 +4051,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; + if (tableView == tableContentView) { - // 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]]]; + // 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 (![wherePart length]) 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 the selected cell hasn't been loaded, load it. - if ([[tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]] isSPNotLoaded]) { + if (![wherePart length]) 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]; + // If the selected cell hasn't been loaded, load it. + if ([[tableValues cellDataAtRow:rowIndex column:[[tableColumn identifier] integerValue]] isSPNotLoaded]) { - SPMySQLResult *tempResult = [mySQLConnection queryString:query]; + // 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 (![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; - } + SPMySQLResult *tempResult = [mySQLConnection queryString:query]; - NSArray *tempRow = [tempResult getRowAsArray]; - - [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; } @@ -4714,13 +4212,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; } /** @@ -4775,76 +4267,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; + } - // 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 (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 @@ -4857,10 +4335,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; @@ -4923,12 +4398,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; } @@ -4968,24 +4442,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 @@ -5128,13 +4584,10 @@ static NSString *SPTableFilterSetDefaultOperator = @"SPTableFilterSetDefaultOper SPClear(dataColumns); SPClear(oldRow); #ifndef SP_CODA - for (id retainedObject in nibObjectsToRelease) [retainedObject release]; + for (id retainedObject in nibObjectsToRelease) [retainedObject release]; SPClear(nibObjectsToRelease); SPClear(paginationPopover); - SPClear(filterTableData); - if (lastEditedFilterTableValue) SPClear(lastEditedFilterTableValue); - if (filterTableDefaultOperator) SPClear(filterTableDefaultOperator); #endif if (selectedTable) SPClear(selectedTable); if (keys) SPClear(keys); diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj index 409f85b4..79afe4e7 100644 --- a/sequel-pro.xcodeproj/project.pbxproj +++ b/sequel-pro.xcodeproj/project.pbxproj @@ -391,6 +391,7 @@ 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 */; }; + 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 +412,7 @@ 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 */; }; + 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 */; }; @@ -1127,6 +1129,8 @@ 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; }; + 9BE76A3D5C9830E2F7738770 /* SPFilterTableController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFilterTableController.m; 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 +1157,7 @@ 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; }; + 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>"; }; @@ -1557,6 +1562,8 @@ 1713C73D140D88D400CFD461 /* Query Controller */, 17381853151FB29C0078FFE2 /* User Manager */, 17846B9D170C95D800414499 /* Process List */, + 9BE76A3D5C9830E2F7738770 /* SPFilterTableController.m */, + 9BE76F9BF9BDA2921CDD05AF /* SPFilterTableController.h */, ); name = "Subview Controllers"; sourceTree = "<group>"; @@ -2158,6 +2165,7 @@ BCCBD7FA104408B000D4C50A /* SaveSPFAccessory.xib */, B58DA7390FF8BB9E00FDDACD /* SSHQuestionDialog.xib */, 4D90B7A0101E0D1500D116A1 /* UserManagerView.xib */, + BAC6BAF720A0D22400247837 /* FilterTableWindow.xib */, ); path = Interfaces; sourceTree = "<group>"; @@ -2847,6 +2855,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 */, @@ -3267,6 +3276,7 @@ 50E217B318174246009D3580 /* SPColorSelectorView.m in Sources */, 50E217B618174280009D3580 /* SPFavoriteColorSupport.m in Sources */, 1A564F74237E2E4958CA593A /* SPPillAttachmentCell.m in Sources */, + 9BE76F2886901784E4FD2321 /* SPFilterTableController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3542,6 +3552,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 = ( |