{ ***************************************************************************** See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** Author: Mattias Gaertner Abstract: Extension for the Object Inspector. - Favorites properties } unit ObjInspExt; {$mode objfpc}{$H+} interface uses Classes, SysUtils, // LCL Forms, Controls, Buttons, StdCtrls, ExtCtrls, Dialogs, Menus, ComCtrls, Grids, CustomTimer, // LazUtils LazFileUtils, LazConfigStorage, LazLoggerBase, // CodeTools CodeToolManager, CodeCache, // BuildIntf BaseIDEIntf, ProjectIntf, // IdeIntf LazIDEIntf, ObjectInspector, OIFavoriteProperties, PropEdits, IDEDialogs, // IDE DialogProcs, LazConf, LazarusIDEStrConsts; type { TOIAddRemoveFavoriteDlg } TOIAddRemoveFavoriteDlg = class(TForm) NoteLabel: TLabel; ClassCombobox: TComboBox; OkButton: TButton; CancelButton: TButton; procedure OkButtonClick(Sender: TObject); private FAddMode: Boolean; FObjectInspector: TObjectInspectorDlg; FPropertyName: string; procedure SetAddMode(const AValue: Boolean); procedure SetObjectInspector(const AValue: TObjectInspectorDlg); procedure UpdateLabel; procedure UpdateComboBox; procedure UpdateMode; public constructor Create(TheOwner: TComponent); override; public property ObjectInspector: TObjectInspectorDlg read FObjectInspector write SetObjectInspector; property PropertyName: string read FPropertyName; property AddMode: Boolean read FAddMode write SetAddMode; end; const DefaultOIFavoriteConfigFilename = 'objectinspectorfavorites.xml'; var DefaultOIFavoriteProperties: TOIFavoriteProperties; function ShowAddRemoveFavoriteDialog(ObjInspector: TObjectInspectorDlg; Add: Boolean): TModalResult; function CreateDefaultOIFavoriteProperties: TOIFavoriteProperties; function LoadOIFavoriteProperties: TOIFavoriteProperties; procedure SaveOIFavoriteProperties(Favorites: TOIFavoriteProperties); function GetOIFavoriteConfigFilename: string; function FindDeclarationOfOIProperty(AnInspector: TObjectInspectorDlg; Row: TOIPropertyGridRow; out Code: TCodeBuffer; out Caret: TPoint; out NewTopLine: integer): Boolean; implementation function CreateDefaultOIFavoriteProperties: TOIFavoriteProperties; procedure Add(ABaseClass: TPersistentClass; const APropertyName: string); begin Result.Add(TOIFavoriteProperty.Create(ABaseClass,APropertyName,true)); end; begin Result:=TOIFavoriteProperties.Create; // TControl Add(TComponent,'Name'); Add(TComponent,'Caption'); Add(TControl,'Anchors'); Add(TControl,'AutoSize'); Add(TControl,'OnClick'); Add(TControl,'OnEditingDone'); // miscellaneous Add(TCustomGroupBox,'Align'); Add(TCustomImage,'Align'); Add(TCustomButton,'ModalResult'); Add(TCustomLabel,'WordWrap'); Add(TCustomEdit,'Text'); Add(TCustomMemo,'Lines'); Add(TCustomMemo,'Align'); Add(TCustomMemo,'ScrollBars'); Add(TCustomCheckBox,'Checked'); Add(TCustomRadioGroup,'Items'); Add(TCustomRadioGroup,'ItemIndex'); Add(TCustomForm,'OnCreate'); Add(TCustomForm,'OnDestroy'); Add(TCustomForm,'OnResize'); Add(TCustomListBox,'Items'); Add(TCustomListBox,'Align'); Add(TCustomTreeView,'Align'); Add(TCustomTreeView,'Options'); Add(TCustomPanel,'Align'); Add(TMenuItem,'OnClick'); Add(TCustomSpeedButton,'GroupIndex'); Add(TCustomSpeedButton,'Glyph'); Add(TCustomImage,'Picture'); Add(TCustomImage,'Align'); Add(TCustomTabControl,'Align'); Add(TScrollBox,'Align'); Add(TCustomGrid,'Align'); Add(TCustomGrid,'Options'); Add(TCustomGrid,'Columns'); Add(TCustomGrid,'ColCount'); Add(TCustomTreeView,'Align'); Add(TCustomTreeView,'Options'); Add(TCustomTimer,'OnTimer'); Result.DeleteDoubles; end; function ShowAddRemoveFavoriteDialog(ObjInspector: TObjectInspectorDlg; Add: Boolean): TModalResult; var OIAddRemoveFavoriteDlg: TOIAddRemoveFavoriteDlg; begin OIAddRemoveFavoriteDlg:=TOIAddRemoveFavoriteDlg.Create(nil); OIAddRemoveFavoriteDlg.ObjectInspector:=ObjInspector; OIAddRemoveFavoriteDlg.AddMode:=Add; Result:=OIAddRemoveFavoriteDlg.ShowModal; OIAddRemoveFavoriteDlg.Free; end; function LoadOIFavoriteProperties: TOIFavoriteProperties; var ConfigStore: TConfigStorage; begin Result:=DefaultOIFavoriteProperties.CreateCopy; {$IFDEF DebugFavoriteroperties} debugln('LoadOIFavoriteProperties A FileExistsUTF8(GetOIFavoriteConfigFilename)=',dbgs(FileExistsUTF8(GetOIFavoriteConfigFilename))); Result.WriteDebugReport; {$ENDIF} if not FileExistsUTF8(GetOIFavoriteConfigFilename) then exit; try ConfigStore:=DefaultConfigClass.Create(GetOIFavoriteConfigFilename,true); try Result.MergeConfig(ConfigStore,'ObjectInspector/Favorites/'); Result.Modified:=false; finally ConfigStore.Free; end; except on E: Exception do begin debugln('Error: LoadOIFavoriteProperties: unable to read ', GetOIFavoriteConfigFilename); end; end; end; procedure SaveOIFavoriteProperties(Favorites: TOIFavoriteProperties); var ConfigStore: TConfigStorage; DefaultFavorites: TOIFavoriteProperties; begin {$IFDEF DebugFavoriteroperties} debugln('SaveOIFavoriteProperties Favorites.Modified=',dbgs(Favorites.Modified), ' FileExistsUTF8(GetOIFavoriteConfigFilename)=',dbgs(FileExistsUTF8(GetOIFavoriteConfigFilename))); {$ENDIF} if (not Favorites.Modified) and FileExistsUTF8(GetOIFavoriteConfigFilename) then exit; DefaultFavorites:=CreateDefaulTOIFavoriteProperties; try if DefaultFavorites.IsEqual(Favorites) then exit; {$IFDEF DebugFavoriteroperties} debugln('SaveOIFavoriteProperties is not default'); DefaultFavorites.WriteDebugReport; Favorites.WriteDebugReport; {$ENDIF} try ConfigStore:=DefaultConfigClass.Create(GetOIFavoriteConfigFilename,false); try Favorites.SaveNewItemsToConfig(ConfigStore,'ObjectInspector/Favorites/', DefaultFavorites); ConfigStore.WriteToDisk; Favorites.Modified:=false; finally ConfigStore.Free; end; except on E: Exception do begin debugln('Error: LoadOIFavoriteProperties: unable to write ', GetOIFavoriteConfigFilename); end; end; finally DefaultFavorites.Free; end; end; function GetOIFavoriteConfigFilename: string; begin Result:=AppendPathDelim(GetPrimaryConfigPath)+DefaultOIFavoriteConfigFilename; end; function FindDeclarationOfOIProperty(AnInspector: TObjectInspectorDlg; Row: TOIPropertyGridRow; out Code: TCodeBuffer; out Caret: TPoint; out NewTopLine: integer): Boolean; var PropPath: String; LookupRoot: TPersistent; AFile: TLazProjectFile; NewCode: TCodeBuffer; NewX, NewY: integer; APersistent: TPersistent; AnUnitName: String; InFilename: String; FilenameOfClass: string; begin Result:=false; Code:=nil; Caret:=Point(0,0); // check Row if AnInspector=nil then begin DebugLn('FindDeclarationOfOIProperty AnInspector=nil'); exit; end; if Row=nil then Row:=AnInspector.GetActivePropertyRow; if Row=nil then begin DebugLn('FindDeclarationOfOIProperty Row=nil'); exit; end; if Row.Editor=nil then begin DebugLn('FindDeclarationOfOIProperty Row.Editor=nil Row=',Row.Name); exit; end; // get first instance of property APersistent:=Row.Editor.GetComponent(0); if APersistent=nil then begin DebugLn('FindDeclarationOfOIProperty APersistent=nil Row=',Row.Name); exit; end; // get unit name of first instance AnUnitName:=GetClassUnitName(APersistent.ClassType); if AnUnitName='' then begin DebugLn('FindDeclarationOfOIProperty no RTTI unit found for APersistent.ClassType=',DbgSName(APersistent.ClassType)); exit; end; // get lookup root if Row.Editor.PropertyHook=nil then begin debugln(['FindDeclarationOfOIProperty Row.Editor.PropertyHook=nil Row=',Row.Name]); exit; end; LookupRoot:=Row.Editor.PropertyHook.LookupRoot; if LookupRoot=nil then begin debugln(['FindDeclarationOfOIProperty Row.Editor.PropertyHook.LookupRoot=nil Row=',Row.Name]); exit; end; // get file of lookup root AFile:=LazarusIDE.GetProjectFileWithRootComponent(TComponent(LookupRoot)); if AFile=nil then begin DebugLn('FindDeclarationOfOIProperty AFile=nil Row=',Row.Name,' LookupRoot=',DbgSName(LookupRoot)); exit; end; InFilename:=''; FilenameOfClass:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath( ExtractFilePath(AFile.Filename),AnUnitName,InFilename); if FilenameOfClass='' then begin debugln(['FindDeclarationOfOIProperty FindUnitSourceInCompletePath failed: Row=',Row.Name,' Instance=',DbgSName(APersistent),' LookupRoot=',DbgSName(LookupRoot),' Unit not found: ',AnUnitName,' started search in directory of lookuproot: ',AFile.Filename]); exit; end; if not LazarusIDE.BeginCodeTools then begin DebugLn('FindDeclarationOfOIProperty LazarusIDE.BeginCodeTools failed'); exit; end; Code:=nil; if LoadCodeBuffer(Code,FilenameOfClass,[],false)<>mrOk then begin debugln(['FindDeclarationOfOIProperty LoadCodeBuffer failed of ',FilenameOfClass]); exit; end; // find the property declaration PropPath:=APersistent.ClassName+'.'+Row.Name; if Row.Editor is TNestedPropertyEditor then begin if Row.Parent=nil then begin debugln(['FindDeclarationOfOIProperty missing parent row ',PropPath,' in unit ',Code.Filename,' Row.Editor=',DbgSName(Row.Editor)]); exit; end; PropPath:=APersistent.ClassName+'.'+Row.Parent.Name+'.'+Row.Name; end; if not CodeToolBoss.FindDeclarationOfPropertyPath(Code,PropPath,NewCode, NewX,NewY,NewTopLine) then begin debugln(['FindDeclarationOfOIProperty failed to find property ',PropPath,' in unit ',Code.Filename]); exit; end; Code:=NewCode; Caret:=Point(NewX,NewY); //DebugLn('FindDeclarationOfOIProperty SUCCESS ',Code.Filename,' ',dbgs(Caret)); Result:=true; end; { TOIAddRemoveFavoriteDlg } procedure TOIAddRemoveFavoriteDlg.OkButtonClick(Sender: TObject); var NewClassName: String; CurClass: TClass; NewFavorite: TOIFavoriteProperty; begin NewClassName:=ClassCombobox.Text; if (ObjectInspector<>nil) and (ObjectInspector.Selection<>nil) and (ObjectInspector.Selection.Count>0) then begin CurClass:=ObjectInspector.Selection[0].ClassType; while CurClass.InheritsFrom(TPersistent) do begin if CompareText(NewClassName,CurClass.ClassName)=0 then begin NewFavorite:=TOIFavoriteProperty.Create(TPersistentClass(CurClass), PropertyName,AddMode); ObjectInspector.Favorites.DeleteConstraints(NewFavorite); ObjectInspector.Favorites.Add(NewFavorite); ObjectInspector.FavoriteGrid.BuildPropertyList; ModalResult:=mrOk; exit; end; CurClass:=CurClass.ClassParent; end; end; IDEMessageDialog(lisClassNotFound, Format(lisOIFClassNotFound, [NewClassName]), mtError, [mbOk]); end; procedure TOIAddRemoveFavoriteDlg.SetObjectInspector(const AValue: TObjectInspectorDlg); var CurRow: TOIPropertyGridRow; begin if FObjectInspector=AValue then exit; FObjectInspector:=AValue; CurRow:=ObjectInspector.GetActivePropertyRow; if (CurRow<>nil) and (CurRow.Editor<>nil) then FPropertyName:=CurRow.Editor.GetName; UpdateLabel; UpdateComboBox; end; procedure TOIAddRemoveFavoriteDlg.SetAddMode(const AValue: Boolean); begin if FAddMode=AValue then exit; FAddMode:=AValue; UpdateMode; end; procedure TOIAddRemoveFavoriteDlg.UpdateLabel; begin NoteLabel.Caption:=Format(lisOIFChooseABaseClassForTheFavoriteProperty, [PropertyName]); end; procedure TOIAddRemoveFavoriteDlg.UpdateComboBox; var CurClass: TClass; NewItems: TStringList; begin NewItems:=TStringList.Create; if (ObjectInspector<>nil) and (ObjectInspector.Selection<>nil) and (ObjectInspector.Selection.Count>0) then begin CurClass:=ObjectInspector.Selection[0].ClassType; // add only classes, that are TPersistent and have a registered class while CurClass.InheritsFrom(TPersistent) do begin // add only registered classes if GetClass(CurClass.ClassName)<>nil then NewItems.Add(CurClass.ClassName); CurClass:=CurClass.ClassParent; end; end; ClassCombobox.Items.Assign(NewItems); if ClassCombobox.Items.Count>0 then ClassCombobox.ItemIndex:=0; NewItems.Free; end; procedure TOIAddRemoveFavoriteDlg.UpdateMode; begin if AddMode then begin Caption:=lisOIFAddToFavoriteProperties; OkButton.Caption:=lisAdd; end else begin Caption:=lisOIFRemoveFromFavoriteProperties; OkButton.Caption:=lisRemove; end; end; constructor TOIAddRemoveFavoriteDlg.Create(TheOwner: TComponent); begin inherited CreateNew(TheOwner); Name:='OIAddToFavoriteDlg'; Width:=300; Height:=150; Position:=poScreenCenter; NoteLabel:=TLabel.Create(Self); with NoteLabel do begin Name:='NoteLabel'; SetBounds(5,5,Self.ClientWidth-10,50); WordWrap:=true; Parent:=Self; end; ClassCombobox:=TComboBox.Create(Self); with ClassCombobox do begin Name:='ClassCombobox'; SetBounds(5,60,200,Height); Parent:=Self; end; OkButton:=TButton.Create(Self); with OkButton do begin Name:='AddButton'; SetBounds(5,100,80,25); Caption:=lisAdd; Parent:=Self; OnClick:=@OkButtonClick; end; DefaultControl:=OkButton; CancelButton:=TButton.Create(Self); with CancelButton do begin Name:='CancelButton'; SetBounds(120,100,80,25); Caption:=lisCancel; Parent:=Self; ModalResult:=mrCancel; end; CancelControl:=CancelButton; UpdateMode; end; initialization DefaultOIFavoriteProperties:=CreateDefaultOIFavoriteProperties; finalization FreeAndNil(DefaultOIFavoriteProperties) end.