{ *************************************************************************** * * * 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 . 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: An IDE dialog to add an Assign method to a pascal class. } unit AddAssignMethodDlg; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Laz_AVL_Tree, // LCL LResources, Forms, Controls, Graphics, Dialogs, ButtonPanel, StdCtrls, ComCtrls, Menus, // LazUtils FileUtil, // Codetools CodeCache, CodeToolManager, FileProcs, PascalParserTool, BasicCodeTools, CodeTree, FindDeclarationTool, // IDEIntf IDEDialogs, LazIDEIntf, SrcEditorIntf, // Cody CodyCtrls, CodyStrConsts, CodyUtils; type { TAAMDItem } TAAMDItem = class public CodePos: TCodeXYPosition; Name: string; Desc: TCodeTreeNodeDesc; Visibility: TCodeTreeNodeDesc; WrittenByProperty: string; Selected: boolean; end; { TAddAssignMethodDialog } TAddAssignMethodDialog = class(TForm) ButtonPanel1: TButtonPanel; DeclGroupBox: TGroupBox; ImageList1: TImageList; InhCallCheckBox: TCheckBox; InhCallOnlyInElseCheckBox: TCheckBox; InheritedEdit: TEdit; InheritedGroupBox: TGroupBox; InheritedLabel: TLabel; InhOverrideCheckBox: TCheckBox; MembersGroupBox: TGroupBox; MembersTreeView: TCodyTreeView; ParamNameEdit: TEdit; ParamNameErrorLabel: TLabel; ParamNameLabel: TLabel; ParamTypeEdit: TEdit; ParamTypeErrorLabel: TLabel; ParamTypeLabel: TLabel; ProcNameEdit: TEdit; ProcNameErrorLabel: TLabel; ProcNameLabel: TLabel; procedure MembersTreeViewMouseDown(Sender: TObject; Button: TMouseButton; {%H-}Shift: TShiftState; X, Y: Integer); procedure OkButtonClick(Sender: TObject); procedure HelpButtonClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure ParamNameEditChange(Sender: TObject); procedure ParamTypeEditChange(Sender: TObject); procedure ProcNameEditChange(Sender: TObject); private FParamChanged: boolean; FProcNameEditChanged: boolean; FChecking: Boolean; FIdleConnected: boolean; procedure SetIdleConnected(const AValue: boolean); procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean); private ImgIDImplementation: LongInt; ImgIDVariable: LongInt; ImgIDProperty: LongInt; // cursor position FCode: TCodeBuffer; FX, FY: integer; FProcName: string; // items FItems: TAVLTree; // tree of TAAMDItem sorted for Name property IdleConnected: boolean read FIdleConnected write SetIdleConnected; public NewPos: TCodeXYPosition; NewTopLine: integer; function Init(NewCode: TCodeBuffer; NewX, NewY: integer; UseInheritedParam: boolean): boolean; end; procedure ShowAddAssignMethodDialog(Sender: TObject); function CompareAAMDItemsByName(Item1, Item2: Pointer): integer; function CompareAnsistringWithAAMDItemName(AnAnsistring, Item: Pointer): integer; implementation procedure ShowAddAssignMethodDialog(Sender: TObject); procedure ErrorNotInClass; begin IDEMessageDialog(crsCWError, crsCAMPleasePositionTheCursorOfTheSourceEditorInAPascalC, mtError,[mbCancel]); end; var AddAssignMethodDialog: TAddAssignMethodDialog; SrcEdit: TSourceEditorInterface; begin // commit changes form source editor to codetools if not LazarusIDE.BeginCodeTools then exit; // check context at cursor SrcEdit:=SourceEditorManagerIntf.ActiveEditor; if SrcEdit=nil then begin ErrorNotInClass; exit; end; AddAssignMethodDialog:=nil; try AddAssignMethodDialog:=TAddAssignMethodDialog.Create(nil); with AddAssignMethodDialog do begin if not Init(SrcEdit.CodeToolsBuffer as TCodeBuffer, SrcEdit.CursorTextXY.X,SrcEdit.CursorTextXY.Y,true) then begin // syntax error or not in a class if CodeToolBoss.ErrorMessage<>'' then LazarusIDE.DoJumpToCodeToolBossError else ErrorNotInClass; exit; end; if ShowModal=mrOk then begin if NewPos.Code<>nil then LazarusIDE.DoOpenFileAndJumpToPos(NewPos.Code.Filename, Point(NewPos.X,NewPos.Y),NewTopLine,-1,-1,[]); end; end; finally AddAssignMethodDialog.Free; end; end; function CompareAAMDItemsByName(Item1, Item2: Pointer): integer; var it1: TAAMDItem absolute Item1; it2: TAAMDItem absolute Item2; begin Result:=SysUtils.CompareText(it1.Name,it2.Name); end; function CompareAnsistringWithAAMDItemName(AnAnsistring, Item: Pointer ): integer; var it: TAAMDItem absolute Item; s: String; begin s:=AnsiString(AnAnsistring); Result:=SysUtils.CompareText(s,it.Name); end; { TAddAssignMethodDialog } procedure TAddAssignMethodDialog.FormCreate(Sender: TObject); begin FProcName:='Assign'; FItems:=TAVLTree.Create(@CompareAAMDItemsByName); ImgIDImplementation := Imagelist1.AddResourceName(HInstance, 'ce_implementation'); ImgIDVariable := Imagelist1.AddResourceName(HInstance, 'ce_variable'); ImgIDProperty := Imagelist1.AddResourceName(HInstance, 'ce_property'); ProcNameEdit.Text:=FProcName; end; procedure TAddAssignMethodDialog.OkButtonClick(Sender: TObject); var FTool: TCodeTool; FClassNode: TCodeTreeNode; FAssignDeclNode: TCodeTreeNode; FMemberNodeExts: TAVLTree; FAssignBodyNode: TCodeTreeNode; FInheritedDeclContext: TFindContext; AVLNode: TAVLTreeNode; NextAVLNode: TAVLTreeNode; NodeExt: TCodeTreeNodeExtension; AssignIt: Boolean; FoundAVLNode: TAVLTreeNode; Item: TAAMDItem; FAssignMembers: TFPList; i: Integer; begin if (not IsValidIdent(FProcName)) or (not IsValidIdent(ParamNameEdit.Text)) or (not IsValidIdent(ParamTypeEdit.Text)) then exit; FMemberNodeExts:=nil; FAssignMembers:=TFPList.Create; try if not CodeToolBoss.FindAssignMethod(FCode,FX,FY, FTool,FClassNode,FAssignDeclNode,FMemberNodeExts,FAssignBodyNode, FInheritedDeclContext,FProcName) then begin // syntax error or not in a class if CodeToolBoss.ErrorMessage<>'' then begin LazarusIDE.DoJumpToCodeToolBossError; ModalResult:=mrCancel; end; exit; end; if FAssignDeclNode<>nil then exit; // use selected members if FMemberNodeExts<>nil then begin AVLNode:=FMemberNodeExts.FindLowest; while AVLNode<>nil do begin NextAVLNode:=FMemberNodeExts.FindSuccessor(AVLNode); NodeExt:=TCodeTreeNodeExtension(AVLNode.Data); AssignIt:=true; if (NodeExt.Txt='') then AssignIt:=false; if AssignIt then begin FoundAVLNode:=FItems.FindKey(PChar(NodeExt.Txt),@CompareAnsistringWithAAMDItemName); if FoundAVLNode<>nil then begin Item:=TAAMDItem(FoundAVLNode.Data); AssignIt:=Item.Selected; end; end; if AssignIt then begin FMemberNodeExts.Delete(AVLNode); FAssignMembers.Add(NodeExt); end; AVLNode:=NextAVLNode; end; end; //debugln(['TAddAssignMethodDialog.OkButtonClick Count=',FAssignMembers.Count]); if not FTool.AddAssignMethod(FClassNode,FAssignMembers,FProcName, ParamNameEdit.Text,ParamTypeEdit.Text, InhOverrideCheckBox.Checked,InhCallCheckBox.Checked, InhCallOnlyInElseCheckBox.Checked, CodeToolBoss.SourceChangeCache, NewPos,NewTopLine) then begin debugln(['TAddAssignMethodDialog.ButtonPanel1Click failed']); ModalResult:=mrCancel; exit; end; finally DisposeAVLTree(FMemberNodeExts); for i:=0 to FAssignMembers.Count-1 do TObject(FAssignMembers[i]).Free; FreeAndNil(FAssignMembers); end; ModalResult:=mrOk; end; procedure TAddAssignMethodDialog.HelpButtonClick(Sender: TObject); begin OpenCodyHelp('#Add_Assign_method'); end; procedure TAddAssignMethodDialog.MembersTreeViewMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var Node: TTreeNode; Item: TAAMDItem; begin Node:=MembersTreeView.GetNodeAt(X,Y); if Node=nil then exit; if TObject(Node.Data) is TAAMDItem then Item:=TAAMDItem(Node.Data) else Item:=nil; if Item<>nil then begin if Button=mbLeft then begin if X>Node.DisplayExpandSignRight then begin Item.Selected:=not Item.Selected; if Item.Selected then Node.ImageIndex:=ImgIDImplementation else Node.ImageIndex:=-1; Node.SelectedIndex:=Node.ImageIndex; end; end; end; end; procedure TAddAssignMethodDialog.FormDestroy(Sender: TObject); begin FItems.FreeAndClear; FreeAndNil(FItems); end; procedure TAddAssignMethodDialog.ParamNameEditChange(Sender: TObject); begin FParamChanged:=true; IdleConnected:=true; end; procedure TAddAssignMethodDialog.ParamTypeEditChange(Sender: TObject); begin FParamChanged:=true; IdleConnected:=true; end; procedure TAddAssignMethodDialog.ProcNameEditChange(Sender: TObject); begin FProcNameEditChanged:=true; IdleConnected:=true; end; procedure TAddAssignMethodDialog.SetIdleConnected(const AValue: boolean); begin if FIdleConnected=AValue then exit; FIdleConnected:=AValue; if IdleConnected then Application.AddOnIdleHandler(@OnIdle) else Application.RemoveOnIdleHandler(@OnIdle); end; procedure TAddAssignMethodDialog.OnIdle(Sender: TObject; var Done: Boolean); begin IdleConnected:=false; Init(FCode,FX,FY,FProcNameEditChanged); FProcNameEditChanged:=false; end; function TAddAssignMethodDialog.Init(NewCode: TCodeBuffer; NewX, NewY: integer; UseInheritedParam: boolean): boolean; var FTool: TCodeTool; FClassNode: TCodeTreeNode; FAssignDeclNode: TCodeTreeNode; FMemberNodeExts: TAVLTree; FAssignBodyNode: TCodeTreeNode; FInheritedDeclContext: TFindContext; FClassName: String; FInheritedParamName: String; FInheritedParamType: String; FInheritedIsTPersistent: Boolean; InheritedClassNode: TCodeTreeNode; ParamNode: TCodeTreeNode; AVLNode: TAVLTreeNode; NodeExt: TCodeTreeNodeExtension; s: String; VisibilityNode: TCodeTreeNode; TVNode: TTreeNode; Item: TAAMDItem; NewProcName: String; begin if FChecking then exit; FCode:=NewCode; FX:=NewX; FY:=NewY; FItems.FreeAndClear; FMemberNodeExts:=nil; try FChecking:=true; FTool:=nil; FClassNode:=nil; FAssignDeclNode:=nil; FMemberNodeExts:=nil; FAssignBodyNode:=nil; FInheritedDeclContext:=CleanFindContext; NewProcName:=ProcNameEdit.Text; if IsValidIdent(NewProcName) then FProcName:=NewProcName; Result:=(FCode<>nil) and CodeToolBoss.FindAssignMethod(FCode,FX,FY, FTool,FClassNode,FAssignDeclNode,FMemberNodeExts,FAssignBodyNode, FInheritedDeclContext,FProcName); if FTool<>nil then FClassName:=FTool.ExtractClassName(FClassNode,false) else FClassName:=''; FInheritedParamName:='Source'; FInheritedParamType:='TObject'; FInheritedIsTPersistent:=false; // check if inherited exists, if it is TPersistent.Assign and use the // inherited parameter name and type if FInheritedDeclContext.Node<>nil then begin InheritedClassNode:=fInheritedDeclContext.Tool.FindClassOrInterfaceNode(fInheritedDeclContext.Node); FInheritedIsTPersistent:=(InheritedClassNode<>nil) and (InheritedClassNode.Parent.Desc=ctnTypeDefinition) and (CompareIdentifiers('TPersistent',@fInheritedDeclContext.Tool.Src[InheritedClassNode.Parent.StartPos])=0); ParamNode:=fInheritedDeclContext.Tool.GetProcParamList(fInheritedDeclContext.Node); if ParamNode<>nil then begin ParamNode:=ParamNode.FirstChild; if ParamNode<>nil then begin FInheritedParamName:=fInheritedDeclContext.Tool.ExtractDefinitionName(ParamNode); if (ParamNode.FirstChild<>nil) and (ParamNode.FirstChild.Desc=ctnIdentifier) then FInheritedParamType:=GetIdentifier(@fInheritedDeclContext.Tool.Src[ParamNode.FirstChild.StartPos]); end; end; end; ButtonPanel1.OKButton.Enabled:=Result and (FMemberNodeExts<>nil) and (FMemberNodeExts.Count>0); ButtonPanel1.HelpButton.Caption:=crsHelp; ButtonPanel1.OKButton.Caption:=crsBTNOK; ButtonPanel1.CancelButton.Caption:=crsBTNCancel; Caption:=Format(crsCAMAddAssignMethodToClass, [FClassName]); DeclGroupBox.Caption:=crsCAMNewMethod; ProcNameLabel.Caption:=crsCAMMethodName; if not IsValidIdent(NewProcName) then ProcNameErrorLabel.Caption:=crsCAMInvalidIdentifier else if not Result then ProcNameErrorLabel.Caption:=crsCAMCursorIsNotInAPascalClassDeclaration else if FAssignDeclNode<>nil then ProcNameErrorLabel.Caption:=crsCAMExistsAlready else ProcNameErrorLabel.Caption:=''; ParamNameLabel.Caption:=crsCAMParameterName; if UseInheritedParam then ParamNameEdit.Text:=FInheritedParamName; if not IsValidIdent(ParamNameEdit.Text) then ParamNameErrorLabel.Caption:=crsCAMInvalidIdentifier else ParamNameErrorLabel.Caption:=''; ParamTypeLabel.Caption:=crsCAMParameterType; if UseInheritedParam then ParamTypeEdit.Text:=FInheritedParamType; if not IsValidIdent(ParamTypeEdit.Text) then ParamTypeErrorLabel.Caption:=crsCAMInvalidIdentifier else ParamTypeErrorLabel.Caption:=''; // show some context information about the inherited method InheritedGroupBox.Caption:=crsCAMInherited; InhOverrideCheckBox.Caption:=crsCAMOverride; InhCallCheckBox.Caption:=crsCAMCallInherited; InhCallOnlyInElseCheckBox.Caption:=crsCAMCallInheritedOnlyIfWrongClass; InheritedEdit.ReadOnly:=true; if FInheritedDeclContext.Node<>nil then begin InheritedGroupBox.Enabled:=true; InheritedLabel.Caption:=crsCAMMethod; InheritedEdit.Text:=FInheritedDeclContext.Tool.ExtractProcHead( FInheritedDeclContext.Node, [phpAddClassName,phpWithDefaultValues,phpWithParameterNames]); InheritedEdit.Enabled:=true; InhOverrideCheckBox.Checked:=true; InhCallCheckBox.Checked:=true; InhCallOnlyInElseCheckBox.Enabled:=SysUtils.CompareText(FClassName,ParamTypeEdit.Text)<>0; InhCallOnlyInElseCheckBox.Checked:=FInheritedIsTPersistent; end else begin InheritedGroupBox.Enabled:=false; InheritedLabel.Caption:=crsCAMThereIsNoInheritedMethod; InheritedEdit.Text:=''; InheritedEdit.Enabled:=false; end; MembersGroupBox.Caption:=crsCAMSelectMembersToAssign; MembersTreeView.BeginUpdate; MembersTreeView.Items.Clear; if FMemberNodeExts<>nil then begin AVLNode:=FMemberNodeExts.FindLowest; while AVLNode<>nil do begin NodeExt:=TCodeTreeNodeExtension(AVLNode.Data); Item:=TAAMDItem.Create; // visibility VisibilityNode:=NodeExt.Node.Parent; if VisibilityNode.Desc in AllClassSubSections then VisibilityNode:=VisibilityNode.Parent; Item.Visibility:=VisibilityNode.Desc; // kind Item.Desc:=NodeExt.Node.Desc; // name Item.Name:=NodeExt.Txt; // written by property if NodeExt.Data<>nil then Item.WrittenByProperty:=FTool.ExtractPropName(TCodeTreeNode(NodeExt.Data),false) else Item.Selected:=true; case Item.Visibility of ctnClassPrivate: s:=crsCAMPrivate; ctnClassProtected: s:=crsCAMProtected; ctnClassPublic: s:=crsCAMPublic; ctnClassPublished: s:=crsCAMPublished; else s:=crsCAMVisibility; end; if Item.Desc=ctnVarDefinition then s:=Format(crsCAMVar, [s]) else s:=Format(crsCAMProperty, [s]); s:=s+' '+Item.Name; if Item.WrittenByProperty<>'' then s:=Format(crsCAMWrittenByProperty, [s, Item.WrittenByProperty]); FItems.Add(Item); TVNode:=MembersTreeView.Items.AddObject(nil,s,Item); TVNode.ImageIndex:=-1; if Item.Selected then TVNode.ImageIndex:=ImgIDImplementation; TVNode.SelectedIndex:=TVNode.ImageIndex; AVLNode:=FMemberNodeExts.FindSuccessor(AVLNode); end; end; MembersTreeView.EndUpdate; finally DisposeAVLTree(FMemberNodeExts); FChecking:=false; end; end; {$R *.lfm} end.