From 3de5774f915db4925892bc73f9df7a1d91b61a87 Mon Sep 17 00:00:00 2001 From: Ansgar Becker Date: Thu, 20 Mar 2025 16:33:53 +0100 Subject: [PATCH] Issue #1482: enable SQL help window --- heidisql.lpi | 7 + heidisql.lpr | 2 +- source/main.pas | 10 +- source/sqlhelp.lfm | 339 +++++++++++++++++++++++++++++++++++ source/sqlhelp.pas | 427 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 779 insertions(+), 6 deletions(-) create mode 100644 source/sqlhelp.lfm create mode 100644 source/sqlhelp.pas diff --git a/heidisql.lpi b/heidisql.lpi index 9da7e5e6..ffada3ad 100644 --- a/heidisql.lpi +++ b/heidisql.lpi @@ -229,6 +229,13 @@ + + + + + + + diff --git a/heidisql.lpr b/heidisql.lpr index af1a20e4..853a372f 100644 --- a/heidisql.lpr +++ b/heidisql.lpr @@ -19,7 +19,7 @@ uses dbstructures.sqlite, change_password, loginform, data_sorting, extra_controls, column_selection, loaddata, csv_detector, createdatabase, editvar, copytable, exportgrid, usermanager, selectdbobject, reformatter, searchreplace, - connections, jsonregistry {, printlist (EnablePrint not defined) } + connections, jsonregistry, sqlhelp {, printlist (EnablePrint not defined) } ; {$R *.res} diff --git a/source/main.pas b/source/main.pas index 90659dbd..2affa0e6 100644 --- a/source/main.pas +++ b/source/main.pas @@ -1415,7 +1415,7 @@ implementation uses 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} @@ -4903,10 +4903,10 @@ end; procedure TMainform.CallSQLHelpWithKeyword( keyword: String ); begin if FActiveDbObj.Connection.Has(frHelpKeyword) then begin - {if not Assigned(SqlHelpDialog) then + if not Assigned(SqlHelpDialog) then SqlHelpDialog := TfrmSQLhelp.Create(Self); SqlHelpDialog.Show; - SqlHelpDialog.Keyword := keyword;} + SqlHelpDialog.Keyword := keyword; end else ErrorDialog(_('SQL help not available.'), f_('HELP requires %s or newer.', ['MySQL 4.1'])); end; @@ -13273,10 +13273,10 @@ begin Editors.Add(frmPreferences.SynMemoSQLSample);} if Assigned(FCreateDatabaseDialog) then Editors.Add(FCreateDatabaseDialog.SynMemoCreateCode); - {if SqlHelpDialog <> nil then begin + if SqlHelpDialog <> nil then begin Editors.Add(SqlHelpDialog.memoDescription); Editors.Add(SqlHelpDialog.MemoExample); - end;} + end; {if Assigned(FTableToolsDialog) then Editors.Add(FTableToolsDialog.SynMemoFindText);} if Assigned(frmCsvDetector) then diff --git a/source/sqlhelp.lfm b/source/sqlhelp.lfm new file mode 100644 index 00000000..c2dfeea9 --- /dev/null +++ b/source/sqlhelp.lfm @@ -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 diff --git a/source/sqlhelp.pas b/source/sqlhelp.pas new file mode 100644 index 00000000..c5c3ea94 --- /dev/null +++ b/source/sqlhelp.pas @@ -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.