2
0
Эх сурвалжийг харах

Merge branch 'jensg-maxmemos12'

Martijn Laan 1 жил өмнө
parent
commit
5e5614b757

+ 144 - 4
Components/NewTabSet.pas

@@ -2,7 +2,7 @@ unit NewTabSet;
 
 {
   Inno Setup
-  Copyright (C) 1997-2020 Jordan Russell
+  Copyright (C) 1997-2024 Jordan Russell
   Portions by Martijn Laan
   For conditions of distribution and use, see LICENSE.TXT.
 
@@ -12,34 +12,52 @@ unit NewTabSet;
 interface
 
 uses
-  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ModernColors;
+  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Math, Generics.Collections,
+  ModernColors, UxTheme;
 
 type
   TTabPosition = (tpTop, tpBottom);
 
+  TBoolList = TList<Boolean>;
+
   TNewTabSet = class(TCustomControl)
   private
+    FCloseButtons: TBoolList;
     FHints: TStrings;
+    FMenuThemeData: HTHEME;
+    FOnCloseButtonClick: TNotifyEvent;
     FTabs: TStrings;
     FTabIndex: Integer;
     FTabPosition: TTabPosition;
+    FTabsOffset: Integer;
     FTheme: TTheme;
+    FThemeDark: Boolean;
     function GetTabRect(Index: Integer): TRect;
+    function GetCloseButtonRect(const TabRect: TRect): TRect;
     procedure InvalidateTab(Index: Integer);
+    procedure CloseButtonsListChanged(Sender: TObject; const Item: Boolean;
+      Action: TCollectionNotification);
     procedure TabsListChanged(Sender: TObject);
+    procedure HintsListChanged(Sender: TObject);
+    procedure SetCloseButtons(Value: TBoolList);
     procedure SetTabs(Value: TStrings);
     procedure SetTabIndex(Value: Integer);
     procedure SetTabPosition(Value: TTabPosition);
     procedure SetTheme(Value: TTheme);
     procedure SetHints(const Value: TStrings);
+    procedure UpdateThemeData(const Open: Boolean);
+    procedure EnsureCurrentTabIsFullyVisible;
   protected
     procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW;
+    procedure WMThemeChanged(var Message: TMessage); message WM_THEMECHANGED;
     procedure CreateParams(var Params: TCreateParams); override;
+    procedure CreateWnd; override;
     procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
     procedure Paint; override;
   public
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
+    property CloseButtons: TBoolList read FCloseButtons write SetCloseButtons;
     property Theme: TTheme read FTheme write SetTheme;
   published
     property Align;
@@ -49,13 +67,18 @@ type
     property TabIndex: Integer read FTabIndex write SetTabIndex;
     property Tabs: TStrings read FTabs write SetTabs;
     property TabPosition: TTabPosition read FTabPosition write SetTabPosition default tpBottom;
+    property PopupMenu;
     property OnClick;
+    property OnCloseButtonClick: TNotifyEvent read FOnCloseButtonClick write FOnCloseButtonClick;
   end;
 
 procedure Register;
 
 implementation
 
+uses
+  WinApi.UxTheme;
+
 procedure Register;
 begin
   RegisterComponents('JR', [TNewTabSet]);
@@ -135,14 +158,18 @@ const
   TabPaddingX = 5;
   TabPaddingY = 3;
   TabSpacing = 1;
+  CloseButtonSizeX = 12;
 
 constructor TNewTabSet.Create(AOwner: TComponent);
 begin
   inherited;
+  FCloseButtons := TBoolList.Create;
+  FCloseButtons.OnNotify := CloseButtonsListChanged;
   FTabs := TStringList.Create;
   TStringList(FTabs).OnChange := TabsListChanged;
   FTabPosition := tpBottom;
   FHints := TStringList.Create;
+  TStringList(FHints).OnChange := HintsListChanged;
   ControlStyle := ControlStyle + [csOpaque];
   Width := 129;
   Height := 21;
@@ -155,8 +182,15 @@ begin
     style := style and not (CS_HREDRAW or CS_VREDRAW);
 end;
 
+procedure TNewTabSet.CreateWnd;
+begin
+  inherited;
+  UpdateThemeData(True);
+end;
+
 destructor TNewTabSet.Destroy;
 begin
+  UpdateThemeData(False);
   FTabs.Free;
   inherited;
 end;
@@ -181,6 +215,13 @@ begin
   end;
 end;
 
+procedure TNewTabSet.WMThemeChanged(var Message: TMessage);
+begin
+  { Don't Run to Cursor into this function, it will interrupt up the theme change }
+  UpdateThemeData(True);
+  inherited;
+end;
+
 function TNewTabSet.GetTabRect(Index: Integer): TRect;
 var
   CR: TRect;
@@ -191,10 +232,12 @@ begin
   Canvas.Font.Assign(Font);
   if FTabPosition = tpBottom then
     Result.Top := 0;
-  Result.Right := 4;
+  Result.Right := 4 - FTabsOffset;
   for I := 0 to FTabs.Count-1 do begin
     Size := Canvas.TextExtent(FTabs[I]);
     SizeX := Size.cx + (TabPaddingX * 2) + TabSpacing;
+    if (I < FCloseButtons.Count) and FCloseButtons[I] then
+      Inc(SizeX, MulDiv(CloseButtonSizeX, CurrentPPI, 96));
     SizeY := Size.cy + (TabPaddingY * 2);
     if FTabPosition = tpTop then
       Result.Top := CR.Bottom - SizeY;
@@ -205,6 +248,12 @@ begin
   SetRectEmpty(Result);
 end;
 
+function TNewTabSet.GetCloseButtonRect(const TabRect: TRect): TRect;
+begin
+  Result := TRect.Create(TabRect.Right - MulDiv(CloseButtonSizeX, CurrentPPI, 96) - TabPaddingX div 2,
+    TabRect.Top, TabRect.Right - TabPaddingX div 2, TabRect.Bottom);
+end;
+
 procedure TNewTabSet.InvalidateTab(Index: Integer);
 var
   R: TRect;
@@ -218,11 +267,22 @@ begin
   end;
 end;
 
+procedure TNewTabSet.CloseButtonsListChanged(Sender: TObject; const Item: Boolean;
+  Action: TCollectionNotification);
+begin
+  Invalidate;
+end;
+
 procedure TNewTabSet.TabsListChanged(Sender: TObject);
 begin
   Invalidate;
 end;
 
+procedure TNewTabSet.HintsListChanged(Sender: TObject);
+begin
+  ShowHint := FHints.Count > 0;
+end;
+
 procedure TNewTabSet.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
   Y: Integer);
 var
@@ -233,6 +293,14 @@ begin
     for I := 0 to FTabs.Count-1 do begin
       R := GetTabRect(I);
       if (X >= R.Left) and (X < R.Right) then begin
+        if (I = TabIndex) and (I < FCloseButtons.Count) and FCloseButtons[I] then begin
+          var R2 := GetCloseButtonRect(R);
+          if PtInRect(R2, TPoint.Create(X, Y)) then begin
+            if Assigned(OnCloseButtonClick) then
+              OnCloseButtonClick(Self);
+            Break;
+          end;
+        end;
         TabIndex := I;
         Break;
       end;
@@ -264,6 +332,23 @@ var
         else
           Canvas.Font.Color := clBtnText;
         Canvas.TextOut(R.Left + TabPaddingX, R.Top + TabPaddingY, FTabs[I]);
+
+       if (I < FCloseButtons.Count) and FCloseButtons[I] then begin
+          var R2 := GetCloseButtonRect(R);
+          if FMenuThemeData <> 0 then begin
+            var Offset := MulDiv(1, CurrentPPI, 96);
+            Inc(R2.Left, Offset);
+            Inc(R2.Top, Offset);
+            DrawThemeBackground(FMenuThemeData, Canvas.Handle,  MENU_SYSTEMCLOSE, MSYSC_NORMAL, R2, nil);
+          end else begin
+            InflateRect(R2, -MulDiv(3, CurrentPPI, 96), -MulDiv(6, CurrentPPI, 96));
+            Canvas.Pen.Color := Canvas.Font.Color;
+            Canvas.MoveTo(R2.Left, R2.Top);
+            Canvas.LineTo(R2.Right, R2.Bottom);
+            Canvas.MoveTo(R2.Left, R2.Bottom-1);
+            Canvas.LineTo(R2.Right, R2.Top-1);
+          end;
+        end;
         ExcludeClipRect(Canvas.Handle, R.Left, R.Top, R.Right, R.Bottom);
         Break;
       end;
@@ -332,10 +417,16 @@ begin
   DrawTabs(False);
 end;
 
+procedure TNewTabSet.SetCloseButtons(Value: TBoolList);
+begin
+  FCloseButtons.Clear;
+  for var V in Value do
+    FCloseButtons.Add(V);
+end;
+
 procedure TNewTabSet.SetHints(const Value: TStrings);
 begin
   FHints.Assign(Value);
-  ShowHint := FHints.Count > 0;
 end;
 
 procedure TNewTabSet.SetTabIndex(Value: Integer);
@@ -344,6 +435,7 @@ begin
     InvalidateTab(FTabIndex);
     FTabIndex := Value;
     InvalidateTab(Value);
+    EnsureCurrentTabIsFullyVisible;
     Click;
   end;
 end;
@@ -367,8 +459,56 @@ procedure TNewTabSet.SetTheme(Value: TTheme);
 begin
   if FTheme <> Value then begin
     FTheme := Value;
+    var NewThemeDark := (FTheme <> nil) and FTheme.Dark;
+    if FThemeDark <> NewThemeDark then
+      UpdateThemeData(True);
+    FThemeDark := NewThemeDark;
     Invalidate;
   end;
 end;
 
+procedure TNewTabSet.UpdateThemeData(const Open: Boolean);
+begin
+  if FMenuThemeData <> 0 then begin
+    CloseThemeData(FMenuThemeData);
+    FMenuThemeData := 0;
+  end;
+
+  if Open and UseThemes then begin
+    if (FTheme <> nil) and FTheme.Dark then
+      FMenuThemeData := OpenThemeData(Handle, 'DarkMode::Menu');
+    if FMenuThemeData = 0 then
+      FMenuThemeData := OpenThemeData(Handle, 'Menu');
+end;
+end;
+
+procedure TNewTabSet.EnsureCurrentTabIsFullyVisible;
+var
+  rcTab, rcCtl, rcLast: TRect;
+  iExtra, iDelta, iNewOffset: Integer;
+begin
+  rcCtl := ClientRect;
+  rcTab := GetTabRect(FTabIndex);
+
+  { Check and modify tabs offset so everything fits }
+  iExtra := Min(rcCtl.Width div 2, rcTab.Width * 4);  { arbitrary value, adjust as needed }
+  iDelta := rcTab.Width div 2;                        { arbitrary value, adjust as needed }
+
+  { Left side is easy, limit is always 0 }
+  if rcTab.Left < rcCtl.Left + iDelta then begin
+    FTabsOffset := Max(0, FTabsOffset - rcCtl.Left - rcTab.Left - iExtra);
+    Invalidate;
+  end;
+
+  { Right side limit depends on last tab and total available space }
+  if rcTab.Right > rcCtl.Right - iDelta then begin
+    iNewOffset := FTabsOffset + (rcTab.Right - rcCtl.Right) + iExtra;
+    FTabsOffset := 0; { We need the last tabs leftmost position w/o any offset }
+    rcLast := GetTabRect(FTabs.Count-1);
+    FTabsOffset := Max(0, Min(iNewOffset, rcLast.Right - rcCtl.Width + 10));
+    Invalidate;
+  end;
+end;
+
+
 end.

+ 36 - 0
Projects/CompForm.dfm

@@ -159,6 +159,10 @@ object CompileForm: TCompileForm
         Text = 'Insert'
         Width = 64
       end
+      item
+        Bevel = pbNone
+        Width = 100
+      end
       item
         Bevel = pbNone
         Style = psOwnerDraw
@@ -173,6 +177,7 @@ object CompileForm: TCompileForm
         Bevel = pbNone
         Width = 50
       end>
+    OnClick = StatusBarClick
     OnDrawPanel = StatusBarDrawPanel
     OnResize = StatusBarResize
   end
@@ -329,10 +334,12 @@ object CompileForm: TCompileForm
     Width = 361
     Height = 21
     Align = alTop
+    OnCloseButtonClick = MemosTabSetOnCloseButtonClick
     TabIndex = 0
     Tabs.Strings = (
       'Main Script')
     TabPosition = tpTop
+    PopupMenu = MemosTabSetPopupMenu
     OnClick = MemosTabSetClick
   end
   object MainMenu1: TMainMenu
@@ -528,6 +535,18 @@ object CompileForm: TCompileForm
         ShortCut = 24585
         OnClick = VPreviousTabClick
       end
+      object VCloseTab: TMenuItem
+        Caption = 'Close Tab'
+        ShortCut = 16499
+        OnClick = VCloseTabClick
+      end
+      object VReopenTab: TMenuItem
+        Caption = 'Reopen Tab'
+      end
+      object VReopenTabs: TMenuItem
+        Caption = 'Reopen All Tabs'
+        OnClick = VReopenTabsClick
+      end
       object N20: TMenuItem
         Caption = '-'
       end
@@ -4307,4 +4326,21 @@ object CompileForm: TCompileForm
     Left = 224
     Top = 149
   end
+  object MemosTabSetPopupMenu: TPopupMenu
+    OnPopup = MemosTabSetPopup
+    Left = 48
+    Top = 51
+    object VCloseTab2: TMenuItem
+      Caption = 'Close Current Tab'
+      ShortCut = 16499
+      OnClick = VCloseTabClick
+    end
+    object VReopenTab2: TMenuItem
+      Caption = 'Reopen Tab'
+    end
+    object VReopenTabs2: TMenuItem
+      Caption = 'Reopen All Tabs'
+      OnClick = VReopenTabsClick
+    end
+  end
 end

+ 265 - 55
Projects/CompForm.pas

@@ -211,6 +211,13 @@ type
     PrintDialog: TPrintDialog;
     FSaveEncodingUTF8NoPreamble: TMenuItem;
     TFilesDesigner: TMenuItem;
+    VCloseTab: TMenuItem;
+    VReopenTab: TMenuItem;
+    VReopenTabs: TMenuItem;
+    MemosTabSetPopupMenu: TPopupMenu;
+    VCloseTab2: TMenuItem;
+    VReopenTab2: TMenuItem;
+    VReopenTabs2: TMenuItem;
     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
     procedure FExitClick(Sender: TObject);
     procedure FOpenMainFileClick(Sender: TObject);
@@ -311,12 +318,19 @@ type
     procedure FindResultsListDblClick(Sender: TObject);
     procedure FPrintClick(Sender: TObject);
     procedure TFilesDesignerClick(Sender: TObject);
+    procedure VCloseTabClick(Sender: TObject);
+    procedure VReopenTabClick(Sender: TObject);
+    procedure VReopenTabsClick(Sender: TObject);
+    procedure MemosTabSetPopup(Sender: TObject);
+    procedure MemosTabSetOnCloseButtonClick(Sender: TObject);
+    procedure StatusBarClick(Sender: TObject);
   private
     { Private declarations }
     FMemos: TList<TCompScintEdit>;                      { FMemos[0] is the main memo and FMemos[1] the preprocessor output memo - also see MemosTabSet comment above }
     FMainMemo: TCompScintFileEdit;                      { Doesn't change }
     FPreprocessorOutputMemo: TCompScintEdit;            { Doesn't change }
-    FFileMemos: TList<TCompScintFileEdit>;              { All memos except FPreprocessorOutputMemo }
+    FFileMemos: TList<TCompScintFileEdit>;              { All memos except FPreprocessorOutputMemo, including those without a tab }
+    FHiddenFiles: TStringList;                          { List of files which *do* use a memo but are hidden by the user and have no tab }
     FActiveMemo: TCompScintEdit;                        { Changes depending on user input }
     FErrorMemo, FStepMemo: TCompScintFileEdit;          { These change depending on user input }
     FMemosStyler: TInnoSetupStyler;                     { Single styler for all memos }
@@ -420,7 +434,7 @@ type
     function InitializeNonFileMemo(const Memo: TCompScintEdit; const PopupMenu: TPopupMenu): TCompScintEdit;
     procedure InitiateAutoComplete(const Key: AnsiChar);
     procedure InvalidateStatusPanel(const Index: Integer);
-    procedure LoadKnownIncludedFilesAndUpdateMemos(const AFilename: String);
+    procedure LoadKnownIncludedAndHiddenFilesAndUpdateMemos(const AFilename: String);
     procedure MemoChange(Sender: TObject; const Info: TScintEditChangeInfo);
     procedure MemoCharAdded(Sender: TObject; Ch: AnsiChar);
     procedure MainMemoDropFiles(Sender: TObject; X, Y: Integer; AFiles: TStrings);
@@ -434,9 +448,10 @@ type
     procedure MemoModifiedChange(Sender: TObject);
     function MemoToTabIndex(const AMemo: TCompScintEdit): Integer;
     procedure MemoUpdateUI(Sender: TObject);
+    procedure UpdateReopenTabMenu(const Menu: TMenuItem);
     procedure ModifyMRUMainFilesList(const AFilename: String; const AddNewItem: Boolean);
     procedure ModifyMRUParametersList(const AParameter: String; const AddNewItem: Boolean);
-    procedure MoveCaretAndActivateMemo(const AMemo: TCompScintEdit; const LineNumber: Integer; const AlwaysResetColumn: Boolean);
+    procedure MoveCaretAndActivateMemo(AMemo: TCompScintFileEdit; const LineNumber: Integer; const AlwaysResetColumn: Boolean);
     procedure NewMainFile;
     procedure NewMainFileUsingWizard;
     procedure OpenFile(AMemo: TCompScintFileEdit; AFilename: String; const MainMemoAddToRecentDocs: Boolean);
@@ -444,10 +459,11 @@ type
     procedure ParseDebugInfo(DebugInfo: Pointer);
     procedure ReadMRUMainFilesList;
     procedure ReadMRUParametersList;
+    procedure ReopenTabOrTabs(const HiddenFileIndex: Integer; const Activate: Boolean);
     procedure ResetAllMemosLineState;
     procedure StartProcess;
     function SaveFile(const AMemo: TCompScintFileEdit; const SaveAs: Boolean): Boolean;
-    procedure SaveKnownIncludedFiles(const AFilename: String);
+    procedure SaveKnownIncludedAndHiddenFiles(const AFilename: String);
     procedure SetErrorLine(const AMemo: TCompScintFileEdit; const ALine: Integer);
     procedure SetStatusPanelVisible(const AVisible: Boolean);
     procedure SetStepLine(const AMemo: TCompScintFileEdit; ALine: Integer);
@@ -465,6 +481,7 @@ type
     procedure UpdateCompileStatusPanels(const AProgress, AProgressMax: Cardinal;
       const ASecondsRemaining: Integer; const ABytesCompressedPerSecond: Cardinal);
     procedure UpdateEditModePanel;
+    procedure UpdateHiddenFilesPanel;
     procedure UpdatePreprocMemos;
     procedure UpdateLineMarkers(const AMemo: TCompScintFileEdit; const Line: Integer);
     procedure UpdateMemosTabSetVisibility;
@@ -475,7 +492,7 @@ type
     procedure UpdateSaveMenuItemAndButton;
     procedure UpdateTargetMenu;
     procedure UpdateTheme;
-    procedure UpdateThemeData(const Close, Open: Boolean);
+    procedure UpdateThemeData(const Open: Boolean);
     procedure UpdateStatusPanelHeight(H: Integer);
     procedure WMCopyData(var Message: TWMCopyData); message WM_COPYDATA;
     procedure WMDebuggerHello(var Message: TMessage); message WM_Debugger_Hello;
@@ -527,16 +544,17 @@ uses
 
 const
   { Memos }
-  MaxMemos = 12; { Includes the main and preprocessor output memo's }
+  MaxMemos = 22; { Includes the main and preprocessor output memos }
   FirstIncludedFilesMemoIndex = 1; { This is an index into FFileMemos }
 
   { Status bar panel indexes }
   spCaretPos = 0;
   spModified = 1;
   spEditMode = 2;
-  spCompileIcon = 3;
-  spCompileProgress = 4;
-  spExtraStatus = 5;
+  spHiddenFilesCount = 3;
+  spCompileIcon = 4;
+  spCompileProgress = 5;
+  spExtraStatus = 6;
 
   { Output tab set indexes }
   tiCompilerOutput = 0;
@@ -764,6 +782,7 @@ begin
   for Memo in FMemos do
     if Memo is TCompScintFileEdit then
       FFileMemos.Add(TCompScintFileEdit(Memo));
+  FHiddenFiles := TStringList.Create(dupError, True, True);
   FActiveMemo := FMainMemo;
   FActiveMemo.Visible := True;
   FErrorMemo := FMainMemo;
@@ -797,7 +816,7 @@ begin
 
   UpdateCaption;
 
-  UpdateThemeData(False, True);
+  UpdateThemeData(True);
 
   if CommandLineCompile then begin
     ReadSignTools(FSignTools);
@@ -849,7 +868,7 @@ destructor TCompileForm.Destroy;
   end;
 
 begin
-  UpdateThemeData(True, False);
+  UpdateThemeData(False);
 
   Application.OnActivate := nil;
   Application.OnIdle := nil;
@@ -870,6 +889,7 @@ begin
   FMRUParametersList.Free;
   FMRUMainFilesList.Free;
   FFileMemos.Free;
+  FHiddenFiles.Free;
   FMemos.Free;
 
   inherited;
@@ -1012,6 +1032,8 @@ begin
     FDebugTarget := dtSetup;
     UpdateTargetMenu;
   end;
+  FHiddenFiles.Clear;
+  UpdateHiddenFilesPanel;
   for Memo in FFileMemos do
     if Memo.Used then
       Memo.BreakPoints.Clear;
@@ -1028,7 +1050,7 @@ begin
   FMainMemo.ClearUndo;
 end;
 
-procedure TCompileForm.LoadKnownIncludedFilesAndUpdateMemos(const AFilename: String);
+procedure TCompileForm.LoadKnownIncludedAndHiddenFilesAndUpdateMemos(const AFilename: String);
 var
   Strings: TStringList;
   IncludedFile: TIncludedFile;
@@ -1041,7 +1063,7 @@ begin
     if AFilename <> '' then begin
       Strings := TStringList.Create;
       try
-        LoadKnownIncludedFiles(AFilename, Strings);
+        LoadKnownIncludedAndHiddenFiles(AFilename, Strings, FHiddenFiles);
         if Strings.Count > 0 then begin
           try
             for I := 0 to Strings.Count-1 do begin
@@ -1065,7 +1087,7 @@ begin
   end;
 end;
 
-procedure TCompileForm.SaveKnownIncludedFiles(const AFilename: String);
+procedure TCompileForm.SaveKnownIncludedAndHiddenFiles(const AFilename: String);
 var
   Strings: TStringList;
   IncludedFile: TIncludedFile;
@@ -1076,7 +1098,7 @@ begin
       try
         for IncludedFile in FIncludedFiles do
           Strings.Add(IncludedFile.Filename);
-        CompFunc.SaveKnownIncludedFiles(AFilename, Strings);
+        CompFunc.SaveKnownIncludedAndHiddenFiles(AFilename, Strings, FHiddenFiles);
       finally
         Strings.Free;
       end;
@@ -1184,7 +1206,8 @@ begin
     ModifyMRUMainFilesList(AFilename, True);
     if MainMemoAddToRecentDocs then
       AddFileToRecentDocs(AFilename);
-    LoadKnownIncludedFilesAndUpdateMemos(AFilename);
+    LoadKnownIncludedAndHiddenFilesAndUpdateMemos(AFilename);
+    UpdateHiddenFilesPanel;
   end;
 end;
 
@@ -1268,7 +1291,7 @@ begin
   Result := True;
   if AMemo = FMainMemo then begin
     ModifyMRUMainFilesList(AMemo.Filename, True);
-    SaveKnownIncludedFiles(AMemo.Filename);
+    SaveKnownIncludedAndHiddenFiles(AMemo.Filename);
   end;
 end;
 
@@ -1433,6 +1456,30 @@ function CompilerCallbackProc(Code: Integer; var Data: TCompilerCallbackData;
     end;
   end;
 
+  procedure CleanHiddenFiles(const IncludedFiles: TIncludedFiles; const HiddenFiles: TStringList);
+  var
+    HiddenFileIncluded: array of Boolean;
+  begin
+    if HiddenFiles.Count > 0 then begin
+      { Clean previously hidden files which are no longer included }
+      if IncludedFiles.Count > 0 then begin
+        SetLength(HiddenFileIncluded, HiddenFiles.Count);
+        for var I := 0 to HiddenFiles.Count-1 do
+          HiddenFileIncluded[I] := False;
+        for var I := 0 to IncludedFiles.Count-1 do begin
+          var IncludedFile := IncludedFiles[I];
+          var HiddenFileIndex := HiddenFiles.IndexOf(IncludedFile.Filename);
+          if HiddenFileIndex <> -1 then
+            HiddenFileIncluded[HiddenFileIndex] := True;
+        end;
+        for var I := HiddenFiles.Count-1 downto 0 do
+          if not HiddenFileIncluded[I] then
+            HiddenFiles.Delete(I);
+      end else
+        HiddenFiles.Clear;
+    end;
+  end;
+
 begin
   Result := iscrSuccess;
   with PAppData(AppData)^ do
@@ -1480,7 +1527,9 @@ begin
         begin
           Form.FPreprocessorOutput := TrimRight(Data.PreprocessedScript);
           DecodeIncludedFilenames(Data.IncludedFilenames, Form.FIncludedFiles); { Also stores last write time }
-          Form.SaveKnownIncludedFiles(Filename);
+          CleanHiddenFiles(Form.FIncludedFiles, Form.FHiddenFiles);
+          Form.UpdateHiddenFilesPanel;
+          Form.SaveKnownIncludedAndHiddenFiles(Filename);
         end;
       iscbNotifySuccess:
         begin
@@ -2247,6 +2296,11 @@ begin
   VStatusBar.Checked := StatusBar.Visible;
   VNextTab.Enabled := MemosTabSet.Visible and (MemosTabSet.Tabs.Count > 1);
   VPreviousTab.Enabled := VNextTab.Enabled;
+  VCloseTab.Enabled := MemosTabSet.Visible and (FActiveMemo <> FMainMemo) and (FActiveMemo <> FPreprocessorOutputMemo);
+  VReopenTab.Visible := MemosTabSet.Visible and (FHiddenFiles.Count > 0);
+  if VReopenTab.Visible then
+    UpdateReopenTabMenu(VReopenTab);
+  VReopenTabs.Visible := VReopenTab.Visible;
   VHide.Checked := not StatusPanel.Visible;
   VCompilerOutput.Checked := StatusPanel.Visible and (OutputTabSet.TabIndex = tiCompilerOutput);
   VDebugOutput.Checked := StatusPanel.Visible and (OutputTabSet.TabIndex = tiDebugOutput);
@@ -2274,6 +2328,59 @@ begin
   MemosTabSet.TabIndex := NewTabIndex;
 end;
 
+procedure TCompileForm.VCloseTabClick(Sender: TObject);
+begin
+  var Index := MemoToTabIndex(FActiveMemo);
+  MemosTabSet.Tabs.Delete(Index);
+  MemosTabSet.Hints.Delete(Index);
+  MemosTabSet.CloseButtons.Delete(Index);
+  FActiveMemo.Visible := False;
+  FHiddenFiles.Add((FActiveMemo as TCompScintFileEdit).Filename);
+  UpdateHiddenFilesPanel;
+  SaveKnownIncludedAndHiddenFiles(FMainMemo.Filename);
+
+  { Select next tab, except when we're already at the end }
+  VNextTabClick(Self);
+  VPreviousTabClick(Self);
+end;
+
+procedure TCompileForm.ReopenTabOrTabs(const HiddenFileIndex: Integer;
+  const Activate: Boolean);
+begin
+  var ReopenFilename: String;
+  if HiddenFileIndex >= 0 then begin
+    ReopenFilename := FHiddenFiles[HiddenFileIndex];
+    FHiddenFiles.Delete(HiddenFileIndex);
+  end else begin
+    ReopenFilename := FHiddenFiles[0];
+    FHiddenFiles.Clear;
+  end;
+  UpdateHiddenFilesPanel;
+
+  UpdatePreprocMemos;
+  SaveKnownIncludedAndHiddenFiles(FMainMemo.Filename);
+
+  { Activate the memo if requested }
+  if Activate then begin
+    for var Memo in FFileMemos do begin
+      if Memo.Used and (PathCompare(Memo.Filename, ReopenFilename) = 0) then begin
+        MemosTabSet.TabIndex := MemoToTabIndex(memo);
+        Break;
+      end;
+    end
+  end;
+end;
+
+procedure TCompileForm.VReopenTabClick(Sender: TObject);
+begin
+  ReopenTabOrTabs((Sender as TMenuItem).Tag, True);
+end;
+
+procedure TCompileForm.VReopenTabsClick(Sender: TObject);
+begin
+  ReopenTabOrTabs(-1, True);
+end;
+
 procedure TCompileForm.SyncZoom;
 var
   Memo: TCompScintEdit;
@@ -2620,17 +2727,53 @@ begin
     OpenFile(FMainMemo, CommandLineFilename, False);
 end;
 
+procedure TCompileForm.UpdateReopenTabMenu(const Menu: TMenuItem);
+begin
+  Menu.Clear;
+  for var I := 0 to FHiddenFiles.Count-1 do begin
+    var MenuItem := TMenuItem.Create(Menu);
+    MenuItem.Caption := ExtractFileName(FHiddenFiles[I]);
+    MenuItem.Tag := I;
+    MenuItem.OnClick := VReopenTabClick;
+    Menu.Add(MenuItem);
+  end;
+end;
+
+procedure TCompileForm.MemosTabSetPopup(Sender: TObject);
+begin
+  { Main and preprocessor memos can't be hidden }
+  VCloseTab2.Enabled := (FActiveMemo <> FMainMemo) and (FActiveMemo <> FPreprocessorOutputMemo);
+
+  VReopenTab2.Visible := FHiddenFiles.Count > 0;
+  if VReopenTab2.Visible then
+    UpdateReopenTabMenu(VReopenTab2);
+  VReopenTabs2.Visible := VReopenTab2.Visible;
+end;
+
 procedure TCompileForm.MemosTabSetClick(Sender: TObject);
 
   { Also see MemoToTabIndex }
-  function TabIndexToMemoIndex(const TabIndex, MaxTabIndex: Integer): Integer;
+  function TabIndexToMemoIndex(const ATabIndex, AMaxTabIndex: Integer): Integer;
   begin
-    if TabIndex = 0 then
+    if ATabIndex = 0 then
       Result := 0 { First tab displays the main memo which is FMemos[0] }
-    else if FPreprocessorOutputMemo.Used and (TabIndex = MaxTabIndex) then
+    else if FPreprocessorOutputMemo.Used and (ATabIndex = AMaxTabIndex) then
       Result := 1 { Last tab displays the preprocessor output memo which is FMemos[1] }
-    else
-      Result := TabIndex+1; { Other tabs display include files which start second tab but at FMemos[2] }
+    else begin
+      { Only count memos not explicitly hidden by the user }
+      var TabIndex := 0;
+      for var MemoIndex := FirstIncludedFilesMemoIndex to FFileMemos.Count-1 do begin
+        if FHiddenFiles.IndexOf(FFileMemos[MemoIndex].Filename) = -1 then begin
+          Inc(TabIndex);
+          if TabIndex = ATabIndex then begin
+            Result := MemoIndex + 1;   { Other tabs display include files which start at second tab but at FMemos[2] }
+            Exit;
+          end;
+        end;
+      end;
+
+      raise Exception.Create('TabIndexToMemoIndex failed');
+    end;
   end;
 
 var
@@ -2655,6 +2798,11 @@ begin
   UpdateModifiedPanel;
 end;
 
+procedure TCompileForm.MemosTabSetOnCloseButtonClick(Sender: TObject);
+begin
+  VCloseTabClick(Self);
+end;
+
 procedure TCompileForm.InitializeFindText(Dlg: TFindDialog);
 var
   S: String;
@@ -3025,6 +3173,7 @@ begin
     
     UpdateCaption;
     UpdatePreprocMemos;
+    UpdateHiddenFilesPanel;
     for Memo in FMemos do begin
       { Move caret to start of line to ensure it doesn't end up in the middle
         of a double-byte character if the code page changes from SBCS to DBCS }
@@ -3080,15 +3229,40 @@ begin
     if not FPreprocessorOutputMemo.Used then
       raise Exception.Create('not FPreprocessorOutputMemo.Used');
     Result := MemosTabSet.Tabs.Count-1 { Last tab displays the preprocessor output memo }
-  end else
-    Result := FFileMemos.IndexOf(AMemo as TCompScintFileEdit) { Other tabs display include files which start second tab }
+  end else begin
+    Result := FFileMemos.IndexOf(AMemo as TCompScintFileEdit); { Other tabs display include files which start second tab }
+
+   { Filter memos explicitly hidden by the user }
+    for var MemoIndex := Result-1 downto 0 do
+      if FHiddenFiles.IndexOf(FFileMemos[MemoIndex].Filename) <> -1 then
+        Dec(Result);
+  end;
 end;
 
-procedure TCompileForm.MoveCaretAndActivateMemo(const AMemo: TCompScintEdit; const LineNumber: Integer;
+procedure TCompileForm.MoveCaretAndActivateMemo(AMemo: TCompScintFileEdit; const LineNumber: Integer;
   const AlwaysResetColumn: Boolean);
 var
   Pos: Integer;
 begin
+  { Reopen tab if needed }
+  var HiddenFileIndex := FHiddenFiles.IndexOf(AMemo.Filename);
+  if HiddenFileIndex <> -1 then begin
+    var SaveFileName := AMemo.Filename;
+    ReopenTabOrTabs(HiddenFileIndex, False);
+    { The above call to ReopenTabOrTabs will currently lead to a call to UpdateIncludedFilesMemos which
+      sets up all the memos. Currently it will keep same memo for the reopened file but in case it no
+      longer does at some point: look it up again }
+    AMemo := nil;
+    for var Memo in FFileMemos do begin
+      if Memo.Used and (PathCompare(Memo.Filename, SaveFilename) = 0) then begin
+        AMemo := Memo;
+        Break;
+      end;
+    end;
+    if AMemo = nil then
+      raise Exception.Create('AMemo MIA');
+  end;
+
   { Move caret }
   if AlwaysResetColumn or (AMemo.CaretLine <> LineNumber) then
     Pos := AMemo.GetPositionFromLine(LineNumber)
@@ -3103,7 +3277,7 @@ begin
   AMemo.CaretPosition := Pos;
 
   { Activate memo }
-  MemosTabSet.TabIndex := MemoToTabIndex(AMemo);
+  MemosTabSet.TabIndex := MemoToTabIndex(AMemo); { This causes MemosTabSetClick to show the memo }
 end;
 
 procedure TCompileForm.SetErrorLine(const AMemo: TCompScintFileEdit; const ALine: Integer);
@@ -3169,6 +3343,14 @@ begin
     StatusBar.Panels[spEditMode].Text := InsertText[FActiveMemo.InsertMode];
 end;
 
+procedure TCompileForm.UpdateHiddenFilesPanel;
+begin
+  if MemosTabSet.Visible and (FHiddenFiles.Count > 0) then begin
+    StatusBar.Panels[spHiddenFilesCount].Text := Format('Tabs closed: %d', [FHiddenFiles.Count]);
+  end else
+    StatusBar.Panels[spHiddenFilesCount].Text := '';
+end;
+
 procedure TCompileForm.UpdateMemosTabSetVisibility;
 begin
   MemosTabSet.Visible := FPreprocessorOutputMemo.Used or FFileMemos[FirstIncludedFilesMemoIndex].Used;
@@ -3186,12 +3368,14 @@ end;
 
 procedure TCompileForm.UpdatePreprocMemos;
 
-  procedure UpdatePreprocessorOutputMemo(const NewTabs, NewHints: TStringList);
+  procedure UpdatePreprocessorOutputMemo(const NewTabs, NewHints: TStringList;
+    const NewCloseButtons: TBoolList);
   begin
     if FOptions.ShowPreprocessorOutput and (FPreprocessorOutput <> '') and
        not SameStr(TrimRight(FMainMemo.Lines.Text), FPreprocessorOutput) then begin
       NewTabs.Add('Preprocessor Output');
       NewHints.Add('');
+      NewCloseButtons.Add(False);
       FPreprocessorOutputMemo.ReadOnly := False;
       try
         FPreprocessorOutputMemo.Lines.Text := FPreprocessorOutput;
@@ -3206,13 +3390,15 @@ procedure TCompileForm.UpdatePreprocMemos;
     end;
   end;
 
-  procedure UpdateIncludedFilesMemos(const NewTabs, NewHints: TStringList);
+  procedure UpdateIncludedFilesMemos(const NewTabs, NewHints: TStringList;
+    const NewCloseButtons: TBoolList);
   var
     IncludedFile: TIncludedFile;
-    I, NextMemoIndex, NewTabIndex: Integer;
+    I: Integer;
   begin
     if FOptions.OpenIncludedFiles and (FIncludedFiles.Count > 0) then begin
-      NextMemoIndex := FirstIncludedFilesMemoIndex;
+      var NextMemoIndex := FirstIncludedFilesMemoIndex;
+      var NextTabIndex := 1; { First tab displays the main memo  }
       FLoadingIncludedFiles := True;
       try
         for IncludedFile in FIncludedFiles do begin
@@ -3228,12 +3414,17 @@ procedure TCompileForm.UpdatePreprocMemos;
               OpenFile(IncludedFile.Memo, IncludedFile.Filename, False); { Also updates FileLastWriteTime }
               IncludedFile.Memo.Used := True;
             end else if IncludedFile.Memo.CompilerFileIndex = UnknownCompilerFileIndex then begin
-             { Previously the included file came from the history }
+             { The file already has a memo but CompilerFileIndex is not set yet.
+               This happens if the initial load was from the history loaded by LoadKnownIncludedFiles and then the user does a compile.  }
               IncludedFile.Memo.CompilerFileIndex := IncludedFile.CompilerFileIndex;
             end;
-            NewTabIndex := 1+NextMemoIndex-FirstIncludedFilesMemoIndex;
-            NewTabs.Insert(NewTabIndex, PathExtractName(IncludedFile.Filename));
-            NewHints.Insert(NewTabIndex, GetFileTitle(IncludedFile.Filename));
+
+            if FHiddenFiles.IndexOf(IncludedFile.Filename) = -1 then begin
+              NewTabs.Insert(NextTabIndex, PathExtractName(IncludedFile.Filename));
+              NewHints.Insert(NextTabIndex, GetFileTitle(IncludedFile.Filename));
+              NewCloseButtons.Insert(NextTabIndex, True);
+              Inc(NextTabIndex);
+            end;
 
             Inc(NextMemoIndex);
             if NextMemoIndex = FFileMemos.Count then
@@ -3267,25 +3458,30 @@ procedure TCompileForm.UpdatePreprocMemos;
 
 var
   NewTabs, NewHints: TStringList;
+  NewCloseButtons: TBoolList;
   I, SaveTabIndex: Integer;
   SaveTabName: String;
 begin
   NewTabs := nil;
   NewHints := nil;
+  NewCloseButtons := nil;
   try
     NewTabs := TStringList.Create;
     NewTabs.Add(MemosTabSet.Tabs[0]); { 'Main Script' }
     NewHints := TStringList.Create;
     NewHints.Add(GetFileTitle(FMainMemo.Filename));
+    NewCloseButtons := TBoolList.Create;
+    NewCloseButtons.Add(False);
 
-    UpdatePreprocessorOutputMemo(NewTabs, NewHints);
-    UpdateIncludedFilesMemos(NewTabs, NewHints);
+    UpdatePreprocessorOutputMemo(NewTabs, NewHints, NewCloseButtons);
+    UpdateIncludedFilesMemos(NewTabs, NewHints, NewCloseButtons);
 
     { Set new tabs, try keep same file open }
     SaveTabIndex := MemosTabSet.TabIndex;
     SaveTabName := MemosTabSet.Tabs[MemosTabSet.TabIndex];
     MemosTabSet.Tabs := NewTabs;
     MemosTabSet.Hints := NewHints;
+    MemosTabSet.CloseButtons := NewCloseButtons;
     I := MemosTabSet.Tabs.IndexOf(SaveTabName);
     if I <> -1 then
        MemosTabSet.TabIndex := I;
@@ -3295,6 +3491,7 @@ begin
       MemosTabSetClick(MemosTabSet);
    end;
   finally
+    NewCloseButtons.Free;
     NewHints.Free;
     NewTabs.Free;
   end;
@@ -4375,26 +4572,21 @@ begin
   SetListTheme(FindResultsList);
 end;
 
-procedure TCompileForm.UpdateThemeData(const Close, Open: Boolean);
+procedure TCompileForm.UpdateThemeData(const Open: Boolean);
 begin
-  if Close then begin
-    if FProgressThemeData <> 0 then begin
-      CloseThemeData(FProgressThemeData);
-      FProgressThemeData := 0;
-    end;
+  if FProgressThemeData <> 0 then begin
+    CloseThemeData(FProgressThemeData);
+    FProgressThemeData := 0;
   end;
 
-  if Open then begin
-    if UseThemes then begin
-      FProgressThemeData := OpenThemeData(Handle, 'Progress');
-      if (GetThemeInt(FProgressThemeData, 0, 0, TMT_PROGRESSCHUNKSIZE, FProgressChunkSize) <> S_OK) or
-         (FProgressChunkSize <= 0) then
-        FProgressChunkSize := 6;
-      if (GetThemeInt(FProgressThemeData, 0, 0, TMT_PROGRESSSPACESIZE, FProgressSpaceSize) <> S_OK) or
-         (FProgressSpaceSize < 0) then  { ...since "OpusOS" theme returns a bogus -1 value }
-        FProgressSpaceSize := 2;
-    end else
-      FProgressThemeData := 0;
+  if Open and UseThemes then begin
+    FProgressThemeData := OpenThemeData(Handle, 'Progress');
+    if (GetThemeInt(FProgressThemeData, 0, 0, TMT_PROGRESSCHUNKSIZE, FProgressChunkSize) <> S_OK) or
+       (FProgressChunkSize <= 0) then
+      FProgressChunkSize := 6;
+    if (GetThemeInt(FProgressThemeData, 0, 0, TMT_PROGRESSSPACESIZE, FProgressSpaceSize) <> S_OK) or
+       (FProgressSpaceSize < 0) then  { ...since "OpusOS" theme returns a bogus -1 value }
+      FProgressSpaceSize := 2;
   end;
 end;
 
@@ -4764,6 +4956,24 @@ begin
   end;
 end;
 
+procedure TCompileForm.StatusBarClick(Sender: TObject);
+begin
+  if MemosTabSet.Visible and (FHiddenFiles.Count > 0) then begin
+    var Point := SmallPointToPoint(TSmallPoint(DWORD(GetMessagePos)));
+    var X := StatusBar.ScreenToClient(Point).X;
+    var W := 0;
+    for var I := 0 to StatusBar.Panels.Count-1 do begin
+      Inc(W, StatusBar.Panels[I].Width);
+      if X < W then begin
+        if I = spHiddenFilesCount then
+          MemosTabSetPopupMenu.Popup(Point.X, Point.Y);
+        Break;
+      end else if I = spHiddenFilesCount then
+        Break;
+    end;
+  end;
+end;
+
 procedure TCompileForm.StatusBarDrawPanel(StatusBar: TStatusBar;
   Panel: TStatusPanel; const Rect: TRect);
 var
@@ -4861,7 +5071,7 @@ end;
 procedure TCompileForm.WMThemeChanged(var Message: TMessage);
 begin
   { Don't Run to Cursor into this function, it will interrupt up the theme change }
-  UpdateThemeData(True, True);
+  UpdateThemeData(True);
   inherited;
 end;
 

+ 21 - 16
Projects/CompFunc.pas

@@ -41,8 +41,8 @@ procedure OpenMailingListSite;
 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 LoadKnownIncludedFiles(const AFilename: String; const IncludedFiles: TStringList);
-procedure SaveKnownIncludedFiles(const AFilename: String; const IncludedFiles: TStringList);
+procedure LoadKnownIncludedAndHiddenFiles(const AFilename: String; const IncludedFiles, HiddenFiles: TStringList);
+procedure SaveKnownIncludedAndHiddenFiles(const AFilename: String; const IncludedFiles, HiddenFiles: TStringList);
 procedure DeleteKnownIncludedFiles(const AFilename: String);
 procedure SetFakeShortCutText(const MenuItem: TMenuItem; const S: String);
 procedure SetFakeShortCut(const MenuItem: TMenuItem; const Key: Word;
@@ -239,26 +239,26 @@ begin
   end;
 end;
 
-procedure LoadKnownIncludedFiles(const AFilename: String; const IncludedFiles: TStringList);
-var
-  Ini: TConfigIniFile;
-  OldDelimiter: Char;
+procedure LoadKnownIncludedAndHiddenFiles(const AFilename: String; const IncludedFiles, HiddenFiles: TStringList);
 begin
-  OldDelimiter := IncludedFiles.Delimiter;
-  Ini := TConfigIniFile.Create;
+  var OldIncludedFilesDelimiter := IncludedFiles.Delimiter;
+  var OldHiddenFilesDelimiter := HiddenFiles.Delimiter;
+  var Ini := TConfigIniFile.Create;
   try
     IncludedFiles.Delimiter := '*';
     IncludedFiles.DelimitedText := Ini.ReadString('IncludedFilesHistory', AFilename, '');
+
+    HiddenFiles.Delimiter := '*';
+    HiddenFiles.DelimitedText := Ini.ReadString('HiddenFilesHistory', AFilename, '');
+
   finally
     Ini.Free;
-    IncludedFiles.Delimiter := OldDelimiter;
+    IncludedFiles.Delimiter := OldIncludedFilesDelimiter;
+    HiddenFiles.Delimiter := OldHiddenFilesDelimiter;
   end;
 end;
 
-procedure SaveKnownIncludedFiles(const AFilename: String; const IncludedFiles: TStringList);
-var
-  Ini: TConfigIniFile;
-  OldDelimiter: Char;
+procedure SaveKnownIncludedAndHiddenFiles(const AFilename: String; const IncludedFiles, HiddenFiles: TStringList);
 begin
   if IncludedFiles.Count = 0 then begin
     DeleteKnownIncludedFiles(AFilename);
@@ -268,14 +268,18 @@ begin
   if AFilename = '' then
     raise Exception.Create('AFilename must be set');
 
-  OldDelimiter := IncludedFiles.Delimiter;
-  Ini := TConfigIniFile.Create;
+  var OldIncludedFilesDelimiter := IncludedFiles.Delimiter;
+  var OldHiddenFilesDelimiter := HiddenFiles.Delimiter;
+  var Ini := TConfigIniFile.Create;
   try
     IncludedFiles.Delimiter := '*';
     Ini.WriteString('IncludedFilesHistory', AFilename, IncludedFiles.DelimitedText);
+    HiddenFiles.Delimiter := '*';
+    Ini.WriteString('HiddenFilesHistory', AFilename, HiddenFiles.DelimitedText);
   finally
     Ini.Free;
-    IncludedFiles.Delimiter := OldDelimiter;
+    IncludedFiles.Delimiter := OldIncludedFilesDelimiter;
+    HiddenFiles.Delimiter := OldHiddenFilesDelimiter;
   end;
 end;
 
@@ -289,6 +293,7 @@ begin
   Ini := TConfigIniFile.Create;
   try
     Ini.DeleteKey('IncludedFilesHistory', AFilename);
+    Ini.DeleteKey('HiddenFilesHistory', AFilename);
   finally
     Ini.Free;
   end;

+ 1 - 1
Projects/CompScintEdit.pas

@@ -42,7 +42,7 @@ type
   TCompScintEdit = class(TScintEdit)
   private
     FTheme: TTheme;
-    FUsed: Boolean;
+    FUsed: Boolean; { The IDE only shows 1 memo at a time so can't use .Visible to check if a memo is used }
   protected
     procedure CreateWnd; override;
   public

+ 2 - 0
Projects/Debug.iss

@@ -2,6 +2,8 @@
 ; Opened when you run the Compil32.dproj project in Debug mode from the Delphi IDE
 ; Use it to test the compiler or Setup or the uninstaller
 
+#include "Debug2.iss"
+
 [Setup]
 AppName=ɯɐɹƃoɹd ʎɯ
 AppVerName=My Program version 1.5

+ 8 - 0
Projects/Debug2.iss

@@ -0,0 +1,8 @@
+; -- Debug2.iss --
+; Included by Debug.iss
+; Use it to test the compiler or Compiler IDE's support for include files
+
+[Code]
+procedure InitializeWizard;
+begin
+end;

+ 14 - 76
whatsnew.htm

@@ -29,105 +29,43 @@ For conditions of distribution and use, see <a href="https://jrsoftware.org/file
 <p><b>Want to be notified by e-mail of new Inno Setup releases?</b> <a href="https://jrsoftware.org/ismail.php">Subscribe</a> to the Inno Setup Mailing List!</p>
 
 <p><a name="6.3.0"></a><span class="ver">6.3.0-dev </span><span class="date">(?)</span></p>
+<p><span class="head2">Support from UTF-8 encoded files improved</span></p>
 <ul>
   <li>Inno Setup now supports UTF-8 encoded .iss script files and .isl messages files without an UTF-8 preamble (also called BOM).</li>
   <li>Compiler IDE changes:
   <ul>
   <li>Added new <i>UTF-8 without BOM</i> menu item to the <i>Save Encoding</i> submenu of the <i>File</i> menu.</li>
   <li>New script files are now saved as UTF-8 with BOM by default. Existing files are still saved as they were.</li>
-  <li>Added new <i>[Files] Entries Designer</i> menu item to the <i>Tools</i> menu to design and insert extra entries to the [Files] section.</li>
   </ul>
   </li>
-  <li>Added new [Setup] section directive <tt>UninstallLogging</tt>, which defaults to <tt>no</tt>. If set to <tt>yes</tt>, the uninstaller will always create a log file if it is launched from the <i>Add/Remove Programs</i> Control Panel applet. Equivalent to passing /LOG on the command line.</li>
-  <li>During startup Setup would always ask Windows to create any missing <tt>{usercf}</tt>, <tt>{userpf}</tt>, and <tt>{usersavedgames}</tt> folders. It no longer does until the script asks for the folder. Note that scripts running in administrative install mode should not do this because it violates the <a href="https://jrsoftware.org/ishelp/index.php?topic=setup_useduserareaswarning">used user areas warning</a>.</li>
-  <li>Added support for IIS group users identifiers (<tt>iisiusrs</tt>) for use in <tt>Permissions</tt> parameters, contributed by Achim Stuy.</li> 
   <li>Pascal Scripting changes:
   <ul>
     <li>Support function <tt>LoadStringsFromFile</tt> now also supports UTF8-encoded files without an UTF-8 preamble.</li>
     <li>Added new <tt>SaveStringsToUTF8FileNoPreamble</tt> support function.</li>
-    <li>Type <tt>TShellFolderID</tt> was removed because it wasn't used by any support function.</li>
   </ul>
   </li>
-  <li>Added official Korean translation.</li>
-  <li>Inno Setup is now built using Delphi 11.3 Alexandria instead of Delphi 10.3 Rio.</li>
-</ul>
-
-<p><a name="6.2.2"></a><span class="ver">6.2.2 </span><span class="date">(2023-02-15)</span></p>
-<ul>
-  <li>Changes to further help protect against potential DLL preloading attacks, contributed by Johannes Schindelin from the Git for Windows team.</li>
-  <li>Pascal Scripting changes: Improved support for downloads using basic authentication, contributed by Christian Beck.
-  <ul>
-    <li>Added new <tt>AddEx</tt> function to the <tt>TDownloadWizardPage</tt> support class.</li>
-    <li>Added new <tt>SetDownloadCredentials</tt> support function.</li>
-  </ul>
-  <li>Added official Hungarian translation.</li>
-</ul>
-
-<p><a name="6.2.1"></a><span class="ver">6.2.1 </span><span class="date">(2022-04-14)</span></p>
-<ul>
-  <li>Changes to further help protect against potential DLL preloading attacks when running installers or uninstallers under the SYSTEM account, contributed by Johannes Schindelin from the Git for Windows team.</li>
-  <li>Fixed a cosmetic issue if the icon file specified by the [Setup] section directive <tt>SetupIconFile</tt> contains more than 13 icons. Thanks to Wilenty and Martin Prikryl for the initial investigation.</li>
-</ul>
-
-<p><a name="6.2.0"></a><span class="ver">6.2.0 </span><span class="date">(2021-06-03)</span></p>
-<p><span class="head2">Graphics modernized</span></p>
-<ul>
-  <li>Updated all Compiler IDE's toolbar icons and the wizard images used by the Compiler IDE's New Script Wizard wizard.</li>
-  <li>Updated the default application icon used by Setup and Uninstall if [Setup] section directive <tt>SetupIconFile</tt> is not set. To use the old icon again set <tt>SetupIconFile</tt> to <tt>compiler:SetupClassicIcon.ico</tt>.</li>
-  <li>[Setup] section directives <tt>WizardImageFile</tt> and <tt>WizardSmallImageFile</tt> now default to a blank value which makes Setup use new built-in wizard images.  To use the old wizard images again set <tt>WizardImageFile</tt> and <tt>WizardSmallImageFile</tt> to <tt>compiler:WizClassicImage.bmp</tt> and <tt>compiler:WizClassicSmallImage.bmp</tt> respectively.</li>
-  <li>Updated Uninstall's default small wizard image if [Setup] section directive <tt>SetupIconFile</tt> is not set. Before it would use Setup's default application icon in this case.</li>
-  <li>Updated the folder, group, and stop icons used by Setup's <i>Select Destination Location</i>, <i>Select Start Menu Folder</i>, and <i>Preparing to Install</i> wizard pages.</li>
-  <li>Updated the disk icon used by Setup's <i>Setup Needs the Next Disk</i> form.</li>
-  <li>Pascal Scripting change: Added new <tt>InitializeBitmapImageFromIcon</tt> support function.</li>
-</ul>
-<p>All these icon and images updates include the automatic use of higher quality versions (which were not available before) on higher DPI settings. This includes new automatic use of higher quality icons for the icon on Setup's <i>Select Setup Language</i> form and Uninstall's small wizard image if <tt>SetupIconFile</tt> is set.</p>
-<p>Example screenshots:</p>
-<ul>
-  <li>Setup wizard pages <a href="https://jrsoftware.org/images/is-welcome-org.png">Select Destination Location</a> and <a href="https://jrsoftware.org/images/is-finished-org.png">Setup Completed</a> at 100% DPI.</li>
-  <li>Setup wizard pages <a href="https://jrsoftware.org/images/is-welcome-org-175.png">Select Destination Location</a> and <a href="https://jrsoftware.org/images/is-finished-org-175.png">Setup Completed</a> at 175% DPI.</li>
-  <li>Compiler IDE themes <a href="https://jrsoftware.org/images/is-ide-org.png">Light</a> and <a href="https://jrsoftware.org/images/is-ide-dark-org.png">Dark</a> at 100% DPI.</li>
-</ul>
-<p>Comparison screenshots of the *previous* version:</p>
-<ul>
-  <li>Setup wizard pages <a href="https://jrsoftware.org/images/is-welcome-org-175-old.png">Select Destination Location</a> and <a href="https://jrsoftware.org/images/is-finished-org-175-old.png">Setup Completed</a> at 175% DPI in version 6.1.2.</li>
 </ul>
 <p><span class="head2">Other changes</span></p>
 <ul>
-  <li>Links displayed by [Setup] section directives <tt>LicenseFile</tt>, <tt>InfoBeforeFile</tt> and <tt>InfoAfterFile</tt> are now executed as the original user if possible when clicked.</li>
-  <li>Added new [Setup] section directives <tt>MissingMessagesWarning</tt> and <tt>NotRecognizedMessagesWarning</tt> to disable warnings about messages missing or not recognized for a language.</li>
-  <li>/LOG: Now logs more uninstaller actions.</li>
-  <li>The <tt>{localappdata}</tt> constant can now correctly trigger a <a href="https://jrsoftware.org/ishelp/index.php?topic=setup_useduserareaswarning">used user areas warning</a>.</li>
-  <li>Compiler IDE change: <i>Fix:</i> Autocomplete support for event functions listed some procedures as functions.</li>
-  <li>Pascal Scripting changes:
+  <li>Compiler IDE changes:
   <ul>
-    <li>Added new <tt>CreateOutputMarqueeProgressPage</tt> support function to show marquee progress to the user. See the <i><a href="https://jrsoftware.github.io/issrc/Examples/AllPagesExample.iss">AllPagesExample.iss</a></i> example script for an example.</li>
-    <li>Added new <tt>ItemFontStyle</tt> and <tt>SubItemFontStyle</tt> properties to the <tt>TNewCheckListBox</tt> support class. See the <i><a href="https://jrsoftware.github.io/issrc/Examples/CodeClasses.iss">CodeClasses.iss</a></i> example script for an example.</li>
-    <li>Added new <tt>IsMsiProductInstalled</tt> and <tt>StrToVersion</tt> support functions.</li>
-    <li>Added new <tt>AbortedByUser</tt> property to the <tt>TDownloadWizardPage</tt> support class.</li>
-    <li><i>Fix:</i> <tt>CreateDownloadPage</tt>'s progress bar now supports files larger than 2 GB.</li>
-    <li>Support functions <tt>ParamCount</tt> and <tt>ParamStr</tt> now exclude undocumented internal parameters used by Setup and Uninstall.</li>
-    <li>The built-in download support now allows the download of files for which the server does not specify the file size and its hash checking is no longer case sensitive.</li>
+  <li>The Compiler IDE now opens up to 20 #include files in tabs, instead of up to 10.</li>
+  <li>Tabs for #include files can now be closed: Added new <i>Close Tab (Ctrl+F4)</i>, <i>Reopen Tab</i>, and  <i>Reopen All Tabs</i> menu items to the <i>View</i> menu, also available by right clicking the tab bar. Additionally the tab for a selected #include file now shows a close button.</li>
+  <li>Added a clickable panel to the Status Bar showing the amount of closed tabs if there are any.</li>
+  <li>Added new <i>[Files] Entries Designer</i> menu item to the <i>Tools</i> menu to design and insert extra entries to the [Files] section.</li>
   </ul>
   </li>
-  <li>ISPP change: Added new <tt>StrToVersion</tt> support function.</li>
-  <li>Added official Bulgarian translation.</li>
-  <li>Various documentation improvements.</li>
-  <li>Minor tweaks.</li>
-</ul>
-<p><span class="head2">Inno Setup FAQ updated</span></p>
-<ul>
-  <li>The <a href="https://jrsoftware.org/isfaq.php">Inno Setup FAQ</a> has been updated with updated versions of articles taken from the Inno Setup Knowledge Base which is now hidden from the website.</li>
-  <li>The content of the FAQ is now <a href="https://github.com/jrsoftware/isfaq/blob/main/isfaq.html">available on GitHub</a> where you can suggest new entries or other improvements using the Edit button.</li>
-</ul>
-<p><span class="head2">QuickStart Pack removed</span></p>
-<ul>
-  <li>The QuickStart Pack installer has been removed because of a lack of added value.</li>
-  <li>The standard Inno Setup installer now offers to download encryption support if it's missing, like the QuickStart Pack installer did before. If you used the QuickStart Pack installer before, you can use the standard installer to update your installation.</li>
+  <li>Added new [Setup] section directive <tt>UninstallLogging</tt>, which defaults to <tt>no</tt>. If set to <tt>yes</tt>, the uninstaller will always create a log file if it is launched from the <i>Add/Remove Programs</i> Control Panel applet. Equivalent to passing /LOG on the command line.</li>
+  <li>During startup Setup would always ask Windows to create any missing <tt>{usercf}</tt>, <tt>{userpf}</tt>, and <tt>{usersavedgames}</tt> folders. It no longer does until the script asks for the folder. Note that scripts running in administrative install mode should not do this because it violates the <a href="https://jrsoftware.org/ishelp/index.php?topic=setup_useduserareaswarning">used user areas warning</a>.</li>
+  <li>Added support for IIS group users identifiers (<tt>iisiusrs</tt>) for use in <tt>Permissions</tt> parameters.</li> 
+  <li>Pascal Scripting change: Type <tt>TShellFolderID</tt> was removed because it wasn't used by any support function.</li>
+  <li>Added official Korean translation.</li>
+  <li>Inno Setup is now built using Delphi 11.3 Alexandria instead of Delphi 10.3 Rio.</li>
 </ul>
 
-<p>Contributions via <a href="https://github.com/jrsoftware/issrc" target="_blank">GitHub</a>: <b>Thanks to Sergii Leonov and Dom Gries for their contributions.</b></p>
+<p>Contributions via <a href="https://github.com/jrsoftware/issrc" target="_blank">GitHub</a>: Thanks to Achim Stuy, ser163, and Jens Geyer for their contributions.</p>
 
-<p><a href="https://jrsoftware.org/files/is6.1-whatsnew.htm">Inno Setup 6.1 Revision History</a></p >
+<p><a href="https://jrsoftware.org/files/is6.2-whatsnew.htm">Inno Setup 6.2 Revision History</a></p >
 
 </body>
 </html>