Merge branch 'master' into translations
4
ARTWORK
|
@ -1,4 +0,0 @@
|
|||
The icons and artwork in this project fall under the
|
||||
Creative Commons Attribution ShareAlike 3.0 License,
|
||||
except for the logo icons from hosting projects,
|
||||
these belong to their respective owners.
|
|
@ -1,6 +1,6 @@
|
|||
SUBDIRS = \
|
||||
build \
|
||||
notify-sharp \
|
||||
NotifySharp \
|
||||
SparkleShare \
|
||||
SparkleDiff \
|
||||
data \
|
||||
|
|
2
README
|
@ -1,4 +1,4 @@
|
|||
SparkleShare Version 0.2 Beta 1
|
||||
SparkleShare Version 0.1
|
||||
===============================
|
||||
|
||||
SparkleShare is a file sharing and collaboration tool inspired by Dropbox.
|
||||
|
|
36
SparkleDiff/LeftRevisionView.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// 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;
|
||||
|
||||
namespace SparkleShare {
|
||||
|
||||
public class LeftRevisionView : RevisionView {
|
||||
|
||||
public LeftRevisionView (string [] revisions) : base (revisions) {
|
||||
|
||||
ComboBox.Active = 1;
|
||||
|
||||
if (Direction == Gtk.TextDirection.Ltr)
|
||||
ScrolledWindow.Placement = CornerType.TopRight;
|
||||
else
|
||||
ScrolledWindow.Placement = CornerType.TopLeft;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,11 @@ LINK = $(REF_SPARKLEDIFF)
|
|||
|
||||
SOURCES = \
|
||||
$(top_srcdir)/SparkleShare/Defines.cs \
|
||||
SparkleDiff.cs
|
||||
SparkleDiff.cs \
|
||||
SparkleDiffWindow.cs \
|
||||
RevisionView.cs \
|
||||
RevisionImage.cs \
|
||||
LeftRevisionView.cs \
|
||||
RightRevisionView.cs
|
||||
|
||||
include $(top_srcdir)/build/build.mk
|
||||
|
|
51
SparkleDiff/RevisionImage.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
// 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 System.Diagnostics;
|
||||
|
||||
namespace SparkleShare {
|
||||
|
||||
// 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
151
SparkleDiff/RevisionView.cs
Normal file
|
@ -0,0 +1,151 @@
|
|||
// 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 System;
|
||||
|
||||
namespace SparkleShare {
|
||||
|
||||
// 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);
|
||||
|
||||
HBox controls = new HBox (false, 3);
|
||||
controls.BorderWidth = 0;
|
||||
|
||||
Arrow arrow_left = new Arrow (ArrowType.Left, ShadowType.None);
|
||||
ButtonPrevious = new Button ();
|
||||
ButtonPrevious.Add (arrow_left);
|
||||
ButtonPrevious.Clicked += PreviousInComboBox;
|
||||
ButtonPrevious.ExposeEvent += EqualizeSizes;
|
||||
|
||||
ValueCount = 0;
|
||||
|
||||
ComboBox = ComboBox.NewText ();
|
||||
|
||||
foreach (string revision in revisions) {
|
||||
ComboBox.AppendText (revision);
|
||||
}
|
||||
|
||||
ComboBox.Active = 0;
|
||||
|
||||
ValueCount = revisions.Length;
|
||||
|
||||
Arrow arrow_right = new Arrow (ArrowType.Right, ShadowType.None);
|
||||
ButtonNext = new Button ();
|
||||
ButtonNext.Add (arrow_right);
|
||||
ButtonNext.Clicked += NextInComboBox;
|
||||
ButtonNext.ExposeEvent += EqualizeSizes;
|
||||
|
||||
controls.PackStart (new Label (""), true, false, 0);
|
||||
controls.PackStart (ButtonPrevious, false, false, 0);
|
||||
controls.PackStart (ButtonNext, false, false, 0);
|
||||
controls.PackStart (ComboBox, false, false, 9);
|
||||
controls.PackStart (new Label (""), true, false, 0);
|
||||
|
||||
PackStart (controls, false, false, 0);
|
||||
PackStart (ScrolledWindow, true, true, 0);
|
||||
|
||||
Shown += delegate {
|
||||
UpdateControls ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Equalizes the height and width of a button when exposed
|
||||
private void EqualizeSizes (object o, ExposeEventArgs args) {
|
||||
|
||||
Button button = (Button) o;
|
||||
button.WidthRequest = button.Allocation.Height;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void NextInComboBox (object o, EventArgs args) {
|
||||
|
||||
if (ComboBox.Active - 1 >= 0)
|
||||
ComboBox.Active--;
|
||||
|
||||
// UpdateControls ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void PreviousInComboBox (object o, EventArgs args) {
|
||||
|
||||
if (ComboBox.Active + 1 < ValueCount)
|
||||
ComboBox.Active++;
|
||||
|
||||
// UpdateControls ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Updates the buttons to be disabled or enabled when needed
|
||||
public void UpdateControls () {
|
||||
|
||||
ButtonPrevious.State = StateType.Normal;
|
||||
ButtonNext.State = StateType.Normal;
|
||||
|
||||
// TODO: Disable Next or Previous buttons when at the first or last value of the combobox
|
||||
// I can't get this to work! >:(
|
||||
|
||||
if (ComboBox.Active == ValueCount - 1) {
|
||||
ButtonPrevious.State = StateType.Insensitive;
|
||||
}
|
||||
|
||||
if (ComboBox.Active == 0) {
|
||||
ButtonNext.State = StateType.Insensitive;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Changes the image that is viewed
|
||||
public void SetImage (Image image) {
|
||||
|
||||
Image = image;
|
||||
Remove (ScrolledWindow);
|
||||
ScrolledWindow = new ScrolledWindow ();
|
||||
ScrolledWindow.AddWithViewport (Image);
|
||||
Add (ScrolledWindow);
|
||||
ShowAll ();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
36
SparkleDiff/RightRevisionView.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// 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;
|
||||
|
||||
namespace SparkleShare {
|
||||
|
||||
public class RightRevisionView : RevisionView {
|
||||
|
||||
public RightRevisionView (string [] revisions) : base (revisions) {
|
||||
|
||||
ComboBox.Active = 0;
|
||||
|
||||
if (Direction == Gtk.TextDirection.Ltr)
|
||||
ScrolledWindow.Placement = CornerType.TopLeft;
|
||||
else
|
||||
ScrolledWindow.Placement = CornerType.TopRight;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -19,18 +19,49 @@ using Mono.Unix;
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SparkleShare {
|
||||
|
||||
public class SparkleDiff
|
||||
{
|
||||
|
||||
// Short alias for the translations
|
||||
public static string _ (string s)
|
||||
{
|
||||
return Catalog.GetString (s);
|
||||
}
|
||||
|
||||
public static void Main (string [] args)
|
||||
{
|
||||
|
||||
Catalog.Init (Defines.GETTEXT_PACKAGE, Defines.LOCALE_DIR);
|
||||
|
||||
// Check whether git is installed
|
||||
Process Process = new Process ();
|
||||
Process.StartInfo.FileName = "git";
|
||||
Process.StartInfo.RedirectStandardOutput = true;
|
||||
Process.StartInfo.UseShellExecute = false;
|
||||
Process.Start ();
|
||||
|
||||
if (Process.StandardOutput.ReadToEnd ().IndexOf ("version") == -1) {
|
||||
Console.WriteLine (_("Git wasn't found."));
|
||||
Console.WriteLine (_("You can get Git from http://git-scm.com/."));
|
||||
Environment.Exit (0);
|
||||
}
|
||||
|
||||
// Don't allow running as root
|
||||
UnixUserInfo UnixUserInfo = new UnixUserInfo (UnixEnvironment.UserName);
|
||||
if (UnixUserInfo.UserId == 0) {
|
||||
Console.WriteLine (_("Sorry, you can't run SparkleShare with these permissions."));
|
||||
Console.WriteLine (_("Things would go utterly wrong."));
|
||||
Environment.Exit (0);
|
||||
}
|
||||
|
||||
if (args.Length > 0) {
|
||||
if (args [0].Equals ("--help") || args [0].Equals ("-h")) {
|
||||
ShowHelp ();
|
||||
Environment.Exit (0);
|
||||
}
|
||||
|
||||
string file_path = args [0];
|
||||
|
||||
|
@ -56,361 +87,23 @@ namespace SparkleShare {
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class SparkleDiffWindow : Window
|
||||
{
|
||||
|
||||
// Short alias for the translations
|
||||
public static string _ (string s)
|
||||
// Prints the help output
|
||||
public static void ShowHelp ()
|
||||
{
|
||||
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 (800, 540);
|
||||
SetPosition (WindowPosition.Center);
|
||||
|
||||
BorderWidth = 12;
|
||||
|
||||
DeleteEvent += Quit;
|
||||
|
||||
IconName = "image-x-generic";
|
||||
// TRANSLATORS: The parameter is a filename
|
||||
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);
|
||||
|
||||
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=\"%ct\t%an\" " + file_name;
|
||||
process.Start ();
|
||||
|
||||
string output = process.StandardOutput.ReadToEnd ();
|
||||
|
||||
string [] revisions_info = Regex.Split (output.Trim (), "\n");
|
||||
|
||||
int i = 0;
|
||||
foreach (string revision_info in revisions_info) {
|
||||
|
||||
string [] parts = Regex.Split (revision_info.Trim (), "\t");
|
||||
|
||||
int timestamp = int.Parse (parts [0]);
|
||||
string author = parts [1];
|
||||
|
||||
if (i == 0)
|
||||
revisions_info [i] = _("Current Revision") + "\t" + author;
|
||||
else
|
||||
// TRANSLATORS: This is a format specifier according to System.Globalization.DateTimeFormatInfo
|
||||
revisions_info [i] = UnixTimestampToDateTime (timestamp).ToString (_("d MMM\tH:mm")) +
|
||||
"\t" + author;
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
ViewLeft = new RevisionView (revisions_info);
|
||||
ViewRight = new RevisionView (revisions_info);
|
||||
|
||||
ViewLeft.ComboBox.Active = 1;
|
||||
ViewRight.ComboBox.Active = 0;
|
||||
|
||||
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 ();
|
||||
|
||||
ViewLeft.ScrolledWindow.Hadjustment = ViewRight.ScrolledWindow.Hadjustment;
|
||||
ViewLeft.ScrolledWindow.Vadjustment = ViewRight.ScrolledWindow.Vadjustment;
|
||||
|
||||
ViewLeft.UpdateControls ();
|
||||
|
||||
};
|
||||
|
||||
ViewRight.ComboBox.Changed += delegate {
|
||||
|
||||
RevisionImage revision_image;
|
||||
revision_image = new RevisionImage (file_path, Revisions [ViewRight.ComboBox.Active]);
|
||||
ViewRight.SetImage (revision_image);
|
||||
|
||||
HookUpViews ();
|
||||
|
||||
ViewRight.ScrolledWindow.Hadjustment = ViewLeft.ScrolledWindow.Hadjustment;
|
||||
ViewRight.ScrolledWindow.Vadjustment = ViewLeft.ScrolledWindow.Vadjustment;
|
||||
|
||||
ViewRight.UpdateControls ();
|
||||
|
||||
};
|
||||
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Converts a UNIX timestamp to a more usable time object
|
||||
public DateTime UnixTimestampToDateTime (int timestamp)
|
||||
{
|
||||
DateTime unix_epoch = new DateTime (1970, 1, 1, 0, 0, 0, 0);
|
||||
return unix_epoch.AddSeconds (timestamp);
|
||||
}
|
||||
|
||||
|
||||
// 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 (false, 6);
|
||||
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.PackStart (ButtonPrevious, false, false, 0);
|
||||
controls.PackStart (ComboBox, false, false, 0);
|
||||
// controls.PackStart (ButtonNext, false, false, 0);
|
||||
|
||||
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;
|
||||
*/
|
||||
|
||||
Console.WriteLine (_("SparkleDiff Copyright (C) 2010 Hylke Bons"));
|
||||
Console.WriteLine (" ");
|
||||
Console.WriteLine (_("This program comes with ABSOLUTELY NO WARRANTY."));
|
||||
Console.WriteLine (_("This is free software, and you are welcome to redistribute it "));
|
||||
Console.WriteLine (_("under certain conditions. Please read the GNU GPLv3 for details."));
|
||||
Console.WriteLine (" ");
|
||||
Console.WriteLine (_("SparkleDiff let's you compare revisions of an image file side by side."));
|
||||
Console.WriteLine (" ");
|
||||
Console.WriteLine (_("Usage: sparklediff [FILE]"));
|
||||
Console.WriteLine (_("Open an image file to show its revisions"));
|
||||
Console.WriteLine (" ");
|
||||
Console.WriteLine (_("Arguments:"));
|
||||
Console.WriteLine (_("\t -h, --help\t\tDisplay this help text."));
|
||||
Console.WriteLine (" ");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
241
SparkleDiff/SparkleDiffWindow.cs
Normal file
|
@ -0,0 +1,241 @@
|
|||
// 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.Text.RegularExpressions;
|
||||
|
||||
namespace SparkleShare {
|
||||
|
||||
// The main window of SparkleDiff
|
||||
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 (800, 540);
|
||||
SetPosition (WindowPosition.Center);
|
||||
|
||||
BorderWidth = 12;
|
||||
|
||||
DeleteEvent += Quit;
|
||||
|
||||
IconName = "image-x-generic";
|
||||
|
||||
// TRANSLATORS: The parameter is a filename
|
||||
Title = String.Format(_("Comparing Revisions of ‘{0}’"), file_name);
|
||||
|
||||
Revisions = GetRevisionsForFilePath (file_path);
|
||||
|
||||
VBox layout_vertical = new VBox (false, 12);
|
||||
|
||||
HBox layout_horizontal = new HBox (false, 12);
|
||||
|
||||
Process process = new Process ();
|
||||
process.EnableRaisingEvents = true;
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
|
||||
process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName (file_path);
|
||||
process.StartInfo.FileName = "git";
|
||||
process.StartInfo.Arguments = "log --format=\"%ct\t%an\" " + file_name;
|
||||
process.Start ();
|
||||
|
||||
string output = process.StandardOutput.ReadToEnd ();
|
||||
string [] revisions_info = Regex.Split (output.Trim (), "\n");
|
||||
|
||||
int i = 0;
|
||||
foreach (string revision_info in revisions_info) {
|
||||
|
||||
string [] parts = Regex.Split (revision_info.Trim (), "\t");
|
||||
|
||||
int timestamp = int.Parse (parts [0]);
|
||||
string author = parts [1];
|
||||
|
||||
if (i == 0)
|
||||
revisions_info [i] = _("Current Revision") + "\t" + author;
|
||||
else
|
||||
|
||||
// TRANSLATORS: This is a format specifier according to System.Globalization.DateTimeFormatInfo
|
||||
revisions_info [i] = UnixTimestampToDateTime (timestamp).ToString (_("d MMM\tH:mm")) +
|
||||
"\t" + author;
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
ViewLeft = new LeftRevisionView (revisions_info);
|
||||
ViewRight = new RightRevisionView (revisions_info);
|
||||
|
||||
ViewLeft.SetImage (new RevisionImage (file_path, Revisions [1]));
|
||||
ViewRight.SetImage (new RevisionImage (file_path, Revisions [0]));
|
||||
|
||||
ViewLeft.ComboBox.Changed += delegate {
|
||||
|
||||
RevisionImage revision_image;
|
||||
revision_image = new RevisionImage (file_path, Revisions [ViewLeft.ComboBox.Active]);
|
||||
ViewLeft.SetImage (revision_image);
|
||||
|
||||
HookUpViews ();
|
||||
|
||||
ViewLeft.ScrolledWindow.Hadjustment = ViewRight.ScrolledWindow.Hadjustment;
|
||||
ViewLeft.ScrolledWindow.Vadjustment = ViewRight.ScrolledWindow.Vadjustment;
|
||||
|
||||
ViewLeft.UpdateControls ();
|
||||
|
||||
};
|
||||
|
||||
ViewRight.ComboBox.Changed += delegate {
|
||||
|
||||
RevisionImage revision_image;
|
||||
revision_image = new RevisionImage (file_path, Revisions [ViewRight.ComboBox.Active]);
|
||||
ViewRight.SetImage (revision_image);
|
||||
|
||||
HookUpViews ();
|
||||
|
||||
ViewRight.ScrolledWindow.Hadjustment = ViewLeft.ScrolledWindow.Hadjustment;
|
||||
ViewRight.ScrolledWindow.Vadjustment = ViewLeft.ScrolledWindow.Vadjustment;
|
||||
|
||||
ViewRight.UpdateControls ();
|
||||
|
||||
};
|
||||
|
||||
|
||||
layout_horizontal.PackStart (ViewLeft);
|
||||
layout_horizontal.PackStart (ViewRight);
|
||||
|
||||
// Order time view according to the user's reading direction
|
||||
if (Direction == Gtk.TextDirection.Rtl) // See Deejay1? I can do i18n too! :P
|
||||
layout_horizontal.ReorderChild (ViewLeft, 1);
|
||||
|
||||
|
||||
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 their scrollbars 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 [] GetRevisionsForFilePath (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 ();
|
||||
string [] revisions = Regex.Split (output.Trim (), "\n");
|
||||
|
||||
return revisions;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Converts a UNIX timestamp to a more usable time object
|
||||
public DateTime UnixTimestampToDateTime (int timestamp)
|
||||
{
|
||||
DateTime unix_epoch = new DateTime (1970, 1, 1, 0, 0, 0, 0);
|
||||
return unix_epoch.AddSeconds (timestamp);
|
||||
}
|
||||
|
||||
|
||||
// Quits the program
|
||||
private void Quit (object o, EventArgs args) {
|
||||
|
||||
Environment.Exit (0);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
13
SparkleDiff/sparklediff.in
Normal file
|
@ -0,0 +1,13 @@
|
|||
#!/bin/bash
|
||||
|
||||
case $1 in
|
||||
|
||||
--help | help)
|
||||
mono "@expanded_libdir@/@PACKAGE@/SparkleDiff.exe" --help
|
||||
;;
|
||||
|
||||
*)
|
||||
mono "@expanded_libdir@/@PACKAGE@/SparkleDiff.exe" $1
|
||||
;;
|
||||
|
||||
esac
|
|
@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 9.00
|
|||
# Visual Studio 2005
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SparkleShare", "SparkleShare\SparkleShare.csproj", "{728483AA-E34B-4441-BF2C-C8BC2901E4E0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "notify-sharp", "notify-sharp\notify-sharp.csproj", "{005CCA8E-DFBF-464A-B6DA-452C62D4589C}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotifySharp", "NotifySharp\NotifySharp.csproj", "{005CCA8E-DFBF-464A-B6DA-452C62D4589C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace SparkleShare {
|
|||
// Use translations
|
||||
Catalog.Init (Defines.GETTEXT_PACKAGE, Defines.LOCALE_DIR);
|
||||
|
||||
// Check if git is installed
|
||||
// Check whether git is installed
|
||||
Process Process = new Process ();
|
||||
Process.StartInfo.FileName = "git";
|
||||
Process.StartInfo.RedirectStandardOutput = true;
|
||||
|
@ -56,7 +56,7 @@ namespace SparkleShare {
|
|||
UnixUserInfo UnixUserInfo = new UnixUserInfo (UnixEnvironment.UserName);
|
||||
if (UnixUserInfo.UserId == 0) {
|
||||
Console.WriteLine (_("Sorry, you can't run SparkleShare with these permissions."));
|
||||
Console.WriteLine (_("Things will go utterly wrong."));
|
||||
Console.WriteLine (_("Things would go utterly wrong."));
|
||||
Environment.Exit (0);
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,7 @@ namespace SparkleShare {
|
|||
HideUI = true;
|
||||
if (Argument.Equals ("--help") || Argument.Equals ("-h")) {
|
||||
ShowHelp ();
|
||||
Environment.Exit (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +100,6 @@ namespace SparkleShare {
|
|||
Console.WriteLine (_("\t -d, --disable-gui\tDon't show the notification icon."));
|
||||
Console.WriteLine (_("\t -h, --help\t\tDisplay this help text."));
|
||||
Console.WriteLine (" ");
|
||||
Environment.Exit (0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ using Gtk;
|
|||
using Mono.Unix;
|
||||
using SparkleShare;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
@ -36,48 +37,48 @@ namespace SparkleShare {
|
|||
|
||||
private SparkleRepo SparkleRepo;
|
||||
private VBox LayoutVertical;
|
||||
private ScrolledWindow LogScrolledWindow;
|
||||
private string SelectedEmail;
|
||||
private ScrolledWindow ScrolledWindow;
|
||||
|
||||
public SparkleWindow (SparkleRepo Repo) : base ("")
|
||||
public SparkleWindow (SparkleRepo sparkle_repo) : base ("")
|
||||
{
|
||||
|
||||
SparkleRepo = Repo;
|
||||
SelectedEmail = "";
|
||||
SparkleRepo = sparkle_repo;
|
||||
SetSizeRequest (640, 480);
|
||||
SetPosition (WindowPosition.Center);
|
||||
BorderWidth = 12;
|
||||
|
||||
// TRANSLATORS: {0} is a folder name, and {1} is a server address
|
||||
Title = String.Format(_("‘{0}’ on {1}"), SparkleRepo.Name,
|
||||
SparkleRepo.RemoteOriginUrl.TrimEnd (("/" + SparkleRepo.Name + ".git").ToCharArray ()));
|
||||
SparkleRepo.RemoteOriginUrl);
|
||||
IconName = "folder";
|
||||
|
||||
LayoutVertical = new VBox (false, 12);
|
||||
|
||||
LayoutVertical.PackStart (CreateEventLog (), true, true, 0);
|
||||
|
||||
HButtonBox DialogButtons = new HButtonBox ();
|
||||
DialogButtons.Layout = ButtonBoxStyle.Edge;
|
||||
DialogButtons.BorderWidth = 0;
|
||||
HButtonBox dialog_buttons = new HButtonBox ();
|
||||
dialog_buttons.Layout = ButtonBoxStyle.Edge;
|
||||
dialog_buttons.BorderWidth = 0;
|
||||
|
||||
Button OpenFolderButton = new Button (_("Open Folder"));
|
||||
OpenFolderButton.Clicked += delegate (object o, EventArgs args) {
|
||||
Process Process = new Process ();
|
||||
Process.StartInfo.FileName = "xdg-open";
|
||||
Process.StartInfo.Arguments =
|
||||
SparkleHelpers.CombineMore (SparklePaths.SparklePath, SparkleRepo.Name);
|
||||
Process.Start ();
|
||||
Button open_folder_button = new Button (_("Open Folder"));
|
||||
open_folder_button.Clicked += delegate (object o, EventArgs args) {
|
||||
Process process = new Process ();
|
||||
process.StartInfo.FileName = "xdg-open";
|
||||
process.StartInfo.Arguments = SparkleHelpers.CombineMore (SparklePaths.SparklePath,
|
||||
SparkleRepo.Name);
|
||||
process.Start ();
|
||||
Destroy ();
|
||||
};
|
||||
|
||||
Button CloseButton = new Button (Stock.Close);
|
||||
CloseButton.Clicked += delegate (object o, EventArgs args) {
|
||||
Button close_button = new Button (Stock.Close);
|
||||
close_button.Clicked += delegate (object o, EventArgs args) {
|
||||
Destroy ();
|
||||
};
|
||||
|
||||
DialogButtons.Add (OpenFolderButton);
|
||||
DialogButtons.Add (CloseButton);
|
||||
dialog_buttons.Add (open_folder_button);
|
||||
dialog_buttons.Add (close_button);
|
||||
|
||||
LayoutVertical.PackStart (DialogButtons, false, false, 0);
|
||||
LayoutVertical.PackStart (dialog_buttons, false, false, 0);
|
||||
|
||||
Add (LayoutVertical);
|
||||
|
||||
|
@ -87,9 +88,10 @@ namespace SparkleShare {
|
|||
public void UpdateEventLog ()
|
||||
{
|
||||
|
||||
LayoutVertical.Remove (LogScrolledWindow);
|
||||
LogScrolledWindow = CreateEventLog ();
|
||||
LayoutVertical.PackStart (LogScrolledWindow, true, true, 0);
|
||||
LayoutVertical.Remove (ScrolledWindow);
|
||||
ScrolledWindow = CreateEventLog ();
|
||||
LayoutVertical.PackStart (ScrolledWindow, true, true, 0);
|
||||
LayoutVertical.ReorderChild (ScrolledWindow, 0);
|
||||
ShowAll ();
|
||||
|
||||
}
|
||||
|
@ -98,138 +100,165 @@ namespace SparkleShare {
|
|||
private ScrolledWindow CreateEventLog ()
|
||||
{
|
||||
|
||||
ListStore LogStore = new ListStore (typeof (Gdk.Pixbuf),
|
||||
typeof (string),
|
||||
typeof (string),
|
||||
typeof (string));
|
||||
Process process = new Process ();
|
||||
process.EnableRaisingEvents = true;
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.WorkingDirectory = SparkleRepo.LocalPath;
|
||||
process.StartInfo.FileName = "git";
|
||||
process.StartInfo.Arguments = "log --format=\"%at☃%an☃%ae☃%s\" -25";
|
||||
|
||||
Process Process = new Process ();
|
||||
Process.EnableRaisingEvents = true;
|
||||
Process.StartInfo.RedirectStandardOutput = true;
|
||||
Process.StartInfo.UseShellExecute = false;
|
||||
Process.StartInfo.FileName = "git";
|
||||
string output = "";
|
||||
|
||||
string Output = "";
|
||||
process.Start ();
|
||||
|
||||
Process.StartInfo.WorkingDirectory = SparkleRepo.LocalPath;
|
||||
// We're using the snowman here to separate messages :)
|
||||
Process.StartInfo.Arguments = "log --format=\"%at☃%s☃%an☃%cr☃%ae\" -25";
|
||||
Process.Start ();
|
||||
output += "\n" + process.StandardOutput.ReadToEnd ().Trim ();
|
||||
|
||||
Output += "\n" + Process.StandardOutput.ReadToEnd ().Trim ();
|
||||
|
||||
Output = Output.TrimStart ("\n".ToCharArray ());
|
||||
string [] Lines = Regex.Split (Output, "\n");
|
||||
output = output.TrimStart ("\n".ToCharArray ());
|
||||
string [] lines = Regex.Split (output, "\n");
|
||||
|
||||
// Sort by time and get the last 25
|
||||
Array.Sort (Lines);
|
||||
Array.Reverse (Lines);
|
||||
Array.Sort (lines);
|
||||
Array.Reverse (lines);
|
||||
|
||||
TreeIter Iter;
|
||||
for (int i = 0; i < 25 && i < Lines.Length; i++) {
|
||||
List <ActivityDay> activity_days = new List <ActivityDay> ();
|
||||
|
||||
string Line = Lines [i];
|
||||
if (Line.Contains (SelectedEmail)) {
|
||||
for (int i = 0; i < 25 && i < lines.Length; i++) {
|
||||
|
||||
string line = lines [i];
|
||||
|
||||
// Look for the snowman!
|
||||
string [] Parts = Regex.Split (Line, "☃");
|
||||
string [] parts = Regex.Split (line, "☃");
|
||||
|
||||
string Message = Parts [1];
|
||||
string UserName = Parts [2];
|
||||
string TimeAgo = Parts [3];
|
||||
string UserEmail = Parts [4];
|
||||
int unix_timestamp = int.Parse (parts [0]);
|
||||
string user_name = parts [1];
|
||||
string user_email = parts [2];
|
||||
string message = parts [3];
|
||||
|
||||
Message = Message.Replace ("/", " → ");
|
||||
Message = Message.Replace ("\n", " ");
|
||||
DateTime date_time = UnixTimestampToDateTime (unix_timestamp);
|
||||
|
||||
Iter = LogStore.Append ();
|
||||
message = message.Replace ("/", " → ");
|
||||
message = message.Replace ("\n", " ");
|
||||
|
||||
LogStore.SetValue (Iter, 0, SparkleHelpers.GetAvatar (UserEmail, 24));
|
||||
LogStore.SetValue (Iter, 1, "<b>" + UserName + "</b>\n" + Message);
|
||||
// TODO Blend text color with treeview color instead of hardcoding it
|
||||
LogStore.SetValue (Iter, 2, "<span fgcolor='grey'>" + TimeAgo + "</span> ");
|
||||
ChangeSet change_set = new ChangeSet (user_name, user_email, message, date_time);
|
||||
|
||||
// We're not showing email, it's only
|
||||
// there for lookup purposes
|
||||
LogStore.SetValue (Iter, 3, UserEmail);
|
||||
bool change_set_inserted = false;
|
||||
foreach (ActivityDay stored_activity_day in activity_days) {
|
||||
|
||||
}
|
||||
if (stored_activity_day.DateTime.Year == change_set.DateTime.Year &&
|
||||
stored_activity_day.DateTime.Month == change_set.DateTime.Month &&
|
||||
stored_activity_day.DateTime.Day == change_set.DateTime.Day) {
|
||||
|
||||
stored_activity_day.Add (change_set);
|
||||
change_set_inserted = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!change_set_inserted) {
|
||||
|
||||
ActivityDay activity_day = new ActivityDay (change_set.DateTime);
|
||||
activity_day.Add (change_set);
|
||||
activity_days.Add (activity_day);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TreeView LogView = new TreeView (LogStore);
|
||||
LogView.HeadersVisible = false;
|
||||
|
||||
LogView.AppendColumn ("", new CellRendererPixbuf (), "pixbuf", 0);
|
||||
VBox layout_vertical = new VBox (false, 0);
|
||||
|
||||
CellRendererText MessageCellRenderer = new CellRendererText ();
|
||||
TreeViewColumn MessageColumn = new TreeViewColumn ();
|
||||
MessageColumn.PackStart (MessageCellRenderer, true);
|
||||
MessageColumn.SetCellDataFunc (MessageCellRenderer, new Gtk.TreeCellDataFunc (RenderMessageRow));
|
||||
LogView.AppendColumn (MessageColumn);
|
||||
foreach (ActivityDay activity_day in activity_days) {
|
||||
|
||||
CellRendererText TimeAgoCellRenderer = new CellRendererText ();
|
||||
TreeViewColumn TimeAgoColumn = new TreeViewColumn ();
|
||||
TimeAgoColumn.PackStart (TimeAgoCellRenderer, true);
|
||||
TimeAgoColumn.SetCellDataFunc (TimeAgoCellRenderer, new Gtk.TreeCellDataFunc (RenderTimeAgoRow));
|
||||
TimeAgoCellRenderer.Xalign = 1;
|
||||
LogView.AppendColumn (TimeAgoColumn);
|
||||
TreeIter iter = new TreeIter ();
|
||||
ListStore list_store = new ListStore (typeof (Gdk.Pixbuf),
|
||||
typeof (string),
|
||||
typeof (string));
|
||||
|
||||
TreeViewColumn [] Columns = LogView.Columns;
|
||||
foreach (ChangeSet change_set in activity_day) {
|
||||
|
||||
Columns [0].MinWidth = 42;
|
||||
iter = list_store.Append ();
|
||||
list_store.SetValue (iter, 0, SparkleHelpers.GetAvatar (change_set.UserEmail , 32));
|
||||
list_store.SetValue (iter, 1, "<b>" + change_set.UserName + "</b>\n" +
|
||||
"<span fgcolor='#777'>" + change_set.Message + "</span>");
|
||||
list_store.SetValue (iter, 2, change_set.UserEmail);
|
||||
|
||||
Columns [1].Expand = true;
|
||||
Columns [1].MinWidth = 350;
|
||||
|
||||
Columns [2].Expand = true;
|
||||
Columns [2].MinWidth = 50;
|
||||
|
||||
// Get the email address of the selected log message each
|
||||
// time the cursor changes
|
||||
LogView.CursorChanged += delegate (object o, EventArgs args) {
|
||||
TreeModel model;
|
||||
TreeIter iter;
|
||||
if (LogView.Selection.GetSelected (out model, out iter)) {
|
||||
SelectedEmail = (string) model.GetValue (iter, 3);
|
||||
}
|
||||
};
|
||||
|
||||
// Compose an e-mail when a row is activated
|
||||
LogView.RowActivated +=
|
||||
delegate (object o, RowActivatedArgs Args) {
|
||||
switch (SparklePlatform.Name) {
|
||||
case "GNOME":
|
||||
Process.StartInfo.FileName = "xdg-open";
|
||||
break;
|
||||
case "OSX":
|
||||
Process.StartInfo.FileName = "open";
|
||||
break;
|
||||
}
|
||||
Process.StartInfo.Arguments = "mailto:" + SelectedEmail;
|
||||
Process.Start ();
|
||||
};
|
||||
Label date_label = new Label ("<b>" + activity_day.DateTime.ToString ("ddd MMM d, yyyy") + "</b>");
|
||||
|
||||
LogScrolledWindow = new ScrolledWindow ();
|
||||
LogScrolledWindow.AddWithViewport (LogView);
|
||||
date_label.UseMarkup = true;
|
||||
date_label.Xalign = 0;
|
||||
date_label.Xpad = 9;
|
||||
date_label.Ypad = 9;
|
||||
|
||||
return LogScrolledWindow;
|
||||
layout_vertical.PackStart (date_label, true, true, 0);
|
||||
|
||||
IconView icon_view = new IconView (list_store);
|
||||
|
||||
icon_view.PixbufColumn = 0;
|
||||
icon_view.MarkupColumn = 1;
|
||||
|
||||
icon_view.Orientation = Orientation.Horizontal;
|
||||
icon_view.ItemWidth = 550;
|
||||
icon_view.Spacing = 9;
|
||||
|
||||
layout_vertical.PackStart (icon_view);
|
||||
|
||||
|
||||
}
|
||||
|
||||
ScrolledWindow = new ScrolledWindow ();
|
||||
ScrolledWindow.AddWithViewport (layout_vertical);
|
||||
|
||||
return ScrolledWindow;
|
||||
|
||||
}
|
||||
|
||||
// Renders a row with custom markup
|
||||
private void RenderMessageRow (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
|
||||
|
||||
// Converts a UNIX timestamp to a more usable time object
|
||||
public DateTime UnixTimestampToDateTime (int timestamp)
|
||||
{
|
||||
string item = (string) model.GetValue (iter, 1);
|
||||
(cell as CellRendererText).Markup = item;
|
||||
DateTime unix_epoch = new DateTime (1970, 1, 1, 0, 0, 0, 0);
|
||||
return unix_epoch.AddSeconds (timestamp);
|
||||
}
|
||||
|
||||
private void RenderTimeAgoRow (TreeViewColumn column, CellRenderer cell, TreeModel model, TreeIter iter)
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class ActivityDay : List <ChangeSet>
|
||||
{
|
||||
|
||||
public DateTime DateTime;
|
||||
|
||||
public ActivityDay (DateTime date_time)
|
||||
{
|
||||
string item = (string) model.GetValue (iter, 2);
|
||||
(cell as CellRendererText).Markup = item;
|
||||
DateTime = date_time;
|
||||
DateTime = new DateTime (DateTime.Year, DateTime.Month, DateTime.Day);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class ChangeSet
|
||||
{
|
||||
|
||||
public string UserName;
|
||||
public string UserEmail;
|
||||
public string Message;
|
||||
public DateTime DateTime;
|
||||
|
||||
public ChangeSet (string user_name, string user_email, string message, DateTime date_time)
|
||||
{
|
||||
UserName = user_name;
|
||||
UserEmail = user_email;
|
||||
Message = message;
|
||||
DateTime = date_time;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -88,9 +88,8 @@ build/m4/shave/shave
|
|||
build/m4/shave/shave-libtool
|
||||
data/Makefile
|
||||
data/icons/Makefile
|
||||
notify-sharp/Makefile
|
||||
NotifySharp/Makefile
|
||||
SparkleDiff/Makefile
|
||||
SparkleDiff/Defines.cs
|
||||
SparkleShare/sparkleshare
|
||||
SparkleShare/Defines.cs
|
||||
SparkleShare/AssemblyInfo.cs
|
||||
|
|
|
@ -5,13 +5,6 @@ hicolordir = $(DESTDIR)$(datadir)/icons/hicolor
|
|||
theme_icons = \
|
||||
animations,process-syncing-sparkleshare-24.png \
|
||||
animations,process-working-48.png \
|
||||
emblems,emblem-synced-22.png \
|
||||
emblems,emblem-synced-24.png \
|
||||
emblems,emblem-sync-error-22.png \
|
||||
emblems,emblem-sync-error-24.png \
|
||||
emblems,emblem-syncing-22.png \
|
||||
emblems,emblem-syncing-24.png \
|
||||
places,fedorahosted-16.png \
|
||||
places,folder-16.png \
|
||||
places,folder-22.png \
|
||||
places,folder-24.png \
|
||||
|
@ -24,9 +17,6 @@ theme_icons = \
|
|||
places,folder-sparkleshare-256.png \
|
||||
places,folder-sparkleshare-32.png \
|
||||
places,folder-sparkleshare-48.png \
|
||||
places,github-16.png \
|
||||
places,gitorious-16.png \
|
||||
places,gnome-16.png \
|
||||
status,avatar-default-16.png \
|
||||
status,avatar-default-22.png \
|
||||
status,avatar-default-24.png \
|
||||
|
@ -35,13 +25,7 @@ theme_icons = \
|
|||
status,document-added-16.png \
|
||||
status,document-edited-16.png \
|
||||
status,document-moved-16.png \
|
||||
status,document-removed-16.png \
|
||||
status,folder-synced-22.png \
|
||||
status,folder-synced-24.png \
|
||||
status,folder-sync-error-22.png \
|
||||
status,folder-sync-error-24.png \
|
||||
status,folder-syncing-22.png \
|
||||
status,folder-syncing-24.png
|
||||
status,document-removed-16.png
|
||||
|
||||
install_icon_exec = $(top_srcdir)/build/icon-theme-installer \
|
||||
-t "$(theme)" \
|
||||
|
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 747 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 440 B |
Before Width: | Height: | Size: 387 B |
Before Width: | Height: | Size: 650 B |
|
@ -1,179 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# icon-theme-installer
|
||||
# Copyright (C) 2006 Novell, Inc.
|
||||
# Written by Aaron Bockover <abock@gnome.org>
|
||||
# Licensed under the MIT/X11 license
|
||||
#
|
||||
# This script is meant to be invoked from within a Makefile/Makefile.am
|
||||
# in the install-data-local and uninstall-data sections. It handles the
|
||||
# task of properly installing icons into the icon theme. It requires a
|
||||
# few arguments to set up its environment, and a list of files to be
|
||||
# installed. The format of the file list is critical:
|
||||
#
|
||||
# <category>,<local-src-file-name>
|
||||
#
|
||||
# apps,music-player-banshee.svg
|
||||
# apps,music-player-banshee-16.png
|
||||
# apps,music-player-banshee-22.png
|
||||
#
|
||||
# <category> is the icon theme category, for instance, apps, devices,
|
||||
# actions, emblems...
|
||||
#
|
||||
# <local-src-file-name> must have a basename in the form of:
|
||||
#
|
||||
# proper-theme-name[-<SIZE>].<EXTENSION>
|
||||
#
|
||||
# Where <SIZE> should be either nothing, which will default to scalable
|
||||
# or \-[0-9]{2}, which will expand to <SIZE>x<SIZE>. For example:
|
||||
#
|
||||
# music-player-banshee-16.png
|
||||
#
|
||||
# The <SIZE> here is -16 and will expand to 16x16 per the icon theme spec
|
||||
#
|
||||
# What follows is an example Makefile.am for icon theme installation:
|
||||
#
|
||||
# ---------------
|
||||
# theme=hicolor
|
||||
# themedir=$(datadir)/icons/$(theme)
|
||||
# theme_icons = \
|
||||
# apps,music-player-banshee.svg \
|
||||
# apps,music-player-banshee-16.png \
|
||||
# apps,music-player-banshee-22.png \
|
||||
# apps,music-player-banshee-24.png \
|
||||
# apps,music-player-banshee-32.png
|
||||
#
|
||||
# install_icon_exec = $(top_srcdir)/build/icon-theme-installer -t $(theme) -s $(srcdir) -d "x$(DESTDIR)" -b $(themedir) -m "$(mkinstalldirs)" -x "$(INSTALL_DATA)"
|
||||
# install-data-local:
|
||||
# $(install_icon_exec) -i $(theme_icons)
|
||||
#
|
||||
# uninstall-hook:
|
||||
# $(install_icon_exec) -u $(theme_icons)
|
||||
#
|
||||
# MAINTAINERCLEANFILES = Makefile.in
|
||||
# EXTRA_DIST = $(wildcard *.svg *.png)
|
||||
# ---------------
|
||||
#
|
||||
# Arguments to this program:
|
||||
#
|
||||
# -i : Install
|
||||
# -u : Uninstall
|
||||
# -t <theme> : Theme name (hicolor)
|
||||
# -b <dir> : Theme installation dest directory [x$(DESTDIR)] - Always prefix
|
||||
# this argument with x; it will be stripped but will act as a
|
||||
# placeholder for zero $DESTDIRs (only set by packagers)
|
||||
# -d <dir> : Theme installation directory [$(hicolordir)]
|
||||
# -s <dir> : Source directory [$(srcdir)]
|
||||
# -m <exec> : Command to exec for directory creation [$(mkinstalldirs)]
|
||||
# -x <exec> : Command to exec for single file installation [$(INSTALL_DATA)]
|
||||
# <remainging> : All remainging should be category,filename pairs
|
||||
|
||||
while getopts "iut:b:d:s:m:x:" flag; do
|
||||
case "$flag" in
|
||||
i) INSTALL=yes ;;
|
||||
u) UNINSTALL=yes ;;
|
||||
t) THEME_NAME=$OPTARG ;;
|
||||
d) INSTALL_DEST_DIR=${OPTARG##x} ;;
|
||||
b) INSTALL_BASE_DIR=$OPTARG ;;
|
||||
s) SRC_DIR=$OPTARG ;;
|
||||
m) MKINSTALLDIRS_EXEC=$OPTARG ;;
|
||||
x) INSTALL_DATA_EXEC=$OPTARG ;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
if test "x$INSTALL" = "xyes" -a "x$UNINSTALL" = "xyes"; then
|
||||
echo "Cannot pass both -i and -u"
|
||||
exit 1
|
||||
elif test "x$INSTALL" = "x" -a "x$UNINSTALL" = "x"; then
|
||||
echo "Must path either -i or -u"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$THEME_NAME"; then
|
||||
echo "Theme name required (-t hicolor)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$INSTALL_BASE_DIR"; then
|
||||
echo "Base theme directory required [-d \$(hicolordir)]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test ! -x $(echo "$MKINSTALLDIRS_EXEC" | cut -f1 -d' '); then
|
||||
echo "Cannot find '$MKINSTALLDIRS_EXEC'; You probably want to pass -m \$(mkinstalldirs)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test ! -x $(echo "$INSTALL_DATA_EXEC" | cut -f1 -d' '); then
|
||||
echo "Cannot find '$INSTALL_DATA_EXEC'; You probably want to pass -x \$(INSTALL_DATA)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$SRC_DIR"; then
|
||||
SRC_DIR=.
|
||||
fi
|
||||
|
||||
for icon in $@; do
|
||||
size=$(echo $icon | sed s/[^0-9]*//g)
|
||||
category=$(echo $icon | cut -d, -f1)
|
||||
build_name=$(echo $icon | cut -d, -f2)
|
||||
install_name=$(echo $build_name | sed "s/[0-9]//g; s/-\././")
|
||||
install_name=$(basename $install_name)
|
||||
|
||||
if test -z $size; then
|
||||
size=scalable;
|
||||
else
|
||||
size=${size}x${size};
|
||||
fi
|
||||
|
||||
install_dir=${INSTALL_DEST_DIR}${INSTALL_BASE_DIR}/$size/$category
|
||||
install_path=$install_dir/$install_name
|
||||
|
||||
if test "x$INSTALL" = "xyes"; then
|
||||
echo "Installing $size $install_name into $THEME_NAME icon theme"
|
||||
|
||||
$($MKINSTALLDIRS_EXEC $install_dir) || {
|
||||
echo "Failed to create directory $install_dir"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$($INSTALL_DATA_EXEC $SRC_DIR/$build_name $install_path) || {
|
||||
echo "Failed to install $SRC_DIR/$build_name into $install_path"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if test ! -e $install_path; then
|
||||
echo "Failed to install $SRC_DIR/$build_name into $install_path"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if test -e $install_path; then
|
||||
echo "Removing $size $install_name from $THEME_NAME icon theme"
|
||||
|
||||
rm $install_path || {
|
||||
echo "Failed to remove $install_path"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if test "x$INSTALL" = "xyes"; then
|
||||
gtk_update_icon_cache_bin="$((which gtk-update-icon-cache || echo /opt/gnome/bin/gtk-update-icon-cache)2>/dev/null)"
|
||||
gtk_update_icon_cache="$gtk_update_icon_cache_bin -f -t $INSTALL_BASE_DIR"
|
||||
|
||||
if test -z "$INSTALL_DEST_DIR"; then
|
||||
if test -x $gtk_update_icon_cache_bin; then
|
||||
echo "Updating GTK icon cache"
|
||||
$gtk_update_icon_cache
|
||||
else
|
||||
echo "*** Icon cache not updated. Could not execute $gtk_update_icon_cache_bin"
|
||||
fi
|
||||
else
|
||||
echo "*** Icon cache not updated. After install, run this:"
|
||||
echo "*** $gtk_update_icon_cache"
|
||||
fi
|
||||
fi
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
||||
xmlns:gnome="http://api.gnome.org/doap-extensions#"
|
||||
xmlns="http://usefulinc.com/ns/doap#">
|
||||
|
||||
<name xml:lang="en">SparkleShare</name>
|
||||
<shortdesc xml:lang="en">An instant update collaboration workflow for Git</shortdesc>
|
||||
<homepage rdf:resource="http://www.sparkleshare.org/" />
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Hylke Bons</foaf:name>
|
||||
<gnome:userid>hbons</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
</Project>
|