lazarus/ide/codetoolsdefpreview.pas

524 lines
17 KiB
ObjectPascal

{
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
* *
***************************************************************************
Author: Mattias Gaertner
Abstract:
Dialog to explore IDE internal codetools structures.
}
unit CodeToolsDefPreview;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Math, AVL_Tree,
// LCL
Forms, Dialogs, ButtonPanel, StdCtrls, ComCtrls, ExtCtrls,
// LazUtils
LazFileUtils, LazUtilities, LazLoggerBase,
// Codetools
DefineTemplates, ExprEval,
// SynEdit
SynEdit,
// IdeIntf
IDEWindowIntf, IDEHelpIntf, InputHistory,
// IdeConfig
RecentListProcs, EnvironmentOpts,
// IDE
LazarusIDEStrConsts, EditorOptions, CodeToolsOptions;
type
TCodeToolsDefinesNodeValues = class
public
Node: TDefineTemplate;
ValueParsed: boolean;
ParsedValue: string;
ExpressionCalculated: boolean;
ExpressionResult: string;
Execute: boolean;
end;
{ TCodeToolsDefinesDialog }
TCodeToolsDefinesDialog = class(TForm)
ButtonPanel: TButtonPanel;
DirectoryBrowseButton: TButton;
DirectoryCombobox: TComboBox;
DirectoryGroupbox: TGroupBox;
PageControl1: TPageControl;
ReportMemo: TMemo;
Splitter1: TSplitter;
ValuesTabSheet: TTabSheet;
ReportTabSheet: TTabSheet;
TemplatesMemo: TMemo;
TemplatesSplitter: TSplitter;
TemplatesGroupBox: TGroupBox;
MainSplitter: TSplitter;
ParsedTemplatesTreeView: TTreeView;
ValueSynedit: TSynEdit;
ValueGroupbox: TGroupBox;
ValuesListview: TListView;
procedure CodeToolsDefinesDialogCLOSE(Sender: TObject;
var {%H-}CloseAction: TCloseAction);
procedure CodeToolsDefinesDialogCREATE(Sender: TObject);
procedure DirectoryBrowseButtonCLICK(Sender: TObject);
procedure DirectoryComboboxCHANGE(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure HelpButtonClick(Sender: TObject);
procedure ParsedTemplatesTreeViewSelectionChanged(Sender: TObject);
procedure ValuesListviewSELECTITEM(Sender: TObject; {%H-}Item: TListItem;
{%H-}Selected: Boolean);
private
FDefineTree: TDefineTree;
FNodeValues: TAvlTree;
fReport: TStringList;
procedure SetDefineTree(const AValue: TDefineTree);
procedure UpdateValues;
procedure UpdateValue;
procedure UpdateTemplateValues;
procedure ClearValues;
procedure FillTemplateTree;
procedure SetComboBox(AComboBox: TComboBox; const NewText: string);
procedure DefineTreeCalculate({%H-}DefineTree: TDefineTree; Node: TDefineTemplate;
ValueParsed: boolean; const ParsedValue: string;
ExpressionCalculated: boolean; const ExpressionResult: string;
Execute: boolean);
public
property DefineTree: TDefineTree read FDefineTree write SetDefineTree;
end;
function ShowCodeToolsDefinesValuesDialog(ADefineTree: TDefineTree;
const InitialDirectory: string): TModalresult;
procedure RebuildDefineTreeView(ATreeView: TTreeView;
RootTemplate: TDefineTemplate);
procedure AddDefineNodes(ATreeView: TTreeView; ANode: TDefineTemplate;
AParent: TTreeNode; WithChilds,WithNextSiblings: boolean);
procedure SetNodeImages(ANode: TTreeNode; WithSubNodes: boolean);
function CompareNodeValues(Data1, Data2: Pointer): Integer;
function CompareNodeAndNodeValues(Node, NodeValues: Pointer): Integer;
implementation
{$R *.lfm}
function ShowCodeToolsDefinesValuesDialog(ADefineTree: TDefineTree;
const InitialDirectory: string): TModalresult;
var
CodeToolsDefinesDialog: TCodeToolsDefinesDialog;
begin
CodeToolsDefinesDialog:=TCodeToolsDefinesDialog.Create(nil);
if InitialDirectory<>'' then
CodeToolsDefinesDialog.SetComboBox(CodeToolsDefinesDialog.DirectoryCombobox,
InitialDirectory);
CodeToolsDefinesDialog.DefineTree:=ADefineTree;
Result:=CodeToolsDefinesDialog.ShowModal;
CodeToolsDefinesDialog.Free;
end;
procedure RebuildDefineTreeView(ATreeView: TTreeView;
RootTemplate: TDefineTemplate);
begin
ATreeView.Items.BeginUpdate;
ATreeView.Items.Clear;
AddDefineNodes(ATreeView,RootTemplate,nil,true,true);
ATreeView.Items.EndUpdate;
end;
procedure AddDefineNodes(
ATreeView: TTreeView; ANode: TDefineTemplate; AParent: TTreeNode;
WithChilds, WithNextSiblings: boolean);
var NewTreeNode: TTreeNode;
begin
if ANode=nil then exit;
ATreeView.Items.BeginUpdate;
NewTreeNode:=ATreeView.Items.AddChildObject(AParent,ANode.Name,ANode);
SetNodeImages(NewTreeNode,false);
if WithChilds and (ANode.FirstChild<>nil) then begin
AddDefineNodes(ATreeView,ANode.FirstChild,NewTreeNode,true,true);
end;
if WithNextSiblings and (ANode.Next<>nil) then begin
AddDefineNodes(ATreeView,ANode.Next,AParent,WithChilds,true);
end;
ATreeView.Items.EndUpdate;
end;
procedure SetNodeImages(ANode: TTreeNode;
WithSubNodes: boolean);
var
ADefineTemplate: TDefineTemplate;
begin
ADefineTemplate := TDefineTemplate(ANode.Data);
ANode.ImageIndex := DefineActionImages[ADefineTemplate.Action];
ANode.SelectedIndex := ANode.ImageIndex;
if ADefineTemplate.IsAutoGenerated then
ANode.StateIndex := AutogeneratedImage
else
ANode.StateIndex := -1;
if WithSubNodes then
begin
ANode:=ANode.GetFirstChild;
while ANode<>nil do
begin
SetNodeImages(ANode,true);
ANode:=ANode.GetNextSibling;
end;
end;
end;
function CompareNodeValues(Data1, Data2: Pointer): Integer;
begin
Result:=ComparePointers(TCodeToolsDefinesNodeValues(Data1).Node,
TCodeToolsDefinesNodeValues(Data2).Node);
end;
function CompareNodeAndNodeValues(Node, NodeValues: Pointer): Integer;
begin
Result:=ComparePointers(TCodeToolsDefinesNodeValues(Node),
TCodeToolsDefinesNodeValues(NodeValues).Node);
end;
{ TCodeToolsDefinesDialog }
procedure TCodeToolsDefinesDialog.FormDestroy(Sender: TObject);
begin
if FNodeValues<>nil then begin
FNodeValues.FreeAndClear;
FreeAndNil(FNodeValues);
end;
end;
procedure TCodeToolsDefinesDialog.HelpButtonClick(Sender: TObject);
begin
LazarusHelp.ShowHelpForIDEControl(Self);
end;
procedure TCodeToolsDefinesDialog.ParsedTemplatesTreeViewSelectionChanged(
Sender: TObject);
begin
UpdateTemplateValues;
end;
procedure TCodeToolsDefinesDialog.ValuesListviewSELECTITEM(Sender: TObject;
Item: TListItem; Selected: Boolean);
begin
UpdateValue;
end;
procedure TCodeToolsDefinesDialog.SetDefineTree(const AValue: TDefineTree);
begin
if FDefineTree=AValue then exit;
FDefineTree:=AValue;
UpdateValues;
FillTemplateTree;
end;
procedure TCodeToolsDefinesDialog.UpdateValues;
// let the codetools calculate the defines for the directory
procedure AddNodeReport(Prefix: string; DefTempl: TDefineTemplate);
var
AVLNode: TAvlTreeNode;
NodeValues: TCodeToolsDefinesNodeValues;
s: String;
begin
while DefTempl<>nil do begin
s:=Prefix+'Name="'+DefTempl.Name+'"';
s:=s+' Description="'+DefTempl.Description+'"';
s:=s+' Action="'+DefineActionNames[DefTempl.Action]+'"';
s:=s+' Variable="'+DefTempl.Variable+'"';
s:=s+' Value="'+dbgstr(DefTempl.Value)+'"';
if FNodeValues<>nil then begin
AVLNode:=FNodeValues.FindKey(DefTempl,@CompareNodeAndNodeValues);
if AVLNode<>nil then begin
NodeValues:=TCodeToolsDefinesNodeValues(AVLNode.Data);
if NodeValues.ValueParsed then
s:=s+' ParsedValue="'+dbgstr(NodeValues.ParsedValue)+'"';
if NodeValues.ExpressionCalculated then
s:=s+' ExpressionResult="'+dbgstr(NodeValues.ExpressionResult)+'"';
s:=s+' Executed="'+dbgs(NodeValues.Execute)+'"';
end;
end;
fReport.Add(s);
if DefTempl.FirstChild<>nil then
AddNodeReport(Prefix+' ',DefTempl.FirstChild);
DefTempl:=DefTempl.Next;
end;
end;
var
Dir: String;
Defines: TExpressionEvaluator;
i: Integer;
ListItem: TListItem;
Value: String;
OldOnCalculate: TDefTreeCalculate;
begin
Dir:=TrimFilename(DirectoryCombobox.Text);
if (DefineTree=nil) or (not FilenameIsAbsolute(Dir))
or (not DirPathExists(Dir)) then begin
ClearValues;
exit;
end;
// set our debug function, clear codetools cache and calculate the values
if FNodeValues<>nil then
FNodeValues.FreeAndClear;
DefineTree.ClearCache;// make sure the defines are reparsed
fReport:=TStringList.Create;
try
fReport.Add('Directory: '+Dir);
OldOnCalculate:=DefineTree.OnCalculate;
DefineTree.OnCalculate:=@DefineTreeCalculate;
try
Defines:=DefineTree.GetDefinesForDirectory(Dir,false);
finally
DefineTree.OnCalculate:=OldOnCalculate;
end;
// fill the ValuesListview
ValuesListview.BeginUpdate;
if Defines<>nil then begin
fReport.Add('Defines:');
for i:=0 to Defines.Count-1 do begin
if ValuesListview.Items.Count<=i then
ListItem:=ValuesListview.Items.Add
else
ListItem:=ValuesListview.Items[i];
fReport.Add(Defines.Names(i)+'='+dbgstr(Defines.Values(i)));
ListItem.Caption:=Defines.Names(i);
Value:=Defines.Values(i);
if length(Value)>100 then
Value:=copy(Value,1,100)+' ...';
if ListItem.SubItems.Count<1 then
ListItem.SubItems.Add(Value)
else
ListItem.SubItems[0]:=Value;
end;
fReport.Add('');
while ValuesListview.Items.Count>Defines.Count do
ValuesListview.Items.Delete(ValuesListview.Items.Count-1);
end else begin
ValuesListview.Items.Clear;
end;
ValuesListview.EndUpdate;
UpdateValue;
// add all nodes to report
fReport.Add('Tree:');
AddNodeReport(' ',DefineTree.RootTemplate);
ReportMemo.Lines.Assign(fReport);
finally
FreeAndNil(fReport);
end;
end;
procedure TCodeToolsDefinesDialog.UpdateValue;
var
VariableName: String;
Dir: String;
Defines: TExpressionEvaluator;
Value: string;
begin
Dir:=TrimFilename(DirectoryCombobox.Text);
if (ValuesListview.Selected=nil) or (DefineTree=nil)
or (not FilenameIsAbsolute(Dir)) then begin
ValueGroupbox.Caption:=lisCTDefnoVariableSelected;
ValueSynedit.Lines.Text:='';
end else begin
VariableName:=ValuesListview.Selected.Caption;
ValueGroupbox.Caption:=Format(lisCTDefVariable, [VariableName]);
Defines:=DefineTree.GetDefinesForDirectory(Dir,false);
Value:=Defines.Variables[VariableName];
ValueSynedit.Lines.Text:=Value;
end;
end;
procedure TCodeToolsDefinesDialog.UpdateTemplateValues;
var
SelTreeNode: TTreeNode;
SelDefNode: TDefineTemplate;
s: string;
AVLNode: TAvlTreeNode;
NodeValues: TCodeToolsDefinesNodeValues;
begin
SelTreeNode:=ParsedTemplatesTreeView.Selected;
if SelTreeNode=nil then begin
TemplatesMemo.Text:='No node selected';
end else begin
SelDefNode:=TDefineTemplate(SelTreeNode.Data);
s:='Name="'+SelDefNode.Name+'"'+LineEnding;
s:=s+'Description="'+SelDefNode.Description+'"'+LineEnding;
s:=s+'Action="'+DefineActionNames[SelDefNode.Action]+'"'+LineEnding;
s:=s+'Variable="'+SelDefNode.Variable+'"'+LineEnding;
s:=s+'Value="'+SelDefNode.Value+'"'+LineEnding;
if FNodeValues<>nil then begin
AVLNode:=FNodeValues.FindKey(SelDefNode,@CompareNodeAndNodeValues);
if AVLNode<>nil then begin
NodeValues:=TCodeToolsDefinesNodeValues(AVLNode.Data);
if NodeValues.ValueParsed then
s:=s+'Parsed Value="'+NodeValues.ParsedValue+'"'+LineEnding;
if NodeValues.ExpressionCalculated then
s:=s+'Expression Result="'+NodeValues.ExpressionResult+'"'+LineEnding;
s:=s+'Executed="'+dbgs(NodeValues.Execute)+'"'+LineEnding;
end;
end;
TemplatesMemo.Text:=s;
end;
end;
procedure TCodeToolsDefinesDialog.ClearValues;
begin
ValuesListview.Items.Clear;
if FNodeValues<>nil then
FNodeValues.FreeAndClear;
end;
procedure TCodeToolsDefinesDialog.FillTemplateTree;
begin
RebuildDefineTreeView(ParsedTemplatesTreeView,DefineTree.RootTemplate);
UpdateTemplateValues;
end;
procedure TCodeToolsDefinesDialog.SetComboBox(AComboBox: TComboBox;
const NewText: string);
var
i: Integer;
begin
i:=AComboBox.Items.IndexOf(NewText);
if i<0 then
AComboBox.Items.Add(NewText)
else
AComboBox.ItemIndex:=i;
AComboBox.Text:=NewText;
//debugln('TCodeToolsDefinesDialog.SetComboBox Text=',AComboBox.Text,' NewText=',NewText);
end;
procedure TCodeToolsDefinesDialog.DefineTreeCalculate(DefineTree: TDefineTree;
Node: TDefineTemplate; ValueParsed: boolean; const ParsedValue: string;
ExpressionCalculated: boolean; const ExpressionResult: string;
Execute: boolean);
var
NewNodeValues: TCodeToolsDefinesNodeValues;
begin
NewNodeValues:=TCodeToolsDefinesNodeValues.Create;
NewNodeValues.Node:=Node;
NewNodeValues.ValueParsed:=ValueParsed;
NewNodeValues.ParsedValue:=ParsedValue;
NewNodeValues.ExpressionCalculated:=ExpressionCalculated;
NewNodeValues.ExpressionResult:=ExpressionResult;
NewNodeValues.Execute:=Execute;
if FNodeValues=nil then
FNodeValues:=TAvlTree.Create(@CompareNodeValues);
FNodeValues.Add(NewNodeValues);
end;
procedure TCodeToolsDefinesDialog.CodeToolsDefinesDialogCREATE(Sender: TObject);
var
ListColumn: TListColumn;
begin
IDEDialogLayoutList.ApplyLayout(Self,485,450);
Caption:=lisCTDefCodeToolsDirectoryValues;
ValuesTabSheet.Caption:=lisValues;
ListColumn:=ValuesListview.Columns.Add;
ListColumn.Caption:=lisCTDefVariableName;
ListColumn.Width:=150;
ListColumn:=ValuesListview.Columns.Add;
ListColumn.Caption:=lisValue;
DirectoryGroupbox.Caption:=lisCodeToolsDefsInsertBehindDirectory;
ButtonPanel.HelpButton.OnClick := @HelpButtonClick;
DirectoryCombobox.Items.Assign(
InputHistories.HistoryLists.GetList(hlCodeToolsDirectories,true,rltFile));
if DirectoryCombobox.Items.Count>0 then
DirectoryCombobox.ItemIndex:=0
else
DirectoryCombobox.Text:='';
TemplatesGroupBox.Caption:=lisCTDefDefineTemplates;
MainSplitter.SetSplitterPosition(
Max(20,Min(ClientWidth-100,CodeToolsOpts.DefinesPreviewMainSplitterPos)));
TemplatesSplitter.SetSplitterPosition(
Max(20,Min(TemplatesGroupBox.ClientHeight-50,
CodeToolsOpts.DefinesPreviewTemplSplitterPos)));
EditorOpts.GetSynEditSettings(ValueSynedit);
ValueSynedit.Gutter.Visible:=false;
ReportTabSheet.Caption:=dlgReport;
PageControl1.PageIndex:=0;
DirectoryCombobox.DropDownCount:=EnvironmentOptions.DropDownCount;
end;
procedure TCodeToolsDefinesDialog.CodeToolsDefinesDialogCLOSE(Sender: TObject;
var CloseAction: TCloseAction);
begin
IDEDialogLayoutList.SaveLayout(Self);
InputHistories.HistoryLists.GetList(hlCodeToolsDirectories,true,rltFile).Assign(
DirectoryCombobox.Items);
CodeToolsOpts.DefinesPreviewMainSplitterPos:=MainSplitter.GetSplitterPosition;
CodeToolsOpts.DefinesPreviewTemplSplitterPos:=TemplatesSplitter.GetSplitterPosition;
CodeToolsOpts.Save;
end;
procedure TCodeToolsDefinesDialog.DirectoryBrowseButtonCLICK(Sender: TObject);
var
OpenDialog: TOpenDialog;
Filename: string;
begin
OpenDialog:=TSelectDirectoryDialog.Create(nil);
try
InputHistories.ApplyFileDialogSettings(OpenDialog);
OpenDialog.Title:=lisCTDefChooseDirectory;
OpenDialog.Options:=OpenDialog.Options+[ofPathMustExist];
if OpenDialog.Execute then begin
Filename:=CleanAndExpandFilename(OpenDialog.Filename);
SetComboBox(DirectoryCombobox,Filename);
UpdateValues;
end;
InputHistories.StoreFileDialogSettings(OpenDialog);
finally
OpenDialog.Free;
end;
end;
procedure TCodeToolsDefinesDialog.DirectoryComboboxCHANGE(Sender: TObject);
begin
UpdateValues;
end;
end.