mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
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:
parent
f423f3ace5
commit
ef80fa3dda
6 changed files with 634 additions and 136 deletions
|
|
@ -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;
|
||||
|
|
|
|||
473
src/fmain.pas
473
src/fmain.pas
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue