tag inserted
ExtLoop:
ini = text_low.IndexOf (term_low, ini+1, text_low.Length-ini-1);
}
highlight_text = null; //only highlight when a search result is clicked
return sb.ToString();
}
//
// Invoked when the mouse is over a link
//
string last_url = "";
public void OnUrlMouseOver (object o, EventArgs args)
{
string new_url = CurrentTab.html.Url;
if (new_url == null)
new_url = "";
if (new_url != last_url){
statusbar.Pop (context_id);
statusbar.Push (context_id, new_url);
last_url = new_url;
}
}
void keypress_event_cb (object o, KeyPressEventArgs args)
{
switch (args.Event.Key) {
case Gdk.Key.Left:
if (((Gdk.ModifierType) args.Event.State &
Gdk.ModifierType.Mod1Mask) !=0)
CurrentTab.history.BackClicked (this, EventArgs.Empty);
args.RetVal = true;
break;
case Gdk.Key.Right:
if (((Gdk.ModifierType) args.Event.State &
Gdk.ModifierType.Mod1Mask) !=0)
CurrentTab.history.ForwardClicked (this, EventArgs.Empty);
args.RetVal = true;
break;
case Gdk.Key.Control_L:
case Gdk.Key.Control_R:
HoldCtrl = true;
break;
case Gdk.Key.Page_Up:
if (HoldCtrl)
tabs_nb.PrevPage();
break;
case Gdk.Key.Page_Down:
if (HoldCtrl)
tabs_nb.NextPage();
break;
}
}
void keyrelease_event_cb (object o, KeyReleaseEventArgs args)
{
switch (args.Event.Key) {
case Gdk.Key.Control_L:
case Gdk.Key.Control_R:
HoldCtrl = false;
break;
}
}
void delete_event_cb (object o, DeleteEventArgs args)
{
Application.Quit ();
}
void on_print_activate (object sender, EventArgs e)
{
// desactivate css temporary
if (UseGecko)
HelpSource.use_css = false;
string url = CurrentUrl;
string html;
Node cur = CurrentTab.CurrentNode;
Node n;
// deal with the two types of urls
if (cur.tree.HelpSource != null) {
html = cur.tree.HelpSource.GetText (url, out n);
if (html == null)
html = help_tree.RenderUrl (url, out n);
} else {
html = help_tree.RenderUrl (url, out n);
}
// sending Html to be printed.
if (html != null)
CurrentTab.html.Print (html);
if (UseGecko)
HelpSource.use_css = true;
}
void OnCommentsActivate (object o, EventArgs args)
{
SettingsHandler.Settings.ShowComments = comments1.Active;
// postcomment.Sensitive = comments1.Active;
// refresh, so we can see the comments
if (CurrentTab != null && CurrentTab.history != null) // catch the case when we are currently loading
CurrentTab.history.ActivateCurrent ();
}
void OnInheritedMembersActivate (object o, EventArgs args)
{
SettingsHandler.Settings.ShowInheritedMembers = showinheritedmembers.Active;
if (CurrentTab != null && CurrentTab.history != null) // catch the case when we are currently loading
CurrentTab.history.ActivateCurrent ();
}
void OnEditingActivate (object o, EventArgs args)
{
SettingsHandler.Settings.EnableEditing = editing1.Active;
// refresh, so we can see the [edit] things
if (CurrentTab != null && CurrentTab.history != null) // catch the case when we are currently loading
CurrentTab.history.ActivateCurrent ();
}
void OnCollapseActivate (object o, EventArgs args)
{
reference_tree.CollapseAll ();
reference_tree.ExpandRow (new TreePath ("0"), false);
}
//
// Invoked when the index_entry Entry line content changes
//
public void OnIndexEntryChanged (object sender, EventArgs a)
{
if (index_browser != null)
index_browser.SearchClosest (index_entry.Text);
}
//
// Invoked when the user presses enter on the index_entry
//
public void OnIndexEntryActivated (object sender, EventArgs a)
{
if (index_browser != null)
index_browser.LoadSelected ();
}
//
// Invoked when the user presses a key on the index_entry
//
public void OnIndexEntryKeyPress (object o, KeyPressEventArgs args)
{
args.RetVal = true;
switch (args.Event.Key) {
case Gdk.Key.Up:
if (matches.Visible == true && index_browser.match_list.Selected != 0)
{
index_browser.match_list.Selected--;
} else {
index_browser.index_list.Selected--;
if (matches.Visible == true)
index_browser.match_list.Selected = index_browser.match_model.Rows - 1;
}
break;
case Gdk.Key.Down:
if (matches.Visible == true && index_browser.match_list.Selected + 1 != index_browser.match_model.Rows) {
index_browser.match_list.Selected++;
} else {
index_browser.index_list.Selected++;
if (matches.Visible == true)
index_browser.match_list.Selected = 0;
}
break;
default:
args.RetVal = false;
break;
}
}
//
// For the accel keystroke
//
public void OnIndexEntryFocused (object sender, EventArgs a)
{
nb.Page = 1;
}
//
// Invoked from File/Quit menu entry.
//
void OnQuitActivate (object sender, EventArgs a)
{
Application.Quit ();
}
//
// Invoked by Edit/Cut menu entry.
//
void OnCutActivate (object sender, EventArgs a)
{
if (CurrentTab.Tab_mode == Mode.Editor) {
Clipboard cb = Clipboard.Get (Gdk.Selection.Clipboard);
CurrentTab.text_editor.Buffer.CutClipboard (cb, true);
}
}
//
// Invoked by Edit/Copy menu entry.
//
void OnCopyActivate (object sender, EventArgs a)
{
if (CurrentTab.Tab_mode == Mode.Viewer)
CurrentTab.html.Copy ();
else {
Clipboard cb = Clipboard.Get (Gdk.Selection.Clipboard);
CurrentTab.text_editor.Buffer.CopyClipboard (cb);
}
}
//
// Invoked by Edit/Paste menu entry.
//
void OnPasteActivate (object sender, EventArgs a)
{
Clipboard cb = Clipboard.Get (Gdk.Selection.Clipboard);
if (!cb.WaitIsTextAvailable ())
return;
//string text = cb.WaitForText ();
//CurrentTab.text_editor.Buffer.InsertAtCursor (text);
CurrentTab.text_editor.Buffer.PasteClipboard (cb);
}
class About {
[Glade.Widget] Window about;
[Glade.Widget] Image logo_image;
[Glade.Widget] Label label_version;
static About AboutBox;
Browser parent;
About (Browser parent)
{
Glade.XML ui = new Glade.XML (null, "browser.glade", "about", null);
ui.Autoconnect (this);
this.parent = parent;
about.TransientFor = parent.window1;
Gdk.Pixbuf icon = new Gdk.Pixbuf (null, "monodoc.png");
if (icon != null) {
about.Icon = icon;
logo_image.Pixbuf = icon;
}
Assembly assembly = Assembly.GetExecutingAssembly ();
label_version.Markup = String.Format ("Version: {0}", assembly.GetName ().Version.ToString ());
}
void OnOkClicked (object sender, EventArgs a)
{
about.Hide ();
}
//
// Called on the Window delete icon clicked
//
void OnDelete (object sender, DeleteEventArgs a)
{
AboutBox = null;
}
static public void Show (Browser parent)
{
if (AboutBox == null)
AboutBox = new About (parent);
AboutBox.about.Show ();
}
}
//
// Hooked up from Glade
//
void OnAboutActivate (object sender, EventArgs a)
{
About.Show (this);
}
void OnUpload (object sender, EventArgs a)
{
string key = SettingsHandler.Settings.Key;
if (key == null || key == "")
ConfigWizard.Run (this);
else
DoUpload ();
}
void DoUpload ()
{
Upload.Run (this);
}
class Upload {
enum State {
GetSerial,
PrepareUpload,
SerialError,
VersionError,
SubmitError,
NetworkError,
Done
}
[Glade.Widget] Dialog upload_dialog;
[Glade.Widget] Label status;
[Glade.Widget] Button cancel;
State state;
ThreadNotify tn;
WebClientAsyncResult war;
ContributionsSoap d;
int serial;
public static void Run (Browser browser)
{
new Upload (browser);
}
Upload (Browser browser)
{
tn = new ThreadNotify (new ReadyEvent (Update));
Glade.XML ui = new Glade.XML (null, "browser.glade", "upload_dialog", null);
ui.Autoconnect (this);
d = new ContributionsSoap ();
if (Environment.GetEnvironmentVariable ("MONODOCTESTING") == null)
d.Url = "http://www.go-mono.com/docs/server.asmx";
status.Text = "Checking Server version";
war = (WebClientAsyncResult) d.BeginCheckVersion (1, new AsyncCallback (VersionChecked), null);
}
void Update ()
{
Console.WriteLine ("In Update: " + state);
switch (state){
case State.NetworkError:
status.Text = "A network error ocurred";
cancel.Label = "Close";
return;
case State.VersionError:
status.Text = "Server has a different version, upgrade your MonoDoc";
cancel.Label = "Close";
return;
case State.GetSerial:
war = (WebClientAsyncResult) d.BeginGetSerial (
SettingsHandler.Settings.Email, SettingsHandler.Settings.Key,
new AsyncCallback (GetSerialDone), null);
return;
case State.SerialError:
status.Text = "Error obtaining serial number from server for this account";
cancel.Label = "Close";
return;
case State.SubmitError:
status.Text = "There was a problem with the documentation uploaded";
cancel.Label = "Close";
return;
case State.PrepareUpload:
GlobalChangeset cs = EditingUtils.GetChangesFrom (serial);
if (cs == null){
status.Text = "No new contributions";
cancel.Label = "Close";
return;
}
CopyXmlNodeWriter w = new CopyXmlNodeWriter ();
GlobalChangeset.serializer.Serialize (w, cs);
Console.WriteLine ("Uploading...");
status.Text = String.Format ("Uploading {0} contributions", cs.Count);
XmlDocument dd = (XmlDocument) w.Document;
war = (WebClientAsyncResult) d.BeginSubmit (
SettingsHandler.Settings.Email, SettingsHandler.Settings.Key,
((XmlDocument) w.Document).DocumentElement,
new AsyncCallback (UploadDone), null);
return;
case State.Done:
status.Text = "All contributions uploaded";
cancel.Label = "Close";
SettingsHandler.Settings.SerialNumber = serial;
SettingsHandler.Save ();
return;
}
}
void UploadDone (IAsyncResult iar)
{
try {
int result = d.EndSubmit (iar);
war = null;
if (result < 0)
state = State.SubmitError;
else {
state = State.Done;
serial = result;
}
} catch (Exception e) {
state = State.NetworkError;
Console.WriteLine ("Upload: " + e);
}
if (tn != null)
tn.WakeupMain ();
}
void GetSerialDone (IAsyncResult iar)
{
try {
serial = d.EndGetSerial (iar);
war = null;
if (serial < 0)
state = State.SerialError;
else
state = State.PrepareUpload;
} catch (Exception e) {
Console.WriteLine ("Serial: " + e);
state = State.NetworkError;
}
if (tn != null)
tn.WakeupMain ();
}
void VersionChecked (IAsyncResult iar)
{
try {
int ver = d.EndCheckVersion (iar);
war = null;
if (ver != 0)
state = State.VersionError;
else
state = State.GetSerial;
} catch (Exception e) {
Console.WriteLine ("Version: " + e);
state = State.NetworkError;
}
if (tn != null)
tn.WakeupMain ();
}
void Cancel_Clicked (object sender, EventArgs a)
{
if (war != null)
war.Abort ();
war = null;
state = State.Done;
upload_dialog.Destroy ();
upload_dialog = null;
tn = null;
}
}
class ConfigWizard {
static ConfigWizard config_wizard;
[Glade.Widget] Window window_config_wizard;
[Glade.Widget] Notebook notebook;
[Glade.Widget] Button button_email_ok;
[Glade.Widget] Entry entry_email;
[Glade.Widget] Entry entry_password;
Browser parent;
ContributionsSoap d;
WebClientAsyncResult war;
ThreadNotify tn;
int new_page;
public static void Run (Browser browser)
{
if (config_wizard == null)
config_wizard = new ConfigWizard (browser);
return;
}
ConfigWizard (Browser browser)
{
tn = new ThreadNotify (new ReadyEvent (UpdateNotebookPage));
Glade.XML ui = new Glade.XML (null, "browser.glade", "window_config_wizard", null);
ui.Autoconnect (this);
//notebook.ShowTabs = false;
parent = browser;
window_config_wizard.TransientFor = browser.window1;
d = new ContributionsSoap ();
if (Environment.GetEnvironmentVariable ("MONODOCTESTING") == null)
d.Url = "http://www.go-mono.com/docs/server.asmx";
notebook.Page = 8;
war = (WebClientAsyncResult) d.BeginCheckVersion (1, new AsyncCallback (VersionChecked), null);
}
void NetworkError ()
{
new_page = 9;
tn.WakeupMain ();
}
void VersionChecked (IAsyncResult iar)
{
int ver = -1;
try {
if (notebook.Page != 8)
return;
ver = d.EndCheckVersion (iar);
if (ver != 0)
new_page = 10;
else
new_page = 0;
tn.WakeupMain ();
} catch (Exception e){
Console.WriteLine ("Error" + e);
NetworkError ();
}
}
//
// Called on the Window delete icon clicked
//
void OnDelete (object sender, DeleteEventArgs a)
{
config_wizard = null;
}
//
// called when the license is approved
//
void OnPage1_Clicked (object sender, EventArgs a)
{
button_email_ok.Sensitive = false;
notebook.Page = 1;
}
//
// Request the user registration.
//
void OnPage2_Clicked (object sender, EventArgs a)
{
notebook.Page = 2;
SettingsHandler.Settings.Email = entry_email.Text;
war = (WebClientAsyncResult) d.BeginRegister (entry_email.Text, new AsyncCallback (RegisterDone), null);
}
void UpdateNotebookPage ()
{
notebook.Page = new_page;
}
void RegisterDone (IAsyncResult iar)
{
int code;
try {
Console.WriteLine ("Registration done");
code = d.EndRegister (iar);
if (code != 0 && code != -2){
NetworkError ();
return;
}
new_page = 4;
} catch {
new_page = 3;
}
tn.WakeupMain ();
}
void PasswordContinue_Clicked (object sender, EventArgs a)
{
notebook.Page = 5;
SettingsHandler.Settings.Key = entry_password.Text;
war = (WebClientAsyncResult) d.BeginGetSerial (entry_email.Text, entry_password.Text, new AsyncCallback (GetSerialDone), null);
}
void GetSerialDone (IAsyncResult iar)
{
try {
int last = d.EndGetSerial (iar);
if (last == -1){
SettingsHandler.Settings.Key = "";
new_page = 11;
tn.WakeupMain ();
return;
}
SettingsHandler.Settings.SerialNumber = last;
new_page = 6;
tn.WakeupMain ();
} catch {
NetworkError ();
}
}
void AccountRequestCancel_Clicked (object sender, EventArgs a)
{
war.Abort ();
notebook.Page = 7;
}
void SerialRequestCancel_Clicked (object sender, EventArgs a)
{
war.Abort ();
notebook.Page = 7;
}
void LoginRequestCancel_Clicked (object sender, EventArgs a)
{
war.Abort ();
notebook.Page = 7;
}
//
// Called when the user clicks `ok' on a terminate page
//
void Terminate_Clicked (object sender, EventArgs a)
{
window_config_wizard.Destroy ();
config_wizard = null;
}
// Called when the registration process has been successful
void Completed_Clicked (object sender, EventArgs a)
{
window_config_wizard.Destroy ();
config_wizard = null;
try {
Console.WriteLine ("Saving");
SettingsHandler.Save ();
parent.DoUpload ();
} catch (Exception e) {
MessageDialog md = new MessageDialog (null,
DialogFlags.DestroyWithParent,
MessageType.Error,
ButtonsType.Close, "Error Saving settings\n" +
e.ToString ());
}
}
void OnEmail_Changed (object sender, EventArgs a)
{
string text = entry_email.Text;
if (text.IndexOf ("@") != -1 && text.Length > 3)
button_email_ok.Sensitive = true;
}
}
void OnContributionStatistics (object sender, EventArgs a)
{
string email = SettingsHandler.Settings.Email;
string key = SettingsHandler.Settings.Key;
if (key == null || key == "") {
MessageDialog md = new MessageDialog (null,
DialogFlags.DestroyWithParent,
MessageType.Info,
ButtonsType.Close,
"You have not obtained or used a contribution key yet.");
md.Title = "No contribution key";
md.Run();
md.Destroy();
}
else
ContributionStatus.GetStatus (email, key);
}
class ContributionStatus {
enum State {
GetStatusError,
NetworkError,
Done
}
State state;
Status status;
string contributoremail;
ThreadNotify tn;
WebClientAsyncResult war;
ContributionsSoap d;
public static void GetStatus (string email, string key)
{
new ContributionStatus(email, key);
}
ContributionStatus (string email, string key)
{
tn = new ThreadNotify (new ReadyEvent (Update));
d = new ContributionsSoap ();
if (Environment.GetEnvironmentVariable ("MONODOCTESTING") == null)
d.Url = "http://www.go-mono.com/docs/server.asmx";
war = (WebClientAsyncResult) d.BeginGetStatus (email, key, new AsyncCallback (GetStatusDone), null);
contributoremail = email;
}
void Update ()
{
MessageDialog md = null;
switch (state) {
case State.GetStatusError:
md = new MessageDialog (null,
DialogFlags.DestroyWithParent,
MessageType.Error, ButtonsType.Close,
"Server returned error while requesting contributor statistics");
md.Title = "Contribution Statistics Error Occurred";
break;
case State.NetworkError:
md = new MessageDialog (null,
DialogFlags.DestroyWithParent,
MessageType.Error, ButtonsType.Close,
"Network error occurred while requesting contributor statistics");
md.Title = "Contribution Statistics Error Occurred";
break;
case State.Done:
md = new MessageDialog (null,
DialogFlags.DestroyWithParent,
MessageType.Info, ButtonsType.Close,
"Contribution statistics for " + contributoremail +
"\n\nTotal contributions: " + status.Contributions +
"\nContributions committed: " + status.Commited +
"\nContributions pending: " + status.Pending);
md.Title = "Contribution Statistics";
break;
}
md.Run();
md.Destroy();
}
void GetStatusDone (IAsyncResult iar)
{
try {
status = d.EndGetStatus (iar);
war = null;
if (status == null)
state = State.GetStatusError;
else
state = State.Done;
} catch (Exception e) {
state = State.NetworkError;
Console.WriteLine ("Error getting status: " + e);
}
if (tn != null)
tn.WakeupMain ();
}
}
class NewComment {
[Glade.Widget] Window newcomment;
[Glade.Widget] Entry entry;
static NewComment NewCommentBox;
Browser parent;
NewComment (Browser browser)
{
Glade.XML ui = new Glade.XML (null, "browser.glade", "newcomment", null);
ui.Autoconnect (this);
parent = browser;
newcomment.TransientFor = browser.window1;
}
void OnOkClicked (object sender, EventArgs a)
{
CommentService service = new CommentService();
// todo
newcomment.Hide ();
}
void OnCancelClicked (object sender, EventArgs a)
{
newcomment.Hide ();
}
//
// Called on the Window delete icon clicked
//
void OnDelete (object sender, DeleteEventArgs a)
{
NewCommentBox = null;
}
static public void Show (Browser browser)
{
if (NewCommentBox == null)
NewCommentBox = new NewComment (browser);
NewCommentBox.newcomment.Show ();
}
}
void OnNewComment (object sender, EventArgs a)
{
NewComment.Show (this);
}
class Lookup {
[Glade.Widget] Window lookup;
[Glade.Widget] Entry entry;
static Lookup LookupBox;
Browser parent;
Lookup (Browser browser)
{
Glade.XML ui = new Glade.XML (null, "browser.glade", "lookup", null);
ui.Autoconnect (this);
parent = browser;
lookup.TransientFor = browser.window1;
}
void OnOkClicked (object sender, EventArgs a)
{
string text = entry.Text;
if (text != "")
parent.LoadUrl (entry.Text);
lookup.Hide ();
}
//
// Called on the Window delete icon clicked
//
void OnDelete(object sender, DeleteEventArgs a)
{
LookupBox = null;
}
static public void Show (Browser browser)
{
if (LookupBox == null)
LookupBox = new Lookup (browser);
LookupBox.lookup.Show ();
}
}
//
// Invoked by File/LookupURL menu entry.
//
void OnLookupURL (object sender, EventArgs a)
{
Lookup.Show (this);
}
//
// Invoked by Edit/Select All menu entry.
//
void OnSelectAllActivate (object sender, EventArgs a)
{
CurrentTab.html.SelectAll ();
}
//
// Invoked by New Tab menu entry.
//
void OnNewTab (object sender, EventArgs a)
{
AddTab();
}
}
//
// This class implements the tree browser
//
public class TreeBrowser {
Browser browser;
TreeView tree_view;
TreeStore store;
RootTree help_tree;
TreeIter root_iter;
//
// This hashtable maps an iter to its node.
//
Hashtable iter_to_node;
//
// This hashtable maps the node to its iter
//
Hashtable node_to_iter;
//
// Maps a node to its TreeIter parent
//
Hashtable node_parent;
public TreeBrowser (RootTree help_tree, TreeView reference_tree, Browser browser)
{
this.browser = browser;
tree_view = reference_tree;
iter_to_node = new Hashtable ();
node_to_iter = new Hashtable ();
node_parent = new Hashtable ();
// Setup the TreeView
tree_view.AppendColumn ("name_col", new CellRendererText (), "text", 0);
// Bind events
tree_view.RowExpanded += new Gtk.RowExpandedHandler (RowExpanded);
tree_view.Selection.Changed += new EventHandler (RowActivated);
tree_view.RowActivated += new Gtk.RowActivatedHandler (RowClicked);
// Setup the model
this.help_tree = help_tree;
store = new TreeStore (typeof (string));
root_iter = store.AppendValues ("Mono Documentation");
iter_to_node [root_iter] = help_tree;
node_to_iter [help_tree] = root_iter;
PopulateNode (help_tree, root_iter);
reference_tree.Model = store;
}
void PopulateNode (Node node, TreeIter parent)
{
if (node.Nodes == null)
return;
TreeIter iter;
foreach (Node n in node.Nodes){
iter = store.AppendValues (parent, n.Caption);
iter_to_node [iter] = n;
node_to_iter [n] = iter;
}
}
Hashtable populated = new Hashtable ();
void RowExpanded (object o, Gtk.RowExpandedArgs args)
{
Node result = iter_to_node [args.Iter] as Node;
Open (result);
}
void RowClicked (object o, Gtk.RowActivatedArgs args)
{
Gtk.TreeModel model;
Gtk.TreeIter iter;
Gtk.TreePath path = args.Path;
tree_view.Selection.GetSelected (out model, out iter);
Node result = iter_to_node [iter] as Node;
if (!tree_view.GetRowExpanded (path)) {
tree_view.ExpandRow (path, false);
Open (result);
} else {
tree_view.CollapseRow (path);
}
}
void Open (Node node)
{
if (node == null){
Console.Error.WriteLine ("Expanding something that I do not know about");
return;
}
if (populated.Contains (node))
return;
//
// We need to populate data on a second level
//
if (node.Nodes == null)
return;
foreach (Node n in node.Nodes){
PopulateNode (n, (TreeIter) node_to_iter [n]);
}
populated [node] = true;
}
void PopulateTreeFor (Node n)
{
if (populated [n] == null){
if (n.Parent != null) {
OpenTree (n.Parent);
}
}
Open (n);
}
public void OpenTree (Node n)
{
PopulateTreeFor (n);
TreeIter iter = (TreeIter) node_to_iter [n];
TreePath path = store.GetPath (iter);
}
public Node SelectedNode
{
get {
Gtk.TreeIter iter;
Gtk.TreeModel model;
if (tree_view.Selection.GetSelected (out model, out iter))
return (Node) iter_to_node [iter];
else
return null;
}
}
public void ShowNode (Node n)
{
if (node_to_iter [n] == null){
OpenTree (n);
if (node_to_iter [n] == null){
Console.Error.WriteLine ("Internal error: no node to iter mapping");
return;
}
}
TreeIter iter = (TreeIter) node_to_iter [n];
TreePath path = store.GetPath (iter);
tree_view.ExpandToPath (path);
IgnoreRowActivated = true;
tree_view.Selection.SelectPath (path);
IgnoreRowActivated = false;
tree_view.ScrollToCell (path, null, false, 0.5f, 0.0f);
}
class NodePageVisit : PageVisit {
Browser browser;
Node n;
string url;
public NodePageVisit (Browser browser, Node n, string url)
{
if (n == null)
throw new Exception ("N is null");
this.browser = browser;
this.n = n;
this.url = url;
}
public override void Go ()
{
string res;
Node x;
// The root tree has no help source
if (n.tree.HelpSource != null)
res = n.tree.HelpSource.GetText (url, out x);
else
res = ((RootTree)n.tree).RenderUrl (url, out x);
browser.Render (res, n, url);
}
}
bool IgnoreRowActivated = false;
//
// This has to handle two kinds of urls: those encoded in the tree
// file, which are used to quickly lookup information precisely
// (things like "ecma:0"), and if that fails, it uses the more expensive
// mechanism that walks trees to find matches
//
void RowActivated (object sender, EventArgs a)
{
//browser.CurrentTab.SetMode (Mode.Viewer);
if (IgnoreRowActivated)
return;
Gtk.TreeIter iter;
Gtk.TreeModel model;
if (tree_view.Selection.GetSelected (out model, out iter)){
Node n = (Node) iter_to_node [iter];
string url = n.URL;
Node match;
string s;
if (n.tree.HelpSource != null)
{
//
// Try the tree-based urls first.
//
s = n.tree.HelpSource.GetText (url, out match);
if (s != null){
((Browser)browser).Render (s, n, url);
browser.CurrentTab.history.AppendHistory (new NodePageVisit (browser, n, url));
return;
}
}
//
// Try the url resolver next
//
s = help_tree.RenderUrl (url, out match);
if (s != null){
((Browser)browser).Render (s, n, url);
browser.CurrentTab.history.AppendHistory (new Browser.LinkPageVisit (browser, url));
return;
}
((Browser)browser).Render ("Unhandled URL
" + "Functionality to view the resource " + n.URL + " is not available on your system or has not yet been implemented.
", null, url);
}
}
}
//
// The index browser
//
class IndexBrowser {
Browser browser;
IndexReader index_reader;
public BigList index_list;
public MatchModel match_model;
public BigList match_list;
IndexEntry current_entry = null;
public static IndexBrowser MakeIndexBrowser (Browser browser)
{
IndexReader ir = browser.help_tree.GetIndex ();
if (ir == null) {
return new IndexBrowser (browser);
}
return new IndexBrowser (browser, ir);
}
ProgressPanel ppanel;
IndexBrowser (Browser parent)
{
browser = parent;
ppanel = new ProgressPanel ("No index found", "Generate", RootTree.MakeIndex, NewIndexCreated);
browser.index_vbox.Add (ppanel);
browser.index_vbox.Show ();
}
void NewIndexCreated ()
{
index_reader = browser.help_tree.GetIndex ();
//restore widgets
browser.index_vbox.Remove (ppanel);
CreateWidget ();
browser.index_vbox.ShowAll ();
}
IndexBrowser (Browser parent, IndexReader ir)
{
browser = parent;
index_reader = ir;
CreateWidget ();
}
void CreateWidget () {
//
// Create the widget
//
Frame frame1 = new Frame ();
VBox vbox1 = new VBox (false, 0);
frame1.Add (vbox1);
// title
HBox hbox1 = new HBox (false, 3);
hbox1.BorderWidth = 3;
Image icon = new Image (Stock.Index, IconSize.Menu);
Label look_for_label = new Label ("Look for:");
look_for_label.Justify = Justification.Left;
look_for_label.Xalign = 0;
hbox1.PackEnd (look_for_label, true, true, 0);
hbox1.PackEnd (icon, false, true, 0);
hbox1.ShowAll ();
vbox1.PackStart (hbox1, false, true, 0);
// entry
vbox1.PackStart (new HSeparator (), false, true, 0);
browser.index_entry = new Entry ();
browser.index_entry.Activated += browser.OnIndexEntryActivated;
browser.index_entry.Changed += browser.OnIndexEntryChanged;
browser.index_entry.FocusInEvent += browser.OnIndexEntryFocused;
browser.index_entry.KeyPressEvent += browser.OnIndexEntryKeyPress;
vbox1.PackStart (browser.index_entry, false, true, 0);
vbox1.PackStart (new HSeparator (), false, true, 0);
//search results
browser.search_box = new VBox ();
vbox1.PackStart (browser.search_box, true, true, 0);
vbox1.ShowAll ();
//
// Setup the widget
//
index_list = new BigList (index_reader);
//index_list.SetSizeRequest (100, 400);
index_list.ItemSelected += new ItemSelected (OnIndexSelected);
index_list.ItemActivated += new ItemActivated (OnIndexActivated);
HBox box = new HBox (false, 0);
box.PackStart (index_list, true, true, 0);
Scrollbar scroll = new VScrollbar (index_list.Adjustment);
box.PackEnd (scroll, false, false, 0);
browser.search_box.PackStart (box, true, true, 0);
box.ShowAll ();
//
// Setup the matches.
//
browser.matches = new Frame ();
match_model = new MatchModel (this);
browser.matches.Hide ();
match_list = new BigList (match_model);
match_list.ItemSelected += new ItemSelected (OnMatchSelected);
match_list.ItemActivated += new ItemActivated (OnMatchActivated);
HBox box2 = new HBox (false, 0);
box2.PackStart (match_list, true, true, 0);
Scrollbar scroll2 = new VScrollbar (match_list.Adjustment);
box2.PackEnd (scroll2, false, false, 0);
box2.ShowAll ();
browser.matches.Add (box2);
index_list.SetSizeRequest (100, 200);
browser.index_vbox.PackStart (frame1);
browser.index_vbox.PackEnd (browser.matches);
}
//
// This class is used as an implementation of the IListModel
// for the matches for a given entry.
//
public class MatchModel : IListModel {
IndexBrowser index_browser;
Browser browser;
public MatchModel (IndexBrowser parent)
{
index_browser = parent;
browser = parent.browser;
}
public int Rows {
get {
if (index_browser.current_entry != null)
return index_browser.current_entry.Count;
else
return 0;
}
}
public string GetValue (int row)
{
Topic t = index_browser.current_entry [row];
// Names from the ECMA provider are somewhat
// ambigious (you have like a million ToString
// methods), so lets give the user the full name
// Filter out non-ecma
if (t.Url [1] != ':')
return t.Caption;
switch (t.Url [0]) {
case 'C': return t.Url.Substring (2) + " constructor";
case 'M': return t.Url.Substring (2) + " method";
case 'P': return t.Url.Substring (2) + " property";
case 'F': return t.Url.Substring (2) + " field";
case 'E': return t.Url.Substring (2) + " event";
default:
return t.Caption;
}
}
public string GetDescription (int row)
{
return GetValue (row);
}
}
void ConfigureIndex (int index)
{
current_entry = index_reader.GetIndexEntry (index);
if (current_entry.Count > 1){
browser.matches.Show ();
match_list.Reload ();
match_list.Refresh ();
} else {
browser.matches.Hide ();
}
}
//
// When an item is selected from the main index list
//
void OnIndexSelected (int index)
{
ConfigureIndex (index);
if (browser.matches.Visible == true)
match_list.Selected = 0;
}
void OnIndexActivated (int index)
{
if (browser.matches.Visible == false)
browser.LoadUrl (current_entry [0].Url);
}
void OnMatchSelected (int index)
{
}
void OnMatchActivated (int index)
{
browser.LoadUrl (current_entry [index].Url);
}
int FindClosest (string text)
{
int low = 0;
int top = index_reader.Rows-1;
int high = top;
bool found = false;
int best_rate_idx = Int32.MaxValue, best_rate = -1;
while (low <= high){
int mid = (high + low) / 2;
//Console.WriteLine ("[{0}, {1}] -> {2}", low, high, mid);
string s;
int p = mid;
for (s = index_reader.GetValue (mid); s [0] == ' ';){
if (p == high){
if (p == low){
if (best_rate_idx != Int32.MaxValue){
//Console.WriteLine ("Bestrated: "+best_rate_idx);
//Console.WriteLine ("Bestrated: "+index_reader.GetValue(best_rate_idx));
return best_rate_idx;
} else {
//Console.WriteLine ("Returning P="+p);
return p;
}
}
high = mid;
break;
}
if (p < 0)
return 0;
s = index_reader.GetValue (++p);
//Console.WriteLine (" Advancing to ->"+p);
}
if (s [0] == ' ')
continue;
int c, rate;
c = Rate (text, s, out rate);
//Console.WriteLine ("[{0}] Text: {1} at {2}", text, s, p);
//Console.WriteLine (" Rate: {0} at {1}", rate, p);
//Console.WriteLine (" Best: {0} at {1}", best_rate, best_rate_idx);
//Console.WriteLine (" {0} - {1}", best_rate, best_rate_idx);
if (rate >= best_rate){
best_rate = rate;
best_rate_idx = p;
}
if (c == 0)
return mid;
if (low == high){
//Console.WriteLine ("THISPATH");
if (best_rate_idx != Int32.MaxValue)
return best_rate_idx;
else
return low;
}
if (c < 0){
high = mid;
} else {
if (low == mid)
low = high;
else
low = mid;
}
}
// Console.WriteLine ("Another");
if (best_rate_idx != Int32.MaxValue)
return best_rate_idx;
else
return high;
}
int Rate (string user_text, string db_text, out int rate)
{
int c = String.Compare (user_text, db_text, true);
if (c == 0){
rate = 0;
return 0;
}
int i;
for (i = 0; i < user_text.Length; i++){
if (db_text [i] != user_text [i]){
rate = i;
return c;
}
}
rate = i;
return c;
}
public void SearchClosest (string text)
{
index_list.Selected = FindClosest (text);
}
public void LoadSelected ()
{
if (browser.matches.Visible == true) {
if (match_list.Selected != -1)
OnMatchActivated (match_list.Selected);
} else {
if (index_list.Selected != -1)
OnIndexActivated (index_list.Selected);
}
}
}
public enum Mode {
Viewer, Editor
}
//
// A Tab is a Notebok with two pages, one for editing and one for visualizing
//
public class Tab : Notebook {
// Our HTML preview during editing.
public IHtmlRender html_preview;
// Where we render the contents
public IHtmlRender html;
public TextView text_editor;
public Mode Tab_mode;
public History history;
private Browser browser;
private Label titleLabel;
private Image EditImg;
public HBox TabLabel;
public string Title {
get { return titleLabel.Text; }
set { titleLabel.Text = value; }
}
public Node CurrentNode;
public System.Xml.XmlNode edit_node;
public string edit_url;
void FocusOut (object sender, FocusOutEventArgs args)
{
if (TabMode == Mode.Editor)
text_editor.GrabFocus ();
}
static IHtmlRender GetRenderer (string file, string type, Browser browser)
{
try {
string exeAssembly = Assembly.GetExecutingAssembly ().Location;
string myPath = System.IO.Path.GetDirectoryName (exeAssembly);
Assembly dll = Assembly.LoadFrom (System.IO.Path.Combine (myPath, file));
Type t = dll.GetType (type, true);
return (IHtmlRender) Activator.CreateInstance (t, new object [1] { browser.help_tree });
} catch {
return null;
}
}
public Tab(Browser br)
{
browser = br;
CurrentNode = br.help_tree;
ShowTabs = false;
ShowBorder = false;
TabBorder = 0;
TabHborder = 0;
history = new History (browser.back_button, browser.forward_button);
//
// First Page
//
ScrolledWindow html_container = new ScrolledWindow();
html_container.Show();
//
// Setup the HTML rendering and preview area
//
if (browser.UseGecko) {
html = GetRenderer ("GeckoHtmlRender.dll", "Monodoc.GeckoHtmlRender", browser);
html_preview = GetRenderer ("GeckoHtmlRender.dll", "Monodoc.GeckoHtmlRender", browser);
}
if (html == null || html_preview == null) {
html = GetRenderer ("GtkHtmlHtmlRender.dll", "Monodoc.GtkHtmlHtmlRender", browser);
html_preview = GetRenderer ("GtkHtmlHtmlRender.dll", "Monodoc.GtkHtmlHtmlRender", browser);
browser.UseGecko = false;
}
if (html == null || html_preview == null)
throw new Exception ("Couldn't find html renderer!");
//Prepare Font for css (TODO: use GConf?)
if (browser.UseGecko && SettingsHandler.Settings.preferred_font_size == 0) {
Pango.FontDescription font_desc = Pango.FontDescription.FromString ("Sans 12");
SettingsHandler.Settings.preferred_font_family = font_desc.Family;
SettingsHandler.Settings.preferred_font_size = 100; //size: 100%
}
html_container.Add (html.HtmlPanel);
html.UrlClicked += new EventHandler (browser.LinkClicked);
html.OnUrl += new EventHandler (browser.OnUrlMouseOver);
browser.context_id = browser.statusbar.GetContextId ("");
AppendPage(html_container, new Label("Html"));
//
// Second Page: editing
//
VBox vbox1 = new VBox(false, 0);
VBox MainPart = new VBox(false, 0);
//
// TextView for XML code
//
ScrolledWindow sw = new ScrolledWindow();
text_editor = new TextView();
text_editor.Buffer.Changed += new EventHandler (EditedTextChanged);
text_editor.WrapMode = WrapMode.Word;
sw.Add(text_editor);
text_editor.FocusOutEvent += new FocusOutEventHandler (FocusOut);
//
// XML editing buttons
//
HBox EdBots = new HBox(false, 2);
Button button1 = new Button("");
EdBots.PackStart(button1);
Button button2 = new Button("");
EdBots.PackStart(button2);
Button button3 = new Button("<_table>");
EdBots.PackStart(button3);
Button button4 = new Button("<_see...>");
EdBots.PackStart(button4);
Button button5 = new Button("<_para>");
EdBots.PackStart(button5);
Button button6 = new Button("Add Note");
EdBots.PackStart(button6);
button1.Clicked += new EventHandler (OnInsertExampleClicked);
button2.Clicked += new EventHandler (OnInsertListClicked);
button3.Clicked += new EventHandler (OnInsertTableClicked);
button4.Clicked += new EventHandler (OnInsertType);
button5.Clicked += new EventHandler (OnInsertParaClicked);
button6.Clicked += new EventHandler (OnInsertNoteClicked);
Frame html_preview_frame = new Frame("Preview");
ScrolledWindow html_preview_container = new ScrolledWindow();
//
// code preview panel
//
html_preview_container.Add (html_preview.HtmlPanel);
html_preview_frame.Add(html_preview_container);
MainPart.PackStart(sw);
MainPart.PackStart(EdBots, false, false, 0);
MainPart.PackStart(html_preview_frame);
//
// Close and Save buttons
//
HBox MainBots = new HBox(false, 3);
HBox Filling = new HBox(false, 0);
Button close = new Button("C_lose");
Button save = new Button("S_ave");
Button restore = new Button("_Restore");
close.Clicked += new EventHandler (OnCancelEdits);
save.Clicked += new EventHandler (OnSaveEdits);
restore.Clicked += new EventHandler (OnRestoreEdits);
MainBots.PackStart(Filling);
MainBots.PackStart(close, false, false, 0);
MainBots.PackStart(save, false, false, 0);
MainBots.PackStart(restore, false, false, 0);
vbox1.PackStart(MainPart);
vbox1.PackStart(MainBots, false, false, 0);
AppendPage(vbox1, new Label("Edit XML"));
//
//Create the Label for the Tab
//
TabLabel = new HBox(false, 2);
titleLabel = new Label("");
//Close Tab button
Button tabClose = new Button();
Image img = new Image(Stock.Close, IconSize.SmallToolbar);
tabClose.Add(img);
tabClose.Relief = Gtk.ReliefStyle.None;
tabClose.SetSizeRequest (18, 18);
tabClose.Clicked += new EventHandler (OnTabClose);
//Icon showed when the Tab is in Edit Mode
EditImg = new Image (Stock.Convert, IconSize.SmallToolbar);
TabLabel.PackStart (EditImg, false, true, 2);
TabLabel.PackStart (titleLabel, true, true, 0);
TabLabel.PackStart (tabClose, false, false, 2);
// needed, otherwise even calling show_all on the notebook won't
// make the hbox contents appear.
TabLabel.ShowAll();
EditImg.Visible = false;
}
public Mode TabMode {
get { return Tab_mode; }
set { Tab_mode = value; }
}
public void SetMode (Mode m)
{
if (Tab_mode == m)
return;
if (m == Mode.Viewer) {
this.Page = 0;
browser.cut1.Sensitive = false;
browser.paste1.Sensitive = false;
browser.print.Sensitive = true;
EditImg.Visible = false;
} else {
this.Page = 1;
browser.cut1.Sensitive = true;
browser.paste1.Sensitive = true;
browser.print.Sensitive = false;
EditImg.Visible = true;
}
Tab_mode = m;
}
// Events for the Editor
void OnInsertParaClicked (object sender, EventArgs a)
{
text_editor.Buffer.InsertAtCursor ("\n\n");
}
void OnInsertNoteClicked (object sender, EventArgs a)
{
text_editor.Buffer.InsertAtCursor ("\n\n \n \n");
}
void OnInsertExampleClicked (object sender, EventArgs a)
{
text_editor.Buffer.InsertAtCursor ("\n\n \n
\n");
}
void OnInsertListClicked (object sender, EventArgs a)
{
text_editor.Buffer.InsertAtCursor ("\n\n - \n First Item\n
\n
");
}
void OnInsertTableClicked (object sender, EventArgs a)
{
text_editor.Buffer.InsertAtCursor ("\n\n \n Column\n" +
" Description\n" +
" \n" +
" - \n" +
" Term\n" +
" Description\n" +
"
\n" +
"
");
}
void OnInsertType (object sender, EventArgs a)
{
text_editor.Buffer.InsertAtCursor ("");
}
void OnSaveEdits (object sender, EventArgs a)
{
try {
edit_node.InnerXml = text_editor.Buffer.Text;
} catch (Exception e) {
browser.statusbar.Pop (browser.context_id);
browser.statusbar.Push (browser.context_id, e.Message);
return;
}
string [] uSplit = EditingUtils.ParseEditUrl (edit_url);
if (uSplit[0].StartsWith ("monodoc:"))
EditingUtils.SaveChange (edit_url, browser.help_tree, edit_node, EcmaHelpSource.GetNiceUrl (browser.CurrentTab.CurrentNode));
else if (uSplit[0].StartsWith ("file:"))
EditingUtils.SaveChange (edit_url, browser.help_tree, edit_node, String.Empty);
else
Console.WriteLine ("Edit url wrong: {0}", edit_url);
SetMode (Mode.Viewer);
history.ActivateCurrent ();
}
void OnCancelEdits (object sender, EventArgs a)
{
SetMode (Mode.Viewer);
history.ActivateCurrent ();
}
void OnRestoreEdits (object sender, EventArgs a)
{
EditingUtils.RemoveChange (edit_url, browser.help_tree);
SetMode (Mode.Viewer);
history.ActivateCurrent ();
}
bool queued = false;
void EditedTextChanged (object sender, EventArgs args)
{
if (queued)
return;
queued = true;
GLib.Timeout.Add (500, delegate {
queued = false;
StringWriter sw = new StringWriter ();
XmlWriter w = new XmlTextWriter (sw);
try {
edit_node.InnerXml = text_editor.Buffer.Text;
EditingUtils.RenderEditPreview (edit_url, browser.help_tree, edit_node, w);
w.Flush ();
} catch (Exception e) {
browser.statusbar.Pop (browser.context_id);
browser.statusbar.Push (browser.context_id, e.Message);
return false;
}
browser.statusbar.Pop (browser.context_id);
browser.statusbar.Push (browser.context_id, "XML OK");
string s = HelpSource.BuildHtml (EcmaHelpSource.css_ecma_code, sw.ToString ());
html_preview.Render(s);
return false;
});
}
void OnTabClose (object sender, EventArgs a)
{
browser.tabs_nb.RemovePage(browser.tabs_nb.PageNum(this));
browser.tabs_nb.ShowTabs = (browser.tabs_nb.NPages > 1);
}
}
}