unit TMGHTML2;
(*
NOTES: By Kevin Toppenberg, MD 5/27/09
Code heavily modified from original code found at www.supermemo.com/source/
Their notes (below) indicate that the code may be freely used.
---------------
This unit encapsulates SHDocVw.dll and MSHTML.dll functionality by subclassing
THtmlEditorBrowser object as THtmlEditor object
THtmlEditor was designed for easy use of HTML display and editing capacity in
SuperMemo 2002 for Windows developed by SuperMemo R&D in Fall 2001.
SuperMemo 2002 implements HTML-based incremental reading in which extensive HTML
support is vital.
Pieces of this units can be used by anyone in other Delphi applications that make
use of HTML WYSIWYG interfaces made open by Microsoft.
*)
(*
NOTICE: Also Derived from EmbeddedED. See notes in that code block.
*)
interface
uses SysUtils, WinTypes, Dialogs, StdCtrls, Menus,
EmbeddedED,
ActiveX, MSHTMLEvents, SHDocVw, {MSHTML,} MSHTML_EWB,
AppEvnts, controls,
IeConst,Messages,Classes,Forms,Graphics;
type
TSetFontMode = (sfAll,sfSize,sfColor,sfName,sfStyle,sfCharset);
TRGBColor = record
R : byte;
G : byte;
B : byte;
end; {record}
TMGColor = record
case boolean of
True: (Color : TColor);
False: (RGBColor : TRGBColor);
end; {record}
type
// THtmlObj=class(TWebBrowser)
THtmlObj=class(TEmbeddedED)
private
CtrlToBeProcessed : boolean;
ShiftToBeProcessed : boolean;
CtrlReturnToBeProcessed: boolean;
Modified: boolean;
FOrigAppOnMessage : TMessageEvent;
FApplication : TApplication;
FActive : boolean;
FEditable: boolean;
ColorDialog: TColorDialog;
AllowNextBlur : boolean;
function GetHTMLText:string;
procedure SetHTMLText(HTML:String);
function GetText:string;
procedure SetText(HTML:string);
function GetEditableState : boolean;
procedure SetEditableState (EditOn : boolean);
procedure SetBackgroundColor(Color:TColor);
function GetBackgroundColor : TColor;
function ColorToMSHTMLStr(color : TColor) : string;
function MSHTMLStrToColor(MSHTMLColor : string) : TColor;
procedure SetTextForegroundColor(Color:TColor);
function GetTextForegroundColor : TColor;
procedure SetTextBackgroundColor(Color:TColor);
function GetTextBackgroundColor : TColor;
function GetFontSize : integer;
procedure SetFontSize (Size : integer);
function GetFontName : string;
procedure SetFontName (Name : string);
function GetSelText:string;
procedure SetSelText (HTMLText : string);
procedure ReassignKeyboardHandler(TurnOn : boolean);
procedure GlobalMsgHandler(var Msg: TMsg; var Handled: Boolean);
procedure HandleBlur(Sender: TObject);
procedure SubMessageHandler(var Msg: TMessage); override;
function SubFocusHandler(fGotFocus: BOOL): HResult; override;
function GetActive : boolean;
{end private}
public
{end public}
PopupMenu: TPopupMenu;
KeyStruck : boolean; // A VERY crude determiner as to if Modified.
NextControl : TWinControl;
PrevControl : TWinControl;
procedure SetMsgActive (Active : boolean);
constructor Create(Owner:TControl; Application : TApplication);
destructor Destroy; override;
procedure Clear;
procedure ToggleBullet;
procedure ToggleItalic;
procedure ToggleBold;
procedure ToggleNumbering;
procedure ToggleUnderline;
procedure ToggleSubscript;
procedure ToggleSuperscript;
procedure Indent;
procedure Outdent;
procedure AlignLeft;
procedure AlignRight;
procedure AlignCenter;
procedure TextForeColorDialog;
procedure TextBackColorDialog;
procedure FontDialog;
function SelStart:integer;
function SelEnd:integer;
function SelLength:integer;
function GetTextRange:IHtmlTxtRange;
procedure ReplaceSelection(HTML:string);
procedure Loaded; Override;
function GetTextLen : integer;
function MoveCaretToEnd : boolean;
function MoveCaretToPos(ScreenPos: TPoint) : HRESULT; //kt added
procedure InsertTextAtCaret(Text : AnsiString); //Note: Text is NOT HTMLtext
property HTMLText:string read GetHTMLText write SetHTMLText;
property Text:string read GetText write SetText;
//property Active : boolean read FActive write SetMsgActive;
property Active : boolean read GetActive;
property Editable : boolean read GetEditableState write SetEditableState;
property BackgroundColor : TColor read GetBackgroundColor write SetBackgroundColor;
property FontSize : integer read GetFontSize write SetFontSize;
property FontName : string read GetFontName write SetFontName;
property SelText : string read GetSelText write SetSelText;
end;
implementation
uses
WinProcs,Variants,Clipbrd, StrUtils, Math,
Windows;
const
FontScale=3;
MaxTextLength = 100;
nl = #13#10;
procedure EError(EText : string; E : Exception);
begin
MessageDlg(EText,mtError,[mbOK],0);
end;
constructor THtmlObj.Create(Owner:TControl; Application : TApplication);
begin
inherited Create(Owner); //Note: Owner should be a descendant of TControl;
FApplication := Application;
FOrigAppOnMessage := Application.OnMessage;
OnBlur := HandleBlur;
AllowNextBlur := false;
KeyStruck := false;
NextControl := nil;
PrevControl := nil;
end;
destructor THtmlObj.Destroy;
begin
SetMsgActive(false); //Turns off local OnMessage handling
inherited Destroy;
end;
procedure THtmlObj.SetMsgActive (Active : boolean);
//NOTE: This object grabs the OnMessage for the entire application, so that
// it can intercept the right-click. As a result, the object needs a
// way that it can turn off this feature when it is covered up by other
// windows application subwindows etc. This function provides this.
begin
FActive := Active;
ReassignKeyboardHandler(FActive);
end;
procedure THtmlObj.SetHTMLText(Html : String);
var //V : OleVariant;
V2 : variant;
body : IHTMLElement;
status : string;
temp : string;
begin
DocumentHTML := Html;
exit; //kt
(*
try
Stop;
if Doc =nil then exit;
body := Doc.body;
if UpperCase(Doc.designMode) <> 'ON' then begin
Doc.designMode := 'on';
repeat //NOTE: potential endless loop. Perhaps loop only status='loading'?
status := Doc.readyState;
{Possible status values:
uninitialized -- Object is not initialized with data.
loading -- Object is loading its data.
loaded -- Object has finished loading its data.
interactive -- User can interact with the object even though it is not fully loaded.
complete -- Object is completely initialized. }
if status <> 'complete' then FApplication.ProcessMessages;
until (status = 'complete') or (status='interactive') or (status='loaded');
end;
body := Doc.body;
if (body = nil) then begin //Do so stuff to get IE to make a 'body'.
V2 := VarArrayCreate([0, 0], VarVariant);
V2[0] := ' '; //Html;
Doc.Write(PSafeArray(System.TVarData(V2).VArray));
body := Doc.body;
Doc.close;
repeat
status := Doc.readyState; //For possible status values, see above)
if status <> 'complete' then FApplication.ProcessMessages;
until (status = 'complete') or (status='interactive') or (status='loaded');
body := Doc.body;
end;
body.innerHTML := Html;
temp := body.innerHTML; //to test if it was set or not...
Modified:=true;
except
on E:Exception do EError('Error setting HTML text',E);
end;
*)
end;
function THtmlObj.GetHTMLText:string;
var WS:WideString;
ch:WideChar;
n:integer;
w:word;
s:string;
begin
//Result:=DocumentHTML;
Result:='';
if Doc=nil then exit;
WS:=Doc.body.innerHTML;
for n:=1 to length(WS) do begin
ch := WS[n];
w := word(ch);
if w>255 then begin
s:=IntToStr(w);
s:=''+s+';';
end else s:=ch;
Result:=Result+s;
end;
end;
function THtmlObj.GetText:string;
var WS:WideString;
ch:WideChar;
n:integer;
w:word;
s:string;
begin
Result:='';
if DOC=nil then exit;
WS:=Doc.body.innerText;
for n:=1 to length(WS) do begin
ch:=WS[n];
w:=word(ch);
if w>255 then begin
w:=(w mod 256)+48;
s:=IntToStr(w);
s:=char(w);
end else s:=ch;
Result:=Result+s;
end;
end;
procedure THtmlObj.SetText(HTML:string);
begin
if (DOC=nil)or(DOC.body=nil) then SetHTMLText(HTML)
else DOC.body.innerHTML:=HTML;
end;
procedure THtmlObj.Clear;
begin
//kt if IsDirty then
NewDocument;
KeyStruck := false;
//SetHTMLText('');
end;
function THtmlObj.GetEditableState : boolean;
var mode : string;
begin
mode := Doc.designMode;
result := (mode = 'On');
end;
procedure THtmlObj.SetEditableState(EditOn : boolean);
var LastMode : string;
count : integer;
begin
LastMode := 'Inherit';
try
count := 0;
repeat
inc (count);
if Doc = nil then begin
FApplication.ProcessMessages;
Sleep (100);
continue;
end else if Doc.body = nil then begin
FApplication.ProcessMessages;
Sleep (100);
continue;
end;
if EditOn then begin
Doc.body.setAttribute('contentEditable','true',0);
Doc.designMode := 'On'; //kt
FEditable:=true;
//SetFocus;
end else begin
Doc.body.setAttribute('contentEditable','false',0);
Doc.designMode := 'Off'; //kt
FEditable:=false;
end;
LastMode := Doc.designMode;
until (LastMode <> 'Inherit') or (count > 20);
except
on E:Exception do EError('Error switching into HTML editing state',E);
end;
end;
procedure THtmlObj.SetBackgroundColor(Color:TColor);
begin
if Doc=nil then exit;
//WaitLoad(true); //kt
WaitForDocComplete;
if Doc.body=nil then exit;
Doc.body.style.backgroundColor := ColorToMSHTMLStr(Color);
end;
function THtmlObj.GetBackgroundColor : TColor;
begin
Result := clBlack; //default;
if Doc=nil then exit;
if Doc.body=nil then exit;
Result := MSHTMLStrToColor(Doc.body.style.backgroundColor);
end;
function THtmlObj.ColorToMSHTMLStr(color : TColor) : string;
//Note: TColor stores colors lo-byte --> hi-byte as RGB
//Function returns '#RRGGBB'
var tempColor : TMGColor;
begin
tempColor.Color := color;
Result := '#'+
IntToHex(tempColor.RGBColor.R,2)+
IntToHex(tempColor.RGBColor.G,2)+
IntToHex(tempColor.RGBColor.B,2);
end;
function THtmlObj.MSHTMLStrToColor(MSHTMLColor : string) : TColor;
//Function converts '#RRGGBB' -- TColor
//Note: TColor stores colors lo-byte --> hi-byte as RGB
var tempColor : TMGColor;
strHexRed,strHexGreen,strHexBlue : string[2];
begin
Result := clBlack; //FIX!!!! IMPLEMENT LATER...
if Pos('#',MSHTMLColor)=1 then begin
// MSHTMLColor := MidStr(MSHTMLColor,2,99);
strHexRed := MidStr(MSHTMLColor,2,2);
strHexGreen := MidStr(MSHTMLColor,4,2);
strHexBlue := MidStr(MSHTMLColor,6,2);
tempColor.RGBColor.R := StrToIntDef('$'+StrHexRed,0);
tempColor.RGBColor.G := StrToIntDef('$'+StrHexGreen,0);
tempColor.RGBColor.B := StrToIntDef('$'+StrHexBlue,0);
Result := tempColor.Color;
//NOTE: This function has not yet been tested....
end;
end;
procedure THtmlObj.ToggleBullet;
begin
if DOC=nil then exit;
//SpecialCommand(IDM_UnORDERLIST,false,true,false,Null);
DOC.execCommand('InsertUnorderedList',false,null);
Modified:=true;
end;
procedure THtmlObj.ToggleItalic;
begin
if DOC=nil then exit;
DOC.execCommand('Italic',false,null);
Modified:=true;
end;
procedure THtmlObj.ToggleBold;
begin
if DOC=nil then exit;
DOC.execCommand('Bold',false,null);
Modified:=true;
end;
procedure THtmlObj.ToggleNumbering;
begin
if DOC=nil then exit;
DOC.execCommand('InsertOrderedList',false,null);
// SpecialCommand(IDM_ORDERLIST,false,true,false,Null);
Modified:=true;
end;
procedure THtmlObj.ToggleUnderline;
begin
if DOC=nil then exit;
DOC.execCommand('Underline',false,null);
Modified:=true;
end;
procedure THtmlObj.ToggleSubscript;
begin
if DOC=nil then exit;
DOC.execCommand('Subscript',False,0);
Modified:=true;
end;
procedure THtmlObj.ToggleSuperscript;
begin
if DOC=nil then exit;
DOC.execCommand('Superscript',False,0);
Modified:=true;
end;
procedure THtmlObj.Indent;
begin
if DOC=nil then exit;
DOC.ExecCommand('Indent',false,0);
Modified:=true;
end;
procedure THtmlObj.Outdent;
begin
if DOC=nil then exit;
DOC.ExecCommand('Outdent',false,0);
Modified:=true;
end;
procedure THtmlObj.AlignLeft;
begin
if DOC=nil then exit;
DOC.ExecCommand('JustifyLeft',false,0);
Modified:=true;
end;
procedure THtmlObj.AlignRight;
begin
if DOC=nil then exit;
DOC.ExecCommand('JustifyRight',false,0);
Modified:=true;
end;
procedure THtmlObj.AlignCenter;
begin
if DOC=nil then exit;
DOC.ExecCommand('JustifyCenter',false,0);
Modified:=true;
end;
procedure THtmlObj.TextForeColorDialog;
begin
if ColorDialog = nil then begin
ColorDialog := TColorDialog.Create(self);
end;
if ColorDialog.Execute then begin
SetTextForegroundColor(ColorDialog.Color);
end;
Modified:=true;
end;
procedure THtmlObj.TextBackColorDialog;
begin
if ColorDialog = nil then begin
ColorDialog := TColorDialog.Create(self);
end;
if ColorDialog.Execute then begin
SetTextBackgroundColor(ColorDialog.Color);
end;
Modified:=true;
end;
procedure THtmlObj.SetTextForegroundColor(Color:TColor);
begin
if DOC=nil then exit;
DOC.ExecCommand('ForeColor',false,Color);
Modified:=true;
end;
function THtmlObj.GetTextForegroundColor:TColor;
var Background : OleVariant;
vt : TVarType;
begin
Result:=clWindow;
try
if DOC=nil then exit;
Background:=DOC.queryCommandValue('ForeColor');
vt:=varType(Background);
if vt<>varNull then Result:=Background;
except
on E:Exception do EError('Error retrieving foreground color',E);
end;
end;
procedure THtmlObj.SetTextBackgroundColor(Color:TColor);
begin
if DOC=nil then exit;
DOC.ExecCommand('BackColor',false,Color);
Modified:=true;
end;
function THtmlObj.GetTextBackgroundColor:TColor;
var Background : OleVariant;
vt : TVarType;
begin
Result:=clWindow;
try
if DOC=nil then exit;
Background:=DOC.queryCommandValue('BackColor');
vt:=varType(Background);
if vt<>varNull then Result:=Background;
except
on E:Exception do EError('Error retrieving background color',E);
end;
end;
procedure THtmlObj.FontDialog;
begin
DoCommand(IDM_FONT);
Modified:=true;
end;
function THtmlObj.GetFontSize : integer;
var FontSize : OleVariant;
vt : TVarType;
begin
FontSize:=Doc.queryCommandValue('FontSize');
vt:=varType(FontSize);
if vt<>varNull then Result := FontSize*FontScale
else Result :=12*FontScale; //kt
end;
procedure THtmlObj.SetFontSize (Size : integer);
begin
if Doc=nil then exit;
Doc.ExecCommand('FontSize', false, Size div FontScale);
end;
function THtmlObj.GetFontName : string;
var FontName :OleVariant;
vt : TVarType;
begin
if DOC=nil then exit;
FontName:=DOC.queryCommandValue('FontName');
vt:=varType(FontName);
if vt<>varNull then Result := FontName
else Result :='Times New Roman'; //kt
end;
procedure THtmlObj.SetFontName (Name : string);
begin
if DOC=nil then exit;
DOC.ExecCommand('FontName', false, Name);
end;
function THtmlObj.SelStart:integer;
var TextRange:IHtmlTxtRange;
begin
Result:=0;
TextRange:=GetTextRange;
if TextRange=nil then exit;
Result:=Abs(Integer(TextRange.move('character',-MaxTextLength)));
end;
function THtmlObj.SelEnd:integer;
var TextRange:IHtmlTxtRange;
begin
Result:=0;
TextRange:=GetTextRange;
if TextRange=nil then exit;
Result:=Abs(Integer(TextRange.MoveEnd('character',-MaxTextLength)));
end;
function THtmlObj.SelLength:integer;
begin
Result:=SelEnd-SelStart;
end;
function THtmlObj.GetTextRange:IHtmlTxtRange;
begin
Result:=nil;
try
if DOC=nil then exit;
while DOC.body=nil do begin
//WaitLoad(true); //kt
WaitForDocComplete;
if DOC.body=nil then begin
if MessageDlg('Wait for document loading?',mtConfirmation,
[mbOK,mbCancel],0) <> mrOK then begin
exit;
end;
end;
end;
if (DOC.Selection.type_='Text') or (DOC.Selection.type_='None') then begin
Result:=DOC.Selection.CreateRange as IHtmlTxtRange;
end;
except
on E:Exception do EError('This type of selection cannot be processed',E);
end;
end;
function THtmlObj.GetSelText:string;
var TextRange:IHtmlTxtRange;
begin
Result:='';
TextRange:=GetTextRange;
if TextRange=nil then
exit;
Result:=TextRange.text;
end;
procedure THtmlObj.SetSelText (HTMLText : string);
begin
ReplaceSelection(HTMLText);
end;
procedure THtmlObj.ReplaceSelection(HTML:string);
var TextRange:IHtmlTxtRange;
begin
try
TextRange:=GetTextRange;
if TextRange=nil then exit;
TextRange.PasteHTML(HTML);
Modified:=true;
except
on E:Exception do begin
// implement later... ShortenString(HTML,80);
EError('Error pasting HTML'+nl+
'Microsoft HTML refuses to paste this string:'+nl+
HTML+nl,E);
end;
end;
end;
function THtmlObj.MoveCaretToEnd : boolean;
//kt added
var //TextRange:IHtmlTxtRange;
count : integer;
begin
if not assigned (FTMGDisplayPointer) then begin
Result := false;
exit;
end;
Result:=(S_OK = FTMGDisplayPointer.MoveUnit(DISPLAY_MOVEUNIT_BottomOfWindow,0));
count := 0;
repeat
Result:=(S_OK = FTMGDisplayPointer.MoveUnit(DISPLAY_MOVEUNIT_NextLine,-1));
inc (count);
until (Result = false) or (count > 500);
Result:=(S_OK = FTMGDisplayPointer.MoveUnit(DISPLAY_MOVEUNIT_CurrentLineEnd,0));
Result:=(S_OK = FCaret.MoveCaretToPointer(FTMGDisplayPointer,
integer(FALSE),
CARET_DIRECTION_SAME));
{
SendMessage(FmsHTMLwinHandle, WM_KEYDOWN, VK_END, 0);
SendMessage(FmsHTMLwinHandle, WM_KEYUP, VK_END, 0);
SendMessage(FmsHTMLwinHandle, WM_KEYDOWN, VK_END, 0);
SendMessage(FmsHTMLwinHandle, WM_KEYUP, VK_END, 0);
}
end;
function THtmlObj.MoveCaretToPos(ScreenPos: TPoint) : HRESULT;
//kt added entire function
var OutTemp : DWORD;
begin
if not assigned (FTMGDisplayPointer) then exit;
FTMGDisplayPointer.moveToPoint(ScreenPos, COORD_SYSTEM_GLOBAL, nil, HT_OPT_AllowAfterEOL, OutTemp);
Result := FCaret.MoveCaretToPointer(FTMGDisplayPointer,Integer(True),CARET_DIRECTION_INDETERMINATE);
FCaret.Show(Integer(True));
end;
procedure THtmlObj.InsertTextAtCaret(Text : AnsiString);
//kt added. Note: inserts external format (not HTML markup)
var P : PWideChar;
begin
P := StringToOleStr(Text);
FCaret.InsertText(P,Length(Text))
end;
procedure THtmlObj.Loaded;
begin
inherited Loaded;
end;
function THtmlObj.GetTextLen : integer;
begin
Result := Length(GetText);
end;
procedure THtmlObj.ReassignKeyboardHandler(TurnOn : boolean);
{assign HTML keyboard handler to HTML component; restore standard if TurnOn=false}
begin
if TurnOn then begin
FApplication.OnMessage := GlobalMsgHandler;
end else begin
FApplication.OnMessage := FOrigAppOnMessage;
end;
end;
procedure THtmlObj.GlobalMsgHandler(var Msg: TMsg; var Handled: Boolean);
{NOTE: This message handler will receive ALL messages directed to CPRS. I
have to do this, because something is filtering messages before they
get to this THTMLObj object. My goal is to do as little here as possible,
and let the OnMessage for THTMLObj (found in EmbeddedED) take care of the rest.
NOTE: This should get activated by OnFocus for object, and deactivated
by OnBlur, so it actually should only get messages when focused. }
var
i : Integer;
NewMsg : TMessage;
function TransformMessage (WinMsg : TMsg) : TMessage;
begin
Result.Msg := WinMsg.message;
Result.WParam := WinMsg.wParam;
Result.LParam := WinMsg.lParam;
Result.Result := 0;
end;
begin
Handled:=false; //default to not handled
if (Msg.Message=WM_KEYDOWN) then begin
if (Msg.WParam=VK_UP) or (Msg.WParam=VK_DOWN) or (Msg.WParam=VK_TAB) then begin
NewMsg := TransformMessage(Msg);
SubMessageHandler(NewMsg);
Handled := (NewMsg.Result = 1);
end;
end;
end;
procedure THtmlObj.SubMessageHandler(var Msg: TMessage);
//Called from parent's EDMessageHandler, or from GlobalMsgHandler
var i : Integer;
WinControl : TWinControl;
begin
Msg.Result := 0; //default to not handled
if not ((Msg.Msg=WM_KEYDOWN) or
(Msg.Msg=WM_KEYUP) or
(Msg.Msg=WM_RBUTTONUP) ) then exit; //Speedy exit of non-handled messages
case Msg.Msg of
WM_RBUTTONUP : begin
if CtrlToBeProcessed then begin
CtrlToBeProcessed := false;
exit; //Ctrl-right click is ignored
end;
if assigned(PopupMenu) then PopupMenu.Popup(Mouse.CursorPos.X,Mouse.CursorPos.Y);
Msg.Result := 1; //Handled
exit;
end;
WM_KEYDOWN : begin
GetSystemTimeAsFileTime(KeyPressTime);
KeyStruck := true;
//beep(200,50);
case Msg.WParam of
VK_ESCAPE : begin
if Assigned(PrevControl) then begin
AllowNextBlur := true;
PrevControl.SetFocus;
end;
end;
VK_CONTROL : begin
CtrlToBeProcessed:=true;
Msg.Result := 1; //Handled
exit;
end;
VK_SHIFT : begin
ShiftToBeProcessed:=true;
Msg.Result := 1; //Handled
exit;
end;
VK_TAB : begin
if (ShiftToBeProcessed and CtrlToBeProcessed) then begin
//This isn't working for some reason...
for i := 0 to 5 do begin
PostMessage(FmsHTMLwinHandle, WM_KEYDOWN, VK_LEFT, 0);
end;
ShiftToBeProcessed := false;
CtrlToBeProcessed := false;
end else if ShiftToBeProcessed then begin
if Assigned(PrevControl) then begin
AllowNextBlur := true;
PrevControl.SetFocus;
end;
ShiftToBeProcessed := false;
end else if CtrlToBeProcessed then begin
if Assigned(NextControl) then begin
AllowNextBlur := true;
NextControl.SetFocus;
end;
CtrltoBeProcessed := false;
end else begin
for i := 0 to 5 do begin
PostMessage(FmsHTMLwinHandle, WM_KEYDOWN, VK_SPACE, 0);
end;
end;
Msg.Result := 1; //Handled
end;
{
VK_RETURN : if CtrlReturnToBeProcessed then begin
Msg.Result := 1; //Handled
CtrlReturnToBeProcessed := false;
end else if CtrlToBeProcessed then begin
Msg.Result := 1; //Handled
CtrlToBeProcessed := False;
CtrlReturnToBeProcessed := true;
//PostMessage(Msg.hwnd, WM_KEYUP, VK_CONTROL, 0);
end else if ShiftToBeProcessed=false then begin
//kt if not FEditable then exit;
keybd_event(VK_SHIFT,0,0,0);
keybd_event(VK_RETURN,0,0,0);
keybd_event(VK_SHIFT,0,KEYEVENTF_KEYUP,0);
Msg.Result := 1; //Handled
end;
}
Ord('B') : if CtrlToBeProcessed then begin
//kt if not FEditable then exit;
ToggleBold;
Msg.Result := 1; //Handled
exit;
end;
Ord('U') : if CtrlToBeProcessed then begin
//kt if not FEditable then exit;
ToggleUnderline;
Msg.Result := 1; //Handled
exit;
end;
Ord('I') : if CtrlToBeProcessed then begin
//kt if not FEditable then exit;
ToggleItalic;
Msg.Result := 1; //Handled
end;
end; {case}
end;
WM_KEYUP : begin
case Msg.WParam of
VK_CONTROL : begin
CtrlToBeProcessed:=false;
Msg.Result := 1; //Handled
if CtrlReturnToBeProcessed then begin
PostMessage(FmsHTMLwinHandle, WM_KEYDOWN, VK_RETURN, 0);
end;
exit;
end;
VK_SHIFT : begin
ShiftToBeProcessed:=false;
Msg.Result := 1; //Handled
exit;
end;
end; {case}
exit;
end;
end; {case}
end;
procedure THtmlObj.HandleBlur(Sender: TObject);
//kt added function
function RecentKeyPressed : boolean;
var NowTime : FILETIME; //kt
KeyTime,NowTime2 : LARGE_INTEGER;
Delta : int64;
begin
GetSystemTimeAsFileTime(NowTime);
NowTime2.LowPart := NowTime.dwLowDateTime;
NowTime2.HighPart := NowTime.dwHighDateTime;
KeyTime.LowPart := KeyPressTime.dwLowDateTime;
KeyTime.HighPart := KeyPressTime.dwHighDateTime;
Delta := floor( (NowTime2.QuadPart - KeyTime.QuadPart) / 100000);
Result := (Delta < 100) and (Delta > 0);
end;
begin
//kt Handle loss of focus when attempting to cursor above top line, or below bottom line.
if (not AllowNextBlur) and RecentKeyPressed then begin //kt entire block
SetFocusToDoc;
//beep(880,100);
KeyPressTime.dwLowDateTime := 0;
KeyPressTime.dwHighDateTime := 0;
exit;
end;
AllowNextBlur := false;
SetMsgActive(false);
end;
function THtmlObj.SubFocusHandler(fGotFocus: BOOL): HResult;
begin
SetMsgActive(fGotFocus);
end;
function THtmlObj.GetActive : boolean;
begin
Result := TWinControl(Owner).Visible;
end;
initialization
finalization
end.