bcstylesform.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. // SPDX-License-Identifier: LGPL-3.0-linking-exception
  2. { Styles form manager
  3. ------------------------------------------------------------------------------
  4. originally written in 2012 by Krzysztof Dibowski dibowski at interia.pl
  5. }
  6. {******************************* CONTRIBUTOR(S) ******************************
  7. - Edivando S. Santos Brasil | mailedivando@gmail.com
  8. (Compatibility with delphi VCL 11/2018)
  9. ***************************** END CONTRIBUTOR(S) *****************************}
  10. unit BCStylesForm;
  11. {$I bgracontrols.inc}
  12. interface
  13. uses
  14. Classes, SysUtils, {$IFDEF FPC}FileUtil, ComponentEditors, PropEdits,{$ELSE}
  15. Windows, DesignIntf, DesignEditors, PropertyCategories,
  16. ToolIntf, ExptIntf, DesignWindows,
  17. {$ENDIF}
  18. Forms, Controls, Graphics, Dialogs, ExtCtrls,
  19. StdCtrls, ActnList, ComCtrls, Buttons,
  20. bcbasectrls;
  21. type
  22. { TBCfrmStyle }
  23. TBCfrmStyle = class(TForm)
  24. ActionRefresh: TAction;
  25. ActionNewFromFile: TAction;
  26. ActionDelete: TAction;
  27. ActionNewFromCtrl: TAction;
  28. ActionList1: TActionList;
  29. BitBtn1: TBitBtn;
  30. BitBtn2: TBitBtn;
  31. gboxPreview: TGroupBox;
  32. gboxStyles: TGroupBox;
  33. lvFiles: TListView;
  34. memoLogs: TMemo;
  35. OpenDialog1: TOpenDialog;
  36. pnlBottom: TPanel;
  37. Splitter1: TSplitter;
  38. sptrLog: TSplitter;
  39. ToolBar1: TToolBar;
  40. btnDelete: TToolButton;
  41. btnNewFromCtrl: TToolButton;
  42. ToolButton1: TToolButton;
  43. btnNewFromFile: TToolButton;
  44. btnRefresh: TToolButton;
  45. procedure ActionDeleteExecute({%H-}Sender: TObject);
  46. procedure ActionNewFromCtrlExecute({%H-}Sender: TObject);
  47. procedure ActionNewFromFileExecute({%H-}Sender: TObject);
  48. procedure ActionRefreshExecute({%H-}Sender: TObject);
  49. procedure FormCloseQuery({%H-}Sender: TObject; var CanClose: boolean);
  50. procedure lvFilesSelectItem({%H-}Sender: TObject; Item: TListItem;
  51. Selected: Boolean);
  52. private
  53. { private declarations }
  54. FControl: TControl;
  55. FPreviewControl: TControl;
  56. FStyleExt: String;
  57. procedure AddLog(const AText: String; AClear: Boolean = True);
  58. procedure CreatePreviewControl;
  59. function GetFileName: String;
  60. function GetStylesDir: String;
  61. public
  62. { public declarations }
  63. constructor {%H-}Create(AControl: TControl; const AFileExt: String);
  64. property FileName: String read GetFileName;
  65. end;
  66. { TBCStyleComponentEditor }
  67. TBCStyleComponentEditor = class(TComponentEditor)
  68. protected
  69. procedure BeginUpdate;
  70. procedure EndUpdate;
  71. function GetStyleExtension: String;
  72. procedure DoShowEditor;
  73. public
  74. procedure ExecuteVerb(Index: Integer); override;
  75. function GetVerb({%H-}Index: Integer): String; override;
  76. function GetVerbCount: Integer; override;
  77. end;
  78. { TBCSylePropertyEditor }
  79. TBCSylePropertyEditor = class({$IFDEF FPC}TClassPropertyEditor{$ELSE}TPropertyEditor{$ENDIF})
  80. private
  81. procedure BeginUpdate;
  82. procedure EndUpdate;
  83. function GetStyleExtension: String;
  84. procedure DoShowEditor;
  85. public
  86. procedure Edit; Override;
  87. function GetAttributes: TPropertyAttributes; Override;
  88. end;
  89. implementation
  90. {$IFDEF FPC}
  91. uses MacroIntf, BCRTTI, IDEImagesIntf;
  92. {$ELSE}
  93. uses BCRTTI;
  94. {$ENDIF}
  95. { TBCSylePropertyEditor }
  96. procedure TBCSylePropertyEditor.BeginUpdate;
  97. begin
  98. if GetComponent(0).InheritsFrom(TBCStyleGraphicControl) then
  99. TBCStyleGraphicControl(GetComponent(0)).BeginUpdate
  100. else
  101. if GetComponent(0).InheritsFrom(TBCStyleCustomControl) then
  102. TBCStyleCustomControl(GetComponent(0)).BeginUpdate;
  103. end;
  104. procedure TBCSylePropertyEditor.EndUpdate;
  105. begin
  106. if GetComponent(0).InheritsFrom(TBCStyleGraphicControl) then
  107. TBCStyleGraphicControl(GetComponent(0)).EndUpdate
  108. else
  109. if GetComponent(0).InheritsFrom(TBCStyleCustomControl) then
  110. TBCStyleCustomControl(GetComponent(0)).EndUpdate;
  111. end;
  112. function TBCSylePropertyEditor.GetStyleExtension: String;
  113. begin
  114. if GetComponent(0).InheritsFrom(TBCStyleGraphicControl) then
  115. Result := TBCStyleGraphicControl(GetComponent(0)).StyleExtension
  116. else
  117. if GetComponent(0).InheritsFrom(TBCStyleCustomControl) then
  118. Result := TBCStyleCustomControl(GetComponent(0)).StyleExtension
  119. else
  120. Result := '';
  121. end;
  122. procedure TBCSylePropertyEditor.DoShowEditor;
  123. var f: TBCfrmStyle;
  124. begin
  125. if GetStyleExtension='' then
  126. begin
  127. {$IFDEF FPC}
  128. MessageDlg('Empty ext', Format('Class %s has empty style extension',
  129. [GetComponent(0).ClassName]),mtError,[mbOK],0);
  130. {$ELSE}
  131. MessageDlg('Empty ext' + #10#13 + Format('Class %s has empty style extension',
  132. [GetComponent(0).ClassName]),mtError,[mbOK],0);
  133. {$ENDIF}
  134. Exit;
  135. end;
  136. f := TBCfrmStyle.Create(TControl(GetComponent(0)),GetStyleExtension);
  137. try
  138. if (f.ShowModal=mrOK) and FileExists(f.FileName) then
  139. begin
  140. try
  141. BeginUpdate;
  142. LoadStyle(GetComponent(0),f.FileName);
  143. finally
  144. EndUpdate;
  145. end;
  146. end;
  147. finally
  148. f.Free;
  149. end;
  150. end;
  151. procedure TBCSylePropertyEditor.Edit;
  152. begin
  153. DoShowEditor;
  154. end;
  155. function TBCSylePropertyEditor.GetAttributes: TPropertyAttributes;
  156. begin
  157. Result := [paDialog, paReadOnly];
  158. end;
  159. { TBCfrmStyle }
  160. procedure TBCfrmStyle.ActionNewFromCtrlExecute(Sender: TObject);
  161. var
  162. sName: String;
  163. sl: TStrings;
  164. begin
  165. sName := 'My new style';
  166. if InputQuery('Create new style', 'Style name', sName) then
  167. begin
  168. if Trim(sName)='' then
  169. raise Exception.Create('Name can not be empty');
  170. sName := IncludeTrailingBackslash(GetStylesDir) + sName+'.'+FStyleExt;
  171. if FileExists(sName) then
  172. raise Exception.Create('Style with this name already exists!');
  173. sl := TStringList.Create;
  174. try
  175. SaveStyle(FControl,'Me','',sl);
  176. sl.SaveToFile(sName);
  177. ActionRefresh.Execute;
  178. finally
  179. sl.Free;
  180. end;
  181. end;
  182. end;
  183. procedure TBCfrmStyle.ActionNewFromFileExecute(Sender: TObject);
  184. begin
  185. if OpenDialog1.Execute then
  186. begin
  187. if FileExists(IncludeTrailingBackslash(GetStylesDir)+ExtractFileName(OpenDialog1.FileName)) then
  188. raise Exception.Create('This style already exists');
  189. {$IFDEF FPC}
  190. CopyFile(OpenDialog1.FileName,IncludeTrailingBackslash(GetStylesDir)+ExtractFileName(OpenDialog1.FileName));
  191. {$ELSE}
  192. CopyFile(PWidechar(OpenDialog1.FileName),PWidechar(IncludeTrailingBackslash(GetStylesDir)+ExtractFileName(OpenDialog1.FileName)),False);
  193. {$ENDIF}
  194. ActionRefresh.Execute;
  195. end;
  196. end;
  197. procedure TBCfrmStyle.ActionRefreshExecute(Sender: TObject);
  198. var
  199. sl: TStrings;
  200. i: Integer;
  201. it: TListItem;
  202. h: TBCStyleHeader;
  203. begin
  204. {$IFDEF FPC}//#
  205. sl := FindAllFiles(GetStylesDir,'*.'+FStyleExt,False);
  206. {$ENDIF}
  207. try
  208. lvFiles.ItemIndex := -1;
  209. lvFiles.Selected := nil;
  210. lvFiles.Clear;
  211. if (sl<>nil) and (sl.Count>0) then
  212. begin
  213. lvFiles.{$IFNDEF FPC}Items.{$ENDIF}BeginUpdate;
  214. try
  215. for i:=0 to Pred(sl.Count) do
  216. begin
  217. it := lvFiles.Items.Add;
  218. it.Caption := ExtractFileName(sl.Strings[i]);
  219. GetStyleHeader(sl.Strings[i],@h);
  220. it.SubItems.Add(h.Author); // Author
  221. it.SubItems.Add(h.Description); // Description
  222. end;
  223. lvFiles.ItemIndex := 0;
  224. lvFiles.Selected := lvFiles.Items.Item[0];
  225. // I noticed that OnSelect event is not called when we change
  226. // selected index manually, so we must call it manually
  227. lvFilesSelectItem(lvFiles,lvFiles.Selected,True);
  228. ActionDelete.Enabled := True;
  229. finally
  230. lvFiles.{$IFNDEF FPC}Items.{$ENDIF}EndUpdate;
  231. end;
  232. end else
  233. begin
  234. memoLogs.Clear;
  235. memoLogs.Visible := False;
  236. sptrLog.Visible := False;
  237. FPreviewControl.Visible := False;
  238. ActionDelete.Enabled := False;
  239. end;
  240. finally
  241. if sl<>nil then sl.Free;
  242. end;
  243. end;
  244. procedure TBCfrmStyle.FormCloseQuery(Sender: TObject; var CanClose: boolean);
  245. begin
  246. if (ModalResult=mrOK) and (lvFiles.ItemIndex=-1) then
  247. begin
  248. {$IFDEF FPC}
  249. MessageDlg('Assign file', 'No style selected', mtError, [mbOK], 0);
  250. {$ELSE}
  251. MessageDlg('Assign file' + #10#13 + 'No style selected', mtError, [mbOK], 0);
  252. {$ENDIF}
  253. CanClose := False;
  254. end
  255. else
  256. CanClose := True;
  257. end;
  258. procedure TBCfrmStyle.ActionDeleteExecute(Sender: TObject);
  259. begin
  260. if (lvFiles.SelCount=0) or
  261. {$IFDEF FPC}
  262. (MessageDlg('Deleting style', 'Do you really want to delete selected style? '+
  263. 'This action delete file: '+IncludeTrailingBackslash(GetStylesDir)+lvFiles.Selected.Caption,
  264. mtConfirmation,mbYesNo,0)=mrNo)
  265. {$ELSE}
  266. (MessageDlg('Deleting style' + #10#13 + 'Do you really want to delete selected style? '+
  267. 'This action delete file: '+ IncludeTrailingBackslash(GetStylesDir) + lvFiles.Selected.Caption,
  268. mtConfirmation,mbYesNo,0)=mrNo)
  269. {$ENDIF}
  270. then
  271. Exit;
  272. {$IFDEF FPC}
  273. DeleteFile(IncludeTrailingBackslash(GetStylesDir)+lvFiles.Selected.Caption);
  274. {$ELSE}
  275. DeleteFile(PWideChar(IncludeTrailingBackslash(GetStylesDir)+lvFiles.Selected.Caption));
  276. {$ENDIF}
  277. ActionRefresh.Execute;
  278. end;
  279. procedure TBCfrmStyle.lvFilesSelectItem(Sender: TObject; Item: TListItem;
  280. Selected: Boolean);
  281. var
  282. sl_logs: TStrings;
  283. i: Integer;
  284. begin
  285. if Selected and (Item<>nil) then
  286. begin
  287. memoLogs.Visible := False;
  288. sptrLog.Visible := False;
  289. memoLogs.Clear;
  290. FPreviewControl.Visible := True;
  291. ActionDelete.Enabled := True;
  292. sl_logs := TStringList.Create;
  293. try
  294. if not FileExists(IncludeTrailingBackslash(GetStylesDir)+Item.Caption) then
  295. Exit;
  296. LoadStyle(FPreviewControl,IncludeTrailingBackslash(GetStylesDir)+Item.Caption,
  297. sl_logs);
  298. // Because load style override it
  299. FPreviewControl.Constraints.MinWidth := 100;
  300. FPreviewControl.Constraints.MinHeight := 100;
  301. // Logs
  302. for i:=0 to Pred(sl_logs.Count) do
  303. AddLog(sl_logs.Strings[i],False);
  304. finally
  305. sl_logs.Free;
  306. end;
  307. end;
  308. end;
  309. procedure TBCfrmStyle.AddLog(const AText: String; AClear: Boolean = True);
  310. begin
  311. if AClear then memoLogs.Clear;
  312. if not memoLogs.Visible then
  313. begin
  314. memoLogs.Visible := True;
  315. sptrLog.Visible := True;
  316. sptrLog.Top := memoLogs.Top - 1;
  317. end;
  318. memoLogs.Lines.Add(AText);
  319. end;
  320. function TBCfrmStyle.GetStylesDir: String;
  321. begin
  322. Result := '$PkgDir(bgracontrols)';
  323. {$IFDEF FPC}
  324. IDEMacros.SubstituteMacros(Result);
  325. {$ENDIF}
  326. Result := IncludeTrailingBackslash(Result)+'styles';
  327. end;
  328. procedure TBCfrmStyle.CreatePreviewControl;
  329. begin
  330. FPreviewControl := TControlClass(FControl.ClassType).Create(Self);
  331. FPreviewControl.Constraints.MinWidth := 100;
  332. FPreviewControl.Constraints.MinHeight := 100;
  333. FPreviewControl.Parent := gboxPreview;
  334. {$IFDEF FPC}//#
  335. FPreviewControl.Caption := FControl.Caption;
  336. if Trim(FPreviewControl.Caption) = '' then
  337. FPreviewControl.Caption := 'Demo';
  338. {$ENDIF}
  339. FPreviewControl.Visible := False;
  340. end;
  341. function TBCfrmStyle.GetFileName: String;
  342. begin
  343. if lvFiles.ItemIndex=-1 then
  344. Result := ''
  345. else
  346. Result := IncludeTrailingBackslash(GetStylesDir)+lvFiles.Selected.Caption;
  347. end;
  348. constructor TBCfrmStyle.Create(AControl: TControl;
  349. const AFileExt: String);
  350. // It seems that method LoadImage load icon on each call. Others lazarus
  351. // component editors doesn't check if icon exist but I will do. Small memory leak
  352. // reduction :P
  353. {$IFDEF FPC}//#
  354. function _LoadImage(AIdx: Integer; const AName: String): Integer;
  355. begin
  356. Result := IDEImages.GetImageIndex(AIdx,AName);
  357. if Result=-1 then
  358. Result := IDEImages.LoadImage(AIdx,AName);
  359. end;
  360. {$ENDIF}
  361. begin
  362. inherited Create(Application);
  363. FControl := AControl;
  364. FStyleExt := AFileExt;
  365. CreatePreviewControl;
  366. ActionRefresh.Execute;
  367. {$IFDEF FPC}//#
  368. ToolBar1.Images := IDEImages.Images_16;
  369. ActionList1.Images := ToolBar1.Images;
  370. ActionDelete.ImageIndex := _LoadImage(16,'laz_delete');
  371. ActionNewFromCtrl.ImageIndex := _LoadImage(16,'laz_add');
  372. ActionNewFromFile.ImageIndex := _LoadImage(16,'laz_open');
  373. ActionRefresh.ImageIndex := _LoadImage(16,'laz_refresh');
  374. {$ENDIF}
  375. ActionDelete.Enabled := False;
  376. OpenDialog1.Filter := 'BC Style|*.'+FStyleExt;
  377. OpenDialog1.DefaultExt := FStyleExt;
  378. OpenDialog1.InitialDir := GetStylesDir;
  379. end;
  380. {$R *.lfm}
  381. { TBCStyleComponentEditor }
  382. procedure TBCStyleComponentEditor.BeginUpdate;
  383. begin
  384. if Component.InheritsFrom(TBCStyleGraphicControl) then
  385. TBCStyleGraphicControl(Component).BeginUpdate
  386. else
  387. if Component.InheritsFrom(TBCStyleCustomControl) then
  388. TBCStyleCustomControl(Component).BeginUpdate;
  389. end;
  390. procedure TBCStyleComponentEditor.EndUpdate;
  391. begin
  392. if Component.InheritsFrom(TBCStyleGraphicControl) then
  393. TBCStyleGraphicControl(Component).EndUpdate
  394. else
  395. if Component.InheritsFrom(TBCStyleCustomControl) then
  396. TBCStyleCustomControl(Component).EndUpdate;
  397. end;
  398. function TBCStyleComponentEditor.GetStyleExtension: String;
  399. begin
  400. if Component.InheritsFrom(TBCStyleGraphicControl) then
  401. Result := TBCStyleGraphicControl(Component).StyleExtension
  402. else
  403. if Component.InheritsFrom(TBCStyleCustomControl) then
  404. Result := TBCStyleCustomControl(Component).StyleExtension
  405. else
  406. Result := '';
  407. end;
  408. procedure TBCStyleComponentEditor.DoShowEditor;
  409. var f: TBCfrmStyle;
  410. begin
  411. if GetStyleExtension='' then
  412. begin
  413. {$IFDEF FPC}
  414. MessageDlg('Empty ext', Format('Class %s has empty style extension',
  415. [Component.ClassName]),mtError,[mbOK],0);
  416. {$ELSE}
  417. MessageDlg('Empty ext' + #10#13 + Format('Class %s has empty style extension',
  418. [Component.ClassName]),mtError,[mbOK],0);
  419. {$ENDIF}
  420. Exit;
  421. end;
  422. f := TBCfrmStyle.Create(TControl(Component),GetStyleExtension);
  423. try
  424. if (f.ShowModal=mrOK) and FileExists(f.FileName) then
  425. begin
  426. try
  427. BeginUpdate;
  428. LoadStyle(Component,f.FileName);
  429. finally
  430. EndUpdate;
  431. end;
  432. end;
  433. finally
  434. f.Free;
  435. end;
  436. end;
  437. procedure TBCStyleComponentEditor.ExecuteVerb(Index: Integer);
  438. begin
  439. case Index of
  440. 0: DoShowEditor;
  441. end;
  442. end;
  443. function TBCStyleComponentEditor.GetVerb(Index: Integer): String;
  444. begin
  445. Result := 'Assign style';
  446. end;
  447. function TBCStyleComponentEditor.GetVerbCount: Integer;
  448. begin
  449. Result := 1;
  450. end;
  451. initialization
  452. RegisterComponentEditor(TBCStyleGraphicControl, TBCStyleComponentEditor);
  453. RegisterComponentEditor(TBCStyleCustomControl, TBCStyleComponentEditor);
  454. {$IFDEF FPC}
  455. RegisterPropertyEditor(ClassTypeInfo(TBCStyleDummyProperty),nil,'',TBCSylePropertyEditor);
  456. {$ELSE}
  457. RegisterPropertyEditor(TypeInfo(TBCStyleDummyProperty),nil,'',TBCSylePropertyEditor);
  458. {$ENDIF}
  459. end.