diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index dd67b38..f3aadd2 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -101,3 +101,11 @@ Job finish with error when you double click, a window with run command, stdout and stderr ![Log](images/screenshot/status-job-distribution5.jpg) + +## Edit wslconfig file + +In tool bar, click on: +![WSLConfig toolbar button](images/screenshot/edit-wslconfig-file-toolbar.jpg) + +then a new window is displayed: +![WSLConfig](images/screenshot/edit-wslconfig-file) diff --git a/README.md b/README.md index d83dc11..5eb0a56 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ Just download executable file in release github section. ## Screenshot -![Main window](images/screenshot/mainwindow.png) +![Main window](images/screenshot/mainwindow.jpg) -![Distribution properties window](images/screenshot/distributionproperties.png) +![Distribution properties window](images/screenshot/distributionproperties.jpg) ## Documenation diff --git a/images/screenshot/distributionproperties.jpg b/images/screenshot/distributionproperties.jpg new file mode 100644 index 0000000..290a094 Binary files /dev/null and b/images/screenshot/distributionproperties.jpg differ diff --git a/images/screenshot/distributionproperties.png b/images/screenshot/distributionproperties.png deleted file mode 100644 index 8a4c0aa..0000000 Binary files a/images/screenshot/distributionproperties.png and /dev/null differ diff --git a/images/screenshot/edit-wslconfig-file-toolbar.jpg b/images/screenshot/edit-wslconfig-file-toolbar.jpg new file mode 100644 index 0000000..b0fd956 Binary files /dev/null and b/images/screenshot/edit-wslconfig-file-toolbar.jpg differ diff --git a/images/screenshot/edit-wslconfig-file.jpg b/images/screenshot/edit-wslconfig-file.jpg new file mode 100644 index 0000000..8960e02 Binary files /dev/null and b/images/screenshot/edit-wslconfig-file.jpg differ diff --git a/images/screenshot/mainwindow.jpg b/images/screenshot/mainwindow.jpg new file mode 100644 index 0000000..4ecef61 Binary files /dev/null and b/images/screenshot/mainwindow.jpg differ diff --git a/images/screenshot/mainwindow.png b/images/screenshot/mainwindow.png deleted file mode 100644 index b1d0d31..0000000 Binary files a/images/screenshot/mainwindow.png and /dev/null differ diff --git a/src/backgroundprocessprogressbar.pas b/src/backgroundprocessprogressbar.pas index 0567f50..5aeabe0 100644 --- a/src/backgroundprocessprogressbar.pas +++ b/src/backgroundprocessprogressbar.pas @@ -23,7 +23,8 @@ interface TBackgroundProcessProgressBarFinished = procedure(ExitStatus: integer; Canceled: boolean) of object; TBackgroundProcessProgressBarRun = procedure() of object; - // Background process + { TBackgroundProcessProgressBar } + TBackgroundProcessProgressBar = class(TObject) FProcess: TProcess; FTimer: TTimer; @@ -67,6 +68,8 @@ TBackgroundProcessProgressBar = class(TObject) implementation +{ TBackgroundProcessProgressBar } + constructor TBackgroundProcessProgressBar.Create(Owner: TForm); begin Create(Owner, 500); diff --git a/src/images/help.png b/src/images/help.png new file mode 100644 index 0000000..6d8ca7a Binary files /dev/null and b/src/images/help.png differ diff --git a/src/mainwindow.lfm b/src/mainwindow.lfm index 8421f30..54a9212 100644 --- a/src/mainwindow.lfm +++ b/src/mainwindow.lfm @@ -1,11 +1,11 @@ object WslGuiToolMainWindow: TWslGuiToolMainWindow - Left = -2012 + Left = 2924 Height = 441 - Top = 214 - Width = 723 + Top = 376 + Width = 722 Caption = 'WSL GUI Tool' ClientHeight = 441 - ClientWidth = 723 + ClientWidth = 722 DesignTimePPI = 120 Icon.Data = { 47AC01000000010006000000000001002000D926000066000000808000000100 @@ -3447,7 +3447,7 @@ object WslGuiToolMainWindow: TWslGuiToolMainWindow Left = 0 Height = 69 Top = 0 - Width = 723 + Width = 722 ButtonHeight = 64 ButtonWidth = 65 Caption = 'ToolBar1' @@ -3559,7 +3559,7 @@ object WslGuiToolMainWindow: TWslGuiToolMainWindow Left = 0 Height = 372 Top = 69 - Width = 723 + Width = 722 Align = alClient AutoSortIndicator = True Columns = < diff --git a/src/wslconfigeditwindow.lfm b/src/wslconfigeditwindow.lfm index 0eb4e8b..3c86b28 100644 --- a/src/wslconfigeditwindow.lfm +++ b/src/wslconfigeditwindow.lfm @@ -1,25 +1,131 @@ object FormWslconfigEdit: TFormWslconfigEdit - Left = -2011 + Left = -1547 Height = 301 - Top = 695 + Top = 385 Width = 400 - Caption = 'FormWslconfigEdit' + Caption = 'Global WSL2 configuration' ClientHeight = 301 ClientWidth = 400 DesignTimePPI = 120 OnCreate = FormCreate + OnDestroy = FormDestroy Position = poOwnerFormCenter LCLVersion = '2.0.12.0' - object Memo1: TMemo + object PanelButtons: TPanel Left = 0 - Height = 301 - Top = 0 + Height = 71 + Top = 230 Width = 400 - Align = alClient - Lines.Strings = ( - 'Memo1' - ) - ScrollBars = ssAutoBoth + Align = alBottom + AutoSize = True + BevelOuter = bvNone + BorderWidth = 20 + ClientHeight = 71 + ClientWidth = 400 + ParentColor = False TabOrder = 0 + object PanelButtonOk: TPanel + Left = 20 + Height = 31 + Top = 20 + Width = 94 + Align = alLeft + AutoSize = True + BevelOuter = bvNone + ClientHeight = 31 + ClientWidth = 94 + TabOrder = 0 + object ButtonSave: TButton + Left = 0 + Height = 31 + Top = 0 + Width = 94 + Caption = '&Save' + Enabled = False + OnClick = ButtonSaveClick + TabOrder = 0 + end + end + object PanelButtonReset: TPanel + Left = 192 + Height = 31 + Top = 20 + Width = 94 + Align = alRight + AutoSize = True + BevelOuter = bvNone + ClientHeight = 31 + ClientWidth = 94 + TabOrder = 1 + object ButtonReset: TButton + Left = 0 + Height = 31 + Top = 0 + Width = 94 + Caption = '&Reset' + OnClick = ButtonResetClick + TabOrder = 0 + end + end + object PanelButtonCancel: TPanel + Left = 286 + Height = 31 + Top = 20 + Width = 94 + Align = alRight + AutoSize = True + BevelOuter = bvNone + ClientHeight = 31 + ClientWidth = 94 + TabOrder = 2 + object ButtonCancel: TButton + Left = 0 + Height = 31 + Top = 0 + Width = 94 + Caption = '&Cancel' + ModalResult = 2 + TabOrder = 0 + end + end + end + object ImageListWslconfig: TImageList + Left = 240 + Top = 111 + Bitmap = { + 4C69010000001000000010000000FFFFFF00FFFFFF00FFFFFF00FFFFFF00DFDF + DF217979798B373737CF161616F0161616F0363636CF7B7B7B8BDCDCDC25FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FBFBFB037B7B7B8C0606 + 06FA212121E46E6E6E9394949476959595766E6E6E93212121E4050505FA7B7B + 7B8CFAFAFA07FFFFFF00FFFFFF00FFFFFF00FBFBFB03515151B90C0C0CF49A9A + 9A6BFCFCFC04FFFFFF00FFFFFF00FFFFFF00FFFFFF00FCFCFC049A9A9A6B0D0D + 0DF4525252B9FAFAFA07FFFFFF00FFFFFF007B7B7B880C0C0CF4C7C7C740FFFF + FF00FFFFFF00FFFFFF00FFFBF503FFFBF503FFFFFF00FFFFFF00FFFFFF00C8C8 + C8400C0C0CF47A7A7A8CFFFFFF00DDDDDD25050505FA9999996BFFFFFF00FFFF + FF00FFFDF901F6BB7285F39825EEF39825EEF5BB7287FFFDF902FFFFFF00FFFF + FF009A9A9A6B050505FADDDDDD217B7B7B8E202020E0FCFCFC03FFFFFF00FFFF + FF00F8CC9463F39621FEF6B86991F6B86991F39621FEF9CC9362FFFFFF00FFFF + FF00FCFCFC03212121E07A7A7A8B363636D36E6E6E9BFFFFFF00FFFFFF00FFFF + FF00F7C68873F6B86B8FFFFFFE00FFFFFE00F2A442CBF4B565AAFFFFFF00FFFF + FF00FFFFFF006E6E6E93373737CF161616F095959576FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFEFC00F9D3A24BF39825F4F6C07B85FFFFFF00FFFF + FF00FFFFFF0096969672161616EC161616EC95959572FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00F7C07A7DF39621FFF4A23BCCFEF1E211FFFFFF00FFFF + FF00FFFFFF0095959576161616F0373737CF70707093FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00F1B263B8F1AC55C1FFFBF702FFFFFF00FFFFFF00FFFF + FF00FFFFFF006D6D6D9B373737D07979798B212121E0FBFBFB03FFFFFF00FFFF + FF00FFFFFF00FFFFFF00F6D7B150F6D7B150FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FBFBFB03202020E47B7B7B8EDDDDDD21050505FA9999996BFFFFFF00FFFF + FF00FFFFFF00FFFFFF00F1B263B4F1B263B4FFFFFF00FFFFFF00FFFFFF00FFFF + FF009999996B050505FADEDEDE21FFFFFF007B7B7B8C0C0C0CF4C9C9C940FFFF + FF00FFFFFF00FFFFFF00FFFBF503FFFBF503FFFFFF00FFFFFF00FFFFFF00C7C7 + C7400C0C0CF47A7A7A88FFFFFF00FFFFFF00FBFBFB07535353B90C0C0CF49999 + 996BFCFCFC04FFFFFF00FFFFFF00FFFFFF00FFFFFF00FCFCFC049A9A9A6B0B0B + 0BF4515151B9FBFBFB03FFFFFF00FFFFFF00FFFFFF00FBFBFB077A7A7A8C0606 + 06FA202020E46F6F6F9395959576959595766E6E6E93222222E0050505FA7A7A + 7A8CFBFBFB03FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00DEDE + DE257979798B373737CF161616F0161616F0373737CF7A7A7A8BDDDDDD25FFFF + FF00FFFFFF00FFFFFF00FFFFFF00 + } end end diff --git a/src/wslconfigeditwindow.pas b/src/wslconfigeditwindow.pas index 34821cc..454439d 100644 --- a/src/wslconfigeditwindow.pas +++ b/src/wslconfigeditwindow.pas @@ -5,93 +5,219 @@ interface uses - Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, WslConfig; + Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, + MaskEdit, EditBtn, SpinEx, WslConfig, WslconfigParameterCtrl; type - { TFormWslconfigEdit } TFormWslconfigEdit = class(TForm) - Memo1: TMemo; + ButtonCancel: TButton; + ButtonReset: TButton; + ButtonSave: TButton; + ImageListWslconfig: TImageList; + PanelButtonCancel: TPanel; + PanelButtonOk: TPanel; + PanelButtonReset: TPanel; + PanelButtons: TPanel; + procedure ButtonResetClick(Sender: TObject); + procedure ButtonSaveClick(Sender: TObject); procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); private - + WslconfigPropertiesPanel: TWslConfigPanel; + WslList: TWslconfigEntryList; + Wslconfig: TWslconfigFile; + procedure OnValueChange(Sender: TObject); + procedure OnValueReset(Sender: TObject); public end; - TWslconfigEntryType = (EntryString, EntrySize, EntryNumber, EntryBoolean); - - TWslconfigEntry = class(TObject) - FCaption: string; - FKey: string; - FType: TWslconfigEntryType; - FHelp: string; - FPreview: boolean; - end; - var FormWslconfigEdit: TFormWslconfigEdit; +const + WSL2_SECTION = 'wsl2'; + WSL2_KERNEL_PROPERTY = 'kernel'; + WSL2_MEMORY_PROPERTY = 'memory'; + WSL2_PROCESSORS_PROPERTY = 'processors'; + WSL2_LOCALHOST_FORWARDING_PROPERTY = 'localhostForwarding'; + WSL2_KERNEL_COMMANDLINE_PROPERTY = 'kernelCommandLine'; + WSL2_SWAP_PROPERTY = 'swap'; + WSL2_SWAP_FILE_PROPERTY = 'swapFile'; + WSL2_PAGE_REPORTING_PROPERTY = 'pageReporting'; + WSL2_GUI_APPLICATIONS_PROPERTY = 'guiApplications'; + WSL2_DEBUG_CONSOLE_PROPERTY = 'debugConsole'; + WSL2_NESTED_VIRTUALIZATION_PROPERTY = 'nestedVirtualization'; + WSL2_VM_IDLE_TIMEOUT_PROPERTY = 'vmIdleTimeout'; + implementation {$R *.lfm} + { TFormWslconfigEdit } procedure TFormWslconfigEdit.FormCreate(Sender: TObject); -var - Wslconfig: TWslconfigFile; begin - Wslconfig := TWslconfigFile.Create(GetUserDir + 'wslconfig.test'); - - Memo1.Lines.Clear; - - Wslconfig.WriteString('wsl4', 'test_emeric', '123'); - - Memo1.Lines.Add('--- All section ----'); - Wslconfig.ReadSections(Memo1.Lines); - - Memo1.Lines.Add('--- All [wsl2] keys ----'); - Wslconfig.ReadSection('wsl2', Memo1.Lines); - - Memo1.Lines.Add('--- Delete key "test_quote" ----'); - Wslconfig.DeleteKey('wsl2', 'test_quote'); - Wslconfig.ReadSectionValues('wsl2', Memo1.Lines); - - Memo1.Lines.Add('--- All [test] values ----'); - Wslconfig.ReadSectionValues('test', Memo1.Lines); - - Memo1.Lines.Add('--- Delete section "test" ----'); - Wslconfig.EraseSection('test'); - - Memo1.Lines.Add('--- All [test] values ----'); - Wslconfig.ReadSectionValues('test', Memo1.Lines); - - Memo1.Lines.Add('--- All section ----'); - Wslconfig.ReadSections(Memo1.Lines); - - Memo1.Lines.Add('--- All [test] values ----'); - Wslconfig.WriteString('test', 'truc', ''); - Wslconfig.ReadSectionValues('test', Memo1.Lines); - Memo1.Lines.Add(Wslconfig.ReadString('test', 'cool', '')); - - // Delete key - Wslconfig.DeleteKey('wsl2', 'processors'); - - // Erase section - Wslconfig.EraseSection('test'); + Wslconfig := TWslconfigFile.Create(GetUserDir + '.wslconfig'); + + WslconfigPropertiesPanel := TWslConfigPanel.Create(Self); + WslconfigPropertiesPanel.Parent := Self; + WslconfigPropertiesPanel.Align := alClient; + WslconfigPropertiesPanel.Images := ImageListWslconfig; + WslconfigPropertiesPanel.HelpImageIndex := 0; + WslconfigPropertiesPanel.OnChange := @OnValueChange; + WslconfigPropertiesPanel.OnReset := @OnValueReset; + + WslList := TWslconfigEntryList.Create(); + + WslList.Add(TWslconfigEntry.Create( + 'Linux kernel path:', + WSL2_KERNEL_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_KERNEL_PROPERTY, ''), + EntryString, + 'An absolute Windows path to a custom Linux kernel.', + 'The Microsoft built kernel provided inbox', + 10, 0)); + WslList.Add(TWslconfigEntry.Create( + 'Maximum memory size:', + WSL2_MEMORY_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_MEMORY_PROPERTY, ''), + EntrySize, + 'How much memory to assign to the WSL 2 VM.', + '50% of total memory on Windows or 8GB, whichever is less; on builds before 20175: 80% of your total memory on Windows', + 10, 0)); + WslList.Add(TWslconfigEntry.Create( + 'Number of processor:', + WSL2_PROCESSORS_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_PROCESSORS_PROPERTY, ''), + EntryNumber, + 'How many processors to assign to the WSL 2 VM.', + 'The same number of processors on Windows', + 10, 0)); + WslList.Add(TWslconfigEntry.Create( + 'Port bound wildcard:', + WSL2_LOCALHOST_FORWARDING_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_LOCALHOST_FORWARDING_PROPERTY, ''), + EntryBoolean, + 'Boolean specifying if ports bound to wildcard or localhost in the WSL 2 VM should be connectable from the host via localhost:port.', + 'true', + 10, 0)); + WslList.Add(TWslconfigEntry.Create( + 'Kernel commande line:', + WSL2_KERNEL_COMMANDLINE_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_KERNEL_COMMANDLINE_PROPERTY, ''), + EntryString, + 'Additional kernel command line arguments.', + '', + 10, 0)); + WslList.Add(TWslconfigEntry.Create( + 'Swap size:', + WSL2_SWAP_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_SWAP_PROPERTY, ''), + EntryPath, + 'How much swap space to add to the WSL 2 VM, 0 for no swap file.', + '25% of memory size on Windows rounded up to the nearest GB', + 10, 0)); + WslList.Add(TWslconfigEntry.Create( + 'Swap filename:', + WSL2_SWAP_FILE_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_SWAP_FILE_PROPERTY, ''), + EntryString, + 'An absolute Windows path to the swap virtual hard disk.', + '%USERPROFILE%\AppData\Local\Temp\swap.vhdx', + 10, 0)); + WslList.Add(TWslconfigEntry.Create( + 'Reclaim unused memory:', + WSL2_PAGE_REPORTING_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_PAGE_REPORTING_PROPERTY, ''), + EntryBoolean, + 'Default true setting enables Windows to reclaim unused memory allocated to WSL 2 virtual machine.', + 'true', + 10, 0)); + WslList.Add(TWslconfigEntry.Create( + 'GUI support:', + WSL2_GUI_APPLICATIONS_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_GUI_APPLICATIONS_PROPERTY, ''), + EntryBoolean, + 'Boolean to turn on or off support for GUI applications (WSLg) in WSL.', + 'true', + 11, 0)); + WslList.Add(TWslconfigEntry.Create( + 'Enable debug console:', + WSL2_DEBUG_CONSOLE_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_DEBUG_CONSOLE_PROPERTY, ''), + EntryBoolean, + 'Boolean to turn on an output console Window that shows the contents of dmesg upon start of a WSL 2 distro instance.', + 'false', + 11, 0)); + WslList.Add(TWslconfigEntry.Create( + 'Need virtualization:', + WSL2_NESTED_VIRTUALIZATION_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_NESTED_VIRTUALIZATION_PROPERTY, ''), + EntryBoolean, + 'Boolean to turn on or off nested virtualization for WSL2.', + 'true', + 11, 0)); + WslList.Add(TWslconfigEntry.Create( + 'Timeout before shutdown:', + WSL2_VM_IDLE_TIMEOUT_PROPERTY, + Wslconfig.ReadString(WSL2_SECTION, WSL2_VM_IDLE_TIMEOUT_PROPERTY, ''), + EntryNumber, + 'The number of milliseconds that a VM is idle, before it is shut down.', + '60000', + 11, 0)); + + WslconfigPropertiesPanel.AddItems(WslList); +end; - // Update key - Wslconfig.WriteString('wsl2', 'processors', '568'); +procedure TFormWslconfigEdit.ButtonResetClick(Sender: TObject); +begin + WslconfigPropertiesPanel.Reset; +end; - // Create new section and new key - Wslconfig.WriteString('wsl2', 'test_quote', 'hello every body!'); +procedure TFormWslconfigEdit.ButtonSaveClick(Sender: TObject); +var + Index: integer; + Key: string; + Value: TWslValue; +begin + for Index := 0 to WslList.Count - 1 do + begin + Key := WslList[Index].Key; + Value := WslconfigPropertiesPanel.GetValue(Key); + + if Value.Found and Value.Changed + then begin + if Length(Value.Value) > 0 + then begin + Wslconfig.WriteString(WSL2_SECTION, Key, Value.Value); + end else begin + Wslconfig.DeleteKey(WSL2_SECTION, Key); + end; + end; + end; Wslconfig.UpdateFile; +end; +procedure TFormWslconfigEdit.FormDestroy(Sender: TObject); +begin + WslconfigPropertiesPanel.Free; + WslList.Free; Wslconfig.Free; end; -end. +procedure TFormWslconfigEdit.OnValueChange(Sender: TObject); +begin + ButtonSave.Enabled := true; +end; +procedure TFormWslconfigEdit.OnValueReset(Sender: TObject); +begin + ButtonSave.Enabled := false; +end; + +end. diff --git a/src/wslconfigparameterctrl.pas b/src/wslconfigparameterctrl.pas new file mode 100644 index 0000000..2a05f44 --- /dev/null +++ b/src/wslconfigparameterctrl.pas @@ -0,0 +1,605 @@ +{ + /*************************************************************************** + wslconfigparameterctrl.pas + -------------------------- + ***************************************************************************/ +} +{ +@abstract(Provide a GUI component to display wslconfig) +@author(Emeric MARTINEAU) +@created(2021) +} +unit WslconfigParameterCtrl; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, Forms, StdCtrls, ExtCtrls, Graphics, Controls, fgl; + +const + SPACE_BETWEEN_ITEM = 5; + +type + { TWslconfigEntryType } + + TWslconfigEntryType = (EntryString, EntrySize, EntryNumber, EntryBoolean, EntryPath); + + TWslconfigEntry = class(TObject) + public + Caption: string; + Key: string; + EntryType: TWslconfigEntryType; + Help: string; + DefaultValue: string; + WinMajorVersion: integer; + WinMinorVersion: integer; + Value: string; + + constructor Create(aCaption: String; aKey: string; aValue: string; + aType: TWslconfigEntryType; aHelp: string; aDefault: string; + aWinMajorVersion: integer; aWinMinorVersion: integer); + end; + + TWslValue = record + Value: string; + Changed: boolean; + Found: boolean; + end; + + { TWslconfigEntryPanel } + + TWslconfigEntryPanel = class(TObject) + FPanel: TPanel; + FCaption: TLabel; + FKey: string; + FType: TWslconfigEntryType; + FHelp: string; + FDefault: string; + FWinMajorVersion: integer; + FWinMinorVersion: integer; + FValue: string; + + FChanged: boolean; + FOnChange: TNotifyEvent; + + EditValue: TEdit; // for EntryNumber, EntryString, EntryPath, EntrySize + ComboValue: TComboBox; // for EntryBoolean, EntrySize + HelpImage: TImage; + private + function GetCaption: string; + function GetHeight: integer; + function GetWidth: integer; + function GetTop: integer; + procedure SetTop(NewTop: integer); + function GetCaptionSize: integer; + procedure SetCaptionSize(NewSize: integer); + function GetEnable: boolean; + procedure SetEnable(NewEnable: boolean); + function GetValue: string; + + procedure OnResizeInput(Sender: TObject); + procedure OnValueChange(Sender: TObject); + public + constructor Create(TheOwner: TWinControl; aCaption: String; aKey: string; + aValue: string; aType: TWslconfigEntryType; aHelp: string; aDefault: string; + aWinMajorVersion: integer; aWinMinorVersion: integer; Images: TImageList; + IndexImage: integer); + destructor Destroy; override; + + procedure Reset; + + property Caption: string read GetCaption; + property Key: string read FKey; + property EntryType: TWslconfigEntryType read FType; + property Help: string read FHelp; + property WinMajorVersion: integer read FWinMajorVersion; + property WinMinorVersion: integer read FWinMinorVersion; + property DefaultValue: string read FDefault; + property CaptionSize: integer read GetCaptionSize write SetCaptionSize; + property Height: integer read GetHeight; + property Width: integer read GetWidth; + property Top: integer read GetTop write SetTop; + property Enable: boolean read GetEnable write SetEnable; + property Value: string read GetValue; + property Changed: boolean read FChanged default false; + property OnChange: TNotifyEvent read FOnChange write FOnChange; + end; + + { TWslconfigEntryList } + + TWslconfigEntryPanelList = specialize TFPGObjectList; + TWslconfigEntryList = specialize TFPGObjectList; + + { TWslConfigPanel } + + TWslConfigPanel = class(TObject) + private + FScrollBox: TScrollBox; + FImages: TImageList; + FHelpImageIndex: integer; + FOnChange: TNotifyEvent; + FOnReset: TNotifyEvent; + + ListWslEntryPanel: TWslconfigEntryPanelList; + + procedure OnValueChange(Sender: TObject); + + function GetParent: TWinControl; + procedure SetParent(NewParent: TWinControl); + function GetVisible: boolean; + procedure SetVisible(NewVisibility: boolean); + function GetAlign: TAlign; + procedure SetAlign(NewAlign: TAlign); + function GetBorderSpacing: TControlBorderSpacing; + procedure TControlBorderSpacing(NewBorderSpacing: TControlBorderSpacing); + function GetBorderStyle: TBorderStyle; + procedure SetBorderStyle(NewStyle: TBorderStyle); + function GetWidth: integer; + public + constructor Create(AOwner: TWinControl); + destructor Destroy; override; + + procedure AddItems(List: TWslconfigEntryList); + procedure Reset; + function GetValue(Key: string): TWslValue; + + property Parent: TWinControl read GetParent write SetParent; + property Visible: boolean read GetVisible write SetVisible default true; + property Align: TAlign read GetAlign write SetAlign default alNone; + property BorderSpacing: TControlBorderSpacing read GetBorderSpacing write TControlBorderSpacing; + property BorderStyle: TBorderStyle read GetBorderStyle write SetBorderStyle default bsNone; + property Images: TImageList read FImages write FImages default nil; + property HelpImageIndex: integer read FHelpImageIndex write FHelpImageIndex default -1; + property OnChange: TNotifyEvent read FOnChange write FOnChange; + property OnReset: TNotifyEvent read FOnReset write FOnReset; + property Width: integer read GetWidth; + end; + +implementation + +{ TWslconfigEntry } + +constructor TWslconfigEntry.Create(aCaption: String; aKey: string; + aValue: string; aType: TWslconfigEntryType; aHelp: string; + aDefault: string; aWinMajorVersion: integer; aWinMinorVersion: integer); +begin + Caption := aCaption; + Key := aKey; + EntryType := aType; + Help := aHelp; + DefaultValue := aDefault; + WinMajorVersion := aWinMajorVersion; + WinMinorVersion := aWinMinorVersion; + Value := aValue; +end; + +{ TWslconfigEntryPanel - Helper function } + +function GenerateItemHint(aHelp: string; aDefault: string; aSupported: boolean; + aWinMajorVersion: integer; aWinMinorVersion: integer): string; +begin + if aSupported + then begin + Result := Format('%s' + #10 + #13 + 'Default value: %s', + [aHelp, aDefault]); + end else begin + Result := Format('%s' + #10 + #13 + 'Default value: %s' + #10 + #13 + + 'Only available on Windows %d.%d.', + [aHelp, aDefault, aWinMajorVersion, aWinMinorVersion]); + end; +end; + +{ TWslconfigEntryPanel } + +constructor TWslconfigEntryPanel.Create(TheOwner: TWinControl; aCaption: String; + aKey: string; aValue: string; aType: TWslconfigEntryType; aHelp: string; + aDefault: string; aWinMajorVersion: integer; aWinMinorVersion: integer; + Images: TImageList; IndexImage: integer); +begin + FPanel := TPanel.Create(TheOwner); + FPanel.Parent := TheOwner; + FPanel.AutoSize := true; + FPanel.Visible := true; + FPanel.Align := alTop; + FPanel.BorderStyle := bsNone; + FPanel.BorderWidth := 2; + FPanel.BevelOuter := bvNone; + + FCaption := TLabel.Create(FPanel); + FCaption.Parent := FPanel; + FCaption.AutoSize := false; + FCaption.Visible := true; + FCaption.Caption := aCaption; + FCaption.Width := FCaption.Canvas.TextWidth(aCaption); + FCaption.Height := FCaption.Canvas.TextHeight(aCaption); + FCaption.Alignment := taRightJustify; + FCaption.Layout := tlCenter; + + FKey := aKey; + FType := aType; + FHelp := aHelp; + FDefault := aDefault; + FWinMajorVersion := aWinMajorVersion; + FWinMinorVersion := aWinMinorVersion; + FValue := aValue; + FOnChange := nil; + + EditValue := TEdit.Create(FPanel); + EditValue.Parent := FPanel; + EditValue.Left := FCaption.Width + SPACE_BETWEEN_ITEM; + EditValue.Visible := false; + EditValue.BorderStyle := bsSingle; + EditValue.OnChange := @OnValueChange; + + ComboValue := TComboBox.Create(FPanel); + ComboValue.Parent := FPanel; + ComboValue.Left := FCaption.Width + SPACE_BETWEEN_ITEM; + ComboValue.Visible := false; + ComboValue.AutoSize := true; + ComboValue.AutoSelect := true; + ComboValue.Style := csDropDownList; + ComboValue.OnChange := @OnValueChange; + + HelpImage := TImage.Create(FPanel); + HelpImage.Parent := FPanel; + HelpImage.Picture.Bitmap := nil; // clear previous image + HelpImage.Hint := GenerateItemHint(aHelp, aDefault, true, FWinMajorVersion, FWinMinorVersion); + HelpImage.ShowHint := true; + + if (Images = nil) or (IndexImage = -1) + then begin + HelpImage.Visible := false; + end else begin + HelpImage.Width := Images.Width; + HelpImage.Height := Images.Height; + Images.GetBitmap(IndexImage, HelpImage.Picture.Bitmap); + end; + + if FType in [EntryNumber, EntryString, EntryPath] + then begin + EditValue.NumbersOnly := (aType = EntryNumber); + EditValue.Visible := true; + EditValue.OnResize := @OnResizeInput; // Need to have same size of Label and Edit + end else if FType = EntryBoolean + then begin + ComboValue.Items.Add(''); + ComboValue.Items.Add('true'); + ComboValue.Items.Add('false'); + ComboValue.Visible := true; + ComboValue.OnResize := @OnResizeInput; // Need to have same size of Label and Edit + end else if FType = EntrySize + then begin + EditValue.NumbersOnly := true; + EditValue.Visible := true; + + ComboValue.Left := EditValue.Left + EditValue.Width; + + ComboValue.Items.Add('KB'); + ComboValue.Items.Add('MB'); + ComboValue.Items.Add('GB'); + ComboValue.Items.Add('TB'); + ComboValue.Items.Add('PB'); + ComboValue.OnResize := @OnResizeInput; // Need to have same size of Label and Edit + ComboValue.Visible := true; + end; + + Reset; +end; + +destructor TWslconfigEntryPanel.Destroy; +begin + EditValue.Free; + HelpImage.Free; + ComboValue.Free; +end; + +procedure TWslconfigEntryPanel.OnResizeInput(Sender: TObject); +begin + FCaption.Height := TControl(Sender).Height; + HelpImage.Left := TControl(Sender).Left + TControl(Sender).Width + SPACE_BETWEEN_ITEM; + HelpImage.Top := (TControl(Sender).Height - HelpImage.Height) div 2; +end; + +procedure TWslconfigEntryPanel.OnValueChange(Sender: TObject); +begin + FChanged := true; + + if FOnChange <> nil + then begin + FOnChange(Self); + end; +end; + +function TWslconfigEntryPanel.GetCaption: string; +begin + Result := FCaption.Caption; +end; + +function TWslconfigEntryPanel.GetHeight: integer; +begin + Result := FPanel.Height; +end; + +function TWslconfigEntryPanel.GetWidth: integer; +begin + Result := FPanel.Width; +end; + +function TWslconfigEntryPanel.GetTop: integer; +begin + Result := FPanel.Top; +end; + +procedure TWslconfigEntryPanel.SetTop(NewTop: integer); +begin + FPanel.Top := NewTop; +end; + +function TWslconfigEntryPanel.GetCaptionSize: integer; +begin + Result := FCaption.Width; +end; + +procedure TWslconfigEntryPanel.SetCaptionSize(NewSize: integer); +begin + FCaption.Width := NewSize; + + if FType in [EntryNumber, EntryString, EntryPath] + then begin + EditValue.Left := NewSize + SPACE_BETWEEN_ITEM; + + HelpImage.Left := EditValue.Left + EditValue.Width + SPACE_BETWEEN_ITEM; + end else if FType = EntryBoolean + then begin + ComboValue.Left := NewSize + SPACE_BETWEEN_ITEM; + + HelpImage.Left := ComboValue.Left + ComboValue.Width + SPACE_BETWEEN_ITEM; + end else if FType = EntrySize + then begin + EditValue.Left := NewSize + SPACE_BETWEEN_ITEM; + ComboValue.Left := EditValue.Left + EditValue.Width; + + HelpImage.Left := ComboValue.Left + ComboValue.Width; + end; +end; + +function TWslconfigEntryPanel.GetEnable: boolean; +begin + Result := FCaption.Enabled; +end; + +procedure TWslconfigEntryPanel.SetEnable(NewEnable: boolean); +begin + FCaption.Enabled := NewEnable; + EditValue.Enabled := NewEnable; + ComboValue.Enabled := NewEnable; + + HelpImage.Hint := GenerateItemHint(FHelp, FDefault, NewEnable, + FWinMajorVersion, FWinMinorVersion); +end; + +procedure TWslconfigEntryPanel.Reset; +begin + if FType in [EntryNumber, EntryString, EntryPath] + then begin + EditValue.Text := FValue; + end else if FType = EntryBoolean + then begin + ComboValue.Text := LowerCase(FValue); + end else if FType = EntrySize + then begin + if Length(FValue) > 2 + then begin + EditValue.Text := Copy(FValue, 1, Length(FValue) - 2); + ComboValue.Text := Copy(FValue, Length(FValue) - 1, 2); + end else begin + EditValue.Text := FValue; + end; + end; +end; + +function TWslconfigEntryPanel.GetValue: string; +begin + if FType in [EntryNumber, EntryString, EntryPath] + then begin + Result := Trim(EditValue.Text); + end else if FType = EntryBoolean + then begin + Result := ComboValue.Text; + end else if FType = EntrySize + then begin + Result := Trim(EditValue.Text); + + if Length(Result) > 0 + then begin + Result := Result + ComboValue.Text; + end; + end; +end; + +{ TWslConfigPanel - Helper function } + +function ComputeNewTop(TheList: TWslconfigEntryPanelList): integer; +var LastItem: TWslconfigEntryPanel; +begin + Result := 0; + + if TheList.Count > 0 + then begin + LastItem := TheList[TheList.Count - 1]; + Result := LastItem.Top + LastItem.Height; + end; +end; + +{ TWslConfigPanel } + +constructor TWslConfigPanel.Create(AOwner: TWinControl); +begin + FScrollBox := TScrollBox.Create(AOwner); + FScrollBox.Width := 20; + FScrollBox.Height := 20; + FScrollBox.Visible := true; + FScrollBox.AutoScroll := true; + FOnChange := nil; + FOnReset := nil; + + ListWslEntryPanel := TWslconfigEntryPanelList.Create(); +end; + +destructor TWslConfigPanel.Destroy; +begin + ListWslEntryPanel.Free; + FScrollBox.Free; +end; + +procedure TWslConfigPanel.SetParent(NewParent: TWinControl); +begin + FScrollBox.Parent := NewParent; +end; + +function TWslConfigPanel.GetParent: TWinControl; +begin + Result := FScrollBox.Parent; +end; + +procedure TWslConfigPanel.SetVisible(NewVisibility: boolean); +begin + FScrollBox.Visible := NewVisibility; +end; + +function TWslConfigPanel.GetVisible: boolean; +begin + Result := FScrollBox.Visible; +end; + +procedure TWslConfigPanel.SetAlign(NewAlign: TAlign); +begin + FScrollBox.Align := NewAlign; +end; + +function TWslConfigPanel.GetAlign: TAlign; +begin + Result := FScrollBox.Align; +end; + +function TWslConfigPanel.GetBorderSpacing: TControlBorderSpacing; +begin + Result := FScrollBox.BorderSpacing; +end; + +procedure TWslConfigPanel.TControlBorderSpacing(NewBorderSpacing: TControlBorderSpacing); +begin + FScrollBox.BorderSpacing := NewBorderSpacing; +end; + +function TWslConfigPanel.GetBorderStyle: TBorderStyle; +begin + Result := FScrollBox.BorderStyle; +end; + +procedure TWslConfigPanel.SetBorderStyle(NewStyle: TBorderStyle); +begin + FScrollBox.BorderStyle := NewStyle; +end; + +procedure TWslConfigPanel.AddItems(List: TWslconfigEntryList); +var + Item: TWslconfigEntryPanel; + CurrentItem: TWslconfigEntry; + Index: integer; + TextCaptionSize: integer; +begin + ListWslEntryPanel.Clear; + + TextCaptionSize := 0; + + for Index := List.Count -1 downto 0 do + begin + CurrentItem := List[Index]; + + Item := TWslconfigEntryPanel.Create( + FScrollBox, + CurrentItem.Caption, + CurrentItem.Key, + CurrentItem.Value, + CurrentItem.EntryType, + CurrentItem.Help, + CurrentItem.DefaultValue, + CurrentItem.WinMajorVersion, + CurrentItem.WinMinorVersion, + FImages, + FHelpImageIndex); + + Item.OnChange := @OnValueChange; + + if (Win32MajorVersion < CurrentItem.WinMajorVersion) or + (Win32MinorVersion < CurrentItem.WinMinorVersion) + then begin + Item.Enable := false; + end; + + if Item.CaptionSize > TextCaptionSize + then begin + TextCaptionSize := Item.CaptionSize; + end; + + ListWslEntryPanel.Add(Item); + end; + + for Index := 0 to ListWslEntryPanel.Count - 1 do + begin + ListWslEntryPanel[Index].CaptionSize := TextCaptionSize; + end; +end; + +procedure TWslConfigPanel.Reset; +var + Index: integer; +begin + for Index := 0 to ListWslEntryPanel.Count - 1 do + begin + ListWslEntryPanel[Index].Reset; + end; + + if FOnReset <> nil + then begin + FOnReset(Self); + end; +end; + +function TWslConfigPanel.GetValue(Key: string): TWslValue; +var + Index: integer; +begin + Result.Value := ''; + Result.Changed := false; + Result.Found := false; + + for Index := 0 to ListWslEntryPanel.Count - 1 do + begin + if Key = ListWslEntryPanel[Index].Key + then begin + Result.Value := ListWslEntryPanel[Index].Value; + Result.Changed := ListWslEntryPanel[Index].Changed; + Result.Found := true; + end; + end; +end; + +procedure TWslConfigPanel.OnValueChange(Sender: TObject); +begin + if FOnChange <> nil + then begin + FOnChange(Self); + end; +end; + +function TWslConfigPanel.GetWidth: integer; +begin + Result := FScrollBox.Width; +end; + +end. + diff --git a/src/wslguitool.lpi b/src/wslguitool.lpi index 01cd2a3..c41a4a2 100644 --- a/src/wslguitool.lpi +++ b/src/wslguitool.lpi @@ -101,12 +101,15 @@ - + - + + + + - + @@ -150,6 +153,7 @@ + @@ -200,6 +204,11 @@ + + + + + @@ -213,6 +222,9 @@ + + + diff --git a/src/wslguitool.lpr b/src/wslguitool.lpr index c37862d..6bf3bd6 100644 --- a/src/wslguitool.lpr +++ b/src/wslguitool.lpr @@ -7,10 +7,11 @@ cthreads, {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset - Forms, MainWindow, WslApi, WslCommandLine, WslRegistry, ApplicationInfo, - AboutWindow, DistributionPropertiesWindow, ImportDistributionWindow, - RunCommandWithUserWindow, PromptWindow, BackgroundProcessProgressBar, - ProcessResultDisplay, WslConfigEditWindow, Wslconfig; + Forms, lazcontrols, MainWindow, WslApi, WslCommandLine, WslRegistry, + ApplicationInfo, AboutWindow, DistributionPropertiesWindow, + ImportDistributionWindow, RunCommandWithUserWindow, PromptWindow, + BackgroundProcessProgressBar, ProcessResultDisplay, WslConfigEditWindow, + Wslconfig, WslconfigParameterCtrl; {$R *.res}