bcstylesform.pas 15 KB

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