123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917 |
- unit IDE.HelperFunc;
- {
- Inno Setup
- Copyright (C) 1997-2025 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- Additional Compiler IDE functions
- }
- interface
- uses
- Windows,
- Classes, Forms, Dialogs, Menus, Controls, StdCtrls, Graphics,
- ScintEdit, IDE.IDEScintEdit, ModernColors;
- const
- MRUListMaxCount = 10;
- type
- TMRUItemCompareProc = function(const S1, S2: String): Integer;
- TAddLinesPrefix = (alpNone, alpTimestamp, alpCountdown);
- TKeyMappingType = (kmtDelphi, kmtVisualStudio);
- procedure InitFormFont(Form: TForm);
- procedure SetControlWindowTheme(const WinControl: TWinControl; const Dark: Boolean);
- procedure InitFormThemeInit(const Theme: TTheme);
- function InitFormTheme(const Form: TForm): Boolean;
- function InitFormThemeGetBkColor(const WindowColor: Boolean): TColor;
- function InitFormThemeIsDark: Boolean;
- function GetDisplayFilename(const Filename: String): String;
- function GetFileTitle(const Filename: String): String;
- function GetCleanFileNameOfFile(const Filename: String): String;
- function GetLastWriteTimeOfFile(const Filename: String;
- LastWriteTime: PFileTime): Boolean;
- procedure AddFileToRecentDocs(const Filename: String);
- function GenerateGuid: String;
- function ISPPInstalled: Boolean;
- function IsISPPBuiltins(const Filename: String): Boolean;
- function WindowsVersionAtLeast(const AMajor, AMinor: Byte; const ABuild: Word = 0): Boolean;
- function IsWindows10: Boolean;
- function IsWindows11: Boolean;
- function GetDefaultThemeType: TThemeType;
- function GetDefaultKeyMappingType: TKeyMappingType;
- function GetDefaultMemoKeyMappingType: TIDEScintKeyMappingType;
- procedure LaunchFileOrURL(const AFilename: String; const AParameters: String = '');
- procedure OpenDonateSite;
- procedure OpenMailingListSite;
- procedure ClearMRUList(const MRUList: TStringList; const Section: String);
- procedure ReadMRUList(const MRUList: TStringList; const Section, Ident: String);
- procedure ModifyMRUList(const MRUList: TStringList; const Section, Ident: String;
- const AItem: String; const AddNewItem: Boolean; CompareProc: TMRUItemCompareProc);
- procedure LoadKnownIncludedAndHiddenFiles(const AFilename: String; const IncludedFiles, HiddenFiles: TStringList);
- procedure SaveKnownIncludedAndHiddenFiles(const AFilename: String; const IncludedFiles, HiddenFiles: TStringList);
- procedure DeleteKnownIncludedAndHiddenFiles(const AFilename: String);
- procedure LoadBreakPointLines(const AFilename: String; const BreakPointLines: TStringList);
- procedure SaveBreakPointLines(const AFilename: String; const BreakPointLines: TStringList);
- procedure DeleteBreakPointLines(const AFilename: String);
- function NewShortCutToText(const ShortCut: TShortCut): String;
- procedure SetFakeShortCutText(const MenuItem: TMenuItem; const S: String);
- procedure SetFakeShortCut(const MenuItem: TMenuItem; const Key: Word;
- const Shift: TShiftState); overload;
- procedure SetFakeShortCut(const MenuItem: TMenuItem; const ShortCut: TShortCut); overload;
- procedure SaveTextToFile(const Filename: String;
- const S: String; const SaveEncoding: TSaveEncoding);
- procedure AddLines(const ListBox: TListBox; const S: String; const AObject: TObject; const LineBreaks: Boolean; const Prefix: TAddLinesPrefix; const PrefixParam: Cardinal);
- procedure SetLowPriority(ALowPriority: Boolean; var SavePriorityClass: DWORD);
- procedure SetHelpFileDark(const Dark: Boolean);
- function GetHelpFile: String;
- function FindOptionsToSearchOptions(const FindOptions: TFindOptions;
- const RegEx: Boolean): TScintFindOptions; overload;
- function FindOptionsToSearchOptions(const MatchCase: Boolean;
- const RegEx: Boolean): TScintFindOptions; overload;
- function RegExToReplaceMode(const RegEx: Boolean): TScintReplaceMode;
- procedure StartAddRemovePrograms;
- function GetSourcePath(const AFilename: String): String;
- function ReadScriptLines(const ALines: TStringList; const ReadFromFile: Boolean;
- const ReadFromFileFilename: String; const NotReadFromFileMemo: TScintEdit): Integer;
- function CreateBitmapInfo(const Width, Height, BitCount: Integer): TBitmapInfo;
- function GetPreferredMemoFont: String;
- function DoubleAmp(const S: String): String;
- function HighContrastActive: Boolean;
- implementation
- uses
- ActiveX, ShlObj, ShellApi, CommDlg, SysUtils, IOUtils, StrUtils, ExtCtrls,
- Messages, DwmApi, Consts,
- Shared.CommonFunc, Shared.CommonFunc.Vcl, PathFunc, Shared.FileClass, NewUxTheme, NewNotebook,
- IDE.MainForm, IDE.Messages, Shared.ConfigIniFile;
- procedure InitFormFont(Form: TForm);
- var
- FontName: String;
- Metrics: TNonClientMetrics;
- begin
- begin
- Metrics.cbSize := SizeOf(Metrics);
- if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SizeOf(Metrics),
- @Metrics, 0) then
- FontName := Metrics.lfMessageFont.lfFaceName;
- { Only allow fonts that we know will fit the text correctly }
- if not SameText(FontName, 'Microsoft Sans Serif') and
- not SameText(FontName, 'Segoe UI') then
- FontName := 'Tahoma';
- end;
- Form.Font.Name := FontName;
- Form.Font.Size := 8;
- end;
- procedure SetControlWindowTheme(const WinControl: TWinControl; const Dark: Boolean);
- { Can be used for memos and listboxes to switch them to (or from) a native dark scrollbar }
- begin
- if UseThemes then begin
- WinControl.StyleName := 'Windows';
- if Dark then
- SetWindowTheme(WinControl.Handle, 'DarkMode_Explorer', nil)
- else
- SetWindowTheme(WinControl.Handle, nil, nil);
- end;
- end;
- var
- FormTheme: TTheme;
- procedure InitFormThemeInit(const Theme: TTheme);
- begin
- FormTheme := Theme;
- end;
- function InitFormTheme(const Form: TForm): Boolean;
- { Assumes forms other then MainForm call this function only once during creation, and assumes they
- don't need any styling if the theme is non dark. Always styles MainForm. Returns True if it did
- style, False otherwise. }
- begin
- Result := (Form = MainForm) or FormTheme.Dark;
- if Result then begin
- Form.Color := InitFormThemeGetBkColor(Form = MainForm); { Prevents some flicker, but not all }
- { Based on https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes
- Unlike this article we check for Windows 10 Version 2004 because that's the first version
- that introduced DWMWA_USE_IMMERSIVE_DARK_MODE as 20 (the now documented value) instead of 19 }
- if WindowsVersionAtLeast(10, 0, 19041) then begin
- Form.StyleElements := Form.StyleElements - [seBorder];
- const DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
- var value: BOOL := FormTheme.Dark;
- DwmSetWindowAttribute(Form.Handle, DWMWA_USE_IMMERSIVE_DARK_MODE, @value, SizeOf(value));
- end;
- end;
- end;
- function InitFormThemeGetBkColor(const WindowColor: Boolean): TColor;
- begin
- if WindowColor then begin
- Result := FormTheme.Colors[tcBack]; { This is white if not dark mode }
- if Result = clWhite then
- Result := GetSysColor(COLOR_WINDOW); { For high contrast themes }
- end else
- Result := FormTheme.Colors[tcToolBack]; { This is gray/btnface if not dark mode }
- end;
- function InitFormThemeIsDark: Boolean;
- begin
- Result := FormTheme.Dark;
- end;
- function GetDisplayFilename(const Filename: String): String;
- var
- Buf: array[0..MAX_PATH-1] of Char;
- begin
- if CommDlg.GetFileTitle(PChar(Filename), Buf, SizeOf(Buf) div SizeOf(Buf[0])) = 0 then
- Result := Buf
- else
- Result := Filename;
- end;
- function GetFileTitle(const Filename: String): String;
- begin
- if Filename = '' then
- Result := 'Untitled'
- else
- Result := Filename;
- end;
- function GetCleanFileNameOfFile(const Filename: String): String;
- begin
- var Files := TDirectory.GetFiles(PathExtractDir(Filename), PathExtractName(Filename));
- if Length(Files) = 1 then
- Result := Files[0]
- else
- Result := Filename;
- end;
- function GetLastWriteTimeOfFile(const Filename: String;
- LastWriteTime: PFileTime): Boolean;
- var
- H: THandle;
- begin
- H := CreateFile(PChar(Filename), 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
- nil, OPEN_EXISTING, 0, 0);
- if H <> INVALID_HANDLE_VALUE then begin
- Result := GetFileTime(H, nil, nil, LastWriteTime);
- CloseHandle(H);
- end
- else
- Result := False;
- end;
- procedure AddFileToRecentDocs(const Filename: String);
- { Notifies the shell that a document has been opened. This will
- add the file to the Recent section of the app's Jump List.
- It is only necessary to call this function when the shell is unaware that
- a file is being opened. Files opened through Explorer or common dialogs get
- added to the Jump List automatically. }
- begin
- SHAddToRecentDocs(SHARD_PATHW, PChar(Filename));
- end;
- function GenerateGuid: String;
- var
- Guid: TGUID;
- P: PWideChar;
- begin
- if CoCreateGuid(Guid) <> S_OK then
- raise Exception.Create('CoCreateGuid failed');
- if StringFromCLSID(Guid, P) <> S_OK then
- raise Exception.Create('StringFromCLSID failed');
- try
- Result := P;
- finally
- CoTaskMemFree(P);
- end;
- end;
- function ISPPInstalled: Boolean;
- begin
- Result := NewFileExists(PathExtractPath(NewParamStr(0)) + 'ISPP.dll');
- end;
- function IsISPPBuiltins(const Filename: String): Boolean;
- begin
- Result := PathCompare(PathExtractName(Filename), 'ISPPBuiltins.iss') = 0;
- end;
- var
- WindowsVersion: Cardinal;
- function WindowsVersionAtLeast(const AMajor, AMinor: Byte; const ABuild: Word): Boolean;
- begin
- Result := WindowsVersion >= Cardinal((AMajor shl 24) or (AMinor shl 16) or ABuild);
- end;
- function IsWindows10: Boolean;
- begin
- Result := WindowsVersionAtLeast(10, 0);
- end;
- function IsWindows11: Boolean;
- begin
- Result := WindowsVersionAtLeast(10, 0, 22000);
- end;
- function GetDefaultThemeType: TThemeType;
- var
- K: HKEY;
- Size, AppsUseLightTheme: DWORD;
- begin
- Result := ttModernLight;
- if IsWindows10 and (RegOpenKeyExView(rvDefault, HKEY_CURRENT_USER, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize', 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS) then begin
- Size := SizeOf(AppsUseLightTheme);
- if (RegQueryValueEx(K, 'AppsUseLightTheme', nil, nil, @AppsUseLightTheme, @Size) = ERROR_SUCCESS) and (AppsUseLightTheme = 0) then
- Result := ttModernDark;
- RegCloseKey(K);
- end;
- end;
- function GetDefaultKeyMappingType: TKeyMappingType;
- begin
- Result := kmtDelphi;
- end;
- function GetDefaultMemoKeyMappingType: TIDEScintKeyMappingType;
- begin
- Result := kmtDefault;
- end;
- procedure LaunchFileOrURL(const AFilename: String; const AParameters: String = '');
- begin
- { SEE_MASK_FLAG_NO_UI isn't used, so error dialogs are possible }
- const OwnerWnd = GetOwnerWndForMessageBox;
- const WindowList = DisableTaskWindows(OwnerWnd);
- try
- const Dir = GetSystemDir;
- var Info: TShellExecuteInfo;
- FillChar(Info, SizeOf(Info), 0);
- Info.cbSize := SizeOf(Info);
- Info.fMask := SEE_MASK_NOASYNC;
- Info.Wnd := OwnerWnd;
- Info.lpVerb := 'open';
- Info.lpFile := PChar(AFilename);
- Info.lpParameters := PChar(AParameters);
- Info.lpDirectory := PChar(Dir);
- Info.nShow := SW_SHOWNORMAL;
- ShellExecuteEx(@Info);
- finally
- EnableTaskWindows(WindowList);
- end;
- end;
- procedure OpenDonateSite;
- begin
- LaunchFileOrURL('https://jrsoftware.org/isdonate.php');
- end;
- procedure OpenMailingListSite;
- begin
- LaunchFileOrURL('https://jrsoftware.org/ismail.php');
- end;
- procedure ClearMRUList(const MRUList: TStringList; const Section: String);
- var
- Ini: TConfigIniFile;
- begin
- Ini := TConfigIniFile.Create;
- try
- MRUList.Clear;
- Ini.EraseSection(Section);
- finally
- Ini.Free;
- end;
- end;
- procedure ReadMRUList(const MRUList: TStringList; const Section, Ident: String);
- { Loads a list of MRU items from the registry }
- var
- Ini: TConfigIniFile;
- I: Integer;
- S: String;
- begin
- Ini := TConfigIniFile.Create;
- try
- MRUList.Clear;
- for I := 0 to MRUListMaxCount-1 do begin
- S := Ini.ReadString(Section, Ident + IntToStr(I), '');
- if S <> '' then MRUList.Add(S);
- end;
- finally
- Ini.Free;
- end;
- end;
- procedure ModifyMRUList(const MRUList: TStringList; const Section, Ident: String;
- const AItem: String; const AddNewItem: Boolean; CompareProc: TMRUItemCompareProc);
- var
- I: Integer;
- Ini: TConfigIniFile;
- S: String;
- begin
- I := 0;
- while I < MRUList.Count do begin
- if CompareProc(MRUList[I], AItem) = 0 then
- MRUList.Delete(I)
- else
- Inc(I);
- end;
- if AddNewItem then
- MRUList.Insert(0, AItem);
- while MRUList.Count > MRUListMaxCount do
- MRUList.Delete(MRUList.Count-1);
- { Save new MRU items }
- Ini := TConfigIniFile.Create;
- try
- { MRU list }
- for I := 0 to MRUListMaxCount-1 do begin
- if I < MRUList.Count then
- S := MRUList[I]
- else
- S := '';
- Ini.WriteString(Section, Ident + IntToStr(I), S);
- end;
- finally
- Ini.Free;
- end;
- end;
- procedure LoadConfigIniList(const AIni: TConfigIniFile; const ASection, AIdent: String;
- const AList: TStringList; const ADelimiter: Char);
- begin
- if ASection = '' then
- raise Exception.Create('ASection must be set');
- var OldDelimiter := AList.Delimiter;
- AList.Delimiter := ADelimiter;
- try
- AList.DelimitedText := AIni.ReadString(ASection, AIdent, '');
- finally
- AList.Delimiter := OldDelimiter;
- end;
- end;
- procedure DeleteConfigIniList(const AIni: TConfigIniFile; const ASection, AIdent: String);
- begin
- if ASection = '' then
- raise Exception.Create('ASection must be set');
- AIni.DeleteKey(ASection, AIdent);
- end;
- procedure SaveConfigIniList(const AIni: TConfigIniFile; const ASection, AIdent: String;
- const AList: TStringList; const ADelimiter: Char);
- begin
- if AList.Count = 0 then begin
- DeleteConfigIniList(AIni, ASection, AIdent);
- Exit;
- end;
- if ASection = '' then
- raise Exception.Create('ASection must be set');
- var OldDelimiter := AList.Delimiter;
- AList.Delimiter := ADelimiter;
- try
- AIni.WriteString(ASection, AIdent, AList.DelimitedText);
- finally
- AList.Delimiter := OldDelimiter;
- end;
- end;
- procedure LoadKnownIncludedAndHiddenFiles(const AFilename: String; const IncludedFiles, HiddenFiles: TStringList);
- begin
- var Ini := TConfigIniFile.Create;
- try
- LoadConfigIniList(Ini, 'IncludedFilesHistory', AFilename, IncludedFiles, '*');
- LoadConfigIniList(Ini, 'HiddenFilesHistory', AFilename, HiddenFiles, '*');
- finally
- Ini.Free;
- end;
- end;
- procedure SaveKnownIncludedAndHiddenFiles(const AFilename: String; const IncludedFiles, HiddenFiles: TStringList);
- begin
- var Ini := TConfigIniFile.Create;
- try
- SaveConfigIniList(Ini, 'IncludedFilesHistory', AFilename, IncludedFiles, '*');
- SaveConfigIniList(Ini, 'HiddenFilesHistory', AFilename, HiddenFiles, '*');
- finally
- Ini.Free;
- end;
- end;
- procedure DeleteKnownIncludedAndHiddenFiles(const AFilename: String);
- begin
- var Ini := TConfigIniFile.Create;
- try
- DeleteConfigIniList(Ini, 'IncludedFilesHistory', AFilename);
- DeleteConfigIniList(Ini, 'HiddenFilesHistory', AFilename);
- finally
- Ini.Free;
- end;
- end;
- procedure LoadBreakPointLines(const AFilename: String; const BreakPointLines: TStringList);
- begin
- var Ini := TConfigIniFile.Create;
- try
- LoadConfigIniList(Ini, 'BreakPointLines', AFilename, BreakPointLines, ',');
- finally
- Ini.Free;
- end;
- end;
- procedure SaveBreakPointLines(const AFilename: String; const BreakPointLines: TStringList);
- begin
- var Ini := TConfigIniFile.Create;
- try
- SaveConfigIniList(Ini, 'BreakPointLines', AFilename, BreakPointLines, ',');
- finally
- Ini.Free;
- end;
- end;
- procedure DeleteBreakPointLines(const AFilename: String);
- begin
- var Ini := TConfigIniFile.Create;
- try
- DeleteConfigIniList(Ini, 'BreakPointLines', AFilename);
- finally
- Ini.Free;
- end;
- end;
- function NewShortCutToText(const ShortCut: TShortCut): String;
- { This function is better than Delphi's ShortCutToText function because it works
- for dead keys. A dead key is a key which waits for the user to press another
- key so it can be combined. For example `+e=è. Pressing space after a dead key
- produces the dead key char itself. For example `+space=`. }
- const
- { List of chars ShortCutToText knows about and doesn't rely on Win32's
- GetKeyNameText for, taken from Vcl.Menus.pas }
- OKKeys = [$08, $09, $0D, $1B, $20..$28, $2D..$2E, $30..$39, $41..$5A, $70..$87];
- begin
- Result := '';
- var Key := LoByte(Word(ShortCut));
- if not (Key in OKKeys) then begin
- { ShortCutToText will use Win32's GetKeyNameText for this key and if it's
- a dead key this gives long names like 'ACCENT CIRCONFLEXE' instead of a
- short name like '^'. Long names are not what we want so handle these dead
- keys ourselves and use ToUnicode instead of GetKeyNameText to find the
- short name. For non-dead keys we always call ShortCutToText even if
- ToUnicode might work as well. }
- var ScanCode := MapVirtualKey(Key, MAPVK_VK_TO_VSC);
- if ScanCode <> 0 then begin
- var KeyboardState: TKeyboardState;
- GetKeyboardState(KeyboardState);
- const TempSize = 64; { Same as Vcl.Touch.Keyboard.pas }
- var TempStr: String;
- SetLength(TempStr, TempSize);
- ZeroMemory(@TempStr[1], TempSize * SizeOf(Char));
- var Size := ToUnicode(Key, ScanCode, KeyboardState, @TempStr[1], TempSize, 0);
- if Size = -1 then begin
- { This was a dead key, now stored in TempStr. Add space to get the dead
- key char itself. }
- ScanCode := MapVirtualKey(VK_SPACE, MAPVK_VK_TO_VSC);
- if ScanCode <> 0 then begin
- Size := ToUnicode(VK_SPACE, ScanCode, KeyboardState, @TempStr[1], TempSize, 0);
- if Size = 1 then begin
- var Name := TempStr[1];
- if ShortCut and scShift <> 0 then Result := Result + SmkcShift;
- if ShortCut and scCtrl <> 0 then Result := Result + SmkcCtrl;
- if ShortCut and scAlt <> 0 then Result := Result + SmkcAlt;
- Result := Result + Name;
- end;
- end;
- end;
- end else begin
- { This virtual key has no scan code meaning it's impossible to enter with
- the current keyboard layout (for example French AZERTY + VK_OEM_MINUS).
- We can just exit because calling ShortCutToText is pointless. }
- Exit;
- end;
- end;
- if Result = '' then
- Result := ShortCutToText(ShortCut);
- { Example CompForm test code:
- SetFakeShortCut(HDonate, ShortCut(VK_OEM_1, []));
- SetFakeShortCut(HShortcutsDoc, ShortCut(VK_OEM_PLUS, []));
- SetFakeShortCut(HDoc, ShortCut(VK_OEM_COMMA, []));
- SetFakeShortCut(HExamples, ShortCut(VK_OEM_MINUS, []));
- SetFakeShortCut(HFaq, ShortCut(VK_OEM_PERIOD, []));
- SetFakeShortCut(HMailingList, ShortCut(VK_OEM_2, []));
- SetFakeShortCut(HWhatsNew, ShortCut(VK_OEM_3, []));
- SetFakeShortCut(HWebsite, ShortCut(VK_OEM_4, []));
- SetFakeShortCut(HISPPDoc, ShortCut(VK_OEM_5, []));
- SetFakeShortCut(HAbout, ShortCut(VK_OEM_6, []));
- SetFakeShortCut(TAddRemovePrograms, ShortCut(VK_OEM_7, []));
- Without our dead key handling this produces for example:
- -US International + VK_OEM_3: "GRAVE"
- -French AZERTY + VK_OEM_7: "ACCENT CIRCONFLEXE"
- To add a keyboard layout follow the instructions at
- https://www.thewindowsclub.com/add-or-remove-keyboard-layout-in-windows-11
- and then switch to the language using the task bar's language bar.
- Also see https://code.visualstudio.com/docs/getstarted/keybindings#_keyboard-layouts }
- end;
- procedure SetFakeShortCutText(const MenuItem: TMenuItem; const S: String);
- begin
- var Caption := MenuItem.Caption;
- var P := Pos(#9, Caption);
- if P <> 0 then
- Delete(Caption, P, MaxInt);
- if S <> '' then
- MenuItem.Caption := Caption + #9 + S
- else
- MenuItem.Caption := Caption;
- end;
- procedure SetFakeShortCut(const MenuItem: TMenuItem; const Key: Word;
- const Shift: TShiftState);
- begin
- SetFakeShortCut(MenuItem, ShortCut(Key, Shift));
- end;
- procedure SetFakeShortCut(const MenuItem: TMenuItem; const ShortCut: TShortCut);
- begin
- SetFakeShortCutText(MenuItem, NewShortCutToText(ShortCut));
- end;
- procedure SaveTextToFile(const Filename: String;
- const S: String; const SaveEncoding: TSaveEncoding);
- var
- AnsiMode: Boolean;
- AnsiStr: AnsiString;
- F: TTextFileWriter;
- begin
- AnsiMode := False;
- if SaveEncoding = seAuto then begin
- AnsiStr := AnsiString(S);
- if S = String(AnsiStr) then
- AnsiMode := True;
- end;
- F := TTextFileWriter.Create(Filename, fdCreateAlways, faWrite, fsNone);
- try
- if AnsiMode then
- F.WriteAnsi(AnsiStr)
- else begin
- F.UTF8WithoutBOM := SaveEncoding <> seUTF8WithBOM;
- F.Write(S);
- end;
- finally
- F.Free;
- end;
- end;
- procedure AddLines(const ListBox: TListBox; const S: String; const AObject: TObject; const LineBreaks: Boolean; const Prefix: TAddLinesPrefix; const PrefixParam: Cardinal);
- var
- ST: TSystemTime;
- LineNumber: Cardinal;
- procedure AddLine(S: String);
- var
- TimestampPrefixTab: Boolean;
- DC: HDC;
- Size: TSize;
- begin
- TimestampPrefixTab := False;
- case Prefix of
- alpTimestamp:
- begin
- if LineNumber = 0 then begin
- { Don't forget about ListBox's DrawItem if you change the format of the following timestamp. }
- Insert(Format('[%.2u%s%.2u%s%.2u%s%.3u] ', [ST.wHour, FormatSettings.TimeSeparator,
- ST.wMinute, FormatSettings.TimeSeparator, ST.wSecond, FormatSettings.DecimalSeparator,
- ST.wMilliseconds]), S, 1);
- end else begin
- Insert(#9, S, 1); { Not actually painted - just for Ctrl+C }
- TimestampPrefixTab := True;
- end;
- end;
- alpCountdown:
- begin
- Insert(Format('[%.2d] ', [PrefixParam-LineNumber]), S, 1);
- end;
- end;
- try
- ListBox.TopIndex := ListBox.Items.AddObject(S, AObject);
- except
- on EOutOfResources do begin
- ListBox.Clear;
- SendMessage(ListBox.Handle, LB_SETHORIZONTALEXTENT, 0, 0);
- ListBox.Items.Add(SCompilerStatusReset);
- ListBox.TopIndex := ListBox.Items.AddObject(S, AObject);
- end;
- end;
- DC := GetDC(0);
- try
- SelectObject(DC, ListBox.Font.Handle);
- if TimestampPrefixTab then
- GetTextExtentPoint(DC, PChar(S)+1, Length(S)-1, Size)
- else
- GetTextExtentPoint(DC, PChar(S), Length(S), Size);
- finally
- ReleaseDC(0, DC);
- end;
- Inc(Size.cx, 5);
- if TimestampPrefixTab then
- Inc(Size.cx, PrefixParam);
- if Size.cx > SendMessage(ListBox.Handle, LB_GETHORIZONTALEXTENT, 0, 0) then
- SendMessage(ListBox.Handle, LB_SETHORIZONTALEXTENT, Size.cx, 0);
- Inc(LineNumber);
- end;
- var
- LineStart, I: Integer;
- LastWasCR: Boolean;
- begin
- GetLocalTime(ST);
- if LineBreaks then begin
- LineNumber := 0;
- LineStart := 1;
- LastWasCR := False;
- { Call AddLine for each line. CR, LF, and CRLF line breaks are supported. }
- for I := 1 to Length(S) do begin
- if S[I] = #13 then begin
- AddLine(Copy(S, LineStart, I - LineStart));
- LineStart := I + 1;
- LastWasCR := True;
- end
- else begin
- if S[I] = #10 then begin
- if not LastWasCR then
- AddLine(Copy(S, LineStart, I - LineStart));
- LineStart := I + 1;
- end;
- LastWasCR := False;
- end;
- end;
- AddLine(Copy(S, LineStart, Maxint));
- end else
- AddLine(S);
- end;
- procedure SetLowPriority(ALowPriority: Boolean; var SavePriorityClass: DWORD);
- begin
- if ALowPriority then begin
- { Save current priority and change to 'low' }
- if SavePriorityClass = 0 then
- SavePriorityClass := GetPriorityClass(GetCurrentProcess);
- SetPriorityClass(GetCurrentProcess, IDLE_PRIORITY_CLASS);
- end
- else begin
- { Restore original priority }
- if SavePriorityClass <> 0 then begin
- SetPriorityClass(GetCurrentProcess, SavePriorityClass);
- SavePriorityClass := 0;
- end;
- end;
- end;
- var
- HelpFileDark: Boolean;
- procedure SetHelpFileDark(const Dark: Boolean);
- begin
- HelpFileDark := Dark;
- end;
- function GetHelpFile: String;
- begin
- Result := Format('%sisetup%s.chm', [PathExtractPath(NewParamStr(0)) {$IFDEF DEBUG} + '..\..\Files\' {$ENDIF}, IfThen(HelpFileDark, '-dark', '')]);
- end;
- function FindOptionsToSearchOptions(const FindOptions: TFindOptions;
- const RegEx: Boolean): TScintFindOptions;
- begin
- Result := [];
- if frMatchCase in FindOptions then
- Include(Result, sfoMatchCase);
- if frWholeWord in FindOptions then
- Include(Result, sfoWholeWord);
- if RegEx then
- Include(Result, sfoRegEx);
- end;
- function FindOptionsToSearchOptions(const MatchCase: Boolean;
- const RegEx: Boolean): TScintFindOptions; overload;
- begin
- Result := [];
- if MatchCase then
- Include(Result, sfoMatchCase);
- if RegEx then
- Include(Result, sfoRegEx);
- end;
- function RegExToReplaceMode(const RegEx: Boolean): TScintReplaceMode;
- begin
- if RegEx then
- Result := srmRegEx
- else
- Result := srmMinimal;
- end;
- procedure StartAddRemovePrograms;
- var
- Dir: String;
- Wow64DisableWow64FsRedirectionFunc: function(var OldValue: Pointer): BOOL; stdcall;
- Wow64RevertWow64FsRedirectionFunc: function(OldValue: Pointer): BOOL; stdcall;
- RedirDisabled: Boolean;
- RedirOldValue: Pointer;
- StartupInfo: TStartupInfo;
- ProcessInfo: TProcessInformation;
- begin
- Dir := GetSystemDir;
- FillChar(StartupInfo, SizeOf(StartupInfo), 0);
- StartupInfo.cb := SizeOf(StartupInfo);
- { Have to disable file system redirection because the 32-bit version of
- appwiz.cpl is buggy on XP x64 RC2 -- it doesn't show any Change/Remove
- buttons on 64-bit MSI entries, and it doesn't list non-MSI 64-bit apps
- at all. }
- Wow64DisableWow64FsRedirectionFunc := GetProcAddress(GetModuleHandle(kernel32),
- 'Wow64DisableWow64FsRedirection');
- Wow64RevertWow64FsRedirectionFunc := GetProcAddress(GetModuleHandle(kernel32),
- 'Wow64RevertWow64FsRedirection');
- RedirDisabled := Assigned(Wow64DisableWow64FsRedirectionFunc) and
- Assigned(Wow64RevertWow64FsRedirectionFunc) and
- Wow64DisableWow64FsRedirectionFunc(RedirOldValue);
- try
- Win32Check(CreateProcess(nil, PChar('"' + AddBackslash(Dir) + 'control.exe" appwiz.cpl'),
- nil, nil, False, 0, nil, PChar(Dir), StartupInfo, ProcessInfo));
- finally
- if RedirDisabled then
- Wow64RevertWow64FsRedirectionFunc(RedirOldValue);
- end;
- CloseHandle(ProcessInfo.hProcess);
- CloseHandle(ProcessInfo.hThread);
- end;
- function GetSourcePath(const AFilename: String): String;
- begin
- if AFilename <> '' then
- Result := PathExtractPath(AFilename)
- else begin
- { If the script was not saved, default to My Documents }
- Result := GetShellFolderPath(CSIDL_PERSONAL);
- if Result = '' then
- raise Exception.Create('GetShellFolderPath failed');
- end;
- end;
- function ReadScriptLines(const ALines: TStringList; const ReadFromFile: Boolean;
- const ReadFromFileFilename: String; const NotReadFromFileMemo: TScintEdit): Integer;
- function ContainsNullChar(const S: String): Boolean;
- var
- I: Integer;
- begin
- Result := False;
- for I := 1 to Length(S) do
- if S[I] = #0 then begin
- Result := True;
- Break;
- end;
- end;
- var
- F: TTextFileReader;
- I: Integer;
- begin
- if ReadFromFile then begin
- F := TTextFileReader.Create(ReadFromFileFilename, fdOpenExisting, faRead, fsRead);
- try
- while not F.Eof do
- ALines.Add(F.ReadLine);
- finally
- F.Free;
- end;
- end
- else begin
- ALines.Capacity := NotReadFromFileMemo.Lines.Count;
- ALines.Assign(NotReadFromFileMemo.Lines);
- end;
- { Check for null characters }
- for I := 0 to ALines.Count-1 do begin
- if ContainsNullChar(ALines[I]) then begin
- Result := I;
- Exit;
- end;
- end;
- Result := -1;
- end;
- function CreateBitmapInfo(const Width, Height, BitCount: Integer): TBitmapInfo;
- begin
- ZeroMemory(@Result, SizeOf(Result));
- Result.bmiHeader.biSize := SizeOf(Result.bmiHeader);
- Result.bmiHeader.biWidth := Width;
- Result.bmiHeader.biHeight := Height;
- Result.bmiHeader.biPlanes := 1;
- Result.bmiHeader.biBitCount := BitCount;
- Result.bmiHeader.biCompression := BI_RGB;
- end;
- var
- PreferredMemoFont: String;
- function GetPreferredMemoFont: String;
- begin
- Result := PreferredMemoFont;
- end;
- function DoubleAmp(const S: String): String;
- var
- I: Integer;
- begin
- Result := S;
- I := 1;
- while I <= Length(Result) do begin
- if Result[I] = '&' then begin
- Inc(I);
- Insert('&', Result, I);
- Inc(I);
- end
- else
- Inc(I, PathCharLength(S, I));
- end;
- end;
- function HighContrastActive: Boolean;
- begin
- var HighContrast: THighContrast;
- HighContrast.cbSize := SizeOf(HighContrast);
- Result := False;
- if SystemParametersInfo(SPI_GETHIGHCONTRAST, HighContrast.cbSize, @HighContrast, 0) then
- Result := (HighContrast.dwFlags and HCF_HIGHCONTRASTON) <> 0;
- end;
- initialization
- var OSVersionInfo: TOSVersionInfo;
- OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
- GetVersionEx(OSVersionInfo);
- WindowsVersion := (Byte(OSVersionInfo.dwMajorVersion) shl 24) or (Byte(OSVersionInfo.dwMinorVersion) shl 16) or Word(OSVersionInfo.dwBuildNumber);
- PreferredMemoFont := 'Consolas';
- if not FontExists(PreferredMemoFont) then
- PreferredMemoFont := 'Courier New';
- end.
|