#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

428 lines
14 KiB

// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Workbench;
namespace ICSharpCode.Svn.Commands
{
/// <summary>
/// Registers event handlers for file added, removed, renamed etc. and
/// executes the appropriate Subversion commands.
/// </summary>
public sealed class RegisterEventsCommand : AbstractCommand
{
public override void Run()
{
FileService.FileRemoving += FileRemoving;
FileService.FileRenaming += FileRenaming;
FileService.FileCopying += FileCopying;
FileService.FileCreated += FileCreated;
ProjectService.SolutionCreated += SolutionCreated;
ProjectService.ProjectCreated += ProjectCreated;
FileUtility.FileSaved += FileSaved;
AbstractProjectBrowserTreeNode.OnNewNode += TreeNodeCreated;
}
void TreeNodeCreated(object sender, TreeViewEventArgs e)
{
SolutionNode sn = e.Node as SolutionNode;
if (sn != null) {
if (CanBeVersionControlledDirectory(sn.Solution.Directory)) {
OverlayIconManager.Enqueue(sn);
}
} else {
DirectoryNode dn = e.Node as DirectoryNode;
if (dn != null) {
if (CanBeVersionControlledDirectory(dn.Directory)) {
OverlayIconManager.Enqueue(dn);
}
} else {
FileNode fn = e.Node as FileNode;
if (fn != null) {
if (CanBeVersionControlledFile(fn.FileName)) {
OverlayIconManager.Enqueue(fn);
}
}
}
}
}
void SolutionCreated(object sender, SolutionEventArgs e)
{
if (!AddInOptions.AutomaticallyAddFiles) return;
string solutionFileName = e.Solution.FileName;
string solutionDirectory = e.Solution.Directory;
if (!CanBeVersionControlledFile(solutionDirectory)) return;
try {
using (SvnClientWrapper client = new SvnClientWrapper()) {
SvnMessageView.HandleNotifications(client);
Status status = client.SingleStatus(solutionDirectory);
if (status.TextStatus == StatusKind.Unversioned) {
client.Add(solutionDirectory, Recurse.None);
}
status = client.SingleStatus(solutionFileName);
if (status.TextStatus == StatusKind.Unversioned) {
client.Add(solutionFileName, Recurse.None);
client.AddToIgnoreList(solutionDirectory, Path.GetFileName(solutionFileName) + ".cache");
}
}
foreach (IProject p in e.Solution.Projects) {
ProjectCreated(null, new ProjectEventArgs(p));
}
} catch (SvnClientException ex) {
MessageService.ShowError(ex.Message);
} catch (Exception ex) {
MessageService.ShowException(ex, "Solution add exception");
}
}
void ProjectCreated(object sender, ProjectEventArgs e)
{
if (!AddInOptions.AutomaticallyAddFiles) return;
if (!CanBeVersionControlledFile(e.Project.Directory)) return;
string projectDir = Path.GetFullPath(e.Project.Directory);
try {
using (SvnClientWrapper client = new SvnClientWrapper()) {
SvnMessageView.HandleNotifications(client);
Status status = client.SingleStatus(projectDir);
if (status.TextStatus != StatusKind.Unversioned)
return;
client.Add(projectDir, Recurse.None);
if (FileUtility.IsBaseDirectory(Path.Combine(projectDir, "bin"), e.Project.OutputAssemblyFullPath)) {
client.AddToIgnoreList(projectDir, "bin");
}
CompilableProject compilableProject = e.Project as CompilableProject;
if (compilableProject != null) {
if (FileUtility.IsBaseDirectory(Path.Combine(projectDir, "obj"), compilableProject.IntermediateOutputFullPath)) {
client.AddToIgnoreList(projectDir, "obj");
}
}
foreach (ProjectItem item in e.Project.Items) {
FileProjectItem fileItem = item as FileProjectItem;
if (fileItem != null) {
if (FileUtility.IsBaseDirectory(projectDir, fileItem.FileName)) {
AddFileWithParentDirectoriesToSvn(client, fileItem.FileName);
}
}
}
AddFileWithParentDirectoriesToSvn(client, e.Project.FileName);
}
} catch (SvnClientException ex) {
MessageService.ShowError(ex.Message);
} catch (Exception ex) {
MessageService.ShowException(ex, "Project add exception");
}
}
void AddFileWithParentDirectoriesToSvn(SvnClientWrapper client, string fileName)
{
if (!CanBeVersionControlledFile(fileName)) {
AddFileWithParentDirectoriesToSvn(client, FileUtility.GetAbsolutePath(fileName, ".."));
}
Status status = client.SingleStatus(fileName);
if (status.TextStatus != StatusKind.Unversioned)
return;
client.Add(fileName, Recurse.None);
}
internal static bool CanBeVersionControlledFile(string fileName)
{
return CanBeVersionControlledDirectory(Path.GetDirectoryName(fileName));
}
internal static bool CanBeVersionControlledDirectory(string directory)
{
return FindWorkingCopyRoot(directory) != null;
}
static string FindWorkingCopyRoot(string directory)
{
if (OverlayIconManager.SubversionDisabled)
return null;
try {
if (!Path.IsPathRooted(directory))
return null;
} catch (ArgumentException) {
return null;
}
DirectoryInfo info = new DirectoryInfo(directory);
while (info != null) {
if (Directory.Exists(Path.Combine(info.FullName, ".svn")))
return info.FullName;
info = info.Parent;
}
return null;
}
void FileSaved(object sender, FileNameEventArgs e)
{
string fileName = e.FileName;
if (!CanBeVersionControlledFile(fileName)) return;
ClearStatusCacheAndEnqueueFile(fileName);
}
void ClearStatusCacheAndEnqueueFile(string fileName)
{
OverlayIconManager.ClearStatusCache();
ProjectBrowserPad pad = ProjectBrowserPad.Instance;
if (pad == null) return;
FileNode node = pad.ProjectBrowserControl.FindFileNode(fileName);
if (node == null) return;
OverlayIconManager.Enqueue(node);
}
/// <summary>
/// Adds the newly created file to the repository.
/// </summary>
/// <param name="sender">Not used.</param>
/// <param name="e">The name of the file and whether it is a directory or not.</param>
void FileCreated(object sender, FileEventArgs e)
{
if (!AddInOptions.AutomaticallyAddFiles) return;
if (!Path.IsPathRooted(e.FileName)) return;
string fullName = Path.GetFullPath(e.FileName);
if (!CanBeVersionControlledFile(fullName)) return;
try {
using (SvnClientWrapper client = new SvnClientWrapper()) {
SvnMessageView.HandleNotifications(client);
Status status = client.SingleStatus(fullName);
switch (status.TextStatus) {
case StatusKind.Unversioned:
case StatusKind.Deleted:
using (SharpDevelop.Gui.AsynchronousWaitDialog.ShowWaitDialog("svn add")) {
client.Add(fullName, Recurse.None);
}
break;
}
}
} catch (SvnClientException ex) {
MessageService.ShowError(ex.Message);
}
}
void FileRemoving(object sender, FileCancelEventArgs e)
{
if (e.Cancel) return;
string fullName = Path.GetFullPath(e.FileName);
if (!CanBeVersionControlledFile(fullName)) return;
if (e.IsDirectory) {
// show "cannot delete directories" message even if
// AutomaticallyDeleteFiles (see below) is off!
using (SvnClientWrapper client = new SvnClientWrapper()) {
SvnMessageView.HandleNotifications(client);
try {
Status status = client.SingleStatus(fullName);
switch (status.TextStatus) {
case StatusKind.None:
case StatusKind.Unversioned:
case StatusKind.Ignored:
break;
default:
// must be done using the subversion client, even if
// AutomaticallyDeleteFiles is off, because we don't want to corrupt the
// working copy
e.OperationAlreadyDone = true;
try {
client.Delete(new string[] { fullName }, false);
} catch (SvnClientException ex) {
LoggingService.Warn("SVN Error" + ex);
LoggingService.Warn(ex);
if (ex.IsKnownError(KnownError.CannotDeleteFileWithLocalModifications)
|| ex.IsKnownError(KnownError.CannotDeleteFileNotUnderVersionControl))
{
if (MessageService.ShowCustomDialog("${res:AddIns.Subversion.DeleteDirectory}",
StringParser.Parse("${res:AddIns.Subversion.ErrorDelete}:\n",
new StringTagPair("File", fullName)) +
ex.Message, 0, 1,
"${res:AddIns.Subversion.ForceDelete}", "${res:Global.CancelButtonText}")
== 0)
{
try {
client.Delete(new string[] { fullName }, true);
} catch (SvnClientException ex2) {
e.Cancel = true;
MessageService.ShowError(ex2.Message);
}
} else {
e.Cancel = true;
}
} else {
e.Cancel = true;
MessageService.ShowError(ex.Message);
}
}
break;
}
} catch (SvnClientException ex3) {
e.Cancel = true;
MessageService.ShowError(ex3.Message);
}
}
return;
}
// not a directory, but a file:
if (!AddInOptions.AutomaticallyDeleteFiles) return;
try {
using (SvnClientWrapper client = new SvnClientWrapper()) {
SvnMessageView.HandleNotifications(client);
Status status = client.SingleStatus(fullName);
switch (status.TextStatus) {
case StatusKind.None:
case StatusKind.Unversioned:
case StatusKind.Ignored:
case StatusKind.Deleted:
return; // nothing to do
case StatusKind.Normal:
// remove without problem
break;
case StatusKind.Modified:
case StatusKind.Replaced:
if (MessageService.AskQuestion("${res:AddIns.Subversion.RevertLocalModifications}")) {
// modified files cannot be deleted, so we need to revert the changes first
client.Revert(new string[] { fullName }, e.IsDirectory ? Recurse.Full : Recurse.None);
} else {
e.Cancel = true;
return;
}
break;
case StatusKind.Added:
if (status.Copied) {
if (!MessageService.AskQuestion("${res:AddIns.Subversion.RemoveMovedFile}")) {
e.Cancel = true;
return;
}
}
client.Revert(new string[] { fullName }, e.IsDirectory ? Recurse.Full : Recurse.None);
return;
default:
MessageService.ShowErrorFormatted("${res:AddIns.Subversion.CannotRemoveError}", status.TextStatus.ToString());
e.Cancel = true;
return;
}
e.OperationAlreadyDone = true;
client.Delete(new string [] { fullName }, true);
}
} catch (Exception ex) {
MessageService.ShowError("File removed exception: " + ex);
}
}
void FileCopying(object sender, FileRenamingEventArgs e)
{
if (e.Cancel) return;
if (!AddInOptions.AutomaticallyRenameFiles) return;
string fullSource = Path.GetFullPath(e.SourceFile);
if (!CanBeVersionControlledFile(fullSource)) return;
string fullTarget = Path.GetFullPath(e.TargetFile);
if (!CanBeVersionControlledFile(fullTarget)) return;
try {
using (SvnClientWrapper client = new SvnClientWrapper()) {
SvnMessageView.HandleNotifications(client);
Status status = client.SingleStatus(fullSource);
switch (status.TextStatus) {
case StatusKind.Unversioned:
case StatusKind.None:
return; // nothing to do
case StatusKind.Normal:
case StatusKind.Modified:
case StatusKind.Replaced:
case StatusKind.Added:
// copy without problem
break;
default:
MessageService.ShowErrorFormatted("${res:AddIns.Subversion.CannotCopyError}", status.TextStatus.ToString());
e.Cancel = true;
return;
}
e.OperationAlreadyDone = true;
client.Copy(fullSource, fullTarget);
}
} catch (Exception ex) {
MessageService.ShowError("File renamed exception: " + ex);
}
}
void FileRenaming(object sender, FileRenamingEventArgs e)
{
if (e.Cancel) return;
if (!AddInOptions.AutomaticallyRenameFiles) return;
string fullSource = Path.GetFullPath(e.SourceFile);
if (!CanBeVersionControlledFile(fullSource)) return;
if (!FileHelpers.CheckRenameOrReplacePossible(e)) {
e.Cancel = true;
return;
}
try {
using (SvnClientWrapper client = new SvnClientWrapper()) {
SvnMessageView.HandleNotifications(client);
Status status = client.SingleStatus(fullSource);
switch (status.TextStatus) {
case StatusKind.Unversioned:
case StatusKind.None:
case StatusKind.Ignored:
return; // nothing to do
case StatusKind.Normal:
case StatusKind.Modified:
case StatusKind.Replaced:
case StatusKind.Added:
// rename without problem
break;
default:
MessageService.ShowErrorFormatted("${res:AddIns.Subversion.CannotMoveError}", status.TextStatus.ToString());
e.Cancel = true;
return;
}
e.OperationAlreadyDone = true;
client.Move(fullSource,
Path.GetFullPath(e.TargetFile),
true
);
}
} catch (Exception ex) {
MessageService.ShowError("File renamed exception: " + ex);
}
}
}
}