407 lines
14 KiB
C#
407 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using mshtml;
|
|
using YARTE.UI.Buttons;
|
|
using System.Diagnostics;
|
|
|
|
namespace YARTE.UI
|
|
{
|
|
public partial class HtmlEditor : UserControl
|
|
{
|
|
private readonly HtmlDocument _doc;
|
|
private readonly IList<IButton> _customButtons;
|
|
|
|
public HtmlEditor()
|
|
{
|
|
InitializeComponent();
|
|
|
|
InitializeWebBrowserAsEditor();
|
|
|
|
_doc = textWebBrowser.Document;
|
|
_customButtons = new List<IButton>();
|
|
|
|
updateToolBarTimer.Start();
|
|
updateToolBarTimer.Tick += updateToolBarTimer_Tick;
|
|
}
|
|
|
|
public void AddFontSizeSelector(IEnumerable<int> fontSizes)
|
|
{
|
|
if (fontSizes.Min() < 1 || fontSizes.Max() > 10)
|
|
{
|
|
throw new ArgumentException("Allowable font sizes are 1 through 10");
|
|
}
|
|
|
|
var fontSizeBox = new ToolStripComboBox();
|
|
fontSizeBox.Items.AddRange(fontSizes.Select(f => f.ToString()).ToArray());
|
|
fontSizeBox.Name = "fontSizeSelector";
|
|
fontSizeBox.Size = new System.Drawing.Size(25, 25);
|
|
fontSizeBox.SelectedIndexChanged += (sender, o) =>
|
|
{
|
|
var size = ((ToolStripComboBox)sender).SelectedItem;
|
|
_doc.ExecCommand("FontSize", false, size);
|
|
};
|
|
fontSizeBox.DropDownStyle = ComboBoxStyle.DropDownList;
|
|
|
|
this.AddToolbarItem(fontSizeBox);
|
|
}
|
|
|
|
public void AddFontSelector(IEnumerable<string> fontNames)
|
|
{
|
|
var dropDown = new ToolStripDropDownButton();
|
|
foreach(var fontName in fontNames)
|
|
{
|
|
dropDown.DropDownItems.Add(GetFontDropDownItem(fontName));
|
|
}
|
|
dropDown.ImageTransparentColor = System.Drawing.Color.Magenta;
|
|
dropDown.Name = "Font";
|
|
dropDown.Size = new System.Drawing.Size(29, 22);
|
|
dropDown.Text = "Font";
|
|
|
|
this.AddToolbarItem(dropDown);
|
|
}
|
|
|
|
private ToolStripItem GetFontDropDownItem(string fontName)
|
|
{
|
|
var dropDownItem = new ToolStripMenuItem();
|
|
dropDownItem.Font = new System.Drawing.Font(fontName, 10, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0);
|
|
dropDownItem.Name = "fontMenuItem" + Guid.NewGuid();
|
|
dropDownItem.Size = new System.Drawing.Size(173, 22);
|
|
dropDownItem.Text = fontName;
|
|
dropDownItem.Click += (sender, e) => _doc.ExecCommand("FontName", false, fontName);
|
|
return dropDownItem;
|
|
}
|
|
|
|
public void AddToolbarItem(ToolStripItem toolStripItem)
|
|
{
|
|
editcontrolsToolStrip.Items.Add(toolStripItem);
|
|
}
|
|
|
|
public void AddToolbarItems(IEnumerable<ToolStripItem> toolStripItems)
|
|
{
|
|
foreach (var stripItem in toolStripItems)
|
|
{
|
|
editcontrolsToolStrip.Items.Add(stripItem);
|
|
}
|
|
}
|
|
|
|
public void AddToolbarItem(IHTMLEditorButton toolbarItem)
|
|
{
|
|
_customButtons.Add(toolbarItem);
|
|
editcontrolsToolStrip.Items.Add(CreateButton(toolbarItem));
|
|
}
|
|
|
|
public void AddToolbarItem(IFunctionButton toolbarItem)
|
|
{
|
|
_customButtons.Add(toolbarItem);
|
|
editcontrolsToolStrip.Items.Add(CreateButton(toolbarItem));
|
|
}
|
|
|
|
public void AddToolbarItems(IEnumerable<IHTMLEditorButton> toolbarItems)
|
|
{
|
|
foreach (var toolbarItem in toolbarItems)
|
|
{
|
|
AddToolbarItem(toolbarItem);
|
|
}
|
|
}
|
|
|
|
public void AddToolbarDivider()
|
|
{
|
|
var divider = new ToolStripSeparator();
|
|
divider.Size = new System.Drawing.Size(6, 25);
|
|
editcontrolsToolStrip.Items.Add(divider);
|
|
}
|
|
|
|
private ToolStripItem CreateButton(IHTMLEditorButton toolbarItem)
|
|
{
|
|
var toolStripButton = new ToolStripButton();
|
|
toolStripButton.DisplayStyle = ToolStripItemDisplayStyle.Image;
|
|
toolStripButton.Image = toolbarItem.IconImage;
|
|
toolStripButton.ImageScaling = ToolStripItemImageScaling.None;
|
|
toolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
|
|
toolStripButton.Name = toolbarItem.IconName;
|
|
toolStripButton.Size = new System.Drawing.Size(25, 24);
|
|
toolStripButton.Text = toolbarItem.IconTooltip;
|
|
|
|
var args = new ButtonArgs();
|
|
args.Document = _doc;
|
|
args.Editor = this;
|
|
|
|
IHTMLEditorButton button = toolbarItem;
|
|
toolStripButton.Click += (sender, o) => button.IconClicked(args);
|
|
|
|
return toolStripButton;
|
|
}
|
|
|
|
private ToolStripItem CreateButton(IFunctionButton toolbarItem)
|
|
{
|
|
var toolStripButton = new ToolStripButton();
|
|
toolStripButton.DisplayStyle = ToolStripItemDisplayStyle.Text;
|
|
toolStripButton.Name = toolbarItem.IconName;
|
|
toolStripButton.Text = toolbarItem.Label;
|
|
|
|
var args = new ButtonArgs();
|
|
args.Document = _doc;
|
|
args.Editor = this;
|
|
|
|
IFunctionButton button = toolbarItem;
|
|
toolStripButton.Click += (sender, o) => button.IconClicked(args,toolStripButton);
|
|
|
|
return toolStripButton;
|
|
}
|
|
|
|
public bool ReadOnly
|
|
{
|
|
get
|
|
{
|
|
if (textWebBrowser.Document != null)
|
|
{
|
|
var doc = textWebBrowser.Document.DomDocument as IHTMLDocument2;
|
|
if (doc != null)
|
|
{
|
|
return doc.designMode != "On";
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
set
|
|
{
|
|
string html = this.Html;
|
|
if (textWebBrowser.Document != null)
|
|
{
|
|
var designMode = value ? "Off" : "On";
|
|
var doc = textWebBrowser.Document.DomDocument as IHTMLDocument2;
|
|
if (doc != null) doc.designMode = designMode;
|
|
}
|
|
this.Html = "";
|
|
if (html != null)
|
|
{
|
|
this.Html = html;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool ShowToolbar
|
|
{
|
|
get
|
|
{
|
|
if (editcontrolsToolStrip != null)
|
|
{
|
|
return editcontrolsToolStrip.Visible;
|
|
}
|
|
return true;
|
|
}
|
|
set { editcontrolsToolStrip.Visible = value; }
|
|
}
|
|
|
|
private void updateToolBarTimer_Tick(object sender, System.EventArgs e)
|
|
{
|
|
CheckCommandStateChanges();
|
|
}
|
|
|
|
private void InitializeWebBrowserAsEditor()
|
|
{
|
|
// It is necessary to add a body to the control before you can apply changes to the DOM document
|
|
textWebBrowser.DocumentText = "<html><body></body></html>";
|
|
if (textWebBrowser.Document != null)
|
|
{
|
|
var doc = textWebBrowser.Document.DomDocument as IHTMLDocument2;
|
|
if (doc != null) doc.designMode = "On";
|
|
|
|
// replace the context menu for the web browser control so the default IE browser context menu doesn't show up
|
|
textWebBrowser.IsWebBrowserContextMenuEnabled = false;
|
|
if (this.ContextMenuStrip == null)
|
|
{
|
|
textWebBrowser.Document.ContextMenuShowing += (sender, e) => { ; };
|
|
}
|
|
}
|
|
}
|
|
|
|
void Document_ContextMenuShowing(object sender, HtmlElementEventArgs e)
|
|
{
|
|
this.ContextMenuStrip.Show(this, this.PointToClient(Cursor.Position));
|
|
}
|
|
|
|
public string Html
|
|
{
|
|
get
|
|
{
|
|
|
|
var doc = textWebBrowser.Document.DomDocument as IHTMLDocument2;
|
|
|
|
// sample for getting CHECKED states
|
|
if (textWebBrowser.Document.GetElementsByTagName("input").Count > 0)
|
|
{
|
|
Console.WriteLine(textWebBrowser.Document.GetElementsByTagName("input")[0].GetAttribute("checked"));
|
|
}
|
|
|
|
/* this previously returned textWebBrowser.DocumentText, so the following HTML is now omitted:
|
|
< !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
|
< HTML >< HEAD >
|
|
< META content = "text/html; charset=unicode" http - equiv = Content - Type >
|
|
< META name = GENERATOR content = "MSHTML 11.00.10570.1001" ></ HEAD >
|
|
< BODY ></ BODY ></ HTML >
|
|
*/
|
|
try
|
|
{
|
|
return doc.body.innerHTML + ""; // this is used during runtime
|
|
// make sure it returns empty string instead of null
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// the Form [Design] view evaluates this
|
|
return textWebBrowser.DocumentText;
|
|
}
|
|
}
|
|
set
|
|
{
|
|
if (value == null)
|
|
{
|
|
value = "<html><body></body></html>";
|
|
}
|
|
|
|
if (textWebBrowser.Document != null)
|
|
{
|
|
// updating this way avoids an alert box
|
|
var doc = textWebBrowser.Document.DomDocument as IHTMLDocument2;
|
|
if (doc != null)
|
|
{
|
|
doc.write(value);
|
|
doc.close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void InsertTextAtCursor(string val)
|
|
{
|
|
_doc.ExecCommand("Paste", false, val);
|
|
}
|
|
|
|
private void CheckCommandStateChanges()
|
|
{
|
|
var doc = (IHTMLDocument2)_doc.DomDocument;
|
|
|
|
var commands = new List<string>();
|
|
foreach (var button in _customButtons)
|
|
{
|
|
if (button is IHTMLEditorButton)
|
|
{
|
|
var temp = (IHTMLEditorButton)button;
|
|
commands.Add(temp.CommandIdentifier);
|
|
}
|
|
}
|
|
//var commands = _customButtons.Select(c => c.CommandIdentifier).Where(c => !string.IsNullOrEmpty(c));
|
|
|
|
foreach (var command in commands)
|
|
{
|
|
var button = (ToolStripButton)editcontrolsToolStrip.Items[command];
|
|
|
|
if (button == null) continue;
|
|
|
|
if (doc.queryCommandState(command))
|
|
{
|
|
if (button.CheckState != CheckState.Checked)
|
|
{
|
|
button.Checked = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (button.CheckState == CheckState.Checked)
|
|
{
|
|
button.Checked = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void HtmlEditor_ContextMenuStripChanged(object sender, System.EventArgs e)
|
|
{
|
|
if (textWebBrowser.Document != null)
|
|
{
|
|
textWebBrowser.Document.ContextMenuShowing += Document_ContextMenuShowing;
|
|
}
|
|
}
|
|
|
|
private void textWebBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
|
|
{
|
|
// http://stackoverflow.com/questions/18035579/how-to-open-a-link-in-webbrowser-control-in-external-browser
|
|
// terminate current Navigating event; launch a new Process with the target URL to open user's default browser
|
|
if (ReadOnly)
|
|
{
|
|
e.Cancel = true;
|
|
|
|
Process.Start(e.Url.ToString());
|
|
}
|
|
}
|
|
|
|
public string insertCheckbox(string label = null)
|
|
{
|
|
Random rand = new Random();
|
|
string letters = "qwertyuiopasdfghjklzxcvbnm";
|
|
string identifier = rand.Next(1000000000, int.MaxValue).ToString() +
|
|
letters[rand.Next(letters.Length)];
|
|
InsertTextAtCursor(identifier); // place random int in checkbox's position
|
|
// pasting HTML doesn't work
|
|
|
|
string html = this.Html;
|
|
if (label == null)
|
|
{
|
|
// place plain checkbox
|
|
html = html.Replace(identifier,
|
|
"<div id=\"" + identifier + "\"><input type=\"checkbox\" id=\"" + identifier + "\"></div>");
|
|
}
|
|
else
|
|
{
|
|
// place checkbox and label\
|
|
html = html.Replace(identifier,
|
|
"<div id=\"" + identifier + "\"><input type=\"checkbox\" id=\"" + identifier + "\">" +
|
|
label + "</div>");
|
|
}
|
|
|
|
this.Html = html; // reload
|
|
|
|
return identifier;
|
|
}
|
|
|
|
public Dictionary<string,bool> getCheckedStates()
|
|
{
|
|
Dictionary<string, bool> output = new Dictionary<string, bool>();
|
|
foreach (HtmlElement item in textWebBrowser.Document.GetElementsByTagName("input"))
|
|
{
|
|
output.Add(item.GetAttribute("id"), item.GetAttribute("checked").Equals("True"));
|
|
}
|
|
return output;
|
|
}
|
|
|
|
public Dictionary<string,CheckboxItem> getCheckboxes()
|
|
{
|
|
Dictionary<string, CheckboxItem> output = new Dictionary<string, CheckboxItem>();
|
|
foreach (HtmlElement item in textWebBrowser.Document.GetElementsByTagName("input"))
|
|
{
|
|
output.Add(item.GetAttribute("id"),
|
|
new CheckboxItem(textWebBrowser.Document.GetElementById(item.GetAttribute("id") + "-label").InnerHtml,
|
|
item.GetAttribute("id"),
|
|
item.GetAttribute("checked").Equals("True")));
|
|
}
|
|
return output;
|
|
}
|
|
|
|
public void updateCheckedState(string identifier, bool checkedState)
|
|
{
|
|
string state;
|
|
if (checkedState)
|
|
{
|
|
state = "True";
|
|
}
|
|
else
|
|
{
|
|
state = ""; // "False" doesn't work
|
|
}
|
|
textWebBrowser.Document.GetElementById(identifier).SetAttribute("CHECKED", state);
|
|
}
|
|
}
|
|
}
|