dexed/src/u_term.pas

430 lines
11 KiB
Plaintext

unit u_term;
{$I u_defines.inc}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, LCLType,
ActnList, LMessages, ExtCtrls, Menus, StdCtrls,
u_widget, TerminalCtrls, u_interfaces, u_writableComponent, u_observer,
u_common, u_synmemo, u_dsgncontrols;
type
TTerminalShortcuts = class(TPersistent)
private
fCopy: TShortCut;
fPaste: TShortCut;
published
property copy: TShortCut read fCopy write fCopy;
property paste: TShortCut read fPaste write fPaste;
public
constructor create;
procedure assign(source: TPersistent); override;
end;
// Terminal options
TTerminalOptionsBase = class(TWritableLfmTextComponent)
private
fBackgroundColor: TColor;
fForegroundColor: TColor;
fSelectedColor: TColor;
fFollowEditors: boolean;
fFollowProjects: boolean;
fFollowExplorer: boolean;
fScrollbackLines: longword;
fFont: TFont;
fShortcuts: TTerminalShortcuts;
procedure setFont(value: TFont);
procedure setShortcuts(value: TTerminalShortcuts);
public
constructor create(AOwner: TComponent); override;
destructor destroy; override;
procedure assign(value: TPersistent); override;
published
property backgroundColor: TColor read fBackgroundColor write fBackgroundColor;
property foregroundColor: TColor read fForegroundColor write fForegroundColor;
property selectedColor: TColor (*read fSelectedColor*) write fSelectedColor stored false;
property font: TFont read fFont write setFont;
property followEditors: boolean read fFollowEditors write fFollowEditors;
property followProjects: boolean read fFollowProjects write fFollowProjects;
property followExplorer: boolean read fFollowExplorer write fFollowExplorer;
property scrollbackLines: longword read fScrollbackLines write fScrollbackLines default 10000;
property shortcuts: TTerminalShortcuts read fShortcuts write fShortcuts;
end;
// Editable and reversible Terminal options
TTerminalOptions = class(TTerminalOptionsBase, IEditableOptions)
private
fBackup: TTerminalOptionsBase;
function optionedWantCategory(): string;
function optionedWantEditorKind: TOptionEditorKind;
function optionedWantContainer: TPersistent;
procedure optionedEvent(event: TOptionEditorEvent);
function optionedOptionsModified: boolean;
public
constructor Create(AOwner: TComponent); override;
procedure applyChanges;
end;
{ TTermWidget }
TTermWidget = class(TDexedWidget, IDocumentObserver, IProjectObserver, IMiniExplorerObserver)
Panel1: TPanel;
ScrollBar1: TScrollBar;
procedure ContentPaint(Sender: TObject);
procedure FormShortCut(var Msg: TLMKey; var Handled: Boolean);
procedure ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode;
var ScrollPos: Integer);
private
fTerm: TTerminal;
fOpts: TTerminalOptions;
fLastCheckedDirectory: string;
fDisableScrollBarSync: boolean;
fNeedApplyChanges: boolean;
fPaintOnce: boolean;
procedure checkDirectory(const dir: string);
procedure updateScrollBar();
procedure terminalTextScrolled(sender: TObject; delta: integer);
procedure docNew(document: TDexedMemo);
procedure docFocused(document: TDexedMemo);
procedure docChanged(document: TDexedMemo);
procedure docClosing(document: TDexedMemo);
procedure mnexDirectoryChanged(const directory: string);
procedure projNew(project: ICommonProject);
procedure projChanged(project: ICommonProject);
procedure projClosing(project: ICommonProject);
procedure projFocused(project: ICommonProject);
procedure projCompiling(project: ICommonProject);
procedure projCompiled(project: ICommonProject; success: boolean);
protected
procedure DoShow; override;
procedure SetVisible(Value: boolean); override;
public
constructor create(aOwner: TComponent); override;
destructor destroy; override;
end;
implementation
{$R *.lfm}
const
optFname = 'terminal.txt';
constructor TTerminalShortcuts.create;
begin
fCopy := KeyToShortCut(word(char('C')), [ssCtrl, ssShift]);
fPaste:= KeyToShortCut(word(char('V')), [ssCtrl, ssShift]);
end;
procedure TTerminalShortcuts.assign(source: TPersistent);
var
s: TTerminalShortcuts;
begin
if source is TTerminalShortcuts then
begin
s := TTerminalShortcuts(source);
fCopy := s.fCopy;
fPaste:= s.fPaste;
end
else inherited;
end;
constructor TTerminalOptionsBase.create(AOwner: TComponent);
begin
inherited;
fFont := TFont.Create;
fBackgroundColor:= clWhite;
fForegroundColor:= clBlack;
fSelectedColor:= clBlack;
fFont.Name:= 'Monospace';
fFont.Size:= 12;
fScrollbackLines:=10000;
fShortcuts := TTerminalShortcuts.create;
end;
destructor TTerminalOptionsBase.destroy;
begin
fFont.Free;
fShortcuts.Free;
inherited;
end;
procedure TTerminalOptionsBase.setFont(value: TFont);
begin
fFont.Assign(value);
end;
procedure TTerminalOptionsBase.setShortcuts(value: TTerminalShortcuts);
begin
fShortcuts.assign(value);
end;
procedure TTerminalOptionsBase.assign(value: TPersistent);
var
s: TTerminalOptionsBase;
begin
if value is TTerminalOptionsBase then
begin
s := TTerminalOptionsBase(value);
fBackgroundColor:=s.fbackgroundColor;
fForegroundColor:=s.fForegroundColor;
fSelectedColor:=s.fSelectedColor;
followEditors:=s.fFollowEditors;
fFont.BeginUpdate;
fFont.Height:=fFont.Height+1;
fFont.Height:=fFont.Height-1;
fFont.Assign(s.font);
fFont.EndUpdate;
fShortcuts.assign(s.fShortcuts);
fScrollbackLines := s.fScrollbackLines;
end
else inherited;
end;
constructor TTerminalOptions.Create(AOwner: TComponent);
begin
inherited;
fBackup := TTerminalOptionsBase.Create(self);
end;
procedure TTerminalOptions.applyChanges;
var
w: TTermWidget;
begin
w := TTermWidget(owner);
w.fTerm.backgroundColor:= backgroundColor;
w.fTerm.foregroundColor:= foregroundColor;
//w.fTerm.selectedColor:= selectedColor;
w.fTerm.Font.BeginUpdate;
w.fTerm.Font.Assign(fFont);
// force the change: assigning does always trigger TTerminal.FontChanged.
w.fTerm.Font.Size := w.fTerm.Font.Size +1;
w.fTerm.Font.Size := w.fTerm.Font.Size -1;
w.fTerm.Font.endUpdate;
w.fTerm.scrollbackLines:=fScrollbackLines;
end;
function TTerminalOptions.optionedWantCategory(): string;
begin
result := 'Terminal';
end;
function TTerminalOptions.optionedWantEditorKind: TOptionEditorKind;
begin
result := oekGeneric;
end;
function TTerminalOptions.optionedWantContainer: TPersistent;
begin
result := self;
end;
procedure TTerminalOptions.optionedEvent(event: TOptionEditorEvent);
begin
case event of
oeeAccept:
begin
fBackup.assign(self);
applyChanges;
end;
oeeCancel:
begin
self.assign(fBackup);
applyChanges;
end;
oeeChange:
begin
applyChanges;
end;
end;
end;
function TTerminalOptions.optionedOptionsModified: boolean;
begin
result := false;
end;
constructor TTermWidget.create(aOwner: TComponent);
var
f: string;
begin
inherited;
toolbarVisible:=false;
fTerm := TTerminal.Create(Panel1);
fTerm.Align:= alClient;
fTerm.BorderSpacing.Around:=4;
fterm.Parent := Panel1;
fTerm.OnTextScrolled:= @terminalTextScrolled;
fOpts:= TTerminalOptions.Create(self);
f := getDocPath + optFname;
if f.fileExists then
fOpts.loadFromFile(f);
EntitiesConnector.addObserver(fOpts);
end;
destructor TTermWidget.destroy;
begin
fOpts.saveToFile(getDocPath + optFname);
EntitiesConnector.removeObserver(fOpts);
inherited;
end;
procedure TTermWidget.DoShow;
begin
inherited;
fNeedApplyChanges := true;
end;
procedure TTermWidget.checkDirectory(const dir: string);
var
i: TTerminalScrollInfo;
begin
fDisableScrollBarSync := true;
fLastCheckedDirectory := dir;
fTerm.SendControlChar(TASCIIControlCharacter.HOME);
fTerm.SendControlChar(TASCIIControlCharacter.VT);
fTerm.SendControlChar(TASCIIControlCharacter.LF);
fTerm.Command('cd ' + dir);
i := fTerm.getVScrollInfo();
ScrollBar1.Max := i.max;
ScrollBar1.Position := ScrollBar1.Max;
fTerm.setVScrollPosition(ScrollBar1.Max);
fDisableScrollBarSync := false;
end;
procedure TTermWidget.SetVisible(Value: boolean);
begin
inherited;
if Value then
fNeedApplyChanges := true;
end;
procedure TTermWidget.ContentPaint(Sender: TObject);
var
s: string;
begin
if not fNeedApplyChanges then
exit;
fNeedApplyChanges:=false;
fOpts.applyChanges;
// ugly fix for https://gitlab.com/basile.b/dexed/-/issues/5
if not fPaintOnce then
begin
s := fLastCheckedDirectory;
fLastCheckedDirectory := '';
if s.isNotEmpty then
checkDirectory(s);
end;
fPaintOnce := true;
end;
procedure TTermWidget.terminalTextScrolled(sender: TObject; delta: integer);
begin
updateScrollBar();
end;
procedure TTermWidget.updateScrollBar();
var
i: TTerminalScrollInfo;
begin
if fDisableScrollBarSync or not visible or fTerm.isNotAssigned then
exit;
i := fTerm.getVScrollInfo();
ScrollBar1.SetParams(i.value, i.min, i.max, i.pageSize);
end;
procedure TTermWidget.FormShortCut(var Msg: TLMKey; var Handled: Boolean);
var
s: TShortCut;
begin
Handled := false;
s := KeyToShortCut(Msg.CharCode, KeyDataToShiftState(Msg.KeyData));
if s = fOpts.shortcuts.copy then
begin
fTerm.copyToClipboard();
handled := true;
end
else if s = fOpts.shortcuts.paste then
begin
fTerm.pasteFromClipboard();
handled := true;
end;
end;
procedure TTermWidget.ScrollBar1Scroll(Sender: TObject;
ScrollCode: TScrollCode; var ScrollPos: Integer);
begin
fTerm.setVScrollPosition(ScrollPos);
end;
procedure TTermWidget.mnexDirectoryChanged(const directory: string);
begin
if fOpts.followExplorer and directory.dirExists and
not SameText(directory, fLastCheckedDirectory) then
checkDirectory(directory);
end;
procedure TTermWidget.docNew(document: TDexedMemo);
begin
end;
procedure TTermWidget.docFocused(document: TDexedMemo);
var
s: string;
begin
s := document.fileName.extractFileDir;
if fOpts.followEditors and s.dirExists and not SameText(s, fLastCheckedDirectory) then
checkDirectory(s);
end;
procedure TTermWidget.docChanged(document: TDexedMemo);
begin
end;
procedure TTermWidget.docClosing(document: TDexedMemo);
begin
end;
procedure TTermWidget.projNew(project: ICommonProject);
begin
end;
procedure TTermWidget.projChanged(project: ICommonProject);
begin
end;
procedure TTermWidget.projClosing(project: ICommonProject);
begin
end;
procedure TTermWidget.projFocused(project: ICommonProject);
var
s: string;
begin
s := project.fileName.extractFileDir;
if fOpts.followProjects and s.dirExists and not SameText(s, fLastCheckedDirectory) then
checkDirectory(s);
end;
procedure TTermWidget.projCompiling(project: ICommonProject);
begin
end;
procedure TTermWidget.projCompiled(project: ICommonProject; success: boolean);
begin
end;
end.