Implement terminal-per-tab, history scrolling, and copy/paste functionality

This commit introduces several highly-requested improvements to the integrated terminal:

- Terminal-per-tab synchronization mode: Users can now link a terminal instance to a specific tab, tracking the directory for that tab only.
- Terminal History & Scrolling: Added a 100-line history buffer that allows users to scroll through past terminal output using the scrollbar or mouse wheel.
- Text Selection: Enabled selecting text in the terminal using the left mouse button.
- Copy/Paste Functionality: Implemented copy/paste shortcuts. Copy via Ctrl+Shift+C / Ctrl+Insert. Paste via Ctrl+Shift+V / Shift+Insert, or by using Middle/Right mouse click.
- Fixed 'Follow tail' behavior so the terminal reliably auto-scrolls to the bottom upon receiving new output.
- Fixed overlapping UI elements in the layout configuration dialog.
This commit is contained in:
Peter P. Lupo 2026-05-22 16:38:24 -04:00
commit ef80fa3dda
6 changed files with 634 additions and 136 deletions

View file

@ -25,7 +25,7 @@ interface
uses
LCLType, Classes, Controls, StdCtrls, ExtCtrls, Forms, Messages, Graphics,
VTEmuEsc, LCLIntf, Types, LazUtf8, LMessages;
VTEmuEsc, LCLIntf, Types, LazUtf8, LMessages, Clipbrd;
type
@ -75,12 +75,18 @@ type
strict private
FRows: Integer;
FColumns: Integer;
FHistory: array[0..99] of Pointer;
FHistoryHead: Integer;
FHistoryCount: Integer;
public
constructor Create(AOwner: TCustomComTerminal);
destructor Destroy; override;
procedure Init(ARows, AColumns: Integer);
procedure SetChar(Column, Row: Integer; TermChar: TComTermChar);
function GetChar(Column, Row: Integer): TComTermChar;
function GetHistoryChar(Column, HistRow: Integer): TComTermChar;
procedure PushHistoryLine(LineData: PByte);
function GetHistoryCount: Integer;
procedure SetTab(Column: Integer; Put: Boolean);
function GetTab(Column: Integer): Boolean;
function NextTab(Column: Integer): Integer;
@ -158,6 +164,9 @@ type
FTopLeft: TPoint;
FCaretHeight: Integer;
FSaveAttr: TTermAttributes;
FSelecting: Boolean;
FSelStart: TPoint;
FSelEnd: TPoint;
FBuffer: TComTermBuffer;
FMainBuffer: TComTermBuffer;
FAlternateBuffer: TComTermBuffer;
@ -192,6 +201,7 @@ type
procedure SetMode(AParams: TStrings; OnOff: Boolean);
procedure ShowCaret;
procedure StringReceived(Str: string);
function IsSelected(AColumn, ARow: Integer): Boolean;
procedure PaintTerminal(Rect: TRect);
procedure PaintDesign;
procedure PutChar(Ch: TUTF8Char);
@ -225,6 +235,7 @@ type
procedure KeyPress(var Key: Char); override;
procedure UTF8KeyPress(var UTF8Key: TUTF8Char); override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure CreateWnd; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
@ -242,6 +253,7 @@ type
procedure MoveCaret(AColumn, ARow: Integer);
procedure Write(const Buffer:string; Size: Integer);
procedure WriteStr(const Str: string);
procedure CopyToClipboard;
procedure WriteEscCode(ACode: TEscapeCode; AParams: TStrings);
procedure LoadFromStream(Stream: TStream);
procedure SaveToStream(Stream: TStream);
@ -355,16 +367,25 @@ const
// create class
constructor TComTermBuffer.Create(AOwner: TCustomComTerminal);
var
I: Integer;
begin
inherited Create;
FOwner := AOwner;
FTopLeft := Classes.Point(1, 1);
FCaretPos := Classes.Point(1, 1);
for I := 0 to 99 do FHistory[I] := nil;
FHistoryCount := 0;
FHistoryHead := 0;
end;
// destroy class
destructor TComTermBuffer.Destroy;
var
I: Integer;
begin
for I := 0 to 99 do
if FHistory[I] <> nil then FreeMem(FHistory[I]);
if FBuffer <> nil then
begin
FreeMem(FBuffer);
@ -397,9 +418,49 @@ begin
Result:= PComTermChar(FBuffer + (Address * SizeOf(TComTermChar)))^;
end;
function TComTermBuffer.GetHistoryCount: Integer;
begin
Result := FHistoryCount;
end;
function TComTermBuffer.GetHistoryChar(Column, HistRow: Integer): TComTermChar;
var
Index: Integer;
LineData: PComTermChar;
begin
if (Column > FColumns) or (HistRow < 1) or (HistRow > FHistoryCount) then
Exit(Default(TComTermChar));
Index := (FHistoryHead - HistRow + 100) mod 100;
LineData := PComTermChar(FHistory[Index]);
if LineData <> nil then
Result := LineData[Column - 1]
else
Result := Default(TComTermChar);
end;
procedure TComTermBuffer.PushHistoryLine(LineData: PByte);
var
LineSize: Integer;
begin
LineSize := FColumns * SizeOf(TComTermChar);
if FHistory[FHistoryHead] = nil then
GetMem(FHistory[FHistoryHead], LineSize);
Move(LineData^, FHistory[FHistoryHead]^, LineSize);
FHistoryHead := (FHistoryHead + 1) mod 100;
if FHistoryCount < 100 then
Inc(FHistoryCount);
end;
// scroll down up line
procedure TComTermBuffer.ScrollDown;
var
LineData: PByte;
begin
if FScrollRange.Top = 1 then
begin
LineData := FBuffer;
PushHistoryLine(LineData);
end;
DeleteLine(FScrollRange.Top, 1);
end;
@ -864,6 +925,8 @@ begin
I+= L;
end;
finally
UpdateScrollRange;
UpdateScrollPos;
ShowCaret;
end;
end;
@ -1098,6 +1161,28 @@ var
begin
inherited KeyDown(Key, Shift);
// Copy: Ctrl+Shift+C or Ctrl+Insert
if ((Key = VK_C) and (ssCtrl in Shift) and (ssShift in Shift)) or
((Key = VK_INSERT) and (ssCtrl in Shift)) then
begin
CopyToClipboard;
Key := 0;
Exit;
end;
// Paste: Ctrl+Shift+V or Shift+Insert
if ((Key = VK_V) and (ssCtrl in Shift) and (ssShift in Shift)) or
((Key = VK_INSERT) and (ssShift in Shift)) then
begin
if Clipboard.HasFormat(CF_TEXT) then
begin
if (FPtyDevice <> nil) and (FPtyDevice.Connected) then
FPtyDevice.WriteStr(Clipboard.AsText);
end;
Key := 0;
Exit;
end;
if (Key in [VK_TAB, VK_ESCAPE]) then
begin
SendChar(Chr(Key));
@ -1187,13 +1272,49 @@ procedure TCustomComTerminal.MouseDown(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
inherited MouseDown(Button, Shift, X, Y);
if (not (FTermMode.MouseMode and FTermMode.MouseTrack)) and (Button = mbLeft) then
begin
FSelecting := True;
FSelStart.X := X div FFontWidth + FTopLeft.X;
FSelStart.Y := Y div FFontHeight + FTopLeft.Y;
FSelEnd := FSelStart;
Invalidate;
end;
MouseEvent(ecMouseDown, Button, Shift, X, Y);
end;
procedure TCustomComTerminal.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
inherited MouseMove(Shift, X, Y);
if FSelecting then
begin
FSelEnd.X := X div FFontWidth + FTopLeft.X;
FSelEnd.Y := Y div FFontHeight + FTopLeft.Y;
if FSelEnd.X < 1 then FSelEnd.X := 1;
if FSelEnd.X > FColumns then FSelEnd.X := FColumns;
Invalidate;
end;
end;
procedure TCustomComTerminal.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseUp(Button, Shift, X, Y);
if FSelecting and (Button = mbLeft) then
begin
FSelecting := False;
FSelEnd.X := X div FFontWidth + FTopLeft.X;
FSelEnd.Y := Y div FFontHeight + FTopLeft.Y;
Invalidate;
end
else if (not FSelecting) and (Button in [mbMiddle, mbRight]) then
begin
if Clipboard.HasFormat(CF_TEXT) then
begin
if (FPtyDevice <> nil) and (FPtyDevice.Connected) then
FPtyDevice.WriteStr(Clipboard.AsText);
end;
end;
MouseEvent(ecMouseUp, Button, Shift, X, Y);
end;
@ -1231,7 +1352,15 @@ begin
for I := Rect.Left to Rect.Right do
begin
X := I + FTopLeft.X - 1;
Ch := FBuffer.GetChar(X, Y);
if Y < 1 then
Ch := FBuffer.GetHistoryChar(X, 1 - Y)
else
Ch := FBuffer.GetChar(X, Y);
if IsSelected(X, Y) then
begin
Ch.BackColor := clHighlight;
Ch.FrontColor := clHighlightText;
end;
if Ch.Ch <> Chr(0) then
DrawChar(I, J, Ch);
end;
@ -1327,6 +1456,73 @@ begin
end;
end;
function TCustomComTerminal.IsSelected(AColumn, ARow: Integer): Boolean;
var
SY1, SY2, SX1, SX2: Integer;
begin
if not FSelecting and (FSelStart.Y = 0) and (FSelEnd.Y = 0) then Exit(False);
if (FSelStart.Y < FSelEnd.Y) or ((FSelStart.Y = FSelEnd.Y) and (FSelStart.X <= FSelEnd.X)) then
begin
SY1 := FSelStart.Y; SX1 := FSelStart.X;
SY2 := FSelEnd.Y; SX2 := FSelEnd.X;
end else
begin
SY1 := FSelEnd.Y; SX1 := FSelEnd.X;
SY2 := FSelStart.Y; SX2 := FSelStart.X;
end;
if (ARow > SY1) and (ARow < SY2) then
Result := True
else if SY1 = SY2 then
Result := (ARow = SY1) and (AColumn >= SX1) and (AColumn <= SX2)
else if ARow = SY1 then
Result := AColumn >= SX1
else if ARow = SY2 then
Result := AColumn <= SX2
else
Result := False;
end;
procedure TCustomComTerminal.CopyToClipboard;
var
SY1, SY2, SX1, SX2: Integer;
R, C: Integer;
S: String;
Ch: TComTermChar;
begin
if (FSelStart.X = 0) and (FSelStart.Y = 0) and (FSelEnd.X = 0) and (FSelEnd.Y = 0) then Exit;
if (FSelStart.Y < FSelEnd.Y) or ((FSelStart.Y = FSelEnd.Y) and (FSelStart.X <= FSelEnd.X)) then
begin
SY1 := FSelStart.Y; SX1 := FSelStart.X;
SY2 := FSelEnd.Y; SX2 := FSelEnd.X;
end else
begin
SY1 := FSelEnd.Y; SX1 := FSelEnd.X;
SY2 := FSelStart.Y; SX2 := FSelStart.X;
end;
S := '';
for R := SY1 to SY2 do
begin
for C := 1 to FColumns do
begin
if IsSelected(C, R) then
begin
if R < 1 then
Ch := FBuffer.GetHistoryChar(C, 1 - R)
else
Ch := FBuffer.GetChar(C, R);
if Ch.Ch = #0 then S := S + ' '
else S := S + Ch.Ch;
end;
end;
if R < SY2 then S := S + LineEnding;
end;
Clipboard.AsText := S;
end;
// move caret after new char is put on screen
procedure TCustomComTerminal.AdvanceCaret(Kind: TAdvanceCaret);
var
@ -1365,10 +1561,10 @@ begin
end;
if FAutoFollow then
begin
if (FCaretPos.Y - FTopLeft.Y) > FVisibleRows then
if (FCaretPos.Y - FTopLeft.Y) >= FVisibleRows then
begin
I:= FCaretPos.Y - FVisibleRows + 1;
ModifyScrollBar(SB_Vert, SB_THUMBPOSITION, I);
I := FCaretPos.Y - FVisibleRows + 1;
ModifyScrollBar(SB_VERT, SB_THUMBPOSITION, I - 1 + FBuffer.GetHistoryCount);
end;
end;
end;
@ -1553,7 +1749,7 @@ begin
Dy := 0;
end else
begin
FTopLeft.Y := APos + 1;
FTopLeft.Y := APos + 1 - FBuffer.GetHistoryCount;
Dx := 0;
Dy := (OldPos - APos) * FFontHeight;
end;
@ -1572,7 +1768,7 @@ begin
end;
if FScrollBars in [ssBoth, ssVertical] then
begin
SetScrollPos(Handle, SB_VERT, FTopLeft.Y - 1, True);
SetScrollPos(Handle, SB_VERT, FTopLeft.Y - 1 + FBuffer.GetHistoryCount, True);
end;
end;
@ -1635,11 +1831,11 @@ var
if OldScrollBars in [ssBoth, ssVertical] then
begin
ARows := AHeight div FFontHeight;
if ARows >= FBuffer.Rows then
if (ARows >= FBuffer.Rows) and (FBuffer.GetHistoryCount = 0) then
SetRange(SB_VERT, 1) // screen is high enough, hide scroll bar
else
begin
Max := FBuffer.Rows - (ARows - 1);
Max := FBuffer.Rows + FBuffer.GetHistoryCount - (ARows - 1);
SetRange(SB_VERT, Max);
end;
end;

View file

@ -938,6 +938,8 @@ type
procedure ReLoadTabs(ANoteBook: TFileViewNotebook);
procedure ShowOptionsLayout(Data: PtrInt);
procedure ToggleFullscreenConsole;
procedure GetOrCreateTabTerminal(Page: TFileViewPage; AParent: TWinControl);
procedure SwitchTabTerminal(Notebook: TFileViewNotebook);
{en
This function is called from various points to handle dropping files
@ -2524,28 +2526,37 @@ begin
if gTermWindow then
begin
if gTermWindowSplit then
if gTermWindowMode >= twmPerPanel then
begin
if Assigned(ConsLeft) then
if gTermWindowMode = twmPerPanel then
begin
if not ConsLeft.Connected then
if Assigned(ConsLeft) then
begin
if Assigned(nbLeft.ActivePage) and Assigned(nbLeft.ActivePage.FileView) then
ConsLeft.InitialDir := nbLeft.ActivePage.FileView.CurrentPath;
ConsLeft.Connected := True;
FLeftTermNeedInit := True;
if not ConsLeft.Connected then
begin
if Assigned(nbLeft.ActivePage) and Assigned(nbLeft.ActivePage.FileView) then
ConsLeft.InitialDir := nbLeft.ActivePage.FileView.CurrentPath;
ConsLeft.Connected := True;
FLeftTermNeedInit := True;
end;
end;
end;
if Assigned(ConsRight) then
end
else SwitchTabTerminal(nbLeft);
if gTermWindowMode = twmPerPanel then
begin
if not ConsRight.Connected then
if Assigned(ConsRight) then
begin
if Assigned(nbRight.ActivePage) and Assigned(nbRight.ActivePage.FileView) then
ConsRight.InitialDir := nbRight.ActivePage.FileView.CurrentPath;
ConsRight.Connected := True;
FRightTermNeedInit := True;
if not ConsRight.Connected then
begin
if Assigned(nbRight.ActivePage) and Assigned(nbRight.ActivePage.FileView) then
ConsRight.InitialDir := nbRight.ActivePage.FileView.CurrentPath;
ConsRight.Connected := True;
FRightTermNeedInit := True;
end;
end;
end;
end
else SwitchTabTerminal(nbRight);
end
else
begin
@ -2729,6 +2740,12 @@ begin
Page := Notebook.ActivePage;
if Assigned(Page) then
begin
if gTermWindowMode = twmPerTab then
begin
SwitchTabTerminal(Notebook);
UpdateTermSyncButtons(Notebook.Side = fpLeft);
end;
if Page.LockState = tlsPathResets then // if locked with directory change
ChooseFileSource(Page.FileView, Page.LockPath);
@ -4936,16 +4953,32 @@ begin
if (fspDirectAccess in FileView.FileSource.GetProperties) then
begin
if gTermWindow and gTermWindowSplit then
if gTermWindow and (gTermWindowMode >= twmPerPanel) then
begin
if (TFileViewPage(FileView.NotebookPage).Notebook = nbLeft) and Assigned(ConsLeft) then
if (TFileViewPage(FileView.NotebookPage).Notebook = nbLeft) then
begin
if gTermSyncModeLeft = 1 then
if gTermWindowMode = twmPerTab then
begin
if (gTermSyncModeLeft = 1) and
Assigned(TFileViewPage(FileView.NotebookPage).PtyDevice) and
(not TFileViewPage(FileView.NotebookPage).TermNeedInit) then
TFileViewPage(FileView.NotebookPage).PtyDevice.SetCurrentDir(FileView.CurrentPath);
end
else if (gTermSyncModeLeft = 1) and TFileViewPage(FileView.NotebookPage).IsActive and
Assigned(ConsLeft) and (not FLeftTermNeedInit) then
ConsLeft.SetCurrentDir(FileView.CurrentPath);
end
else if (TFileViewPage(FileView.NotebookPage).Notebook = nbRight) and Assigned(ConsRight) then
else if (TFileViewPage(FileView.NotebookPage).Notebook = nbRight) then
begin
if gTermSyncModeRight = 1 then
if gTermWindowMode = twmPerTab then
begin
if (gTermSyncModeRight = 1) and
Assigned(TFileViewPage(FileView.NotebookPage).PtyDevice) and
(not TFileViewPage(FileView.NotebookPage).TermNeedInit) then
TFileViewPage(FileView.NotebookPage).PtyDevice.SetCurrentDir(FileView.CurrentPath);
end
else if (gTermSyncModeRight = 1) and TFileViewPage(FileView.NotebookPage).IsActive and
Assigned(ConsRight) and (not FRightTermNeedInit) then
ConsRight.SetCurrentDir(FileView.CurrentPath);
end;
end;
@ -4965,6 +4998,38 @@ begin
UpdateFreeSpace(Page.Notebook.Side, False);
end;
UpdateFileView;
// Sync terminal on tab change when in Panel→Terminal mode
if gTermWindow and (gTermWindowMode >= twmPerPanel) and
(fspDirectAccess in FileView.FileSource.GetProperties) then
begin
if (FileView.NotebookPage is TFileViewPage) and
(TFileViewPage(FileView.NotebookPage).Notebook = nbLeft) then
begin
if gTermWindowMode = twmPerTab then
begin
if (gTermSyncModeLeft = 1) and
Assigned(TFileViewPage(FileView.NotebookPage).PtyDevice) and
(not TFileViewPage(FileView.NotebookPage).TermNeedInit) then
TFileViewPage(FileView.NotebookPage).PtyDevice.SetCurrentDir(FileView.CurrentPath);
end
else if (gTermSyncModeLeft = 1) and Assigned(ConsLeft) and (not FLeftTermNeedInit) then
ConsLeft.SetCurrentDir(FileView.CurrentPath);
end
else if (FileView.NotebookPage is TFileViewPage) and
(TFileViewPage(FileView.NotebookPage).Notebook = nbRight) then
begin
if gTermWindowMode = twmPerTab then
begin
if (gTermSyncModeRight = 1) and
Assigned(TFileViewPage(FileView.NotebookPage).PtyDevice) and
(not TFileViewPage(FileView.NotebookPage).TermNeedInit) then
TFileViewPage(FileView.NotebookPage).PtyDevice.SetCurrentDir(FileView.CurrentPath);
end
else if (gTermSyncModeRight = 1) and Assigned(ConsRight) and (not FRightTermNeedInit) then
ConsRight.SetCurrentDir(FileView.CurrentPath);
end;
end;
end;
procedure TfrmMain.FileViewFilesChanged(FileView: TFileView);
@ -4993,16 +5058,30 @@ begin
FileView.SetFocus;
if (fspDirectAccess in FileView.FileSource.GetProperties) then
begin
if gTermWindow and gTermWindowSplit then
if gTermWindow and (gTermWindowMode >= twmPerPanel) then
begin
if (TFileViewPage(FileView.NotebookPage).Notebook = nbLeft) and Assigned(ConsLeft) then
if (TFileViewPage(FileView.NotebookPage).Notebook = nbLeft) then
begin
if gTermSyncModeLeft = 1 then
if gTermWindowMode = twmPerTab then
begin
if (gTermSyncModeLeft = 1) and
Assigned(TFileViewPage(FileView.NotebookPage).PtyDevice) and
(not TFileViewPage(FileView.NotebookPage).TermNeedInit) then
TFileViewPage(FileView.NotebookPage).PtyDevice.SetCurrentDir(FileView.CurrentPath);
end
else if (gTermSyncModeLeft = 1) and Assigned(ConsLeft) and (not FLeftTermNeedInit) then
ConsLeft.SetCurrentDir(FileView.CurrentPath);
end
else if (TFileViewPage(FileView.NotebookPage).Notebook = nbRight) and Assigned(ConsRight) then
else if (TFileViewPage(FileView.NotebookPage).Notebook = nbRight) then
begin
if gTermSyncModeRight = 1 then
if gTermWindowMode = twmPerTab then
begin
if (gTermSyncModeRight = 1) and
Assigned(TFileViewPage(FileView.NotebookPage).PtyDevice) and
(not TFileViewPage(FileView.NotebookPage).TermNeedInit) then
TFileViewPage(FileView.NotebookPage).PtyDevice.SetCurrentDir(FileView.CurrentPath);
end
else if (gTermSyncModeRight = 1) and Assigned(ConsRight) and (not FRightTermNeedInit) then
ConsRight.SetCurrentDir(FileView.CurrentPath);
end;
end
@ -5549,10 +5628,12 @@ begin
end;
procedure TfrmMain.TerminalSyncTimerTimer(Sender: TObject);
var
Target: AnsiString;
const
Ticks: Integer = 0;
var
Target: String;
LeftPty, RightPty: TCustomPtyDevice;
Page: TFileViewPage;
begin
// Process any pending TThread.Synchronize calls (e.g. from PTY reader threads)
CheckSynchronize;
@ -5561,28 +5642,70 @@ begin
begin
ConsLeft.WriteStr(#13);
FLeftTermNeedInit := False;
if (gTermSyncModeLeft = 1) and Assigned(nbLeft.ActivePage) and Assigned(nbLeft.ActivePage.FileView) then
ConsLeft.SetCurrentDir(nbLeft.ActivePage.FileView.CurrentPath);
end;
if FRightTermNeedInit and Assigned(ConsRight) and ConsRight.Connected then
begin
ConsRight.WriteStr(#13);
FRightTermNeedInit := False;
if (gTermSyncModeRight = 1) and Assigned(nbRight.ActivePage) and Assigned(nbRight.ActivePage.FileView) then
ConsRight.SetCurrentDir(nbRight.ActivePage.FileView.CurrentPath);
end;
if FTermNeedInit and Assigned(Cons) and Cons.Connected then
begin
Cons.WriteStr(#13);
FTermNeedInit := False;
if (gTermSyncModeLeft = 1) and Assigned(ActiveFrame) then
Cons.SetCurrentDir(ActiveFrame.CurrentPath);
end;
if not gTermWindowSplit then Exit;
if gTermWindowMode = twmPerTab then
begin
if Assigned(nbLeft.ActivePage) then
begin
Page := TFileViewPage(nbLeft.ActivePage);
if Page.TermNeedInit and Assigned(Page.PtyDevice) and Page.PtyDevice.Connected then
begin
Page.PtyDevice.WriteStr(#13);
Page.TermNeedInit := False;
if (gTermSyncModeLeft = 1) and Assigned(Page.FileView) then
Page.PtyDevice.SetCurrentDir(Page.FileView.CurrentPath);
end;
end;
if Assigned(nbRight.ActivePage) then
begin
Page := TFileViewPage(nbRight.ActivePage);
if Page.TermNeedInit and Assigned(Page.PtyDevice) and Page.PtyDevice.Connected then
begin
Page.PtyDevice.WriteStr(#13);
Page.TermNeedInit := False;
if (gTermSyncModeRight = 1) and Assigned(Page.FileView) then
Page.PtyDevice.SetCurrentDir(Page.FileView.CurrentPath);
end;
end;
end;
if gTermWindowMode < twmPerPanel then Exit;
Inc(Ticks);
if Ticks mod 5 <> 0 then Exit;
Ticks := 0;
if Assigned(ConsLeft) and (gTermSyncModeLeft = 2) then
if gTermWindowMode = twmPerTab then
begin
if Assigned(nbLeft.ActivePage) then LeftPty := TFileViewPage(nbLeft.ActivePage).PtyDevice else LeftPty := nil;
if Assigned(nbRight.ActivePage) then RightPty := TFileViewPage(nbRight.ActivePage).PtyDevice else RightPty := nil;
end
else
begin
LeftPty := ConsLeft;
RightPty := ConsRight;
end;
if Assigned(LeftPty) and (gTermSyncModeLeft = 2) then
begin
{$IF DEFINED(UNIX) and not DEFINED(DARWIN)}
Target := fpReadLink('/proc/' + IntToStr(ConsLeft.ChildPid) + '/cwd');
Target := fpReadLink('/proc/' + IntToStr(LeftPty.ChildPid) + '/cwd');
if Target <> FLastTermCwdLeft then
begin
FLastTermCwdLeft := Target;
@ -5594,11 +5717,10 @@ begin
end;
{$ENDIF}
end;
if Assigned(ConsRight) and (gTermSyncModeRight = 2) then
if Assigned(RightPty) and (gTermSyncModeRight = 2) then
begin
{$IF DEFINED(UNIX) and not DEFINED(DARWIN)}
Target := fpReadLink('/proc/' + IntToStr(ConsRight.ChildPid) + '/cwd');
Target := fpReadLink('/proc/' + IntToStr(RightPty.ChildPid) + '/cwd');
if Target <> FLastTermCwdRight then
begin
FLastTermCwdRight := Target;
@ -5706,17 +5828,43 @@ begin
// Initialize/sync immediately when turned on
if IsLeft then
begin
if (gTermSyncModeLeft = 1) and Assigned(ConsLeft) then
ConsLeft.SetCurrentDir(nbLeft.ActivePage.FileView.CurrentPath)
else if (gTermSyncModeLeft = 2) and Assigned(ConsLeft) then
FLastTermCwdLeft := ''; // Force polling timer to sync CWD immediately
if gTermWindowMode = twmPerTab then
begin
if Assigned(nbLeft.ActivePage) then
begin
if (gTermSyncModeLeft = 1) and Assigned(TFileViewPage(nbLeft.ActivePage).PtyDevice) then
TFileViewPage(nbLeft.ActivePage).PtyDevice.SetCurrentDir(TFileViewPage(nbLeft.ActivePage).FileView.CurrentPath)
else if (gTermSyncModeLeft = 2) then
FLastTermCwdLeft := '';
end;
end
else
begin
if (gTermSyncModeLeft = 1) and Assigned(ConsLeft) then
ConsLeft.SetCurrentDir(nbLeft.ActivePage.FileView.CurrentPath)
else if (gTermSyncModeLeft = 2) then
FLastTermCwdLeft := '';
end;
end
else
begin
if (gTermSyncModeRight = 1) and Assigned(ConsRight) then
ConsRight.SetCurrentDir(nbRight.ActivePage.FileView.CurrentPath)
else if (gTermSyncModeRight = 2) and Assigned(ConsRight) then
FLastTermCwdRight := '';
if gTermWindowMode = twmPerTab then
begin
if Assigned(nbRight.ActivePage) then
begin
if (gTermSyncModeRight = 1) and Assigned(TFileViewPage(nbRight.ActivePage).PtyDevice) then
TFileViewPage(nbRight.ActivePage).PtyDevice.SetCurrentDir(TFileViewPage(nbRight.ActivePage).FileView.CurrentPath)
else if (gTermSyncModeRight = 2) then
FLastTermCwdRight := '';
end;
end
else
begin
if (gTermSyncModeRight = 1) and Assigned(ConsRight) then
ConsRight.SetCurrentDir(nbRight.ActivePage.FileView.CurrentPath)
else if (gTermSyncModeRight = 2) then
FLastTermCwdRight := '';
end;
end;
end
else
@ -5760,13 +5908,29 @@ begin
// Sync immediately: Panel to Terminal
if IsLeft then
begin
if Assigned(ConsLeft) then
ConsLeft.SetCurrentDir(nbLeft.ActivePage.FileView.CurrentPath);
if gTermWindowMode = twmPerTab then
begin
if Assigned(nbLeft.ActivePage) and Assigned(TFileViewPage(nbLeft.ActivePage).PtyDevice) then
TFileViewPage(nbLeft.ActivePage).PtyDevice.SetCurrentDir(nbLeft.ActivePage.FileView.CurrentPath);
end
else
begin
if Assigned(ConsLeft) then
ConsLeft.SetCurrentDir(nbLeft.ActivePage.FileView.CurrentPath);
end;
end
else
begin
if Assigned(ConsRight) then
ConsRight.SetCurrentDir(nbRight.ActivePage.FileView.CurrentPath);
if gTermWindowMode = twmPerTab then
begin
if Assigned(nbRight.ActivePage) and Assigned(TFileViewPage(nbRight.ActivePage).PtyDevice) then
TFileViewPage(nbRight.ActivePage).PtyDevice.SetCurrentDir(nbRight.ActivePage.FileView.CurrentPath);
end
else
begin
if Assigned(ConsRight) then
ConsRight.SetCurrentDir(nbRight.ActivePage.FileView.CurrentPath);
end;
end;
end;
end;
@ -5776,39 +5940,49 @@ end;
function TfrmMain.GetActivePtyDevice: TCustomPtyDevice;
begin
if gTermWindow and gTermWindowSplit then
if gTermWindow then
begin
if TFileViewPage(ActiveFrame.NotebookPage).Notebook = nbLeft then
Result := ConsLeft
if gTermWindowMode = twmPerTab then
Result := TFileViewPage(ActiveFrame.NotebookPage).PtyDevice
else if gTermWindowMode = twmPerPanel then
begin
if TFileViewPage(ActiveFrame.NotebookPage).Notebook = nbLeft then
Result := ConsLeft
else
Result := ConsRight;
end
else
Result := ConsRight;
Result := Cons;
end
else if gTermWindow then
Result := Cons
else
Result := nil;
end;
function TfrmMain.GetActiveConsole: TVirtualTerminal;
begin
if gTermWindow and gTermWindowSplit then
if gTermWindow then
begin
if TFileViewPage(ActiveFrame.NotebookPage).Notebook = nbLeft then
Result := cmdConsoleLeft
if gTermWindowMode = twmPerTab then
Result := TFileViewPage(ActiveFrame.NotebookPage).Terminal
else if gTermWindowMode = twmPerPanel then
begin
if TFileViewPage(ActiveFrame.NotebookPage).Notebook = nbLeft then
Result := cmdConsoleLeft
else
Result := cmdConsoleRight;
end
else
Result := cmdConsoleRight;
Result := cmdConsole;
end
else if gTermWindow then
Result := cmdConsole
else
Result := nil;
end;
procedure TfrmMain.ToggleConsole;
begin
if gTermWindow and gTermWindowSplit then
if gTermWindow and (gTermWindowMode >= twmPerPanel) then
begin
// Split terminals enabled
// Split terminals enabled (per panel or per tab)
if not Assigned(pnlTermContainerLeft) then
begin
pnlTermContainerLeft := TPanel.Create(Self);
@ -5831,7 +6005,7 @@ begin
btnTermLinkLeft := TSpeedButton.Create(Self);
btnTermLinkLeft.Parent := tlbTermLeft;
btnTermLinkLeft.Align := alLeft;
btnTermLinkLeft.Align := alRight;
btnTermLinkLeft.Width := 36;
btnTermLinkLeft.ShowHint := True;
btnTermLinkLeft.GroupIndex := 10;
@ -5841,7 +6015,7 @@ begin
btnTermDirLeft := TSpeedButton.Create(Self);
btnTermDirLeft.Parent := tlbTermLeft;
btnTermDirLeft.Align := alLeft;
btnTermDirLeft.Align := alRight;
btnTermDirLeft.Width := 36;
btnTermDirLeft.ShowHint := True;
btnTermDirLeft.GroupIndex := 11;
@ -5852,31 +6026,11 @@ begin
// Initialize the button states and captions/hints based on the loaded sync mode
UpdateTermSyncButtons(True);
cmdConsoleLeft := TVirtualTerminal.Create(pnlTermContainerLeft);
cmdConsoleLeft.Width := 400;
cmdConsoleLeft.Height := 176;
cmdConsoleLeft.Parent := pnlTermContainerLeft;
cmdConsoleLeft.Align := alClient;
cmdConsoleLeft.ShowHint := False;
// Adjust Z-order: splitter needs to be above the container.
// So container must be backmost (processed first), then splitter.
splitTermLeft.SendToBack;
pnlTermContainerLeft.SendToBack;
end;
FontOptionsToFont(gFonts[dcfConsole], cmdConsoleLeft.Font);
if not Assigned(ConsLeft) then
begin
ConsLeft := TPtyDevice.Create(Self);
cmdConsoleLeft.PtyDevice := ConsLeft;
if FInitializedView then
begin
if Assigned(nbLeft.ActivePage) and Assigned(nbLeft.ActivePage.FileView) then
ConsLeft.InitialDir := nbLeft.ActivePage.FileView.CurrentPath;
ConsLeft.Connected := True;
FLeftTermNeedInit := True;
end;
end;
if not Assigned(pnlTermContainerRight) then
begin
@ -5900,7 +6054,7 @@ begin
btnTermLinkRight := TSpeedButton.Create(Self);
btnTermLinkRight.Parent := tlbTermRight;
btnTermLinkRight.Align := alLeft;
btnTermLinkRight.Align := alRight;
btnTermLinkRight.Width := 36;
btnTermLinkRight.ShowHint := True;
btnTermLinkRight.GroupIndex := 20;
@ -5910,7 +6064,7 @@ begin
btnTermDirRight := TSpeedButton.Create(Self);
btnTermDirRight.Parent := tlbTermRight;
btnTermDirRight.Align := alLeft;
btnTermDirRight.Align := alRight;
btnTermDirRight.Width := 36;
btnTermDirRight.ShowHint := True;
btnTermDirRight.GroupIndex := 21;
@ -5921,30 +6075,73 @@ begin
// Initialize the button states and captions/hints based on the loaded sync mode
UpdateTermSyncButtons(False);
cmdConsoleRight := TVirtualTerminal.Create(pnlTermContainerRight);
cmdConsoleRight.Width := 400;
cmdConsoleRight.Height := 176;
cmdConsoleRight.Parent := pnlTermContainerRight;
cmdConsoleRight.Align := alClient;
cmdConsoleRight.ShowHint := False;
// Adjust Z-order: splitter needs to be above the container.
// So container must be backmost (processed first), then splitter.
splitTermRight.SendToBack;
pnlTermContainerRight.SendToBack;
end;
FontOptionsToFont(gFonts[dcfConsole], cmdConsoleRight.Font);
if not Assigned(ConsRight) then
if gTermWindowMode = twmPerPanel then
begin
ConsRight := TPtyDevice.Create(Self);
cmdConsoleRight.PtyDevice := ConsRight;
if FInitializedView then
// Create left terminal
if not Assigned(cmdConsoleLeft) then
begin
if Assigned(nbRight.ActivePage) and Assigned(nbRight.ActivePage.FileView) then
ConsRight.InitialDir := nbRight.ActivePage.FileView.CurrentPath;
ConsRight.Connected := True;
FRightTermNeedInit := True;
cmdConsoleLeft := TVirtualTerminal.Create(pnlTermContainerLeft);
cmdConsoleLeft.Width := 400;
cmdConsoleLeft.Height := 176;
cmdConsoleLeft.Parent := pnlTermContainerLeft;
cmdConsoleLeft.Align := alClient;
cmdConsoleLeft.ShowHint := False;
end;
FontOptionsToFont(gFonts[dcfConsole], cmdConsoleLeft.Font);
if not Assigned(ConsLeft) then
begin
ConsLeft := TPtyDevice.Create(Self);
cmdConsoleLeft.PtyDevice := ConsLeft;
if FInitializedView then
begin
if Assigned(nbLeft.ActivePage) and Assigned(nbLeft.ActivePage.FileView) then
ConsLeft.InitialDir := nbLeft.ActivePage.FileView.CurrentPath;
ConsLeft.Connected := True;
FLeftTermNeedInit := True;
end;
end;
// Create right terminal
if not Assigned(cmdConsoleRight) then
begin
cmdConsoleRight := TVirtualTerminal.Create(pnlTermContainerRight);
cmdConsoleRight.Width := 400;
cmdConsoleRight.Height := 176;
cmdConsoleRight.Parent := pnlTermContainerRight;
cmdConsoleRight.Align := alClient;
cmdConsoleRight.ShowHint := False;
end;
FontOptionsToFont(gFonts[dcfConsole], cmdConsoleRight.Font);
if not Assigned(ConsRight) then
begin
ConsRight := TPtyDevice.Create(Self);
cmdConsoleRight.PtyDevice := ConsRight;
if FInitializedView then
begin
if Assigned(nbRight.ActivePage) and Assigned(nbRight.ActivePage.FileView) then
ConsRight.InitialDir := nbRight.ActivePage.FileView.CurrentPath;
ConsRight.Connected := True;
FRightTermNeedInit := True;
end;
end;
end
else
begin
// twmPerTab mode
// Destroy per-panel terminals if they exist
if Assigned(cmdConsoleLeft) then FreeAndNil(cmdConsoleLeft);
if Assigned(ConsLeft) then FreeAndNil(ConsLeft);
if Assigned(cmdConsoleRight) then FreeAndNil(cmdConsoleRight);
if Assigned(ConsRight) then FreeAndNil(ConsRight);
SwitchTabTerminal(nbLeft);
SwitchTabTerminal(nbRight);
end;
pnlTermContainerLeft.Visible := True;
@ -5971,7 +6168,7 @@ begin
nbConsole.Visible := False;
ConsoleSplitter.Visible := False;
end
else if gTermWindow and not gTermWindowSplit then
else if gTermWindow and (gTermWindowMode = twmSingle) then
begin
// Single terminal enabled
if not Assigned(cmdConsole) then
@ -6013,6 +6210,10 @@ begin
splitTermLeft.Visible := False;
pnlTermContainerRight.Visible := False;
splitTermRight.Visible := False;
if Assigned(cmdConsoleLeft) then FreeAndNil(cmdConsoleLeft);
if Assigned(ConsLeft) then FreeAndNil(ConsLeft);
if Assigned(cmdConsoleRight) then FreeAndNil(cmdConsoleRight);
if Assigned(ConsRight) then FreeAndNil(ConsRight);
end;
end
else
@ -6034,12 +6235,78 @@ begin
splitTermLeft.Visible := False;
pnlTermContainerRight.Visible := False;
splitTermRight.Visible := False;
if Assigned(cmdConsoleLeft) then FreeAndNil(cmdConsoleLeft);
if Assigned(ConsLeft) then FreeAndNil(ConsLeft);
if Assigned(cmdConsoleRight) then FreeAndNil(cmdConsoleRight);
if Assigned(ConsRight) then FreeAndNil(ConsRight);
end;
if Assigned(FTerminalSyncTimer) then
FTerminalSyncTimer.Enabled := False;
end;
end;
procedure TfrmMain.GetOrCreateTabTerminal(Page: TFileViewPage; AParent: TWinControl);
begin
if not Assigned(Page.Terminal) then
begin
Page.Terminal := TVirtualTerminal.Create(Page);
Page.Terminal.Width := 400;
Page.Terminal.Height := 176;
Page.Terminal.ShowHint := False;
FontOptionsToFont(gFonts[dcfConsole], Page.Terminal.Font);
Page.PtyDevice := TPtyDevice.Create(Page);
Page.Terminal.PtyDevice := Page.PtyDevice;
Page.Terminal.Parent := AParent;
if Assigned(Page.FileView) then
Page.PtyDevice.InitialDir := Page.FileView.CurrentPath;
Page.PtyDevice.Connected := True;
Page.TermInitialized := True;
Page.TermNeedInit := True;
end;
end;
procedure TfrmMain.SwitchTabTerminal(Notebook: TFileViewNotebook);
var
i: Integer;
Page: TFileViewPage;
Container: TPanel;
begin
if not gTermWindow or (gTermWindowMode <> twmPerTab) then Exit;
if not Assigned(Notebook) or not Assigned(Notebook.ActivePage) then Exit;
if Notebook = nbLeft then
Container := pnlTermContainerLeft
else
Container := pnlTermContainerRight;
if not Assigned(Container) then Exit;
for i := 0 to Notebook.PageCount - 1 do
begin
Page := TFileViewPage(Notebook.Page[i]);
if Assigned(Page.Terminal) then
begin
Page.Terminal.Hide;
Page.Terminal.Parent := nil;
end;
end;
Page := TFileViewPage(Notebook.ActivePage);
GetOrCreateTabTerminal(Page, Container);
Page.Terminal.Parent := Container;
Page.Terminal.Align := alClient;
Page.Terminal.Show;
if Notebook = nbLeft then
UpdateTermSyncButtons(True)
else
UpdateTermSyncButtons(False);
end;
procedure TfrmMain.ShowOptionsLayout(Data: PtrInt);
begin
ShowOptions('TfrmOptionsLayout');
@ -6052,9 +6319,9 @@ begin
if MessageDlg(rsMsgTerminalDisabled, mtWarning, [mbYes, mbNo], 0, mbYes) = mrYes then
Application.QueueAsyncCall(@ShowOptionsLayout, 0);
end
else if gTermWindowSplit then
else if gTermWindowMode >= twmPerPanel then
begin
// Fullscreen toggle not currently supported for split terminals
// Fullscreen toggle not currently supported for split/tab terminals
end
else if nbConsole.Height < (nbConsole.Height + pnlNotebooks.Height - 1) then
begin

View file

@ -1,8 +1,9 @@
inherited frmOptionsLayout: TfrmOptionsLayout
Height = 550
Height = 650
Width = 784
HelpKeyword = '/configuration.html#ConfigLayout'
ClientHeight = 550
AutoSize = True
ClientHeight = 650
ClientWidth = 784
DesignLeft = 276
DesignTop = 44
@ -12,7 +13,7 @@ inherited frmOptionsLayout: TfrmOptionsLayout
AnchorSideRight.Control = Owner
AnchorSideRight.Side = asrBottom
Left = 6
Height = 530
Height = 630
Top = 6
Width = 772
Anchors = [akTop, akLeft, akRight]
@ -21,7 +22,7 @@ inherited frmOptionsLayout: TfrmOptionsLayout
Caption = ' Screen layout '
ChildSizing.LeftRightSpacing = 12
ChildSizing.TopBottomSpacing = 6
ClientHeight = 512
ClientHeight = 612
ClientWidth = 768
TabOrder = 0
object cbShowMainMenu: TCheckBox
@ -193,17 +194,28 @@ inherited frmOptionsLayout: TfrmOptionsLayout
OnChange = cbTermWindowChange
TabOrder = 18
end
object cbTermSplit: TCheckBox
object rgTermMode: TRadioGroup
AnchorSideLeft.Control = cbTermWindow
AnchorSideTop.Control = cbTermWindow
AnchorSideTop.Side = asrBottom
Left = 28
Height = 22
Top = 460
Width = 320
AutoSize = True
BorderSpacing.Left = 16
BorderSpacing.Top = 2
Caption = 'Show separate terminal under each file panel'
Caption = 'Terminal mode'
ChildSizing.LeftRightSpacing = 6
ChildSizing.TopBottomSpacing = 6
ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
ChildSizing.EnlargeVertical = crsHomogenousChildResize
ChildSizing.ShrinkHorizontal = crsScaleChilds
ChildSizing.ShrinkVertical = crsScaleChilds
ChildSizing.Layout = cclLeftToRightThenTopToBottom
ChildSizing.ControlsPerLine = 1
Items.Strings = (
'Single shared terminal'
'Terminal per panel'
'Terminal per tab'
)
TabOrder = 19
end
object cbFreespaceInd: TCheckBox
@ -218,11 +230,11 @@ inherited frmOptionsLayout: TfrmOptionsLayout
TabOrder = 9
end
object cbProgInMenuBar: TCheckBox
AnchorSideTop.Control = cbTermSplit
AnchorSideTop.Control = rgTermMode
AnchorSideTop.Side = asrBottom
Left = 12
Height = 22
Top = 484
Top = 562
Width = 236
BorderSpacing.Top = 2
Caption = 'Show common progress in menu bar'
@ -233,7 +245,7 @@ inherited frmOptionsLayout: TfrmOptionsLayout
AnchorSideTop.Side = asrBottom
Left = 12
Height = 22
Top = 508
Top = 586
Width = 248
BorderSpacing.Top = 2
Caption = 'Show panel of operation in background'

View file

@ -27,7 +27,7 @@ unit fOptionsLayout;
interface
uses
Classes, SysUtils, StdCtrls,
Classes, SysUtils, StdCtrls, ExtCtrls,
fOptionsFrame;
type
@ -53,7 +53,7 @@ type
cbShowTabHeader: TCheckBox;
cbShowTabs: TCheckBox;
cbTermWindow: TCheckBox;
cbTermSplit: TCheckBox;
rgTermMode: TRadioGroup;
cbTwoDiskPanels: TCheckBox;
cbShowShortDriveFreeSpace: TCheckBox;
chkShowMiddleToolBar: TCheckBox;
@ -92,8 +92,7 @@ end;
procedure TfrmOptionsLayout.cbTermWindowChange(Sender: TObject);
begin
cbTermSplit.Enabled := cbTermWindow.Checked;
if not(cbTermWindow.Checked) then cbTermSplit.Checked := False;
rgTermMode.Enabled := cbTermWindow.Checked;
end;
class function TfrmOptionsLayout.GetIconIndex: Integer;
@ -124,8 +123,8 @@ begin
cbFlatInterface.Checked := gInterfaceFlat;
cbLogWindow.Checked := gLogWindow;
cbTermWindow.Checked := gTermWindow;
cbTermSplit.Checked := gTermWindowSplit;
cbTermSplit.Enabled := gTermWindow;
rgTermMode.ItemIndex := gTermWindowMode;
rgTermMode.Enabled := gTermWindow;
cbShowDriveFreeSpace.Checked := gDriveFreeSpace;
cbFreespaceInd.Checked := gDriveInd;
cbProgInMenuBar.Checked := gProgInMenuBar;
@ -153,7 +152,7 @@ begin
gInterfaceFlat := cbFlatInterface.Checked;
gLogWindow := cbLogWindow.Checked;
gTermWindow := cbTermWindow.Checked;
gTermWindowSplit := cbTermSplit.Checked;
gTermWindowMode := rgTermMode.ItemIndex;
gDriveFreeSpace := cbShowDriveFreeSpace.Checked;
gDriveInd := cbFreespaceInd.Checked;
gProgInMenuBar := cbProgInMenuBar.Checked;

View file

@ -36,7 +36,7 @@ interface
uses
Classes, SysUtils, Controls, ComCtrls, LMessages,
LCLType, Forms,
uFileView, uFilePanelSelect, DCXmlConfig;
uFileView, uFilePanelSelect, DCXmlConfig, VTEmuCtl, VTEmuPty;
type
@ -61,6 +61,10 @@ type
FBackupColumnSet: String;
FOnChangeFileView: TNotifyEvent;
FBackupViewClass: TFileViewClass;
FTerminal: TVirtualTerminal;
FPtyDevice: TCustomPtyDevice;
FTermInitialized: Boolean;
FTermNeedInit: Boolean;
procedure AssignPage(OtherPage: TFileViewPage);
procedure AssignProperties(OtherPage: TFileViewPage);
@ -90,6 +94,7 @@ type
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
function IsActive: Boolean;
procedure MakeActive;
@ -109,6 +114,10 @@ type
property BackupColumnSet: String read FBackupColumnSet write FBackupColumnSet;
property BackupViewClass: TFileViewClass read FBackupViewClass write FBackupViewClass;
property OnChangeFileView: TNotifyEvent read FOnChangeFileView write FOnChangeFileView;
property Terminal: TVirtualTerminal read FTerminal write FTerminal;
property PtyDevice: TCustomPtyDevice read FPtyDevice write FPtyDevice;
property TermInitialized: Boolean read FTermInitialized write FTermInitialized;
property TermNeedInit: Boolean read FTermNeedInit write FTermNeedInit;
end;
{ TFileViewNotebook }
@ -233,6 +242,13 @@ begin
inherited Create(TheOwner);
end;
destructor TFileViewPage.Destroy;
begin
if Assigned(FTerminal) then FTerminal.Free;
if Assigned(FPtyDevice) then FPtyDevice.Free;
inherited Destroy;
end;
{$IF DEFINED(LCLWIN32)}
procedure TFileViewPage.RealSetText(const AValue: TCaption);
begin

View file

@ -234,6 +234,10 @@ const
// 16 - Move DirectoryHotList to localconfig.xml
ConfigVersion = 16;
twmSingle = 0;
twmPerPanel = 1;
twmPerTab = 2;
COLORS_JSON = 'colors.json';
// Configuration related filenames
@ -298,7 +302,6 @@ var
gCmdLine,
gLogWindow,
gTermWindow,
gTermWindowSplit,
gKeyButtons,
gInterfaceFlat,
gDriveInd,
@ -315,6 +318,8 @@ var
gTermSyncModeLeft: Integer;
gTermSyncModeRight: Integer;
gTermWindowMode: Integer;
{ Toolbar }
gMiddleToolBarFlat,
gMiddleToolBarShowCaptions,
@ -2027,7 +2032,7 @@ begin
gCmdLine := True;
gLogWindow := False;
gTermWindow := False;
gTermWindowSplit := False;
gTermWindowMode := twmSingle;
gTermSyncModeLeft := 0; // Detach
gTermSyncModeRight := 0; // Detach
gKeyButtons := True;
@ -2965,7 +2970,10 @@ begin
gCmdLine := GetValue(Node, 'CmdLine', gCmdLine);
gLogWindow := GetValue(Node, 'LogWindow', gLogWindow);
gTermWindow := GetValue(Node, 'TermWindow', gTermWindow);
gTermWindowSplit := GetValue(Node, 'TermWindowSplit', gTermWindowSplit);
if GetValue(Node, 'TermWindowSplit', False) then
gTermWindowMode := twmPerPanel
else
gTermWindowMode := GetValue(Node, 'TermWindowMode', gTermWindowMode);
gTermSyncModeLeft := GetValue(Node, 'TermSyncModeLeft', gTermSyncModeLeft);
gTermSyncModeRight := GetValue(Node, 'TermSyncModeRight', gTermSyncModeRight);
gKeyButtons := GetValue(Node, 'KeyButtons', gKeyButtons);
@ -3685,7 +3693,7 @@ begin
SetValue(Node, 'CmdLine', gCmdLine);
SetValue(Node, 'LogWindow', gLogWindow);
SetValue(Node, 'TermWindow', gTermWindow);
SetValue(Node, 'TermWindowSplit', gTermWindowSplit);
SetValue(Node, 'TermWindowMode', gTermWindowMode);
SetValue(Node, 'TermSyncModeLeft', gTermSyncModeLeft);
SetValue(Node, 'TermSyncModeRight', gTermSyncModeRight);
SetValue(Node, 'KeyButtons', gKeyButtons);