SparkleShare/SparkleDiff/SparkleDiff.cs
2010-06-30 01:25:02 +01:00

361 lines
8.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SparkleShare, an instant update workflow to Git.
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using Gtk;
using Mono.Unix;
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
namespace SparkleShare {
public class SparkleDiff
{
public static void Main (string [] args)
{
if (args.Length > 0) {
string file_path = args [0];
if (File.Exists (file_path)) {
Gtk.Application.Init ();
SparkleDiffWindow sparkle_diff_window;
sparkle_diff_window = new SparkleDiffWindow (file_path);
sparkle_diff_window.ShowAll ();
// The main loop
Gtk.Application.Run ();
} else {
Console.WriteLine ("SparkleDiff: " + file_path + ": No such file or directory.");
Environment.Exit (0);
}
}
}
}
public class SparkleDiffWindow : Window
{
// Short alias for the translations
public static string _ (string s)
{
return Catalog.GetString (s);
}
private RevisionView ViewLeft;
private RevisionView ViewRight;
private string [] Revisions;
public SparkleDiffWindow (string file_path) : base ("")
{
string file_name = System.IO.Path.GetFileName (file_path);
SetSizeRequest (1024, 600);
SetPosition (WindowPosition.Center);
BorderWidth = 12;
DeleteEvent += Quit;
IconName = "image-x-generic";
Title = String.Format(_("Comparing Revisions of {0}"), file_name);
Revisions = GetRevisionsForFile (file_path);
VBox layout_vertical = new VBox (false, 12);
HBox layout_horizontal = new HBox (false, 12);
ViewLeft = new RevisionView (Revisions);
ViewRight = new RevisionView (Revisions);
RevisionImage revision_image_left = new RevisionImage (file_path, Revisions [1]);
RevisionImage revision_image_right = new RevisionImage (file_path, Revisions [0]);
ViewLeft.SetImage (revision_image_left);
ViewRight.SetImage (revision_image_right);
ViewLeft.ComboBox.Changed += delegate {
RevisionImage revision_image;
revision_image = new RevisionImage (file_path, Revisions [ViewLeft.ComboBox.Active]);
ViewLeft.SetImage (revision_image);
HookUpViews ();
};
ViewRight.ComboBox.Changed += delegate {
RevisionImage revision_image;
revision_image = new RevisionImage (file_path, Revisions [ViewRight.ComboBox.Active]);
ViewRight.SetImage (revision_image);
HookUpViews ();
};
layout_horizontal.PackStart (ViewLeft);
layout_horizontal.PackStart (ViewRight);
HookUpViews ();
HButtonBox dialog_buttons = new HButtonBox ();
dialog_buttons.Layout = ButtonBoxStyle.End;
dialog_buttons.BorderWidth = 0;
Button CloseButton = new Button (Stock.Close);
CloseButton.Clicked += delegate (object o, EventArgs args) {
Environment.Exit (0);
};
dialog_buttons.Add (CloseButton);
layout_vertical.PackStart (layout_horizontal, true, true, 0);
layout_vertical.PackStart (dialog_buttons, false, false, 0);
Add (layout_vertical);
}
// Hooks up two views so they will be kept in sync
private void HookUpViews () {
ViewLeft.ScrolledWindow.Hadjustment.ValueChanged += SyncViewsHorizontally;
ViewLeft.ScrolledWindow.Vadjustment.ValueChanged += SyncViewsVertically;
ViewRight.ScrolledWindow.Hadjustment.ValueChanged += SyncViewsHorizontally;
ViewRight.ScrolledWindow.Vadjustment.ValueChanged += SyncViewsVertically;
}
// Keeps the two image views in sync horizontally
private void SyncViewsHorizontally (object o, EventArgs args) {
Adjustment source_adjustment = (Adjustment) o;
if (source_adjustment == ViewLeft.ScrolledWindow.Hadjustment)
ViewRight.ScrolledWindow.Hadjustment = source_adjustment;
else
ViewLeft.ScrolledWindow.Hadjustment = source_adjustment;
}
// Keeps the two image views in sync vertically
private void SyncViewsVertically (object o, EventArgs args) {
Adjustment source_adjustment = (Adjustment) o;
if (source_adjustment == ViewLeft.ScrolledWindow.Vadjustment)
ViewRight.ScrolledWindow.Vadjustment = source_adjustment;
else
ViewLeft.ScrolledWindow.Vadjustment = source_adjustment;
}
// Gets a list of all earlier revisions of this file
private string [] GetRevisionsForFile (string file_path)
{
string file_name = System.IO.Path.GetFileName (file_path);
Process process = new Process ();
process.EnableRaisingEvents = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
// TODO: Nice commit summary and "Current Revision"
process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName (file_path);
process.StartInfo.FileName = "git";
process.StartInfo.Arguments = "log --format=\"%H\" " + file_name;
process.Start ();
string output = process.StandardOutput.ReadToEnd ();
return Regex.Split (output.Trim (), "\n");
}
// Quits the program
private void Quit (object o, EventArgs args) {
Environment.Exit (0);
}
}
// An image grabbed from a stream generated by Git
public class RevisionImage : Image
{
public string Revision;
public string FilePath;
public RevisionImage (string file_path, string revision) : base ()
{
Revision = revision;
FilePath = file_path;
Process process = new Process ();
process.EnableRaisingEvents = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName (FilePath);
process.StartInfo.FileName = "git";
process.StartInfo.Arguments = "show " + revision + ":" + System.IO.Path.GetFileName (FilePath);
process.Start ();
Pixbuf = new Gdk.Pixbuf ((System.IO.Stream) process.StandardOutput.BaseStream);
}
}
// A custom widget containing an image view,
// previous/next buttons and a combobox
public class RevisionView : VBox
{
public ScrolledWindow ScrolledWindow;
public ComboBox ComboBox;
public Button ButtonPrevious;
public Button ButtonNext;
private int ValueCount;
private Image Image;
public RevisionView (string [] revisions) : base (false, 6)
{
Image = new Image ();
ScrolledWindow = new ScrolledWindow ();
ScrolledWindow.AddWithViewport (Image);
PackStart (ScrolledWindow, true, true, 0);
HBox controls = new HBox ();
controls.BorderWidth = 0;
Image image_previous = new Image ();
image_previous.IconName = "go-previous";
ButtonPrevious = new Button (image_previous);
ButtonPrevious.Clicked += PreviousInComboBox;
ValueCount = 0;
ComboBox = ComboBox.NewText ();
foreach (string revision in revisions) {
ComboBox.AppendText (revision);
}
ComboBox.Active = 0;
ValueCount = revisions.Length;
Image image_next = new Image ();
image_next.IconName = "go-next";
ButtonNext = new Button (image_next);
ButtonNext.Clicked += NextInComboBox;
controls.Add (ButtonPrevious);
controls.Add (ComboBox);
controls.Add (ButtonNext);
PackStart (controls, false, false, 0);
UpdateControls ();
}
public void NextInComboBox (object o, EventArgs args) {
if (ComboBox.Active > 0)
ComboBox.Active--;
UpdateControls ();
}
public void PreviousInComboBox (object o, EventArgs args) {
if (ComboBox.Active + 1 < ValueCount)
ComboBox.Active++;
UpdateControls ();
}
// Changes the image that is viewed
public void SetImage (Image image) {
Image = image;
Remove (ScrolledWindow);
ScrolledWindow = new ScrolledWindow ();
ScrolledWindow.AddWithViewport (Image);
Add (ScrolledWindow);
ReorderChild (ScrolledWindow, 0);
ShowAll ();
}
// Updates the buttons to be disabled or enabled when needed
public void UpdateControls () {
// TODO: Doesn't work yet. Sleepy -.-
ButtonPrevious.State = StateType.Normal;
ButtonNext.State = StateType.Normal;
if (ComboBox.Active == 0)
ButtonNext.State = StateType.Insensitive;
if (ComboBox.Active + 1 == ValueCount)
ButtonPrevious.State = StateType.Insensitive;
}
}
}