unit TMGHTML; (* 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. *) interface uses SysUtils, WinTypes, Dialogs, StdCtrls, Menus, EmbeddedED, ActiveX, MSHTMLEvents, SHDocVw, {MSHTML,} MSHTML_EWB, AppEvnts, IeConst,Messages,Classes,Forms,Graphics; const CGID_MSHTML:TGUID='{DE4BA900-59CA-11CF-9592-444553540000}'; IID_IOleCommandTarget:TGUID='{B722BCCB-4E68-101B-A2BC-00AA00404770}'; CGID_WebBrowser:TGUID='{ED016940-BD5B-11cf-BA4E-00C04FD70816}'; FontScale=3; 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 FEditable: boolean; Modified: boolean; DocEvents: TMSHTMLHTMLDocumentEvents; //elh WinEvents: TMSHTMLHTMLWindowEvents2; CtrlReturnToBeProcessed: boolean; CtrlToBeProcessed : boolean; ShiftToBeProcessed : boolean; ColorDialog: TColorDialog; FOrigAppOnMessage : TMessageEvent; FCustKeyboardHandlerOn: boolean; FActive : boolean; FApplication : TApplication; procedure WaitLoad(peek:boolean); function GetEditableState : boolean; procedure SetEditableState (EditOn : boolean); procedure SetBackgroundColor(Color:TColor); function GetBackgroundColor : TColor; procedure SetTextBackgroundColor(Color:TColor); function GetTextBackgroundColor : TColor; procedure SetTextForegroundColor(Color:TColor); function GetTextForegroundColor : TColor; function GetFontSize : integer; procedure SetFontSize (Size : integer); function GetFontName : string; procedure SetFontName (Name : string); procedure SetActive (Active : boolean); function GetHTMLText:string; procedure SetHTMLText(HTML:String); function GetText:string; procedure SetText(HTML:string); function GetSelText:string; procedure SetSelText (HTMLText : string); procedure DefineDocEvents; //elh procedure DefineWinEvents; procedure OnDocFocusOut(Sender:TObject); //elh procedure SetDefaultFont; function ColorToMSHTMLStr(color : TColor) : string; function MSHTMLStrToColor(MSHTMLColor : string) : TColor; //Events ------------------ procedure NavigateComplete2(Sender: TObject;const pDisp: IDispatch; var URL: OleVariant); procedure LocalMessageHandler(var Msg: TMsg; var Handled: Boolean); procedure CompleteLoading; procedure ProcessLoadMessages; function SpecialCommand(Cmd : Cardinal; PromptUser : boolean; editModeOnly : boolean; bTriEditCommandGroup : boolean; InputArgs : OleVariant) : HRESULT; function HrExecCommand(ucmdID: cardinal; const pVarIn: OleVariant; var pVarOut: OleVariant; bPromptUser, bTriEditCmdGroup: boolean): HResult; procedure ReassignKeyboardHandler(TurnOn : boolean); {end private} public TheDoc: IHTMLDocument2; //MSHTML HTML Document 2 interface // HTMLEvents: HTMLWindowsEvents2; TheWind: IHTMLWindow2; DocCmd,WebCmd: IOleCommandTarget; //MSHTML IOLECommandTarget interface PopupMenu: TPopupMenu; DefaultFontSize : Integer; DefaultFontName : string; constructor Create(Owner:TComponent; Application : TApplication); destructor Destroy; override; //Properties --- property Editable : boolean read GetEditableState write SetEditableState; property BackgroundColor : TColor read GetBackgroundColor write SetBackgroundColor; property TextBackgroundColor : TColor read GetTextBackgroundColor write SetTextBackgroundColor; property TextForegroundColor : TColor read GetTextForegroundColor write SetTextForegroundColor; property FontSize : integer read GetFontSize write SetFontSize; property FontName : string read GetFontName write SetFontName; property HTMLText:string read GetHTMLText write SetHTMLText; property Text:string read GetText write SetText; property Active : boolean read FActive write SetActive; property SelText : string read GetSelText write SetSelText; //Methods ------- function GetTextLen : integer; procedure Clear; procedure LoadFile(FileName:string); 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 FontDialog; procedure TextForeColorDialog; procedure TextBackColorDialog; procedure SetSelection(Start,Length:integer); function GetTextRange:IHtmlTxtRange; function SelStart:integer; function SelEnd:integer; function SelLength:integer; procedure ClearSelection; procedure ReplaceSelection(HTML:string); {end public} end; type THtmlEditor=class(THtmlObj); implementation uses WinProcs,Controls,Variants,Clipbrd, StrUtils; const MaxTextLength = 100; nl = #13#10; procedure EError(EText : string; E : Exception); begin MessageDlg(EText,mtError,[mbOK],0); end; constructor THtmlObj.Create(Owner:TComponent; Application : TApplication); begin inherited Create(Owner); TheDoc:=nil; DocCmd:=nil; WebCmd:=nil; ColorDialog := nil; FApplication := Application; FOrigAppOnMessage := Application.OnMessage; FEditable := false; DefaultFontSize := 10; OnBlur := OnDocFocusOut; DefaultFontName := 'Times New Roman'; FCustKeyboardHandlerOn := false; OnNavigateComplete2 := NavigateComplete2; end; destructor THtmlObj.Destroy; begin SetActive(false); // ReassignKeyboardHandler(false); ColorDialog.Free; inherited Destroy; end; procedure THtmlObj.LoadFile(FileName:string); var OldWidth,OldHeight:integer; begin try self.Cursor := crHourGlass; OldHeight:=Height; OldWidth:=Width; Navigate(FileName); Width:=OldWidth; {due to a bug that sizes down HTML components on start}{Oct 15, 2001} Height:=OldHeight; TheDoc:=nil; if DocCmd<>nil then begin DocCmd._Release; DocCmd:=nil; end; WaitLoad(true); //kt self.Cursor := crDefault; //kt except on E:Exception do begin EError('Cannot load '+Filename,E); end; end; end; procedure THtmlObj.WaitLoad(peek:boolean); begin try TheDoc:=Document as IHTMLDocument2; while TheDoc=nil do begin if peek then ProcessLoadMessages else exit; TheDoc:=Document as IHTMLDocument2; end; repeat ControlInterface.QueryInterface(IID_IOleCommandTarget,WebCmd); until WebCmd<>nil; repeat TheDoc.QueryInterface(IOleCommandTarget,DocCmd); until DocCmd<>nil; repeat TheWind:=TheDoc.parentWindow; until TheWind<>nil; while (TheDoc=nil)or((theDoc.ReadyState<>'complete')and(theDoc.ReadyState<>'interactive')) do begin {remove messages that should not be processed while the element is loading} {TheDoc can become nil when switching applications!} if TheDoc=nil then MessageBeep(0); {this beep is sounded while page is loading while control is no longer in forefront} if peek then ProcessLoadMessages else exit; end; except on E:Exception do EError('Error loading the document',E); end; end; procedure THtmlObj.ProcessLoadMessages; var msg:TMsg; MessageQueue:array of TMsg; m:integer; begin while PeekMessage(msg,0,wm_KeyFirst,wm_KeyLast,pm_Remove) do; {remove keyboard input first} while PeekMessage(msg,0,wm_MouseFirst,wm_MouseLast,pm_Remove) do; {remove mouse input} while PeekMessage(msg,0,wm_Close,wm_Close,pm_Remove) do; {disallow closing the application} while PeekMessage(msg,0,wm_ActivateApp,wm_ActivateApp,pm_Remove) do; {disallow activating the application} //ktwhile PeekMessage(msg,0,wm_User,cm_LastUserMessage,pm_Remove) do begin while PeekMessage(msg,0,wm_User,wm_User+$200,pm_Remove) do begin SetLength(MessageQueue,length(MessageQueue)+1); MessageQueue[length(MessageQueue)-1]:=msg; end; forms.Application.ProcessMessages; {process messages needed to complete navigation} for m:=1 to length(MessageQueue) do begin msg:=MessageQueue[m-1]; PostMessage(msg.hwnd,msg.message,msg.WParam,msg.lParam); end; end; function THtmlObj.SpecialCommand(Cmd:Cardinal;PromptUser:boolean; editModeOnly:boolean;bTriEditCommandGroup:boolean; InputArgs:OleVariant):HRESULT; begin Result:=HrExecCommand(Cmd,null,InputArgs,promptUser,bTriEditCommandGroup); end; function THtmlObj.HrExecCommand(ucmdID: cardinal; const pVarIn: OleVariant; var pVarOut: OleVariant; bPromptUser, bTriEditCmdGroup: boolean): HResult; var dwCmdOpt:DWORD; begin result := S_OK; if DocCmd = nil then Exit; if (bPromptUser) then dwCmdOpt := MSOCMDEXECOPT_PROMPTUSER else dwCmdOpt := MSOCMDEXECOPT_DONTPROMPTUSER; if (bTriEditCmdGroup) then result := DocCmd.Exec(@GUID_TriEditCommandGroup,ucmdID,dwCmdOpt,pVarIn,pVarOut) else result := DocCmd.Exec(@CMDSETID_Forms3,ucmdID,dwCmdOpt,pVarIn,pVarOut); end; procedure THtmlObj.SetDefaultFont; begin if DefaultFontName <> '' then SetFontName(DefaultFontName); if DefaultFontSize <> 0 then SetFontSize(DefaultFontSize); end; function THtmlObj.GetEditableState : boolean; var mode : string; begin mode := TheDoc.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 TheDoc = nil then begin FApplication.ProcessMessages; Sleep (100); continue; end else if TheDoc.body = nil then begin FApplication.ProcessMessages; Sleep (100); continue; end; if EditOn then begin TheDoc.body.setAttribute('contentEditable','true',0); TheDoc.designMode := 'On'; //kt FEditable:=true; //SetFocus; end else begin TheDoc.body.setAttribute('contentEditable','false',0); TheDoc.designMode := 'Off'; //kt FEditable:=false; end; LastMode := TheDoc.designMode; until (LastMode <> 'Inherit') or (count > 20); except on E:Exception do EError('Error switching into HTML editing state',E); end; end; procedure THtmlObj.ToggleBullet; begin if TheDoc=nil then exit; //SpecialCommand(IDM_UnORDERLIST,false,true,false,Null); TheDoc.execCommand('InsertUnorderedList',false,null); Modified:=true; end; procedure THtmlObj.ToggleItalic; begin if TheDoc=nil then exit; //SpecialCommand(IDM_UnORDERLIST,false,true,false,Null); TheDoc.execCommand('Italic',false,null); Modified:=true; end; procedure THtmlObj.ToggleBold; begin if TheDoc=nil then exit; TheDoc.execCommand('Bold',false,null); Modified:=true; end; procedure THtmlObj.ToggleNumbering; begin if TheDoc=nil then exit; TheDoc.execCommand('InsertOrderedList',false,null); // SpecialCommand(IDM_ORDERLIST,false,true,false,Null); Modified:=true; end; procedure THtmlObj.ToggleUnderline; begin if TheDoc=nil then exit; TheDoc.execCommand('Underline',false,null); Modified:=true; end; procedure THtmlObj.ToggleSubscript; begin if TheDoc=nil then exit; TheDoc.execCommand('Subscript',False,0); Modified:=true; end; procedure THtmlObj.ToggleSuperscript; begin if TheDoc=nil then exit; TheDoc.execCommand('Superscript',False,0); Modified:=true; end; procedure THtmlObj.Indent; begin if TheDoc=nil then exit; TheDoc.ExecCommand('Indent',false,0); Modified:=true; end; procedure THtmlObj.Outdent; begin if TheDoc=nil then exit; TheDoc.ExecCommand('Outdent',false,0); Modified:=true; end; procedure THtmlObj.AlignLeft; begin if TheDoc=nil then exit; TheDoc.ExecCommand('JustifyLeft',false,0); Modified:=true; end; procedure THtmlObj.AlignRight; begin if TheDoc=nil then exit; TheDoc.ExecCommand('JustifyRight',false,0); Modified:=true; end; procedure THtmlObj.AlignCenter; begin if TheDoc=nil then exit; TheDoc.ExecCommand('JustifyCenter',false,0); Modified:=true; end; procedure THtmlObj.SetBackgroundColor(Color:TColor); begin if TheDoc=nil then exit; WaitLoad(true); //kt if TheDoc.body=nil then exit; TheDoc.body.style.backgroundColor := ColorToMSHTMLStr(Color); end; function THtmlObj.GetBackgroundColor : TColor; begin Result := clBlack; //default; if TheDoc=nil then exit; if TheDoc.body=nil then exit; Result := MSHTMLStrToColor(TheDoc.body.style.backgroundColor); 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.SetTextBackgroundColor(Color:TColor); begin if TheDoc=nil then exit; TheDoc.ExecCommand('BackColor',false,Color); Modified:=true; end; function THtmlObj.GetTextBackgroundColor:TColor; var Background : OleVariant; vt : TVarType; begin Result:=clWindow; try if TheDoc=nil then exit; Background:=TheDoc.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.SetTextForegroundColor(Color:TColor); begin if TheDoc=nil then exit; TheDoc.ExecCommand('ForeColor',false,Color); Modified:=true; end; function THtmlObj.GetTextForegroundColor:TColor; var Background : OleVariant; vt : TVarType; begin Result:=clWindow; try if TheDoc=nil then exit; Background:=TheDoc.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.FontDialog; begin SpecialCommand(IDM_FONT,True,True,False,Null); Modified:=true; end; function THtmlObj.GetFontSize : integer; var FontSize : OleVariant; vt : TVarType; begin FontSize:=TheDoc.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 TheDoc=nil then exit; TheDoc.ExecCommand('FontSize', false, Size div FontScale); end; function THtmlObj.GetFontName : string; var FontName :OleVariant; vt : TVarType; begin if TheDoc=nil then exit; FontName:=TheDoc.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 TheDoc=nil then exit; TheDoc.ExecCommand('FontName', false, Name); end; procedure THtmlObj.SetActive (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.NavigateComplete2(Sender: TObject;const pDisp: IDispatch; var URL: OleVariant); begin CompleteLoading; end; procedure THtmlObj.CompleteLoading; begin Waitload(false); {used only to set up interface variables} SetActive(true); // ReassignKeyboardHandler(true); //DefineDocEvents; //elh end; procedure THtmlObj.DefineDocEvents; //NOTE: When this function is called, keyboard strokes fire this event, but then // the characters never show up in the editor window as having been typed. begin // if DocEvents<>nil then Events.Free; // DocEvents := TMSHTMLHTMLDocumentEvents.Create(Self); // DocEvents.Connect(IUnknown(Document)); // DocEvents.OnFocusOut:=OnDocFocusOut; end; procedure THtmlObj.DefineWinEvents; //NOTE: When this function is called, keyboard strokes fire this event, but then // the characters never show up in the editor window as having been typed. begin //if DocEvents<>nil then Events.Free; //WinEvents := TMSHTMLHTMLWindowEvents2.Create(Self); //DocEvents.Connect(IUnknown(Document)); //DocEvents.OnFocusOut:=OnDocFocusOut; end; procedure THtmlObj.OnDocFocusOut(Sender:TObject); begin messagedlg('This is the new one', mtWarning,mbOKCancel,0); 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 := LocalMessageHandler; FCustKeyboardHandlerOn := true; end else begin FApplication.OnMessage := FOrigAppOnMessage; FCustKeyboardHandlerOn := false; end; end; procedure THtmlObj.LocalMessageHandler(var Msg: TMsg; var Handled: Boolean); var Cursor : TPoint; i : Integer; begin Handled:=false; //default to not handled exit; if not FCustKeyboardHandlerOn then exit; if not ((Msg.Message=WM_KEYDOWN) or (Msg.Message=WM_KEYUP) or (Msg.Message=WM_RBUTTONUP) ) then exit; //Speedy exit of non-handled messages case Msg.Message of WM_RBUTTONUP : begin Cursor := ScreenToClient(Msg.pt); //Ignore message if mouse not over this HTML control if (Cursor.X<0) or (Cursor.X>Width) or (Cursor.Y<0) or (Cursor.Y>Height) then exit; 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); Handled:=true; exit; end; WM_KEYDOWN : begin case Msg.WParam of VK_CONTROL : begin CtrlToBeProcessed:=true; Handled:=true; exit; end; VK_SHIFT : begin ShiftToBeProcessed:=true; Handled:=true; exit; end; VK_TAB : begin //kt if not FEditable then exit; if ShiftToBeProcessed then begin for i := 0 to 5 do begin PostMessage(Msg.hwnd, WM_KEYDOWN, VK_LEFT, 0); end; end else begin for i := 0 to 5 do begin PostMessage(Msg.hwnd, WM_KEYDOWN, VK_SPACE, 0); end; end; Handled:=true; end; VK_RETURN : if CtrlReturnToBeProcessed then begin Handled:=false; CtrlReturnToBeProcessed := false; end else if CtrlToBeProcessed then begin Handled:=true; 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); Handled:=true; end; Ord('B') : if CtrlToBeProcessed then begin //kt if not FEditable then exit; ToggleBold; Handled:=true; exit; end; Ord('U') : if CtrlToBeProcessed then begin //kt if not FEditable then exit; ToggleUnderline; Handled:=true; exit; end; Ord('I') : if CtrlToBeProcessed then begin //kt if not FEditable then exit; ToggleItalic; Handled:=true; end; end; {case} end; WM_KEYUP : begin case Msg.WParam of VK_CONTROL : begin CtrlToBeProcessed:=false; Handled:=true; if CtrlReturnToBeProcessed then begin PostMessage(Msg.hwnd, WM_KEYDOWN, VK_RETURN, 0); end; exit; end; VK_SHIFT : begin ShiftToBeProcessed:=false; Handled:=true; exit; end; end; {case} //messagedlg('I''m Am Not Active', mtWarning,mbOKCancel,0); exit; end; end; {case} end; procedure THtmlObj.SetHTMLText(Html : String); //After this command, Copy and Paste will not work -- ?? why? Still true?? var V : OleVariant; V2 : variant; body : IHTMLElement; status : string; temp : string; begin try Stop; TheDoc:=Document as IHTMLDocument2; if TheDoc=nil then exit; body := TheDoc.body; if UpperCase(TheDoc.designMode) <> 'ON' then begin TheDoc.designMode := 'on'; repeat //NOTE: potential endless loop. Perhaps loop only status='loading'? status := TheDoc.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 := TheDoc.body; if (body = nil) then begin //Do so stuff to get IE to make a 'body'. V2 := VarArrayCreate([0, 0], VarVariant); V2[0] := ' '; //Html; TheDoc.Write(PSafeArray(System.TVarData(V2).VArray)); body := TheDoc.body; TheDoc.close; repeat status := TheDoc.readyState; //For possible status values, see above) if status <> 'complete' then FApplication.ProcessMessages; until (status = 'complete') or (status='interactive') or (status='loaded'); body := TheDoc.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; (* procedure THtmlObj.SetHTMLText(Html : String); //After this command, Copy and Paste will not work -- ?? why? Still true?? var V : OleVariant; V2 : variant; status : string; begin try if (TheDoc=nil) or (TheDoc.body=nil) then begin Stop; V := Document;// as IHTMLDocument2; V.Open; V.Clear; V.Write(Html); V.Close; //Fix: Need a way to set font and size in this operation... end else begin TheDoc.body.innerHTML := Html; end; 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:=''; if TheDoc=nil then exit; WS:=TheDoc.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 TheDoc=nil then exit; WS:=TheDoc.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 (TheDoc=nil)or(TheDoc.body=nil) then SetHTMLText(HTML) else TheDoc.body.innerHTML:=HTML; end; function THtmlObj.GetTextLen : integer; begin Result := Length(GetText); end; procedure THtmlObj.Clear; begin SetHTMLText(''); SetDefaultFont; 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 TheDoc=nil then exit; while TheDoc.body=nil do begin WaitLoad(true); if TheDoc.body=nil then begin if MessageDlg('Wait for document loading?',mtConfirmation, [mbOK,mbCancel],0) <> mrOK then begin exit; end; end; end; if (TheDoc.Selection.type_='Text') or (TheDoc.Selection.type_='None') then begin Result:=TheDoc.Selection.CreateRange as IHtmlTxtRange; end; except on E:Exception do EError('This type of selection cannot be processed',E); end; end; procedure THtmlObj.SetSelection(Start,Length:integer); var TextRange:IHtmlTxtRange; l : integer ; //kt begin try if TheDoc=nil then exit; TheDoc.Selection.Empty; TextRange:=GetTextRange; if TextRange=nil then exit; TextRange.collapse(true); l:=TextRange.moveEnd('character',Start+Length); l:=TextRange.moveStart('character',Start); TextRange.select; except on E:Exception do EError('Error setting HTML selection'+nl+ 'Start='+IntToStr(Start)+nl+ 'Length='+IntToStr(Length),E); end; end; procedure THtmlObj.ClearSelection; begin if TheDoc=nil then exit; TheDoc.Selection.Clear; Modified:=true; 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.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; 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.SetBorder(Border:boolean); begin if TheDoc=nil then exit; if TheDoc.body=nil then exit; if not Border then begin if not FEditable then begin TheDoc.body.style.backgroundColor := clYellow; //kt TheDoc.body.style.borderStyle:='none'; TheDoc.body.style.borderWidth:='thin'; TheDoc.body.style.borderColor:='white'; end; if FEditable then begin TheDoc.body.style.backgroundColor := clRed; //kt TheDoc.body.style.borderStyle:='none'; // TheDoc.body.filters. TheDoc.body.style.borderWidth:='thin'; TheDoc.body.style.borderColor:='blue'; end; end; if Border then begin if not FEditable then begin TheDoc.body.style.borderStyle:='solid'; TheDoc.body.style.borderWidth:='thin'; TheDoc.body.style.borderColor:='silver'; end; if FEditable then begin //TheDoc.body.style.backgroundColor := ColorToStr(clLime); TheDoc.body.style.backgroundColor := 'BtnFace'; TheDoc.body.style.borderStyle:='solid'; TheDoc.body.style.borderWidth:='thin'; TheDoc.body.style.borderColor:='green'; end; end; end; *) initialization finalization end.