using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
namespace onlyconnect
{
///
/// Implements MSHTML as active document in a control
/// With thanks to Lutz Roeder
///
/// also thanks to Steven Wood for the HTML Event handling idea
/// and code.
///
/// and thanks to Christopher Slee for the region marking
///
/// and thanks to James Hancock for ideas/code re. making this a better-behaved winform
/// control.
///
/// and thanks to Peter Šulek (terrorix@centrum.sk) for the IHTMLTable interfaces
///
/// This version does not require the Microsoft.mshtml Primary Interop Assembly.
///
/// There is no specific licence but this demo code is free to use for any purpose.
/// It is supplied with no warranty whatsoever, use at your own risk.
/// Comments, improvements, suggestions -
/// email tim@itwriting.com
/// or visit the messageboard at http://www.itwriting.com/htmleditor.php
///
[System.ComponentModel.ToolboxItemAttribute(true)]
public class HtmlEditor : Control
{
#region class variables and constructor
private bool bLoadDocumentWhenReady = false;
private bool bLoadUrlWhenReady = false;
private bool bSetComposeSettingsWhenReady = false;
private int iLoadAttempts = 0;
private EncodingType mDocumentEncoding = EncodingType.WindowsCurrent;
private EncodingType mDefaultPreamble = EncodingType.UTF8;
private bool mIsWin98 = false;
private bool mAlwaysLoadAnsi = false;
internal bool mLinksNewWindow = false;
internal bool bNeedsActivation = false;
internal bool mAcceptsTab = false;
internal bool mAcceptsReturn = true;
internal DocHTML mDocHTML = null;
internal HtmlSite theSite;
String url = String.Empty;
String sDocument = String.Empty;
internal bool mDesignMode = false;
private bool mIsContextMenuEnabled = true;
private string mOptionKeyPath = null;
internal bool mAllowActivation = true;
internal HTMLDocument mHtmlDoc = null;
internal IHTMLElement mcurrentElement = null;
private IntPtr mDocumentHandle = IntPtr.Zero;
private bool mCreating = true;
private NativeWindow mNativeWindow = null;
private HandleWndProc mNativeDocWindow = null;
private System.Windows.Forms.Timer activateTimer;
private System.Windows.Forms.Timer createTimer;
private System.ComponentModel.IContainer components = null;
private ComposeSettings mComposeSettings = null;
internal bool mEnableActiveContent = true;
internal bool mEnableAutoComplete = false;
internal bool mDivOnEnter = false;
internal bool mShowScrollBars = true;
internal bool mEnableUrlDetection = false;
internal uint changeCookie;
private ChangeMonitor mChangeMonitor;
public HtmlEditor()
{
//initialize components
InitializeComponent();
//Detect Windows version
mIsWin98 = (System.Environment.OSVersion.Platform == PlatformID.Win32Windows);
//force creation of handle, needed to host mshtml
this.CreateControl();
//see OnHandleCreated for purpose of mCreating
mCreating = false;
}
#endregion class variables and constructor
#region Keyboard and mouse handling
internal void InvokeOnMouseDown(MouseEventArgs e)
{
this.OnMouseDown(e);
}
internal void InvokeOnDoubleClick()
{
this.OnDoubleClick(EventArgs.Empty);
}
///
/// Executes The short cut keys that should be available and handles all of the cases of design mode versus not.
///
/// The key to process.
///
private bool doShortCut(Keys Key)
{
//fire the BeforeShortcut event and cancel if necessary
BeforeShortcutEventArgs e = new BeforeShortcutEventArgs(Key);
this.OnBeforeShortcut(e);
//if cancelled, return True for Handled
return (e.Cancel);
}
#endregion
#region Override WndProc ===============
internal void InvokeWndProc(ref Message msg)
{
Message message = Message.Create(this.Handle, msg.Msg, msg.WParam, msg.LParam);
this.WndProc(ref message);
}
internal void setupWndProc()
{
if (this.mNativeDocWindow != null)
{
this.mNativeDocWindow.ReleaseHandle();
}
this.mNativeDocWindow = new HandleWndProc();
this.mNativeDocWindow.thecontrol = this;
this.mNativeDocWindow.AssignHandle(this.theSite.DocumentHandle);
}
internal void releaseWndProc()
{
if (this.mNativeDocWindow != null)
{
this.mNativeDocWindow.ReleaseHandle();
this.mNativeDocWindow = null;
}
}
override protected void WndProc(ref Message m)
{
bool isHandled = false;
switch (m.Msg)
{
case win32.WM_KEYDOWN:
//if ctrl pressed, pass it to doShortCut
if (win32.GetKeyState((int)Keys.ControlKey) < 0)
{
//check NOT alt
if (!((win32.GetKeyState((int)Keys.Alt) < 0)
||
(win32.GetKeyState((int)Keys.LMenu) < 0)
||
(win32.GetKeyState((int)Keys.RMenu) < 0)
))
{
Keys k = (Keys)m.WParam.ToInt32();
isHandled = doShortCut(k);
}
}
//if not handled, pass it to TranslateAccelerator
if (!isHandled)
{
if (theSite != null)
{
MSG msg = new MSG();
msg.hwnd = m.HWnd;
msg.wParam = m.WParam;
msg.lParam = m.LParam;
msg.message = m.Msg;
Point pos = new Point(win32.GetMessagePos());
msg.pt_x = pos.X;
msg.pt_y = pos.Y;
msg.time = win32.GetMessageTime();
isHandled = theSite.CallTranslateAccelerator(msg);
}
}
break;
case win32.WM_MOUSEACTIVATE:
{
this.Select();
if (theSite != null)
{
if (!this.DesignMode)
{
IntPtr fromHandle = win32.GetFocus();
this.mDocumentHandle = theSite.DocumentHandle;
if ((!this.Focused) & (fromHandle != this.Handle) & (fromHandle != this.mDocumentHandle))
{
win32.SendMessage(this.Handle, win32.WM_SETFOCUS, (int)fromHandle, 0);
}
}
}
break;
}
case win32.WM_SETFOCUS:
this.setFocusToMshtml();
isHandled = true;
break;
case win32.WM_KEYUP:
break;
}
if (!isHandled)
{
base.WndProc(ref m);
}
}
#endregion Override WndProc ===============
#region Override Handle events ===============
protected override void OnHandleCreated(EventArgs e)
{
//earlier versions of HtmlEditor always init mshtml
//here. However, according to BoundsChecker that
//causes a memory overrun. It also means that the
//document is passed a rect with zero bounds, as
//the control has not yet been sized.
//Therefore, mshtml is now initialized for the first
//time in OnParentChanged. In most cases, that will
//be when the designer-generated code adds the control
//to the form's controls collection.
if (!mCreating)
{
initMshtml();
}
}
protected override void OnHandleDestroyed(EventArgs e)
{
CleanupControl();
base.OnHandleDestroyed(e);
}
#endregion
#region Overrides of container control methods ===============
protected override void OnParentChanged(System.EventArgs e)
{
if (this.Parent == null)
{
return;
}
if (theSite == null)
{
this.createTimer.Enabled = true;
//initMshtml();
}
base.OnParentChanged(e);
}
protected override void OnVisibleChanged(EventArgs e)
{
if (this.Visible)
{
if (this.bNeedsActivation)
{
this.activateTimer.Enabled = true;
}
}
base.OnVisibleChanged(e);
}
protected override void OnPaint(PaintEventArgs pevent)
{
if (this.DesignMode)
{
pevent.Graphics.FillRectangle(System.Drawing.Brushes.White, this.ClientRectangle);
}
base.OnPaint(pevent);
}
public override Boolean PreProcessMessage(ref Message m)
{
bool callBase = true;
if (m.Msg == win32.WM_KEYDOWN)
{
Keys k = (Keys)m.WParam.ToInt32();
if ((k == Keys.Return) && this.AcceptsReturn)
{
callBase = false;
}
if ((k == Keys.Tab) && this.mAcceptsTab)
{
callBase = false;
}
if ((k == Keys.Right) || (k == Keys.Left) || (k == Keys.Up) || (k == Keys.Down))
{
callBase = false;
}
}
if (callBase)
{
return base.PreProcessMessage(ref m);
}
else
{
return false; // don't call base method, don't return true
}
}
protected override bool ProcessDialogChar(char charCode)
{
/* return false here to prevent this from bubbling up the hierarchy and eventually being
* processed by something (a button say) that has the character set as a mnemonic, if that
* happens the character is never entered into the editor so you end up missing any chars
* that are a mnemonic for another control!
*/
return false;
}
#endregion
#region initialization and cleanup
#region initialization
void initMshtml()
{
//don't create in design mode
if (this.DesignMode)
{
return;
}
if (this.isCreated)
{
return; //can't create if already created
}
// force creating Host handle since we need it to parent MSHTML
if (!this.IsHandleCreated)
{
IntPtr hostHandle = Handle;
}
theSite = new HtmlSite(this);
theSite.CreateDocument();
if (this.Visible)
{
theSite.ActivateDocument();
}
else
{
this.bNeedsActivation = true;
this.activateTimer.Enabled = true;
}
this.mHtmlDoc = (HTMLDocument)theSite.Document;
this.mDocHTML = new DocHTML(mHtmlDoc);
if (this.mDesignMode)
{
IHTMLDocument2 htmldoc = (IHTMLDocument2)this.mHtmlDoc;
htmldoc.SetDesignMode("On");
}
theSite.SetPropertyNotifyEvents();
if (url != String.Empty)
{
LoadUrl(url);
}
else
{
//always initialize with at least the blank url
LoadUrl("About:Blank");
}
if (sDocument != String.Empty)
{
LoadDocument(sDocument);
}
else
{
if (this.mDesignMode)
{
LoadDocument("");
}
}
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.activateTimer = new System.Windows.Forms.Timer(this.components);
this.createTimer = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// activateTimer
//
this.activateTimer.Interval = 1;
this.activateTimer.Enabled = false;
this.activateTimer.Tick += new System.EventHandler(this.activateTimer_Tick);
this.createTimer.Interval = 1;
this.createTimer.Enabled = false;
this.createTimer.Tick += new System.EventHandler(this.createTimer_Tick);
this.ResumeLayout(false);
}
private void activateTimer_Tick(object sender, System.EventArgs e)
{
this.activateTimer.Enabled = false;
if (theSite != null)
{
theSite.ActivateDocument();
}
}
private void createTimer_Tick(object sender, System.EventArgs e)
{
this.createTimer.Enabled = false;
if (!this.isCreated)
{
this.initMshtml();
}
}
#endregion
#region cleanup
protected override void Dispose(bool disposing)
{
if (disposing)
{
//remove timer event handlers
if (this.activateTimer != null)
{
this.activateTimer.Tick -= new System.EventHandler(this.activateTimer_Tick);
}
if (this.createTimer != null)
{
this.createTimer.Tick -= new System.EventHandler(this.createTimer_Tick);
}
if (this.mComposeSettings != null)
{
this.mComposeSettings = null;
}
if (components != null)
{
components.Dispose();
}
IntPtr ptr = Marshal.GetIDispatchForObject(this);
int i = Marshal.Release(ptr);
while (i > 0)
{
i = Marshal.Release(ptr);
}
if (this.mNativeWindow != null)
{
this.mNativeWindow.ReleaseHandle();
this.mNativeWindow = null;
}
}
base.Dispose(disposing);
}
///
/// Clean up any resources being used.
///
public void CleanupControl()
{
if (isCreated)
{
theSite.CloseDocument();
if (this.theSite != null)
{
IntPtr ptr = Marshal.GetIDispatchForObject(this.theSite);
int i = Marshal.Release(ptr);
while (i > 0)
{
i = Marshal.Release(ptr);
}
this.theSite.Dispose();
this.theSite = null;
}
this.mHtmlDoc = null;
this.mDocHTML = null;
}
}
public void ReloadMshtml()
{
CleanupControl();
initMshtml();
}
#endregion
#endregion
#region Declare Events ====================================================
//declare the events
public event HtmlNavigateEventHandler Navigate;
public event ReadyStateChangedHandler ReadyStateChanged;
public event UpdateUIHandler UpdateUI;
public event HtmlKeyPressHandler HtmlKeyPress;
public event HtmlEventHandler HtmlEvent;
public event BeforeNavigateEventHandler BeforeNavigate;
public event BeforeShortcutEventHandler BeforeShortcut;
public event BeforePasteHandler BeforePaste;
[Description("Fires when content is edited in design mode.")]
public event EventHandler ContentChanged;
#endregion Declare Events ===========
#region Invoke Events =========================================================
//invoke the UpdateUI event
public void InvokeUpdateUI(IHTMLElement ae)
{
if (UpdateUI != null)
{
HtmlUpdateUIEventArgs ea = new HtmlUpdateUIEventArgs();
ea.currentElement = ae;
UpdateUI(this, ea);
}
}
//invoke the ReadyStateChanged event
public void InvokeReadyStateChanged(String newReadyState)
{
if (ReadyStateChanged != null)
{
ReadyStateChangedEventArgs ea = new ReadyStateChangedEventArgs(newReadyState);
ReadyStateChanged(this, ea);
}
}
public void InvokeHtmlKeyPress(ref IHTMLEventObj eobj)
{
if (HtmlKeyPress != null)
{
HtmlKeyPressEventArgs ea = new HtmlKeyPressEventArgs(ref eobj);
HtmlKeyPress(this, ea);
}
}
[DispId(0)]
public void InvokeHtmlEvent()
{
if (HtmlEvent != null)
{
// Get the event.
IHTMLEventObj pobjEvent = ((IHTMLDocument2)
mHtmlDoc).GetParentWindow().eventobj;
HtmlEventArgs ea = new HtmlEventArgs(pobjEvent);
HtmlEvent(this, ea);
}
return;
}
public void InvokeNavigate(String target)
{
if (Navigate != null) Navigate(this, new HtmlNavigateEventArgs(target));
}
internal void InvokeContentChanged()
{
if (ContentChanged != null)
{
ContentChanged(this, new System.EventArgs());
}
}
///
/// Fires the BeforeShortcut event. Handle this event to pre-process or cancel the
/// HtmlEditor's shortcut events.
///
/// Cancellable event args
protected virtual void OnBeforeShortcut(onlyconnect.BeforeShortcutEventArgs e)
{
if (BeforeShortcut != null) BeforeShortcut(this, e);
}
///
/// Fires the BeforeNavigateEvent
///
/// Cancellable event args
protected internal virtual void OnBeforeNavigate(onlyconnect.BeforeNavigateEventArgs e)
{
if (BeforeNavigate != null) BeforeNavigate(this, e);
}
///
/// Fires the BeforePaste event
///
///
protected internal virtual void OnBeforePaste(BeforePasteArgs e)
{
if (BeforePaste != null) BeforePaste(this, e);
}
#endregion Invoke Events ===============
#region Setup Event Management ======================================================
internal void ReadyStateChangeActions(IHTMLEventObj o)
{
//defensive - I've known this to be called
//after doc was deactivated
if (this.mHtmlDoc == null) return;
string theReadyState = this.HtmlDocument2.GetReadyState();
if (theReadyState == "complete")
{
//if changed to "COMPLETE", set edit designer
if (this.bLoadDocumentWhenReady)
{
Debug.WriteLine("Now loading doc");
this.LoadDocument(string.Empty);
return;
}
else if (this.bLoadUrlWhenReady)
{
Debug.WriteLine("Now loading url");
this.LoadUrl(this.url);
return;
}
Debug.WriteLine("Setting events");
{
if (this.IsDesignMode)
{
this.SetEditDesigner();
this.execCommand(commandids.IDM_AUTOURLDETECT_MODE, this.mEnableUrlDetection, false, false);
this.setChangeNotify();
}
}
if (this.bSetComposeSettingsWhenReady)
{
this.setDefaultFont();
}
//set HTMLEvents
this.SetHTMLEvents();
//refresh ReadyState since the above actions could have changed it
theReadyState = this.HtmlDocument2.GetReadyState();
}
//invoke ready state changed event
this.InvokeReadyStateChanged(theReadyState);
}
private void setChangeNotify()
{
//only set this if there is an event handler
if (ContentChanged == null)
{
return;
}
int hr;
this.mChangeMonitor = new ChangeMonitor(this);
hr = ((IMarkupContainer2)this.HtmlDocument2).RegisterForDirtyRange(mChangeMonitor, out changeCookie);
}
public void SetEditDesigner()
{
IHTMLDocument2 htmldoc = (IHTMLDocument2)this.mHtmlDoc;
htmldoc.SetDesignMode("On");
onlyconnect.IServiceProvider isp = (onlyconnect.IServiceProvider)mHtmlDoc;
onlyconnect.IHTMLEditServices es;
System.Guid IHtmlEditServicesGuid = new System.Guid("3050f663-98b5-11cf-bb82-00aa00bdce0b");
System.Guid SHtmlEditServicesGuid = new System.Guid(0x3050f7f9, 0x98b5, 0x11cf, 0xbb, 0x82, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x0b);
IntPtr ppv;
onlyconnect.IHTMLEditDesigner ds = (onlyconnect.IHTMLEditDesigner)theSite;
if (isp != null)
{
isp.QueryService(ref SHtmlEditServicesGuid, ref IHtmlEditServicesGuid, out ppv);
es = (onlyconnect.IHTMLEditServices)Marshal.GetObjectForIUnknown(ppv);
int retval = es.AddDesigner(ds);
Marshal.Release(ppv);
}
}
void SetHTMLEvents()
{
if ((mHtmlDoc != null) && (HtmlEvent != null))
{
//m_htmldoc.onkeydown = this;
//keydown does not work, keypress does...
IHTMLDocument2 htmldoc = (IHTMLDocument2)this.mHtmlDoc;
IHTMLDocument4 htmldoc4 = (IHTMLDocument4)this.mHtmlDoc;
IHTMLDocument5 htmldoc5 = (IHTMLDocument5)this.mHtmlDoc;
htmldoc.SetOnkeypress(this);
htmldoc.SetOnclick(this);
htmldoc4.onselectionchange = this;
htmldoc5.onfocusin = this;
htmldoc5.onfocusout = this;
}
}
#endregion Setup Event Management ================
#region The main public API
[Browsable(false)]
public IHTMLElement CurrentElement
{
get
{
return mcurrentElement;
}
}
[System.ComponentModel.TypeConverter(typeof(System.ComponentModel.ExpandableObjectConverter)),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Description("Sets the initial font used by the editor. Disabled by default as it inserts a FONT tag.")]
public ComposeSettings DefaultComposeSettings
{
get
{
if (this.mComposeSettings == null)
{
mComposeSettings = new ComposeSettings(this);
}
return mComposeSettings;
}
}
[Description("Encoding to use when getting the string value of the HTML document"), DefaultValue(false)]
public EncodingType DocumentEncoding
{
get { return mDocumentEncoding; }
set { mDocumentEncoding = value; }
}
[Description("The preamble to be added by the editor if no byte order mark is detected"), DefaultValue(false)]
public EncodingType DefaultPreamble
{
get { return mDefaultPreamble; }
set { mDefaultPreamble = value; }
}
[Description("Indicates if return characters are accepted as input."),
DefaultValue(true)]
public bool AcceptsReturn
{
get
{
return this.mAcceptsReturn;
}
set
{
this.mAcceptsReturn = value;
}
}
[Description("Indicates if tab characters are accepted as input. Set false to enable tabbing between controls."),
DefaultValue(false)]
public bool AcceptsTab
{
get
{
return this.mAcceptsTab;
}
set
{
this.mAcceptsTab = value;
}
}
[Description("Turn off auto-detection of string type in LoadDocument and always treat it as an Ansi string."), DefaultValue(false)]
public Boolean IsAnsiStreamAlwaysUsed
{
get
{
return this.mAlwaysLoadAnsi;
}
set
{
this.mAlwaysLoadAnsi = value;
}
}
[Description("Is the control in edit mode so that users can edit the contents."), DefaultValue(false)]
public Boolean IsDesignMode
{
get { return mDesignMode; }
set
{
if (value)
{
mDesignMode = true;
if (this.mHtmlDoc != null)
{
IHTMLDocument2 htmldoc = (IHTMLDocument2)this.mHtmlDoc;
htmldoc.SetDesignMode("On");
if (this.mComposeSettings.Enabled)
{
this.setDefaultFont();
}
//refresh url detection
this.IsUrlDetectionEnabled = this.mEnableUrlDetection;
this.LoadDocument("");
}
}
else
{
mDesignMode = false;
if (this.mHtmlDoc != null)
{
IHTMLDocument2 htmldoc = (IHTMLDocument2)this.mHtmlDoc;
htmldoc.SetDesignMode("Off");
}
}
}
}
[Description("Gets/Sets if the context menu on right clicks will be displayed."), DefaultValue(true)]
public bool IsContextMenuEnabled
{
get
{
return mIsContextMenuEnabled;
}
set
{
mIsContextMenuEnabled = value;
}
}
[Description("Gets/Sets if the control can receive the focus."), DefaultValue(true)]
public bool IsActivationEnabled
{
get
{
return mAllowActivation;
}
set
{
mAllowActivation = value;
}
}
[Description("Gets/Sets whether the Tab key, in design mode, stays within the HtmlEditor or sets focus on the next control in the tab order."),
DefaultValue(true), Browsable(false)]
public bool IsTabToNextControl
{
get
{
return !AcceptsTab;
}
set
{
AcceptsTab = !value;
}
}
[Description("Gets/Sets where the control stores preferences in the registry."),
DefaultValue(null)]
public string OptionKeyPath
{
get
{
return this.mOptionKeyPath;
}
set
{
mOptionKeyPath = value;
}
}
[Description("Gets/Sets if scripts, Java etc is enabled"), DefaultValue(true)]
public bool IsActiveContentEnabled
{
//This must be set BEFORE the document is created
get
{
return mEnableActiveContent;
}
set
{
mEnableActiveContent = value;
}
}
[Description("Gets/Sets if autocomplete is enabled"), DefaultValue(false)]
public bool IsAutoCompleteEnabled
{
//This must be set BEFORE the document is created
get
{
return mEnableAutoComplete;
}
set
{
mEnableAutoComplete = value;
}
}
[Description("Gets/Sets if urls are automatically changed to hyperlinks"), DefaultValue(false)]
public bool IsUrlDetectionEnabled
{
get
{
return this.mEnableUrlDetection;
}
set
{
this.mEnableUrlDetection = value;
// we does this both here and in ReadyStateCompleteActions
// it doesn't take effect if doc is not ready
if (this.IsDesignMode)
{
this.SetEditDesigner();
this.execCommand(commandids.IDM_AUTOURLDETECT_MODE, this.mEnableUrlDetection, false, true);
}
}
}
[Description("Gets/Sets if pressing Enter creates DIV rather than P"), DefaultValue(false)]
public bool IsDivOnEnter
{
//This must be set BEFORE the document is created
get
{
return mDivOnEnter;
}
set
{
mDivOnEnter = value;
}
}
[Description("Gets/Sets if a vertical scroll bar is shown"), DefaultValue(true)]
public bool IsScrollBarShown
{
//This must be set BEFORE the document is created
get
{
return this.mShowScrollBars;
}
set
{
mShowScrollBars = value;
}
}
[Browsable(false)]
public HTMLDocument Document
{
get
{
if (theSite == null)
{
return null;
}
if (theSite.Document != null)
{
return (HTMLDocument)theSite.Document;
}
else
{
return null;
}
}
}
///
/// The thinking behind this is to wrap selected key properties and methods
/// in a developer-friendly class - called DocHTML / DocumentHTML to distinguish
/// from the other official items
///
[Browsable(false)]
public DocHTML DocumentHTML
{
get
{
return this.mDocHTML;
}
}
///
/// Quick access to the key IHTMLDocument2 interface
///
[Browsable(false)]
public IHTMLDocument2 HtmlDocument2
{
get
{
if (theSite == null)
{
return null;
}
if (theSite.Document != null)
{
return (IHTMLDocument2)theSite.Document;
}
else
{
return null;
}
}
}
[Browsable(false)]
public string ReadyState
{
get
{
if (this.mHtmlDoc == null)
{
return String.Empty;
}
return HtmlDocument2.GetReadyState().ToLower();
}
}
///
/// Gets/Sets if links that are clicked on in the editor will be opened in the editor or launch your default browser.
///
[Description("Gets/Sets if links that are clicked on in the editor will be opened in the editor or launch your default browser."), DefaultValue(false)]
public bool OpenLinksInNewWindow
{
get { return mLinksNewWindow; }
set
{
mLinksNewWindow = value;
}
}
public void SetStyleSheet(string sFileName)
{
if (this.mHtmlDoc == null)
{
return;
}
IHTMLDocument2 htmldoc = (IHTMLDocument2)this.mHtmlDoc;
if (htmldoc.GetReadyState().ToLower() == "complete")
{
htmldoc.CreateStyleSheet(sFileName, 0);
}
}
public bool Copy()
{
return this.execCommand(commandids.IDM_COPY, null, false, true);
}
public bool Paste()
{
BeforePasteArgs e = new BeforePasteArgs();
this.OnBeforePaste(e);
if (e.Cancel)
{
return false;
}
else
return this.execCommand(commandids.IDM_PASTE, null, false, true);
}
public bool SaveAs(string DefaultPath)
{
return this.execCommand(commandids.IDM_SAVEAS, DefaultPath, true, true);
}
public bool Undo()
{
return this.execCommand(commandids.IDM_UNDO, null, false, true);
}
public bool Redo()
{
return this.execCommand(commandids.IDM_REDO, null, false, true);
}
public bool Cut()
{
return this.execCommand(commandids.IDM_CUT, null, false, true);
}
public bool SetSelectionBold()
{
return this.execCommand(commandids.IDM_BOLD, null, false, true);
}
public bool SetSelectionItalic()
{
return this.execCommand(commandids.IDM_ITALIC, null, false, true);
}
public bool ClearSelectionFormatting()
{
return this.execCommand(commandids.IDM_REMOVEFORMAT, null, false, true);
}
[Browsable(false)]
public Color SelectionBackColor
{
get
{
if (Document == null) return Color.Empty;
else
{
object Result = HtmlDocument2.QueryCommandValue("BackColor");
try
{
return System.Drawing.ColorTranslator.FromHtml(Result.ToString());
}
catch
{
return Color.Empty;
}
}
}
set
{
//need to send a COLORREF value
this.execCommand(commandids.IDM_BACKCOLOR, utils.GetCsColor(value), false, true);
}
}
///
/// Gets/Sets the color for the selected text.
///
[Description("Gets/Sets the color of the selected text."), Browsable(false)]
public Color SelectionForeColor
{
get
{
if (Document == null) return Color.Empty;
else
{
object Result = HtmlDocument2.QueryCommandValue("ForeColor");
try
{
return System.Drawing.ColorTranslator.FromHtml(Result.ToString());
}
catch
{
return Color.Empty;
}
}
}
set
{
//need to send a COLORREF value
this.execCommand(commandids.IDM_FORECOLOR, utils.GetCsColor(value), false, true);
}
}
[Description("Gets/sets the font for the selected text"), Browsable(false)]
public Font SelectionFont
{
get
{
try
{
//This is here because of hosting issues on a UserControl
if (this.Document == null) return null;
System.Drawing.FontStyle fs = new FontStyle();
int FontSize = 8;
if (Convert.ToBoolean(HtmlDocument2.QueryCommandValue("bold"))) fs |= FontStyle.Bold;
if (Convert.ToBoolean(HtmlDocument2.QueryCommandValue("italic"))) fs |= FontStyle.Italic;
if (Convert.ToBoolean(HtmlDocument2.QueryCommandValue("underline"))) fs |= FontStyle.Underline;
switch (Convert.ToInt32(HtmlDocument2.QueryCommandValue("FontSize")))
{
case 1:
FontSize = 8;
break;
case 2:
FontSize = 10;
break;
case 3:
FontSize = 12;
break;
case 4:
FontSize = 18;
break;
case 5:
FontSize = 24;
break;
case 6:
FontSize = 36;
break;
case 7:
FontSize = 48;
break;
}
string FontName = HtmlDocument2.QueryCommandValue("FontName").ToString();
return new Font(FontName, FontSize, fs);
}
catch
{
return null;
}
}
set
{
try
{ //This is here because of hosting issues on a UserControl
if ((Document == null) | (value == null)) return;
HtmlDocument2.ExecCommand("FontName", false, value.Name);
if (value.SizeInPoints <= 8)
{
HtmlDocument2.ExecCommand("FontSize", false, 1);
}
else if (value.SizeInPoints <= 10)
{
HtmlDocument2.ExecCommand("FontSize", false, 2);
}
else if (value.SizeInPoints <= 12)
{
HtmlDocument2.ExecCommand("FontSize", false, 3);
}
else if (value.SizeInPoints <= 18)
{
HtmlDocument2.ExecCommand("FontSize", false, 4);
}
else if (value.SizeInPoints <= 24)
{
HtmlDocument2.ExecCommand("FontSize", false, 5);
}
else if (value.SizeInPoints <= 36)
{
HtmlDocument2.ExecCommand("FontSize", false, 6);
}
else
{
HtmlDocument2.ExecCommand("FontSize", false, 7);
}
if (Convert.ToBoolean(HtmlDocument2.QueryCommandValue("bold")) != Font.Bold) HtmlDocument2.ExecCommand("bold", false, null);
if (Convert.ToBoolean(HtmlDocument2.QueryCommandValue("italic")) != Font.Bold) HtmlDocument2.ExecCommand("italic", false, null);
if (Convert.ToBoolean(HtmlDocument2.QueryCommandValue("underline")) != Font.Bold) HtmlDocument2.ExecCommand("underline", false, null);
}
catch { }
}
}
///
/// Gets/Sets the alignment of the selected text.
///
[Description("Gets/Sets the alignment of the selected text."), Browsable(false)]
public System.Windows.Forms.HorizontalAlignment SelectionAlignment
{
get
{
if (Document == null) return HorizontalAlignment.Left;
else
{
if (Convert.ToBoolean(HtmlDocument2.QueryCommandValue("JustifyRight")))
{
return HorizontalAlignment.Right;
}
else if (Convert.ToBoolean(HtmlDocument2.QueryCommandValue("JustifyCenter")))
{
return HorizontalAlignment.Center;
}
else
{
return HorizontalAlignment.Left;
}
}
}
set
{
if (Document == null) return;
switch (value)
{
case HorizontalAlignment.Left:
HtmlDocument2.ExecCommand("JustifyLeft", false, null);
break;
case HorizontalAlignment.Center:
HtmlDocument2.ExecCommand("JustifyCenter", false, null);
break;
case HorizontalAlignment.Right:
HtmlDocument2.ExecCommand("JustifyRight", false, null);
break;
}
}
}
///
/// Get/Sets if numbering is on for the selected text.
///
[Description("Get/Sets if numbering is on for the selected text."), Browsable(false)]
public bool SelectionNumbering
{
get
{
if (Document == null) return false;
else return Convert.ToBoolean(HtmlDocument2.QueryCommandValue("InsertOrderedList"));
}
set
{
if (Document != null && Convert.ToBoolean(HtmlDocument2.QueryCommandValue("InsertOrderedList")) != value) HtmlDocument2.ExecCommand("InsertOrderedList", false, null);
}
}
///
/// Gets/Sets if bullets are on or off for the selected text.
///
[Description("Gets/Sets if bullets are on or off for the selected text."), Browsable(false)]
public bool SelectionBullets
{
get
{
if (Document == null) return false;
else return Convert.ToBoolean(HtmlDocument2.QueryCommandValue("InsertUnorderedList"));
}
set
{
if (Document != null && Convert.ToBoolean(HtmlDocument2.QueryCommandValue("InsertUnorderedList")) != value) HtmlDocument2.ExecCommand("InsertUnorderedList", false, null);
}
}
public bool SelectAll()
{
return this.execCommand(commandids.IDM_SELECTALL, null, false, true);
}
public void ClearSelection()
{
this.HtmlDocument2.GetSelection().Empty();
}
public void DeleteSelection()
{
this.execCommand(commandids.IDM_DELETE, null, false, true);
}
#region Load and save documents
public void LoadDocument(String documentVal)
{
if ((documentVal != string.Empty) | (!this.bLoadDocumentWhenReady))
{
//if doc is waiting to load, it is already in string variable
this.bLoadDocumentWhenReady = false;
sDocument = documentVal;
}
else
{
this.bLoadDocumentWhenReady = false;
this.iLoadAttempts += 1;
}
if (!isCreated)
{
if (iLoadAttempts < 2)
{
this.bLoadDocumentWhenReady = true;
return;
}
else
{
throw new HtmlEditorException("Document not created");
}
}
if ((this.HtmlDocument2.GetReadyState().ToLower() != "complete") & (this.HtmlDocument2.GetReadyState().ToLower() != "interactive"))
//try to load on interactive as well as complete
{
if (iLoadAttempts < 2)
{
this.bLoadDocumentWhenReady = true;
return;
}
else
{
throw new HtmlEditorException("Document not ready");
}
}
//this is a fix for exception raised in UpdateUI
theSite.mFullyActive = false;
Encoding theDefaultPreamble = getEncodingFromEncodingType(this.mDefaultPreamble);
utils.LoadDocument(ref this.mHtmlDoc, sDocument, this.mAlwaysLoadAnsi, false, theDefaultPreamble);
this.iLoadAttempts = 0;
}
public void LoadUrl(String url)
{
this.bLoadUrlWhenReady = false;
this.url = url;
if (!isCreated)
{
Debug.WriteLine("Doc not created" + iLoadAttempts.ToString());
if (iLoadAttempts < 2)
{
this.bLoadUrlWhenReady = true;
return;
}
else
{
throw new HtmlEditorException("Document not created");
}
}
if (!isCreated) return;
//this is a workaround for a problem calling Caret.SetLocation before it
//is ready, in UpdateUI
theSite.mFullyActive = false;
//don't ask for downloadOnly since clientsite is already set
utils.LoadUrl(ref mHtmlDoc, url, false);
iLoadAttempts = 0;
}
public String GetDocumentSource()
{
if (!isCreated) return null;
HTMLDocument thedoc = this.Document;
if (thedoc == null) return null;
return utils.GetDocumentSource(ref thedoc, this.mDocumentEncoding);
}
#endregion
public void ShowFindDialog()
{
this.execCommand(commandids.IDM_FIND, null, true, true);
}
public void Print(bool bPreview)
{
this.Print(bPreview, string.Empty, true);
}
public void Print(bool bPreview, String sTemplatePath)
{
this.Print(bPreview, sTemplatePath, true);
}
public void Print(bool bPreview, String sTemplatePath, bool bPromptUser)
{
Object pvaIn;
if (sTemplatePath == string.Empty)
{
pvaIn = null;
}
else if (!File.Exists(sTemplatePath))
{
pvaIn = null;
}
else
{
pvaIn = sTemplatePath;
}
if (bPreview)
{
this.execCommand(commandids.IDM_PRINTPREVIEW, pvaIn, bPromptUser, true);
}
else
{
this.execCommand(commandids.IDM_PRINT, pvaIn, bPromptUser, true);
}
}
public bool Stop()
{
return this.execCommand(commandids.IDM_STOP, null, false, false);
}
#endregion
#region private properties and methods
Boolean isCreated
{
get
{
return (theSite != null) && (theSite.Document != null);
}
}
internal bool execCommand(int iCommand, object argument, bool bPromptUser, bool checkReadystate)
{
if (this.mHtmlDoc == null)
{
return false;
}
//Check readystate
if (checkReadystate)
{
if (this.ReadyState != "complete")
{
//throw new HtmlEditorException("Document is not ready");
return false;
}
}
//get the command target
IOleCommandTarget ct = (IOleCommandTarget)this.mHtmlDoc;
if (ct == null)
{
throw new HtmlEditorException("Cannot get COM command target");
}
//exec the command
System.Guid pguidCmdGroup = new Guid("DE4BA900-59CA-11CF-9592-444553540000");
object[] pvaOut = null;
object[] args = new object[] {argument};
int iRetval;
int iPromptUser;
if (bPromptUser)
{
iPromptUser = (int)OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER;
}
else
{
iPromptUser = (int)OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER;
}
iRetval = ct.Exec(ref pguidCmdGroup, iCommand, iPromptUser, args, pvaOut);
return (iRetval == 0);
}
///
/// Queries the status of the specified command
///
private CommandStatus GetCommandInfo(int command)
{
//get the command target
IOleCommandTarget ct = (IOleCommandTarget)this.mHtmlDoc;
if (ct == null)
{
throw new HtmlEditorException("Cannot get COM command target");
}
System.Guid pguidCmdGroup = new Guid("DE4BA900-59CA-11CF-9592-444553540000");
//Query the command target for the command status
OLECMD oleCommand = new OLECMD();
oleCommand.cmdID = command;
onlyconnect.OLECMDTEXT ot = new onlyconnect.OLECMDTEXT();
int hr = ct.QueryStatus(ref pguidCmdGroup, 1, oleCommand, ref ot);
if (hr != HRESULT.S_OK)
{
return CommandStatus.Unknown;
}
if ((oleCommand.cmdf | (int)OLECMDF.OLECMDF_LATCHED) == (int)OLECMDF.OLECMDF_LATCHED)
{
return CommandStatus.EnabledAndToggledOn;
}
else if ((oleCommand.cmdf | (int)OLECMDF.OLECMDF_ENABLED) == (int)OLECMDF.OLECMDF_ENABLED)
{
return CommandStatus.Enabled;
}
else if ((oleCommand.cmdf | (int)OLECMDF.OLECMDF_SUPPORTED) == (int)OLECMDF.OLECMDF_SUPPORTED)
{
return CommandStatus.Disabled;
}
else
{
return CommandStatus.Unsupported;
}
}
private bool isCommandEnabled(int command)
{
CommandStatus cs = this.GetCommandInfo(command);
if ((cs == CommandStatus.Enabled) | (cs == CommandStatus.EnabledAndToggledOn))
{
return true;
}
else
{
return false;
}
}
///
/// set focus to the hosted document, not just to the winform control
///
private void setFocusToMshtml()
{
if (this.mNativeDocWindow != null)
{
win32.SetFocus(this.mNativeDocWindow.Handle);
}
}
internal bool setDefaultFont()
{
if (!this.mDesignMode) return false;
if (this.HtmlDocument2.GetReadyState() != "complete")
{
this.bSetComposeSettingsWhenReady = true;
return false;
}
this.bSetComposeSettingsWhenReady = false;
object o = mComposeSettings.CommandString;
if (this.execCommand(commandids.IDM_HTMLEDITMODE, true, false, false))
{
return this.execCommand(commandids.IDM_COMPOSESETTINGS, o, false, false);
}
else
{
return false;
}
}
Encoding getEncodingFromEncodingType(EncodingType enc)
{
switch (enc)
{
case EncodingType.ASCII: return Encoding.ASCII;
case EncodingType.Auto: return Encoding.Default;
case EncodingType.Unicode: return Encoding.Unicode;
case EncodingType.UTF7: return Encoding.UTF7;
case EncodingType.UTF8: return Encoding.UTF8;
case EncodingType.WindowsCurrent: return Encoding.Default;
default: return Encoding.Default;
}
}
#endregion
}
class HandleWndProc : NativeWindow
{
internal HtmlEditor thecontrol;
protected override void WndProc(ref Message message)
{
bool bPassToControl = false;
//The idea is only pass the key presses and mouse clicks (and not the right clicks) to the base form to process the events correctly.
if ((message.Msg >= 0x100 && message.Msg <= 0x0108) ||
(message.Msg >= 0x200 && message.Msg <= 0x020A &&
message.Msg != win32.WM_LBUTTONDOWN
&& message.Msg != win32.WM_RBUTTONDOWN
&& message.Msg != win32.WM_LBUTTONDBLCLK
&& message.Msg != win32.WM_MBUTTONDBLCLK
&& message.Msg != win32.WM_RBUTTONDBLCLK))
{
bPassToControl = true;
}
else
{
//These are separate because if you pass it to the base control then the right mouse clicks etc. don't fire.
switch (message.Msg)
{
case win32.WM_LBUTTONDOWN:
thecontrol.InvokeOnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, message.LParam.ToInt32() & 0xffff, Convert.ToInt32((message.LParam.ToInt32() & 0xffff0000) >> 16), 0));
break;
case win32.WM_RBUTTONDOWN:
thecontrol.InvokeOnMouseDown(new MouseEventArgs(MouseButtons.Right, 1, message.LParam.ToInt32() & 0xffff, Convert.ToInt32((message.LParam.ToInt32() & 0xffff0000) >> 16), 0));
break;
case win32.WM_LBUTTONDBLCLK:
case win32.WM_MBUTTONDBLCLK:
case win32.WM_RBUTTONDBLCLK:
thecontrol.InvokeOnDoubleClick();
break;
}
}
if (message.Msg == win32.WM_KEYDOWN)
{
Keys k = (Keys)message.WParam.ToInt32();
if (k == Keys.Back)
{
bPassToControl = false;
}
if ((k == Keys.Right) || (k == Keys.Left) || (k == Keys.Up) || (k == Keys.Down))
{
bPassToControl = false;
}
}
if (bPassToControl)
{
thecontrol.InvokeWndProc(ref message);
}
base.WndProc(ref message);
}
}
public enum EncodingType
{
UTF7,
UTF8,
Unicode,
ASCII,
WindowsCurrent,
Auto
}
public enum CommandStatus : byte
{
Unsupported,
Disabled,
Enabled,
EnabledAndToggledOn,
Unknown
}
}