// SPDX-License-Identifier: LGPL-3.0-linking-exception { Styles form manager ------------------------------------------------------------------------------ originally written in 2012 by Krzysztof Dibowski dibowski at interia.pl } {******************************* CONTRIBUTOR(S) ****************************** - Edivando S. Santos Brasil | mailedivando@gmail.com (Compatibility with delphi VCL 11/2018) ***************************** END CONTRIBUTOR(S) *****************************} unit BCStylesForm; {$I bgracontrols.inc} interface uses Classes, SysUtils, {$IFDEF FPC}FileUtil, ComponentEditors, PropEdits,{$ELSE} Windows, DesignIntf, DesignEditors, PropertyCategories, ToolIntf, ExptIntf, DesignWindows, {$ENDIF} Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, ActnList, ComCtrls, Buttons, bcbasectrls; type { TBCfrmStyle } TBCfrmStyle = class(TForm) ActionRefresh: TAction; ActionNewFromFile: TAction; ActionDelete: TAction; ActionNewFromCtrl: TAction; ActionList1: TActionList; BitBtn1: TBitBtn; BitBtn2: TBitBtn; gboxPreview: TGroupBox; gboxStyles: TGroupBox; lvFiles: TListView; memoLogs: TMemo; OpenDialog1: TOpenDialog; pnlBottom: TPanel; Splitter1: TSplitter; sptrLog: TSplitter; ToolBar1: TToolBar; btnDelete: TToolButton; btnNewFromCtrl: TToolButton; ToolButton1: TToolButton; btnNewFromFile: TToolButton; btnRefresh: TToolButton; procedure ActionDeleteExecute({%H-}Sender: TObject); procedure ActionNewFromCtrlExecute({%H-}Sender: TObject); procedure ActionNewFromFileExecute({%H-}Sender: TObject); procedure ActionRefreshExecute({%H-}Sender: TObject); procedure FormCloseQuery({%H-}Sender: TObject; var CanClose: boolean); procedure lvFilesSelectItem({%H-}Sender: TObject; Item: TListItem; Selected: Boolean); private { private declarations } FControl: TControl; FPreviewControl: TControl; FStyleExt: String; procedure AddLog(const AText: String; AClear: Boolean = True); procedure CreatePreviewControl; function GetFileName: String; function GetStylesDir: String; public { public declarations } constructor {%H-}Create(AControl: TControl; const AFileExt: String); property FileName: String read GetFileName; end; { TBCStyleComponentEditor } TBCStyleComponentEditor = class(TComponentEditor) protected procedure BeginUpdate; procedure EndUpdate; function GetStyleExtension: String; procedure DoShowEditor; public procedure ExecuteVerb(Index: Integer); override; function GetVerb({%H-}Index: Integer): String; override; function GetVerbCount: Integer; override; end; { TBCSylePropertyEditor } TBCSylePropertyEditor = class({$IFDEF FPC}TClassPropertyEditor{$ELSE}TPropertyEditor{$ENDIF}) private procedure BeginUpdate; procedure EndUpdate; function GetStyleExtension: String; procedure DoShowEditor; public procedure Edit; Override; function GetAttributes: TPropertyAttributes; Override; end; implementation {$IFDEF FPC} uses MacroIntf, BCRTTI, IDEImagesIntf; {$ELSE} uses BCRTTI; {$ENDIF} { TBCSylePropertyEditor } procedure TBCSylePropertyEditor.BeginUpdate; begin if GetComponent(0).InheritsFrom(TBCStyleGraphicControl) then TBCStyleGraphicControl(GetComponent(0)).BeginUpdate else if GetComponent(0).InheritsFrom(TBCStyleCustomControl) then TBCStyleCustomControl(GetComponent(0)).BeginUpdate; end; procedure TBCSylePropertyEditor.EndUpdate; begin if GetComponent(0).InheritsFrom(TBCStyleGraphicControl) then TBCStyleGraphicControl(GetComponent(0)).EndUpdate else if GetComponent(0).InheritsFrom(TBCStyleCustomControl) then TBCStyleCustomControl(GetComponent(0)).EndUpdate; end; function TBCSylePropertyEditor.GetStyleExtension: String; begin if GetComponent(0).InheritsFrom(TBCStyleGraphicControl) then Result := TBCStyleGraphicControl(GetComponent(0)).StyleExtension else if GetComponent(0).InheritsFrom(TBCStyleCustomControl) then Result := TBCStyleCustomControl(GetComponent(0)).StyleExtension else Result := ''; end; procedure TBCSylePropertyEditor.DoShowEditor; var f: TBCfrmStyle; begin if GetStyleExtension='' then begin {$IFDEF FPC} MessageDlg('Empty ext', Format('Class %s has empty style extension', [GetComponent(0).ClassName]),mtError,[mbOK],0); {$ELSE} MessageDlg('Empty ext' + #10#13 + Format('Class %s has empty style extension', [GetComponent(0).ClassName]),mtError,[mbOK],0); {$ENDIF} Exit; end; f := TBCfrmStyle.Create(TControl(GetComponent(0)),GetStyleExtension); try if (f.ShowModal=mrOK) and FileExists(f.FileName) then begin try BeginUpdate; LoadStyle(GetComponent(0),f.FileName); finally EndUpdate; end; end; finally f.Free; end; end; procedure TBCSylePropertyEditor.Edit; begin DoShowEditor; end; function TBCSylePropertyEditor.GetAttributes: TPropertyAttributes; begin Result := [paDialog, paReadOnly]; end; { TBCfrmStyle } procedure TBCfrmStyle.ActionNewFromCtrlExecute(Sender: TObject); var sName: String; sl: TStrings; begin sName := 'My new style'; if InputQuery('Create new style', 'Style name', sName) then begin if Trim(sName)='' then raise Exception.Create('Name can not be empty'); sName := IncludeTrailingBackslash(GetStylesDir) + sName+'.'+FStyleExt; if FileExists(sName) then raise Exception.Create('Style with this name already exists!'); sl := TStringList.Create; try SaveStyle(FControl,'Me','',sl); sl.SaveToFile(sName); ActionRefresh.Execute; finally sl.Free; end; end; end; procedure TBCfrmStyle.ActionNewFromFileExecute(Sender: TObject); begin if OpenDialog1.Execute then begin if FileExists(IncludeTrailingBackslash(GetStylesDir)+ExtractFileName(OpenDialog1.FileName)) then raise Exception.Create('This style already exists'); {$IFDEF FPC} CopyFile(OpenDialog1.FileName,IncludeTrailingBackslash(GetStylesDir)+ExtractFileName(OpenDialog1.FileName)); {$ELSE} CopyFile(PWidechar(OpenDialog1.FileName),PWidechar(IncludeTrailingBackslash(GetStylesDir)+ExtractFileName(OpenDialog1.FileName)),False); {$ENDIF} ActionRefresh.Execute; end; end; procedure TBCfrmStyle.ActionRefreshExecute(Sender: TObject); var sl: TStrings; i: Integer; it: TListItem; h: TBCStyleHeader; begin {$IFDEF FPC}//# sl := FindAllFiles(GetStylesDir,'*.'+FStyleExt,False); {$ENDIF} try lvFiles.ItemIndex := -1; lvFiles.Selected := nil; lvFiles.Clear; if (sl<>nil) and (sl.Count>0) then begin lvFiles.{$IFNDEF FPC}Items.{$ENDIF}BeginUpdate; try for i:=0 to Pred(sl.Count) do begin it := lvFiles.Items.Add; it.Caption := ExtractFileName(sl.Strings[i]); GetStyleHeader(sl.Strings[i],@h); it.SubItems.Add(h.Author); // Author it.SubItems.Add(h.Description); // Description end; lvFiles.ItemIndex := 0; lvFiles.Selected := lvFiles.Items.Item[0]; // I noticed that OnSelect event is not called when we change // selected index manually, so we must call it manually lvFilesSelectItem(lvFiles,lvFiles.Selected,True); ActionDelete.Enabled := True; finally lvFiles.{$IFNDEF FPC}Items.{$ENDIF}EndUpdate; end; end else begin memoLogs.Clear; memoLogs.Visible := False; sptrLog.Visible := False; FPreviewControl.Visible := False; ActionDelete.Enabled := False; end; finally if sl<>nil then sl.Free; end; end; procedure TBCfrmStyle.FormCloseQuery(Sender: TObject; var CanClose: boolean); begin if (ModalResult=mrOK) and (lvFiles.ItemIndex=-1) then begin {$IFDEF FPC} MessageDlg('Assign file', 'No style selected', mtError, [mbOK], 0); {$ELSE} MessageDlg('Assign file' + #10#13 + 'No style selected', mtError, [mbOK], 0); {$ENDIF} CanClose := False; end else CanClose := True; end; procedure TBCfrmStyle.ActionDeleteExecute(Sender: TObject); begin if (lvFiles.SelCount=0) or {$IFDEF FPC} (MessageDlg('Deleting style', 'Do you really want to delete selected style? '+ 'This action delete file: '+IncludeTrailingBackslash(GetStylesDir)+lvFiles.Selected.Caption, mtConfirmation,mbYesNo,0)=mrNo) {$ELSE} (MessageDlg('Deleting style' + #10#13 + 'Do you really want to delete selected style? '+ 'This action delete file: '+ IncludeTrailingBackslash(GetStylesDir) + lvFiles.Selected.Caption, mtConfirmation,mbYesNo,0)=mrNo) {$ENDIF} then Exit; {$IFDEF FPC} DeleteFile(IncludeTrailingBackslash(GetStylesDir)+lvFiles.Selected.Caption); {$ELSE} DeleteFile(PWideChar(IncludeTrailingBackslash(GetStylesDir)+lvFiles.Selected.Caption)); {$ENDIF} ActionRefresh.Execute; end; procedure TBCfrmStyle.lvFilesSelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); var sl_logs: TStrings; i: Integer; begin if Selected and (Item<>nil) then begin memoLogs.Visible := False; sptrLog.Visible := False; memoLogs.Clear; FPreviewControl.Visible := True; ActionDelete.Enabled := True; sl_logs := TStringList.Create; try if not FileExists(IncludeTrailingBackslash(GetStylesDir)+Item.Caption) then Exit; LoadStyle(FPreviewControl,IncludeTrailingBackslash(GetStylesDir)+Item.Caption, sl_logs); // Because load style override it FPreviewControl.Constraints.MinWidth := 100; FPreviewControl.Constraints.MinHeight := 100; // Logs for i:=0 to Pred(sl_logs.Count) do AddLog(sl_logs.Strings[i],False); finally sl_logs.Free; end; end; end; procedure TBCfrmStyle.AddLog(const AText: String; AClear: Boolean = True); begin if AClear then memoLogs.Clear; if not memoLogs.Visible then begin memoLogs.Visible := True; sptrLog.Visible := True; sptrLog.Top := memoLogs.Top - 1; end; memoLogs.Lines.Add(AText); end; function TBCfrmStyle.GetStylesDir: String; begin Result := '$PkgDir(bgracontrols)'; {$IFDEF FPC} IDEMacros.SubstituteMacros(Result); {$ENDIF} Result := IncludeTrailingBackslash(Result)+'styles'; end; procedure TBCfrmStyle.CreatePreviewControl; begin FPreviewControl := TControlClass(FControl.ClassType).Create(Self); FPreviewControl.Constraints.MinWidth := 100; FPreviewControl.Constraints.MinHeight := 100; FPreviewControl.Parent := gboxPreview; {$IFDEF FPC}//# FPreviewControl.Caption := FControl.Caption; if Trim(FPreviewControl.Caption) = '' then FPreviewControl.Caption := 'Demo'; {$ENDIF} FPreviewControl.Visible := False; end; function TBCfrmStyle.GetFileName: String; begin if lvFiles.ItemIndex=-1 then Result := '' else Result := IncludeTrailingBackslash(GetStylesDir)+lvFiles.Selected.Caption; end; constructor TBCfrmStyle.Create(AControl: TControl; const AFileExt: String); // It seems that method LoadImage load icon on each call. Others lazarus // component editors doesn't check if icon exist but I will do. Small memory leak // reduction :P {$IFDEF FPC}//# function _LoadImage(AIdx: Integer; const AName: String): Integer; begin Result := IDEImages.GetImageIndex(AIdx,AName); if Result=-1 then Result := IDEImages.LoadImage(AIdx,AName); end; {$ENDIF} begin inherited Create(Application); FControl := AControl; FStyleExt := AFileExt; CreatePreviewControl; ActionRefresh.Execute; {$IFDEF FPC}//# ToolBar1.Images := IDEImages.Images_16; ActionList1.Images := ToolBar1.Images; ActionDelete.ImageIndex := _LoadImage(16,'laz_delete'); ActionNewFromCtrl.ImageIndex := _LoadImage(16,'laz_add'); ActionNewFromFile.ImageIndex := _LoadImage(16,'laz_open'); ActionRefresh.ImageIndex := _LoadImage(16,'laz_refresh'); {$ENDIF} ActionDelete.Enabled := False; OpenDialog1.Filter := 'BC Style|*.'+FStyleExt; OpenDialog1.DefaultExt := FStyleExt; OpenDialog1.InitialDir := GetStylesDir; end; {$R *.lfm} { TBCStyleComponentEditor } procedure TBCStyleComponentEditor.BeginUpdate; begin if Component.InheritsFrom(TBCStyleGraphicControl) then TBCStyleGraphicControl(Component).BeginUpdate else if Component.InheritsFrom(TBCStyleCustomControl) then TBCStyleCustomControl(Component).BeginUpdate; end; procedure TBCStyleComponentEditor.EndUpdate; begin if Component.InheritsFrom(TBCStyleGraphicControl) then TBCStyleGraphicControl(Component).EndUpdate else if Component.InheritsFrom(TBCStyleCustomControl) then TBCStyleCustomControl(Component).EndUpdate; end; function TBCStyleComponentEditor.GetStyleExtension: String; begin if Component.InheritsFrom(TBCStyleGraphicControl) then Result := TBCStyleGraphicControl(Component).StyleExtension else if Component.InheritsFrom(TBCStyleCustomControl) then Result := TBCStyleCustomControl(Component).StyleExtension else Result := ''; end; procedure TBCStyleComponentEditor.DoShowEditor; var f: TBCfrmStyle; begin if GetStyleExtension='' then begin {$IFDEF FPC} MessageDlg('Empty ext', Format('Class %s has empty style extension', [Component.ClassName]),mtError,[mbOK],0); {$ELSE} MessageDlg('Empty ext' + #10#13 + Format('Class %s has empty style extension', [Component.ClassName]),mtError,[mbOK],0); {$ENDIF} Exit; end; f := TBCfrmStyle.Create(TControl(Component),GetStyleExtension); try if (f.ShowModal=mrOK) and FileExists(f.FileName) then begin try BeginUpdate; LoadStyle(Component,f.FileName); finally EndUpdate; end; end; finally f.Free; end; end; procedure TBCStyleComponentEditor.ExecuteVerb(Index: Integer); begin case Index of 0: DoShowEditor; end; end; function TBCStyleComponentEditor.GetVerb(Index: Integer): String; begin Result := 'Assign style'; end; function TBCStyleComponentEditor.GetVerbCount: Integer; begin Result := 1; end; initialization RegisterComponentEditor(TBCStyleGraphicControl, TBCStyleComponentEditor); RegisterComponentEditor(TBCStyleCustomControl, TBCStyleComponentEditor); {$IFDEF FPC} RegisterPropertyEditor(ClassTypeInfo(TBCStyleDummyProperty),nil,'',TBCSylePropertyEditor); {$ELSE} RegisterPropertyEditor(TypeInfo(TBCStyleDummyProperty),nil,'',TBCSylePropertyEditor); {$ENDIF} end.