[Monodevelop-patches-list] r2604 - in trunk/MonoDevelop: . Extras Extras/VersionControl Extras/VersionControl/AddIn Extras/VersionControl/Diff Extras/VersionControl/DiffWidget Extras/VersionControl/VersionControl

Joshua Tauberer joshua at mono-cvs.ximian.com
Wed Jun 22 15:12:34 EDT 2005


Author: joshua
Date: 2005-06-22 15:12:34 -0400 (Wed, 22 Jun 2005)
New Revision: 2604

Added:
   trunk/MonoDevelop/Extras/VersionControl/
   trunk/MonoDevelop/Extras/VersionControl/AddIn/
   trunk/MonoDevelop/Extras/VersionControl/AddIn/Addin.cs
   trunk/MonoDevelop/Extras/VersionControl/AddIn/Diffs.cs
   trunk/MonoDevelop/Extras/VersionControl/AddIn/Logs.cs
   trunk/MonoDevelop/Extras/VersionControl/AddIn/Makefile.am
   trunk/MonoDevelop/Extras/VersionControl/AddIn/README
   trunk/MonoDevelop/Extras/VersionControl/AddIn/Statuses.cs
   trunk/MonoDevelop/Extras/VersionControl/AddIn/Task.cs
   trunk/MonoDevelop/Extras/VersionControl/AddIn/Update.cs
   trunk/MonoDevelop/Extras/VersionControl/AddIn/VersionControl.addin.xml
   trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_added.png
   trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_conflicted.png
   trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_locked.png
   trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_modified.png
   trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_normal.png
   trunk/MonoDevelop/Extras/VersionControl/ChangeLog
   trunk/MonoDevelop/Extras/VersionControl/Diff/
   trunk/MonoDevelop/Extras/VersionControl/Diff/Diff.cs
   trunk/MonoDevelop/Extras/VersionControl/Diff/Makefile.am
   trunk/MonoDevelop/Extras/VersionControl/Diff/Merge.cs
   trunk/MonoDevelop/Extras/VersionControl/Diff/Patch.cs
   trunk/MonoDevelop/Extras/VersionControl/Diff/README
   trunk/MonoDevelop/Extras/VersionControl/Diff/StructuredDiff.cs
   trunk/MonoDevelop/Extras/VersionControl/Diff/TextDiff.cs
   trunk/MonoDevelop/Extras/VersionControl/Diff/UnifiedDiff.cs
   trunk/MonoDevelop/Extras/VersionControl/DiffWidget/
   trunk/MonoDevelop/Extras/VersionControl/DiffWidget/Makefile.am
   trunk/MonoDevelop/Extras/VersionControl/DiffWidget/README
   trunk/MonoDevelop/Extras/VersionControl/DiffWidget/widget.cs
   trunk/MonoDevelop/Extras/VersionControl/Makefile.am
   trunk/MonoDevelop/Extras/VersionControl/README
   trunk/MonoDevelop/Extras/VersionControl/VersionControl/
   trunk/MonoDevelop/Extras/VersionControl/VersionControl/Makefile.am
   trunk/MonoDevelop/Extras/VersionControl/VersionControl/Subversion.cs
   trunk/MonoDevelop/Extras/VersionControl/VersionControl/VersionControl.cs
Modified:
   trunk/MonoDevelop/ChangeLog
   trunk/MonoDevelop/configure.in
Log:
Checked in VersionControl files.

Modified: trunk/MonoDevelop/ChangeLog
===================================================================
--- trunk/MonoDevelop/ChangeLog	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/ChangeLog	2005-06-22 19:12:34 UTC (rev 2604)
@@ -1,3 +1,8 @@
+2005-06-22  Joshua Tauberer  <tauberer at for.net>
+	* configure.in: Added Extras/VersionControl automake files to the
+	  build.
+	* Extras/VersionControl: Checked in VersionControl addin files.
+
 2005-06-08  Todd Berman  <tberman at off.net>
 
 	* monodevelop.in: Remove bashism, patch from meebey (mail at meebey.net)

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Addin.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Addin.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Addin.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,244 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Gtk;
+
+using MonoDevelop.Gui;
+using MonoDevelop.Core;
+using MonoDevelop.Gui.Pads;
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Commands;
+using MonoDevelop.Services;
+
+using VersionControl;
+
+namespace VersionControlPlugin {
+
+	public class VersionControlService  {
+	
+		public static ArrayList Providers = new ArrayList();
+	
+		static Gdk.Pixbuf overlay_normal = Gdk.Pixbuf.LoadFromResource("overlay_normal.png");
+		static Gdk.Pixbuf overlay_modified = Gdk.Pixbuf.LoadFromResource("overlay_modified.png");
+		static Gdk.Pixbuf overlay_conflicted = Gdk.Pixbuf.LoadFromResource("overlay_conflicted.png");
+		static Gdk.Pixbuf overlay_added = Gdk.Pixbuf.LoadFromResource("overlay_added.png");
+
+		public static Gdk.Pixbuf LoadIconForStatus(NodeStatus status) {
+			if (status == NodeStatus.Unchanged)
+				return overlay_normal;
+			if (status == NodeStatus.Modified)
+				return overlay_modified;
+			if (status == NodeStatus.Conflicted)
+				return overlay_conflicted;
+			if (status == NodeStatus.ScheduledAdd)
+				return overlay_added;
+			return null;
+		}
+		
+		static VersionControlService() {
+			Providers.Add(new SubversionVersionControl());
+		}
+	}
+	
+	public class VersionControlNodeExtension : NodeBuilderExtension {
+		public override bool CanBuildNode (Type dataType) {
+			//Console.Error.WriteLine(dataType);
+			return typeof(ProjectFile).IsAssignableFrom (dataType)
+				|| typeof(DotNetProject).IsAssignableFrom (dataType);
+				// TODO: Folders
+		}		
+		
+		public override void BuildNode (ITreeBuilder builder, object dataObject, ref string label, ref Gdk.Pixbuf icon, ref Gdk.Pixbuf closedIcon) {
+			// Add status overlays
+			
+			// TODO: Watch the files in some way to detect
+			// when the overlay should be changed.
+			
+			if (!(dataObject is ProjectFile)) return;
+			if (!builder.Options["ShowVersionControlOverlays"])
+				return;
+		
+			ProjectFile file = (ProjectFile) dataObject;
+			try {
+				foreach (VersionControlSystem vc in VersionControlService.Providers) {
+					if (vc.IsFileStatusAvailable(file.FilePath)) {
+						Node node = vc.GetFileStatus(file.FilePath, false);
+						
+						Gdk.Pixbuf overlay = VersionControlService.LoadIconForStatus(node.Status);
+						
+						double scale = (double)(2*icon.Width/3) / (double)overlay.Width;
+						int w = (int)(overlay.Width*scale);
+						int h = (int)(overlay.Height*scale);
+						icon = icon.Copy();
+						overlay.Composite(icon,
+							icon.Width-w,  icon.Height-h,
+							w, h,
+							icon.Width-w, icon.Height-h,
+							scale,scale, Gdk.InterpType.Bilinear, 255); 
+						break;
+					}
+				}
+			} catch (Exception e) {
+				Console.Error.WriteLine(e);
+			}
+		}
+		
+		public override Type CommandHandlerType {
+			get { return typeof(AddinCommandHandler); }
+		}
+	}
+
+	public enum Commands {
+    	Update,
+    	Diff,
+    	Log,
+    	Status
+	}
+	
+	class AddinCommandHandler : NodeCommandHandler {
+		[CommandHandler (Commands.Update)]
+		protected void OnUpdate() {
+			RunCommand(Commands.Update, false);
+		}
+		
+		[CommandUpdateHandler (Commands.Update)]
+		protected void UpdateUpdate(CommandInfo item) {
+			TestCommand(Commands.Update, item);
+		}
+		
+		[CommandHandler (Commands.Diff)]
+		protected void OnDiff() {
+			RunCommand(Commands.Diff, false);
+		}
+		
+		[CommandUpdateHandler (Commands.Diff)]
+		protected void UpdateDiff(CommandInfo item) {
+			TestCommand(Commands.Diff, item);
+		}
+		
+		[CommandHandler (Commands.Log)]
+		protected void OnLog() {
+			RunCommand(Commands.Log, false);
+		}
+		
+		[CommandUpdateHandler (Commands.Log)]
+		protected void UpdateLog(CommandInfo item) {
+			TestCommand(Commands.Log, item);
+		}
+		
+		[CommandHandler (Commands.Status)]
+		protected void OnStatus() {
+			RunCommand(Commands.Status, false);
+		}
+		
+		[CommandUpdateHandler (Commands.Status)]
+		protected void UpdateStatus(CommandInfo item) {
+			TestCommand(Commands.Status, item);
+		}
+		
+		private void TestCommand(Commands cmd, CommandInfo item) {
+			item.Visible = RunCommand(cmd, true);
+		}
+		
+		private bool RunCommand(Commands cmd, bool test) {
+			string path;
+			bool isDir;
+		
+			if (CurrentNode.DataItem is ProjectFile) {
+				ProjectFile file = (ProjectFile)CurrentNode.DataItem;
+				path = file.FilePath;
+				isDir = false;
+			} else if (CurrentNode.DataItem is DotNetProject) {
+				DotNetProject project = (DotNetProject)CurrentNode.DataItem;
+				path = project.BaseDirectory;
+				isDir = true;
+			} else {
+				Console.Error.WriteLine(CurrentNode.DataItem);
+				return false;
+			}
+			
+			switch (cmd) {
+				case Commands.Update:
+					return UpdateCommand.Update(path, test);
+				case Commands.Diff:
+					return DiffView.Show(path, test);
+				case Commands.Log:
+					return LogView.Show(path, isDir, null, test);
+				case Commands.Status:
+					return StatusView.Show(path, test);
+			}
+			return false;
+		}
+	}
+
+	public abstract class BaseView : AbstractBaseViewContent, IViewContent {
+		string name;
+		public BaseView(string name) { this.name = name; }
+		
+		protected virtual void SaveAs(string fileName) {
+		}
+
+		void IViewContent.Load(string fileName) {
+			throw new InvalidOperationException();
+		}
+		void IViewContent.Save() {
+			throw new InvalidOperationException();
+		}
+		void IViewContent.Save(string fileName) {
+			SaveAs(fileName);
+		}
+		
+		string IViewContent.ContentName {
+			get { return name; }
+			set { }
+		}
+		
+		bool IViewContent.HasProject {
+			get { return false; }
+			set { }
+		}
+		
+		bool IViewContent.IsDirty {
+			get { return false; }
+			set { }
+		}
+		
+		bool IViewContent.IsReadOnly {
+			get { return true; }
+		}
+
+		bool IViewContent.IsUntitled {
+			get { return false; }
+		}
+
+		bool IViewContent.IsViewOnly {
+			get { return false; }
+		}
+		
+		string IViewContent.PathRelativeToProject {
+			get { return ""; }
+		}
+		
+		MonoDevelop.Internal.Project.Project IViewContent.Project {
+			get { return null; }
+			set { }
+		}
+		
+		string IViewContent.TabPageLabel {
+			get { return name; }
+		}
+		
+		string IViewContent.UntitledName {
+			get { return ""; }
+			set { }
+		}
+		
+		event EventHandler IViewContent.BeforeSave { add { } remove { } }
+		event EventHandler IViewContent.ContentChanged { add { } remove { } }
+		event EventHandler IViewContent.ContentNameChanged { add { } remove { } }
+		event EventHandler IViewContent.DirtyChanged { add { } remove { } }
+	}
+	
+
+}

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Diffs.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Diffs.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Diffs.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,141 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Gtk;
+
+using VersionControl;
+
+using MonoDevelop.Core.AddIns;
+using MonoDevelop.Core.AddIns.Codons;
+using MonoDevelop.Core.AddIns.Conditions;
+using MonoDevelop.Gui;
+using MonoDevelop.Gui.Dialogs;
+
+using Algorithm.Diff.Gtk;
+
+namespace VersionControlPlugin {
+	public class DiffView : BaseView {
+		object left, right;
+		Algorithm.Diff.Diff diff;
+		HBox box = new HBox(true, 0);
+		DiffWidget widget;
+		ThreadNotify threadnotify;
+		
+		System.IO.FileSystemWatcher leftwatcher, rightwatcher;
+		
+		double pos = -1;
+		
+		public static bool Show(string filepath, bool test) {
+			foreach (VersionControlSystem vc in VersionControlService.Providers) {
+				if (vc.IsDiffAvailable(filepath)) {
+					if (test) return true;
+					DiffView d = new DiffView(
+						Path.GetFileName(filepath),
+						vc.GetPathToBaseText(filepath),
+						filepath);
+					MonoDevelop.Gui.WorkbenchSingleton.Workbench.ShowView(d, true);
+					return true;
+				}
+			}
+			return false;
+		}
+			
+		static string[] split(string text) {
+			if (text == "") return new string[0];
+			return text.Split('\n', '\r');
+		}
+			
+		public static void Show(string name, string lefttext, string righttext) {
+			DiffView d = new DiffView(name, split(lefttext), split(righttext));
+			MonoDevelop.Gui.WorkbenchSingleton.Workbench.ShowView(d, true);
+		}
+		
+		public DiffView(string name, string left, string right) 
+			: base(name + " Changes") {
+			this.left = left;
+			this.right = right;
+			
+			Refresh();
+			
+			threadnotify = new ThreadNotify(new ReadyEvent(Refresh));
+			
+			leftwatcher = new System.IO.FileSystemWatcher(Path.GetDirectoryName(left), Path.GetFileName(left));
+			rightwatcher = new System.IO.FileSystemWatcher(Path.GetDirectoryName(right), Path.GetFileName(right));
+			
+			leftwatcher.Changed += new FileSystemEventHandler(filechanged);
+			rightwatcher.Changed += new FileSystemEventHandler(filechanged);
+			
+			leftwatcher.EnableRaisingEvents = true;
+			rightwatcher.EnableRaisingEvents = true;
+		}
+		
+		public DiffView(string name, string[] left, string[] right) 
+			: base(name + " Changes") {
+			this.left = left;
+			this.right = right;
+			
+			Refresh();
+		}
+		
+		void filechanged(object src, FileSystemEventArgs args) {
+			threadnotify.WakeupMain();			
+		}
+		
+		private void Refresh() {
+			box.Show();
+			
+			try {
+				if (left is string)
+					diff = new Algorithm.Diff.Diff((string)left, (string)right, false, true);
+				else if (left is string[])
+					diff = new Algorithm.Diff.Diff((string[])left, (string[])right, null, null);
+			} catch (Exception e) {
+				Console.Error.WriteLine(e.ToString());
+				return;
+			} 
+			
+			if (widget != null) {
+				pos = widget.Position;
+				box.Remove(widget);
+				widget.Dispose();
+			}
+						
+			DiffWidget.Options opts = new DiffWidget.Options();
+			opts.Font = MonoDevelop.EditorBindings.Properties.TextEditorProperties.Font.ToString();
+			opts.LeftName = "Repository";
+			opts.RightName = "Working Copy";
+			widget = new DiffWidget(diff, opts);
+			
+			box.Add(widget);
+			box.ShowAll();
+			
+			widget.ExposeEvent += new ExposeEventHandler(OnExposed);
+		}
+
+		void OnExposed (object o, ExposeEventArgs args) {
+			if (pos != -1)
+				widget.Position = pos;
+			pos = -1;
+		}		
+		
+		protected override void SaveAs(string fileName) {
+			if (!(left is string)) return;
+		
+			using (StreamWriter writer = new StreamWriter(fileName)) {
+				Algorithm.Diff.UnifiedDiff.WriteUnifiedDiff(
+					diff,
+					writer,
+					Path.GetFileName((string)right) + "    (repository)",
+					Path.GetFileName((string)right) + "    (working copy)",
+					3);
+			}
+		}
+			
+		public override Gtk.Widget Control { 
+			get {
+				return box;
+			}
+		}
+	}
+}

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Logs.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Logs.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Logs.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,224 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Gtk;
+using VersionControl;
+
+using MonoDevelop.Gui.Widgets;
+using MonoDevelop.SourceEditor.Gui;
+
+namespace VersionControlPlugin {
+	public class LogView : BaseView {
+		string filepath;
+		Widget widget;
+		Hashtable buttons = new Hashtable();
+		VersionControlSystem vc;
+		RevisionPtr since;
+		
+		private class RevItem {
+			public RevisionPtr Rev;
+			public object Path;
+		}
+	
+		public static bool Show(string filepath, bool isDirectory, RevisionPtr since, bool test) {
+			foreach (VersionControlSystem vc in VersionControlService.Providers) {
+				if (vc.IsHistoryAvailable(filepath)) {
+					if (test) return true;
+					new Worker(vc, filepath, isDirectory, since).Start();
+					return true;
+				}
+			}
+			return false;
+		}
+		
+		private class Worker : Task {
+			VersionControlSystem vc;
+			string filepath;
+			bool isDirectory;
+			RevisionPtr since;
+			RevisionDescription[] history;
+						
+			public Worker(VersionControlSystem vc, string filepath, bool isDirectory, RevisionPtr since) {
+				this.vc = vc;
+				this.filepath = filepath;
+				this.isDirectory = isDirectory;
+				this.since = since;
+			}
+			
+			protected override string GetDescription() {
+				return "Retrieving history for " + Path.GetFileName(filepath) + "...";
+			}
+			
+			protected override void Run() {
+				history = vc.GetHistory(filepath, since);
+			}
+		
+			protected override void Finished() {
+				LogView d = new LogView(filepath, isDirectory, history, vc);
+				MonoDevelop.Gui.WorkbenchSingleton.Workbench.ShowView(d, true);
+			}
+		}
+		
+		public LogView(string filepath, bool isDirectory, RevisionDescription[] history, VersionControlSystem vc) 
+			: base(Path.GetFileName(filepath) + " Log") {
+			this.vc = vc;
+			this.filepath = filepath;
+			
+			ScrolledWindow scroller = new ScrolledWindow();
+			Viewport viewport = new Viewport(); 
+			VBox box = new VBox(false, 5);
+			
+			viewport.Add(box);
+			scroller.Add(viewport);
+			widget = scroller;
+			 
+			foreach (RevisionDescription d in history) {
+				RevItem revitem = new RevItem();
+				revitem.Path = d.RepositoryPath;
+				revitem.Rev = d.Revision;
+			
+				VBox item = new VBox(false, 1);
+				
+				HBox header_row = new HBox(false, 2);
+				item.PackStart(header_row, false, false, 0);
+
+				Label header = new Label(d.Revision + " -- " + d.Time + " -- " + d.Author);
+				header.Xalign = 0;
+				header_row.Add(header);
+				
+				if (!isDirectory) {
+					Button viewdiff = new Button("View Changes");
+					viewdiff.Clicked += new EventHandler(DiffButtonClicked);
+					header_row.Add(viewdiff);
+					buttons[viewdiff] = revitem;
+					
+					Button viewtext = new Button("View File");
+					viewtext.Clicked += new EventHandler(ViewTextButtonClicked);
+					header_row.Add(viewtext);
+					buttons[viewtext] = revitem;
+				}
+				
+				TextView message = new TextView();
+				message.Editable = false;
+				message.WrapMode = Gtk.WrapMode.WordChar;
+				message.Buffer.Text = d.Message == "" ? "No message." : d.Message;
+				item.PackStart(message, false, false, 0);
+				
+				box.PackStart(item, false, false, 0);
+			}
+		}
+		
+		void DiffButtonClicked(object src, EventArgs args) {
+			RevItem item = (RevItem)buttons[src];
+			new DiffWorker(Path.GetFileName(filepath), vc, item.Path, item.Rev).Start();
+		}
+		
+		void ViewTextButtonClicked(object src, EventArgs args) {
+			RevItem item = (RevItem)buttons[src];
+			HistoricalFileView.Show(filepath, vc, item.Path, item.Rev);
+		}
+		
+		public override Gtk.Widget Control { 
+			get {
+				return widget;
+			}
+		}
+		
+		internal class DiffWorker : Task {
+			VersionControlSystem vc;
+			string name;
+			object revPath;
+			RevisionPtr revision;
+			string text1, text2;
+						
+			public DiffWorker(string name, VersionControlSystem vc, object revPath, RevisionPtr revision) {
+				this.name = name;
+				this.vc = vc;
+				this.revPath = revPath;
+				this.revision = revision;
+			}
+			
+			protected override string GetDescription() {
+				return "Retreiving changes in " + name + " at " + revision + "...";
+			}
+			
+			protected override void Run() {
+				Log("Getting text of " + revPath + " at " + revision.GetPrevious() + "...");
+				try {
+					text1 = vc.GetTextAtRevision(revPath, revision.GetPrevious());
+				} catch (Exception e) {
+					// If the file was added in this revision, no previous
+					// text exists.
+					text1 = "";
+				}
+				Log("Getting text of " + revPath + " at " + revision + "...");
+				text2 = vc.GetTextAtRevision(revPath, revision);
+			}
+		
+			protected override void Finished() {
+				DiffView.Show(name + " " + revision.ToString(), text1, text2);
+			}
+		}
+		
+	}
+
+	public class HistoricalFileView : BaseView {
+		SourceEditor widget;
+	
+		public static void Show(string name, string file, string text) {
+			HistoricalFileView d = new HistoricalFileView(name, file, text);
+			MonoDevelop.Gui.WorkbenchSingleton.Workbench.ShowView(d, true);
+		}
+			
+		public static void Show(string file, VersionControlSystem vc, object revPath, RevisionPtr revision) {
+			new Worker(Path.GetFileName(file) + " " + revision.ToString(),
+				file, vc, revPath, revision).Start();
+		}
+		
+			
+		public HistoricalFileView(string name, string file, string text) 
+			: base(name) {
+			
+			// How do I get it to recognize the language of the file?
+			widget = new SourceEditor(null);
+			widget.Text = text;
+			widget.View.Editable = false;
+		}
+		
+		public override Gtk.Widget Control { 
+			get {
+				return widget;
+			}
+		}
+	
+		internal class Worker : Task {
+			VersionControlSystem vc;
+			string name, file;
+			object revPath;
+			RevisionPtr revision;
+			string text;
+						
+			public Worker(string name, string file, VersionControlSystem vc, object revPath, RevisionPtr revision) {
+				this.name = name;
+				this.file = file;
+				this.vc = vc;
+				this.revPath = revPath;
+				this.revision = revision;
+			}
+			
+			protected override string GetDescription() {
+				return "Retreiving content of " + name + " at " + revision + "...";
+			}
+			
+			protected override void Run() {
+				text = vc.GetTextAtRevision(revPath, revision);
+			}
+		
+			protected override void Finished() {
+				HistoricalFileView.Show(name, file, text);
+			}
+		}
+	}
+
+}

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Makefile.am
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Makefile.am	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Makefile.am	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,42 @@
+FILES = *.cs
+
+REFS = /r:$(ADDIN_BUILD)/Diff.dll \
+	   /r:$(ADDIN_BUILD)/DiffWidget.dll \
+	   /r:$(ADDIN_BUILD)/VersionControl.dll \
+	   /r:$(top_builddir)/build/bin/MonoDevelop.Core.dll \
+       /r:$(top_builddir)/build/bin/MonoDevelop.Base.dll \
+       /r:$(top_builddir)/build/bin/MonoDevelop.Gui.Widgets.dll \
+       /r:$(top_builddir)/build/bin/MonoDevelop.SourceEditor.dll \
+       /r:System.Data.dll \
+       $(GTK_SHARP_LIBS) \
+       $(GLADE_SHARP_LIBS) \
+       $(GCONF_SHARP_LIBS) \
+       $(GTKSOURCEVIEW_SHARP_LIBS)
+
+RES = -resource:overlay_added.png \
+	  -resource:overlay_modified.png \
+	  -resource:overlay_conflicted.png \
+	  -resource:overlay_normal.png \
+	  -resource:overlay_locked.png
+
+ADDIN = VersionControl.addin.xml
+
+ADDIN_BUILD = $(top_builddir)/build/AddIns/VersionControl
+
+DLL = $(ADDIN_BUILD)/VersionControlAddIn.dll
+
+all: $(ADDIN_BUILD)/$(ADDIN) $(DLL)
+
+$(ADDIN_BUILD)/$(ADDIN): $(srcdir)/$(ADDIN)
+	mkdir -p $(ADDIN_BUILD)
+	cp $(srcdir)/$(ADDIN) $(ADDIN_BUILD)/$(ADDIN)
+
+$(DLL): $(FILES)
+	mkdir -p $(ADDIN_BUILD)
+	$(CSC) $(CSC_FLAGS) -target:library -out:$@ $(FILES) $(REFS) $(RES)
+
+CLEANFILES = $(DLL) $(DLL).mdb $(ADDIN_BUILD)/$(ADDIN)
+
+EXTRA_DIST = $(FILES) $(ADDIN)
+
+

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/README
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/README	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/README	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,6 @@
+This is the actual addin for version control
+support in MD.
+
+The overlay_*.png icons come from the Tortoise
+project (http://tortoisesvn.tigris.org), which
+is a GPL project.

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Statuses.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Statuses.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Statuses.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,361 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Gtk;
+using VersionControl;
+
+using MonoDevelop.Core.AddIns;
+using MonoDevelop.Core.AddIns.Codons;
+using MonoDevelop.Core.AddIns.Conditions;
+using MonoDevelop.Core.Services;
+using MonoDevelop.Gui;
+using MonoDevelop.Gui.Dialogs;
+using MonoDevelop.SourceEditor.Gui;
+using MonoDevelop.Services;
+
+namespace VersionControlPlugin {
+	public class StatusView : BaseView {
+		string filepath;
+		VersionControlSystem vc;
+		
+		Widget widget;
+		HBox commandbar;
+		VBox box, main;
+		Label status;
+		Table table;
+		Button showRemoteStatus;
+		Button buttonCommit;
+		
+		bool commitShown = false;
+		Hashtable checkCommit;
+		VBox boxCommit;
+		TextView textCommitMessage;
+		Button buttonCommitCancel;
+		Button buttonCommitCommit;
+		
+		Node[] statuses;
+		
+		bool remoteStatus = false;
+		
+		Hashtable buttonsShowLog;
+		Hashtable buttonsShowDiff;
+		
+		private class RevItem {
+			public RevisionPtr BaseRev;
+			public string Path;
+		}
+	
+		public static bool Show(string path, bool test) {
+			foreach (VersionControlSystem vc in VersionControlService.Providers) {
+				if (vc.IsDirectoryStatusAvailable(path)) {
+					if (test) return true;
+					StatusView d = new StatusView(path, vc);
+					MonoDevelop.Gui.WorkbenchSingleton.Workbench.ShowView(d, true);
+					return true;
+				}
+			}
+			return false;
+		}
+		
+		public StatusView(string filepath, VersionControlSystem vc) 
+			: base(Path.GetFileName(filepath) + " Status") {
+			this.vc = vc;
+			this.filepath = filepath;
+			
+			main = new VBox(false, 5);
+			widget = main;
+			main.Show();
+			
+			commandbar = new HBox(false, 5);
+			main.PackStart(commandbar, false, false, 5);
+			
+			showRemoteStatus = new Button("Show Remote Status");
+			commandbar.PackEnd(showRemoteStatus, false, false, 0);
+			showRemoteStatus.Clicked += new EventHandler(OnShowRemoteStatusClicked);
+			
+			buttonCommit = new Button("Commit...");
+			commandbar.PackEnd(buttonCommit, false, false, 0);
+			buttonCommit.Clicked += new EventHandler(OnCommitClicked);
+
+			boxCommit = new VBox(false, 2);
+			textCommitMessage = new TextView();
+			HBox boxCommitButtons = new HBox(false, 2);
+			buttonCommitCancel = new Button("Cancel");
+			buttonCommitCommit = new Button("Commit");
+			textCommitMessage.Show();
+			buttonCommitCancel.Show();
+			buttonCommitCommit.Show();
+			boxCommit.PackStart(textCommitMessage, true, true, 0);
+			boxCommit.PackStart(boxCommitButtons, false, false, 0);
+			boxCommitButtons.PackEnd(buttonCommitCancel, false, false, 0);
+			boxCommitButtons.PackEnd(buttonCommitCommit, false, false, 0);
+			buttonCommitCancel.Clicked += new EventHandler(OnCommitCancelClicked);
+			buttonCommitCommit.Clicked += new EventHandler(OnCommitCommitClicked);
+			
+			ScrolledWindow scroller = new ScrolledWindow();
+			Viewport viewport = new Viewport(); 
+			box = new VBox(false, 5);
+			main.Add(scroller);
+			
+			viewport.Add(box);
+			scroller.Add(viewport);
+			
+			box.Show();
+			
+			StartUpdate();
+		}
+		
+		public override Gtk.Widget Control { 
+			get {
+				return widget;
+			}
+		}
+		
+		private void StartUpdate() {
+			if (table != null) {
+				box.Remove(table);
+				table.Destroy();
+				table = null;
+			}
+			
+			if (status == null) {
+				status = new Label();
+				box.Add(status);
+				status.Show();
+			}
+			if (!remoteStatus)
+				status.Text = "Scanning for changes...";
+			else
+				status.Text = "Scanning for local and remote changes...";
+			
+			showRemoteStatus.Sensitive = false;
+			buttonCommit.Sensitive = false;
+			
+			new Worker(vc, filepath, remoteStatus, this).Start();
+		}
+		
+		private class HeaderLabel : Label {
+			public HeaderLabel(string text) : base() {
+				Markup = "<b>" + text + "</b>";
+				Show();
+			}
+		}
+	
+		private void Update() {
+			showRemoteStatus.Sensitive = !remoteStatus;
+			
+			if (statuses.Length == 0) {
+				if (!remoteStatus)
+					this.status.Text = "No files have local modifications.";
+				else
+					this.status.Text = "No files have local or remote modifications.";
+				return;
+			}
+			
+			buttonsShowLog = new Hashtable();
+			buttonsShowDiff = new Hashtable();
+			
+			box.Remove(this.status);
+			this.status = null;
+			
+			if (vc.CanCommit(filepath))
+				buttonCommit.Sensitive = true;
+			checkCommit = new Hashtable();
+						
+			table = new Table((uint)statuses.Length+1, (uint)5 + (uint)(remoteStatus ? 2 : 0), false);
+			box.Add(table);
+
+			uint row = 0;		
+		
+			table.Attach(new HeaderLabel("Status"), 0, 3, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+			table.Attach(new HeaderLabel("Path"), 3, 4, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+			
+			if (remoteStatus)
+				table.Attach(new HeaderLabel("Remote Status"), 4, 6, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+					
+			for (int i = 0; i < statuses.Length; i++) {
+				Node n = statuses[i];
+				
+				RevItem item = new RevItem();
+				item.Path = Path.Combine(filepath, n.LocalRelativePath);
+				item.BaseRev = n.BaseRevision;
+				
+				uint col = 0;
+				row++;
+				
+				CheckButton check = new CheckButton();
+				checkCommit[check] = item;
+				table.Attach(check, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+				check.Visible = false;
+					
+				Gdk.Pixbuf statusicon = VersionControlService.LoadIconForStatus(n.Status);
+				if (n.Status == NodeStatus.Modified) {
+					Button b = new Button();
+					if (statusicon != null) {
+						Image img = new Image(statusicon);
+						img.Show();
+						b.Add(img);
+					} else {
+						b.Label = "Diff";
+					}
+					
+					b.Relief = ReliefStyle.Half;
+					buttonsShowDiff[b] = item;
+					table.Attach(b, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+					b.Clicked += new EventHandler(OnShowDiffClicked);
+					b.Show();
+				} else if (statusicon != null) {
+					Image img = new Image(statusicon);
+					img.Show();
+					table.Attach(img, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Fill, 2, 2);
+				} else {
+					++col;
+				}
+				
+				Label status = new Label(n.Status.ToString());
+				status.Show();				
+				table.Attach(status, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+
+				Label name = new Label();
+				name.Justify = Justification.Left;
+				name.Layout.Alignment = Pango.Alignment.Left;
+				name.Xalign = 0;
+				name.Text = n.LocalRelativePath;
+				name.Show();
+				table.Attach(name, col, ++col, row, row+1, AttachOptions.Expand, AttachOptions.Shrink, 2, 2);
+				
+				if (remoteStatus) {
+					Label rstatus = new Label(n.RemoteStatus.ToString());
+					rstatus.Show();
+				
+					table.Attach(rstatus, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+					
+					if (n.RemoteStatus == NodeStatus.Modified) {
+						Button b = new Button("View");
+						b.Relief = ReliefStyle.Half;
+						buttonsShowLog[b] = item;
+						table.Attach(b, col, ++col, row, row+1, AttachOptions.Shrink, AttachOptions.Shrink, 2, 2);
+						b.Clicked += new EventHandler(OnShowLogClicked);
+						b.Show();
+					}
+				}
+			}
+
+			table.Show();
+		}
+		
+		private void OnShowRemoteStatusClicked(object src, EventArgs args) {
+			if (commitShown)
+				buttonCommitCancel.Click();
+		
+			remoteStatus = true;
+			StartUpdate();
+		}
+		
+		private void OnShowLogClicked(object src, EventArgs args) {
+			RevItem file = (RevItem)buttonsShowLog[src];
+			LogView.Show(file.Path, false, file.BaseRev, false);
+		}
+		
+		private void OnShowDiffClicked(object src, EventArgs args) {
+			RevItem file = (RevItem)buttonsShowDiff[src];
+			DiffView.Show(file.Path, false);
+		}
+		
+		private void OnCommitClicked(object src, EventArgs args) {
+			buttonCommit.Sensitive = false;
+			main.Add(boxCommit);
+			boxCommit.ShowAll();
+			commitShown = true;
+			foreach (CheckButton check in checkCommit.Keys)
+				check.Visible = true;
+		}
+		
+		private void OnCommitCommitClicked(object src, EventArgs args) {
+			ArrayList paths = new ArrayList();
+			foreach (CheckButton check in checkCommit.Keys) {
+				if (check.Active) {
+					RevItem file = (RevItem)checkCommit[check];
+					paths.Add(file.Path);
+				}
+			}
+			
+			if (paths.Count == 0)
+				return; // TODO: Show message.
+			
+			new CommitWorker(
+				vc,
+				(string[])paths.ToArray(typeof(string)),
+				textCommitMessage.Buffer.Text,
+				this).Start();
+				
+			OnCommitCancelClicked(null, null);
+		}
+		
+		private void OnCommitCancelClicked(object src, EventArgs args) {
+			foreach (CheckButton check in checkCommit.Keys)
+				check.Visible = false;
+			commitShown = false;
+			buttonCommit.Sensitive = true;
+			main.Remove(boxCommit);
+		}
+		
+		private class Worker : Task {
+			StatusView view;
+			VersionControlSystem vc;
+			string filepath;
+			bool remoteStatus;
+						
+			public Worker(VersionControlSystem vc, string filepath, bool remoteStatus, StatusView view) {
+				this.vc = vc;
+				this.filepath = filepath;
+				this.view = view;
+				this.remoteStatus = remoteStatus;
+			}
+			
+			protected override string GetDescription() {
+				return "Retrieving status for " + Path.GetFileName(filepath) + "...";
+			}
+			
+			protected override void Run() {
+				view.statuses = vc.GetDirectoryStatus(filepath, remoteStatus, true);
+			}
+		
+			protected override void Finished() {
+				view.Update();
+			}
+		}
+		
+		private class CommitWorker : Task {
+			StatusView view;
+			VersionControlSystem vc;
+			string[] paths;
+			string message;
+						
+			public CommitWorker(VersionControlSystem vc, string[] paths, string message, StatusView view) {
+				this.vc = vc;
+				this.paths = paths;
+				this.message = message;
+				this.view = view;
+			}
+			
+			protected override string GetDescription() {
+				return "Committing changes...";
+			}
+			
+			protected override void Run() {
+				vc.Commit(paths, message, new UpdateCallback(Callback));
+			}
+		
+			protected override void Finished() {
+				view.StartUpdate();
+			}
+			
+			void Callback(string path, string action) {
+				Log(action + "\t" + path);
+			}
+		}
+	}
+
+}

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Task.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Task.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Task.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,66 @@
+using System;
+using System.Collections;
+using System.Threading;
+
+using Gtk;
+
+using MonoDevelop.Core.Services;
+using MonoDevelop.Services;
+
+namespace VersionControlPlugin {
+
+	public abstract class Task {
+		IProgressMonitor tracker;
+		ThreadNotify threadnotify;
+		
+		protected abstract string GetDescription();
+		
+		// This occurs in the background.
+		protected abstract void Run();
+		
+		// This occurs on the main thread when the background
+		// task is complete.
+		protected abstract void Finished();
+
+		protected Task() {
+			threadnotify = new ThreadNotify(new ReadyEvent(Wakeup));
+			
+			/*tracker = ((TaskService)ServiceManager.GetService(typeof(TaskService)))
+				.GetStatusProgressMonitor("Version Control", null, true);*/
+			tracker = ((TaskService)ServiceManager.GetService(typeof(TaskService)))
+				.GetOutputProgressMonitor("Version Control", null, true, true);
+		}
+		
+		public void Start() {
+			tracker.BeginTask(GetDescription(), 0);
+			new Thread(new ThreadStart(BackgroundWorker)).Start();
+		}
+		
+		void BackgroundWorker() {
+			try {
+				Run();
+				tracker.ReportSuccess("Done.");
+			} catch (Exception e) {
+				tracker.ReportError(e.Message, null);
+				Console.Error.WriteLine(e);
+			} finally {			
+				threadnotify.WakeupMain();
+			}
+		}
+	
+		public void Wakeup() {
+			tracker.EndTask();
+			tracker.Dispose();
+			Finished();
+		}
+		
+		protected void Log(string logtext) {
+			tracker.Log.WriteLine(logtext);
+		}
+		
+		protected void Warn(string logtext) {
+			tracker.ReportWarning(logtext);
+		}
+		
+	}
+}

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/Update.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/Update.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/Update.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,58 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Gtk;
+
+using MonoDevelop.Internal.Project;
+using MonoDevelop.Gui.Pads;
+using MonoDevelop.Internal.Parser;
+using MonoDevelop.Services;
+using MonoDevelop.Core.AddIns.Codons;
+using MonoDevelop.Commands;
+using MonoDevelop.Gui;
+using MonoDevelop.Gui.Widgets;
+
+using VersionControl;
+
+namespace VersionControlPlugin {
+	public class UpdateCommand {
+		public static bool Update(string path, bool test) {
+			foreach (VersionControlSystem vc in VersionControlService.Providers) {
+				if (vc.CanUpdate(path)) {
+					if (test) return true;
+					new UpdateWorker(vc, path).Start();
+					return true;
+				}
+			}
+			return false;
+		}
+
+		private class UpdateWorker : Task {
+			VersionControlSystem vc;
+			string path;
+						
+			public UpdateWorker(VersionControlSystem vc, string path) {
+				this.vc = vc;
+				this.path = path;
+			}
+			
+			protected override string GetDescription() {
+				return "Updating " + path + "...";
+			}
+			
+			protected override void Run() {
+				vc.Update(path, true, new UpdateCallback(Callback));
+			}
+		
+			protected override void Finished() {
+			}
+			
+			private void Callback(string path, string action) {
+				Log(action + "\t" + path);
+			}
+		}
+		
+	}
+
+}

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/VersionControl.addin.xml
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/AddIn/VersionControl.addin.xml	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/AddIn/VersionControl.addin.xml	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,50 @@
+<AddIn name      = "MonoDevelop Version Control Addin"
+       author    = "Joshua Tauberer"
+       copyright = "GPL"
+       url       = "http://taubz.for.net/code/diff"
+       description = "A MonoDevelop addin for using version control systems like Subversion"
+       version   = "0.1">
+ 
+   <Runtime>
+           <Import assembly="Diff.dll"/>
+           <Import assembly="DiffWidget.dll"/>
+           <Import assembly="VersionControl.dll"/>
+           <Import assembly="VersionControlAddIn.dll"/>
+   </Runtime>
+ 
+	<Extension path = "/SharpDevelop/Workbench/Pads/MonoDevelop.Gui.Pads.ProjectPad">
+		<PadOption id = "ShowVersionControlOverlays" _label = "Show version control overlay icons" defaultValue = "True" />
+		<NodeBuilder id = "VersionControlNodeExtension" class = "VersionControlPlugin.VersionControlNodeExtension"/>
+	</Extension>
+        
+	<Extension path = "/SharpDevelop/Commands">
+		<Command id = "VersionControlPlugin.Commands.Diff" 
+			_label = "Diff"
+			description = "Show the changes made to the local copy since the last update."/>
+		<Command id = "VersionControlPlugin.Commands.Log" 
+			_label = "Log"
+			description = "Shows the commit history of the file or folder."/>
+		<Command id = "VersionControlPlugin.Commands.Status" 
+			_label = "Status"
+			description = "Shows the status of files in the folder."/>
+		<Command id = "VersionControlPlugin.Commands.Update" 
+			_label = "Update"
+			description = "Updates the local copy with remote changes."/>
+	</Extension>
+	
+	<Extension path = "/SharpDevelop/Views/ProjectBrowser/ContextMenu/ProjectFileNode">
+		<CommandItem id = "VersionControlPlugin.Commands.Diff"/>
+		<CommandItem id = "VersionControlPlugin.Commands.Log"/>
+	</Extension>
+	<Extension path = "/SharpDevelop/Views/ProjectBrowser/ContextMenu/DefaultDirectoryNode">
+		<CommandItem id = "VersionControlPlugin.Commands.Update"/>
+		<CommandItem id = "VersionControlPlugin.Commands.Status"/>
+		<CommandItem id = "VersionControlPlugin.Commands.Log"/>
+	</Extension>
+	<Extension path = "/SharpDevelop/Views/ProjectBrowser/ContextMenu/ProjectBrowserNode">
+		<CommandItem id = "VersionControlPlugin.Commands.Update"/>
+		<CommandItem id = "VersionControlPlugin.Commands.Status"/>
+		<CommandItem id = "VersionControlPlugin.Commands.Log"/>
+	</Extension>
+</AddIn>
+	

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_added.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_added.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_conflicted.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_conflicted.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_locked.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_locked.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_modified.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_modified.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_normal.png
===================================================================
(Binary files differ)


Property changes on: trunk/MonoDevelop/Extras/VersionControl/AddIn/overlay_normal.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/MonoDevelop/Extras/VersionControl/ChangeLog
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/ChangeLog	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/ChangeLog	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,4 @@
+2005-06-22  Joshua Tauberer <tauberer at for.net>
+
+	* Added VersionControl into repo (but sadly the addin
+	  cannot add itself into the repo yet!).

Added: trunk/MonoDevelop/Extras/VersionControl/Diff/Diff.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/Diff.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/Diff.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,727 @@
+/*
+ * Diff Algorithm in C#
+ * Based on Tye McQueen's Algorithm::Diff Perl module version 1.19_01
+ * Converted to C# by Joshua Tauberer <tauberer at for.net>
+ * 
+ * The Perl module's copyright notice:
+ * Parts Copyright (c) 2000-2004 Ned Konz.  All rights reserved.
+ * Parts by Tye McQueen.
+ *
+ * The Perl module's readme has a ridiculously long list of
+ * thanks for all of the previous authors, who are:
+ * Mario Wolczko (author of SmallTalk code the module is based on)
+ * Ned Konz
+ * Mark-Jason Dominus
+ * Mike Schilli
+ * Amir Karger
+ * Christian Murphy
+ *
+ * The Perl module was released under the Perl Artistic License,
+ * and I leave my additions in the public domain, so I leave
+ * it up to you to figure out what you need to do if you want
+ * to distribute this file in some form.
+ *
+ * Embedded at the end is an IntList class which is based on Mono's
+ * ArrayList class.  When everybody has C# generics, it will disappear.
+ */
+
+
+using System;
+using System.Collections;
+using System.Text;
+
+//using IntList = System.Collections.Generic.List<int>;
+//using TrioList = System.Collections.Generic.List<Algorithm.Diff.Trio>;
+//using IntList = System.Collections.ArrayList;
+using TrioList = System.Collections.ArrayList;
+
+namespace Algorithm.Diff {
+	public interface IDiff : IEnumerable {
+		IList Left { get; }
+		IList Right { get; }
+	}
+	
+	public abstract class Hunk {
+		internal Hunk() { }
+		
+		public abstract int ChangedLists { get; }
+		
+		public abstract bool Same { get; }
+		public abstract bool Conflict { get; }
+		
+		public abstract bool IsSame(int index);
+
+		public abstract Range Original();
+		public abstract Range Changes(int index);
+		
+		public int MaxLines() {
+			int m = Original().Count;
+			for (int i = 0; i < ChangedLists; i++)
+				if (Changes(i).Count > m)
+					m = Changes(i).Count;
+			return m;
+		}
+	}
+	
+	public class Diff : IDiff {
+		internal IList left, right;
+		IComparer comparer;
+		IHashCodeProvider hashcoder;
+		
+		public IList Left { get { return left; } }
+		public  IList Right { get { return right; } }
+		
+		private class Trio {
+			public Trio a;
+			public int b, c;
+			public Trio(Trio a, int b, int c) {
+				this.a = a;
+				this.b = b;
+				this.c = c;
+			}
+		}
+		
+		public class Hunk : Algorithm.Diff.Hunk {
+			IList left, right;
+			int s1start, s1end, s2start, s2end;
+			bool same;
+			
+			internal Hunk(IList left, IList right, int s1start, int s1end, int s2start, int s2end, bool same) {
+				this.left = left;
+				this.right = right;
+				this.s1start = s1start;
+				this.s1end = s1end;
+				this.s2start = s2start;
+				this.s2end = s2end;
+				this.same = same;
+			}
+			
+			internal void SetLists(IList left, IList right) {
+				this.left = left;
+				this.right = right;
+			}
+			
+			public override int ChangedLists { get { return 1; } }
+
+			public override bool Same { get { return same; } }
+			
+			public override bool Conflict { get { return false; } }
+			
+			public override bool IsSame(int index) {
+				if (index != 0) throw new ArgumentException();
+				return Same;
+			}
+
+			private Range get(int seq) {
+				int start = (seq==1?s1start:s2start);
+				int end = (seq==1?s1end:s2end);
+				IList list = (seq==1?left:right);
+				if (end < start) return new Range(list, start, 0);
+				return new Range(list, start, end-start+1);
+			}
+			
+			public Range Left { get { return get(1); } }
+			public Range Right { get { return get(2); } }
+
+			public override Range Original() { return Left; }
+			public override Range Changes(int index) {
+				if (index != 0) throw new ArgumentException();
+				return Right;
+			}
+				
+			public override int GetHashCode() {
+				return unchecked(s1start + s1end + s2start + s2end);
+			}
+			
+			public override bool Equals(object o) {
+				Hunk h = o as Hunk;
+				return
+					s1start == h.s1start &&
+					s1start == h.s1end &&
+					s1start == h.s2start &&
+					s1start == h.s2end &&
+					same == h.same;
+			}
+			
+			public override string ToString() {
+				if (left == null || right == null)
+					return base.ToString();
+				return DiffString();
+			}
+			
+			public string DiffString() {
+				if (left == null || right == null)
+					throw new InvalidOperationException("This hunk is based on a patch which does not have the compared data.");
+					
+				StringBuilder ret = new StringBuilder();
+				
+				if (Same) {
+					foreach (object item in Left) {
+						ret.Append(" ");
+						ret.Append(item.ToString());
+						ret.Append("\n");
+					}
+				} else {
+					foreach (object item in Left) {
+						ret.Append("<");
+						ret.Append(item.ToString());
+						ret.Append("\n");
+					}
+					foreach (object item in Right) {
+						ret.Append(">");
+						ret.Append(item.ToString());
+						ret.Append("\n");
+					}
+				}
+				
+				return ret.ToString();
+			}
+			
+			internal Hunk Crop(int shiftstart, int shiftend) {
+				return new Diff.Hunk(left, right, Left.Start+shiftstart, Left.End-shiftend, Right.Start+shiftstart, Right.End-shiftend, same);
+			}
+			
+			internal Hunk Reverse() {
+				return new Diff.Hunk(right, left, Right.Start, Right.End, Left.Start, Left.End, same);
+			}
+		}
+	
+		public Diff(IList left, IList right, IComparer comparer, IHashCodeProvider hashcoder) {
+			this.left = left;
+			this.right = right;
+			this.comparer = comparer;
+			this.hashcoder = hashcoder;
+			init();
+		}
+		
+		public Diff(string leftFile, string rightFile, bool caseSensitive, bool compareWhitespace)
+			: this(UnifiedDiff.LoadFileLines(leftFile), UnifiedDiff.LoadFileLines(rightFile), caseSensitive, compareWhitespace) {
+		}
+		
+		public Diff(string[] left, string[] right, bool caseSensitive, bool compareWhitespace) 
+			: this(
+				StripWhitespace(left, !compareWhitespace),
+				StripWhitespace(right, !compareWhitespace),
+				caseSensitive ? (IComparer)Comparer.Default : (IComparer)CaseInsensitiveComparer.Default,
+				caseSensitive ? null : CaseInsensitiveHashCodeProvider.Default
+				) {
+		}
+		
+		////////////////////////////////////////////////////////////
+		
+		
+		private static string[] StripWhitespace(string[] lines, bool strip) {
+			if (!strip) return lines;
+			string[] ret = new string[lines.Length];
+			for (int i = 0; i < lines.Length; i++) {
+				StringBuilder sb = new StringBuilder();
+				foreach (char c in lines[i])
+					if (!char.IsWhiteSpace(c))
+						sb.Append(c);
+				ret[i] = sb.ToString();
+			}
+			return ret;
+		}
+
+		////////////////////////////////////////////////////////////
+		
+		IEnumerator IEnumerable.GetEnumerator() {
+			if (cdif == null)
+				throw new InvalidOperationException("No comparison has been performed.");
+			return new Enumerator(this);
+		}
+		
+		public override string ToString() {
+			System.IO.StringWriter w = new System.IO.StringWriter();
+			UnifiedDiff.WriteUnifiedDiff(this, w);
+			return w.ToString();
+		}
+		
+		public Patch CreatePatch() {
+			int ctr = 0;
+			foreach (Hunk hunk in this)
+				if (!hunk.Same)
+					ctr += hunk.Right.Count;
+				
+			object[] rightData = new object[ctr];
+				
+			ArrayList hunks = new ArrayList();
+			ctr = 0;
+			foreach (Hunk hunk in this) {
+				if (hunk.Same) {
+					hunks.Add(new Patch.Hunk(rightData, hunk.Left.Start, hunk.Left.Count, 0, 0, true));
+				} else {
+					hunks.Add(new Patch.Hunk(rightData, hunk.Left.Start, hunk.Left.Count, ctr, hunk.Right.Count, false));
+					for (int i = 0; i < hunk.Right.Count; i++)
+						rightData[ctr++] = hunk.Right[i];
+				}
+			}
+			
+			
+			return new Patch((Patch.Hunk[])hunks.ToArray(typeof(Hunk)));
+		}
+
+		/*
+		# McIlroy-Hunt diff algorithm
+		# Adapted from the Smalltalk code of Mario I. Wolczko, <mario at wolczko.com>
+		# by Ned Konz, perl at bike-nomad.com
+		# Updates by Tye McQueen, http://perlmonks.org/?node=tye
+		
+		# Create a hash that maps each element of $aCollection to the set of
+		# positions it occupies in $aCollection, restricted to the elements
+		# within the range of indexes specified by $start and $end.
+		# The fourth parameter is a subroutine reference that will be called to
+		# generate a string to use as a key.
+		# Additional parameters, if any, will be passed to this subroutine.
+		#
+		# my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen );
+		*/
+
+		Hashtable _withPositionsOfInInterval(IList aCollection, int start, int end) {
+			Hashtable d = new Hashtable(hashcoder, comparer);
+			for (int index = start; index <= end; index++) {
+				object element = aCollection[index];
+				if (d.ContainsKey(element)) {
+					IntList list = (IntList)d[element];
+					list.Add(index);
+				} else {
+					IntList list = new IntList();
+					list.Add(index);
+					d[element] = list;
+				}
+			}
+			foreach (IntList list in d.Values)
+				list.Reverse();
+			return d;
+		}
+
+		/*
+		# Find the place at which aValue would normally be inserted into the
+		# array. If that place is already occupied by aValue, do nothing, and
+		# return undef. If the place does not exist (i.e., it is off the end of
+		# the array), add it to the end, otherwise replace the element at that
+		# point with aValue.  It is assumed that the array's values are numeric.
+		# This is where the bulk (75%) of the time is spent in this module, so
+		# try to make it fast!
+		*/
+		// NOTE: Instead of returning undef, it returns -1.
+		int _replaceNextLargerWith(IntList array, int value, int high) {
+			if (high <= 0)
+				high = array.Count-1;
+		
+			// off the end?
+			if (high == -1 || value > (int)array[array.Count-1]) {
+				array.Add(value);
+				return array.Count-1;
+			}
+		
+			// binary search for insertion point...
+			int low = 0;
+			int index, found;
+			while (low <= high) {
+				index = (high + low) / 2;
+		
+				found = (int)array[index];
+		
+				if (value == found) 
+					return -1;
+				else if (value > found)
+					low = index + 1;
+				else
+					high = index - 1;
+			}
+		
+			// # now insertion point is in $low.
+			array[low] = value;    // overwrite next larger
+			return low;
+		}
+
+		/*
+		# This method computes the longest common subsequence in $a and $b.
+		
+		# Result is array or ref, whose contents is such that
+		#   $a->[ $i ] == $b->[ $result[ $i ] ]
+		# foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined.
+		
+		# An additional argument may be passed; this is a hash or key generating
+		# function that should return a string that uniquely identifies the given
+		# element.  It should be the case that if the key is the same, the elements
+		# will compare the same. If this parameter is undef or missing, the key
+		# will be the element as a string.
+		
+		# By default, comparisons will use "eq" and elements will be turned into keys
+		# using the default stringizing operator '""'.
+		
+		# Additional parameters, if any, will be passed to the key generation
+		# routine.
+		*/
+		
+		bool compare(object a, object b) {
+			if (comparer == null) return a.Equals(b);
+			return comparer.Compare(a, b) == 0;
+		}
+		
+		bool IsPrepared(out Hashtable bMatches) {
+			bMatches = null;
+			return false;
+		}
+		
+		IntList _longestCommonSubsequence(IList a, IList b) {
+			int aStart = 0;
+			int aFinish = a.Count-1;
+			IntList matchVector = new IntList();
+			Hashtable bMatches;
+			
+			// initialize matchVector to length of a
+			for (int i = 0; i < a.Count; i++)
+				matchVector.Add(-1);
+			
+			if (!IsPrepared(out bMatches)) {
+				int bStart = 0;
+				int bFinish = b.Count-1;
+		
+				// First we prune off any common elements at the beginning
+				while (aStart <= aFinish && bStart <= bFinish && compare(a[aStart], b[bStart]))
+					matchVector[aStart++] = bStart++;
+		
+				// now the end
+				while (aStart <= aFinish && bStart <= bFinish && compare(a[aFinish], b[bFinish]))
+					matchVector[aFinish--] = bFinish--;
+		
+				// Now compute the equivalence classes of positions of elements
+				bMatches =
+				  _withPositionsOfInInterval(b, bStart, bFinish);
+			}
+			
+			IntList thresh = new IntList();
+			TrioList links = new TrioList();
+		
+			for (int i = aStart; i <= aFinish; i++) {
+				IntList aimatches = (IntList)bMatches[a[i]];
+				if (aimatches != null) {
+					int k = 0;
+					for (int ji = 0; ji < aimatches.Count; ji++) {
+						int j = aimatches[ji];
+						// # optimization: most of the time this will be true
+						if (k>0 && (int)thresh[k] > j && (int)thresh[k-1] < j)
+							thresh[k] = j;
+						else
+							k = _replaceNextLargerWith(thresh, j, k);
+		
+						// oddly, it's faster to always test this (CPU cache?).
+						if (k != -1) {
+							Trio t = new Trio( (Trio)( k>0 ? links[k-1] : null ), i, j );
+							if (k == links.Count)
+								links.Add( t );
+							else
+								links[k] = t;
+						}
+					}
+				}
+			}
+		
+			if (thresh.Count > 0) {
+				for (Trio link = (Trio)links[thresh.Count-1]; link != null; link = link.a)
+					matchVector[link.b] = link.c;
+			}
+			
+			return matchVector;
+		}
+
+		/*void prepare(IList list) {
+			prepared = _withPositionsOfInInterval(list, 0, list.Count-1);
+			preparedlist = list;
+		}*/
+		
+		void LCSidx(IList a, IList b, out IntList am, out IntList bm) {
+			IntList match = _longestCommonSubsequence(a, b);
+			am = new IntList();
+			for (int i = 0; i < match.Count; i++)
+				if ((int)match[i] != -1)
+					am.Add(i);
+			bm = new IntList();
+			for (int vi = 0; vi < am.Count; vi++)
+				bm.Add(match[am[vi]]);
+		}
+		
+		IntList compact_diff(IList a, IList b) {
+			IntList am, bm, cdiff;
+			LCSidx(a, b, out am, out bm);
+			cdiff = new IntList();
+			int ai = 0, bi = 0;
+			cdiff.Add(ai);
+			cdiff.Add(bi);
+			while (true) {
+				while(am.Count > 0 && ai == (int)am[0] && bi == (int)bm[0]) {
+					am.RemoveAt(0);
+					bm.RemoveAt(0);
+					++ai;
+					++bi;
+				}
+				
+				cdiff.Add(ai);
+				cdiff.Add(bi);
+				if (am.Count == 0) break;
+				ai = (int)am[0];
+				bi = (int)bm[0];
+				cdiff.Add(ai);
+				cdiff.Add(bi);
+			}
+
+			if (ai < a.Count || bi < b.Count) {
+				cdiff.Add(a.Count);
+				cdiff.Add(b.Count);
+			}
+
+			return cdiff;
+		}
+		
+		int _End;
+		bool _Same;
+		IntList cdif = null;
+		
+		void init() {
+			cdif = compact_diff(left, right);
+			_Same = true;
+			if (0 == (int)cdif[2] && 0 == (int)cdif[3]) {
+				_Same = false;
+				cdif.RemoveAt(0);
+				cdif.RemoveAt(0);
+			}
+			
+			_End = (1+cdif.Count)/2;
+		}
+		
+		private class Enumerator : IEnumerator {
+			Diff diff;
+			int _Pos, _Off;
+			
+			public Enumerator(Diff diff) {
+				this.diff = diff;
+				Reset();
+			}
+			
+			public object Current { get { _ChkPos(); return gethunk(); } }
+			
+			public bool MoveNext() { return next(); }
+			
+			public void Reset() { reset(0); }
+			
+			void _ChkPos() {
+				if (_Pos == 0) throw new InvalidOperationException("Position is reset.");
+			}
+			
+			void reset(int pos) {
+				if (pos < 0 || diff._End <= pos) pos = -1;
+				_Pos = pos;
+				_Off = 2*pos - 1;
+			}
+			
+			bool next() {
+				reset(_Pos+1);
+				return _Pos != -1;
+			}
+			
+			Hunk gethunk() {
+				_ChkPos();
+				
+				int a1, a2, b1, b2;
+				
+				int off1 = 1 + _Off;
+				int off2 = 2 + _Off;
+				
+				a1 = (int)diff.cdif[off1-2];
+				a2 = (int)diff.cdif[off1] - 1;
+				b1 = (int)diff.cdif[off2-2];
+				b2 = (int)diff.cdif[off2] - 1;
+				
+				bool s = same();
+				return new Hunk(diff.left, diff.right, a1, a2, b1, b2, s);
+			}
+			
+			bool same() {
+				_ChkPos();
+				if (diff._Same != ((1 & _Pos) != 0))
+					return false;
+				return true;
+			}
+		}
+	}
+	
+	internal class IntList {
+		private const int DefaultInitialCapacity = 0x10;
+		private int _size;
+		private int[] _items;
+
+		public IntList() {
+			_items = new int[DefaultInitialCapacity];
+		}		
+
+		public int this[int index] { 
+			get { return _items[index]; }
+			set { _items[index] = value; } 
+		}
+
+		public int Count { get { return _size; } }
+
+		private void EnsureCapacity(int count) { 
+			if (count <= _items.Length) return; 
+			int newLength;
+			int[] newData;
+			newLength = _items.Length << 1;
+			if (newLength == 0)
+				newLength = DefaultInitialCapacity;
+			while (newLength < count) 
+				newLength <<= 1;
+			newData = new int[newLength];
+			Array.Copy(_items, 0, newData, 0, _items.Length);
+			_items = newData;
+		}
+		
+		private void Shift(int index, int count) { 
+			if (count > 0) { 
+				if (_size + count > _items.Length) { 
+					int newLength;
+					int[] newData;
+					newLength = (_items.Length > 0) ? _items.Length << 1 : 1;
+					while (newLength < _size + count) 
+						newLength <<= 1;
+					newData = new int[newLength];
+					Array.Copy(_items, 0, newData, 0, index);
+					Array.Copy(_items, index, newData, index + count, _size - index);
+					_items = newData;
+				} else {
+					Array.Copy(_items, index, _items, index + count, _size - index);
+				}
+			} else if (count < 0) {
+				int x = index - count ;
+				Array.Copy(_items, x, _items, index, _size - x);
+			}
+		}
+
+		public int Add(int value) { 
+			if (_items.Length <= _size /* same as _items.Length < _size + 1) */) 
+				EnsureCapacity(_size + 1);
+			_items[_size] = value;
+			return _size++;
+		}
+
+		public virtual void Clear() { 
+			Array.Clear(_items, 0, _size);
+			_size = 0;
+		}
+
+		public virtual void RemoveAt(int index) { 
+			if (index < 0 || index >= _size) 
+				throw new ArgumentOutOfRangeException("index", index,
+					"Less than 0 or more than list count.");
+			Shift(index, -1);
+			_size--;
+		}
+
+		public void Reverse() {
+			for (int i = 0; i <= Count / 2; i++) {
+				int t = this[i];
+				this[i] = this[Count-i-1];
+				this[Count-i-1] = t;
+			}				
+		}
+	}
+	
+	public class Range : IList {
+		IList list;
+		int start, count;
+		
+		static ArrayList EmptyList = new ArrayList();
+		
+		public Range(IList list, int start, int count) {
+			this.list = list;
+			this.start = start;
+			this.count = count;
+		}
+		
+		public int Start { get { return start; } }
+		
+		public int Count { get { return count; } }
+		
+		public int End { get { return start+count-1; } }
+		
+		private void Check() {
+			if (count > 0 && list == null)
+				throw new InvalidOperationException("This range does not refer to a list with data.");
+		}
+		
+		public object this[int index] {
+			get {
+				Check();
+				if (index < 0 || index >= count)
+					throw new ArgumentException("index");
+				return list[index+start];
+			}
+		}
+		
+		// IEnumerable Functions
+
+		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+			if (count == 0 && list == null) return EmptyList.GetEnumerator();
+			Check();
+			return new Enumer(this);
+		}
+		
+		private class Enumer : IEnumerator {
+			Range list;
+			int index = -1;
+			public Enumer(Range list) { this.list = list; }
+			public void Reset() { index = -1; }
+			public bool MoveNext() {
+				index++;
+				return index < list.Count;
+			}
+			public object Current { get { return list[index]; } }
+		}
+		
+		// ICollection Functions
+		
+		void ICollection.CopyTo(Array array, int index) {
+			Check();
+			for (int i = 0; i < Count; i++)
+				array.SetValue(this[i], i + index);
+		}
+		object ICollection.SyncRoot {
+				get { return null; }
+			}
+		bool ICollection.IsSynchronized {
+			get { return false; }
+		}
+		
+		// IList Functions
+		
+		bool IList.IsFixedSize { get { return true; } }
+
+		bool IList.IsReadOnly { get { return true; } }
+		
+		object IList.this[int index] { set { throw new InvalidOperationException(); } }
+
+		int IList.Add(object obj) { throw new InvalidOperationException(); }
+		
+		void IList.Clear() { throw new InvalidOperationException(); }
+		
+		void IList.Insert(int index, object obj) { throw new InvalidOperationException(); }
+		
+		void IList.Remove(object obj) { throw new InvalidOperationException(); }
+		
+		void IList.RemoveAt(int index) { throw new InvalidOperationException(); }
+		
+		public bool Contains(object obj) {
+			return IndexOf(obj) != -1;
+		}
+		
+		public int IndexOf(object obj) {
+			for (int i = 0; i < Count; i++)
+				if (obj.Equals(this[i]))
+					return i;
+			return -1;
+		}
+	}
+
+}

Added: trunk/MonoDevelop/Extras/VersionControl/Diff/Makefile.am
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/Makefile.am	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/Makefile.am	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,18 @@
+
+ADDIN_BUILD = $(top_builddir)/build/AddIns/VersionControl
+ASSEMBLY = $(ADDIN_BUILD)/Diff.dll
+
+FILES = *.cs
+
+$(ADDIN_BUILD)/$(ADDIN): $(srcdir)/$(ADDIN)
+
+$(ASSEMBLY): $(FILES)
+	mkdir -p $(ADDIN_BUILD)
+	$(CSC) $(CSC_FLAGS) $(FILES) -out:$@ -target:library
+
+CLEANFILES = $(ASSEMBLY) $(ASSEMBLY).mdb
+EXTRA_DIST = $(FILES)
+
+all: $(ASSEMBLY)
+
+include $(top_srcdir)/Makefile.include

Added: trunk/MonoDevelop/Extras/VersionControl/Diff/Merge.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/Merge.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/Merge.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,299 @@
+/*
+ * Merges, a supporting class for Diffs
+ */
+
+using System;
+using System.Collections;
+using System.Text;
+
+namespace Algorithm.Diff {
+	public class Merge : IEnumerable {
+		IDiff[] diffs;
+		ArrayList hunks = new ArrayList();
+		
+		public Merge(IList original, IList[] changed, IComparer comparer, IHashCodeProvider hashcoder)
+		: this(makediffs(original, changed, comparer, hashcoder))
+		{
+		}
+		
+		public Merge(string original, string[] changed, IComparer comparer)
+		: this(makediffs(original, changed, comparer))
+		{
+		}
+
+		private static IDiff[] makediffs(IList original, IList[] changed, IComparer comparer, IHashCodeProvider hashcoder) {
+			IDiff[] diffs = new IDiff[changed.Length];
+			for (int i = 0; i < changed.Length; i++)
+				diffs[i] = new Diff(original, changed[i], comparer, hashcoder);
+			return diffs;
+		}
+		
+		private static IDiff[] makediffs(string original, string[] changed, IComparer comparer) {
+			IDiff[] diffs = new IDiff[changed.Length];
+			for (int i = 0; i < changed.Length; i++)
+				diffs[i] = new TextDiff(original, changed[i], comparer);
+			return diffs;
+		}
+
+		public Merge(IDiff[] diffs) {
+			this.diffs = diffs;
+			
+			// initialize data structures
+			
+			IEnumerator[] enumerators = new IEnumerator[diffs.Length];
+			ArrayList[] hunks = new ArrayList[diffs.Length];
+
+			for (int i = 0; i < hunks.Length; i++) {
+				enumerators[i] = ((IEnumerable)diffs[i]).GetEnumerator();
+				hunks[i] = new ArrayList();
+			}
+			
+			int startline = 0;
+			
+			while (true) {
+				int endline = -1;
+				bool hasmore = false;
+
+				// Get the next hunk for each diff, and find the longest
+				// hunk for which there are changes.
+				
+				for (int i = 0; i < hunks.Length; i++) {
+					if (hunks[i].Count > 0) continue;
+					if (!enumerators[i].MoveNext()) return;
+					hasmore = true;
+					Diff.Hunk hunk = (Diff.Hunk)enumerators[i].Current;
+					hunks[i].Add(hunk);
+					if (!hunk.Same && hunk.Left.End > endline)
+						endline = hunk.Left.End;
+				}
+				
+				if (!hasmore) return;
+				
+				if (endline == -1) {
+					// All of the hunks represented no change. Find the shortest hunk,
+					// create a hunk from the current start line to the end of the
+					// shortest hunk, and retain all of the hunks that overlap into that
+					// hunk's next region.  (Clear the rest.)
+					int start = int.MaxValue;
+					for (int i = 0; i < hunks.Length; i++) {
+						Diff.Hunk h = (Diff.Hunk)hunks[i][0];
+						if (h.Left.End < start) start = h.Left.End;
+					}
+					
+					// Crop all of the hunks to the shortest region.
+					Diff.Hunk[][] h2 = new Diff.Hunk[hunks.Length][];
+					for (int i = 0; i < hunks.Length; i++) {
+						h2[i] = new Diff.Hunk[1];
+						h2[i][0] = (Diff.Hunk)hunks[i][0];
+						h2[i][0] = h2[i][0].Crop(startline - h2[i][0].Left.Start, h2[i][0].Left.End - start);
+					}
+					this.hunks.Add( new Hunk(this, h2, startline, start - startline + 1, true) );
+					
+					for (int i = 0; i < hunks.Length; i++) {
+						Diff.Hunk h = (Diff.Hunk)hunks[i][0];
+						if (h.Left.End == start) hunks[i].Clear();
+					}
+					startline = start+1;
+					continue;
+				}
+				
+				// For each diff, add in all of the non-same hunks that fall
+				// at least partially within the largest hunk region.  If
+				// a hunk crosses the edge, push the edge further and then
+				// add more hunks again.
+				bool moreToAdd = true;
+				while (moreToAdd) {
+					moreToAdd = false;
+					
+					for (int i = 0; i < hunks.Length; i++) {
+						Diff.Hunk last = (Diff.Hunk)hunks[i][hunks[i].Count-1];
+						while (last.Left.End < endline) {
+							if (!enumerators[i].MoveNext()) continue;
+							last = (Diff.Hunk)enumerators[i].Current;
+							hunks[i].Add(last);
+							if (last.Same) continue;
+							if (last.Left.End > endline) {
+								endline = last.Left.End;
+								moreToAdd = true;
+							}
+						}
+					}
+				}
+				
+				Diff.Hunk[][] hunks2 = new Diff.Hunk[hunks.Length][];
+				for (int i = 0; i < hunks.Length; i++) {
+					// any same hunks that overlap the start or end need to be replaced
+					ArrayList hunks3 = new ArrayList();
+					foreach (Diff.Hunk h in hunks[i]) {
+						Diff.Hunk h2 = h;
+						int shiftstart = 0, shiftend = 0;
+						if (h2.Same && h2.Left.Start < startline)
+							shiftstart = startline - h2.Left.Start;
+						if (h2.Same && h2.Left.End > endline)
+							shiftend = h2.Left.End - endline;
+						if (shiftstart != 0 || shiftend != 0)
+							h2 = h2.Crop(shiftstart, shiftend);
+						hunks3.Add(h2);
+					}
+					hunks2[i] = (Diff.Hunk[])hunks3.ToArray(typeof(Diff.Hunk));
+				}
+				this.hunks.Add( new Hunk(this, hunks2, startline, endline - startline + 1, false) );
+				
+				// In each hunk list, retain only the last hunk if it
+				// overlaps into the next region.
+				startline = endline+1;
+				for (int i = 0; i < hunks.Length; i++) {
+					if (hunks[i].Count == 0) continue;
+					Diff.Hunk h = (Diff.Hunk)hunks[i][hunks[i].Count-1];
+					hunks[i].Clear();
+					if (h.Left.End >= startline)
+						hunks[i].Add(h);
+				}
+				
+			}
+		}
+		
+		IEnumerator IEnumerable.GetEnumerator() {
+			return hunks.GetEnumerator();
+		}
+		
+		public static IList MergeLists(IList original, IList[] changed, IComparer comparer, IHashCodeProvider hashcoder) {
+			Merge m = new Merge(original, changed, comparer, hashcoder);
+			ArrayList ret = new ArrayList();
+			ArrayList newlines = new ArrayList();
+			foreach (Hunk h in m) {
+				newlines.Clear();
+				
+				for (int i = 0; i < changed.Length; i++)
+					if (!h.IsSame(i))
+						newlines.Add(h.Changes(i));
+				
+				// If there were no differences in this region, take the original.
+				if (newlines.Count == 0)
+					ret.AddRange(h.Original());
+				
+				// If one list has changes, take them
+				else if (newlines.Count == 1)
+					ret.AddRange((Range)newlines[0]);
+				
+				// Indicate conflict
+				else
+					ret.Add(new Conflict((Range[])newlines.ToArray(typeof(Range))));
+			}
+			return ret;
+		}
+		
+		public class Conflict : IEnumerable {
+			Range[] ranges;
+			
+			internal Conflict(Range[] ranges) {
+				this.ranges = ranges;
+			}
+			
+			public Range[] Ranges { get { return ranges; } }
+			
+			IEnumerator IEnumerable.GetEnumerator() { return ranges.GetEnumerator(); }
+			
+			public override string ToString() {
+				StringBuilder b = new StringBuilder();
+				b.Append("<<<<<<<<<<\n");
+				for (int i = 0; i < ranges.Length; i++) {
+					if (i > 0) b.Append("----------\n");
+					foreach (object item in ranges[i]) { 
+						b.Append(item);
+						b.Append("\n");
+					}
+				}
+				b.Append(">>>>>>>>>>\n");
+				return b.ToString();
+			}
+		}
+		
+		public class Hunk : Algorithm.Diff.Hunk {
+			Merge merge;
+			Diff.Hunk[][] hunks;
+			int start, count;
+			bool same, conflict;
+			
+			internal Hunk(Merge merge, Diff.Hunk[][] hunks, int start, int count, bool same) {
+				this.merge = merge;
+				this.hunks = hunks;
+				this.start = start;
+				this.count = count;
+				this.same = same;
+				
+				int ct = 0;
+				foreach (Diff.Hunk[] hh in hunks) {
+					foreach (Diff.Hunk h in hh) {
+						if (!h.Same) {
+							ct++;
+							break;
+						}
+					}
+				}
+				conflict = (ct > 1);
+			}
+			
+			public override int ChangedLists { get { return merge.diffs.Length; } }
+			
+			// Returns the set of changes within this hunk's range for the
+			// diff of the given index.
+			public Diff.Hunk[] ChangesHunks(int index) {
+				return hunks[index];
+			}
+			
+			public int ChangedIndex() {
+				if (Conflict) throw new InvalidOperationException("ChangedIndex cannot be called if there is a conflict.");
+				for (int i = 0; i < hunks.Length; i++) {
+					foreach (Diff.Hunk h in hunks[i])
+						if (!h.Same) return i;
+				}
+				return -1;
+			}
+			
+			// Returns the range of elements corresponding to this hunk's range, in the original.
+			public override Range Original() {
+				return new Range(merge.diffs[0].Left, start, count);
+			}
+			
+			// Returns the range of elements corresponding to this hunk's range, in the diff of the given index.
+			public override Range Changes(int index) {
+				return new Range(merge.diffs[index].Right, hunks[index][0].Right.Start, hunks[index][hunks[index].Length-1].Right.End - hunks[index][0].Right.Start + 1);
+			}
+			
+			public override bool Same { get { return same; } }
+			
+			public override bool Conflict { get { return conflict; } }
+			
+			public override bool IsSame(int index) {
+				foreach (Diff.Hunk h in hunks[index])
+					if (!h.Same) return false;
+				return true;
+			}
+			
+			public override string ToString() {
+				StringBuilder b = new StringBuilder();
+				if (Same) {
+					foreach (object item in Original()) {
+						b.Append(" ");
+						b.Append(item);
+						b.Append("\n");
+					}
+					return b.ToString();
+				}
+				
+				b.Append("==========\n");
+				for (int i = 0; i < hunks.Length; i++) {
+					if (i > 0)
+						b.Append("----------\n");
+					foreach (Diff.Hunk h in hunks[i])
+						b.Append(h);
+				}
+				b.Append("==========\n");				
+				return b.ToString();
+			}
+
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/VersionControl/Diff/Patch.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/Patch.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/Patch.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,59 @@
+/*
+ * Patches, a supporting class for Diffs
+ */
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+using System.Xml;
+
+namespace Algorithm.Diff {
+	
+	public class Patch : IEnumerable {
+		Hunk[] hunks;
+		
+		internal Patch(Hunk[] hunks) {
+			this.hunks = hunks;
+		}
+		
+		public class Hunk {
+			object[] rightData;
+			int leftstart, leftcount, rightstart, rightcount;
+			bool same;
+			
+			internal Hunk(object[] rightData, int st, int c, int rs, int rc, bool s) {
+				this.rightData = rightData;
+				leftstart = st;
+				leftcount = c;
+				rightstart = rs;
+				rightcount = rc;
+				same = s;
+			}
+			
+			public bool Same { get { return same; } }
+			
+			public int Start { get { return leftstart; } }
+			public int Count { get { return leftcount; } }
+			public int End { get { return leftstart+leftcount-1; } }
+			
+			public IList Right { get { if (same) return null; return new Range(rightData, rightstart, rightcount); } }
+		}
+		
+		IEnumerator IEnumerable.GetEnumerator() {
+			return hunks.GetEnumerator();
+		}
+		
+		public IList Apply(IList original) {
+			ArrayList right = new ArrayList();
+			foreach (Hunk hunk in this) {
+				if (hunk.Same)
+					right.AddRange(new Range(original, hunk.Start, hunk.Count));
+				else
+					right.AddRange(hunk.Right);
+			}
+			return right;
+		}
+		
+	}
+}

Added: trunk/MonoDevelop/Extras/VersionControl/Diff/README
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/README	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/README	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,5 @@
+This code comes from the Diff library at
+  http://taubz.for.net/code/diff
+which is the Perl Algorithm::Diff module
+converted to C#, and so may be covered by
+the Perl Artistic License.

Added: trunk/MonoDevelop/Extras/VersionControl/Diff/StructuredDiff.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/StructuredDiff.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/StructuredDiff.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,305 @@
+using System;
+using System.Collections;
+using System.Xml;
+
+namespace Algorithm.Diff {
+	public abstract class StructuredDiff {
+		internal Hashtable nodeInterfaces = new Hashtable();
+		internal Hashtable comparisonCache = new Hashtable();
+		
+		public virtual void AddInterface(Type type, NodeInterface nodeInterface) {
+			nodeInterfaces[type] = nodeInterface;
+		}
+		
+		internal NodeInterface GetInterface(object obj) {
+			bool store = false;
+			Type type = obj.GetType();
+			while (type != null) {
+				NodeInterface ret = (NodeInterface)nodeInterfaces[type];
+				if (ret != null) {
+					if (store) nodeInterfaces[obj.GetType()] = ret;
+					return ret;
+				}
+				type = type.BaseType;
+				store = true;
+			}
+			throw new ArgumentException("Node type has no interface defined: " + obj.GetType());
+		}
+		
+		public float CompareLists(IList left, IList right) {
+			return CompareLists(left, right, 0, false);
+		}
+		
+		public void Compare(object left, object right) {
+			comparisonCache.Clear();
+			CompareLists(new object[] { left }, new object[] { right }, 0, true);
+		}
+		
+		private float CompareLists(IList left, IList right, float threshold, bool output) {
+			// Given two lists, find the elements in the list that correspond.
+			// Two elements correspond if their 'difference metric' is less than
+			// or equal to threshold.  For the hunks of correspondent items,
+			// recursively descend into items not truly equal.  For hunks of
+			// irreconsiliable material, raise the threshold to the next useful
+			// level and rescan the items.
+			
+			if (left.Count == 0 && right.Count == 0)
+				return 0;
+			
+			NodeComparerWrapper comparer = new NodeComparerWrapper(threshold, this);
+			
+			Diff diff = new Diff(left, right, comparer, new HashCodeProvider(this));
+			
+			int nitems = 0, ndiffs = 0;
+			
+			foreach (Diff.Hunk hunk in diff) {
+				if (hunk.Same || (hunk.Left.Count == 1 && hunk.Right.Count == 1)) {
+					// This comprises a block of correspondent items who
+					// differ by no more than the threshold value.
+					
+					nitems += hunk.Left.Count;
+
+					bool inSameRegion = false;
+					
+					for (int i = 0; i < hunk.Left.Count; i++) {
+						object oleft = hunk.Left[i];
+						object oright = hunk.Right[i];
+						
+						NodeInterface ileft = GetInterface(oleft);
+						NodeInterface iright = GetInterface(oright);
+						
+						IList cleft = null, cright = null;
+						cleft = ileft.GetChildren(oleft);
+						cright = iright.GetChildren(oright);
+						
+						float comp = 0;
+						if (ileft == iright)
+							comp = ileft.Compare(oleft, oright, this);
+						
+						// If the nodes are equal, emit one node.
+						if (ileft == iright && comp == 0) {
+							if (output) {
+								if (!inSameRegion) { WritePushSame(); inSameRegion = true; }
+								WriteNodeSame(ileft, oleft, oright);
+							}
+							
+						// Recurse into the lists of each node.
+						} else if (ileft == iright && cleft != null && cright != null && cleft.Count > 0 && cright.Count > 0 && comp <= 1.0) {
+							if (output && inSameRegion) { WritePopSame(); inSameRegion = false; }
+							if (output) WritePushNode(ileft, oleft, oright);
+							float d = CompareLists(cleft, cright, 0, output);
+							d *= hunk.Left.Count;
+							if (d < 1) d = 1;
+							ndiffs += (int)d;
+							if (output) WritePopNode();
+							
+						// The nodes are not equal, so emit removed and added nodes.
+						} else {
+							if (output && inSameRegion) { WritePopSame(); inSameRegion = false; }
+							if (output) WriteNodeChange(ileft, oleft, iright, oright);
+							ndiffs += hunk.Left.Count;
+						}
+					}
+					
+					if (output && inSameRegion) WritePopSame();
+				} else {
+					int ct = hunk.Left.Count + hunk.Right.Count;
+					nitems += ct;
+					ndiffs += ct;
+					
+					if (output) {
+						bool noRecurse = comparer.minimumDifference >= 1;
+						if (hunk.Right.Count == 0 || (hunk.Left.Count > 0 && noRecurse))
+							WriteNodesRemoved(hunk.Left);
+						if (hunk.Left.Count == 0 || (hunk.Right.Count > 0 && noRecurse))
+							WriteNodesAdded(hunk.Right);
+						if (hunk.Right.Count != 0 && hunk.Left.Count != 0 && !noRecurse)
+							CompareLists(hunk.Left, hunk.Right, comparer.minimumDifference, output);
+					}
+				}
+			}
+			
+			return (float)ndiffs / (float)nitems;
+		}
+		
+		protected abstract void WritePushNode(NodeInterface nodeInterface, object left, object right);
+
+		protected abstract void WritePushSame();
+		
+		protected abstract void WriteNodeSame(NodeInterface nodeInterface, object left, object right);
+
+		protected abstract void WritePopSame();
+
+		protected abstract void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right);
+
+		protected abstract void WriteNodesRemoved(IList objects);
+
+		protected abstract void WriteNodesAdded(IList objects);
+
+		protected abstract void WritePopNode();
+	}
+		
+	public class XmlOutputStructuredDiff : StructuredDiff {
+		bool deepOutput, allContext;
+		XmlWriter output;
+		Hashtable contextNodes;
+		
+		public XmlOutputStructuredDiff(XmlWriter output, string context) {
+			this.output = output;
+			this.deepOutput = (context == null);
+			this.allContext = (context != null && context == "*");
+			
+			if (!deepOutput && !allContext) {
+				contextNodes = new Hashtable();
+				foreach (string name in context.Split(','))
+					contextNodes[name.Trim()] = contextNodes;
+			}
+		}
+		
+		public override void AddInterface(Type type, NodeInterface nodeInterface) {
+			if (!(nodeInterface is XmlOutputNodeInterface))
+				throw new ArgumentException("Node interfaces for the XmlOutputStructuredDiff must implement XmlOutputNodeInterface.");
+			base.AddInterface(type, nodeInterface);
+		}
+		
+		protected override void WritePushSame() {
+		}
+		
+		protected override void WritePopSame() {
+		}
+
+		protected override void WriteNodeSame(NodeInterface nodeInterface, object left, object right) {
+			bool deep = deepOutput;
+			
+			if (left is XmlNode && !deepOutput && !allContext) {
+				if (!contextNodes.ContainsKey(((XmlNode)left).Name)) {
+					return;
+				} else {
+					deep = true;
+				}
+			}
+
+			((XmlOutputNodeInterface)nodeInterface).WriteBeginNode(left, left, (XmlWriter)output);
+			output.WriteAttributeString("Status", "Same");
+			if (deep)
+				((XmlOutputNodeInterface)nodeInterface).WriteNodeChildren(left, (XmlWriter)output);
+			output.WriteEndElement();
+		}
+
+		protected override void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right) {
+			((XmlOutputNodeInterface)leftInterface).WriteBeginNode(left, right, (XmlWriter)output);
+			output.WriteAttributeString("Status", "Changed");
+				((XmlOutputNodeInterface)leftInterface).WriteBeginNode(left, left, (XmlWriter)output);
+					((XmlOutputNodeInterface)leftInterface).WriteNodeChildren(left, (XmlWriter)output);
+				output.WriteEndElement();
+				((XmlOutputNodeInterface)rightInterface).WriteBeginNode(right, right, (XmlWriter)output);
+					((XmlOutputNodeInterface)rightInterface).WriteNodeChildren(right, (XmlWriter)output);
+				output.WriteEndElement();
+			output.WriteEndElement();
+		}
+
+		protected override void WritePushNode(NodeInterface nodeInterface, object left, object right) {
+			((XmlOutputNodeInterface)nodeInterface).WriteBeginNode(left, right, output);
+		}
+
+		protected override void WritePopNode() {
+			output.WriteEndElement();
+		}
+		
+		void AddRemove(IList objects, string status) {
+			foreach (object obj in objects) {
+				XmlOutputNodeInterface i = (XmlOutputNodeInterface)GetInterface(obj);
+				((XmlOutputNodeInterface)i).WriteBeginNode(obj, obj, output);
+				output.WriteAttributeString("Status", status);
+				i.WriteBeginNode(obj, obj, output);
+				i.WriteNodeChildren(obj, output);
+				output.WriteEndElement();
+				output.WriteEndElement();
+			}
+		}
+
+		protected override void WriteNodesRemoved(IList objects) {
+			AddRemove(objects, "Removed");
+		}
+		protected override void WriteNodesAdded(IList objects) {
+			AddRemove(objects, "Added");
+		}
+	}
+	
+	public abstract class NodeInterface {
+		public abstract IList GetChildren(object node);
+		public abstract float Compare(object left, object right, StructuredDiff comparer);
+		public virtual int GetHashCode(object node) {
+			return node.GetHashCode();
+		}
+	}
+	
+	public interface XmlOutputNodeInterface {
+		void WriteNodeChildren(object node, XmlWriter output);
+		void WriteBeginNode(object left, object right, XmlWriter output);
+	}
+	
+	internal class HashCodeProvider : IHashCodeProvider {
+		StructuredDiff differ;
+		public HashCodeProvider(StructuredDiff differ) { this.differ = differ; }
+		public int GetHashCode(object obj) {
+			return differ.GetInterface(obj).GetHashCode(obj);
+		}
+	}
+	
+	internal class NodeComparerWrapper : IComparer {
+		float threshold;
+		StructuredDiff differ;
+		
+		public float minimumDifference = 1;
+		
+		bool useCache = true;
+		
+		public NodeComparerWrapper(float threshold, StructuredDiff differ) {
+			this.threshold = threshold;
+			this.differ = differ;
+		}
+		
+		private class Pair {
+			object left, right;
+			int code;
+			public Pair(object a, object b, StructuredDiff differ) {
+				left = a; right = b;
+				code = unchecked(differ.GetInterface(left).GetHashCode(left) + differ.GetInterface(right).GetHashCode(right));
+			}
+			public override bool Equals(object o) {
+				return ((Pair)o).left == left && ((Pair)o).right == right;
+			}
+			public override int GetHashCode() {
+				return code;
+			}
+		}
+		
+		int IComparer.Compare(object left, object right) {
+			float ret;
+			
+			Pair pair = new Pair(left, right, differ);
+			
+			if (left.GetType() != right.GetType()) {
+				ret = 1;
+			} else if (useCache && differ.comparisonCache.ContainsKey(pair)) {
+				ret = (float)differ.comparisonCache[pair];
+			} else {
+				NodeInterface comparer = differ.GetInterface(left);
+				ret = comparer.Compare(left, right, differ);
+			}
+			
+			if (useCache)
+				differ.comparisonCache[pair] = ret;
+			
+			if (ret < minimumDifference && ret > threshold)
+				minimumDifference = ret;
+			
+			if (ret <= threshold)
+				return 0;
+			else
+				return 1;
+		}
+	}
+
+}

Added: trunk/MonoDevelop/Extras/VersionControl/Diff/TextDiff.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/TextDiff.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/TextDiff.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,177 @@
+using System;
+using System.Collections;
+
+namespace Algorithm.Diff {
+	
+	public class TextDiff : IDiff {
+		Node left, right;
+		ArrayList hunks = new ArrayList();
+		
+		public TextDiff(string left, string right) : this (left, right, null) {
+		}
+		
+		public TextDiff(string left, string right, IComparer comparer) {
+			this.left = new Node(left, 0, left.Length, 0);
+			this.right = new Node(right, 0, right.Length, 0);
+			
+
+			TextDiffStructuredDiff diff = new TextDiffStructuredDiff(hunks, left.ToCharArray(), right.ToCharArray());
+			diff.AddInterface(typeof(Node), new TextDiffNodeInterface(comparer));
+			diff.Compare(this.left, this.right);
+		}
+		
+		public IList Left { get { return left.ToString().ToCharArray(); } }
+		public  IList Right { get { return right.ToString().ToCharArray(); } }
+		
+		IEnumerator IEnumerable.GetEnumerator() {
+			return hunks.GetEnumerator();
+		}
+
+		private class TextDiffStructuredDiff : StructuredDiff {
+			ArrayList hunks;
+			IList leftlist, rightlist;
+			
+			public TextDiffStructuredDiff(ArrayList hunks, IList leftlist, IList rightlist) {
+				this.hunks = hunks;
+				this.leftlist = leftlist;
+				this.rightlist = rightlist;
+			}
+			
+			protected override void WritePushNode(NodeInterface nodeInterface, object left, object right) {
+			}
+	
+			protected override void WritePushSame() {
+			}
+			
+			private void AddHunk(int lcount, int rcount, bool same) {
+				if (hunks.Count > 0 && same == ((Diff.Hunk)hunks[hunks.Count-1]).Same) {
+					Diff.Hunk prev = (Diff.Hunk)hunks[hunks.Count-1];
+					hunks[hunks.Count-1] = new Diff.Hunk(
+						leftlist,
+						rightlist,
+						prev.Left.Start,
+						prev.Left.End + lcount,
+						prev.Right.Start,
+						prev.Right.End + rcount,
+						same);
+					return;
+				}
+				
+				int le = -1;
+				int re = -1;
+				if (hunks.Count > 0) {
+					Diff.Hunk prev = (Diff.Hunk)hunks[hunks.Count-1];
+					le = prev.Left.End;
+					re = prev.Right.End;
+				}
+				
+				hunks.Add( new Diff.Hunk(
+						leftlist,
+						rightlist,
+						le + 1,
+						le + lcount,
+						re + 1,
+						re + rcount,
+						same) );
+			}
+		
+			protected override void WriteNodeSame(NodeInterface nodeInterface, object left, object right) {
+				AddHunk(((Node)left).count, ((Node)right).count, true);
+			}
+
+			protected override void WritePopSame() {
+			}
+	
+			protected override void WriteNodeChange(NodeInterface leftInterface, object left, NodeInterface rightInterface, object right) {
+				AddHunk(((Node)left).count, ((Node)right).count, false);
+			}
+	
+			protected override void WriteNodesRemoved(IList objects) {
+				int start = ((Node)objects[0]).start;
+				int end = ((Node)objects[objects.Count-1]).start + ((Node)objects[objects.Count-1]).count - 1;
+				
+				AddHunk(end - start + 1, 0, false);
+			}
+	
+			protected override void WriteNodesAdded(IList objects) {
+				int start = ((Node)objects[0]).start;
+				int end = ((Node)objects[objects.Count-1]).start + ((Node)objects[objects.Count-1]).count - 1;
+				
+				AddHunk(0, end - start + 1, false);
+			}
+	
+			protected override void WritePopNode() {
+			}
+		}
+		
+		private class TextDiffNodeInterface : NodeInterface {
+			IComparer comparer;
+			
+			public TextDiffNodeInterface(IComparer comparer) { this.comparer = comparer; }
+			
+			public override IList GetChildren(object node) {
+				if (((Node)node).children.Count == 0) return null;
+				return ((Node)node).children;
+			}
+			
+			private bool Equal(string a, string b) {
+				if (comparer == null)
+					return a == b;
+				return comparer.Compare(a, b) == 0;
+			}
+			
+			public override float Compare(object left, object right, StructuredDiff comparer) {
+				string l = left.ToString(), r = right.ToString();
+				if (Equal(l, r)) return 0;
+				if (l.Length == 1 || r.Length == 1) return 1;
+				float d = comparer.CompareLists(GetChildren(left), GetChildren(right));
+				if (((Node)left).level == 2 && d >= .75) d = 1.1f;
+				return d;
+			}
+			
+			public override int GetHashCode(object node) {
+				return node.ToString().GetHashCode();
+			}
+		}	
+		
+		private class Node {
+			public string source;
+			public int start, count;
+			
+			public int level;
+			public ArrayList children = new ArrayList();
+			
+			static char[][] delims = new char[][] {
+				new char[] { '\n', '\r' },
+				new char[] { ' ', '\t', '.', ',' }
+				};
+			
+			public Node(string source, int start, int count, int level) {
+				this.source = source;
+				this.start = start;
+				this.count = count;
+				this.level = level;
+				
+				if (level <= 1) {
+					int pos = start;
+					foreach (string child in ToString().Split(delims[level])) {
+						if (child.Length >= 1)
+							children.Add(new Node(source, pos, child.Length, level+1));
+						if (pos + child.Length < count)
+							children.Add(new Node(source, pos+child.Length, 1, level+1));
+						pos += child.Length + 1;
+					}
+				} else if (level == 2) {
+					for (int i = start; i < start + count; i++)
+						children.Add(new Node(source, i, 1, level+1));
+				}
+				
+			}
+			
+			public override string ToString() {
+				return source.Substring(start, count);
+			}
+		}
+	}
+	
+}

Added: trunk/MonoDevelop/Extras/VersionControl/Diff/UnifiedDiff.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Diff/UnifiedDiff.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Diff/UnifiedDiff.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,157 @@
+/*
+ * A utility class for writing unified diffs.
+ */
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+namespace Algorithm.Diff {
+	
+	public class UnifiedDiff {
+		private UnifiedDiff() {
+		}
+		
+		public static void WriteUnifiedDiff(string[] leftLines, string leftName, string[] rightLines, string rightName, System.IO.TextWriter writer, int context, bool caseSensitive, bool compareWhitespace) {
+			Diff diff = new Diff(leftLines, rightLines, caseSensitive, compareWhitespace);
+			WriteUnifiedDiff(diff, writer, leftName, rightName, context);
+		}
+
+		public static void WriteUnifiedDiff(string leftFile, string rightFile, System.IO.TextWriter writer, int context, bool caseSensitive, bool compareWhitespace) {
+			WriteUnifiedDiff(LoadFileLines(leftFile), leftFile, LoadFileLines(rightFile), rightFile, writer, context, caseSensitive, compareWhitespace);
+		}
+		
+		internal static string[] LoadFileLines(string file) {
+			ArrayList lines = new ArrayList();
+			using (System.IO.StreamReader reader = new System.IO.StreamReader(file)) {
+				string s;
+				while ((s = reader.ReadLine()) != null)
+					lines.Add(s);
+			}
+			return (string[])lines.ToArray(typeof(string));
+		}
+
+		public static void WriteUnifiedDiff(Diff diff, TextWriter writer) {
+			WriteUnifiedDiff(diff, writer, "Left", "Right", 2);
+		}
+		
+		public static void WriteUnifiedDiff(Diff diff, TextWriter writer, string fromfile, string tofile, int context) {
+			writer.Write("--- ");
+			writer.WriteLine(fromfile);
+			writer.Write("+++ ");
+			writer.WriteLine(tofile);
+			
+			ArrayList hunkset = new ArrayList();
+			
+			foreach (Diff.Hunk hunk in diff) {
+				Diff.Hunk lasthunk = null;
+				if (hunkset.Count > 0) lasthunk = (Diff.Hunk)hunkset[hunkset.Count-1];
+				
+				if (hunk.Same) {
+					// At the start of a hunk set, keep only context lines of context.
+					if (lasthunk == null) {
+						if (hunk.Left.Count > context)
+							hunkset.Add( hunk.Crop(hunk.Left.Count-context, 0) );
+						else
+							hunkset.Add( hunk );
+					// Can't have two same hunks in a row, so the last one was a difference.
+					} else {
+						// Small enough context that this unified diff range will not stop.
+						if (hunk.Left.Count <= context*2) {
+							hunkset.Add( hunk );
+							
+						// Too much of the same.  Keep context lines and end this section.
+						// And then keep the last context lines as context for the next section.
+						} else {
+							hunkset.Add( hunk.Crop(0, hunk.Left.Count-context) );
+							WriteUnifiedDiffSection(writer, hunkset);
+							hunkset.Clear();
+							
+							if (hunk.Left.Count > context)
+								hunkset.Add( hunk.Crop(hunk.Left.Count-context, 0) );
+							else
+								hunkset.Add( hunk );
+						}
+					}
+				
+				} else {
+					hunkset.Add(hunk);
+				}
+			}
+			
+			if (hunkset.Count > 0 && !(hunkset.Count == 1 && ((Diff.Hunk)hunkset[0]).Same))
+				WriteUnifiedDiffSection(writer, hunkset);
+		}
+			
+		private static void WriteUnifiedDiffSection(TextWriter writer, ArrayList hunks) {
+			Diff.Hunk first = (Diff.Hunk)hunks[0];
+			Diff.Hunk last = (Diff.Hunk)hunks[hunks.Count-1];
+			
+			writer.Write("@@ -");
+			writer.Write(first.Left.Start+1);
+			writer.Write(",");
+			writer.Write(last.Left.End-first.Left.Start+1);
+			writer.Write(" +");
+			writer.Write(first.Right.Start+1);
+			writer.Write(",");
+			writer.Write(last.Right.End-first.Right.Start+1);
+			writer.WriteLine(" @@");
+			
+			foreach (Diff.Hunk hunk in hunks) {
+				if (hunk.Same) {
+					WriteBlock(writer, ' ', hunk.Left);
+					continue;
+				}
+				
+				WriteBlock(writer, '-', hunk.Left);
+				WriteBlock(writer, '+', hunk.Right);
+			}
+		}
+		
+		private static void WriteBlock(TextWriter writer, char prefix, Range items) {
+			if (items.Count > 0 && items[0] is char)
+				WriteCharBlock(writer, prefix, items);
+			else
+				WriteStringBlock(writer, prefix, items);
+		}
+		
+		private static void WriteStringBlock(TextWriter writer, char prefix, Range items) {
+			foreach (object item in items) {
+				writer.Write(prefix);
+				writer.WriteLine(item.ToString());
+			}
+		}
+		
+		private static void WriteCharBlock(TextWriter writer, char prefix, Range items) {
+			bool newline = true;
+			int counter = 0;
+			foreach (char c in items) {
+				if (c == '\n' && !newline) {
+					writer.WriteLine();
+					newline = true;
+				}
+				
+				if (newline) {
+					writer.Write(prefix);
+					newline = false;
+					counter = 0;
+				}
+				
+				if (c == '\n') {
+					writer.WriteLine("[newline]");
+					newline = true;
+				} else {
+					writer.Write(c);
+					counter++;
+					if (counter == 60) {
+						writer.WriteLine();
+						newline = true;
+					}
+				}
+			}
+			if (!newline) writer.WriteLine();
+		}
+	}
+}
+

Added: trunk/MonoDevelop/Extras/VersionControl/DiffWidget/Makefile.am
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/DiffWidget/Makefile.am	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/DiffWidget/Makefile.am	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,19 @@
+
+ADDIN_BUILD = $(top_builddir)/build/AddIns/VersionControl
+ASSEMBLY = $(ADDIN_BUILD)/DiffWidget.dll
+
+DLLS = 
+FILES = *.cs
+
+$(ADDIN_BUILD)/$(ADDIN): $(srcdir)/$(ADDIN)
+
+$(ASSEMBLY): $(FILES)
+	mkdir -p $(ADDIN_BUILD)
+	$(CSC) $(CSC_FLAGS) $(FILES) -out:$@ -target:library -r:$(ADDIN_BUILD)/Diff.dll $(GTK_SHARP_LIBS)
+
+CLEANFILES = $(ASSEMBLY) $(ASSEMBLY).mdb
+EXTRA_DIST = $(FILES)
+
+all: $(ASSEMBLY)
+
+include $(top_srcdir)/Makefile.include

Added: trunk/MonoDevelop/Extras/VersionControl/DiffWidget/README
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/DiffWidget/README	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/DiffWidget/README	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,2 @@
+This file creates a Gtk# widget for displaying
+differences between files.

Added: trunk/MonoDevelop/Extras/VersionControl/DiffWidget/widget.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/DiffWidget/widget.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/DiffWidget/widget.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,416 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Reflection;
+using System.Text;
+
+using Gtk;
+using Pango;
+
+namespace Algorithm.Diff.Gtk {
+
+	public class DiffWidget : VBox {
+	
+		ScrolledWindow scroller;
+	
+		public double Position {
+			get {
+				return scroller.Vadjustment.Value;
+			}
+			set {
+				scroller.Vadjustment.Value = value;
+			}
+		}
+		 
+		public class Options {
+			public string LeftName = null;
+			public string RightName = null;
+			public bool SideBySide = false;
+			public bool LineWrap = true;
+			public bool LineNumbers = true;
+			public string Font = "Mono 9";
+		}
+		
+		static Gdk.Color
+			ColorChanged = new Gdk.Color (0xFF, 0x99, 0x99),
+			ColorChangedHighlight = new Gdk.Color (0xFF, 0xBB, 0xBB),
+			ColorAdded = new Gdk.Color (0xFF, 0xAA, 0x33),
+			ColorAddedHighlight = new Gdk.Color (0xFF, 0xBB, 0x66),
+			ColorRemoved = new Gdk.Color (0x33, 0xAA, 0xFF),
+			ColorRemovedHighlight = new Gdk.Color (0x66, 0xBB, 0xFF),
+			ColorDefault = new Gdk.Color (0xFF, 0xFF, 0xFF),
+			ColorDefaultHighlight = new Gdk.Color (0xEE, 0xEE, 0xCC),
+			ColorGrey = new Gdk.Color (0x99, 0x99, 0x99),
+			ColorBlack = new Gdk.Color (0, 0, 0);
+
+		public DiffWidget(Diff diff, Options options) : this(Hunkify(diff), options) {
+		}
+			
+		public DiffWidget(Merge merge, Options options) : this(Hunkify(merge), options) {
+		}
+		
+		private static Hunk[] Hunkify(IEnumerable e) {
+			ArrayList a = new ArrayList();
+			foreach (Hunk x in e)
+				a.Add(x);
+			return (Hunk[])a.ToArray(typeof(Hunk));
+		}
+
+		private DiffWidget(Hunk[] hunks, Options options) : base(false, 0) {
+			if (hunks == null || hunks.Length == 0 || options == null)
+				throw new ArgumentException();
+			
+			if (options.SideBySide && options.LeftName != null && options.RightName != null) {
+				HBox filetitles = new HBox(true, 2);
+				PackStart(filetitles, false, false, 2);
+				Label leftlabel = new Label(options.LeftName);
+				Label rightlabel = new Label(options.RightName);
+				filetitles.PackStart(leftlabel);
+				filetitles.PackStart(rightlabel);
+			}
+			
+			HBox centerpanel = new HBox(false, 0);
+			PackStart(centerpanel);
+
+			scroller = new ScrolledWindow();
+			
+			centerpanel.PackStart(new OverviewRenderer(scroller, hunks, options.SideBySide), false, false, 0);
+			
+			Viewport textviewport = new Viewport();
+			
+			centerpanel.PackStart(scroller);
+			scroller.Add(textviewport);
+			
+			int nRows = 0;
+			foreach (Hunk hunk in hunks) {
+				if (options.SideBySide) {
+					nRows += hunk.MaxLines();
+				} else {
+					if (hunk.Same) {
+						nRows += hunk.Original().Count;
+					} else {
+						for (int i = 0; i < hunk.ChangedLists; i++)
+							nRows += hunk.Changes(i).Count;
+					}
+				}
+			}
+			
+			uint nCols = 1 + (uint)hunks[0].ChangedLists;
+			if (options.SideBySide) nCols += 2;
+			if (options.LineNumbers) nCols++;
+			
+			VBox tablecontainer = new VBox(false, 0);
+			textviewport.Add(tablecontainer);
+			
+			Table difftable = new Table((uint)nRows, (uint)nCols, false);
+			tablecontainer.PackStart(difftable, false, false, 0);	
+			
+			uint row = 0;
+			
+			Pango.FontDescription font = null;
+			if (options.Font != null)
+				font = Pango.FontDescription.FromString(options.Font);
+			
+			foreach (Hunk hunk in hunks) {
+				char leftmode = hunk.Same ? ' ' : (hunk.ChangedLists == 1 && hunk.Changes(0).Count == 0) ? '-' : 'C';
+				uint inc = 0;
+				
+				if (options.SideBySide) {
+					ComposeLines(hunk.Original(), leftmode, -1, difftable, row, false, 0, options.LineWrap, font, options.LineNumbers);
+					inc = (uint)hunk.Original().Count;
+				} else { 
+					if (leftmode == 'C') leftmode = '-';
+					int altlines = -1;
+					if (hunk.ChangedLists == 1 && hunk.Same)
+						altlines = hunk.Changes(0).Start;
+					ComposeLines(hunk.Original(), leftmode, altlines, difftable, row, true, 0, options.LineWrap, font, options.LineNumbers);
+					row += (uint)hunk.Original().Count;
+				}
+
+				for (int i = 0; i < hunk.ChangedLists; i++) {
+					char rightmode = hunk.Same ? ' ' : hunk.Original().Count == 0 ? '+' : 'C';
+					
+					if (options.SideBySide) {
+						int colsper = 1 + (options.LineNumbers ? 1 : 0);			
+						ComposeLines(hunk.Changes(i), rightmode, -1, difftable, row, false, (uint)((i+1)*colsper), options.LineWrap, font, options.LineNumbers);
+						if (hunk.Changes(i).Count > inc)
+							inc = (uint)hunk.Changes(i).Count;
+					} else {
+						if (rightmode == 'C') rightmode = '+';
+		
+						if (!hunk.Same) 
+							ComposeLines(hunk.Changes(i), rightmode, -1, difftable, row, true, 0, options.LineWrap, font, options.LineNumbers);
+						
+						if (!hunk.Same) row += (uint)hunk.Changes(i).Count;
+					}
+				}
+				
+				if (options.SideBySide)
+					row += inc;
+			}
+		}
+		
+		void ComposeLines(Algorithm.Diff.Range range, char style, int otherStart, Table table, uint startRow, bool axn, uint col, bool wrap, Pango.FontDescription font, bool lineNumbers) {
+			if (range.Count == 0) return;
+			
+			StringBuilder text = new StringBuilder();
+			for (uint i = 0; i < range.Count; i++) {
+				if (axn) {
+					text.Append(style);
+					text.Append(' ');
+				}
+
+				if (lineNumbers) {
+					string lineNo = (range.Start + i + 1).ToString();
+					if (otherStart != -1 && range.Start != otherStart)
+					lineNo += "/" + (otherStart + i + 1).ToString();
+					text.Append(lineNo);
+					text.Append('\t');
+				}
+				
+				text.Append((string)range[(int)i]);
+				if (i < range.Count-1) text.Append('\n');
+			}
+			RangeEventBox rangebox = new RangeEventBox(new RangeRenderer(text.ToString(), style, wrap, font));
+			table.Attach(rangebox, 0,1, startRow,(uint)(startRow+range.Count));
+			
+			/*
+				int off1 = lineNumbers ? 1 : 0;
+				int off2 = off1 + (axn ? 1 : 0);
+				
+				for (uint i = 0; i < range.Count; i++) {
+					if (lineNumbers) {
+						string lineNo = (range.Start + i + 1).ToString();
+						if (otherStart != -1 && range.Start != otherStart)
+						lineNo += "/" + (otherStart + i + 1).ToString();
+					
+						Label label = new Label(lineNo);
+						label.ModifyFont( font );
+						label.Yalign = 0;
+						table.Attach(label, col,col+1, startRow+i,startRow+i+1, AttachOptions.Shrink, AttachOptions.Shrink, 1, 1);
+					}
+					
+					if (axn) {
+						Label actionlabel = new Label(style.ToString());
+						table.Attach(actionlabel, (uint)(col+off1),(uint)(col+off1+1), startRow+i,startRow+i+1, AttachOptions.Shrink, AttachOptions.Shrink, 1, 1);
+					}
+
+					RangeEventBox line = new RangeEventBox(new RangeRenderer((string)range[(int)i], style, wrap, font));
+					table.Attach(line, (uint)(col+off2),(uint)(col+off2+1), startRow+i,startRow+i+1);
+				}
+			*/
+		}
+		
+		/*string GetRangeText(Algorithm.Diff.Range range) {
+			string text = "";
+			foreach (string line in range)
+				text += line + "\n";
+			return text;
+		}*/
+
+		class RangeEventBox : EventBox {
+			RangeRenderer renderer;
+			
+			public RangeEventBox(RangeRenderer r) {
+				renderer = r;
+				Add(r);
+				this.EnterNotifyEvent += new EnterNotifyEventHandler(EnterNotifyEventHandler);
+				this.LeaveNotifyEvent += new LeaveNotifyEventHandler(LeaveNotifyEventHandler);
+			}
+
+			void EnterNotifyEventHandler (object o, EnterNotifyEventArgs args) {
+				renderer.Highlight();
+			}		
+			void LeaveNotifyEventHandler (object o, LeaveNotifyEventArgs args) {
+				renderer.ClearHighlight();
+			}		
+		}
+		
+		class RangeRenderer : DrawingArea {
+			Pango.Layout layout;
+			Pango.FontDescription font;
+			string text;
+			bool wrap;
+
+			Gdk.Color bg_color;
+			Gdk.Color highlight_bg_color;
+
+			public RangeRenderer(string text, char type, bool wrap, Pango.FontDescription font) {
+				this.text = text;
+				this.wrap = wrap;
+				this.font = font;
+				this.Realized += new EventHandler(OnRealized);
+				this.ExposeEvent += new ExposeEventHandler(OnExposed);
+				this.SizeAllocated += new SizeAllocatedHandler(SizeAllocatedHandler);
+				
+				switch (type) {
+					case 'C':
+						bg_color = DiffWidget.ColorChanged;
+						highlight_bg_color = DiffWidget.ColorChangedHighlight;
+						break;
+					case '+':
+						bg_color = DiffWidget.ColorAdded;
+						highlight_bg_color = DiffWidget.ColorAddedHighlight;
+						break;
+					case '-':
+						bg_color = DiffWidget.ColorRemoved;
+						highlight_bg_color = DiffWidget.ColorRemovedHighlight;
+						break;
+					default:
+						bg_color = DiffWidget.ColorDefault;
+						highlight_bg_color = DiffWidget.ColorDefaultHighlight;
+						break;
+				}
+				
+				Colormap.AllocColor(ref bg_color, false, true);
+				Colormap.AllocColor(ref highlight_bg_color, false, true);
+				ClearHighlight();
+			}
+			
+			public void Highlight() {
+				ModifyBg(StateType.Normal, highlight_bg_color);
+			}
+			
+			public void ClearHighlight() {
+				ModifyBg(StateType.Normal, bg_color);
+			}
+
+			void OnExposed (object o, ExposeEventArgs args) {
+				GdkWindow.DrawLayout (this.Style.TextGC (StateType.Normal), 1, 1, layout);
+			}
+			
+			void OnRealized (object o, EventArgs args) {
+				layout = new Pango.Layout(PangoContext);
+				layout.SingleParagraphMode = false;
+				layout.FontDescription = font;
+				layout.Indent = (int)(-Pango.Scale.PangoScale * 20);
+				
+				layout.SetText(text);
+				Render();
+			}
+			
+			void SizeAllocatedHandler (object o, SizeAllocatedArgs args) {
+				Render();
+			}
+			
+			void Render() {
+				if (layout == null) return;
+				
+				if (wrap)
+					layout.Width = (int)(Allocation.Width * Pango.Scale.PangoScale);
+				else
+					layout.Width = int.MaxValue;
+				
+				Rectangle ink, log;
+				layout.GetPixelExtents(out ink, out log);
+				HeightRequest = ink.Y + ink.Height + 3;
+				if (!wrap)
+					WidthRequest = ink.X + ink.Width + 3;
+			}
+		}
+		
+		class OverviewRenderer : EventBox {
+			ScrolledWindow scroller;
+			public OverviewRenderer(ScrolledWindow scroller, Hunk[] hunks, bool sidebyside) {
+				this.scroller = scroller;
+				this.ButtonPressEvent += new ButtonPressEventHandler(ButtonPressHandler);
+				Add(new OverviewRenderer2(scroller, hunks, sidebyside));
+			}
+			
+			void ButtonPressHandler(object o, ButtonPressEventArgs args) {
+				double position = ((double)args.Event.Y / Allocation.Height - (double)scroller.Allocation.Height/scroller.Vadjustment.Upper/2) * scroller.Vadjustment.Upper;
+				if (position < 0) position = 0;
+				if (position + scroller.Allocation.Height > scroller.Vadjustment.Upper) position = scroller.Vadjustment.Upper - scroller.Allocation.Height;
+				scroller.Vadjustment.Value = position;
+			}
+		}
+		
+		class OverviewRenderer2 : DrawingArea {
+			Hunk[] hunks;
+			ScrolledWindow scroller;
+			bool sidebyside;
+			
+			public OverviewRenderer2(ScrolledWindow scroller, Hunk[] hunks, bool sidebyside) {
+				this.hunks = hunks;
+				this.scroller = scroller;
+				this.sidebyside = sidebyside;
+				this.ExposeEvent += new ExposeEventHandler(OnExposed);
+				scroller.ExposeEvent += new ExposeEventHandler(OnScroll);
+				WidthRequest = 50;
+			}
+			
+			void OnScroll (object o, ExposeEventArgs args) {
+				QueueDrawArea(0, 0, Allocation.Width, Allocation.Height);
+			}
+			
+			void OnExposed (object o, ExposeEventArgs args) {
+				Gdk.GC gc = this.Style.BaseGC(StateType.Normal);
+				
+				gc.Colormap.AllocColor(ref DiffWidget.ColorChanged, false, true);
+				gc.Colormap.AllocColor(ref DiffWidget.ColorChangedHighlight, false, true);
+				gc.Colormap.AllocColor(ref DiffWidget.ColorAdded, false, true);
+				gc.Colormap.AllocColor(ref DiffWidget.ColorAddedHighlight, false, true);
+				gc.Colormap.AllocColor(ref DiffWidget.ColorRemoved, false, true);
+				gc.Colormap.AllocColor(ref DiffWidget.ColorRemovedHighlight, false, true);
+				gc.Colormap.AllocColor(ref DiffWidget.ColorDefault, false, true);
+				gc.Colormap.AllocColor(ref DiffWidget.ColorDefaultHighlight, false, true);
+				gc.Colormap.AllocColor(ref DiffWidget.ColorBlack, false, true);
+				gc.Colormap.AllocColor(ref DiffWidget.ColorGrey, false, true);
+
+				int count = 0;
+				foreach (Hunk h in hunks) {
+					IncPos(h, ref count);
+				}
+				
+				int start = 0;
+				foreach (Hunk h in hunks) {
+					int size = 0;
+					IncPos(h, ref size);
+					
+					if (h.Same)
+						gc.Foreground = DiffWidget.ColorDefault;
+					else if (h.Original().Count == 0)
+						gc.Foreground = DiffWidget.ColorAdded;
+					else if (h.ChangedLists == 1 && h.Changes(0).Count == 0)
+						gc.Foreground = DiffWidget.ColorRemoved;
+					else
+						gc.Foreground = DiffWidget.ColorChanged;
+					
+					GdkWindow.DrawRectangle(gc, true, 0, Allocation.Height*start/count, Allocation.Width, Allocation.Height*size/count);
+					
+					start += size;
+				}
+
+				gc.Foreground = DiffWidget.ColorGrey;
+				GdkWindow.DrawRectangle(gc, false,
+					1,
+					(int)(Allocation.Height*scroller.Vadjustment.Value/scroller.Vadjustment.Upper) + 1,
+					Allocation.Width-3,
+					(int)(Allocation.Height*((double)scroller.Allocation.Height/scroller.Vadjustment.Upper))-2);
+				
+				gc.Foreground = DiffWidget.ColorBlack;
+				GdkWindow.DrawRectangle(gc, false,
+					0,
+					(int)(Allocation.Height*scroller.Vadjustment.Value/scroller.Vadjustment.Upper),
+					Allocation.Width-1,
+					(int)(Allocation.Height*((double)scroller.Allocation.Height/scroller.Vadjustment.Upper)));
+				
+				// Reset this otherwise MD colors get messed up.
+				// But what should I really do here?
+				gc.Foreground = DiffWidget.ColorDefault;
+			}
+			
+			private void IncPos(Hunk h, ref int pos) {
+				if (sidebyside)
+					pos += h.MaxLines();
+				else if (h.Same)
+					pos += h.Original().Count;
+				else {
+					pos += h.Original().Count;
+					for (int i = 0; i < h.ChangedLists; i++)
+						pos += h.Changes(i).Count;
+				}
+			}
+		}	
+	}
+}

Added: trunk/MonoDevelop/Extras/VersionControl/Makefile.am
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/Makefile.am	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/Makefile.am	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,2 @@
+
+SUBDIRS = Diff DiffWidget VersionControl AddIn

Added: trunk/MonoDevelop/Extras/VersionControl/README
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/README	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/README	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,7 @@
+This is version control support for MD, including
+supporting libraries:
+	Diff: for finding differences between files
+	DiffWidget: for displaying differences in Gtk
+	VersionControl: abstraction over various systems
+		e.g. subversion
+The addin itself is in the AddIn directory.

Added: trunk/MonoDevelop/Extras/VersionControl/VersionControl/Makefile.am
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/VersionControl/Makefile.am	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/VersionControl/Makefile.am	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,19 @@
+
+ADDIN_BUILD = $(top_builddir)/build/AddIns/VersionControl
+ASSEMBLY = $(ADDIN_BUILD)/VersionControl.dll
+
+DLLS = 
+FILES = *.cs
+
+$(ADDIN_BUILD)/$(ADDIN): $(srcdir)/$(ADDIN)
+
+$(ASSEMBLY): $(FILES)
+	mkdir -p $(ADDIN_BUILD)
+	$(CSC) $(CSC_FLAGS) $(FILES) -out:$@ -target:library
+
+CLEANFILES = $(ASSEMBLY) $(ASSEMBLY).mdb
+EXTRA_DIST = $(FILES)
+
+all: $(ASSEMBLY)
+
+include $(top_srcdir)/Makefile.include

Added: trunk/MonoDevelop/Extras/VersionControl/VersionControl/Subversion.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/VersionControl/Subversion.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/VersionControl/Subversion.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,919 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using System.Runtime.InteropServices;
+
+namespace VersionControl {
+
+	public class SubversionVersionControl : VersionControlSystem {
+		SvnClient client;
+		
+		SvnClient Client {
+			get {
+				if (client == null) client = new SvnClient();
+				return client;
+			}
+		}
+	
+		string GetTextBase(string sourcefile) {
+			return Path.Combine(
+				Path.Combine(
+					Path.Combine(
+						Path.GetDirectoryName(sourcefile),
+						 ".svn"),
+					"text-base"),
+				Path.GetFileName(sourcefile) + ".svn-base"); 
+		}
+	
+		string GetDirectoryDotSvn(string sourcepath) {
+			return Path.Combine(sourcepath, ".svn");
+		}
+		
+		public override bool IsDiffAvailable(string sourcefile) {
+			return File.Exists(GetTextBase(sourcefile));			
+		}
+		
+		public override bool IsHistoryAvailable(string sourcefile) {
+			return IsFileStatusAvailable(sourcefile) || IsDirectoryStatusAvailable(sourcefile);
+		}
+		
+		public override bool IsFileStatusAvailable(string sourcefile) {
+			return IsDiffAvailable(sourcefile);
+		}
+
+		public override bool IsDirectoryStatusAvailable(string sourcepath) {
+			return Directory.Exists(GetDirectoryDotSvn(sourcepath));
+		}
+		
+		public override bool CanUpdate(string sourcepath) {
+			return IsFileStatusAvailable(sourcepath)
+				|| IsDirectoryStatusAvailable(sourcepath);
+		}
+		
+		public override bool CanCommit(string sourcepath) {
+			return CanUpdate(sourcepath);
+		}
+		
+		public override string GetPathToBaseText(string sourcefile) {
+			return GetTextBase(sourcefile);
+		}
+		
+		public override RevisionDescription[] GetHistory(string sourcefile, RevisionPtr since) {
+			ArrayList revs = new ArrayList();
+			string curPath = Client.GetPathUrl(sourcefile);
+			if (curPath == null) return null;
+
+			SvnClient.Rev sincerev = SvnClient.Rev.First;
+			if (since != null)
+				sincerev = ((SvnRevisionPtr)since).ForApi();
+						
+			foreach (SvnClient.LogEnt log in Client.Log(sourcefile, SvnClient.Rev.Head, sincerev)) {
+				RevisionDescription d = new RevisionDescription();
+				d.Author = log.Author;
+				d.Message = log.Message;
+				d.Revision = new SvnRevisionPtr(log.Revision);
+				d.RepositoryPath = curPath;
+				d.Time = log.Time;
+				revs.Add(d);
+
+				// Be aware of the change in path resulting from a copy
+				foreach (SvnClient.LogEntChangedPath chg in log.ChangedPaths) {
+					if (curPath.EndsWith(chg.Path) && chg.CopyFromPath != null) {
+						curPath = curPath.Substring(0, curPath.Length-chg.Path.Length) + chg.CopyFromPath;
+					}
+				}
+			}
+			return (RevisionDescription[])revs.ToArray(typeof(RevisionDescription));
+		}
+		
+		public override string GetTextAtRevision(object repositoryPath, RevisionPtr revision) {
+			return Client.Cat(repositoryPath.ToString(), ((SvnRevisionPtr)revision).ForApi() );
+		}
+		
+		public override Node GetFileStatus(string sourcefile, bool getRemoteStatus) {
+			IList statuses = Client.Status(sourcefile, SvnClient.Rev.Head, false, false, getRemoteStatus);
+			if (statuses.Count != 1)
+				throw new ArgumentException("Path does not refer to a file.");
+			SvnClient.StatusEnt ent = (SvnClient.StatusEnt)statuses[0];
+			if (ent.IsDirectory)
+				throw new ArgumentException("Path does not refer to a file.");
+			return CreateNode(ent, 0);
+		}
+		
+		public override Node[] GetDirectoryStatus(string sourcepath, bool getRemoteStatus, bool recursive) {
+			ArrayList ret = new ArrayList();
+			string sourceurl = Client.GetPathUrl(sourcepath);
+			foreach (SvnClient.StatusEnt ent in Client.Status(sourcepath, SvnClient.Rev.Head, recursive, true, getRemoteStatus))
+				ret.Add( CreateNode(ent, sourceurl.Length+1) );
+			return (Node[])ret.ToArray(typeof(Node));
+		}
+		
+		public override void Update(string path, bool recurse, UpdateCallback callback) {
+			Client.Update(path, recurse, callback);
+		}
+		
+		public override void Commit(string[] paths, string message, UpdateCallback callback) {
+			Client.Commit(paths, message, callback);
+		}
+		
+		private Node CreateNode(SvnClient.StatusEnt ent, int pathchop) {
+			Node ret = new Node();
+			
+			ret.RepositoryPath = ent.Url;
+			ret.LocalRelativePath = ent.Url.Substring(pathchop);
+			ret.IsDirectory = ent.IsDirectory;
+			ret.BaseRevision = new SvnRevisionPtr(ent.Revision);
+			ret.Status = ConvertStatus(ent.Schedule, ent.TextStatus);
+			
+			if (ent.RemoteTextStatus != SvnClient.NodeStatus.EMPTY) {
+				ret.RemoteStatus = ConvertStatus(SvnClient.NodeSchedule.Normal, ent.RemoteTextStatus);
+				ret.RemoteUpdate = new RevisionDescription();
+				ret.RemoteUpdate.RepositoryPath = ent.Url;
+				ret.RemoteUpdate.Revision = new SvnRevisionPtr(ent.LastCommitRevision);
+				ret.RemoteUpdate.Author = ent.LastCommitAuthor;
+				ret.RemoteUpdate.Message = "(unavailable)";
+				ret.RemoteUpdate.Time = ent.LastCommitDate;
+			}
+			
+			return ret;
+		}
+		
+		private NodeStatus ConvertStatus(SvnClient.NodeSchedule schedule, SvnClient.NodeStatus status) {
+			switch (schedule) {
+				case SvnClient.NodeSchedule.Add: return NodeStatus.ScheduledAdd;
+				case SvnClient.NodeSchedule.Delete: return NodeStatus.ScheduledDelete;
+				case SvnClient.NodeSchedule.Replace: return NodeStatus.ScheduledReplace;
+			}
+		
+			switch (status) {
+				case SvnClient.NodeStatus.None: return NodeStatus.Unchanged;
+				case SvnClient.NodeStatus.Unversioned: return NodeStatus.Unversioned;
+				case SvnClient.NodeStatus.Modified: return NodeStatus.Modified;
+				case SvnClient.NodeStatus.Merged: return NodeStatus.Modified;
+				case SvnClient.NodeStatus.Conflicted: return NodeStatus.Conflicted;
+				case SvnClient.NodeStatus.Ignored: return NodeStatus.UnversionedIgnored;
+				case SvnClient.NodeStatus.Obstructed: return NodeStatus.Obstructed;
+			}
+			return NodeStatus.Unknown;
+		}
+
+		private class SvnRevisionPtr : RevisionPtr {
+			public readonly int Rev;
+			public SvnRevisionPtr(int rev) { Rev = rev; }
+			
+			public override string ToString() {
+				return "Rev " + Rev;
+			}
+		
+			public override RevisionPtr GetPrevious() {
+				return new SvnRevisionPtr(Rev-1);
+			}
+			
+			public SvnClient.Rev ForApi() {
+				return SvnClient.Rev.Number(Rev);
+			}
+		}
+
+	}
+	
+	public class SvnClient {
+		IntPtr pool;
+		IntPtr ctx;
+		
+		object sync = new object();
+		bool inProgress = false;
+		
+		UpdateCallback updatecallback;
+		string commitmessage = null;
+		
+		// retain this so the delegates aren't GC'ed
+		svn_client_ctx_t ctxstruct;
+		
+		static SvnClient() {
+			apr_initialize();
+		}
+
+		private IntPtr newpool(IntPtr parent) {
+			IntPtr p;
+			apr_pool_create_ex(out p, parent, IntPtr.Zero, IntPtr.Zero);
+			if (p == IntPtr.Zero)
+				throw new InvalidOperationException("Could not create an APR pool.");
+			return p;
+		}
+		
+		public SvnClient() {
+			// Allocate the APR pool and the SVN client context.
+			
+			pool = newpool(IntPtr.Zero);
+		
+			if (svn_client_create_context(out ctx, pool) != IntPtr.Zero)
+				throw new InvalidOperationException("Could not create a Subversion client context.");
+				
+			// Set the callbacks on the client context structure.
+		
+			// This is quite a roudabout way of doing this.  The task
+			// is to set the notify field of the unmanaged structure
+			// at the address 'ctx' -- with the address of a delegate.
+			// There's no way to get an address for the delegate directly,
+			// as far as I could figure out, so instead I create a managed
+			// structure that mirrors the start of the unmanaged structure
+			// I want to modify.  Then I marshal the managed structure
+			// *onto* to unmanaged one, overwriting fields in the process.
+			// I don't use references to the structure itself in the API
+			// calls because it needs to be allocated by SVN.  I hope
+			// this doesn't cause any memory leaks.
+			ctxstruct = new svn_client_ctx_t();
+			ctxstruct.NotifyFunc = new svn_wc_notify_func_t(svn_wc_notify_func_t_impl);
+			ctxstruct.LogMsgFunc = new svn_client_get_commit_log_t(svn_client_get_commit_log_impl);
+			Marshal.StructureToPtr(ctxstruct, ctx, false);
+		}
+		
+		[StructLayout(LayoutKind.Sequential)]
+		private struct svn_client_ctx_t {
+			public IntPtr auth_baton;
+			public svn_wc_notify_func_t NotifyFunc;
+			public IntPtr notify_baton;
+			public svn_client_get_commit_log_t LogMsgFunc;
+			public IntPtr logmsg_baton;
+		}
+
+		~SvnClient() {
+			apr_pool_destroy(pool);
+		}
+
+		// Wrappers for native interop
+		
+		public string Version {
+			get {
+				IntPtr ptr = svn_client_version();
+				svn_version_t ver = (svn_version_t)Marshal.PtrToStructure(ptr, typeof(svn_version_t));				
+				return ver.major + "." + ver.minor + "." + ver.patch;
+			}
+		}
+		
+		public IList List(string pathorurl, bool recurse, Rev revision) {
+			if (pathorurl == null) throw new ArgumentException();
+			
+			IntPtr localpool = newpool(pool);
+			ArrayList items = new ArrayList();
+			try {
+				IntPtr hash;
+			
+				CheckError(svn_client_ls(out hash, pathorurl, ref revision,
+	               recurse ? 1 : 0, ctx, localpool));
+	               
+	             IntPtr item = apr_hash_first(localpool, hash);
+	             while (item != IntPtr.Zero) {
+	             	IntPtr nameptr, val;
+	             	int namelen;
+	             	apr_hash_this(item, out nameptr, out namelen, out val);
+	             
+	             	string name = Marshal.PtrToStringAnsi(nameptr);
+					svn_dirent_t ent = (svn_dirent_t)Marshal.PtrToStructure(val, typeof(svn_dirent_t));				
+	             	item = apr_hash_next(item);
+	             	
+	             	items.Add(new DirEnt(name, ent));
+	             }	             
+	             
+			} finally {
+				apr_pool_destroy(localpool);
+			}
+			return items;
+		}
+
+		public IList Status(string path, Rev revision) {
+			return Status(path, revision, false, false, false);
+		}
+		
+		public IList Status(string path, Rev revision, bool descendDirs, bool changedItemsOnly, bool remoteStatus) {
+			if (path == null) throw new ArgumentException();
+		
+			ArrayList ret = new ArrayList();
+			int result_rev = 0;
+
+			StatusCollector collector = new StatusCollector(ret);
+			CheckError(svn_client_status (ref result_rev, path, ref revision,
+				new svn_wc_status_func_t(collector.Func),
+				IntPtr.Zero,
+				descendDirs ? 1 : 0, changedItemsOnly ? 0 : 1, remoteStatus ? 1 : 0, 1,
+				ctx, pool));
+				
+			return ret;
+		}
+		
+		public IList Log(string path, Rev revisionStart, Rev revisionEnd) {
+			if (path == null) throw new ArgumentException();
+			
+			ArrayList ret = new ArrayList();
+			IntPtr localpool = newpool(pool);
+			IntPtr strptr = IntPtr.Zero;
+			try {
+				IntPtr array = apr_array_make(localpool, 0, IntPtr.Size);
+				IntPtr first = apr_array_push(array);
+				strptr = Marshal.StringToHGlobalAnsi(path);
+				Marshal.WriteIntPtr(first, strptr);
+
+				LogCollector collector = new LogCollector(ret);
+				
+				CheckError(svn_client_log (
+					array,
+					ref revisionStart, ref revisionEnd,
+					1,
+		            0,
+		        	new svn_log_message_receiver_t(collector.Func),
+		            IntPtr.Zero,
+		            ctx, localpool));
+			} finally {
+				if (strptr != IntPtr.Zero)
+					Marshal.FreeHGlobal(strptr);
+				apr_pool_destroy(localpool);
+			}
+			return ret;
+		}
+		
+		public string GetPathUrl(string path) {
+			if (path == null) throw new ArgumentException();
+			
+			IntPtr ret = IntPtr.Zero;
+			CheckError(svn_client_url_from_path(ref ret, path, pool));
+			if (ret == IntPtr.Zero) return null;
+			return Marshal.PtrToStringAnsi(ret);
+		}
+		
+		public string Cat(string pathorurl, Rev revision) {
+			MemoryStream memstream = new MemoryStream();
+			Cat(pathorurl, revision, memstream);
+			try {
+				return System.Text.Encoding.UTF8.GetString(memstream.GetBuffer());
+			} catch (Exception e) {
+			}
+			return System.Text.Encoding.ASCII.GetString(memstream.GetBuffer());
+		}
+
+		public void Cat(string pathorurl, Rev revision, Stream stream) {
+			if (pathorurl == null) throw new ArgumentException();
+			if (stream == null) throw new ArgumentException();
+			
+			IntPtr localpool = newpool(pool);
+			try {
+				StreamCollector collector = new StreamCollector(stream);
+				IntPtr svnstream = svn_stream_create(IntPtr.Zero, localpool);
+				svn_stream_set_write(svnstream, new svn_readwrite_fn_t(collector.Func));
+				CheckError(svn_client_cat(svnstream, pathorurl, ref revision, ctx, localpool));
+			} finally {
+				apr_pool_destroy(localpool);
+			}
+		}
+
+		public void Update(string path, bool recurse, UpdateCallback callback) {
+			if (path == null || callback == null) throw new ArgumentException();
+			
+			lock (sync) {
+				if (inProgress)
+					throw new SubversionException("Another Subversion operation is already in progress.");
+				inProgress = true;
+			}
+			
+			updatecallback = callback;
+			int result_rev;
+			Rev rev = Rev.Head;
+			IntPtr localpool = newpool(pool);
+			try {
+				CheckError(svn_client_update(out result_rev, path, ref rev, recurse ? 1 : 0, ctx, localpool));
+			} finally {
+				apr_pool_destroy(localpool);
+				updatecallback = null;
+				inProgress = false;
+			}
+		}
+
+		public void Commit(string[] paths, string message, UpdateCallback callback) {
+			if (paths == null || message == null || callback == null)
+				throw new ArgumentNullException();
+		
+			lock (sync) {
+				if (inProgress) throw new SubversionException("Another Subversion operation is already in progress.");
+				inProgress = true;
+			}
+
+			updatecallback = callback;
+			
+			IntPtr localpool = newpool(pool);
+			try {
+				// Put each item into an APR array.
+				IntPtr array = apr_array_make(localpool, 0, IntPtr.Size);
+				foreach (string path in paths) {
+					IntPtr item = apr_array_push(array);
+					Marshal.WriteIntPtr(item, apr_pstrdup(localpool, path));
+				}
+
+				IntPtr commit_info = IntPtr.Zero;
+				
+				commitmessage = message;
+		
+				CheckError(svn_client_commit (
+					ref commit_info, array,
+					0, ctx, localpool));
+			} finally {
+				commitmessage = null;
+				updatecallback = null;
+				apr_pool_destroy(localpool);
+				inProgress = false;
+			}
+		}
+		
+		IntPtr svn_client_get_commit_log_impl(ref IntPtr log_msg,
+			ref IntPtr tmp_file, IntPtr commit_items, IntPtr baton,
+			IntPtr pool) {
+			log_msg = apr_pstrdup(pool, commitmessage);
+			tmp_file = IntPtr.Zero;
+			return IntPtr.Zero;
+		}
+
+		private void CheckError(IntPtr error) {
+			if (error == IntPtr.Zero) return;
+			svn_error_t error_t = (svn_error_t)Marshal.PtrToStructure(error, typeof(svn_error_t));				
+			throw new SubversionException(error_t.message);
+		}
+		
+		void svn_wc_notify_func_t_impl(IntPtr baton, IntPtr path,
+			NotifyAction action, NodeKind kind, IntPtr mime_type,
+			NotifyState content_state, NotifyState prop_state, long revision) {
+				string actiondesc = action.ToString();
+				switch (action) {
+					case NotifyAction.UpdateAdd: actiondesc = "Added"; break;
+					case NotifyAction.UpdateDelete: actiondesc = "Deleted"; break;
+					case NotifyAction.UpdateUpdate: actiondesc = "Updating"; break;
+					case NotifyAction.UpdateExternal: actiondesc = "External Updated"; break;
+					case NotifyAction.UpdateCompleted: actiondesc = "Finished"; break;
+					
+					case NotifyAction.CommitAdded: actiondesc = "Added"; break;
+					case NotifyAction.CommitDeleted: actiondesc = "Deleted"; break;
+					case NotifyAction.CommitModified: actiondesc = "Modified"; break;
+					case NotifyAction.CommitReplaced: actiondesc = "Replaced"; break;
+					case NotifyAction.CommitPostfixTxDelta: actiondesc = "Sending Content"; break;
+			/*Add,
+			Copy,
+			Delete,
+			Restore,
+			Revert,
+			FailedRevert,
+			Resolved,
+			Skip,
+			StatusCompleted,
+			StatusExternal,
+			BlameRevision*/
+				}
+			
+				if (updatecallback != null)
+					updatecallback(Marshal.PtrToStringAnsi(path), actiondesc);
+		}
+		
+		private class StatusCollector {
+			ArrayList statuses;
+			public StatusCollector(ArrayList statuses) { this.statuses = statuses; }
+			public void Func(IntPtr baton, IntPtr path, ref svn_wc_status_t status) {
+				if (status.to__svn_wc_entry_t == IntPtr.Zero)
+					return;				
+				statuses.Add(new StatusEnt(status));
+			}
+  
+		}
+
+		private class LogCollector {
+			static readonly DateTime Epoch = new DateTime(1970,1,1);
+
+			ArrayList logs;
+			public LogCollector(ArrayList logs) { this.logs = logs; }
+			public IntPtr Func(IntPtr baton, IntPtr apr_hash_changed_paths, int revision, IntPtr author, IntPtr date, IntPtr message, IntPtr pool) {
+				long time;
+				svn_time_from_cstring(out time, Marshal.PtrToStringAnsi(date), pool);
+				string smessage = "";
+				if (message != IntPtr.Zero) smessage = Marshal.PtrToStringAnsi(message).Trim();
+			
+				ArrayList items = new ArrayList();
+
+				IntPtr item = apr_hash_first(pool, apr_hash_changed_paths);
+				while (item != IntPtr.Zero) {
+					IntPtr nameptr, val;
+					int namelen;
+					apr_hash_this(item, out nameptr, out namelen, out val);
+	             
+					string name = Marshal.PtrToStringAnsi(nameptr);
+					svn_log_changed_path_t ch = (svn_log_changed_path_t)Marshal.PtrToStructure(val, typeof(svn_log_changed_path_t));				
+					item = apr_hash_next(item);
+	             	
+					items.Add(new LogEntChangedPath(name, ch));
+	             }	             
+
+				logs.Add(new LogEnt(revision, Marshal.PtrToStringAnsi(author), Epoch.AddTicks(time*10), smessage, 
+					(LogEntChangedPath[])items.ToArray(typeof(LogEntChangedPath))));
+				
+				return IntPtr.Zero;
+			}
+		}
+		
+		private class StreamCollector {
+			Stream buf;
+			public StreamCollector(Stream buf) { this.buf = buf; }
+			public IntPtr Func(IntPtr baton, IntPtr data, ref int len) {
+				Console.WriteLine(len);
+				for (int i = 0; i < len; i++)
+					buf.WriteByte(Marshal.ReadByte((IntPtr)((int)data+i)));
+				return IntPtr.Zero;
+			}
+		}
+		
+		public class DirEnt {
+			public readonly string Name;
+			public readonly bool IsDirectory;
+			public readonly long Size;
+			public readonly bool HasProps;
+			public readonly int CreatedRevision;
+			public readonly DateTime Time;
+			public readonly string LastAuthor;
+			
+			static readonly DateTime Epoch = new DateTime(1970,1,1);
+			
+			internal DirEnt(string name, svn_dirent_t ent) {
+				Name = name;
+				IsDirectory = (ent.kind == (int)NodeKind.Dir);
+				Size = ent.size;
+				HasProps = ent.has_props != 0;
+				CreatedRevision = ent.created_rev;
+				Time = Epoch.AddTicks(ent.time*10);
+				LastAuthor = ent.last_author;
+			}
+			
+		}
+		
+		public class StatusEnt {
+			public readonly string Name;
+			public readonly int Revision;
+  			public readonly string Url;
+  			public readonly string Repository;
+  			public readonly string RepositoryUuid;
+  			public readonly bool IsDirectory;
+  			public readonly NodeSchedule Schedule;
+  			public readonly bool Copied;
+  			public readonly bool Deleted;
+  			public readonly bool Absent;
+  			public readonly bool Incomplete;
+  			public readonly string CopiedFrom;
+  			public readonly int CopiedFromRevision;
+  			public readonly string ConflictOld;
+  			public readonly string ConflictNew;
+  			public readonly string ConflictWorking;
+  			public readonly string PropertyRejectFile;
+  			public readonly DateTime TextTime;
+  			public readonly DateTime PropTime;
+  			public readonly string Checksum; //(base64 for text-base, or NULL);
+  			public readonly int LastCommitRevision;
+  			public readonly DateTime LastCommitDate;
+  			public readonly string LastCommitAuthor;	
+ 			
+			public readonly NodeStatus TextStatus;
+			public readonly NodeStatus PropsStatus;
+			public readonly bool Locked;
+			public readonly bool Switched;
+			public readonly NodeStatus RemoteTextStatus;
+			public readonly NodeStatus RemotePropsStatus;
+			
+			static readonly DateTime Epoch = new DateTime(1970,1,1);
+
+			internal StatusEnt(svn_wc_status_t status) {
+				TextStatus = (NodeStatus)status.svn_wc_status_kind__text;
+				PropsStatus = (NodeStatus)status.svn_wc_status_kind__props;
+				Locked = status.locked != 0;
+				Copied = status.copied != 0;
+				Switched = status.switched != 0;
+				RemoteTextStatus = (NodeStatus)status.svn_wc_status_kind__text__repo;
+				RemotePropsStatus = (NodeStatus)status.svn_wc_status_kind__props__repo;
+				
+				if (status.to__svn_wc_entry_t == IntPtr.Zero)
+					return;
+					
+				svn_wc_entry_t ent = (svn_wc_entry_t)Marshal.PtrToStructure(status.to__svn_wc_entry_t, typeof(svn_wc_entry_t));
+				Name = ent.name;
+				Revision = ent.revision;
+	  			Url = ent.url;
+	  			Repository = ent.repos;
+	  			RepositoryUuid = ent.repos_uuid;
+	  			IsDirectory = (ent.node_kind == (int)NodeKind.Dir);
+	  			Schedule = (NodeSchedule)ent.schedule;
+	  			Copied = ent.copied != 0;
+	  			Deleted = ent.deleted != 0;
+	  			Absent = ent.absent != 0;
+	  			Incomplete = ent.incomplete != 0;
+	  			CopiedFrom = ent.copied_from;
+	  			CopiedFromRevision = ent.copied_rev;
+	  			ConflictOld = ent.conflict_old;
+	  			ConflictNew = ent.conflict_new;
+	  			ConflictWorking = ent.conflict_working;
+	  			PropertyRejectFile = ent.property_reject_file;
+	  			TextTime = Epoch.AddTicks(ent.text_time*10);
+	  			PropTime = Epoch.AddTicks(ent.prop_time*10);
+	  			Checksum = ent.checksum;
+	  			LastCommitRevision = ent.last_commit_rev;
+	  			LastCommitDate = Epoch.AddTicks(ent.last_commit_date*10);
+	  			LastCommitAuthor = ent.last_commit_author;	
+			}
+
+		}
+		
+		public class LogEnt {
+			public readonly int Revision;
+			public readonly string Author;
+			public readonly DateTime Time;
+			public readonly string Message;
+			public readonly LogEntChangedPath[] ChangedPaths;
+			
+			internal LogEnt(int rev, string author, DateTime time, string msg, LogEntChangedPath[] changes) {
+				Revision = rev;
+				Author = author;
+				Time = time;
+				Message = msg;
+				ChangedPaths = changes;
+			}
+		}
+		
+		public class LogEntChangedPath {
+			public readonly string Path;
+			public readonly ActionType Action;
+			public readonly string CopyFromPath;
+			public readonly int CopyFromRevision;
+			
+			internal LogEntChangedPath(string path, svn_log_changed_path_t info) {
+				Path = path;
+				CopyFromPath = info.copy_from_path;
+				CopyFromRevision = info.copy_from_rev;
+				
+				switch (info.action) {
+				case 'A': Action = ActionType.Add; break;
+				case 'D': Action = ActionType.Delete; break;
+				case 'R': Action = ActionType.Replace; break;
+				default: Action = ActionType.Modify; break;
+				}
+			}
+			
+			public enum ActionType {
+				Add,
+				Delete,
+				Replace,
+				Modify
+			}
+		}
+		
+		// Native Interop
+		
+		private const string aprlib = "libapr-0.so.0";
+
+		[DllImport(aprlib)] static extern void apr_initialize();
+		
+		[DllImport(aprlib)] static extern void apr_pool_create_ex(out IntPtr pool, IntPtr parent, IntPtr abort, IntPtr allocator);
+
+		[DllImport(aprlib)] static extern void apr_pool_destroy(IntPtr pool);
+
+		[DllImport(aprlib)] static extern IntPtr apr_hash_first(IntPtr pool, IntPtr hash);
+
+		[DllImport(aprlib)] static extern IntPtr apr_hash_next(IntPtr hashindex);
+
+		[DllImport(aprlib)] static extern void apr_hash_this(IntPtr hashindex, out IntPtr key, out int keylen, out IntPtr val);
+		
+		[DllImport(aprlib)] static extern IntPtr apr_array_make(IntPtr pool, int nelts, int elt_size);
+		
+		[DllImport(aprlib)] static extern IntPtr apr_array_push(IntPtr arr);
+		
+		[DllImport(aprlib)] static extern IntPtr apr_pstrdup(IntPtr pool, string s);
+
+		private const string svnclientlib = "libsvn_client-1.so.0";
+		
+		private struct svn_error_t {
+			public int apr_err;
+			public string message;
+			public IntPtr svn_error_t__child;
+			public IntPtr pool;
+		}
+		
+		private struct svn_version_t {
+  			public int major;
+  			public int minor;
+  			public int patch;
+			public string tag;	
+  		}
+  		
+  		enum NodeKind {
+  			None,
+  			File,
+  			Dir,
+  			Unknown
+  		}
+
+		internal struct svn_dirent_t {
+			public int kind;
+			public long size;
+			public int has_props;
+			public int created_rev;
+			public long time; // created
+			public string last_author;
+		}
+		
+		public enum NodeSchedule {
+			Normal,
+			Add,
+			Delete,
+			Replace
+		}
+		
+		public enum NodeStatus {
+			EMPTY,
+			None,
+			Unversioned,
+			Normal,
+			Added,
+			Missing,
+			Deleted,
+			Replaced,
+			Modified,
+			Merged,
+			Conflicted,
+			Ignored,
+			Obstructed,
+			External,
+			Incomplete
+		}
+		
+		internal struct svn_wc_entry_t {
+			public string name;
+			public int revision;
+  			public string url;
+  			public string repos;
+  			public string repos_uuid;
+  			public int node_kind;
+  			public int schedule;
+  			public int copied;
+  			public int deleted;
+  			public int absent;
+  			public int incomplete;
+  			public string copied_from;
+  			public int copied_rev;
+  			public string conflict_old;
+  			public string conflict_new;
+  			public string conflict_working;
+  			public string property_reject_file;
+  			public long text_time; // or zero
+  			public long prop_time;
+  			public string checksum; //(base64 for text-base, or NULL);
+  			public int last_commit_rev;
+  			public long last_commit_date;
+  			public string last_commit_author;
+  		}
+  				
+		internal struct svn_wc_status_t {
+			public IntPtr to__svn_wc_entry_t;
+			public int svn_wc_status_kind__text;
+			public int svn_wc_status_kind__props;
+			public int locked;
+			public int copied;
+			public int switched;
+			public int svn_wc_status_kind__text__repo;
+			public int svn_wc_status_kind__props__repo;
+		}
+		
+		public struct Rev {
+			public int kind;
+			public int number;
+			
+			public Rev(int kind, int number) {
+				this.kind = kind;
+				this.number = number;
+			}
+			
+			public static Rev Number(int rev) { return new Rev(1, rev); }
+			
+			public readonly static Rev Blank = new Rev(0, 0);
+			public readonly static Rev First = new Rev(1, 1);
+			public readonly static Rev Committed = new Rev(3, 0);
+			public readonly static Rev Previous = new Rev(4, 0);
+			public readonly static Rev Base = new Rev(5, 0);
+			public readonly static Rev Working = new Rev(6, 0);
+			public readonly static Rev Head = new Rev(7, 0);
+		}
+		
+		internal struct svn_log_changed_path_t {
+			public char action;
+			public string copy_from_path;
+			public int copy_from_rev;
+		}
+		
+		internal enum NotifyAction {
+			Add,
+			Copy,
+			Delete,
+			Restore,
+			Revert,
+			FailedRevert,
+			Resolved,
+			Skip,
+			UpdateDelete,
+			UpdateAdd,
+			UpdateUpdate,
+			UpdateCompleted,
+			UpdateExternal,
+			StatusCompleted,
+			StatusExternal,
+			CommitModified,
+			CommitAdded,
+			CommitDeleted,
+			CommitReplaced,
+			CommitPostfixTxDelta,
+			BlameRevision
+		}
+		
+		internal enum NotifyState {
+			Inapplicable,
+			Unknown,
+			Unchanged,
+			Missing,
+			Obstructed,
+			Changed,
+			Merged,
+			Conflicted
+		}
+		
+		delegate void svn_wc_status_func_t(IntPtr baton, IntPtr path,
+			ref svn_wc_status_t status);
+		
+		delegate IntPtr svn_log_message_receiver_t(IntPtr baton,
+			IntPtr apr_hash_changed_paths, int revision, IntPtr author,
+			IntPtr date, IntPtr message, IntPtr pool);
+		
+		delegate IntPtr svn_readwrite_fn_t(IntPtr baton, IntPtr data, ref int len);
+		
+		delegate void svn_wc_notify_func_t(IntPtr baton, IntPtr path,
+			NotifyAction action, NodeKind kind, IntPtr mime_type,
+			NotifyState content_state, NotifyState prop_state, long revision);
+			
+		delegate IntPtr svn_client_get_commit_log_t(ref IntPtr log_msg,
+			ref IntPtr tmp_file, IntPtr commit_items, IntPtr baton,
+			IntPtr pool);
+
+		[DllImport(svnclientlib)] static extern IntPtr svn_client_version();
+		
+		[DllImport(svnclientlib)] static extern IntPtr svn_client_create_context(out IntPtr ctx, IntPtr pool);
+		
+		[DllImport(svnclientlib)] static extern IntPtr svn_client_ls (
+				out IntPtr dirents, string path_or_url, ref Rev revision,
+               int recurse, IntPtr ctx, IntPtr pool);
+
+		[DllImport(svnclientlib)] static extern IntPtr svn_client_status (
+			ref int result_rev, string path, ref Rev revision,
+			svn_wc_status_func_t status_func, IntPtr status_baton,
+			int descend, int get_all, int update, int no_ignore,
+			IntPtr ctx, IntPtr pool);
+			
+		[DllImport(svnclientlib)] static extern IntPtr svn_client_log (
+			IntPtr apr_array_header_t__targets,
+			ref Rev rev_start, ref Rev rev_end,
+			int discover_changed_paths,
+            int strict_node_history,
+            svn_log_message_receiver_t receiver,
+            IntPtr receiver_baton,
+            IntPtr ctx, IntPtr pool);
+            
+        [DllImport(svnclientlib)] static extern IntPtr svn_time_from_cstring (
+        	out long aprtime, string time, IntPtr pool);
+        	
+		[DllImport(svnclientlib)] static extern IntPtr svn_client_url_from_path (
+			ref IntPtr url, string path_or_url, IntPtr pool);
+
+		[DllImport(svnclientlib)] static extern IntPtr svn_client_cat (
+			IntPtr stream, string path_or_url,
+			ref Rev revision,
+			IntPtr ctx, IntPtr pool);
+		
+		[DllImport(svnclientlib)] static extern IntPtr svn_stream_create (
+			IntPtr baton, IntPtr pool);
+		
+		//[DllImport(svnclientlib)] static extern IntPtr svn_stream_set_read (
+		//	IntPtr stream, svn_readwrite_fn_t reader);
+
+		[DllImport(svnclientlib)] static extern IntPtr svn_stream_set_write (
+			IntPtr stream, svn_readwrite_fn_t writer);
+			
+		[DllImport(svnclientlib)] static extern IntPtr svn_client_update (
+			out int result_rev,
+			string path, ref Rev revision,
+			int recurse, IntPtr ctx, IntPtr pool);
+			
+		[DllImport(svnclientlib)] static extern IntPtr svn_client_commit (
+			ref IntPtr svn_client_commit_info_t__commit_info,
+			IntPtr apr_array_header_t__targets, int nonrecursive,
+			IntPtr ctx, IntPtr pool);
+	}
+
+	public class SubversionException : ApplicationException {
+		public SubversionException(string message) : base(message) { }
+	}
+}

Added: trunk/MonoDevelop/Extras/VersionControl/VersionControl/VersionControl.cs
===================================================================
--- trunk/MonoDevelop/Extras/VersionControl/VersionControl/VersionControl.cs	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/Extras/VersionControl/VersionControl/VersionControl.cs	2005-06-22 19:12:34 UTC (rev 2604)
@@ -0,0 +1,63 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace VersionControl {
+	public abstract class VersionControlSystem {
+		public abstract bool IsDiffAvailable(string sourcefile);
+		public abstract bool IsHistoryAvailable(string sourcefile);
+		public abstract bool IsFileStatusAvailable(string sourcefile);
+		public abstract bool IsDirectoryStatusAvailable(string sourcepath);
+		public abstract bool CanUpdate(string sourcepath);
+		public abstract bool CanCommit(string sourcepath);
+		
+		public abstract string GetPathToBaseText(string sourcefile);
+		public abstract string GetTextAtRevision(object repositoryPath, RevisionPtr revision);
+		public abstract RevisionDescription[] GetHistory(string sourcefile, RevisionPtr since);
+		public abstract Node GetFileStatus(string sourcefile, bool getRemoteStatus);
+		public abstract Node[] GetDirectoryStatus(string sourcepath, bool getRemoteStatus, bool recursive);
+		public abstract void Update(string path, bool recurse, UpdateCallback callback);
+		public abstract void Commit(string[] paths, string message, UpdateCallback callback);
+	}
+	
+	public delegate void UpdateCallback(string path, string action);
+	
+	public class RevisionDescription {
+		public object RepositoryPath;
+		public RevisionPtr Revision;
+		public DateTime Time;
+		public string Author;
+		public string Message;
+	}
+	
+	public abstract class RevisionPtr {
+		public abstract RevisionPtr GetPrevious();
+	}
+	
+	public class Node {
+		public string LocalRelativePath;
+		public object RepositoryPath;
+ 		public bool IsDirectory;
+ 		
+		public NodeStatus Status = NodeStatus.Unknown;
+		public RevisionPtr BaseRevision;
+
+		public NodeStatus RemoteStatus = NodeStatus.Unknown;
+		public RevisionDescription RemoteUpdate;
+	}
+	
+	public enum NodeStatus {
+		Unknown,
+		Unversioned,
+		UnversionedIgnored,
+		Missing,
+		Obstructed,
+		Unchanged,
+		Modified,
+		ScheduledAdd,
+		ScheduledDelete,
+		ScheduledReplace,
+		Conflicted
+	}
+	
+}

Modified: trunk/MonoDevelop/configure.in
===================================================================
--- trunk/MonoDevelop/configure.in	2005-06-18 20:04:30 UTC (rev 2603)
+++ trunk/MonoDevelop/configure.in	2005-06-22 19:12:34 UTC (rev 2604)
@@ -227,6 +227,11 @@
 Extras/MonoDeveloperExtensions/Makefile
 Extras/JavaBinding/Makefile
 Extras/BooBinding/Makefile
+Extras/VersionControl/Makefile
+Extras/VersionControl/Diff/Makefile
+Extras/VersionControl/DiffWidget/Makefile
+Extras/VersionControl/VersionControl/Makefile
+Extras/VersionControl/AddIn/Makefile
 contrib/Makefile
 ])
 




More information about the Monodevelop-patches-list mailing list