Issue #1482: enable SQL help window

This commit is contained in:
Ansgar Becker
2025-03-20 16:33:53 +01:00
parent 0bbc72cac7
commit 3de5774f91
5 changed files with 779 additions and 6 deletions

View File

@ -229,6 +229,13 @@
<Filename Value="source\jsonregistry.pas"/> <Filename Value="source\jsonregistry.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
</Unit> </Unit>
<Unit>
<Filename Value="source\sqlhelp.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="frmSQLhelp"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
</Unit>
</Units> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>

View File

@ -19,7 +19,7 @@ uses
dbstructures.sqlite, change_password, loginform, data_sorting, extra_controls, dbstructures.sqlite, change_password, loginform, data_sorting, extra_controls,
column_selection, loaddata, csv_detector, createdatabase, editvar, copytable, column_selection, loaddata, csv_detector, createdatabase, editvar, copytable,
exportgrid, usermanager, selectdbobject, reformatter, searchreplace, exportgrid, usermanager, selectdbobject, reformatter, searchreplace,
connections, jsonregistry {, printlist (EnablePrint not defined) } connections, jsonregistry, sqlhelp {, printlist (EnablePrint not defined) }
; ;
{$R *.res} {$R *.res}

View File

@ -1415,7 +1415,7 @@ implementation
uses uses
FileInfo, winpeimagereader, elfreader, machoreader, About, data_sorting, column_selection, loaddata, editvar, FileInfo, winpeimagereader, elfreader, machoreader, About, data_sorting, column_selection, loaddata, editvar,
copytable, csv_detector, exportgrid, usermanager, selectdbobject, reformatter, connections; copytable, csv_detector, exportgrid, usermanager, selectdbobject, reformatter, connections, sqlhelp;
{$R *.lfm} {$R *.lfm}
@ -4903,10 +4903,10 @@ end;
procedure TMainform.CallSQLHelpWithKeyword( keyword: String ); procedure TMainform.CallSQLHelpWithKeyword( keyword: String );
begin begin
if FActiveDbObj.Connection.Has(frHelpKeyword) then begin if FActiveDbObj.Connection.Has(frHelpKeyword) then begin
{if not Assigned(SqlHelpDialog) then if not Assigned(SqlHelpDialog) then
SqlHelpDialog := TfrmSQLhelp.Create(Self); SqlHelpDialog := TfrmSQLhelp.Create(Self);
SqlHelpDialog.Show; SqlHelpDialog.Show;
SqlHelpDialog.Keyword := keyword;} SqlHelpDialog.Keyword := keyword;
end else end else
ErrorDialog(_('SQL help not available.'), f_('HELP <keyword> requires %s or newer.', ['MySQL 4.1'])); ErrorDialog(_('SQL help not available.'), f_('HELP <keyword> requires %s or newer.', ['MySQL 4.1']));
end; end;
@ -13273,10 +13273,10 @@ begin
Editors.Add(frmPreferences.SynMemoSQLSample);} Editors.Add(frmPreferences.SynMemoSQLSample);}
if Assigned(FCreateDatabaseDialog) then if Assigned(FCreateDatabaseDialog) then
Editors.Add(FCreateDatabaseDialog.SynMemoCreateCode); Editors.Add(FCreateDatabaseDialog.SynMemoCreateCode);
{if SqlHelpDialog <> nil then begin if SqlHelpDialog <> nil then begin
Editors.Add(SqlHelpDialog.memoDescription); Editors.Add(SqlHelpDialog.memoDescription);
Editors.Add(SqlHelpDialog.MemoExample); Editors.Add(SqlHelpDialog.MemoExample);
end;} end;
{if Assigned(FTableToolsDialog) then {if Assigned(FTableToolsDialog) then
Editors.Add(FTableToolsDialog.SynMemoFindText);} Editors.Add(FTableToolsDialog.SynMemoFindText);}
if Assigned(frmCsvDetector) then if Assigned(frmCsvDetector) then

339
source/sqlhelp.lfm Normal file
View File

@ -0,0 +1,339 @@
object frmSQLhelp: TfrmSQLhelp
Left = 0
Height = 444
Top = 0
Width = 728
BorderWidth = 10
Caption = 'Integrated SQL-help'
ClientHeight = 444
ClientWidth = 728
Color = clBtnFace
DesignTimePPI = 120
FormStyle = fsStayOnTop
OnClose = FormClose
OnCreate = FormCreate
OnShow = FormShow
object pnlMain: TPanel
Left = 10
Height = 382
Top = 10
Width = 708
Align = alClient
BevelOuter = bvNone
ClientHeight = 382
ClientWidth = 708
ParentBackground = False
TabOrder = 0
object Splitter1: TSplitter
Cursor = crSizeWE
Left = 191
Height = 382
Top = 0
Width = 10
end
object pnlLeft: TPanel
Left = 0
Height = 382
Top = 0
Width = 191
Align = alLeft
BevelOuter = bvNone
ClientHeight = 382
ClientWidth = 191
ParentBackground = False
TabOrder = 0
object editFilter: TEditButton
Left = 0
Height = 28
Top = 0
Width = 191
Align = alTop
ButtonWidth = 29
Images = MainForm.ImageListIcons8
ImageIndex = 193
MaxLength = 0
NumGlyphs = 1
OnButtonClick = editFilterRightButtonClick
OnChange = editFilterChange
PasswordChar = #0
TabOrder = 0
TextHint = 'Filter'
end
object treeTopics: TLazVirtualStringTree
Left = 0
Height = 354
Top = 28
Width = 191
Align = alClient
Constraints.MinWidth = 38
Header.AutoSizeIndex = 0
Header.Columns = <>
Header.MainColumn = -1
Images = MainForm.ImageListIcons8
TabOrder = 1
TreeOptions.PaintOptions = [toHotTrack, toShowButtons, toShowDropmark, toShowRoot, toShowTreeLines, toThemeAware, toUseBlendedImages, toUseExplorerTheme, toHideTreeLinesIfThemed]
OnFocusChanged = treeTopicsFocusChanged
OnFreeNode = treeTopicsFreeNode
OnGetText = treeTopicsGetText
OnGetImageIndex = treeTopicsGetImageIndex
OnGetNodeDataSize = treeTopicsGetNodeDataSize
OnInitChildren = treeTopicsInitChildren
OnInitNode = treeTopicsInitNode
end
end
object pnlRight: TPanel
Left = 201
Height = 382
Top = 0
Width = 507
Align = alClient
BevelOuter = bvNone
ClientHeight = 382
ClientWidth = 507
ParentBackground = False
TabOrder = 1
object Splitter2: TSplitter
Cursor = crVSplit
Left = 0
Height = 10
Top = 212
Width = 507
Align = alTop
ResizeAnchor = akTop
end
object lblDescription: TLabel
Left = 0
Height = 20
Top = 1
Width = 507
Align = alTop
Caption = 'Description:'
end
object lblKeyword: TLabel
Left = 0
Height = 1
Top = 0
Width = 507
Align = alTop
ShowAccelChar = False
end
object lblExample: TLabel
Left = 0
Height = 20
Top = 222
Width = 507
Align = alTop
Caption = 'Example:'
end
inline memoDescription: TSynEdit
Left = 0
Height = 191
Top = 21
Width = 507
Align = alTop
Constraints.MinHeight = 38
Font.Height = -16
Font.Name = 'Courier New'
Font.Pitch = fpFixed
Font.Quality = fqNonAntialiased
ParentColor = False
ParentFont = False
TabOrder = 0
OnKeyDown = memosKeyDown
Gutter.Visible = False
Gutter.Width = 72
Gutter.MouseActions = <>
RightGutter.Width = 0
RightGutter.MouseActions = <>
Keystrokes = <>
MouseActions = <>
MouseTextActions = <>
MouseSelActions = <>
Options = [eoAutoIndent, eoGroupUndo, eoShowScrollHint, eoSmartTabs, eoTabsToSpaces, eoDragDropEditing]
MouseOptions = [emDragDropEditing]
VisibleSpecialChars = [vscSpace, vscTabAtLast]
ReadOnly = True
RightEdge = 0
SelectedColor.BackPriority = 50
SelectedColor.ForePriority = 50
SelectedColor.FramePriority = 50
SelectedColor.BoldPriority = 50
SelectedColor.ItalicPriority = 50
SelectedColor.UnderlinePriority = 50
SelectedColor.StrikeOutPriority = 50
BracketHighlightStyle = sbhsBoth
BracketMatchColor.Background = clNone
BracketMatchColor.Foreground = clNone
BracketMatchColor.Style = [fsBold]
FoldedCodeColor.Background = clNone
FoldedCodeColor.Foreground = clGray
FoldedCodeColor.FrameColor = clGray
MouseLinkColor.Background = clNone
MouseLinkColor.Foreground = clBlue
LineHighlightColor.Background = clNone
LineHighlightColor.Foreground = clNone
inline SynLeftGutterPartList1: TSynGutterPartList
object SynGutterMarks1: TSynGutterMarks
Width = 30
MouseActions = <>
end
object SynGutterLineNumber1: TSynGutterLineNumber
Width = 21
MouseActions = <>
MarkupInfo.Background = clBtnFace
MarkupInfo.Foreground = clNone
DigitCount = 2
ShowOnlyLineNumbersMultiplesOf = 1
ZeroStart = False
LeadingZeros = False
end
object SynGutterChanges1: TSynGutterChanges
Width = 5
MouseActions = <>
ModifiedColor = 59900
SavedColor = clGreen
end
object SynGutterSeparator1: TSynGutterSeparator
Width = 3
MouseActions = <>
MarkupInfo.Background = clWhite
MarkupInfo.Foreground = clGray
end
object SynGutterCodeFolding1: TSynGutterCodeFolding
Width = 13
MouseActions = <>
MarkupInfo.Background = clNone
MarkupInfo.Foreground = clGray
MouseActionsExpanded = <>
MouseActionsCollapsed = <>
end
end
end
inline MemoExample: TSynEdit
Left = 0
Height = 140
Top = 242
Width = 507
Align = alClient
Constraints.MinHeight = 38
Font.Height = -16
Font.Name = 'Courier New'
Font.Pitch = fpFixed
Font.Quality = fqNonAntialiased
ParentColor = False
ParentFont = False
TabOrder = 1
OnKeyDown = memosKeyDown
Gutter.Visible = False
Gutter.Width = 72
Gutter.MouseActions = <>
RightGutter.Width = 0
RightGutter.MouseActions = <>
Keystrokes = <>
MouseActions = <>
MouseTextActions = <>
MouseSelActions = <>
Options = [eoAutoIndent, eoGroupUndo, eoShowScrollHint, eoSmartTabs, eoTabsToSpaces, eoDragDropEditing]
MouseOptions = [emDragDropEditing]
VisibleSpecialChars = [vscSpace, vscTabAtLast]
ReadOnly = True
RightEdge = 0
SelectedColor.BackPriority = 50
SelectedColor.ForePriority = 50
SelectedColor.FramePriority = 50
SelectedColor.BoldPriority = 50
SelectedColor.ItalicPriority = 50
SelectedColor.UnderlinePriority = 50
SelectedColor.StrikeOutPriority = 50
BracketHighlightStyle = sbhsBoth
BracketMatchColor.Background = clNone
BracketMatchColor.Foreground = clNone
BracketMatchColor.Style = [fsBold]
FoldedCodeColor.Background = clNone
FoldedCodeColor.Foreground = clGray
FoldedCodeColor.FrameColor = clGray
MouseLinkColor.Background = clNone
MouseLinkColor.Foreground = clBlue
LineHighlightColor.Background = clNone
LineHighlightColor.Foreground = clNone
inline SynLeftGutterPartList1: TSynGutterPartList
object SynGutterMarks1: TSynGutterMarks
Width = 30
MouseActions = <>
end
object SynGutterLineNumber1: TSynGutterLineNumber
Width = 21
MouseActions = <>
MarkupInfo.Background = clBtnFace
MarkupInfo.Foreground = clNone
DigitCount = 2
ShowOnlyLineNumbersMultiplesOf = 1
ZeroStart = False
LeadingZeros = False
end
object SynGutterChanges1: TSynGutterChanges
Width = 5
MouseActions = <>
ModifiedColor = 59900
SavedColor = clGreen
end
object SynGutterSeparator1: TSynGutterSeparator
Width = 3
MouseActions = <>
MarkupInfo.Background = clWhite
MarkupInfo.Foreground = clGray
end
object SynGutterCodeFolding1: TSynGutterCodeFolding
Width = 13
MouseActions = <>
MarkupInfo.Background = clNone
MarkupInfo.Foreground = clGray
MouseActionsExpanded = <>
MouseActionsCollapsed = <>
end
end
end
end
end
object pnlBottom: TPanel
Left = 10
Height = 42
Top = 392
Width = 708
Align = alBottom
BevelOuter = bvNone
ClientHeight = 42
ClientWidth = 708
ParentBackground = False
TabOrder = 1
object btnSearchOnline: TSpeedButton
Left = 444
Height = 31
Top = 8
Width = 129
Anchors = [akTop, akRight]
Caption = 'Search online'
Images = MainForm.ImageListIcons8
ImageIndex = 69
OnClick = ButtonOnlinehelpClick
end
object ButtonClose: TButton
Left = 580
Height = 31
Top = 8
Width = 128
Anchors = [akTop, akRight]
Cancel = True
Caption = 'Close'
Default = True
TabOrder = 0
OnClick = ButtonCloseClick
end
end
object timerSearch: TTimer
Interval = 500
OnTimer = DoSearch
Left = 10
Top = 400
end
end

427
source/sqlhelp.pas Normal file
View File

@ -0,0 +1,427 @@
unit sqlhelp;
{$mode delphi}{$H+}
interface
uses
SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, ComCtrls, ExtCtrls,
Buttons, SynEdit, SynEditHighlighter, extra_controls,
laz.VirtualTrees, Graphics, EditBtn,
dbconnection;
type
{ TfrmSQLhelp }
TfrmSQLhelp = class(TExtForm)
//URIOpenerDescription: TSynURIOpener;
//URIHighlighter: TSynURISyn;
//URIOpenerExample: TSynURIOpener;
btnSearchOnline: TSpeedButton;
ButtonClose: TButton;
pnlBottom: TPanel;
pnlMain: TPanel;
pnlLeft: TPanel;
treeTopics: TLazVirtualStringTree;
editFilter: TEditButton;
pnlRight: TPanel;
Splitter2: TSplitter;
Splitter1: TSplitter;
lblDescription: TLabel;
lblKeyword: TLabel;
memoDescription: TSynEdit;
lblExample: TLabel;
MemoExample: TSynEdit;
timerSearch: TTimer;
procedure FormCreate(Sender: TObject);
procedure memosKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure ButtonOnlinehelpClick(Sender: TObject);
procedure ButtonCloseClick(Sender: TObject);
procedure DoSearch(Sender: TObject);
procedure treeTopicsGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
procedure treeTopicsInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
var InitialStates: TVirtualNodeInitStates);
procedure treeTopicsInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
var ChildCount: Cardinal);
procedure treeTopicsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
procedure treeTopicsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
procedure treeTopicsFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
procedure treeTopicsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex);
procedure editFilterChange(Sender: TObject);
procedure editFilterRightButtonClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
FConnection: TDBConnection;
FKeyword: String;
FRootTopics: TDBQuery;
function GetHelpResult(Node: PVirtualNode): TDBQuery;
procedure SetKeyword(Value: String);
public
{ Public declarations }
property Keyword: String read FKeyword write SetKeyword;
end;
var
SqlHelpDialog: TfrmSQLhelp; // global var, so we can apply settings via SetupSynEditors
const
DEFAULT_WINDOW_CAPTION : String = 'Integrated SQL-help' ;
ICONINDEX_CATEGORY_CLOSED : Integer = 66;
ICONINDEX_CATEGORY_OPENED : Integer = 67;
ICONINDEX_HELPITEM : Integer = 68;
implementation
uses apphelpers, main;
{$I const.inc}
{$R *.lfm}
procedure TfrmSQLhelp.FormCreate(Sender: TObject);
begin
// Set window-layout
lblKeyword.Font.Style := [fsBold];
Caption := DEFAULT_WINDOW_CAPTION;
FixVT(treeTopics);
HasSizeGrip := True;
treeTopics.Clear;
FreeAndNil(FRootTopics);
FConnection := MainForm.ActiveConnection;
FRootTopics := FConnection.GetResults('HELP '+FConnection.EscapeString('CONTENTS'));
treeTopics.RootNodeCount := FRootTopics.RecordCount;
end;
procedure TfrmSQLhelp.FormClose(Sender: TObject; var Action: TCloseAction);
begin
AppSettings.WriteInt(asSQLHelpWindowLeft, Left );
AppSettings.WriteInt(asSQLHelpWindowTop, Top );
AppSettings.WriteIntDpiAware(asSQLHelpWindowWidth, Self, Width);
AppSettings.WriteIntDpiAware(asSQLHelpWindowHeight, Self, Height);
AppSettings.WriteIntDpiAware(asSQLHelpPnlLeftWidth, Self, pnlLeft.Width);
AppSettings.WriteIntDpiAware(asSQLHelpPnlRightTopHeight, Self, memoDescription.Height);
Action := caFree;
SqlHelpDialog := nil;
end;
procedure TfrmSQLhelp.treeTopicsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex);
var
Results: TDBQuery;
VT: TVirtualStringTree;
begin
// Topic selected
VT := Sender as TVirtualStringTree;
if not Assigned(VT.FocusedNode) then
Exit;
if VT.HasChildren[VT.FocusedNode] then
Exit;
FKeyword := VT.Text[VT.FocusedNode, VT.FocusedColumn];
lblKeyword.Caption := Copy(FKeyword, 0, 100);
MemoDescription.Lines.Clear;
MemoExample.Lines.Clear;
Caption := DEFAULT_WINDOW_CAPTION;
if FKeyword <> '' then try
Screen.Cursor := crHourglass;
Results := FConnection.GetResults('HELP '+FConnection.EscapeString(FKeyword));
Caption := Caption + ' - ' + FKeyword;
MemoDescription.Text := fixNewlines(Results.Col('description', True));
MemoExample.Text := fixNewlines(Results.Col('example', True));
finally
FreeAndNil(Results);
Screen.Cursor := crDefault;
end;
// Show the user if topic is (not) available
if memoDescription.GetTextLen = 0 then
memoDescription.Text := _('No help available for this keyword or no keyword was selected.');
if memoExample.GetTextLen = 0 then
memoExample.Text := _('No example available or no keyword was selected.');
end;
procedure TfrmSQLhelp.treeTopicsFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
Results: PDBQuery;
begin
// Node gets destroyed - free memory used for bound SQL result
Results := Sender.GetNodeData(Node);
if Assigned(Results^) then
Results^.Free;
end;
procedure TfrmSQLhelp.treeTopicsGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode;
Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: TImageIndex);
begin
// Return open or closed book icon for folders, or document icon for topics
if not (Kind in [ikNormal, ikSelected]) then
Exit;
if Sender.HasChildren[Node] then begin
if Sender.Expanded[Node] then
ImageIndex := ICONINDEX_CATEGORY_OPENED
else
ImageIndex := ICONINDEX_CATEGORY_CLOSED;
end else
ImageIndex := ICONINDEX_HELPITEM;
end;
procedure TfrmSQLhelp.treeTopicsGetNodeDataSize(Sender: TBaseVirtualTree;
var NodeDataSize: Integer);
begin
// We bind one TDBQuery to a node
NodeDataSize := SizeOf(TDBQuery);
end;
function TfrmSQLhelp.GetHelpResult(Node: PVirtualNode): TDBQuery;
var
P: PDBQuery;
begin
// Find right result set for given node
if treeTopics.GetNodeLevel(Node) = 0 then
Result := FRootTopics
else begin
P := treeTopics.GetNodeData(Node.Parent);
Result := P^;
end;
end;
procedure TfrmSQLhelp.treeTopicsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
Results: TDBQuery;
begin
// Ask result set for node text
Results := GetHelpResult(Node);
Results.RecNo := Node.Index;
CellText := Results.Col('name');
end;
procedure TfrmSQLhelp.treeTopicsInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
var ChildCount: Cardinal);
var
Results: PDBQuery;
VT: TVirtualStringTree;
begin
// Return number of children for folder
VT := Sender as TVirtualStringTree;
Results := VT.GetNodeData(Node);
Results^ := FConnection.GetResults('HELP '+FConnection.EscapeString(VT.Text[Node, VT.Header.MainColumn]));
ChildCount := Results.RecordCount;
end;
procedure TfrmSQLhelp.treeTopicsInitNode(Sender: TBaseVirtualTree; ParentNode,
Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
Results: TDBQuery;
ThisFolder, PrevFolder: String;
N: PVirtualNode;
VT: TVirtualStringTree;
RecursionAlarm: Boolean;
begin
// Display plus button for nodes which are folders
VT := Sender as TVirtualStringTree;
Results := GetHelpResult(Node);
Results.RecNo := Node.Index;
if Results.Col('is_it_category', True) = 'Y' then begin
// Some random server versions have duplicated category names in help tables, which would cause
// infinite tree recursion, e.g. for "Polygon properties" > "Contents". Do not display these
// duplicates as folder
RecursionAlarm := False;
ThisFolder := Results.Col('name');
N := VT.GetPreviousInitialized(Node);
while Assigned(N) do begin
PrevFolder := VT.Text[N, VT.Header.MainColumn];
if VT.HasChildren[N] and ((ThisFolder=PrevFolder) or (ThisFolder='Contents')) then begin
RecursionAlarm := True;
break;
end;
N := VT.GetPreviousInitialized(N);
end;
if not RecursionAlarm then
Include(InitialStates, ivsHasChildren);
end;
end;
procedure TfrmSQLhelp.ButtonCloseClick(Sender: TObject);
begin
Close;
end;
procedure TfrmSQLhelp.FormShow(Sender: TObject);
begin
Top := AppSettings.ReadInt(asSQLHelpWindowTop);
Left := AppSettings.ReadInt(asSQLHelpWindowLeft);
Width := AppSettings.ReadIntDpiAware(asSQLHelpWindowWidth, Self);
Height := AppSettings.ReadIntDpiAware(asSQLHelpWindowHeight, Self);
MakeFullyVisible;
pnlLeft.Width := AppSettings.ReadIntDpiAware(asSQLHelpPnlLeftWidth, Self);
memoDescription.Height := AppSettings.ReadIntDpiAware(asSQLHelpPnlRightTopHeight, Self);
// Apply themed colors in OnShow, not OnCreate, as a check with <> nil returns false otherwise
MainForm.SetupSynEditors(Self);
// These SynMemo's don't have any (SQL) highligher, so we have to assign correct colors for basic text
memoDescription.Font.Color := GetThemeColor(clWindowText);
MemoExample.Font.Color := GetThemeColor(clWindowText);
editFilter.SetFocus;
end;
procedure TfrmSQLhelp.ButtonOnlinehelpClick(Sender: TObject);
begin
// Link/redirect to mysql.com for further help
ShellExec(APPDOMAIN + 'sqlhelp.php?mysqlversion='+inttostr(FConnection.ServerVersionInt)+
'&keyword='+EncodeURLParam(keyword));
end;
procedure TfrmSQLhelp.memosKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
// Esc pressed - close form.
// Seems that if we're in a memo, the ButtonClose.Cancel=True doesn't have an effect
//if Key = VK_ESCAPE then
// Close;
end;
procedure TfrmSQLhelp.SetKeyword(Value: string);
var
VT: TVirtualStringTree;
Node: PVirtualNode;
Results: TDBQuery;
SearchNoInit: Boolean;
begin
// Find keyword in tree
FKeyword := Value;
if FKeyword = '' then
Exit;
Results := FConnection.GetResults('HELP '+FConnection.EscapeString(FKeyword));
while not Results.Eof do begin
if Results.Col('is_it_category', true) = 'N' then begin
FKeyword := Results.Col('name');
break;
end;
Results.Next;
end;
FreeAndNil(Results);
VT := treeTopics;
if (not Assigned(VT.FocusedNode)) // No node selected
or VT.HasChildren[VT.FocusedNode] // Selected node is a folder, not a document
or (VT.Text[VT.FocusedNode, VT.Header.MainColumn] <> FKeyword) // Node is not the right one
then begin
// Start searching in initialized nodes, to minimize number of "HELP xyz" queries in certain cases
Node := VT.GetFirst;
SearchNoInit := False;
while Assigned(Node) do begin
if (not VT.HasChildren[Node]) and (UpperCase(VT.Text[Node, VT.Header.MainColumn]) = UpperCase(FKeyword)) then begin
SelectNode(VT, Node);
break;
end;
if not SearchNoInit then begin
Node := VT.GetNextInitialized(Node);
if not Assigned(Node) then begin
SearchNoInit := True;
Node := VT.GetFirst;
end;
end;
if SearchNoInit then
Node := VT.GetNext(Node);
end;
end;
end;
procedure TfrmSQLhelp.editFilterChange(Sender: TObject);
begin
timerSearch.Enabled := False;
timerSearch.Enabled := True;
editFilter.Button.Enabled := Trim(editFilter.Text) <> '';
end;
procedure TfrmSQLhelp.editFilterRightButtonClick(Sender: TObject);
begin
editFilter.Clear;
end;
procedure TfrmSQLhelp.DoSearch(Sender: TObject);
var
Node: PVirtualNode;
Search: String;
Vis: Boolean;
function HasVisibleChildItems(Node: PVirtualNode): Boolean;
var
N: PVirtualNode;
begin
N := treeTopics.GetFirstChild(Node);
Result := False;
while Assigned(N) do begin
if treeTopics.HasChildren[N] then
Result := HasVisibleChildItems(N)
else
Result := treeTopics.IsVisible[N];
if Result then
Exit;
N := treeTopics.GetNextSibling(N);
end;
end;
begin
// Apply filter text
Screen.Cursor := crHourglass;
treeTopics.BeginUpdate;
timerSearch.Enabled := False;
Search := Trim(editFilter.Text);
if Search = '' then begin
// Show all items
Node := treeTopics.GetFirstInitialized;
while Assigned(Node) do begin
treeTopics.IsVisible[Node] := True;
Node := treeTopics.GetNextInitialized(Node);
end;
end else begin
// Hide non matching child items
Node := treeTopics.GetFirst;
while Assigned(Node) do begin
if not treeTopics.HasChildren[Node] then
treeTopics.IsVisible[Node] := Pos(UpperCase(Search), UpperCase(treeTopics.Text[Node, treeTopics.Header.MainColumn])) > 0;
Node := treeTopics.GetNext(Node);
end;
// Hide empty folders
Node := treeTopics.GetFirst;
while Assigned(Node) do begin
if treeTopics.HasChildren[Node] then begin
Vis := HasVisibleChildItems(Node);
treeTopics.Expanded[Node] := Vis;
treeTopics.IsVisible[Node] := Vis;
end;
Node := treeTopics.GetNext(Node);
end;
end;
treeTopics.EndUpdate;
Screen.Cursor := crDefault;
end;
end.