Merge branch 'master' of ssh://github.com/hbons/SparkleShare

This commit is contained in:
Hylke Bons 2012-10-19 23:42:43 +01:00
commit a679e29d4c
18 changed files with 456 additions and 91 deletions

View file

@ -1,4 +1,15 @@
0.9.3 for Linux, Mac and Windows (??? 2012) 0.9.4 for Linux, Mac and Windows (Fri Oct 19 2012)
Hylke:
- Remove Nautilus extension
- Restore previous revisions of files from the event log
- Fix Mac file system watcher not always triggering
- Add symbolic icon for GNOME 3 (by Lapo)
- New Bitbucket and default user icon
- For encrypted projects, use a different salt for each project
0.9.3 for Linux, Mac and Windows (Mon Oct 1 2012)
Hylke: Hylke:
- Fix endless loop when adding empty folders - Fix endless loop when adding empty folders

View file

@ -19,7 +19,7 @@ using System;
using System.Reflection; using System.Reflection;
[assembly:AssemblyTitle ("SparkleLib")] [assembly:AssemblyTitle ("SparkleLib")]
[assembly:AssemblyVersion ("0.9.3")] [assembly:AssemblyVersion ("0.9.4")]
[assembly:AssemblyCopyright ("Copyright (c) 2010 Hylke Bons and others")] [assembly:AssemblyCopyright ("Copyright (c) 2010 Hylke Bons and others")]
[assembly:AssemblyTrademark ("SparkleShare is a trademark of SparkleShare Ltd.")] [assembly:AssemblyTrademark ("SparkleShare is a trademark of SparkleShare Ltd.")]

View file

@ -40,6 +40,8 @@ namespace SparkleLib.Git {
// Check if the repo's salt is stored in a branch... // Check if the repo's salt is stored in a branch...
SparkleGit git = new SparkleGit (TargetFolder, "branch -a"); SparkleGit git = new SparkleGit (TargetFolder, "branch -a");
git.StartAndWaitForExit ();
string [] branches = git.StartAndReadStandardOutput ().Split (Environment.NewLine.ToCharArray ()); string [] branches = git.StartAndReadStandardOutput ().Split (Environment.NewLine.ToCharArray ());
// TODO double check env.newline ^ // TODO double check env.newline ^

View file

@ -20,6 +20,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading;
using SparkleLib; using SparkleLib;
@ -29,6 +30,7 @@ namespace SparkleLib.Git {
private bool user_is_set; private bool user_is_set;
private bool use_git_bin; private bool use_git_bin;
private bool is_encrypted;
public SparkleRepo (string path, SparkleConfig config) : base (path, config) public SparkleRepo (string path, SparkleConfig config) : base (path, config)
@ -50,6 +52,11 @@ namespace SparkleLib.Git {
git = new SparkleGit (LocalPath, "rebase --abort"); git = new SparkleGit (LocalPath, "rebase --abort");
git.StartAndWaitForExit (); git.StartAndWaitForExit ();
} }
string password_file_path = Path.Combine (LocalPath, ".git", "password");
if (File.Exists (password_file_path))
this.is_encrypted = true;
} }
@ -238,9 +245,8 @@ namespace SparkleLib.Git {
if (line.Contains ("|")) { if (line.Contains ("|")) {
speed = line.Substring (line.IndexOf ("|") + 1).Trim (); speed = line.Substring (line.IndexOf ("|") + 1).Trim ();
speed = speed.Replace (", done.", "").Trim (); speed = speed.Replace (", done.", "").Trim ();
speed = speed.Replace ("i", ""); speed = speed.Replace ("KiB/s", "ᴋʙ/s");
speed = speed.Replace ("KB/s", "ᴋʙ/s"); speed = speed.Replace ("MiB/s", "ᴍʙ/s");
speed = speed.Replace ("MB/s", "ᴍʙ/s");
} }
} }
@ -468,6 +474,7 @@ namespace SparkleLib.Git {
foreach (string line in lines) { foreach (string line in lines) {
string conflicting_path = line.Substring (3); string conflicting_path = line.Substring (3);
conflicting_path = EnsureSpecialCharacters (conflicting_path); conflicting_path = EnsureSpecialCharacters (conflicting_path);
conflicting_path = conflicting_path.Replace ("\"", "\\\"");
SparkleLogger.LogInfo ("Git", Name + " | Conflict type: " + line); SparkleLogger.LogInfo ("Git", Name + " | Conflict type: " + line);
@ -539,7 +546,7 @@ namespace SparkleLib.Git {
} }
public override void RevertFile (string path, string revision) public override void RestoreFile (string path, string revision, string target_file_path)
{ {
if (path == null) if (path == null)
throw new ArgumentNullException ("path"); throw new ArgumentNullException ("path");
@ -547,27 +554,58 @@ namespace SparkleLib.Git {
if (revision == null) if (revision == null)
throw new ArgumentNullException ("revision"); throw new ArgumentNullException ("revision");
path = path.Replace ("\\", "/"); SparkleLogger.LogInfo ("Git", Name + " | Restoring \"" + path + "\" (revision " + revision + ")");
SparkleGit git = new SparkleGit (LocalPath, "checkout " + revision + " \"" + path + "\""); // FIXME: git-show doesn't decrypt objects, so we can't use it to retrieve
git.StartAndWaitForExit (); // files from the index. This is a suboptimal workaround but it does the job
if (this.is_encrypted) {
// Restore the older file...
SparkleGit git = new SparkleGit (LocalPath, "checkout " + revision + " \"" + path + "\"");
git.StartAndWaitForExit ();
if (git.ExitCode == 0) string local_file_path = Path.Combine (LocalPath, path);
SparkleLogger.LogInfo ("Git", Name + " | Checked out \"" + path + "\" (" + revision + ")");
else // ...move it...
SparkleLogger.LogInfo ("Git", Name + " | Failed to check out \"" + path + "\" (" + revision + ")"); try {
File.Move (local_file_path, target_file_path);
} catch {
SparkleLogger.LogInfo ("Git",
Name + " | Could not move \"" + local_file_path + "\" to \"" + target_file_path + "\"");
}
// ...and restore the most recent revision
git = new SparkleGit (LocalPath, "checkout " + CurrentRevision + " \"" + path + "\"");
git.StartAndWaitForExit ();
// The correct way
} else {
path = path.Replace ("\"", "\\\"");
SparkleGit git = new SparkleGit (LocalPath, "show " + revision + ":\"" + path + "\"");
git.Start ();
FileStream stream = File.OpenWrite (target_file_path);
git.StandardOutput.BaseStream.CopyTo (stream);
stream.Close ();
git.WaitForExit ();
}
if (target_file_path.StartsWith (LocalPath))
new Thread (() => OnFileActivity (null)).Start ();
} }
public override List<SparkleChangeSet> GetChangeSets (string path, int count) public override List<SparkleChangeSet> GetChangeSets (string path)
{ {
return GetChangeSetsInternal (path, count); return GetChangeSetsInternal (path);
} }
public override List<SparkleChangeSet> GetChangeSets (int count) public override List<SparkleChangeSet> GetChangeSets ()
{ {
return GetChangeSetsInternal (null, count); return GetChangeSetsInternal (null);
} }
@ -590,31 +628,27 @@ namespace SparkleLib.Git {
if (Error != ErrorStatus.None) { if (Error != ErrorStatus.None) {
SparkleLogger.LogInfo ("Git", Name + " | Error status changed to " + Error); SparkleLogger.LogInfo ("Git", Name + " | Error status changed to " + Error);
return true; return true;
} else { } else {
return false; return false;
} }
} }
private List<SparkleChangeSet> GetChangeSetsInternal (string path, int count) private List<SparkleChangeSet> GetChangeSetsInternal (string path)
{ {
if (count < 1)
throw new ArgumentOutOfRangeException ("count");
count = 150;
List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> (); List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> ();
SparkleGit git; SparkleGit git;
if (path == null) { if (path == null) {
git = new SparkleGit (LocalPath, "log -" + count + " --raw --find-renames --date=iso " + git = new SparkleGit (LocalPath, "log --since=1.month --raw --find-renames --date=iso " +
"--format=medium --no-color --no-merges"); "--format=medium --no-color --no-merges");
} else { } else {
path = path.Replace ("\\", "/"); path = path.Replace ("\\", "/");
git = new SparkleGit (LocalPath, "log -" + count + " --raw --find-renames --date=iso " + git = new SparkleGit (LocalPath, "log --raw --find-renames --date=iso " +
"--format=medium --no-color --no-merges -- " + path); "--format=medium --no-color --no-merges -- \"" + path + "\"");
} }
string output = git.StartAndReadStandardOutput (); string output = git.StartAndReadStandardOutput ();
@ -681,13 +715,16 @@ namespace SparkleLib.Git {
if (entry_line.StartsWith (":")) { if (entry_line.StartsWith (":")) {
string type_letter = entry_line [37].ToString (); string type_letter = entry_line [37].ToString ();
string file_path = entry_line.Substring (39); string file_path = entry_line.Substring (39);
bool change_is_folder = false;
if (file_path.EndsWith (".empty"))
file_path = file_path.Substring (0, file_path.Length - ".empty".Length);
if (file_path.Equals (".sparkleshare")) if (file_path.Equals (".sparkleshare"))
continue; continue;
if (file_path.EndsWith (".empty")) {
file_path = file_path.Substring (0, file_path.Length - ".empty".Length);
change_is_folder = true;
}
file_path = EnsureSpecialCharacters (file_path); file_path = EnsureSpecialCharacters (file_path);
file_path = file_path.Replace ("\\\"", "\""); file_path = file_path.Replace ("\\\"", "\"");
@ -702,15 +739,20 @@ namespace SparkleLib.Git {
file_path = file_path.Replace ("\\\"", "\""); file_path = file_path.Replace ("\\\"", "\"");
to_file_path = to_file_path.Replace ("\\\"", "\""); to_file_path = to_file_path.Replace ("\\\"", "\"");
if (file_path.EndsWith (".empty")) if (file_path.EndsWith (".empty")) {
file_path = file_path.Substring (0, file_path.Length - 6); file_path = file_path.Substring (0, file_path.Length - 6);
change_is_folder = true;
}
if (to_file_path.EndsWith (".empty")) if (to_file_path.EndsWith (".empty")) {
to_file_path = to_file_path.Substring (0, to_file_path.Length - 6); to_file_path = to_file_path.Substring (0, to_file_path.Length - 6);
change_is_folder = true;
}
change_set.Changes.Add ( change_set.Changes.Add (
new SparkleChange () { new SparkleChange () {
Path = file_path, Path = file_path,
IsFolder = change_is_folder,
MovedToPath = to_file_path, MovedToPath = to_file_path,
Timestamp = change_set.Timestamp, Timestamp = change_set.Timestamp,
Type = SparkleChangeType.Moved Type = SparkleChangeType.Moved
@ -730,6 +772,7 @@ namespace SparkleLib.Git {
change_set.Changes.Add ( change_set.Changes.Add (
new SparkleChange () { new SparkleChange () {
Path = file_path, Path = file_path,
IsFolder = change_is_folder,
Timestamp = change_set.Timestamp, Timestamp = change_set.Timestamp,
Type = change_type Type = change_type
} }

View file

@ -184,6 +184,7 @@ namespace SparkleLib {
if (File.Exists (identifier_path)) { if (File.Exists (identifier_path)) {
Identifier = File.ReadAllText (identifier_path).Trim (); Identifier = File.ReadAllText (identifier_path).Trim ();
File.SetAttributes (identifier_path, FileAttributes.Hidden);
} else { } else {
Identifier = CreateIdentifier (); Identifier = CreateIdentifier ();
@ -220,9 +221,8 @@ namespace SparkleLib {
"Any files you add or change in this folder will be automatically synced to " + n + "Any files you add or change in this folder will be automatically synced to " + n +
uri_builder.ToString () + " and everyone connected to it." + n + uri_builder.ToString () + " and everyone connected to it." + n +
n + n +
"SparkleShare is an Open Source software program that helps people " + n + "SparkleShare is an Open Source software program that helps people collaborate and " + n +
"collaborate and share files. If you like what we do, please consider a small " + n + "share files. If you like what we do, consider buying us a beer: http://www.sparkleshare.org/" + n +
"donation to support the project: http://www.sparkleshare.org/" + n +
n + n +
"Have fun! :)" + n; "Have fun! :)" + n;
} }

View file

@ -54,9 +54,9 @@ namespace SparkleLib {
public abstract bool HasRemoteChanges { get; } public abstract bool HasRemoteChanges { get; }
public abstract bool SyncUp (); public abstract bool SyncUp ();
public abstract bool SyncDown (); public abstract bool SyncDown ();
public abstract List<SparkleChangeSet> GetChangeSets (int count); public abstract List<SparkleChangeSet> GetChangeSets ();
public abstract List<SparkleChangeSet> GetChangeSets (string path, int count); public abstract List<SparkleChangeSet> GetChangeSets (string path);
public abstract void RevertFile (string path, string revision); public abstract void RestoreFile (string path, string revision, string target_file_path);
public event SyncStatusChangedEventHandler SyncStatusChanged = delegate { }; public event SyncStatusChangedEventHandler SyncStatusChanged = delegate { };
public delegate void SyncStatusChangedEventHandler (SyncStatus new_status); public delegate void SyncStatusChangedEventHandler (SyncStatus new_status);
@ -195,19 +195,17 @@ namespace SparkleLib {
// Sync up everything that changed // Sync up everything that changed
// since we've been offline // since we've been offline
if (!this.is_syncing && (HasLocalChanges || HasUnsyncedChanges)) { new Thread (() => {
SyncUpBase (); if (!this.is_syncing && (HasLocalChanges || HasUnsyncedChanges)) {
while (HasLocalChanges)
SyncUpBase (); SyncUpBase ();
}
this.remote_timer.Start (); while (HasLocalChanges && !this.is_syncing)
} SyncUpBase ();
}
this.remote_timer.Start ();
public List<SparkleChangeSet> GetChangeSets () {
return GetChangeSets (30); }).Start ();
} }
@ -218,9 +216,11 @@ namespace SparkleLib {
if (IsBuffering) if (IsBuffering)
return; return;
foreach (string exclude_path in ExcludePaths) { if (args != null) {
if (args.FullPath.Contains (exclude_path)) foreach (string exclude_path in ExcludePaths) {
return; if (args.FullPath.Contains (exclude_path))
return;
}
} }
lock (this.buffer_lock) { lock (this.buffer_lock) {

View file

@ -47,6 +47,7 @@ namespace SparkleLib {
public SparkleChangeType Type; public SparkleChangeType Type;
public DateTime Timestamp; public DateTime Timestamp;
public bool IsFolder = false;
public string Path; public string Path;
public string MovedToPath; public string MovedToPath;

View file

@ -10,8 +10,9 @@
$('dl dd:nth-child(-n+10)').css('display', 'block'); $('dl dd:nth-child(-n+10)').css('display', 'block');
$('.day-entry-content .event-entry:last-child').css('border', 'none'); $('.day-entry-content .event-entry:last-child').css('border', 'none');
$('dd a.windows').click(function () { $('a').click(function (event) {
window.external.LinkClicked($(this).attr("href")); window.external.LinkClicked($(this).attr("href"));
event.preventDefault();
}); });
// Update the Today and Yesterday labels after midnight // Update the Today and Yesterday labels after midnight
@ -26,7 +27,7 @@
} }
}, 60 * 1000); }, 60 * 1000);
// Hide the 'Show all' link when there are less than 10 events // Hide the 'Show all' link when there are fewer than 10 events
$('.show').each (function () { $('.show').each (function () {
var entry_count = $(this).parent ().find ('dl').children ().length; var entry_count = $(this).parent ().find ('dl').children ().length;
@ -67,7 +68,6 @@
a.show { a.show {
font-size: 80%; font-size: 80%;
margin-bottom: 9px;
} }
a:hover { a:hover {
@ -76,7 +76,7 @@
cursor: pointer; cursor: pointer;
} }
small { small, small a, small a:hover {
font-size: <!-- $small-font-size -->; font-size: <!-- $small-font-size -->;
color: <!-- $secondary-font-color -->; color: <!-- $secondary-font-color -->;
} }
@ -91,13 +91,12 @@
display: none; display: none;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap;
width: 90%; width: 90%;
padding: 0 0 1px 20px; padding: 0 0 1px 20px;
margin: 0 0 4px 0; margin: 0 0 4px 0;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center left; background-position: center left;
text-overflow: ellipsis;
white-space: nowrap;
} }
.day-entry-header { .day-entry-header {
@ -107,13 +106,24 @@
font-weight: bold; font-weight: bold;
} }
.history-header {
color: #aaa;
padding-top: 22px;
float: left;
width: 90%;
margin-left: 32px;
margin-right: 32px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.event-entry { .event-entry {
padding: 24px 14px 14px 64px; padding: 24px 14px 14px 64px;
margin: 0 32px 0 32px; margin: 0 32px 0 32px;
border-bottom: 1px #ddd solid; border-bottom: 1px #ddd solid;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: 36px 24px; background-position: 36px 24px;
min-height: 100px;
} }
.event-user-name { .event-user-name {
@ -152,7 +162,54 @@
.moved { .moved {
background-image: url('<!-- $document-moved-background-image -->'); background-image: url('<!-- $document-moved-background-image -->');
} }
table {
width: 100%;
}
.table-wrapper {
padding: 64px 32px;
}
td {
padding: 0;
margin: 0;
}
td.name {
font-weight: bold;
width: 45%;
}
td.time {
color: <!-- $secondary-font-color -->;
padding-right: 9px;
font-size: <!-- $small-font-size -->;
}
td.date {
color: <!-- $secondary-font-color -->;
text-align: right;
padding-right: 6px;
font-size: <!-- $small-font-size -->;
}
td.restore {
text-align: right;
font-size: <!-- $small-font-size -->;
}
td.avatar {
width: 32px;
}
td.avatar img {
margin-top: 2px;
border-radius: 3px;
width: 24px;
height: 24px;
}
</style> </style>
</head> </head>
<body oncontextmenu="return false;"> <body oncontextmenu="return false;">

View file

@ -131,12 +131,35 @@ namespace SparkleShare {
Present (); Present ();
}); });
}; };
Controller.ShowSaveDialogEvent += delegate (string file_name, string target_folder_path) {
Application.Invoke (delegate {
FileChooserDialog dialog = new FileChooserDialog ("Restore from History",
this, FileChooserAction.Save, "Cancel", ResponseType.Cancel, "Save", ResponseType.Ok);
dialog.CurrentName = file_name;
dialog.SetCurrentFolder (target_folder_path);
if (dialog.Run () == (int) ResponseType.Ok)
Controller.SaveDialogCompleted (dialog.Filename);
else
Controller.SaveDialogCancelled ();
dialog.Destroy ();
});
};
Controller.UpdateChooserEvent += delegate (string [] folders) { Controller.UpdateChooserEvent += delegate (string [] folders) {
Application.Invoke (delegate { Application.Invoke (delegate {
UpdateChooser (folders); UpdateChooser (folders);
}); });
}; };
Controller.UpdateChooserEnablementEvent += delegate (bool enabled) {
Application.Invoke (delegate {
this.combo_box.Sensitive = enabled;
});
};
Controller.UpdateContentEvent += delegate (string html) { Controller.UpdateContentEvent += delegate (string html) {
Application.Invoke (delegate { Application.Invoke (delegate {
@ -168,7 +191,7 @@ namespace SparkleShare {
private void WebViewNavigationRequested (object o, WebKit.NavigationRequestedArgs args) { private void WebViewNavigationRequested (object o, WebKit.NavigationRequestedArgs args) {
Controller.LinkClicked (args.Request.Uri.Substring (7)); Controller.LinkClicked (args.Request.Uri);
// Don't follow HREFs (as this would cause a page refresh) // Don't follow HREFs (as this would cause a page refresh)
if (!args.Request.Uri.Equals ("file:")) if (!args.Request.Uri.Equals ("file:"))
@ -260,7 +283,8 @@ namespace SparkleShare {
html = html.Replace ("<!-- $day-entry-header-background-color -->", SparkleUIHelpers.GdkColorToHex (Style.Background (StateType.Normal))); html = html.Replace ("<!-- $day-entry-header-background-color -->", SparkleUIHelpers.GdkColorToHex (Style.Background (StateType.Normal)));
html = html.Replace ("<!-- $secondary-font-color -->", SparkleUIHelpers.GdkColorToHex (Style.Foreground (StateType.Insensitive))); html = html.Replace ("<!-- $secondary-font-color -->", SparkleUIHelpers.GdkColorToHex (Style.Foreground (StateType.Insensitive)));
html = html.Replace ("<!-- $small-color -->", SparkleUIHelpers.GdkColorToHex (Style.Foreground (StateType.Insensitive))); html = html.Replace ("<!-- $small-color -->", SparkleUIHelpers.GdkColorToHex (Style.Foreground (StateType.Insensitive)));
html = html.Replace ("<!-- $small-font-size -->", "85%");
html = html.Replace ("<!-- $pixmaps-path -->", pixmaps_path); html = html.Replace ("<!-- $pixmaps-path -->", pixmaps_path);
html = html.Replace ("<!-- $document-added-background-image -->", html = html.Replace ("<!-- $document-added-background-image -->",

View file

@ -83,5 +83,5 @@ SparkleShare/Windows/SparkleShare.wxs
### Uninstalling ### Uninstalling
Simple remove the SparkleShare bundle. Simply remove the SparkleShare bundle.

View file

@ -80,6 +80,7 @@ namespace SparkleShare {
new SizeF (ContentView.Frame.Width, ContentView.Frame.Height - 39)) new SizeF (ContentView.Frame.Width, ContentView.Frame.Height - 39))
}; };
this.hidden_close_button = new NSButton () { this.hidden_close_button = new NSButton () {
KeyEquivalentModifierMask = NSEventModifierMask.CommandKeyMask, KeyEquivalentModifierMask = NSEventModifierMask.CommandKeyMask,
KeyEquivalent = "w" KeyEquivalent = "w"
@ -197,7 +198,7 @@ namespace SparkleShare {
}); });
} }
}; };
Controller.UpdateChooserEvent += delegate (string [] folders) { Controller.UpdateChooserEvent += delegate (string [] folders) {
using (var a = new NSAutoreleasePool ()) using (var a = new NSAutoreleasePool ())
{ {
@ -207,6 +208,15 @@ namespace SparkleShare {
} }
}; };
Controller.UpdateChooserEnablementEvent += delegate (bool enabled) {
using (var a = new NSAutoreleasePool ())
{
InvokeOnMainThread (delegate {
this.popup_button.Enabled = enabled;
});
}
};
Controller.UpdateContentEvent += delegate (string html) { Controller.UpdateContentEvent += delegate (string html) {
using (var a = new NSAutoreleasePool ()) using (var a = new NSAutoreleasePool ())
{ {
@ -228,7 +238,7 @@ namespace SparkleShare {
}); });
} }
}; };
Controller.UpdateSizeInfoEvent += delegate (string size, string history_size) { Controller.UpdateSizeInfoEvent += delegate (string size, string history_size) {
using (var a = new NSAutoreleasePool ()) using (var a = new NSAutoreleasePool ())
{ {
@ -238,6 +248,30 @@ namespace SparkleShare {
}); });
} }
}; };
Controller.ShowSaveDialogEvent += delegate (string file_name, string target_folder_path) {
using (var a = new NSAutoreleasePool ())
{
InvokeOnMainThread (() => {
// TODO: Make this a sheet
NSSavePanel panel = new NSSavePanel () {
DirectoryUrl = new NSUrl (target_folder_path, true),
NameFieldStringValue = file_name,
ParentWindow = this,
Title = "Restore from History",
PreventsApplicationTerminationWhenModal = false
};
if ((NSPanelButtonType) panel.RunModal ()== NSPanelButtonType.Ok) {
string target_file_path = Path.Combine (panel.DirectoryUrl.RelativePath, panel.NameFieldStringValue);
Controller.SaveDialogCompleted (target_file_path);
} else {
Controller.SaveDialogCancelled ();
}
});
}
};
} }
@ -346,6 +380,7 @@ namespace SparkleShare {
html = html.Replace ("<!-- $body-font-size -->", "13.4px"); html = html.Replace ("<!-- $body-font-size -->", "13.4px");
html = html.Replace ("<!-- $secondary-font-color -->", "#bbb"); html = html.Replace ("<!-- $secondary-font-color -->", "#bbb");
html = html.Replace ("<!-- $small-color -->", "#ddd"); html = html.Replace ("<!-- $small-color -->", "#ddd");
html = html.Replace ("<!-- $small-font-size -->", "10px");
html = html.Replace ("<!-- $day-entry-header-background-color -->", "#f5f5f5"); html = html.Replace ("<!-- $day-entry-header-background-color -->", "#f5f5f5");
html = html.Replace ("<!-- $a-color -->", "#0085cf"); html = html.Replace ("<!-- $a-color -->", "#0085cf");
html = html.Replace ("<!-- $a-hover-color -->", "#009ff8"); html = html.Replace ("<!-- $a-hover-color -->", "#009ff8");
@ -372,7 +407,7 @@ namespace SparkleShare {
this.web_view.MainFrame.LoadHtmlString (html, new NSUrl ("")); this.web_view.MainFrame.LoadHtmlString (html, new NSUrl (""));
web_view.PolicyDelegate = new SparkleWebPolicyDelegate (); this.web_view.PolicyDelegate = new SparkleWebPolicyDelegate ();
ContentView.AddSubview (this.web_view); ContentView.AddSubview (this.web_view);
(this.web_view.PolicyDelegate as SparkleWebPolicyDelegate).LinkClicked += (this.web_view.PolicyDelegate as SparkleWebPolicyDelegate).LinkClicked +=

View file

@ -76,13 +76,13 @@ namespace SparkleShare {
this.syncing_idle_image = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-idle.png")); this.syncing_idle_image = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-idle.png"));
this.syncing_up_image = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-up.png")); this.syncing_up_image = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-up.png"));
this.syncing_down_image = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-down.png")); this.syncing_down_image = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-down.png"));
this.syncing_image = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing.png")); this.syncing_image = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing.png"));
this.syncing_error_image = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-error.png")); this.syncing_error_image = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-error.png"));
this.syncing_idle_image_active = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-idle-active.png")); this.syncing_idle_image_active = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-idle-active.png"));
this.syncing_up_image_active = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-up-active.png")); this.syncing_up_image_active = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-up-active.png"));
this.syncing_down_image_active = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-down-active.png")); this.syncing_down_image_active = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-down-active.png"));
this.syncing_image_active = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-active.png")); this.syncing_image_active = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-active.png"));
this.syncing_error_image_active = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-error-active.png")); this.syncing_error_image_active = new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-error-active.png"));
this.status_item.Image = this.syncing_idle_image; this.status_item.Image = this.syncing_idle_image;

View file

@ -260,6 +260,7 @@ namespace SparkleShare {
CheckRepositories (); CheckRepositories ();
RepositoriesLoaded = true; RepositoriesLoaded = true;
FolderListChanged (); FolderListChanged ();
UpdateState ();
}).Start (); }).Start ();
} }

View file

@ -19,6 +19,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using SparkleLib; using SparkleLib;
@ -33,15 +34,23 @@ namespace SparkleShare {
public event UpdateContentEventEventHandler UpdateContentEvent = delegate { }; public event UpdateContentEventEventHandler UpdateContentEvent = delegate { };
public delegate void UpdateContentEventEventHandler (string html); public delegate void UpdateContentEventEventHandler (string html);
public event UpdateChooserEventHandler UpdateChooserEvent = delegate { }; public event UpdateChooserEventHandler UpdateChooserEvent = delegate { };
public delegate void UpdateChooserEventHandler (string [] folders); public delegate void UpdateChooserEventHandler (string [] folders);
public event UpdateChooserEnablementEventHandler UpdateChooserEnablementEvent = delegate { };
public delegate void UpdateChooserEnablementEventHandler (bool enabled);
public event UpdateSizeInfoEventHandler UpdateSizeInfoEvent = delegate { }; public event UpdateSizeInfoEventHandler UpdateSizeInfoEvent = delegate { };
public delegate void UpdateSizeInfoEventHandler (string size, string history_size); public delegate void UpdateSizeInfoEventHandler (string size, string history_size);
public event ShowSaveDialogEventHandler ShowSaveDialogEvent = delegate { };
public delegate void ShowSaveDialogEventHandler (string file_name, string target_folder_path);
private string selected_folder; private string selected_folder;
private RevisionInfo restore_revision_info;
private bool history_view_active;
public bool WindowIsOpen { get; private set; } public bool WindowIsOpen { get; private set; }
@ -57,10 +66,10 @@ namespace SparkleShare {
ContentLoadingEvent (); ContentLoadingEvent ();
UpdateSizeInfoEvent ("…", "…"); UpdateSizeInfoEvent ("…", "…");
Stopwatch watch = new Stopwatch ();
watch.Start ();
new Thread (() => { new Thread (() => {
Stopwatch watch = new Stopwatch ();
watch.Start ();
string html = HTML; string html = HTML;
watch.Stop (); watch.Stop ();
@ -161,6 +170,7 @@ namespace SparkleShare {
Thread.Sleep (delay - (int) watch.ElapsedMilliseconds); Thread.Sleep (delay - (int) watch.ElapsedMilliseconds);
UpdateChooserEvent (Folders); UpdateChooserEvent (Folders);
UpdateChooserEnablementEvent (true);
UpdateContentEvent (html); UpdateContentEvent (html);
UpdateSizeInfoEvent (Size, HistorySize); UpdateSizeInfoEvent (Size, HistorySize);
@ -173,6 +183,9 @@ namespace SparkleShare {
}; };
Program.Controller.OnIdle += delegate { Program.Controller.OnIdle += delegate {
if (this.history_view_active)
return;
ContentLoadingEvent (); ContentLoadingEvent ();
UpdateSizeInfoEvent ("…", "…"); UpdateSizeInfoEvent ("…", "…");
@ -212,15 +225,98 @@ namespace SparkleShare {
public void LinkClicked (string url) public void LinkClicked (string url)
{ {
url = url.Replace ("%20", " "); url = url.Replace ("%20", " ");
if (url.StartsWith (Path.VolumeSeparatorChar.ToString ()) ||
url.Substring (1, 1).Equals (":")) {
Program.Controller.OpenFile (url);
} else if (url.StartsWith ("http")) { if (url.StartsWith ("http")) {
Program.Controller.OpenWebsite (url); Program.Controller.OpenWebsite (url);
} else if (url.StartsWith ("restore://") && this.restore_revision_info == null) {
Regex regex = new Regex ("restore://(.+)/([a-f0-9]+)/(.+)/(.{3} [0-9]+ [0-9]+h[0-9]+)/(.+)", RegexOptions.Compiled);
Match match = regex.Match (url);
if (match.Success) {
string author_name = match.Groups [3].Value;
string timestamp = match.Groups [4].Value;
this.restore_revision_info = new RevisionInfo () {
Folder = new SparkleFolder (match.Groups [1].Value),
Revision = match.Groups [2].Value,
FilePath = match.Groups [5].Value
};
string file_name = Path.GetFileNameWithoutExtension (this.restore_revision_info.FilePath) +
" (" + author_name + " " + timestamp + ")" + Path.GetExtension (this.restore_revision_info.FilePath);
string target_folder_path = Path.Combine (this.restore_revision_info.Folder.FullPath,
Path.GetDirectoryName (this.restore_revision_info.FilePath));
ShowSaveDialogEvent (file_name, target_folder_path);
}
} else if (url.StartsWith ("back://")) {
this.history_view_active = false;
SelectedFolder = this.selected_folder; // TODO: Return to the same position on the page
UpdateChooserEnablementEvent (true);
} else if (url.StartsWith ("history://")) {
this.history_view_active = true;
ContentLoadingEvent ();
UpdateSizeInfoEvent ("…", "…");
UpdateChooserEnablementEvent (false);
string folder = url.Replace ("history://", "").Split ("/".ToCharArray ()) [0];
string file_path = url.Replace ("history://" + folder + "/", "");
foreach (SparkleRepoBase repo in Program.Controller.Repositories) {
if (!repo.Name.Equals (folder))
continue;
new Thread (() => {
Stopwatch watch = new Stopwatch ();
watch.Start ();
List<SparkleChangeSet> change_sets = repo.GetChangeSets (file_path);
string html = GetHistoryHTMLLog (change_sets, file_path);
watch.Stop ();
int delay = 500;
if (watch.ElapsedMilliseconds < delay)
Thread.Sleep (delay - (int) watch.ElapsedMilliseconds);
UpdateContentEvent (html);
}).Start ();
break;
}
} else {
Program.Controller.OpenFile (url);
}
}
public void SaveDialogCompleted (string target_file_path)
{
foreach (SparkleRepoBase repo in Program.Controller.Repositories) {
if (repo.Name.Equals (this.restore_revision_info.Folder.Name)) {
repo.RestoreFile (this.restore_revision_info.FilePath,
this.restore_revision_info.Revision, target_file_path);
break;
}
} }
this.restore_revision_info = null;
Program.Controller.OpenFolder (Path.GetDirectoryName (target_file_path));
}
public void SaveDialogCancelled ()
{
this.restore_revision_info = null;
} }
@ -261,6 +357,56 @@ namespace SparkleShare {
} }
public string GetHistoryHTMLLog (List<SparkleChangeSet> change_sets, string file_path)
{
string html = "<div class='history-header'>" +
"<a class='windows' href='back://'>&laquo; Back</a> &nbsp;|&nbsp; ";
if (change_sets.Count > 1)
html += "Revisions for <b>&ldquo;";
else
html += "No revisions for <b>&ldquo;";
html += Path.GetFileName (file_path) + "&rdquo;</b>";
html += "</div><div class='table-wrapper'><table>";
int count = 0;
foreach (SparkleChangeSet change_set in change_sets) {
count++;
if (count == 1)
continue;
string change_set_avatar = Program.Controller.GetAvatar (change_set.User.Email, 24);
if (change_set_avatar != null)
change_set_avatar = "file://" + change_set_avatar.Replace ("\\", "/");
else
change_set_avatar = "file://<!-- $pixmaps-path -->/user-icon-default.png";
html += "<tr>" +
"<td class='avatar'><img src='" + change_set_avatar + "'></td>" +
"<td class='name'>" + change_set.User.Name + "</td>" +
"<td class='date'>" + change_set.Timestamp.ToString ("d MMM yyyy") + "</td>" +
"<td class='time'>" + change_set.Timestamp.ToString ("HH:mm") + "</td>" +
"<td class='restore'>" +
"<a href='restore://" + change_set.Folder.Name + "/" +
change_set.Revision + "/" + change_set.User.Name + "/" +
change_set.Timestamp.ToString ("MMM d H\\hmm") + "/" +
file_path + "'>Restore&hellip;</a>" +
"</td>" +
"</tr>";
count++;
}
html += "</table></div>";
html = Program.Controller.EventLogHTML.Replace ("<!-- $event-log-content -->", html);
return html.Replace ("<!-- $midnight -->", "100000000");
}
public string GetHTMLLog (List<SparkleChangeSet> change_sets) public string GetHTMLLog (List<SparkleChangeSet> change_sets)
{ {
if (change_sets.Count == 0) if (change_sets.Count == 0)
@ -307,12 +453,22 @@ namespace SparkleShare {
foreach (SparkleChange change in change_set.Changes) { foreach (SparkleChange change in change_set.Changes) {
if (change.Type != SparkleChangeType.Moved) { if (change.Type != SparkleChangeType.Moved) {
event_entry += "<dd class='" + change.Type.ToString ().ToLower () + "'>"; event_entry += "<dd class='" + change.Type.ToString ().ToLower () + "'>";
event_entry += "<small>" + change.Timestamp.ToString ("HH:mm") +"</small> &nbsp;";
if (!change.IsFolder) {
event_entry += "<small><a href=\"history://" + change_set.Folder.Name + "/" +
change.Path + "\" title=\"View revisions\">" + change.Timestamp.ToString ("HH:mm") +
"</a></small> &nbsp;";
} else {
event_entry += "<small>" + change.Timestamp.ToString ("HH:mm") + "</small> &nbsp;";
}
event_entry += FormatBreadCrumbs (change_set.Folder.FullPath, change.Path); event_entry += FormatBreadCrumbs (change_set.Folder.FullPath, change.Path);
event_entry += "</dd>"; event_entry += "</dd>";
} else { } else {
event_entry += "<dd class='moved'>"; event_entry += "<dd class='moved'>";
event_entry += "<small>" + change.Timestamp.ToString ("HH:mm") +"</small> &nbsp;";
event_entry += FormatBreadCrumbs (change_set.Folder.FullPath, change.Path); event_entry += FormatBreadCrumbs (change_set.Folder.FullPath, change.Path);
event_entry += "<br>"; event_entry += "<br>";
event_entry += "<small>" + change.Timestamp.ToString ("HH:mm") +"</small> &nbsp;"; event_entry += "<small>" + change.Timestamp.ToString ("HH:mm") +"</small> &nbsp;";
@ -444,5 +600,12 @@ namespace SparkleShare {
Date = new DateTime (date_time.Year, date_time.Month, date_time.Day); Date = new DateTime (date_time.Year, date_time.Month, date_time.Day);
} }
} }
private class RevisionInfo {
public SparkleFolder Folder;
public string FilePath;
public string Revision;
}
} }
} }

View file

@ -120,7 +120,7 @@ namespace SparkleShare {
SparkleLink website_link = new SparkleLink ("Website", Controller.WebsiteLinkAddress); SparkleLink website_link = new SparkleLink ("Website", Controller.WebsiteLinkAddress);
SparkleLink credits_link = new SparkleLink ("Credits", Controller.CreditsLinkAddress); SparkleLink credits_link = new SparkleLink ("Credits", Controller.CreditsLinkAddress);
SparkleLink report_problem_link = new SparkleLink ("Report a problem", Controller.ReportProblemLinkAddress); SparkleLink report_problem_link = new SparkleLink ("Report a problem", Controller.ReportProblemLinkAddress);
SparkleLink debug_log_link = new SparkleLink ("Debig log", Controller.DebugLogLinkAddress); SparkleLink debug_log_link = new SparkleLink ("Debug log", Controller.DebugLogLinkAddress);
Canvas canvas = new Canvas (); Canvas canvas = new Canvas ();
@ -154,7 +154,7 @@ namespace SparkleShare {
canvas.Children.Add (debug_log_link); canvas.Children.Add (debug_log_link);
Canvas.SetLeft (debug_log_link, 289 + website_link.ActualWidth + credits_link.ActualWidth + Canvas.SetLeft (debug_log_link, 289 + website_link.ActualWidth + credits_link.ActualWidth +
report_problem_link.ActualWidth + 180); report_problem_link.ActualWidth + 220);
Canvas.SetTop (debug_log_link, 222); Canvas.SetTop (debug_log_link, 222);
Content = canvas; Content = canvas;

View file

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see (http://www.gnu.org/licenses/). // along with this program. If not, see (http://www.gnu.org/licenses/).
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
@ -26,6 +25,7 @@ using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using Microsoft.Win32;
using Shapes = System.Windows.Shapes; using Shapes = System.Windows.Shapes;
namespace SparkleShare { namespace SparkleShare {
@ -50,7 +50,7 @@ namespace SparkleShare {
ResizeMode = ResizeMode.NoResize; // TODO ResizeMode = ResizeMode.NoResize; // TODO
Background = new SolidColorBrush (Color.FromRgb (240, 240, 240)); Background = new SolidColorBrush (Color.FromRgb (240, 240, 240));
AllowsTransparency = false; AllowsTransparency = false;
Icon = SparkleUIHelpers.GetImageSource("sparkleshare-app", "ico"); Icon = SparkleUIHelpers.GetImageSource ("sparkleshare-app", "ico");
int x = (int) (SystemParameters.PrimaryScreenWidth * 0.61); int x = (int) (SystemParameters.PrimaryScreenWidth * 0.61);
int y = (int) (SystemParameters.PrimaryScreenHeight * 0.5 - (Height * 0.5)); int y = (int) (SystemParameters.PrimaryScreenHeight * 0.5 - (Height * 0.5));
@ -104,6 +104,7 @@ namespace SparkleShare {
this.web_browser.ObjectForScripting = new SparkleScriptingObject (); this.web_browser.ObjectForScripting = new SparkleScriptingObject ();
spinner = new SparkleSpinner (22); spinner = new SparkleSpinner (22);
// Disable annoying IE clicking sound // Disable annoying IE clicking sound
@ -177,6 +178,12 @@ namespace SparkleShare {
}); });
}; };
Controller.UpdateChooserEnablementEvent += delegate (bool enabled) {
Dispatcher.BeginInvoke ((Action) delegate {
this.combo_box.IsEnabled = enabled;
});
};
Controller.UpdateContentEvent += delegate (string html) { Controller.UpdateContentEvent += delegate (string html) {
Dispatcher.BeginInvoke ((Action) delegate { Dispatcher.BeginInvoke ((Action) delegate {
UpdateContent (html); UpdateContent (html);
@ -191,6 +198,25 @@ namespace SparkleShare {
this.canvas.Children.Remove (this.web_browser); this.canvas.Children.Remove (this.web_browser);
}); });
}; };
Controller.ShowSaveDialogEvent += delegate (string file_name, string target_folder_path) {
Dispatcher.BeginInvoke ((Action) delegate {
SaveFileDialog dialog = new SaveFileDialog () {
FileName = file_name,
InitialDirectory = target_folder_path,
Title = "Restore from History",
DefaultExt = "." + Path.GetExtension (file_name),
Filter = "All Files|*.*"
};
Nullable<bool> result = dialog.ShowDialog (this);
if (result == true)
Controller.SaveDialogCompleted (dialog.FileName);
else
Controller.SaveDialogCancelled ();
});
};
} }
@ -262,6 +288,7 @@ namespace SparkleShare {
html = html.Replace ("<!-- $body-font-size -->", "12px"); html = html.Replace ("<!-- $body-font-size -->", "12px");
html = html.Replace ("<!-- $secondary-font-color -->", "#bbb"); html = html.Replace ("<!-- $secondary-font-color -->", "#bbb");
html = html.Replace ("<!-- $small-color -->", "#ddd"); html = html.Replace ("<!-- $small-color -->", "#ddd");
html = html.Replace ("<!-- $small-font-size -->", "90%");
html = html.Replace ("<!-- $day-entry-header-background-color -->", "#f5f5f5"); html = html.Replace ("<!-- $day-entry-header-background-color -->", "#f5f5f5");
html = html.Replace ("<!-- $a-color -->", "#0085cf"); html = html.Replace ("<!-- $a-color -->", "#0085cf");
html = html.Replace ("<!-- $a-hover-color -->", "#009ff8"); html = html.Replace ("<!-- $a-hover-color -->", "#009ff8");
@ -282,9 +309,10 @@ namespace SparkleShare {
Dispatcher.BeginInvoke ((Action) delegate { Dispatcher.BeginInvoke ((Action) delegate {
this.spinner.Stop (); this.spinner.Stop ();
this.web_browser.NavigateToString (html); this.web_browser.ObjectForScripting = new SparkleScriptingObject ();
this.web_browser.NavigateToString (html);
if (!this.canvas.Children.Contains (this.web_browser)) { if (!this.canvas.Children.Contains (this.web_browser)) {
this.canvas.Children.Add (this.web_browser); this.canvas.Children.Add (this.web_browser);
Canvas.SetLeft (this.web_browser, 0); Canvas.SetLeft (this.web_browser, 0);
@ -322,8 +350,8 @@ namespace SparkleShare {
string [] actions = new string [] {"added", "deleted", "edited", "moved"}; string [] actions = new string [] {"added", "deleted", "edited", "moved"};
foreach (string action in actions) { foreach (string action in actions) {
BitmapSource image = SparkleUIHelpers.GetImageSource ("document-" + action + "-12"); image = SparkleUIHelpers.GetImageSource ("document-" + action + "-12");
string file_path = Path.Combine (pixmaps_path, "document-" + action + "-12.png"); file_path = Path.Combine (pixmaps_path, "document-" + action + "-12.png");
using (FileStream stream = new FileStream (file_path, FileMode.Create)) using (FileStream stream = new FileStream (file_path, FileMode.Create))
{ {
@ -345,8 +373,8 @@ namespace SparkleShare {
[DllImport ("urlmon.dll")] [DllImport ("urlmon.dll")]
[PreserveSig] [PreserveSig]
[return:MarshalAs (UnmanagedType.Error)] [return:MarshalAs (UnmanagedType.Error)]
static extern int CoInternetSetFeatureEnabled ( static extern int CoInternetSetFeatureEnabled (int feature,
int feature, [MarshalAs (UnmanagedType.U4)] int flags, bool enable); [MarshalAs (UnmanagedType.U4)] int flags, bool enable);
} }

View file

@ -2,7 +2,7 @@
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> <Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Name='SparkleShare' Id='184950D5-67F6-4D06-9717-7E2F1607A7B0' UpgradeCode='D3DF1D99-87F5-47A7-A349-863DD6E4B73A' <Product Name='SparkleShare' Id='184950D5-67F6-4D06-9717-7E2F1607A7B0' UpgradeCode='D3DF1D99-87F5-47A7-A349-863DD6E4B73A'
Language='1033' Codepage='1252' Version='0.9.3' Manufacturer='SparkleShare'> Language='1033' Codepage='1252' Version='0.9.4' Manufacturer='SparkleShare'>
<Package Id='*' Keywords='Installer' Description="SparkleShare Setup" Manufacturer='SparkleShare' <Package Id='*' Keywords='Installer' Description="SparkleShare Setup" Manufacturer='SparkleShare'
InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' /> InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />

View file

@ -1,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
m4_define([sparkleshare_version], [0.9.3]) m4_define([sparkleshare_version], [0.9.4])
AC_PREREQ([2.54]) AC_PREREQ([2.54])
AC_INIT([SparkleShare], sparkleshare_version) AC_INIT([SparkleShare], sparkleshare_version)