diff options
-rw-r--r-- | Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m | 1 | ||||
-rw-r--r-- | Interfaces/English.lproj/DBView.xib | 196 | ||||
-rw-r--r-- | Interfaces/English.lproj/HelpViewer.xib | 213 | ||||
-rw-r--r-- | Resources/Templates/SPMySQLHelpTemplate.html | 19 | ||||
-rw-r--r-- | Source/SPCustomQuery.h | 40 | ||||
-rw-r--r-- | Source/SPCustomQuery.m | 531 | ||||
-rw-r--r-- | Source/SPDatabaseDocument.h | 4 | ||||
-rw-r--r-- | Source/SPDatabaseDocument.m | 14 | ||||
-rw-r--r-- | Source/SPHelpViewerClient.h | 66 | ||||
-rw-r--r-- | Source/SPHelpViewerClient.m | 262 | ||||
-rw-r--r-- | Source/SPHelpViewerController.h | 99 | ||||
-rw-r--r-- | Source/SPHelpViewerController.m | 415 | ||||
-rw-r--r-- | Source/SPTextView.m | 3 | ||||
-rw-r--r-- | sequel-pro.xcodeproj/project.pbxproj | 24 |
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 = ( |