Files
HeidiSQL/components/synedit/Source/SynURIOpener.pas

393 lines
12 KiB
ObjectPascal

{-------------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: SynURIOpener.pas, released 2003-09-25.
The Initial Author of this file is Maël Hörz.
Unicode translation by Maël Hörz.
All Rights Reserved.
Contributors to the SynEdit project are listed in the Contributors.txt file.
Alternatively, the contents of this file may be used under the terms of the
GNU General Public License Version 2 or later (the "GPL"), in which case
the provisions of the GPL are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms
of the GPL and not to allow others to use your version of this file
under the MPL, indicate your decision by deleting the provisions above and
replace them with the notice and other provisions required by the GPL.
If you do not delete the provisions above, a recipient may use your version
of this file under either the MPL or the GPL.
You may retrieve the latest version of SynEdit from the SynEdit home page,
located at http://SynEdit.SourceForge.net
-------------------------------------------------------------------------------}
{
@abstract(Plugin for SynEdit to make links (URIs) clickable)
@author(Maël Hörz)
@created(2003)
@lastmod(2004-03-19)
The SynURIOpener unit extends SynEdit to make links highlighted by SynURISyn
clickable.
http://www.mh-net.de.vu
}
{$IFNDEF QSYNURIOPENER}
unit SynURIOpener;
{$ENDIF}
{$I SynEdit.inc}
interface
uses
{$IFDEF SYN_LINUX}
Xlib,
{$ELSE}
Windows,
{$ENDIF}
Controls,
SynEditTypes,
SynEdit,
SynHighlighterURI,
SynUnicode,
Classes;
type
TSynURIOpener = class(TComponent)
private
FControlDown: Boolean;
FCtrlActivatesLinks: Boolean;
FEditor: TCustomSynEdit;
FMouseDownX: Integer;
FMouseDownY: Integer;
FURIHighlighter: TSynURISyn;
FVisitedURIs: TStringList;
{$IFDEF SYN_LINUX}
FFtpClientCmd: string;
FGopherClientCmd: string;
FMailClientCmd: string;
FNewsClientCmd: string;
FNntpClientCmd: string;
FProsperoClientCmd: string;
FTelnetClientCmd: string;
FWaisClientCmd: string;
FWebBrowserCmd: string;
{$ENDIF}
procedure OpenLink(URI: string; LinkType: Integer);
function MouseInSynEdit: Boolean;
protected
procedure NewKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure NewKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure NewMouseCursor(Sender: TObject; const aLineCharPos: TBufferCoord;
var aCursor: TCursor);
procedure NewMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure NewMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure SetEditor(const Value: TCustomSynEdit);
procedure SetURIHighlighter(const Value: TSynURISyn);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function VisitedURI(URI: UnicodeString): Boolean;
published
property CtrlActivatesLinks: Boolean read FCtrlActivatesLinks
write FCtrlActivatesLinks default True;
property Editor: TCustomSynEdit read FEditor write SetEditor;
property URIHighlighter: TSynURISyn read FURIHighlighter
write SetURIHighlighter;
{$IFDEF SYN_LINUX}
// examples how to set WebBrowserCmd; %s is the placeholder for the URI
// 'kfmclient openURL %s'
// 'mozilla %s'
// 'netscape %s'
// 'kfmclient exec %s' similar to Windows ShellExecute
//
// You should let the user set these properties as there is no command
// or environment variable valid/available on all UN*X-systems.
// It depends on what window-manager and browser is installed.
property FtpClientCmd: string read FFtpClientCmd write FFtpClientCmd;
property GopherClientCmd: string read FGopherClientCmd write FGopherClientCmd;
property MailClientCmd: string read FMailClientCmd write FMailClientCmd;
property NewsClientCmd: string read FNewsClientCmd write FNewsClientCmd;
property NntpClientCmd: string read FNntpClientCmd write FNntpClientCmd;
property ProsperoClientCmd: string read FProsperoClientCmd write FProsperoClientCmd;
property TelnetClientCmd: string read FTelnetClientCmd write FTelnetClientCmd;
property WaisClientCmd: string read FWaisClientCmd write FWaisClientCmd;
property WebBrowserCmd: string read FWebBrowserCmd write FWebBrowserCmd;
{$ENDIF}
end;
implementation
uses
{$IFDEF SYN_LINUX}
Libc,
{$ELSE}
ShellAPI,
{$ENDIF}
Forms,
SynEditHighlighter,
SynEditKeyConst,
SysUtils;
type
TAccessCustomSynEdit = class(TCustomSynEdit);
TAccessSynURISyn = class(TSynURISyn);
{ TSynURIOpener }
constructor TSynURIOpener.Create(AOwner: TComponent);
begin
inherited;
FCtrlActivatesLinks := True;
FVisitedURIs := TStringList.Create;
FVisitedURIs.Sorted := True;
end;
destructor TSynURIOpener.Destroy;
begin
FVisitedURIs.Free;
inherited;
end;
function TSynURIOpener.MouseInSynEdit: Boolean;
var
pt: TPoint;
begin
{$IFDEF SYN_COMPILER_6_UP}
pt := Mouse.CursorPos;
{$ELSE}
GetCursorPos(pt);
{$ENDIF}
Result := PtInRect(FEditor.ClientRect, FEditor.ScreenToClient(pt))
end;
procedure TSynURIOpener.NewKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (Key = SYNEDIT_CONTROL) and not FControlDown and MouseInSynEdit then
begin
FControlDown := True;
TAccessCustomSynEdit(FEditor).UpdateMouseCursor;
end;
end;
procedure TSynURIOpener.NewKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (Key = SYNEDIT_CONTROL) and FControlDown then
begin
FControlDown := False;
TAccessCustomSynEdit(FEditor).UpdateMouseCursor;
end;
end;
function IsControlPressed: Boolean;
{$IFDEF SYN_LINUX}
var
keymap: TXQueryKeyMap;
{$ENDIF}
begin
{$IFDEF SYN_LINUX}
XQueryKeymap(Xlib.PDisplay(QtDisplay), keymap);
Result := (Byte(keymap[4]) and $20 = $20);
{$ELSE}
Result := GetAsyncKeyState(VK_CONTROL) <> 0;
{$ENDIF}
end;
procedure TSynURIOpener.NewMouseCursor(Sender: TObject;
const aLineCharPos: TBufferCoord; var aCursor: TCursor);
var
TokenType, Start: Integer;
Token: UnicodeString;
Attri: TSynHighlighterAttributes;
begin
FControlDown := IsControlPressed;
if not(FCtrlActivatesLinks and not FControlDown or
(csDesigning in FEditor.ComponentState)) and FEditor.Focused
then
with FEditor do
begin
GetHighlighterAttriAtRowColEx(aLineCharPos, Token, TokenType, Start, Attri);
if Assigned(URIHighlighter) and ((Attri = URIHighlighter.URIAttri) or
(Attri = URIHighlighter.VisitedURIAttri)) and
not((eoDragDropEditing in Options) and IsPointInSelection(aLineCharPos))
then
aCursor := crHandPoint
end
end;
procedure TSynURIOpener.NewMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (Button = mbLeft) and not(FCtrlActivatesLinks) or FControlDown then
begin
FMouseDownX := X;
FMouseDownY := Y;
end
end;
procedure TSynURIOpener.NewMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
ptLineCol: TBufferCoord;
TokenType, Start: Integer;
Token: UnicodeString;
Attri: TSynHighlighterAttributes;
begin
if (Button <> mbLeft) or (FCtrlActivatesLinks and not FControlDown) or
(Abs(FMouseDownX - X) > 4) or (Abs(FMouseDownY - Y) > 4) then Exit;
with TAccessCustomSynEdit(FEditor) do
begin
if (eoDragDropEditing in Options) and IsPointInSelection(ptLineCol) then
Exit;
if X >= fGutterWidth then
begin
ptLineCol := DisplayToBufferPos(PixelsToRowColumn(X,Y));
GetHighlighterAttriAtRowColEx(ptLineCol, Token, TokenType, Start, Attri);
if Assigned(URIHighlighter) and ((Attri = URIHighlighter.URIAttri) or
(Attri = URIHighlighter.VisitedURIAttri)) and
not((eoDragDropEditing in Options) and IsPointInSelection(ptLineCol)) then
begin
OpenLink(Token, TokenType);
InvalidateLine(ptLineCol.Line);
end;
end
end;
end;
procedure TSynURIOpener.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and Assigned(Editor) and (AComponent = Editor) then
Editor := nil;
if (Operation = opRemove) and Assigned(URIHighlighter) and
(AComponent = URIHighlighter)
then
URIHighlighter := nil;
end;
procedure TSynURIOpener.OpenLink(URI: string; LinkType: Integer);
{$IFDEF SYN_LINUX}
var
CmdLine: string;
{$ENDIF}
begin
FVisitedURIs.Add(URI);
case TtkTokenKind(LinkType) of
tkMailtoLink:
if (Pos('mailto:', URI) <> 1) then URI := 'mailto:' + URI;
tkWebLink:
URI := 'http://' + URI;
end;
{$IFDEF SYN_LINUX}
case TtkTokenKind(LinkType) of
tkFtpLink:
CmdLine := Format(FFtpClientCmd, [URI]);
tkGopherLink:
CmdLine := Format(FGopherClientCmd, [URI]);
tkMailtoLink:
CmdLine := Format(FMailClientCmd, [URI]);
tkNewsLink:
CmdLine := Format(FNewsClientCmd, [URI]);
tkNntpLink:
CmdLine := Format(FNntpClientCmd, [URI]);
tkProsperoLink:
CmdLine := Format(FProsperoClientCmd, [URI]);
tkTelnetLink:
CmdLine := Format(FTelnetClientCmd, [URI]);
tkWaisLink:
CmdLine := Format(FWaisClientCmd, [URI]);
tkWebLink, tkHttpLink, tkHttpsLink:
CmdLine := Format(FWebBrowserCmd, [URI]);
end;
Libc.system(PAnsiChar(CmdLine + ' &')); // add an ampersand to return immediately
{$ELSE}
ShellExecute(0, nil, PChar(URI), nil, nil, 1{SW_SHOWNORMAL});
{$ENDIF}
end;
procedure TSynURIOpener.SetEditor(const Value: TCustomSynEdit);
begin
if Editor <> Value then
begin
if not(csDesigning in ComponentState) and Assigned(FEditor) then
begin
with FEditor do
begin
RemoveKeyDownHandler(NewKeyDown);
RemoveKeyUpHandler(NewKeyUp);
RemoveMouseCursorHandler(NewMouseCursor);
RemoveMouseDownHandler(NewMouseDown);
RemoveMouseUpHandler(NewMouseUp);
end;
end;
FEditor := Value;
if not(csDesigning in ComponentState) and Assigned(FEditor) then
begin
with FEditor do
begin
AddKeyDownHandler(NewKeyDown);
AddKeyUpHandler(NewKeyUp);
AddMouseCursorHandler(NewMouseCursor);
AddMouseDownHandler(NewMouseDown);
AddMouseUpHandler(NewMouseUp);
end;
end;
end;
end;
procedure TSynURIOpener.SetURIHighlighter(const Value: TSynURISyn);
begin
if not(csDesigning in ComponentState) and Assigned(URIHighlighter) then
TAccessSynURISyn(FURIHighlighter).SetAlreadyVisitedURIFunc(nil);
FURIHighlighter := Value;
if not(csDesigning in ComponentState) and Assigned(URIHighlighter) then
TAccessSynURISyn(FURIHighlighter).SetAlreadyVisitedURIFunc(VisitedURI);
end;
function TSynURIOpener.VisitedURI(URI: UnicodeString): Boolean;
var
Dummy: Integer;
begin
Result := FVisitedURIs.Find(URI, Dummy);
end;
const
IDC_LINK = MakeIntResource(32649);
var
CursorHandle: THandle;
initialization
CursorHandle := LoadCursor(0, IDC_LINK);
if CursorHandle <> 0 then
Screen.Cursors[crHandPoint] := CursorHandle;
end.