386 lines
12 KiB
C#
386 lines
12 KiB
C#
|
using System;
|
|||
|
using System.ComponentModel;
|
|||
|
using System.Drawing;
|
|||
|
using System.IO;
|
|||
|
using System.Net;
|
|||
|
using System.Windows.Forms;
|
|||
|
|
|||
|
namespace bzit.bomg
|
|||
|
{
|
|||
|
public class BookmarkItem
|
|||
|
{
|
|||
|
public delegate void RetrieveCompleted(BookmarkItem sender, bool hasError, string message);
|
|||
|
|
|||
|
protected const char pathSeparator = '|';
|
|||
|
|
|||
|
protected TreeNode treeNode = null;
|
|||
|
protected BackgroundWorker mainThread = null;
|
|||
|
|
|||
|
protected bool hasRetrieveError = false;
|
|||
|
protected string retrieveErrorMessage = null;
|
|||
|
|
|||
|
public BookmarkItem()
|
|||
|
{
|
|||
|
this.Clear();
|
|||
|
}
|
|||
|
|
|||
|
public new string ToString()
|
|||
|
{
|
|||
|
string rv = "";
|
|||
|
rv += "Name=" + this.Fullpath + Environment.NewLine;
|
|||
|
rv += "Address=" + this.Address + Environment.NewLine;
|
|||
|
rv += "Description=" + this.Description + Environment.NewLine;
|
|||
|
rv += "Created=" + this.Created?.Trim();
|
|||
|
|
|||
|
return rv;
|
|||
|
}
|
|||
|
|
|||
|
#region public properties
|
|||
|
|
|||
|
public TreeNode TreeViewNode
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return this.treeNode;
|
|||
|
}
|
|||
|
|
|||
|
set
|
|||
|
{
|
|||
|
this.treeNode = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Bitmap Icon
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (this.IconData == null)
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
Image img = Image.FromStream(new MemoryStream(this.IconData));
|
|||
|
return new Bitmap(img, 16, 16);
|
|||
|
}
|
|||
|
catch
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[DefaultValue(null)]
|
|||
|
public byte[] IconData { get; set; }
|
|||
|
|
|||
|
public string Created { get; set; }
|
|||
|
|
|||
|
public string TempName { get; set; }
|
|||
|
|
|||
|
public string Fullpath { get; set; }
|
|||
|
|
|||
|
public string IconAddress { get; set; }
|
|||
|
|
|||
|
public string Address { get; set; }
|
|||
|
|
|||
|
public string Description { get; set; }
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region public methods
|
|||
|
|
|||
|
public void Clear()
|
|||
|
{
|
|||
|
this.Fullpath = string.Empty;
|
|||
|
this.Address = string.Empty;
|
|||
|
this.Description = string.Empty;
|
|||
|
this.Created = string.Empty;
|
|||
|
|
|||
|
if (this.mainThread == null)
|
|||
|
{
|
|||
|
this.mainThread = new BackgroundWorker();
|
|||
|
this.mainThread.WorkerReportsProgress = this.mainThread.WorkerSupportsCancellation = true;
|
|||
|
this.mainThread.DoWork += retrieveWorker_DoWork;
|
|||
|
this.mainThread.RunWorkerCompleted += retrieveWorker_RunWorkerCompleted;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string GetName()
|
|||
|
{
|
|||
|
if (this.Fullpath.Contains(pathSeparator.ToString()))
|
|||
|
{
|
|||
|
return PathDecode(this.Fullpath.Substring(this.Fullpath.LastIndexOf(pathSeparator) + 1)).Trim();
|
|||
|
}
|
|||
|
|
|||
|
return PathDecode(this.Fullpath).Trim();
|
|||
|
}
|
|||
|
|
|||
|
public void ChangeName(string newName)
|
|||
|
{
|
|||
|
string prefix = (this.Fullpath.Contains(pathSeparator.ToString()) ? this.Fullpath.Substring(0, this.Fullpath.IndexOf(pathSeparator)).Trim() : string.Empty);
|
|||
|
|
|||
|
this.Fullpath = string.Concat(prefix, pathSeparator, PathEncode(newName.Trim()));
|
|||
|
}
|
|||
|
|
|||
|
public void GetFaviconAddress()
|
|||
|
{
|
|||
|
hasRetrieveError = false;
|
|||
|
retrieveErrorMessage = string.Empty;
|
|||
|
|
|||
|
this.IconData = null;
|
|||
|
|
|||
|
WebClient webClient = new WebClient();
|
|||
|
webClient.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
|
|||
|
|
|||
|
string sourceCode = string.Empty;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
sourceCode = webClient.DownloadString(this.Address);
|
|||
|
}
|
|||
|
catch (Exception exc)
|
|||
|
{
|
|||
|
hasRetrieveError = true;
|
|||
|
retrieveErrorMessage = exc.Message;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
HtmlAgilityPack.HtmlDocument document = new HtmlAgilityPack.HtmlDocument();
|
|||
|
document.LoadHtml(sourceCode);
|
|||
|
|
|||
|
// parse icon
|
|||
|
HtmlAgilityPack.HtmlNodeCollection hnc = document.DocumentNode.SelectNodes("//link");
|
|||
|
if (hnc != null)
|
|||
|
{
|
|||
|
foreach (HtmlAgilityPack.HtmlNode node in hnc)
|
|||
|
{
|
|||
|
if (node.Attributes["rel"] == null)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
string nodeAttr = node.Attributes["rel"].Value.Trim();
|
|||
|
if (nodeAttr.ToLower().Contains("icon"))
|
|||
|
{
|
|||
|
if (!string.IsNullOrEmpty(this.IconAddress))
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (node.Attributes["href"] != null)
|
|||
|
{
|
|||
|
Uri iconPath;
|
|||
|
bool rv = Uri.TryCreate(new Uri(this.Address), WebUtility.HtmlDecode(node.Attributes["href"].Value).Trim(), out iconPath);
|
|||
|
if (rv)
|
|||
|
{
|
|||
|
this.IconAddress = iconPath.ToString();
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// default favicon
|
|||
|
if (string.IsNullOrEmpty(this.IconAddress))
|
|||
|
{
|
|||
|
Uri iconPath;
|
|||
|
if (Uri.TryCreate(new Uri(this.Address), "/favicon.ico", out iconPath))
|
|||
|
{
|
|||
|
this.IconAddress = iconPath.ToString();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// load icon image
|
|||
|
if (!string.IsNullOrEmpty(this.IconAddress))
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
this.IconData = webClient.DownloadData(this.IconAddress);
|
|||
|
|
|||
|
if (!RyzStudio.IO.FileType.IsImage(this.IconData))
|
|||
|
{
|
|||
|
this.IconData = null;
|
|||
|
}
|
|||
|
}
|
|||
|
catch
|
|||
|
{
|
|||
|
// do nothing
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void RetrieveAsync()
|
|||
|
{
|
|||
|
if (this.mainThread.IsBusy)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
this.mainThread.RunWorkerAsync();
|
|||
|
}
|
|||
|
|
|||
|
public void RetrieveAsync(string address)
|
|||
|
{
|
|||
|
if (this.mainThread.IsBusy)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
this.Address = address;
|
|||
|
this.mainThread.RunWorkerAsync();
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region events & delegate
|
|||
|
|
|||
|
public RetrieveCompleted OnRetrieveCompleted = null;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
protected void retrieveWorker_DoWork(object sender, DoWorkEventArgs e)
|
|||
|
{
|
|||
|
hasRetrieveError = false;
|
|||
|
retrieveErrorMessage = string.Empty;
|
|||
|
|
|||
|
this.IconData = null;
|
|||
|
this.Fullpath = string.Empty;
|
|||
|
this.Description = string.Empty;
|
|||
|
this.IconAddress = string.Empty;
|
|||
|
|
|||
|
WebClient webClient = new WebClient();
|
|||
|
webClient.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
|
|||
|
|
|||
|
string sourceCode = string.Empty;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
sourceCode = webClient.DownloadString(this.Address);
|
|||
|
}
|
|||
|
catch (Exception exc)
|
|||
|
{
|
|||
|
hasRetrieveError = true;
|
|||
|
retrieveErrorMessage = exc.Message;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
HtmlAgilityPack.HtmlDocument document = new HtmlAgilityPack.HtmlDocument();
|
|||
|
document.LoadHtml(sourceCode);
|
|||
|
|
|||
|
// parse title
|
|||
|
HtmlAgilityPack.HtmlNodeCollection hnc = document.DocumentNode.SelectNodes("//title");
|
|||
|
if (hnc != null)
|
|||
|
{
|
|||
|
if (hnc.Count > 0)
|
|||
|
{
|
|||
|
this.TempName = WebUtility.HtmlDecode(hnc[0].InnerHtml).Trim();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// parse description
|
|||
|
hnc = document.DocumentNode.SelectNodes("//meta");
|
|||
|
if (hnc != null)
|
|||
|
{
|
|||
|
foreach (HtmlAgilityPack.HtmlNode node in hnc)
|
|||
|
{
|
|||
|
if (node.Attributes["name"] == null)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
string nodeAttr = node.Attributes["name"].Value.Trim();
|
|||
|
if (nodeAttr.Equals("description", StringComparison.CurrentCultureIgnoreCase))
|
|||
|
{
|
|||
|
if (!string.IsNullOrEmpty(this.Description))
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (node.Attributes["content"] != null)
|
|||
|
{
|
|||
|
this.Description = WebUtility.HtmlDecode(node.Attributes["content"].Value).Trim();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// parse icon
|
|||
|
hnc = document.DocumentNode.SelectNodes("//link");
|
|||
|
if (hnc != null)
|
|||
|
{
|
|||
|
foreach (HtmlAgilityPack.HtmlNode node in hnc)
|
|||
|
{
|
|||
|
if (node.Attributes["rel"] == null)
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
string nodeAttr = node.Attributes["rel"].Value.Trim();
|
|||
|
if (nodeAttr.ToLower().Contains("icon"))
|
|||
|
{
|
|||
|
if (!string.IsNullOrEmpty(this.IconAddress))
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (node.Attributes["href"] != null)
|
|||
|
{
|
|||
|
Uri iconPath;
|
|||
|
bool rv = Uri.TryCreate(new Uri(this.Address), WebUtility.HtmlDecode(node.Attributes["href"].Value).Trim(), out iconPath);
|
|||
|
if (rv)
|
|||
|
{
|
|||
|
this.IconAddress = iconPath.ToString();
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// default favicon
|
|||
|
if (string.IsNullOrEmpty(this.IconAddress))
|
|||
|
{
|
|||
|
Uri iconPath;
|
|||
|
if (Uri.TryCreate(new Uri(this.Address), "/favicon.ico", out iconPath))
|
|||
|
{
|
|||
|
this.IconAddress = iconPath.ToString();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// load icon image
|
|||
|
if (!string.IsNullOrEmpty(this.IconAddress))
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
this.IconData = webClient.DownloadData(this.IconAddress);
|
|||
|
|
|||
|
if (!RyzStudio.IO.FileType.IsImage(this.IconData))
|
|||
|
{
|
|||
|
this.IconData = null;
|
|||
|
}
|
|||
|
}
|
|||
|
catch
|
|||
|
{
|
|||
|
// do nothing
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected void retrieveWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
|
|||
|
{
|
|||
|
this.OnRetrieveCompleted?.Invoke(this, hasRetrieveError, retrieveErrorMessage);
|
|||
|
}
|
|||
|
|
|||
|
// protected string CustomDecode(string text) { return WebUtility.HtmlDecode(text); }
|
|||
|
|
|||
|
////protected string PathEncode(string text) { return RyzStudio.String.EncodeTo64(text); }
|
|||
|
////protected string PathDecode(string text) { return RyzStudio.String.DecodeFrom64(text); }
|
|||
|
protected string PathEncode(string text) { return System.Web.HttpUtility.UrlEncodeUnicode(text); }
|
|||
|
protected string PathDecode(string text) { return System.Web.HttpUtility.UrlDecode(text); }
|
|||
|
}
|
|||
|
}
|