258 строки
5.7 KiB
C#
258 строки
5.7 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Threading;
|
|
using System.Xml.Serialization;
|
|
using System.Xml;
|
|
using Gtk;
|
|
using Gecko;
|
|
|
|
class MsdnView : Window {
|
|
NodeStore Store;
|
|
|
|
public MsdnView () : base ("Msdn View")
|
|
{
|
|
DefaultSize = new Gdk.Size (1024,1024);
|
|
|
|
HPaned hb = new HPaned ();
|
|
|
|
Store = new NodeStore (typeof (TreeNode));
|
|
WebControl wc = new WebControl ();
|
|
ScrolledWindow sw = new ScrolledWindow ();
|
|
NodeView view = new NodeView (Store);
|
|
view.HeadersVisible = false;
|
|
view.AppendColumn ("Name", new CellRendererText (), "text", 0);
|
|
sw.WidthRequest = 300;
|
|
InitTree ();
|
|
Add (hb);
|
|
hb.Add (sw);
|
|
hb.Add (wc);
|
|
sw.Add (view);
|
|
|
|
// Events
|
|
DeleteEvent += delegate {
|
|
Application.Quit ();
|
|
};
|
|
|
|
view.NodeSelection.Changed += delegate {
|
|
TreeNode n = (TreeNode) view.NodeSelection.SelectedNode;
|
|
if (n == null)
|
|
return;
|
|
|
|
//
|
|
// Fool msdn's code that tries to detect if it
|
|
// is in a frame
|
|
//
|
|
string html = @"
|
|
<frameset>
|
|
<frame src='" + n.Href + @"?frame=true' />
|
|
</frameset>";
|
|
|
|
wc.OpenStream (MsdnClient.BaseUrl, "text/html");
|
|
wc.AppendData (html);
|
|
wc.CloseStream ();
|
|
|
|
};
|
|
view.RowExpanded += delegate (object o, RowExpandedArgs args) {
|
|
TreeNode n = (TreeNode) Store.GetNode (args.Path);
|
|
n.PopulateChildrenAsync ();
|
|
};
|
|
}
|
|
|
|
void InitTree ()
|
|
{
|
|
Tree t = Tree.GetDefault ();
|
|
|
|
foreach (TreeNode n in t.Children) {
|
|
Store.AddNode (n);
|
|
n.PopulateGuiChildren ();
|
|
}
|
|
}
|
|
|
|
static void Main ()
|
|
{
|
|
Application.Init ();
|
|
|
|
new MsdnView ().ShowAll ();
|
|
|
|
Application.Run ();
|
|
}
|
|
}
|
|
|
|
static class MsdnClient {
|
|
static readonly XmlSerializer [] serializers = XmlSerializer.FromTypes (new Type [] {typeof (Tree), typeof (TreeNode)});
|
|
static readonly XmlSerializer tree_ser = serializers [0];
|
|
static readonly XmlSerializer node_ser = serializers [1];
|
|
|
|
#if USE_WHIDBEY
|
|
// This is used by the dexplore.exe browser in whidbey beta
|
|
// 2. however, it seems to be extremely broken. Maybe they
|
|
// will fix it by RTM ;-).
|
|
public const string BaseUrl = "http://whidbey.msdn.microsoft.com";
|
|
public const string TopXml = "/library/en-us/toc/msdnlibWhidbeytest/top.xml";
|
|
#elif USE_WINFX
|
|
// Avalon, Indigo, Longhorn (or whatever lame names they got)'
|
|
// Sadly, because msft does not like to program for firefox,
|
|
// these don't actually do too much :-(.
|
|
public const string BaseUrl = "http://winfx.msdn.microsoft.com";
|
|
public const string TopXml = "/library/en-us/toc/winfxsdk/top.xml";
|
|
#else
|
|
// Standard msdn
|
|
public const string BaseUrl = "http://msdn.microsoft.com";
|
|
public const string TopXml = "/library/en-us/toc/msdnlib/top.xml";
|
|
#endif
|
|
|
|
public static Stream OpenRead (string s)
|
|
{
|
|
// Pretend you are using a 56k connection ;-)
|
|
// Thread.Sleep (1000);
|
|
|
|
WebClient wc = new WebClient ();
|
|
wc.BaseAddress = BaseUrl;
|
|
return wc.OpenRead (s);
|
|
}
|
|
|
|
public static Tree OpenTree (string s)
|
|
{
|
|
return (Tree) tree_ser.Deserialize (MsdnClient.OpenRead (s));
|
|
}
|
|
|
|
public static TreeNode OpenTreeNode (string s)
|
|
{
|
|
return (TreeNode) node_ser.Deserialize (MsdnClient.OpenRead (s));
|
|
}
|
|
}
|
|
|
|
public class DummyNode : TreeNode {
|
|
public DummyNode () {
|
|
Title = "Loading...";
|
|
}
|
|
}
|
|
|
|
public class Tree : TreeNode {
|
|
public static Tree GetDefault ()
|
|
{
|
|
return MsdnClient.OpenTree (MsdnClient.TopXml);
|
|
}
|
|
}
|
|
|
|
public class TreeNode : Gtk.TreeNode {
|
|
[XmlAttribute]
|
|
public string NodeId;
|
|
|
|
[XmlAttribute]
|
|
[TreeNodeValue (Column=0)]
|
|
public string Title;
|
|
|
|
[XmlAttribute]
|
|
public string Href;
|
|
|
|
[XmlAttribute]
|
|
public string ParentXmlSrc;
|
|
|
|
[XmlAttribute]
|
|
public string NodeXmlSrc;
|
|
|
|
TreeNode [] children;
|
|
|
|
[XmlElement ("TreeNode"), XmlElement ("Tree", typeof (Tree))]
|
|
public TreeNode [] Children {
|
|
get {
|
|
return children;
|
|
}
|
|
set {
|
|
if (value == null)
|
|
return;
|
|
ArrayList ar = new ArrayList (value.Length);
|
|
Flatten (ar, value);
|
|
children = (TreeNode []) ar.ToArray (typeof (TreeNode));
|
|
}
|
|
}
|
|
|
|
static void Flatten (ArrayList ar, TreeNode [] nodes)
|
|
{
|
|
foreach (TreeNode n in nodes) {
|
|
if (n is Tree) {
|
|
// Trees always seem to have nodes
|
|
// included in the xml, so I am not
|
|
// sure if the populaltion is
|
|
// necessary. But let's be safe
|
|
n.PopulateChildrenData ();
|
|
Flatten (ar, n.Children);
|
|
} else
|
|
ar.Add (n);
|
|
}
|
|
}
|
|
|
|
public void PopulateChildrenData ()
|
|
{
|
|
if (Children != null || NodeXmlSrc == null)
|
|
return;
|
|
|
|
TreeNode n;
|
|
if (this is Tree)
|
|
// I've never seen this, but just in case...
|
|
n = MsdnClient.OpenTree (NodeXmlSrc);
|
|
else
|
|
n = MsdnClient.OpenTreeNode (NodeXmlSrc);
|
|
|
|
Children = n.Children;
|
|
}
|
|
|
|
bool populating;
|
|
public void PopulateChildrenAsync ()
|
|
{
|
|
// Fastpath filled ones
|
|
if (Children != null || NodeXmlSrc == null)
|
|
return;
|
|
|
|
// Prevent race conditions. We don't need a CAS here
|
|
// because the population can only start from one
|
|
// thread. The issue is that this can be re-entered
|
|
// before we are done populating.
|
|
if (populating)
|
|
return;
|
|
populating = true;
|
|
|
|
ThreadPool.QueueUserWorkItem (delegate {
|
|
PopulateChildrenData ();
|
|
|
|
Application.Invoke (delegate {
|
|
PopulateGuiChildren ();
|
|
});
|
|
});
|
|
}
|
|
|
|
public void PopulateGuiChildren ()
|
|
{
|
|
if (Children != null) {
|
|
foreach (TreeNode c in Children) {
|
|
AddChild (c);
|
|
c.PopulateGuiChildren ();
|
|
}
|
|
|
|
if (this [0] is DummyNode)
|
|
this.RemoveChild (this [0] as DummyNode);
|
|
} else if (NodeXmlSrc != null && ChildCount == 0)
|
|
AddChild (new DummyNode ());
|
|
}
|
|
|
|
public override string ToString ()
|
|
{
|
|
return string.Format ("TreeNode [{0}:{1}]", NodeId, Title);
|
|
}
|
|
|
|
public string ToStringRecurse ()
|
|
{
|
|
string s = this.ToString ();
|
|
if (Children != null) {
|
|
s += "\n{\n";
|
|
foreach (TreeNode c in Children)
|
|
s += c.ToStringRecurse () + "\n";
|
|
s += "}\n";
|
|
}
|
|
return s;
|
|
}
|
|
}
|