mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-07 05:18:00 +02:00
407 lines
14 KiB
ObjectPascal
407 lines
14 KiB
ObjectPascal
{***************************************************************************
|
|
* *
|
|
* 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 <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Abstract:
|
|
Modal form to show all possible fpc options, parsed from -h output,
|
|
and enable/disable in custom compiler options.
|
|
}
|
|
unit AllCompilerOptions;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Math, Contnrs,
|
|
// LCL
|
|
Forms, Controls, StdCtrls, Buttons, ButtonPanel, EditBtn, ExtCtrls,
|
|
// LazUtils
|
|
LazUTF8, LazLoggerBase,
|
|
// IdeIntf
|
|
IDEImagesIntf,
|
|
Compiler, LazarusIDEStrConsts;
|
|
|
|
type
|
|
|
|
{ TfrmAllCompilerOptions }
|
|
|
|
TfrmAllCompilerOptions = class(TForm)
|
|
btnResetOptionsFilter: TSpeedButton;
|
|
ButtonPanel1: TButtonPanel;
|
|
cbShowModified: TCheckBox;
|
|
cbUseComments: TCheckBox;
|
|
edOptionsFilter: TEdit;
|
|
pnlFilter: TPanel;
|
|
sbAllOptions: TScrollBox;
|
|
procedure btnResetOptionsFilterClick(Sender: TObject);
|
|
procedure cbShowModifiedClick(Sender: TObject);
|
|
procedure sbMouseWheel(Sender: TObject; {%H-}Shift: TShiftState;
|
|
WheelDelta: Integer; {%H-}MousePos: TPoint; var Handled: Boolean);
|
|
procedure edOptionsFilterChange(Sender: TObject);
|
|
procedure FormCreate(Sender: TObject);
|
|
private
|
|
FIdleConnected: Boolean;
|
|
FOptionsReader: TCompilerOptReader;
|
|
FOptionsThread: TCompilerOptThread;
|
|
FGeneratedControls: TComponentList;
|
|
FEffectiveFilter: string;
|
|
FEffectiveShowModified: Boolean;
|
|
FRenderedOnce: Boolean;
|
|
procedure SetIdleConnected(AValue: Boolean);
|
|
procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
|
|
procedure CheckBoxClick(Sender: TObject);
|
|
procedure EditChange(Sender: TObject);
|
|
procedure ComboChange(Sender: TObject);
|
|
procedure RenderAndFilterOptions;
|
|
private
|
|
property IdleConnected: Boolean read FIdleConnected write SetIdleConnected;
|
|
public
|
|
constructor Create(TheOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
function ToCustomOptions(aStrings: TStrings): TModalResult;
|
|
public
|
|
property OptionsReader: TCompilerOptReader read FOptionsReader write FOptionsReader;
|
|
property OptionsThread: TCompilerOptThread read FOptionsThread write FOptionsThread;
|
|
end;
|
|
|
|
var
|
|
frmAllCompilerOptions: TfrmAllCompilerOptions;
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
{ TfrmAllCompilerOptions }
|
|
|
|
constructor TfrmAllCompilerOptions.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
FGeneratedControls := TComponentList.Create;
|
|
sbAllOptions.OnMouseWheel := @sbMouseWheel;
|
|
end;
|
|
|
|
destructor TfrmAllCompilerOptions.Destroy;
|
|
begin
|
|
IdleConnected:=false;
|
|
FGeneratedControls.Clear;
|
|
FreeAndNil(FGeneratedControls);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TfrmAllCompilerOptions.ToCustomOptions(aStrings: TStrings): TModalResult;
|
|
begin
|
|
Result := FOptionsReader.ToCustomOptions(aStrings, cbUseComments.Checked);
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.FormCreate(Sender: TObject);
|
|
begin
|
|
Caption:=lisAllOptions;
|
|
edOptionsFilter.Hint := lisFilterTheAvailableOptionsList;
|
|
IDEImages.AssignImage(btnResetOptionsFilter, ResBtnListFilter);
|
|
btnResetOptionsFilter.Enabled := False;
|
|
btnResetOptionsFilter.Hint := lisClearTheFilterForOptions;
|
|
cbShowModified.Caption:=lisShowOnlyModified;
|
|
cbUseComments.Caption:=lisUseCommentsInCustomOptions;
|
|
FEffectiveFilter:=#1; // Set an impossible value first, makes sure options are filtered.
|
|
ButtonPanel1.ShowBevel := False;
|
|
FRenderedOnce := False;
|
|
IdleConnected := True;
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.edOptionsFilterChange(Sender: TObject);
|
|
begin
|
|
btnResetOptionsFilter.Enabled := edOptionsFilter.Text<>'';
|
|
// Filter the list of options in OnIdle handler
|
|
IdleConnected := True;
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.btnResetOptionsFilterClick(Sender: TObject);
|
|
begin
|
|
edOptionsFilter.Text := '';
|
|
btnResetOptionsFilter.Enabled := False;
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.cbShowModifiedClick(Sender: TObject);
|
|
begin
|
|
IdleConnected := True;
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.SetIdleConnected(AValue: Boolean);
|
|
begin
|
|
if csDestroying in ComponentState then
|
|
AValue:=false;
|
|
if FIdleConnected = AValue then exit;
|
|
FIdleConnected := AValue;
|
|
if FIdleConnected then
|
|
Application.AddOnIdleHandler(@OnIdle)
|
|
else
|
|
Application.RemoveOnIdleHandler(@OnIdle);
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.OnIdle(Sender: TObject; var Done: Boolean);
|
|
|
|
function FormatTimeWithMs(aTime: TDateTime): string;
|
|
var
|
|
fs: TFormatSettings;
|
|
begin
|
|
fs.TimeSeparator := ':';
|
|
Result := FormatDateTime('nn:ss', aTime, fs)+'.'+FormatDateTime('zzz', aTime);
|
|
end;
|
|
|
|
var
|
|
StartTime: TDateTime;
|
|
begin
|
|
IdleConnected := False;
|
|
if FOptionsThread=nil then exit;
|
|
Screen.BeginWaitCursor;
|
|
try
|
|
FOptionsThread.EndParsing; // Make sure the options are read.
|
|
if FOptionsReader.ErrorMsg <> '' then
|
|
DebugLn(FOptionsReader.ErrorMsg)
|
|
else begin
|
|
StartTime := Now;
|
|
RenderAndFilterOptions;
|
|
DebugLn(Format('AllCompilerOptions: Time for reading options: %s, rendering GUI: %s',
|
|
[FormatTimeWithMs(FOptionsThread.ReadTime),
|
|
FormatTimeWithMs(Now-StartTime)]));
|
|
end;
|
|
finally
|
|
Screen.EndWaitCursor;
|
|
end;
|
|
FRenderedOnce := True;
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.sbMouseWheel(Sender: TObject;
|
|
Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
|
|
begin
|
|
with sbAllOptions.VertScrollBar do
|
|
Position := Position - Sign(WheelDelta) * Increment;
|
|
Handled := True;
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.CheckBoxClick(Sender: TObject);
|
|
var
|
|
cb: TCheckBox;
|
|
Opt: TCompilerOpt;
|
|
begin
|
|
cb := Sender as TCheckBox;
|
|
Opt := FOptionsReader.FindOptionById(cb.Tag);
|
|
if Assigned(Opt) then
|
|
begin
|
|
if cb.Checked then
|
|
Opt.Value := 'True'
|
|
else
|
|
Opt.Value := '';
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.EditChange(Sender: TObject);
|
|
var
|
|
ed: TCustomEdit;
|
|
Opt: TCompilerOpt;
|
|
begin
|
|
ed := Sender as TCustomEdit;
|
|
Opt := FOptionsReader.FindOptionById(ed.Tag);
|
|
if Assigned(Opt) then
|
|
Opt.Value := ed.Text;
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.ComboChange(Sender: TObject);
|
|
var
|
|
cb: TComboBox;
|
|
Opt: TCompilerOpt;
|
|
begin
|
|
cb := Sender as TComboBox;
|
|
Opt := FOptionsReader.FindOptionById(cb.Tag);
|
|
if Assigned(Opt) then
|
|
Opt.Value := cb.Text;
|
|
end;
|
|
|
|
procedure TfrmAllCompilerOptions.RenderAndFilterOptions;
|
|
var
|
|
Opt: TCompilerOpt;
|
|
yLoc: Integer;
|
|
Container: TCustomControl;
|
|
|
|
{ Calculated Heights and Left positions considering Runtime PPI }
|
|
lEditHeight: integer; // Edit + Dropdown lists
|
|
lCbHeight: integer; // Checkboxes and labels
|
|
lLeftMargin, // Leftmost position of text
|
|
lIndentPixs, // Nb of pixels to multiply by Indentation to
|
|
// place left text
|
|
lLeftDescrGroup: integer; // Nb of pixels where comments sould start
|
|
|
|
{ Replace TCompilerOpt.CalcLeft to repsect screen PPI scaling }
|
|
function CalcLeft(aRefCntrl: TControl; aOpt: TCompilerOpt) : integer;
|
|
begin
|
|
if (aRefCntrl is TEdit) or (aRefCntrl is TComboBox) then
|
|
Result := aRefCntrl.Left + aRefCntrl.Width + lLeftMargin
|
|
else begin
|
|
Result := Container.Canvas.GetTextWidth(aOpt.Option) + aRefCntrl.Left + lCbHeight;
|
|
if Result < lLeftDescrGroup then
|
|
Result := lLeftDescrGroup;
|
|
end;
|
|
end;
|
|
|
|
function MakeOptionCntrl(aCntrlClass: TControlClass; aCaption: string;
|
|
aTopOffs: integer=0): TControl;
|
|
// Header Label or TCheckBox
|
|
begin
|
|
Result := aCntrlClass.Create(Nil);
|
|
Result.Parent := Container;
|
|
Result.Top := yLoc+aTopOffs;
|
|
Result.Left := Opt.Indentation * lIndentPixs;
|
|
Result.Height := lCbHeight;
|
|
Result.Caption := aCaption;
|
|
Result.Tag := Opt.Id;
|
|
FGeneratedControls.Add(Result);
|
|
end;
|
|
|
|
function MakeEditCntrl(aLbl: TControl; aCntrlClass: TControlClass): TControl;
|
|
// TEdit or TComboBox
|
|
begin
|
|
Result := aCntrlClass.Create(Nil);
|
|
Result.Parent := Container;
|
|
Result.AnchorSide[akTop].Control := aLbl;
|
|
Result.AnchorSide[akTop].Side := asrCenter;
|
|
Result.Left := lLeftDescrGroup; // Now use Left instead of anchors
|
|
Result.Width := 20 * lLeftMargin;
|
|
Result.Height := lEditHeight;
|
|
Result.Anchors := [akLeft,akTop];
|
|
Result.Tag := Opt.Id;
|
|
FGeneratedControls.Add(Result);
|
|
end;
|
|
|
|
procedure MakeDescrLabel(aCntrl: TControl; aLeft: integer);
|
|
// Description label after CheckBox / Edit control
|
|
var
|
|
Lbl: TLabel;
|
|
begin
|
|
Lbl := TLabel.Create(Nil);
|
|
Lbl.Parent := Container;
|
|
Lbl.Caption := Opt.Description;
|
|
Lbl.AnchorSide[akTop].Control := aCntrl;
|
|
Lbl.AnchorSide[akTop].Side := asrCenter;
|
|
Lbl.Height := lCbHeight;
|
|
Lbl.Left := aLeft;
|
|
Lbl.Anchors := [akLeft,akTop];
|
|
Lbl.OnMouseWheel := @sbMouseWheel;
|
|
FGeneratedControls.Add(Lbl);
|
|
end;
|
|
|
|
procedure RenderOneLevel(aParentGroup: TCompilerOptGroup);
|
|
var
|
|
Cntrl, Lbl: TControl;
|
|
cb: TComboBox;
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to aParentGroup.CompilerOpts.Count-1 do begin
|
|
Opt := TCompilerOpt(aParentGroup.CompilerOpts[i]);
|
|
if Opt.Ignored or not Opt.Visible then Continue; // Maybe filtered out
|
|
case Opt.EditKind of
|
|
oeGroup, oeSet: begin // Label for group or set
|
|
Cntrl := MakeOptionCntrl(TLabel, Opt.Option+Opt.Suffix);
|
|
TLabel(Cntrl).OnMouseWheel := @sbMouseWheel;
|
|
MakeDescrLabel(Cntrl, CalcLeft(Cntrl,Opt));
|
|
end;
|
|
oeBoolean: begin // CheckBox
|
|
Cntrl := MakeOptionCntrl(TCheckBox, Opt.Option);
|
|
Assert((Opt.Value='') or (Opt.Value='True'), 'Wrong value in Boolean option '+Opt.Option);
|
|
TCheckBox(Cntrl).Checked := Opt.Value<>'';
|
|
TCheckBox(Cntrl).OnMouseWheel := @sbMouseWheel;
|
|
Cntrl.OnClick := @CheckBoxClick;
|
|
MakeDescrLabel(Cntrl, CalcLeft(Cntrl, Opt));
|
|
end;
|
|
oeSetElem: begin // Sub-item for set, CheckBox
|
|
Cntrl := MakeOptionCntrl(TCheckBox, Opt.Option+Opt.Description);
|
|
Assert((Opt.Value='') or (Opt.Value='True'), 'Wrong value in Boolean option '+Opt.Option);
|
|
TCheckBox(Cntrl).Checked := Opt.Value<>'';
|
|
TCheckBox(Cntrl).OnMouseWheel := @sbMouseWheel;
|
|
Cntrl.OnClick := @CheckBoxClick;
|
|
end;
|
|
oeNumber, oeText, oeSetNumber: begin // Edit
|
|
Lbl := MakeOptionCntrl(TLabel, Opt.Option+Opt.Suffix, lIndentPixs {3});
|
|
TLabel(Lbl).OnMouseWheel := @sbMouseWheel;
|
|
Cntrl := MakeEditCntrl(Lbl, TEdit);
|
|
if Opt.EditKind <> oeText then
|
|
TEdit(Cntrl).Width := 16 * lLeftMargin ;
|
|
TEdit(Cntrl).Text := Opt.Value;
|
|
TEdit(Cntrl).OnChange := @EditChange;
|
|
TEdit(Cntrl).OnMouseWheel := @sbMouseWheel;
|
|
MakeDescrLabel(Cntrl, CalcLeft(Cntrl, nil));
|
|
end;
|
|
oeList: begin // ComboBox
|
|
Lbl := MakeOptionCntrl(TLabel, Opt.Option+Opt.Suffix, lIndentPixs {3});
|
|
TLabel(Lbl).OnMouseWheel := @sbMouseWheel;
|
|
Cntrl := MakeEditCntrl(Lbl, TComboBox);
|
|
cb := TComboBox(Cntrl);
|
|
cb.Style := csDropDownList;
|
|
if Assigned(Opt.Choices) then
|
|
cb.Items.Assign(Opt.Choices);
|
|
cb.Text := Opt.Value;
|
|
cb.OnChange := @ComboChange;
|
|
MakeDescrLabel(Cntrl, CalcLeft(Cntrl, nil));
|
|
end
|
|
else
|
|
raise Exception.Create('TCompilerOptsRenderer.Render: Unknown EditKind.');
|
|
end;
|
|
Inc(yLoc, Cntrl.Height+2);
|
|
if Opt is TCompilerOptGroup then
|
|
RenderOneLevel(TCompilerOptGroup(Opt)); // Show other levels recursively
|
|
end;
|
|
end;
|
|
|
|
{ determine dimensions using height of existing controls }
|
|
procedure PickupLeftsAndHeights;
|
|
begin
|
|
lCbHeight := cbShowModified.Height;
|
|
lEditHeight := edOptionsFilter.Height;
|
|
|
|
lLeftMargin := lEditHeight div 4;
|
|
lIndentPixs := lEditHeight div 8;
|
|
lLeftDescrGroup := lLeftMargin * 16;
|
|
end;
|
|
|
|
begin
|
|
if (FEffectiveFilter = edOptionsFilter.Text)
|
|
and (FEffectiveShowModified = cbShowModified.Checked) then Exit;
|
|
Container := sbAllOptions;
|
|
Container.Perform(CM_PARENTFONTCHANGED, 0, 0);
|
|
Container.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TfrmAllCompilerOptions.RenderAndFilterOptions'){$ENDIF};
|
|
PickupLeftsAndHeights;
|
|
try
|
|
// First filter and set Visible flag.
|
|
FOptionsReader.FilterOptions(UTF8LowerCase(edOptionsFilter.Text),
|
|
cbShowModified.Checked);
|
|
// Then create and place new controls in GUI
|
|
FGeneratedControls.Clear;
|
|
yLoc := 0;
|
|
RenderOneLevel(FOptionsReader.RootOptGroup);
|
|
FEffectiveFilter := edOptionsFilter.Text;
|
|
FEffectiveShowModified := cbShowModified.Checked;
|
|
FocusControl(edOptionsFilter);
|
|
finally
|
|
Container.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TfrmAllCompilerOptions.RenderAndFilterOptions'){$ENDIF};
|
|
Container.Invalidate;
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|