aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m1
-rw-r--r--Interfaces/English.lproj/DBView.xib196
-rw-r--r--Interfaces/English.lproj/HelpViewer.xib213
-rw-r--r--Resources/Templates/SPMySQLHelpTemplate.html19
-rw-r--r--Source/SPCustomQuery.h40
-rw-r--r--Source/SPCustomQuery.m531
-rw-r--r--Source/SPDatabaseDocument.h4
-rw-r--r--Source/SPDatabaseDocument.m14
-rw-r--r--Source/SPHelpViewerClient.h66
-rw-r--r--Source/SPHelpViewerClient.m262
-rw-r--r--Source/SPHelpViewerController.h99
-rw-r--r--Source/SPHelpViewerController.m415
-rw-r--r--Source/SPTextView.m3
-rw-r--r--sequel-pro.xcodeproj/project.pbxproj24
14 files changed, 1130 insertions, 757 deletions
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
index ce52f397..16ff22f3 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
@@ -100,6 +100,7 @@
NSData *escapedData;
if (includeQuotes) {
+#warning This code assumes that the encoding cData is in is still ASCII-compatible which may not be the case (e.g. for UTF16, EBCDIC)
// Add quotes if requested
escBuffer[0] = '\'';
escBuffer[escapedLength+1] = '\'';
diff --git a/Interfaces/English.lproj/DBView.xib b/Interfaces/English.lproj/DBView.xib
index 6d2e2cbd..ac4c5f78 100644
--- a/Interfaces/English.lproj/DBView.xib
+++ b/Interfaces/English.lproj/DBView.xib
@@ -4,7 +4,6 @@
<deployment identifier="macosx"/>
<development version="8000" identifier="xcode"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14109"/>
- <plugIn identifier="com.apple.WebKitIBPlugin" version="14109"/>
<capability name="box content view" minToolsVersion="7.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -38,6 +37,7 @@
<outlet property="documentActivityScrollView" destination="7691" id="7790"/>
<outlet property="exportControllerInstance" destination="5377" id="6619"/>
<outlet property="extendedTableInfoInstance" destination="1277" id="5848"/>
+ <outlet property="helpViewerClientInstance" destination="AxW-sg-bzq" id="QCc-bz-uzm"/>
<outlet property="historyControl" destination="6293" id="6296"/>
<outlet property="inputTextWindow" destination="6492" id="6504"/>
<outlet property="inputTextWindowHeader" destination="6497" id="6505"/>
@@ -4003,190 +4003,6 @@ Gw
<outlet property="initialFirstResponder" destination="964" id="6813"/>
</connections>
</window>
- <window title="MySQL Help" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="MYSQL_HELP_WINDOW" animationBehavior="default" id="5428" userLabel="Help Panel" customClass="NSPanel">
- <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
- <windowPositionMask key="initialPositionMask" leftStrut="YES" bottomStrut="YES"/>
- <rect key="contentRect" x="415" y="136" width="505" height="308"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
- <value key="minSize" type="size" width="351" height="120"/>
- <view key="contentView" id="5429">
- <rect key="frame" x="0.0" y="0.0" width="505" height="308"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <webView id="5441">
- <rect key="frame" x="0.0" y="0.0" width="505" height="271"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <webPreferences key="preferences" defaultFontSize="16" defaultFixedFontSize="13" minimumFontSize="0">
- <nil key="identifier"/>
- </webPreferences>
- <connections>
- <outlet property="UIDelegate" destination="134" id="5508"/>
- <outlet property="nextKeyView" destination="5474" id="5528"/>
- <outlet property="policyDelegate" destination="134" id="5451"/>
- </connections>
- </webView>
- <searchField verticalHuggingPriority="750" id="5452">
- <rect key="frame" x="122" y="280" width="218" height="19"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
- <searchFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" selectable="YES" editable="YES" borderStyle="bezel" placeholderString="Search" usesSingleLineMode="YES" bezelStyle="round" sendsWholeSearchString="YES" id="5453">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
- </searchFieldCell>
- <connections>
- <action selector="showHelpForSearchString:" target="134" id="5455"/>
- <outlet property="nextKeyView" destination="5510" id="5526"/>
- </connections>
- </searchField>
- <button verticalHuggingPriority="750" id="5464">
- <rect key="frame" x="81" y="-91" width="77" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="larger" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5465">
- <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="makeTextLarger:" target="5441" id="5469"/>
- </connections>
- </button>
- <button verticalHuggingPriority="750" id="5466">
- <rect key="frame" x="150" y="-91" width="86" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="smaller" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5467">
- <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="makeTextSmaller:" target="5441" id="5468"/>
- </connections>
- </button>
- <segmentedControl verticalHuggingPriority="750" id="5474">
- <rect key="frame" x="11" y="280" width="104" height="20"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <segmentedCell key="cell" borderStyle="border" alignment="left" style="roundRect" trackingMode="momentary" id="5475">
- <font key="font" metaFont="titleBar" size="12"/>
- <segments>
- <segment toolTip="Show the previous page" image="NSLeftFacingTriangleTemplate" width="32">
- <nil key="label"/>
- </segment>
- <segment toolTip="MySQL Table of Contents" image="NSListViewTemplate" width="32" tag="1"/>
- <segment toolTip="Show the next page" image="NSRightFacingTriangleTemplate" width="32"/>
- </segments>
- </segmentedCell>
- <connections>
- <action selector="helpSegmentDispatcher:" target="134" id="5524"/>
- <outlet property="nextKeyView" destination="5452" id="5529"/>
- </connections>
- </segmentedControl>
- <button verticalHuggingPriority="750" id="5497">
- <rect key="frame" x="157" y="-48" width="132" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="focusToSearch" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5498">
- <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="selectText:" target="5452" id="5506"/>
- </connections>
- </button>
- <button verticalHuggingPriority="750" id="5500">
- <rect key="frame" x="71" y="-48" width="97" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="FindNext" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5501">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent">g</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="helpSearchFindNextInPage:" target="134" id="5502"/>
- </connections>
- </button>
- <button verticalHuggingPriority="750" id="5503">
- <rect key="frame" x="72" y="-70" width="96" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="FindPrev" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5504">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent">G</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="helpSearchFindPreviousInPage:" target="134" id="5505"/>
- </connections>
- </button>
- <box verticalHuggingPriority="750" boxType="separator" id="5509">
- <rect key="frame" x="0.0" y="269" width="505" height="5"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
- </box>
- <segmentedControl verticalHuggingPriority="750" id="5510">
- <rect key="frame" x="347" y="279" width="152" height="20"/>
- <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
- <segmentedCell key="cell" borderStyle="border" alignment="left" style="roundRect" trackingMode="selectOne" id="5511">
- <font key="font" metaFont="smallSystem"/>
- <segments>
- <segment label="MySQL" toolTip="Search in MySQL Help [⇧⌘M]" width="48" selected="YES"/>
- <segment label="Page" toolTip="Search in current page [⇧⌘P]" width="48" tag="1"/>
- <segment label="Web" toolTip="Search in the online documentation [⇧⌘W]" width="48" tag="2"/>
- </segments>
- </segmentedCell>
- <connections>
- <action selector="helpTargetDispatcher:" target="134" id="5513"/>
- <outlet property="nextKeyView" destination="5441" id="5527"/>
- </connections>
- </segmentedControl>
- <button verticalHuggingPriority="750" id="5514">
- <rect key="frame" x="129" y="-50" width="96" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="Web" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5515">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent">W</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="helpSelectHelpTargetWeb:" target="134" id="5523"/>
- </connections>
- </button>
- <button verticalHuggingPriority="750" id="5517">
- <rect key="frame" x="220" y="-50" width="96" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="page" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5518">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent">P</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="helpSelectHelpTargetPage:" target="134" id="5522"/>
- </connections>
- </button>
- <button verticalHuggingPriority="750" id="5519">
- <rect key="frame" x="308" y="-50" width="96" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <buttonCell key="cell" type="push" title="mysql" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5520">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent">M</string>
- <modifierMask key="keyEquivalentModifierMask" command="YES"/>
- </buttonCell>
- <connections>
- <action selector="helpSelectHelpTargetMySQL:" target="134" id="5521"/>
- </connections>
- </button>
- </subviews>
- </view>
- <connections>
- <outlet property="delegate" destination="134" id="7715"/>
- <outlet property="initialFirstResponder" destination="5452" id="5525"/>
- </connections>
- </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"/>
@@ -4551,12 +4367,6 @@ Gw
<outlet property="errorText" destination="8248" id="8250"/>
<outlet property="errorTextScrollView" destination="8245" id="8249"/>
<outlet property="errorTextTitle" destination="7429" id="8334"/>
- <outlet property="helpNavigator" destination="5474" id="5478"/>
- <outlet property="helpSearchField" destination="5452" id="5454"/>
- <outlet property="helpSearchFieldCell" destination="5453" id="5496"/>
- <outlet property="helpTargetSelector" destination="5510" id="5512"/>
- <outlet property="helpWebView" destination="5441" id="5442"/>
- <outlet property="helpWebViewWindow" destination="5428" id="5433"/>
<outlet property="nextHistoryMenuItem" destination="7242" id="7368"/>
<outlet property="previousHistoryMenuItem" destination="7243" id="7369"/>
<outlet property="queryEditorSplitView" destination="7209" id="8023"/>
@@ -4846,6 +4656,7 @@ Gw
<outlet property="tablesList" destination="68" id="7075"/>
</connections>
</customObject>
+ <customObject id="AxW-sg-bzq" customClass="SPHelpViewerClient"/>
<menu id="6180" userLabel="Table List Menu">
<items>
<menuItem title="Rename Table..." toolTip="Rename the selected item" id="6186">
@@ -5178,9 +4989,6 @@ Gw
<image name="NSApplicationIcon" width="128" height="128"/>
<image name="NSGoLeftTemplate" width="9" height="12"/>
<image name="NSGoRightTemplate" width="9" height="12"/>
- <image name="NSLeftFacingTriangleTemplate" width="9" height="12"/>
- <image name="NSListViewTemplate" width="14" height="10"/>
- <image name="NSRightFacingTriangleTemplate" width="9" height="12"/>
<image name="button_action" width="30" height="22"/>
<image name="button_add" width="30" height="22"/>
<image name="button_bar_handle" width="15" height="23"/>
diff --git a/Interfaces/English.lproj/HelpViewer.xib b/Interfaces/English.lproj/HelpViewer.xib
new file mode 100644
index 00000000..4ec08105
--- /dev/null
+++ b/Interfaces/English.lproj/HelpViewer.xib
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14109"/>
+ <plugIn identifier="com.apple.WebKitIBPlugin" version="14109"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="SPHelpViewerController">
+ <connections>
+ <outlet property="helpNavigator" destination="9ni-sS-mn6" id="V5n-Pk-5gF"/>
+ <outlet property="helpSearchField" destination="oUd-9E-CbL" id="21a-wb-JFd"/>
+ <outlet property="helpSearchFieldCell" destination="MYp-fW-swr" id="UKu-OI-Qsg"/>
+ <outlet property="helpTargetSelector" destination="RYG-b9-3OO" id="zks-tj-Ne2"/>
+ <outlet property="helpWebView" destination="Zz1-fo-n30" id="LbJ-6e-dsh"/>
+ <outlet property="window" destination="YFf-V2-Qgx" id="S00-KE-3Zv"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <window title="MySQL Help" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="MYSQL_HELP_WINDOW" animationBehavior="default" id="YFf-V2-Qgx" userLabel="Help Panel" customClass="NSPanel">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="415" y="136" width="505" height="308"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
+ <value key="minSize" type="size" width="351" height="120"/>
+ <view key="contentView" id="3dn-pQ-caR">
+ <rect key="frame" x="0.0" y="0.0" width="505" height="308"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <webView id="Zz1-fo-n30">
+ <rect key="frame" x="0.0" y="0.0" width="505" height="271"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <webPreferences key="preferences" defaultFontSize="16" defaultFixedFontSize="13" minimumFontSize="0">
+ <nil key="identifier"/>
+ </webPreferences>
+ <connections>
+ <outlet property="UIDelegate" destination="-2" id="xcm-st-hpB"/>
+ <outlet property="nextKeyView" destination="9ni-sS-mn6" id="2y3-Am-slE"/>
+ <outlet property="policyDelegate" destination="-2" id="Te1-TF-CEj"/>
+ </connections>
+ </webView>
+ <searchField wantsLayer="YES" verticalHuggingPriority="750" id="oUd-9E-CbL">
+ <rect key="frame" x="122" y="280" width="218" height="19"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
+ <searchFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" selectable="YES" editable="YES" borderStyle="bezel" placeholderString="Search" usesSingleLineMode="YES" bezelStyle="round" sendsWholeSearchString="YES" id="MYp-fW-swr">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </searchFieldCell>
+ <connections>
+ <action selector="showHelpForSearchString:" target="-2" id="44Q-aY-bHW"/>
+ <outlet property="nextKeyView" destination="RYG-b9-3OO" id="EhW-Ao-i9c"/>
+ </connections>
+ </searchField>
+ <button verticalHuggingPriority="750" id="ndR-6a-tnv">
+ <rect key="frame" x="81" y="-91" width="77" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="larger" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="RHn-b0-51Z">
+ <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="makeTextLarger:" target="Zz1-fo-n30" id="bw1-Rn-Ooi"/>
+ </connections>
+ </button>
+ <button verticalHuggingPriority="750" id="Bb4-ZC-x93">
+ <rect key="frame" x="150" y="-91" width="86" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="smaller" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="82i-dp-u56">
+ <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="makeTextSmaller:" target="Zz1-fo-n30" id="srz-cY-2MG"/>
+ </connections>
+ </button>
+ <segmentedControl verticalHuggingPriority="750" id="9ni-sS-mn6">
+ <rect key="frame" x="11" y="280" width="104" height="20"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <segmentedCell key="cell" borderStyle="border" alignment="left" style="roundRect" trackingMode="momentary" id="f9j-uE-rFe">
+ <font key="font" metaFont="titleBar" size="12"/>
+ <segments>
+ <segment toolTip="Show the previous page" image="NSLeftFacingTriangleTemplate" width="32">
+ <nil key="label"/>
+ </segment>
+ <segment toolTip="MySQL Table of Contents" image="NSListViewTemplate" width="32" tag="1"/>
+ <segment toolTip="Show the next page" image="NSRightFacingTriangleTemplate" width="32"/>
+ </segments>
+ </segmentedCell>
+ <connections>
+ <action selector="helpSegmentDispatcher:" target="-2" id="4VA-mn-whM"/>
+ <outlet property="nextKeyView" destination="oUd-9E-CbL" id="2Sd-Fc-BVh"/>
+ </connections>
+ </segmentedControl>
+ <button verticalHuggingPriority="750" id="xPm-H1-KgK">
+ <rect key="frame" x="157" y="-48" width="132" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="focusToSearch" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="B2e-0a-dSE">
+ <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="selectText:" target="oUd-9E-CbL" id="BfF-BI-CW2"/>
+ </connections>
+ </button>
+ <button verticalHuggingPriority="750" id="U9h-Fp-3vs">
+ <rect key="frame" x="71" y="-48" width="97" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="FindNext" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="43z-K4-pe3">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent">g</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="helpSearchFindNextInPage:" target="-2" id="ZHw-AS-guO"/>
+ </connections>
+ </button>
+ <button verticalHuggingPriority="750" id="VlU-rM-dhC">
+ <rect key="frame" x="72" y="-70" width="96" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="FindPrev" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="y5p-vV-b9H">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent">G</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="helpSearchFindPreviousInPage:" target="-2" id="ASL-Hq-M6w"/>
+ </connections>
+ </button>
+ <box verticalHuggingPriority="750" boxType="separator" id="ebM-jX-qKP">
+ <rect key="frame" x="0.0" y="269" width="505" height="5"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
+ </box>
+ <segmentedControl verticalHuggingPriority="750" id="RYG-b9-3OO">
+ <rect key="frame" x="347" y="279" width="152" height="20"/>
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
+ <segmentedCell key="cell" borderStyle="border" alignment="left" style="roundRect" trackingMode="selectOne" id="ayu-f2-q41">
+ <font key="font" metaFont="smallSystem"/>
+ <segments>
+ <segment label="MySQL" toolTip="Search in MySQL Help [⇧⌘M]" width="48" selected="YES"/>
+ <segment label="Page" toolTip="Search in current page [⇧⌘P]" width="48" tag="1"/>
+ <segment label="Web" toolTip="Search in the online documentation [⇧⌘W]" width="48" tag="2"/>
+ </segments>
+ </segmentedCell>
+ <connections>
+ <action selector="helpTargetDispatcher:" target="-2" id="1YQ-YJ-1R9"/>
+ <outlet property="nextKeyView" destination="Zz1-fo-n30" id="0uS-V5-HBl"/>
+ </connections>
+ </segmentedControl>
+ <button verticalHuggingPriority="750" id="9LS-D2-w9e">
+ <rect key="frame" x="129" y="-50" width="96" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="Web" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="4D0-b4-VY2">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent">W</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="helpSelectHelpTargetWeb:" target="-2" id="BfR-sp-8he"/>
+ </connections>
+ </button>
+ <button verticalHuggingPriority="750" id="3yk-2p-BnH">
+ <rect key="frame" x="220" y="-50" width="96" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="page" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="p0f-ar-uTn">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent">P</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="helpSelectHelpTargetPage:" target="-2" id="c56-R2-y1o"/>
+ </connections>
+ </button>
+ <button verticalHuggingPriority="750" id="Mne-35-1Rh">
+ <rect key="frame" x="308" y="-50" width="96" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <buttonCell key="cell" type="push" title="mysql" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Uqc-Nt-BeG">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent">M</string>
+ <modifierMask key="keyEquivalentModifierMask" command="YES"/>
+ </buttonCell>
+ <connections>
+ <action selector="helpSelectHelpTargetMySQL:" target="-2" id="aqb-uo-vKI"/>
+ </connections>
+ </button>
+ </subviews>
+ </view>
+ <connections>
+ <outlet property="delegate" destination="-2" id="Uk1-Ps-UNI"/>
+ <outlet property="initialFirstResponder" destination="oUd-9E-CbL" id="OKe-oU-hi5"/>
+ </connections>
+ <point key="canvasLocation" x="90" y="718"/>
+ </window>
+ </objects>
+ <resources>
+ <image name="NSLeftFacingTriangleTemplate" width="9" height="12"/>
+ <image name="NSListViewTemplate" width="14" height="10"/>
+ <image name="NSRightFacingTriangleTemplate" width="9" height="12"/>
+ </resources>
+</document>
diff --git a/Resources/Templates/SPMySQLHelpTemplate.html b/Resources/Templates/SPMySQLHelpTemplate.html
index 548c30df..c5e59e88 100644
--- a/Resources/Templates/SPMySQLHelpTemplate.html
+++ b/Resources/Templates/SPMySQLHelpTemplate.html
@@ -2,12 +2,12 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-
+ <title>{{title}}</title>
<style type="text/css" media="all">
body {
margin: 2px;
padding: 10px;
- font-family:'Helvetica';
+ font-family: 'Helvetica', 'Helvetica Neue', sans-serif;
font-size:9pt;
}
@@ -23,19 +23,28 @@
}
.description {
- font-family: Monaco;
+ font-family: Monaco, monospace;
}
.example {
- font-family: Courier;
+ font-family: Courier, monospace;
}
.header {
padding-bottom: 5px;
}
+
+ .nothing {
+ color: gray;
+ }
+
+ .error {
+ color: #ff415a;
+ font-family: monospace;
+ }
</style>
</head>
<body>
-%@
+{{body}}
</body>
</html>
diff --git a/Source/SPCustomQuery.h b/Source/SPCustomQuery.h
index 24695ba2..f06b63dc 100644
--- a/Source/SPCustomQuery.h
+++ b/Source/SPCustomQuery.h
@@ -31,17 +31,6 @@
#import "SPDatabaseContentViewDelegate.h"
-#import <WebKit/WebKit.h>
-
-#define SP_HELP_TOC_SEARCH_STRING @"contents"
-#define SP_HELP_SEARCH_IN_MYSQL 0
-#define SP_HELP_SEARCH_IN_PAGE 1
-#define SP_HELP_SEARCH_IN_WEB 2
-#define SP_HELP_GOBACK_BUTTON 0
-#define SP_HELP_SHOW_TOC_BUTTON 1
-#define SP_HELP_GOFORWARD_BUTTON 2
-#define SP_HELP_NOT_AVAILABLE @"__no_help_available"
-
#define SP_SAVE_ALL_FAVORTITE_MENUITEM_TAG 100001
#define SP_SAVE_SELECTION_FAVORTITE_MENUITEM_TAG 100000
#define SP_FAVORITE_HEADER_MENUITEM_TAG 200000
@@ -122,12 +111,6 @@
#ifndef SP_CODA
IBOutlet NSMenuItem *previousHistoryMenuItem;
IBOutlet NSMenuItem *nextHistoryMenuItem;
- IBOutlet NSWindow *helpWebViewWindow;
- IBOutlet WebView *helpWebView;
- IBOutlet NSSearchField *helpSearchField;
- IBOutlet NSSearchFieldCell *helpSearchFieldCell;
- IBOutlet NSSegmentedControl *helpNavigator;
- IBOutlet NSSegmentedControl *helpTargetSelector;
#endif
IBOutlet NSButton *queryInfoButton;
@@ -145,17 +128,10 @@
NSArray *currentQueryRanges;
BOOL currentQueryBeforeCaret;
- NSString *mySQLversion;
NSTableColumn *sortColumn;
NSUInteger queryStartPosition;
-#ifndef SP_CODA
- NSUInteger helpTarget;
- WebHistory *helpHistory;
- NSString *helpHTMLTemplate;
-#endif
-
SPDataStorage *resultData;
pthread_mutex_t resultDataLock;
NSArray *cqColumnDefinition;
@@ -213,17 +189,6 @@
- (IBAction)chooseQueryHistory:(id)sender;
- (IBAction)closeSheet:(id)sender;
- (IBAction)gearMenuItemSelected:(id)sender;
-#ifndef SP_CODA
-- (IBAction)showHelpForCurrentWord:(id)sender;
-- (IBAction)showHelpForSearchString:(id)sender;
-- (IBAction)helpSegmentDispatcher:(id)sender;
-- (IBAction)helpTargetDispatcher:(id)sender;
-- (IBAction)helpSearchFindNextInPage:(id)sender;
-- (IBAction)helpSearchFindPreviousInPage:(id)sender;
-- (IBAction)helpSelectHelpTargetMySQL:(id)sender;
-- (IBAction)helpSelectHelpTargetPage:(id)sender;
-- (IBAction)helpSelectHelpTargetWeb:(id)sender;
-#endif
- (IBAction)filterQueryFavorites:(id)sender;
- (IBAction)filterQueryHistory:(id)sender;
- (IBAction)saveQueryHistory:(id)sender;
@@ -268,11 +233,6 @@
#ifndef SP_CODA
// MySQL Help
- (void)showAutoHelpForCurrentWord:(id)sender;
-- (NSString *)getHTMLformattedMySQLHelpFor:(NSString *)searchString calledByAutoHelp:(BOOL)autoHelp;
-- (void)showHelpFor:(NSString *)aString addToHistory:(BOOL)addToHistory calledByAutoHelp:(BOOL)autoHelp;
-- (void)helpTargetValidation;
-- (void)openMySQLonlineDocumentationWithString:(NSString *)searchString;
-- (NSWindow *)helpWebViewWindow;
#endif
- (void)setMySQLversion:(NSString *)theVersion;
diff --git a/Source/SPCustomQuery.m b/Source/SPCustomQuery.m
index e8c1d6ad..62ff3e18 100644
--- a/Source/SPCustomQuery.m
+++ b/Source/SPCustomQuery.m
@@ -61,6 +61,8 @@
#import "SPBundleHTMLOutputController.h"
#endif
#import "SPFunctions.h"
+#import "SPHelpViewerClient.h"
+#import "SPHelpViewerController.h"
#import <pthread.h>
#import <SPMySQL/SPMySQL.h>
@@ -68,10 +70,10 @@
@interface SPCustomQuery ()
- (id)_resultDataItemAtRow:(NSInteger)row columnIndex:(NSUInteger)column preserveNULLs:(BOOL)preserveNULLs asPreview:(BOOL)asPreview;
-+ (NSString *)linkToHelpTopic:(NSString *)aTopic;
- (void)documentWillClose:(NSNotification *)notification;
- (void)queryFavoritesHaveBeenUpdated:(NSNotification *)notification;
- (void)historyItemsHaveBeenUpdated:(NSNotification *)notification;
+- (void)helpWindowClosedByUser:(NSNotification *)notification;
@end
@@ -2830,221 +2832,14 @@
#endif
-#pragma mark -
-#pragma mark MySQL Help
-
/**
- * Set the MySQL version as X.Y for Help window title and online search
+ * Set the MySQL version as X.Y
*/
- (void)setMySQLversion:(NSString *)theVersion
{
- mySQLversion = [[theVersion substringToIndex:3] retain];
- [textView setConnection:mySQLConnection withVersion:[[[mySQLversion componentsSeparatedByString:@"."] objectAtIndex:0] integerValue]];
-}
-
-#ifndef SP_CODA
-/**
- * Return the Help window.
- */
-- (NSWindow *)helpWebViewWindow
-{
- return helpWebViewWindow;
+ [textView setConnection:mySQLConnection withVersion:[[[[theVersion substringToIndex:3] componentsSeparatedByString:@"."] objectAtIndex:0] integerValue]];
}
-/**
- * Show the data for "HELP 'searchString'".
- */
-- (void)showHelpFor:(NSString *)searchString addToHistory:(BOOL)addToHistory calledByAutoHelp:(BOOL)autoHelp
-{
-
- // If there's no search string, show nothing if called by autohelp, and the index otherwise
- if (![searchString length]) {
- if (autoHelp) return;
- searchString = SP_HELP_TOC_SEARCH_STRING;
- }
-
- NSString *helpString = [self getHTMLformattedMySQLHelpFor:searchString calledByAutoHelp:autoHelp];
-
- if(autoHelp && [helpString isEqualToString:SP_HELP_NOT_AVAILABLE]) {
- [helpWebViewWindow orderOut:nil];
- return;
- }
-
- // Order out resp. init the Help window if not visible
- if(![helpWebViewWindow isVisible])
- {
- // set title of the Help window
- [helpWebViewWindow setTitle:[NSString stringWithFormat:@"%@ (%@ %@)", NSLocalizedString(@"MySQL Help", @"mysql help"), NSLocalizedString(@"version", @"version"), mySQLversion]];
-
- // init goback/forward buttons
- if([[helpWebView backForwardList] backListCount] < 1)
- {
- [helpNavigator setEnabled:NO forSegment:SP_HELP_GOBACK_BUTTON];
- [helpNavigator setEnabled:NO forSegment:SP_HELP_GOFORWARD_BUTTON];
- } else {
- [helpNavigator setEnabled:[[helpWebView backForwardList] backListCount] forSegment:SP_HELP_GOBACK_BUTTON];
- [helpNavigator setEnabled:[[helpWebView backForwardList] forwardListCount] forSegment:SP_HELP_GOFORWARD_BUTTON];
- }
-
- // set default to search in MySQL help
- helpTarget = SP_HELP_SEARCH_IN_MYSQL;
- [helpTargetSelector setSelectedSegment:SP_HELP_SEARCH_IN_MYSQL];
- [self helpTargetValidation];
-
- // order out Help window if Help is available
- if(![helpString isEqualToString:SP_HELP_NOT_AVAILABLE])
- [helpWebViewWindow orderFront:helpWebView];
- }
-
- // close Help window if no Help available
- if([helpString isEqualToString:SP_HELP_NOT_AVAILABLE])
- [helpWebViewWindow close];
-
- if(![helpString length]) return;
-
- // add searchString to history list
- if(addToHistory)
- {
- WebHistoryItem *aWebHistoryItem = [[WebHistoryItem alloc] initWithURLString:[NSString stringWithFormat:@"applewebdata://%@", searchString] title:searchString lastVisitedTimeInterval:[[NSDate date] timeIntervalSinceDate:[NSDate distantFuture]]];
- [[helpWebView backForwardList] addItem:aWebHistoryItem];
- [aWebHistoryItem release];
- }
-
- // validate goback/forward buttons
- [helpNavigator setEnabled:[[helpWebView backForwardList] backListCount] forSegment:SP_HELP_GOBACK_BUTTON];
- [helpNavigator setEnabled:[[helpWebView backForwardList] forwardListCount] forSegment:SP_HELP_GOFORWARD_BUTTON];
-
- // load HTML formatted help into the webview
- [[helpWebView mainFrame] loadHTMLString:helpString baseURL:nil];
-}
-
-/**
- * Show the data for "HELP 'search word'" according to helpTarget
- */
-- (IBAction)showHelpForSearchString:(id)sender
-{
- NSString *searchString = [[helpSearchField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- switch(helpTarget)
- {
- case SP_HELP_SEARCH_IN_PAGE:
- if(![helpWebView searchFor:searchString direction:YES caseSensitive:NO wrap:YES])
- if([searchString length]) NSBeep();
- break;
- case SP_HELP_SEARCH_IN_WEB:
- if(![searchString length])
- break;
- [self openMySQLonlineDocumentationWithString:searchString];
- break;
- case SP_HELP_SEARCH_IN_MYSQL:
- [self showHelpFor:searchString addToHistory:YES calledByAutoHelp:NO];
- break;
- }
-}
-
-/**
- * Show the Help for the selected text in the webview
- */
-- (IBAction)showHelpForWebViewSelection:(id)sender
-{
- [self showHelpFor:[[helpWebView selectedDOMRange] text] addToHistory:YES calledByAutoHelp:NO];
-}
-
-/*
- * Show MySQL's online documentation for the selected text in the webview
- */
-- (IBAction)searchInDocForWebViewSelection:(id)sender
-{
- NSString *searchString = [[[helpWebView selectedDOMRange] text] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- if(![searchString length])
- {
- NSBeep();
- return;
- }
- [self openMySQLonlineDocumentationWithString:searchString];
-}
-
-
-/**
- * Show the data for "HELP 'currentWord'"
- */
-- (IBAction)showHelpForCurrentWord:(id)sender
-{
- NSString *searchString = [[sender string] substringWithRange:[sender getRangeForCurrentWord]];
- [self showHelpFor:searchString addToHistory:YES calledByAutoHelp:NO];
-}
-
-/**
- * Find Next/Previous in current page
- */
-- (IBAction)helpSearchFindNextInPage:(id)sender
-{
- if(helpTarget == SP_HELP_SEARCH_IN_PAGE)
- if(![helpWebView searchFor:[[helpSearchField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] direction:YES caseSensitive:NO wrap:YES])
- NSBeep();
-}
-
-- (IBAction)helpSearchFindPreviousInPage:(id)sender
-{
- if(helpTarget == SP_HELP_SEARCH_IN_PAGE)
- if(![helpWebView searchFor:[[helpSearchField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] direction:NO caseSensitive:NO wrap:YES])
- NSBeep();
-}
-
-/**
- * Navigation for back/TOC/forward
- */
-- (IBAction)helpSegmentDispatcher:(id)sender
-{
- switch([helpNavigator selectedSegment])
- {
- case SP_HELP_GOBACK_BUTTON:
- [helpWebView goBack];
- break;
- case SP_HELP_SHOW_TOC_BUTTON:
- [self showHelpFor:SP_HELP_TOC_SEARCH_STRING addToHistory:YES calledByAutoHelp:NO];
- break;
- case SP_HELP_GOFORWARD_BUTTON:
- [helpWebView goForward];
- break;
- }
-
- // validate goback and goforward buttons according history
- [helpNavigator setEnabled:[[helpWebView backForwardList] backListCount] forSegment:SP_HELP_GOBACK_BUTTON];
- [helpNavigator setEnabled:[[helpWebView backForwardList] forwardListCount] forSegment:SP_HELP_GOFORWARD_BUTTON];
-
-}
-
-/**
- * Set helpTarget according user choice via mouse and keyboard short-cuts.
- */
-- (IBAction)helpSelectHelpTargetMySQL:(id)sender
-{
- helpTarget = SP_HELP_SEARCH_IN_MYSQL;
- [helpTargetSelector setSelectedSegment:SP_HELP_SEARCH_IN_MYSQL];
- [self helpTargetValidation];
-}
-
-- (IBAction)helpSelectHelpTargetPage:(id)sender
-{
- helpTarget = SP_HELP_SEARCH_IN_PAGE;
- [helpTargetSelector setSelectedSegment:SP_HELP_SEARCH_IN_PAGE];
- [self helpTargetValidation];
-}
-
-- (IBAction)helpSelectHelpTargetWeb:(id)sender
-{
- helpTarget = SP_HELP_SEARCH_IN_WEB;
- [helpTargetSelector setSelectedSegment:SP_HELP_SEARCH_IN_WEB];
- [self helpTargetValidation];
-}
-
-- (IBAction)helpTargetDispatcher:(id)sender
-{
- helpTarget = [helpTargetSelector selectedSegment];
- [self helpTargetValidation];
-}
-#endif
-
- (IBAction)showCompletionList:(id)sender
{
NSRange insertRange = NSMakeRange([textView selectedRange].location, 0);
@@ -3068,294 +2863,22 @@
- (void)showAutoHelpForCurrentWord:(id)sender
{
NSString *searchString = [[sender string] substringWithRange:[sender getRangeForCurrentWord]];
- [self showHelpFor:searchString addToHistory:YES calledByAutoHelp:YES];
-}
-
-/**
- * Control the help search field behaviour.
- */
-- (void)helpTargetValidation
-{
- switch(helpTarget)
- {
- case SP_HELP_SEARCH_IN_PAGE:
- case SP_HELP_SEARCH_IN_WEB:
- [helpSearchFieldCell setSendsWholeSearchString:YES];
- break;
- case SP_HELP_SEARCH_IN_MYSQL:
- [helpSearchFieldCell setSendsWholeSearchString:NO];
- break;
- }
-}
-
-- (void)openMySQLonlineDocumentationWithString:(NSString *)searchString
-{
- NSString *version = nil;
- if([[mySQLversion stringByReplacingOccurrencesOfString:@"." withString:@""] integerValue] < 42)
- version = @"4.1";
- else
- version = [NSString stringWithString:mySQLversion];
-
- [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:
- [[NSString stringWithFormat:
- SPMySQLSearchURL,
- version,
- NSLocalizedString(@"en", @"MySQL search language code - eg in http://search.mysql.com/search?q=select&site=refman-50&lr=lang_en"),
- searchString]
- stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]]];
-}
-
-/**
- * Return the help string HTML formatted from executing "HELP 'searchString'".
- * If more than one help topic was found return a link list.
- */
-- (NSString *)getHTMLformattedMySQLHelpFor:(NSString *)searchString calledByAutoHelp:(BOOL)autoHelp
-{
-
- if(![searchString length]) return @"";
-
- // Don't escape % when being used as a wildcard, but escape it when it's being used by itself.
- if ([searchString isEqualToString:@"%"]) searchString = @"\\%";
-
- NSRange aRange;
- SPMySQLResult *theResult = nil;
- NSDictionary *tableDetails;
- NSMutableString *theHelp = [NSMutableString string];
-
- [theHelp setString:@""];
-
- // search via: HELP 'searchString'
- theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"HELP '%@'", [searchString stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"]]];
- if ([mySQLConnection queryErrored])
- {
- // if an error or HELP is not supported fall back to online search but
- // don't open it if autoHelp is enabled
- if(!autoHelp)
- [self openMySQLonlineDocumentationWithString:searchString];
-
- [helpWebViewWindow close];
- return SP_HELP_NOT_AVAILABLE;
- }
-
- // nothing found?
- if(![theResult numberOfRows]) {
-
- // try to search via: HELP 'searchString%'
- theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"HELP '%@%%'", [searchString stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"]]];
-
- // really nothing found?
- if(![theResult numberOfRows])
- return [NSString stringWithFormat:@"<em style='color: gray'>%@</em>", NSLocalizedString(@"No results found.", @"Mysql Help Viewer : Search : No results")];
- }
-
- // Ensure rows are returned as strings to prevent data problems with older 4.1 servers
- [theResult setReturnDataAsStrings:YES];
-
- tableDetails = [[NSDictionary alloc] initWithDictionary:[theResult getRowAsDictionary]];
-
- if ([tableDetails objectForKey:@"description"]) { // one single help topic found
- if ([tableDetails objectForKey:@"name"]) {
- [theHelp appendString:@"<h2 class='header'>"];
- [theHelp appendString:[[[tableDetails objectForKey:@"name"] copy] autorelease]];
- [theHelp appendString:@"</h2>"];
-
- }
- if ([tableDetails objectForKey:@"description"]) {
- NSMutableString *desc = [NSMutableString string];
- NSError *err1 = NULL;
- NSString *aUrl;
-
- [desc setString:[[[tableDetails objectForKey:@"description"] copy] autorelease]];
-
- //[desc replaceOccurrencesOfString:[searchString uppercaseString] withString:[NSString stringWithFormat:@"<span class='searchstring'>%@</span>", [searchString uppercaseString]] options:NSLiteralSearch range:NSMakeRange(0,[desc length])];
-
- // detect and generate http links
- aRange = NSMakeRange(0,0);
- NSInteger safeCnt = 0; // safety counter - not more than 200 loops allowed
- while(1){
- aRange = [desc rangeOfRegex:@"\\s((https?|ftp|file)://.*?html)" options:RKLNoOptions inRange:NSMakeRange(NSMaxRange(aRange), [desc length]-aRange.location-aRange.length) capture:1 error:&err1];
- if(aRange.location != NSNotFound) {
- aUrl = [desc substringWithRange:aRange];
- [desc replaceCharactersInRange:aRange withString:[NSString stringWithFormat:@"<a href='%@'>%@</a>", aUrl, aUrl]];
- }
- else
- break;
- safeCnt++;
- if(safeCnt > 200)
- break;
- }
-
- // Detect and generate cross-links. First, handle the old-style [HELP ...] text.
- [desc replaceOccurrencesOfRegex:@"(\\[HELP ([^\\]]*?)\\]" withString:[SPCustomQuery linkToHelpTopic:@"$1"]];
-
- // Handle "see [...]" and "in [...]"-style 5.x links.
- //look-behind won't work here because of the \s+
- [desc replaceOccurrencesOfRegex:@"(See|see|In|in|and)\\s+\\[(?:HELP\\s+)?([^\\]]*?)\\]" withString:[NSString stringWithFormat:@"$1 %@",[SPCustomQuery linkToHelpTopic:@"$2"]]];
-
- [theHelp appendFormat:@"<pre class='description'>%@</pre>", desc];
- }
- // are examples available?
- if([tableDetails objectForKey:@"example"]){
- NSString *examples = [[[tableDetails objectForKey:@"example"] copy] autorelease];
- if([examples length])
- [theHelp appendFormat:@"<br><i><b>%1$@</b></i><br><pre class='example'>%2$@</pre>",NSLocalizedString(@"Example:",@"Mysql Help Viewer : Help Topic: Example section title"), examples];
-
- }
- } else { // list all found topics
-
- // check if HELP 'contents' is called
- if(![searchString isEqualToString:SP_HELP_TOC_SEARCH_STRING])
- [theHelp appendFormat:@"<br><i>%@</i><br>", [NSString stringWithFormat:NSLocalizedString(@"Help topics for “%@”", @"MySQL Help Viewer : Results list : Page title"), searchString]];
- else
- [theHelp appendFormat:@"<br><b>%@:</b><br>", NSLocalizedString(@"MySQL Help – Categories", @"mysql help categories")];
-
- // iterate through all found rows and print them as HTML ul/li list
- [theHelp appendString:@"<ul>"];
- [theResult setDefaultRowReturnType:SPMySQLResultRowAsArray];
- for (NSArray *eachRow in theResult) {
- NSString *topic = [eachRow objectAtIndex:[eachRow count]-2];
- [theHelp appendFormat:@"<li>%@</li>",[SPCustomQuery linkToHelpTopic:topic]];
- }
- [theHelp appendString:@"</ul>"];
- }
-
- [tableDetails release];
-
- return [NSString stringWithFormat:helpHTMLTemplate, theHelp];
-}
-
-+ (NSString *)linkToHelpTopic:(NSString *)aTopic
-{
- NSString *linkTitle = [NSString stringWithFormat:NSLocalizedString(@"Show MySQL help for “%@”", @"MySQL Help Viewer : Results list : Link tooltip"),aTopic];
- return [NSString stringWithFormat:@"<a title='%2$@' href='%1$@' class='internallink'>%1$@</a>", aTopic, linkTitle];
-}
-
-#pragma mark -
-#pragma mark WebView delegate methods
-
-/**
- * Link detector: If user clicked at an http link open it in the default browser,
- * otherwise search for it in the MySQL help. Additionally handle back/forward events from
- * keyboard and context menu.
- */
-- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener
-{
- NSInteger navigationType = [[actionInformation objectForKey:WebActionNavigationTypeKey] integerValue];
-
- if([[[request URL] scheme] isEqualToString:@"applewebdata"] && navigationType == WebNavigationTypeLinkClicked){
- [self showHelpFor:[[[request URL] path] lastPathComponent] addToHistory:YES calledByAutoHelp:NO];
- [listener ignore];
- } else {
- if (navigationType == WebNavigationTypeOther) {
- // catch reload event
- // if([[[actionInformation objectForKey:WebActionOriginalURLKey] absoluteString] isEqualToString:@"about:blank"])
- // [listener use];
- // else
- [listener use];
- } else if (navigationType == WebNavigationTypeLinkClicked) {
- // show http in browser
- [[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]];
- [listener ignore];
- } else if (navigationType == WebNavigationTypeBackForward) {
- // catch back/forward events from contextual menu
- [self showHelpFor:[[[[actionInformation objectForKey:WebActionOriginalURLKey] absoluteString] lastPathComponent] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding] addToHistory:NO calledByAutoHelp:NO];
- [listener ignore];
- } else if (navigationType == WebNavigationTypeReload) {
- // just in case
- [listener ignore];
- } else {
- // Ignore WebNavigationTypeFormSubmitted, WebNavigationTypeFormResubmitted.
- [listener ignore];
- }
- }
-}
-
-/**
- * Manage contextual menu in helpWebView
- * Ignore "Reload", "Open Link", "Open Link in new Window", "Download link" etc.
- */
-- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems
-{
-
- NSMutableArray *webViewMenuItems = [[defaultMenuItems mutableCopy] autorelease];
-
- if (webViewMenuItems)
- {
- // Remove all needless default menu items
- NSEnumerator *itemEnumerator = [defaultMenuItems objectEnumerator];
- NSMenuItem *menuItem = nil;
-
- while ((menuItem = [itemEnumerator nextObject]))
- {
- NSInteger tag = [menuItem tag];
-
- switch (tag)
- {
- case 2000: // WebMenuItemTagOpenLink
- case WebMenuItemTagOpenLinkInNewWindow:
- case WebMenuItemTagDownloadLinkToDisk:
- case WebMenuItemTagOpenImageInNewWindow:
- case WebMenuItemTagDownloadImageToDisk:
- case WebMenuItemTagCopyImageToClipboard:
- case WebMenuItemTagOpenFrameInNewWindow:
- case WebMenuItemTagStop:
- case WebMenuItemTagReload:
- case WebMenuItemTagCut:
- case WebMenuItemTagPaste:
- case WebMenuItemTagSpellingGuess:
- case WebMenuItemTagNoGuessesFound:
- case WebMenuItemTagIgnoreSpelling:
- case WebMenuItemTagLearnSpelling:
- case WebMenuItemTagOther:
- case WebMenuItemTagOpenWithDefaultApplication:
- [webViewMenuItems removeObjectIdenticalTo: menuItem];
- break;
- }
- }
- }
-
- // Add two menu items for a selection if no link is given
- if(webViewMenuItems
- && [[element objectForKey:@"WebElementIsSelected"] boolValue]
- && ![[element objectForKey:@"WebElementLinkIsLive"] boolValue])
- {
-
- NSMenuItem *searchInMySQL;
- NSMenuItem *searchInMySQLonline;
-
- [webViewMenuItems insertObject:[NSMenuItem separatorItem] atIndex:0];
-
- searchInMySQLonline = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Search in MySQL Documentation", @"Search in MySQL Documentation") action:@selector(searchInDocForWebViewSelection:) keyEquivalent:@""];
- [searchInMySQLonline setEnabled:YES];
- [searchInMySQLonline setTarget:self];
- [webViewMenuItems insertObject:searchInMySQLonline atIndex:0];
- [searchInMySQLonline release];
-
- searchInMySQL = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Search in MySQL Help", @"Search in MySQL Help") action:@selector(showHelpForWebViewSelection:) keyEquivalent:@""];
- [searchInMySQL setEnabled:YES];
- [searchInMySQL setTarget:self];
- [webViewMenuItems insertObject:searchInMySQL atIndex:0];
- [searchInMySQL release];
-
- }
-
- return webViewMenuItems;
+ [[tableDocumentInstance helpViewerClient] showHelpFor:searchString addToHistory:YES calledByAutoHelp:YES];
}
/**
* Detect when the help window is closed (manually) and disable autohelp to ensure it
* isn't reopened on keypresses.
*/
-- (BOOL)windowShouldClose:(id)sender
+- (void)helpWindowClosedByUser:(NSNotification *)notification
{
- if (sender == helpWebViewWindow) {
- [prefs setBool:NO forKey:SPCustomQueryUpdateAutoHelp];
- [prefs synchronize];
- [autohelpMenuItem setState:NSOffState];
- [textView setAutohelp:NO];
- }
+ if ([notification object] != [tableDocumentInstance helpViewerClient]) return;
- return YES;
+ //TODO: this doesn't belong in the document context, since multiple open documents can become out of sync through this
+ [prefs setBool:NO forKey:SPCustomQueryUpdateAutoHelp];
+ [prefs synchronize];
+ [autohelpMenuItem setState:NSOffState];
+ [textView setAutohelp:NO];
}
#endif
@@ -3749,26 +3272,6 @@
selectionIndexToRestore = nil;
selectionViewportToRestore = NSZeroRect;
-#ifndef SP_CODA
- // init helpHTMLTemplate
- NSError *error;
-
- helpHTMLTemplate = [[NSString alloc]
- initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:SPHTMLHelpTemplate ofType:@"html"]
- encoding:NSUTF8StringEncoding
- error:&error];
-
- // an error occurred while reading
- if (helpHTMLTemplate == nil) {
- NSLog(@"%@", [NSString stringWithFormat:@"Error reading “%@.html”!<br>%@", SPHTMLHelpTemplate, [error localizedFailureReason]]);
- NSBeep();
- }
-
- // init search history
- [helpWebView setMaintainsBackForwardList:YES];
- [[helpWebView backForwardList] setCapacity:20];
-#endif
-
// init tableView's data source
resultData = [[SPDataStorage alloc] init];
editedRow = -1;
@@ -4003,6 +3506,10 @@
selector:@selector(historyItemsHaveBeenUpdated:)
name:SPHistoryItemsHaveBeenUpdatedNotification
object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(helpWindowClosedByUser:)
+ name:SPUserClosedHelpViewerNotification
+ object:[tableDocumentInstance helpViewerClient]];
#ifndef SP_CODA
[prefs addObserver:self forKeyPath:SPGlobalResultTableFont options:NSKeyValueObservingOptionNew context:NULL];
@@ -4088,10 +3595,6 @@
if(fieldEditor) SPClear(fieldEditor);
-#ifndef SP_CODA
- if (helpHTMLTemplate) SPClear(helpHTMLTemplate);
-#endif
- if (mySQLversion) SPClear(mySQLversion);
if (sortField) SPClear(sortField);
if (cqColumnDefinition) SPClear(cqColumnDefinition);
if (selectionIndexToRestore) SPClear(selectionIndexToRestore);
diff --git a/Source/SPDatabaseDocument.h b/Source/SPDatabaseDocument.h
index 20fb34d1..ceaca039 100644
--- a/Source/SPDatabaseDocument.h
+++ b/Source/SPDatabaseDocument.h
@@ -60,6 +60,7 @@
@class SPExtendedTableInfo;
@class SPTableTriggers;
@class SPTableRelations;
+@class SPHelpViewerClient;
#import "SPDatabaseContentViewDelegate.h"
#import "SPConnectionControllerDelegateProtocol.h"
@@ -92,6 +93,7 @@
IBOutlet id spHistoryControllerInstance;
IBOutlet id exportControllerInstance;
#endif
+ IBOutlet SPHelpViewerClient *helpViewerClientInstance;
IBOutlet id statusTableAccessoryView;
IBOutlet id statusTableView;
@@ -325,6 +327,8 @@
@property (readonly) SPDatabaseStructure *databaseStructureRetrieval;
@property (readonly) int64_t instanceId;
+- (SPHelpViewerClient *)helpViewerClient;
+
#ifndef SP_CODA /* method decls */
- (BOOL)isUntitled;
#endif
diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m
index 4bdd0aad..4909282c 100644
--- a/Source/SPDatabaseDocument.m
+++ b/Source/SPDatabaseDocument.m
@@ -48,7 +48,6 @@
#import "SPSQLParser.h"
#import "SPTableData.h"
#import "SPDatabaseData.h"
-#import "SPDatabaseStructure.h"
#import "SPExtendedTableInfo.h"
#import "SPHistoryController.h"
#import "SPPreferenceController.h"
@@ -85,6 +84,8 @@
#import "ICUTemplateMatcher.h"
#import "SPFavoritesOutlineView.h"
#import "SPSSHTunnel.h"
+#import "SPHelpViewerClient.h"
+#import "SPHelpViewerController.h"
#import <SPMySQL/SPMySQL.h>
@@ -511,6 +512,8 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
// Set the custom query editor's MySQL version
[customQueryInstance setMySQLversion:mySQLVersion];
+ [helpViewerClientInstance setConnection:mySQLConnection];
+
#ifndef SP_CODA
[self updateWindowTitle:self];
@@ -2661,6 +2664,11 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
}
}
+- (SPHelpViewerClient *)helpViewerClient
+{
+ return helpViewerClientInstance;
+}
+
/**
* Is current document Untitled?
*/
@@ -3602,8 +3610,8 @@ static int64_t SPDatabaseDocumentInstanceCounter = 0;
*/
- (IBAction)showMySQLHelp:(id)sender
{
- [customQueryInstance showHelpFor:SP_HELP_TOC_SEARCH_STRING addToHistory:YES calledByAutoHelp:NO];
- [[customQueryInstance helpWebViewWindow] makeKeyWindow];
+ [helpViewerClientInstance showHelpFor:SPHelpViewerSearchTOC addToHistory:YES calledByAutoHelp:NO];
+ [[helpViewerClientInstance helpWebViewWindow] makeKeyWindow];
}
#endif
diff --git a/Source/SPHelpViewerClient.h b/Source/SPHelpViewerClient.h
new file mode 100644
index 00000000..b169e99f
--- /dev/null
+++ b/Source/SPHelpViewerClient.h
@@ -0,0 +1,66 @@
+//
+// SPHelpViewerClient.h
+// sequel-pro
+//
+// Created by Max Lohrmann on 25.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+// Parts 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 SPHelpViewerController;
+@class SPMySQLConnection;
+@class MGTemplateEngine;
+
+/**
+ * This is the client side of the Help Viewer window, i.e. this class
+ * can be instantiated from within an xib file as a custom object.
+ *
+ * It also contains the logic to look up the help in the mysql database
+ * using the mySQLConnection (which does not belong into the Help Viewer's
+ * window controller).
+ *
+ * Notifications posted:
+ * * SPUserClosedHelpViewerNotification
+ * When the user triggered closing the help viewer window
+ */
+@interface SPHelpViewerClient : NSObject
+{
+ SPHelpViewerController *controller;
+
+ NSString *helpHTMLTemplate;
+ SPMySQLConnection *mySQLConnection;
+
+ MGTemplateEngine *engine;
+}
+
+- (void)setConnection:(SPMySQLConnection *)theConnection;
+
+- (NSWindow *)helpWebViewWindow;
+
+- (void)showHelpFor:(NSString *)aString addToHistory:(BOOL)addToHistory calledByAutoHelp:(BOOL)autoHelp;
+
+// this is not bound in Interface Builder, but used by the SPTextView context menu
+- (IBAction)showHelpForCurrentWord:(id)sender;
+@end
diff --git a/Source/SPHelpViewerClient.m b/Source/SPHelpViewerClient.m
new file mode 100644
index 00000000..54d8633d
--- /dev/null
+++ b/Source/SPHelpViewerClient.m
@@ -0,0 +1,262 @@
+//
+// SPHelpViewerClient.m
+// sequel-pro
+//
+// Created by Max Lohrmann on 25.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+// Parts 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 "SPHelpViewerClient.h"
+#import "SPHelpViewerController.h"
+#import <SPMySQL/SPMySQL.h>
+#import "RegexKitLite.h"
+#import "MGTemplateEngine.h"
+#import "ICUTemplateMatcher.h"
+
+@interface SPHelpViewerClient () <SPHelpViewerDataSource>
+
++ (NSString *)linkToHelpTopic:(NSString *)aTopic;
+
+- (void)helpViewerClosed:(NSNotification *)notification;
+
+@end
+
+@implementation SPHelpViewerClient
+
+- (instancetype)init
+{
+ if (self = [super init]) {
+ controller = [[SPHelpViewerController alloc] init];
+ [controller setDataSource:self];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(helpViewerClosed:) name:SPUserClosedHelpViewerNotification object:controller];
+
+ // init helpHTMLTemplate
+ NSError *error;
+
+ helpHTMLTemplate = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:SPHTMLHelpTemplate ofType:@"html"]
+ encoding:NSUTF8StringEncoding
+ error:&error];
+
+ // Set up template engine with your chosen matcher
+ engine = [[MGTemplateEngine alloc] init];
+ [engine setMatcher:[ICUTemplateMatcher matcherWithTemplateEngine:engine]];
+
+ // an error occurred while reading
+ if (helpHTMLTemplate == nil) {
+ helpHTMLTemplate = [@"<html><body>{{body}}</body></html>" copy]; //fallback
+ NSLog(@"%@", [NSString stringWithFormat:@"Error reading “%@.html”!<br>%@", SPHTMLHelpTemplate, [error localizedFailureReason]]);
+ NSBeep();
+ }
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [controller setDataSource:nil]; // we are the (unretained) datasource, but the controller may outlive us (if retained by other objects)
+ [controller close]; // hide the window if it is still visible (can't update anymore without delegate anyway)
+
+ mySQLConnection = nil;
+ SPClear(controller);
+ SPClear(helpHTMLTemplate);
+ SPClear(engine);
+ [super dealloc];
+}
+
+- (void)helpViewerClosed:(NSNotification *)notification
+{
+ //we'll just proxy that notification because outsiders can't/shouldn't access the controller
+ [[NSNotificationCenter defaultCenter] postNotificationName:SPUserClosedHelpViewerNotification object:self];
+}
+
+- (void)openOnlineHelpForTopic:(NSString *)searchString
+{
+ NSString *version = nil;
+ if(![mySQLConnection serverVersionIsGreaterThanOrEqualTo:4 minorVersion:1 releaseVersion:0])
+ version = @"4.1";
+ else
+ version = [NSString stringWithFormat:@"%u.%u",(unsigned int)[mySQLConnection serverMajorVersion], (unsigned int)[mySQLConnection serverMinorVersion]];
+
+ NSString *url = [[NSString stringWithFormat:
+ SPMySQLSearchURL,
+ version,
+ NSLocalizedString(@"en", @"MySQL search language code - eg in http://search.mysql.com/search?q=select&site=refman-50&lr=lang_en"),
+ searchString]
+ stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
+
+ if([url length]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]];
+}
+
+- (NSString *)HTMLHelpContentsForSearchString:(NSString *)searchString autoHelp:(BOOL)autoHelp
+{
+ if(![searchString length]) return @"";
+
+ NSMutableString *theTitle = [NSMutableString stringWithFormat:NSLocalizedString(@"Version %@", @"Mysql Help Viewer : window title : mysql server version"),[mySQLConnection serverVersionString]];
+ NSMutableString *theHelp = [NSMutableString string];
+
+ // Don't escape % when being used as a wildcard, but escape it when it's being used by itself.
+ if ([searchString isEqualToString:@"%"]) searchString = @"\\%";
+
+ // search via: HELP 'searchString'
+ SPMySQLResult *theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"HELP %@", [searchString tickQuotedString]]];
+ if ([mySQLConnection queryErrored]) {
+ [theTitle setString:NSLocalizedString(@"Error", @"Mysql Help Viewer : window title : query error")];
+ NSString *errMsg = [NSString stringWithFormat:@"ERROR %lu (%@): %@", (unsigned long)[mySQLConnection lastErrorID], [mySQLConnection lastSqlstate], [mySQLConnection lastErrorMessage]];
+ [theHelp appendFormat:@"<b>%@:</b><br><p class='error'>%@</p>", NSLocalizedString(@"MySQL Help Query Failed", @"Mysql Help Viewer : title of error message"), errMsg];
+ goto generate_help;
+ }
+
+ // nothing found?
+ if(![theResult numberOfRows]) {
+ // try to search via: HELP 'searchString%'
+ theResult = [mySQLConnection queryString:[NSString stringWithFormat:@"HELP %@", [[searchString stringByAppendingString:@"%"] tickQuotedString]]];
+
+ // really nothing found?
+ if(![theResult numberOfRows]) {
+ [theTitle appendFormat:@": %@", NSLocalizedString(@"No Results", @"Mysql Help Viewer : window title : nothing found")];
+ [theHelp appendFormat:@"<em class='nothing'>%@</em>", NSLocalizedString(@"No results found.", @"Mysql Help Viewer : Search : No results")];
+ goto generate_help;
+ }
+ }
+
+ // Ensure rows are returned as strings to prevent data problems with older 4.1 servers
+ [theResult setReturnDataAsStrings:YES];
+
+ NSDictionary *tableDetails = [[NSDictionary alloc] initWithDictionary:[theResult getRowAsDictionary]];
+
+ if ([tableDetails objectForKey:@"description"]) { // one single help topic found
+ if ([tableDetails objectForKey:@"name"]) {
+ [theTitle appendFormat:@": %@", [tableDetails objectForKey:@"name"]];
+ [theHelp appendString:@"<h2 class='header'>"];
+ [theHelp appendString:[tableDetails objectForKey:@"name"]];
+ [theHelp appendString:@"</h2>"];
+ }
+ if ([tableDetails objectForKey:@"description"]) {
+ NSMutableString *desc = [NSMutableString string];
+ NSError *err1 = NULL;
+ NSString *aUrl;
+
+ [desc setString:[tableDetails objectForKey:@"description"]];
+
+ //[desc replaceOccurrencesOfString:[searchString uppercaseString] withString:[NSString stringWithFormat:@"<span class='searchstring'>%@</span>", [searchString uppercaseString]] options:NSLiteralSearch range:NSMakeRange(0,[desc length])];
+
+ // detect and generate http links
+ NSRange aRange = NSMakeRange(0,0);
+ NSInteger safeCnt = 0; // safety counter - not more than 200 loops allowed
+ while(1) {
+ aRange = [desc rangeOfRegex:@"\\s((https?|ftp|file)://.*?html)" options:RKLNoOptions inRange:NSMakeRange(NSMaxRange(aRange), [desc length]-aRange.location-aRange.length) capture:1 error:&err1];
+ if(aRange.location != NSNotFound) {
+ aUrl = [desc substringWithRange:aRange];
+ [desc replaceCharactersInRange:aRange withString:[NSString stringWithFormat:@"<a href='%@'>%@</a>", aUrl, aUrl]];
+ }
+ else {
+ break;
+ }
+ safeCnt++;
+ if(safeCnt > 200) break;
+ }
+
+ // Detect and generate cross-links. First, handle the old-style [HELP ...] text.
+ [desc replaceOccurrencesOfRegex:@"(\\[HELP ([^\\]]*?)\\]" withString:[[self class] linkToHelpTopic:@"$1"]];
+
+ // Handle "see [...]" and "in [...]"-style 5.x links.
+ //look-behind won't work here because of the \s+
+ [desc replaceOccurrencesOfRegex:@"(See|see|In|in|and)\\s+\\[(?:HELP\\s+)?([^\\]]*?)\\]" withString:[NSString stringWithFormat:@"$1 %@",[[self class] linkToHelpTopic:@"$2"]]];
+
+ [theHelp appendFormat:@"<pre class='description'>%@</pre>", desc];
+ }
+ // are examples available?
+ if([tableDetails objectForKey:@"example"]){
+ NSString *examples = [[[tableDetails objectForKey:@"example"] copy] autorelease];
+ if([examples length]) [theHelp appendFormat:@"<br><i><b>%1$@</b></i><br><pre class='example'>%2$@</pre>",NSLocalizedString(@"Example:",@"Mysql Help Viewer : Help Topic: Example section title"), examples];
+ }
+ }
+ else { // list all found topics
+ // check if HELP 'contents' is called
+ if(![searchString isEqualToString:SPHelpViewerSearchTOC]) {
+ [theTitle appendString:@": "];
+ [theTitle appendFormat:NSLocalizedString(@"Multiple Results for “%@”", @"Mysql Help Viewer : window title : multiple topics found"), searchString];
+ [theHelp appendFormat:@"<br><i>%@</i><br>", [NSString stringWithFormat:NSLocalizedString(@"Help topics for “%@”", @"MySQL Help Viewer : Results list : Page title"), searchString]];
+ }
+ else {
+ [theTitle appendFormat:@": %@", NSLocalizedString(@"Table of Contents", @"Mysql Help Viewer : window title : TOC")];
+ [theHelp appendFormat:@"<br><b>%@:</b><br>", NSLocalizedString(@"MySQL Help – Categories", @"mysql help categories")];
+ }
+
+ // iterate through all found rows and print them as HTML ul/li list
+ [theHelp appendString:@"<ul>"];
+ [theResult setDefaultRowReturnType:SPMySQLResultRowAsArray];
+ for (NSArray *eachRow in theResult) {
+ NSString *topic = [eachRow objectAtIndex:[eachRow count]-2];
+ [theHelp appendFormat:@"<li>%@</li>",[[self class] linkToHelpTopic:topic]];
+ }
+ [theHelp appendString:@"</ul>"];
+ }
+
+ [tableDetails release];
+
+generate_help:
+ return [engine processTemplate:helpHTMLTemplate withVariables:@{
+ @"title": theTitle,
+ @"body": theHelp,
+ }];
+}
+
++ (NSString *)linkToHelpTopic:(NSString *)aTopic
+{
+ NSString *linkTitle = [NSString stringWithFormat:NSLocalizedString(@"Show MySQL help for “%@”", @"MySQL Help Viewer : Results list : Link tooltip"),aTopic];
+ return [NSString stringWithFormat:@"<a title='%2$@' href='%1$@' class='internallink'>%1$@</a>", aTopic, linkTitle];
+}
+
+- (void)setConnection:(SPMySQLConnection *)theConnection
+{
+ mySQLConnection = theConnection;
+}
+
+/**
+ * Return the Help window.
+ */
+- (NSWindow *)helpWebViewWindow
+{
+ return [controller window];
+}
+
+- (void)showHelpFor:(NSString *)aString addToHistory:(BOOL)addToHistory calledByAutoHelp:(BOOL)autoHelp
+{
+ [controller showHelpFor:aString addToHistory:addToHistory calledByAutoHelp:autoHelp];
+}
+
+/**
+ * Show the data for "HELP 'currentWord'"
+ */
+- (IBAction)showHelpForCurrentWord:(id)sender
+{
+ NSString *searchString = [[sender string] substringWithRange:[sender getRangeForCurrentWord]];
+ [controller showHelpFor:searchString addToHistory:YES calledByAutoHelp:NO];
+}
+
+@end
diff --git a/Source/SPHelpViewerController.h b/Source/SPHelpViewerController.h
new file mode 100644
index 00000000..c4de0d54
--- /dev/null
+++ b/Source/SPHelpViewerController.h
@@ -0,0 +1,99 @@
+//
+// SPHelpViewerController.h
+// sequel-pro
+//
+// Created by Max Lohrmann on 21.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+// Parts 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 WebView;
+
+//private
+typedef NS_ENUM(NSUInteger, HelpTarget) {
+ HelpTargetMySQL = 0,
+ HelpTargetPage = 1,
+ HelpTargetWeb = 2,
+};
+
+NSString * const SPHelpViewerSearchTOC;
+
+/**
+ * This notification is posted by the SPHelpViewerController when the user
+ * triggered closing the help viewer window (or by -performClose:).
+ * The window is not guaranteed to be off screen already, when the notification is sent.
+ *
+ * It will NOT be sent when the window was closed or hidden by code (including app termination).
+ */
+NSString * const SPUserClosedHelpViewerNotification;
+
+@protocol SPHelpViewerDataSource <NSObject>
+
+@required
+/**
+ * When called with a search string this method should open the user's default browser
+ * with an URL to the MySQL online manual for the page that explains the search string.
+ */
+- (void)openOnlineHelpForTopic:(NSString *)searchString;
+
+/**
+ * This method is called by the SPHelpViewerController when it wants to receive the HTML
+ * page to display in response to a search string.
+ *
+ * The implementation has to handle the magic search string SPHelpViewerSearchTOC to
+ * return a table of contents document.
+ */
+- (NSString *)HTMLHelpContentsForSearchString:(NSString *)searchString autoHelp:(BOOL)autoHelp;
+
+@end
+
+/**
+ * This is the window controller class for the MySQL Help Viewer panel.
+ *
+ * See SPHelpViewerClient for the class that provides data for this controller and which
+ * can be instantiated from within an XIB.
+ *
+ * - Do NOT instantiate this class from within an XIB.
+ * - None of the methods in this class are thread-safe - always use the UI thread!
+ */
+@interface SPHelpViewerController : NSWindowController
+{
+ IBOutlet WebView *helpWebView;
+
+ IBOutlet NSSearchField *helpSearchField;
+ IBOutlet NSSearchFieldCell *helpSearchFieldCell;
+ IBOutlet NSSegmentedControl *helpNavigator;
+ IBOutlet NSSegmentedControl *helpTargetSelector;
+
+ HelpTarget helpTarget;
+
+ id<SPHelpViewerDataSource> dataSource;
+}
+
+@property (assign, nonatomic) id <SPHelpViewerDataSource> dataSource;
+
+- (void)showHelpFor:(NSString *)aString addToHistory:(BOOL)addToHistory calledByAutoHelp:(BOOL)autoHelp;
+
+@end
diff --git a/Source/SPHelpViewerController.m b/Source/SPHelpViewerController.m
new file mode 100644
index 00000000..a2c13000
--- /dev/null
+++ b/Source/SPHelpViewerController.m
@@ -0,0 +1,415 @@
+//
+// SPHelpViewerController.m
+// sequel-pro
+//
+// Created by Max Lohrmann on 21.05.18.
+// Copyright (c) 2018 Max Lohrmann. All rights reserved.
+// Parts 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 "SPHelpViewerController.h"
+
+#import <WebKit/WebKit.h>
+
+NSString * const SPHelpViewerSearchTOC = @"contents";
+
+NSString * const SPUserClosedHelpViewerNotification = @"SPUserClosedHelpViewer";
+
+typedef NS_ENUM(NSInteger, HelpNavButton) {
+ HelpNavButtonGoBack = 0,
+ HelpNavButtonShowTOC = 1,
+ HelpNavButtonGoForward = 2,
+};
+
+static void *HelpViewerControllerKVOContext = &HelpViewerControllerKVOContext;
+
+@interface SPHelpViewerController () <WebPolicyDelegate, WebUIDelegate, NSWindowDelegate>
+- (IBAction)showHelpForSearchString:(id)sender;
+- (IBAction)helpSegmentDispatcher:(id)sender;
+- (IBAction)helpSearchFindNextInPage:(id)sender;
+- (IBAction)helpSearchFindPreviousInPage:(id)sender;
+- (IBAction)helpTargetDispatcher:(id)sender;
+- (IBAction)helpSelectHelpTargetMySQL:(id)sender;
+- (IBAction)helpSelectHelpTargetPage:(id)sender;
+- (IBAction)helpSelectHelpTargetWeb:(id)sender;
+
+- (IBAction)showHelpForWebViewSelection:(id)sender;
+- (IBAction)searchInDocForWebViewSelection:(id)sender;
+- (void)helpTargetValidation;
+- (void)updateWindowTitle;
+@end
+
+#pragma mark -
+
+@implementation SPHelpViewerController
+
+@synthesize dataSource = dataSource;
+
+- (instancetype)init
+{
+ if ((self = [super initWithWindowNibName:@"HelpViewer"])) {
+ //force window to be loaded for simplicity
+ [self window];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [helpWebView removeObserver:self forKeyPath:@"mainFrameTitle"]; //TODO: update to ...context: variant after 10.6
+ [super dealloc];
+}
+
+- (void)windowDidLoad
+{
+ // init search history
+ [helpWebView setMaintainsBackForwardList:YES];
+ [[helpWebView backForwardList] setCapacity:20];
+
+ [self updateWindowTitle];
+
+ [helpWebView addObserver:self forKeyPath:@"mainFrameTitle" options:0 context:HelpViewerControllerKVOContext];
+}
+
+- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context
+{
+ if(context == HelpViewerControllerKVOContext) {
+ if([@"mainFrameTitle" isEqualToString:keyPath] && object == helpWebView) {
+ [self updateWindowTitle];
+ }
+ }
+ else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ }
+}
+
+- (void)updateWindowTitle
+{
+ NSString *title = NSLocalizedString(@"MySQL Help", @"mysql help");
+
+ NSString *webTitle = [helpWebView mainFrameTitle];
+ if([webTitle length]) title = [title stringByAppendingFormat:@" (%@)", webTitle];
+
+ [[self window] setTitle:title];
+}
+
+#pragma mark -
+#pragma mark MySQL Help
+
+/**
+ * Show the data for "HELP 'searchString'".
+ */
+- (void)showHelpFor:(NSString *)searchString addToHistory:(BOOL)addToHistory calledByAutoHelp:(BOOL)autoHelp
+{
+ // If there's no search string, ignore if called by autohelp, show the index otherwise
+ if (![searchString length]) {
+ if (autoHelp) return;
+ searchString = SPHelpViewerSearchTOC;
+ }
+
+ NSString *helpString = [dataSource HTMLHelpContentsForSearchString:searchString autoHelp:autoHelp];
+
+ // init the Help window if not visible
+ if(![[self window] isVisible]) {
+ // init goback/forward buttons
+ if([[helpWebView backForwardList] backListCount] < 1) {
+ [helpNavigator setEnabled:NO forSegment:HelpNavButtonGoBack];
+ [helpNavigator setEnabled:NO forSegment:HelpNavButtonGoForward];
+ }
+ else {
+ [helpNavigator setEnabled:([[helpWebView backForwardList] backListCount] != 0) forSegment:HelpNavButtonGoBack];
+ [helpNavigator setEnabled:([[helpWebView backForwardList] forwardListCount] != 0) forSegment:HelpNavButtonGoForward];
+ }
+
+ // set default to search in MySQL help
+ helpTarget = HelpTargetMySQL;
+ [helpTargetSelector setSelectedSegment:HelpTargetMySQL];
+ [self helpTargetValidation];
+
+ // show Help window
+ [[self window] orderFront:helpWebView];
+ }
+
+ if(![helpString length]) return;
+
+ // add searchString to history list
+ if(addToHistory) {
+ WebHistoryItem *aWebHistoryItem = [[WebHistoryItem alloc] initWithURLString:[NSString stringWithFormat:@"applewebdata://%@", searchString] title:searchString lastVisitedTimeInterval:[[NSDate date] timeIntervalSinceDate:[NSDate distantFuture]]];
+ [[helpWebView backForwardList] addItem:aWebHistoryItem];
+ [aWebHistoryItem release];
+ }
+
+ // validate goback/forward buttons
+ [helpNavigator setEnabled:([[helpWebView backForwardList] backListCount] != 0) forSegment:HelpNavButtonGoBack];
+ [helpNavigator setEnabled:([[helpWebView backForwardList] forwardListCount] != 0) forSegment:HelpNavButtonGoForward];
+
+ // load HTML formatted help into the webview
+ [[helpWebView mainFrame] loadHTMLString:helpString baseURL:nil];
+}
+
+/**
+ * Show the data for "HELP 'search word'" according to helpTarget
+ */
+- (IBAction)showHelpForSearchString:(id)sender
+{
+ NSString *searchString = [[helpSearchField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ switch(helpTarget) {
+ case HelpTargetPage:
+ if(![helpWebView searchFor:searchString direction:YES caseSensitive:NO wrap:YES]) {
+ if([searchString length]) NSBeep();
+ }
+ break;
+ case HelpTargetWeb:
+ if(![searchString length]) break;
+ [dataSource openOnlineHelpForTopic:searchString];
+ break;
+ case HelpTargetMySQL:
+ [self showHelpFor:searchString addToHistory:YES calledByAutoHelp:NO];
+ break;
+ }
+}
+
+/**
+ * Show the Help for the selected text in the webview
+ */
+- (IBAction)showHelpForWebViewSelection:(id)sender
+{
+ [self showHelpFor:[[helpWebView selectedDOMRange] text] addToHistory:YES calledByAutoHelp:NO];
+}
+
+/**
+ * Show MySQL's online documentation for the selected text in the webview
+ */
+- (IBAction)searchInDocForWebViewSelection:(id)sender
+{
+ NSString *searchString = [[[helpWebView selectedDOMRange] text] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ if(![searchString length]) {
+ NSBeep();
+ return;
+ }
+ [dataSource openOnlineHelpForTopic:searchString];
+}
+
+/**
+ * Find Next/Previous in current page
+ */
+- (IBAction)helpSearchFindNextInPage:(id)sender
+{
+ if(helpTarget == HelpTargetPage) {
+ if(![helpWebView searchFor:[[helpSearchField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] direction:YES caseSensitive:NO wrap:YES]) NSBeep();
+ }
+}
+
+- (IBAction)helpSearchFindPreviousInPage:(id)sender
+{
+ if(helpTarget == HelpTargetPage) {
+ if(![helpWebView searchFor:[[helpSearchField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] direction:NO caseSensitive:NO wrap:YES]) NSBeep();
+ }
+}
+
+/**
+ * Navigation for back/TOC/forward
+ */
+- (IBAction)helpSegmentDispatcher:(id)sender
+{
+ switch((HelpNavButton)[helpNavigator selectedSegment]) {
+ case HelpNavButtonGoBack:
+ [helpWebView goBack];
+ break;
+ case HelpNavButtonShowTOC:
+ [self showHelpFor:SPHelpViewerSearchTOC addToHistory:YES calledByAutoHelp:NO];
+ break;
+ case HelpNavButtonGoForward:
+ [helpWebView goForward];
+ break;
+ }
+
+ // validate goback and goforward buttons according history
+ [helpNavigator setEnabled:([[helpWebView backForwardList] backListCount] != 0) forSegment:HelpNavButtonGoBack];
+ [helpNavigator setEnabled:([[helpWebView backForwardList] forwardListCount] != 0) forSegment:HelpNavButtonGoForward];
+}
+
+/**
+ * Set helpTarget according user choice via mouse and keyboard short-cuts.
+ */
+- (IBAction)helpSelectHelpTargetMySQL:(id)sender
+{
+ helpTarget = HelpTargetMySQL;
+ [helpTargetSelector setSelectedSegment:HelpTargetMySQL];
+ [self helpTargetValidation];
+}
+
+- (IBAction)helpSelectHelpTargetPage:(id)sender
+{
+ helpTarget = HelpTargetPage;
+ [helpTargetSelector setSelectedSegment:HelpTargetPage];
+ [self helpTargetValidation];
+}
+
+- (IBAction)helpSelectHelpTargetWeb:(id)sender
+{
+ helpTarget = HelpTargetWeb;
+ [helpTargetSelector setSelectedSegment:HelpTargetWeb];
+ [self helpTargetValidation];
+}
+
+- (IBAction)helpTargetDispatcher:(id)sender
+{
+ helpTarget = (HelpTarget)[helpTargetSelector selectedSegment];
+ [self helpTargetValidation];
+}
+
+/**
+ * Control the help search field behaviour.
+ */
+- (void)helpTargetValidation
+{
+ switch(helpTarget) {
+ case HelpTargetPage:
+ case HelpTargetWeb:
+ [helpSearchFieldCell setSendsWholeSearchString:YES];
+ break;
+ case HelpTargetMySQL:
+ [helpSearchFieldCell setSendsWholeSearchString:NO];
+ break;
+ }
+}
+
+- (BOOL)windowShouldClose:(NSWindow *)sender
+{
+ // -windowShouldClose: is the only method that will ONLY be invoked when the user closes the window (or by -performClose:)
+ [[NSNotificationCenter defaultCenter] postNotificationName:SPUserClosedHelpViewerNotification object:self];
+ return YES;
+}
+
+#pragma mark -
+#pragma mark WebView delegate methods
+
+/**
+ * Link detector: If user clicked at an http link open it in the default browser,
+ * otherwise search for it in the MySQL help. Additionally handle back/forward events from
+ * keyboard and context menu.
+ */
+- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener
+{
+ NSInteger navigationType = [[actionInformation objectForKey:WebActionNavigationTypeKey] integerValue];
+
+ if([[[request URL] scheme] isEqualToString:@"applewebdata"] && navigationType == WebNavigationTypeLinkClicked) {
+ [self showHelpFor:[[[request URL] path] lastPathComponent] addToHistory:YES calledByAutoHelp:NO];
+ [listener ignore];
+ }
+ else {
+ if (navigationType == WebNavigationTypeOther) {
+ // catch reload event
+ // if([[[actionInformation objectForKey:WebActionOriginalURLKey] absoluteString] isEqualToString:@"about:blank"])
+ // [listener use];
+ // else
+ [listener use];
+ }
+ else if (navigationType == WebNavigationTypeLinkClicked) {
+ // show http in browser
+ [[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]];
+ [listener ignore];
+ }
+ else if (navigationType == WebNavigationTypeBackForward) {
+ // catch back/forward events from contextual menu
+ [self showHelpFor:[[[[actionInformation objectForKey:WebActionOriginalURLKey] absoluteString] lastPathComponent] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding] addToHistory:NO calledByAutoHelp:NO];
+ [listener ignore];
+ }
+ else {
+ // Ignore WebNavigationTypeFormSubmitted, WebNavigationTypeFormResubmitted, WebNavigationTypeReload.
+ [listener ignore];
+ }
+ }
+}
+
+/**
+ * Manage contextual menu in helpWebView
+ * Ignore "Reload", "Open Link", "Open Link in new Window", "Download link" etc.
+ */
+- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems
+{
+ NSMutableArray *webViewMenuItems = [[defaultMenuItems mutableCopy] autorelease];
+
+ if (webViewMenuItems) {
+ // Remove all needless default menu items
+ NSEnumerator *itemEnumerator = [defaultMenuItems objectEnumerator];
+ NSMenuItem *menuItem = nil;
+
+ while ((menuItem = [itemEnumerator nextObject])) {
+ NSInteger tag = [menuItem tag];
+
+ switch (tag) {
+ case 2000: // WebMenuItemTagOpenLink
+ case WebMenuItemTagOpenLinkInNewWindow:
+ case WebMenuItemTagDownloadLinkToDisk:
+ case WebMenuItemTagOpenImageInNewWindow:
+ case WebMenuItemTagDownloadImageToDisk:
+ case WebMenuItemTagCopyImageToClipboard:
+ case WebMenuItemTagOpenFrameInNewWindow:
+ case WebMenuItemTagStop:
+ case WebMenuItemTagReload:
+ case WebMenuItemTagCut:
+ case WebMenuItemTagPaste:
+ case WebMenuItemTagSpellingGuess:
+ case WebMenuItemTagNoGuessesFound:
+ case WebMenuItemTagIgnoreSpelling:
+ case WebMenuItemTagLearnSpelling:
+ case WebMenuItemTagOther:
+ case WebMenuItemTagOpenWithDefaultApplication:
+ [webViewMenuItems removeObjectIdenticalTo: menuItem];
+ break;
+ }
+ }
+ }
+
+ // Add two menu items for a selection if no link is given
+ if(webViewMenuItems
+ && [[element objectForKey:@"WebElementIsSelected"] boolValue]
+ && ![[element objectForKey:@"WebElementLinkIsLive"] boolValue])
+ {
+
+ NSMenuItem *searchInMySQL;
+ NSMenuItem *searchInMySQLonline;
+
+ [webViewMenuItems insertObject:[NSMenuItem separatorItem] atIndex:0];
+
+ searchInMySQLonline = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Search in MySQL Documentation", @"Search in MySQL Documentation") action:@selector(searchInDocForWebViewSelection:) keyEquivalent:@""];
+ [searchInMySQLonline setEnabled:YES];
+ [searchInMySQLonline setTarget:self];
+ [webViewMenuItems insertObject:searchInMySQLonline atIndex:0];
+ [searchInMySQLonline release];
+
+ searchInMySQL = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Search in MySQL Help", @"Search in MySQL Help") action:@selector(showHelpForWebViewSelection:) keyEquivalent:@""];
+ [searchInMySQL setEnabled:YES];
+ [searchInMySQL setTarget:self];
+ [webViewMenuItems insertObject:searchInMySQL atIndex:0];
+ [searchInMySQL release];
+ }
+
+ return webViewMenuItems;
+}
+
+@end
diff --git a/Source/SPTextView.m b/Source/SPTextView.m
index de1ddc04..6b98217c 100644
--- a/Source/SPTextView.m
+++ b/Source/SPTextView.m
@@ -50,6 +50,7 @@
#import "SPCopyTable.h"
#import "SPEditorTokens.h"
#import "SPSyntaxParser.h"
+#import "SPHelpViewerClient.h"
#import <SPMySQL/SPMySQL.h>
@@ -1107,7 +1108,7 @@ retry:
*/
- (IBAction) showMySQLHelpForCurrentWord:(id)sender
{
- [customQueryInstance showHelpForCurrentWord:self];
+ [[tableDocumentInstance helpViewerClient] showHelpForCurrentWord:self];
}
#endif
diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj
index 16a3957d..435e2b82 100644
--- a/sequel-pro.xcodeproj/project.pbxproj
+++ b/sequel-pro.xcodeproj/project.pbxproj
@@ -391,8 +391,10 @@
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 */; };
+ 9BE765682376A00C82FB93AA /* SPHelpViewerClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE764320CE8E86E8F63647B /* SPHelpViewerClient.m */; };
9BE765EBBDFD2F121C13D274 /* SPFillView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE768F3989033CEDDC2027E /* SPFillView.m */; };
9BE76F2886901784E4FD2321 /* SPFilterTableController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE76A3D5C9830E2F7738770 /* SPFilterTableController.m */; };
+ 9BE76F2B943AFDBA6EDC52BE /* SPHelpViewerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BE760B3C4586EA3B1A48600 /* SPHelpViewerController.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 */; };
@@ -418,6 +420,7 @@
BA6B044120A4FEDC00B012E1 /* button_filter_active.png in Resources */ = {isa = PBXBuildFile; fileRef = BA6B043F20A4FEDB00B012E1 /* button_filter_active.png */; };
BA6B044220A4FEDC00B012E1 /* button_filter.png in Resources */ = {isa = PBXBuildFile; fileRef = BA6B044020A4FEDC00B012E1 /* button_filter.png */; };
BAC6BAF920A0D22400247837 /* FilterTableWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = BAC6BAF720A0D22400247837 /* FilterTableWindow.xib */; };
+ BAE25E0120B32BDA00DD9A40 /* HelpViewer.xib in Resources */ = {isa = PBXBuildFile; fileRef = BAE25DFF20B32BDA00DD9A40 /* HelpViewer.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 */; };
@@ -1134,8 +1137,12 @@
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; };
+ 9BE760B3C4586EA3B1A48600 /* SPHelpViewerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPHelpViewerController.m; sourceTree = "<group>"; };
+ 9BE761AE45D4F6B9B90E67DE /* SPHelpViewerClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPHelpViewerClient.h; sourceTree = "<group>"; };
+ 9BE764320CE8E86E8F63647B /* SPHelpViewerClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPHelpViewerClient.m; sourceTree = "<group>"; };
9BE768F3989033CEDDC2027E /* SPFillView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFillView.m; sourceTree = "<group>"; };
9BE76A3D5C9830E2F7738770 /* SPFilterTableController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPFilterTableController.m; sourceTree = "<group>"; };
+ 9BE76C6E8377959C794385E5 /* SPHelpViewerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPHelpViewerController.h; sourceTree = "<group>"; };
9BE76CC0CCE3A4A74E3E8D5E /* SPFillView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPFillView.h; sourceTree = "<group>"; };
9BE76F9BF9BDA2921CDD05AF /* SPFilterTableController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPFilterTableController.h; sourceTree = "<group>"; };
B51D6B9D114C310C0074704E /* toolbar-switch-to-table-triggers.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar-switch-to-table-triggers.png"; sourceTree = "<group>"; };
@@ -1169,6 +1176,7 @@
BA6B043F20A4FEDB00B012E1 /* button_filter_active.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = button_filter_active.png; sourceTree = "<group>"; };
BA6B044020A4FEDC00B012E1 /* button_filter.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = button_filter.png; sourceTree = "<group>"; };
BAC6BAF820A0D22400247837 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/FilterTableWindow.xib; sourceTree = "<group>"; };
+ BAE25E0020B32BDA00DD9A40 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/HelpViewer.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>"; };
@@ -1575,6 +1583,10 @@
17846B9D170C95D800414499 /* Process List */,
9BE76A3D5C9830E2F7738770 /* SPFilterTableController.m */,
9BE76F9BF9BDA2921CDD05AF /* SPFilterTableController.h */,
+ 9BE760B3C4586EA3B1A48600 /* SPHelpViewerController.m */,
+ 9BE76C6E8377959C794385E5 /* SPHelpViewerController.h */,
+ 9BE764320CE8E86E8F63647B /* SPHelpViewerClient.m */,
+ 9BE761AE45D4F6B9B90E67DE /* SPHelpViewerClient.h */,
);
name = "Subview Controllers";
sourceTree = "<group>";
@@ -2183,6 +2195,7 @@
B58DA7390FF8BB9E00FDDACD /* SSHQuestionDialog.xib */,
4D90B7A0101E0D1500D116A1 /* UserManagerView.xib */,
BAC6BAF720A0D22400247837 /* FilterTableWindow.xib */,
+ BAE25DFF20B32BDA00DD9A40 /* HelpViewer.xib */,
);
path = Interfaces;
sourceTree = "<group>";
@@ -2988,6 +3001,7 @@
582E942016835DD4003459FD /* button_edit_mode.png in Resources */,
582E942416835EA9003459FD /* button_edit.png in Resources */,
582E942616835FAE003459FD /* button_remove.png in Resources */,
+ BAE25E0120B32BDA00DD9A40 /* HelpViewer.xib in Resources */,
582E942B1683628C003459FD /* button_select_all.png in Resources */,
582E942C1683628C003459FD /* button_select_none.png in Resources */,
582E942E1683658A003459FD /* clearconsole.png in Resources */,
@@ -3299,6 +3313,8 @@
1A564F74237E2E4958CA593A /* SPPillAttachmentCell.m in Sources */,
9BE76F2886901784E4FD2321 /* SPFilterTableController.m in Sources */,
9BE765EBBDFD2F121C13D274 /* SPFillView.m in Sources */,
+ 9BE76F2B943AFDBA6EDC52BE /* SPHelpViewerController.m in Sources */,
+ 9BE765682376A00C82FB93AA /* SPHelpViewerClient.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3582,6 +3598,14 @@
name = FilterTableWindow.xib;
sourceTree = "<group>";
};
+ BAE25DFF20B32BDA00DD9A40 /* HelpViewer.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ BAE25E0020B32BDA00DD9A40 /* English */,
+ );
+ name = HelpViewer.xib;
+ sourceTree = "<group>";
+ };
BC30C00F111C98BD002701C9 /* DataMigrationDialog.xib */ = {
isa = PBXVariantGroup;
children = (