예제 #1
0
		public void Equality()
		{
			var projA = new ProjectId("xml", "monkey", null);
			var projB = new ProjectId("xml", "monkey", null);
			Assert.AreEqual(FDOBackendProviderType.kXML, projA.Type);
			Assert.IsTrue(projA.Equals(projB));
			Assert.AreEqual(projA.GetHashCode(), projB.GetHashCode());
		}
예제 #2
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Checks to see whether this instance is connected to the requested project. If so,
		/// starts up the requested app or activates a main window for that app if already
		/// running.
		/// </summary>
		/// <param name="projectId">The requested project ID.</param>
		/// <param name="args">The application arguments</param>
		/// <returns>The result of checking to see if the specified project matches the
		/// project this instance is running</returns>
		/// ------------------------------------------------------------------------------------
		public ProjectMatch HandleOpenProjectRequest(ProjectId projectId, FwAppArgs args)
		{
			ProjectMatch isMyProject = FieldWorks.GetProjectMatchStatus(projectId);
			if (isMyProject != ProjectMatch.ItsMyProject)
				return isMyProject;

			FieldWorks.KickOffAppFromOtherProcess(args);
			return ProjectMatch.ItsMyProject; // The request has been handled at this point
		}
예제 #3
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Handles a link request. This handles determining the correct application to start
		/// up and for the correct project. This method is thread safe.
		/// </summary>
		/// <param name="link">The link.</param>
		/// ------------------------------------------------------------------------------------
		internal static void HandleLinkRequest(FwAppArgs link)
		{
			s_threadHelper.Invoke(() =>
			{
				Debug.Assert(s_projectId != null, "We shouldn't try to handle a link request until an application is started");
				ProjectId linkedProject = new ProjectId(link.DatabaseType, link.Database, link.Server);
				if (IsSharedXmlBackendNeeded(linkedProject))
					linkedProject.Type = FDOBackendProviderType.kSharedXML;
				if (linkedProject.Equals(s_projectId))
					FollowLink(link);
				else if (!TryFindLinkHandler(link))
				{
					// No other FieldWorks process was running that could handle the request, so
					// start a brand new process for the project requested by the link.
					// REVIEW: it might look strange to dispose the return value of OpenProjectWithNewProcess.
					// However, that is a Process that gets started, and it is ok to dispose that
					// right away if we don't work with the process object. It might be better
					// though to change the signature of OpenProjectWithNewProcess to return
					// a boolean (true iff the link was successfully handled).
					using (OpenProjectWithNewProcess(linkedProject, link.AppAbbrev, "-" + FwLinkArgs.kLink, link.ToString()))
					{
					}
				}
			});
		}
예제 #4
0
		public void CheckProperties()
		{
			string expectedProjectDir = Path.Combine(FwDirectoryFinder.ProjectsDirectory, "SomeTest");
			m_mockFileOs.ExistingDirectories.Add(expectedProjectDir);

			const string type = "db4ocs";
			const string host = "127.0.0.1";
			string filename = FdoFileHelper.GetDb4oDataFileName("SomeTest");

			var proj = new ProjectId(type, filename, host);
			proj.AssertValid();
			Assert.AreEqual(Path.Combine(expectedProjectDir, filename), proj.Path);

			proj = new ProjectId(filename, host);
			proj.AssertValid();
			Assert.AreEqual(Path.Combine(expectedProjectDir, filename), proj.Path);
		}
예제 #5
0
		public void AssertValid_InvalidProjectType()
		{
			var proj = new ProjectId(FDOBackendProviderType.kInvalid, FdoFileHelper.GetXmlDataFileName("invalid"), null);
			try
			{
				proj.AssertValid();
				Assert.Fail("FwStartupException expected");
			}
			catch (StartupException exception)
			{
				Assert.IsTrue(exception.ReportToUser);
			}
		}
예제 #6
0
		public void AssertValid_Invalid_NoName()
		{
			var proj = new ProjectId(string.Empty, null);
			try
			{
				proj.AssertValid();
				Assert.Fail("FwStartupException expected");
			}
			catch (StartupException exception)
			{
				Assert.IsFalse(exception.ReportToUser);
			}
		}
예제 #7
0
		public void CleanUpNameForType_XML_RelativePath()
		{
			string relativePath = Path.Combine("primate", FdoFileHelper.GetXmlDataFileName("monkey"));
			string expectedPath = Path.Combine(FwDirectoryFinder.ProjectsDirectory, relativePath);
			m_mockFileOs.AddExistingFile(expectedPath);

			ProjectId proj = new ProjectId(relativePath, null);
			Assert.AreEqual(expectedPath, proj.Path);
			Assert.AreEqual(FDOBackendProviderType.kXML, proj.Type);
			Assert.IsTrue(proj.IsValid);
		}
예제 #8
0
		public void CleanUpNameForType_XML_NameWithPeriod_NotExist()
		{
			string expectedPath = GetXmlProjectFilename("my.monkey");
			m_mockFileOs.AddExistingFile(expectedPath);

			var proj = new ProjectId("my.monkey", null);
			Assert.AreEqual(expectedPath, proj.Path);
			Assert.AreEqual(FDOBackendProviderType.kXML, proj.Type);
			Assert.IsTrue(proj.IsValid);
		}
예제 #9
0
		private static FdoCache CreateCache(ProjectId projectId)
		{
			Debug.Assert(projectId.IsValid);

			WriteSplashScreen(string.Format(Properties.Resources.kstidLoadingProject, projectId.UiName));
			Form owner = s_splashScreen != null ? s_splashScreen.Form : Form.ActiveForm;
			using (var progressDlg = new ProgressDialogWithTask(owner))
			{
				FdoCache cache = FdoCache.CreateCacheFromExistingData(projectId, s_sWsUser, s_ui, FwDirectoryFinder.FdoDirectories, CreateFdoSettings(), progressDlg);
				EnsureValidLinkedFilesFolder(cache);
				// Make sure every project has one of these. (Getting it has a side effect if it does not exist.)
				// Crashes have been caused by trying to create it at an unsafe time (LT-15695).
				var dummy = cache.LangProject.DefaultPronunciationWritingSystem;
				cache.ProjectNameChanged += ProjectNameChanged;
				cache.ServiceLocator.GetInstance<IUndoStackManager>().OnSave += FieldWorks_OnSave;

				SetupErrorPropertiesNeedingCache(cache);
				return cache;
			}
		}
예제 #10
0
		private static bool IsSharedXmlBackendNeeded(ProjectId projectId)
		{
			return projectId.Type == FDOBackendProviderType.kXML && ParatextHelper.GetAssociatedProject(projectId) != null;
		}
예제 #11
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Launches the application when requested from the command-line.
		/// </summary>
		/// <param name="appArgs">The application command-line arguments.</param>
		/// ------------------------------------------------------------------------------------
		private static bool LaunchApplicationFromCommandLine(FwAppArgs appArgs)
		{
			// Get the application requested on the command line
			if (!CreateApp(appArgs))
				return false;

			// Get the project the user wants to open and attempt to launch it.
			ProjectId projectId = DetermineProject(appArgs);
			if (projectId != null && IsSharedXmlBackendNeeded(projectId))
				projectId.Type = FDOBackendProviderType.kSharedXML;

			// s_projectId can be non-null if the user decided to restore a project from
			// the Welcome to Fieldworks dialog. (FWR-2146)
			if (s_projectId == null && !LaunchProject(appArgs, ref projectId))
				return false;

			// The project was successfully loaded so store it. This will let any other
			// FieldWorks processes that are waiting on us be able to continue.
			s_projectId = projectId;

			WarnUserAboutFailedLiftImportIfNecessary(GetOrCreateApplication(appArgs));

			if (s_noUserInterface)
			{
				// We should have a main window by now, so the help button on the dialog
				// will work if needed.
				CheckForMovingExternalLinkDirectory(GetOrCreateApplication(appArgs));
			}

			return true;
		}
예제 #12
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Determines the match status for the project used by this FieldWorks process and
		/// the specified project. This method is thread-safe.
		/// </summary>
		/// <param name="projectId">The project to test.</param>
		/// <returns>
		/// The result of checking to see if the specified project matches the project used
		/// by this FieldWorks processinstance is running
		/// </returns>
		/// ------------------------------------------------------------------------------------
		internal static ProjectMatch GetProjectMatchStatus(ProjectId projectId)
		{
			if (s_fSingleProcessMode)
				return ProjectMatch.SingleProcessMode;

			if (s_fWaitingForUserOrOtherFw)
				return ProjectMatch.WaitingForUserOrOtherFw;

			ProjectId thisProjectId = s_projectId; // Store in temp variable for thread safety
			if (thisProjectId == null)
				return ProjectMatch.DontKnowYet;

			return thisProjectId.Equals(projectId) ? ProjectMatch.ItsMyProject :
				ProjectMatch.ItsNotMyProject;
		}
예제 #13
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Executes the requested action with all FW applications temporarily shut down and
		/// then (re)starts the applications in a sensible order. At the end, we're guaranteed
		/// to have at least one app started or FieldWorks will be shut down.
		/// </summary>
		/// <param name="abbrevOfDefaultAppToStart">The abbreviation of the default application
		/// to start.</param>
		/// <param name="action">The action to execute.</param>
		/// ------------------------------------------------------------------------------------
		private static void ExecuteWithAppsShutDown(string abbrevOfDefaultAppToStart, Func<ProjectId> action)
		{
			bool allowFinalShutdownOrigValue = s_allowFinalShutdown;
			s_allowFinalShutdown = false; // don't shutdown when we close all windows!

			// Remember which apps were running (and the order in which to restore them)
			List<string> appsToRestore = new List<string>();
			// If the requested default application is running, then add it as the first app to restore
			if (GetAppFromAppNameOrAbbrev(abbrevOfDefaultAppToStart) != null)
				appsToRestore.Add(abbrevOfDefaultAppToStart);
			if (s_flexApp != null && !appsToRestore.Contains(FwUtils.ksFlexAbbrev))
				appsToRestore.Add(FwUtils.ksFlexAbbrev);
			if (s_teApp != null && !appsToRestore.Contains(FwUtils.ksTeAbbrev))
				appsToRestore.Add(FwUtils.ksTeAbbrev);
			if (appsToRestore.Count == 0)
			{
				if (abbrevOfDefaultAppToStart.Equals(FwUtils.ksTeAbbrev, StringComparison.InvariantCultureIgnoreCase) &&
					FwUtils.IsTEInstalled)
				{
					appsToRestore.Add(FwUtils.ksTeAbbrev);
				}
				else
					appsToRestore.Add(FwUtils.ksFlexAbbrev);
			}
			// Now shut down everything (windows, apps, cache, etc.)
			GracefullyShutDown();

			if (s_applicationExiting)
			{
				// Something bad must have happened because we are shutting down. There is no point in
				// executing the action or in restarting the applications since we have no idea what
				// state the applications/data are in. (FWR-3179)
				Debug.Assert(s_allowFinalShutdown, "If something bad happened, we should be allowing application shutdown");
				return;
			}

			try
			{
				// Run the action
				ProjectId projId = action();

				if (projId == null)
					return;

				s_projectId = null; // Needs to be null in InitializeFirstApp

				// Restart the default app from which the action was kicked off
				FwApp app = GetOrCreateApplication(new FwAppArgs(appsToRestore[0],
					projId.Handle, projId.ServerName, string.Empty, Guid.Empty));
				if (!InitializeFirstApp(app, projId))
					return;

				s_projectId = projId; // Process needs to know its project

				// Reopen other apps if necessary (shouldn't ever be more then one) :P
				for (int i = 1; i < appsToRestore.Count; i++)
				{
					app = GetOrCreateApplication(new FwAppArgs(appsToRestore[i], projId.Handle,
						projId.ServerName, string.Empty, Guid.Empty));
					InitializeApp(app, null);
				}
			}
			finally
			{
				s_allowFinalShutdown = allowFinalShutdownOrigValue; // mustn't suppress any longer (unless we already were).
				ExitIfNoAppsRunning();
			}
		}
예제 #14
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Tries to find an existing FieldWorks process that is running the specified project.
		/// See the class comment on FwLinkArgs for details on how all the parts of hyperlinking work.
		/// </summary>
		/// <param name="project">The project we want to conect to.</param>
		/// <param name="args">The application arguments.</param>
		/// <returns>
		/// True if an existing process was found with the specified project and
		/// control was released to the found process, false otherwise
		/// </returns>
		/// ------------------------------------------------------------------------------------
		private static bool TryFindExistingProcess(ProjectId project, FwAppArgs args)
		{
			return RunOnRemoteClients(kFwRemoteRequest, requestor =>
			{
				ProjectMatch isMyProject;
				Func<ProjectId, FwAppArgs, ProjectMatch> invoker = requestor.HandleOpenProjectRequest;
				var start = DateTime.Now;
				do
				{
					IAsyncResult ar = invoker.BeginInvoke(project, args, null, null);
					while (!ar.IsCompleted)
					{
						s_fWaitingForUserOrOtherFw = true;
						// Wait until this process knows which project it is loading.
						if (!ar.AsyncWaitHandle.WaitOne(9000, false))
						{
							// timed out.
							if (MessageBox.Show(Properties.Resources.kstidFieldWorksDidNotRespond, Properties.Resources.kstidStartupProblem,
								MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
							{
								return true;
							}
						}
					}
					// We can now ask for the answer.
					isMyProject = invoker.EndInvoke(ar);

					if (isMyProject == ProjectMatch.SingleProcessMode)
					{
						Logger.WriteEvent("WEIRD! Detected single FW process mode while this process is trying to open a project.");
						Debug.Fail("We don't think this can happen, but it's no big deal.");
						return true; // Should kill this process
					}
					if (DateTime.Now - start > new TimeSpan(0, 0, 10))
					{
						// Some other process apparently keeps telling us it doesn't know. It's probably stuck in this same loop,
						// waiting for us!
						MessageBox.Show(Properties.Resources.kstidFieldWorksRespondedNotSure, Properties.Resources.kstidStartupProblem,
							MessageBoxButtons.OK, MessageBoxIcon.Warning);
						return true; // pretends some other process has the project opened and is handling the request; this process will quit
					}
				} while (isMyProject == ProjectMatch.DontKnowYet);

				s_fWaitingForUserOrOtherFw = false;
				return (isMyProject == ProjectMatch.ItsMyProject);
			});
		}
예제 #15
0
		private static bool InitializeFirstApp(FwApp app, ProjectId projectId)
		{
			Debug.Assert(s_cache == null && s_projectId == null, "This should only get called once");
			Debug.Assert(projectId != null, "Should have exited the program");

			using (var process = Process.GetCurrentProcess())
			{
				app.RegistrySettings.LoadingProcessId = process.Id;
			}
			if (String.IsNullOrEmpty(app.RegistrySettings.LatestProject))
			{
				// Until something gets saved, we will keep track of the first project opened.
				app.RegistrySettings.LatestProject = projectId.Handle;
				app.RegistrySettings.LatestServer = projectId.ServerName ?? string.Empty;
			}

			UsageEmailDialog.IncrementLaunchCount(app.SettingsKey); // count launches for bug reporting

			ShowSplashScreen(app);

			try
			{
				// Create the cache and let the application init the cache for what it needs
				s_cache = CreateCache(projectId);
				Debug.Assert(s_cache != null, "At this point we should know which project to load and have loaded it!");

				if (s_noUserInterface || InitializeApp(app, s_splashScreen))
				{
					app.RegistrySettings.LoadingProcessId = 0;
#if !__MonoCS__
					if (!WindowsInstallerQuery.IsThisInstalled() || app.ActiveMainWindow == null)
						return true;

					// Initialize NetSparkle to check for updates:
					Settings.Default.IsBTE = WindowsInstallerQuery.IsThisBTE();

					var appCastUrl = Settings.Default.IsBTE
						? (Settings.Default.CheckForBetaUpdates
							? CoreImpl.Properties.Resources.ResourceManager.GetString("kstidAppcastBteBetasUrl")
							: CoreImpl.Properties.Resources.ResourceManager.GetString("kstidAppcastBteUrl"))
						: (Settings.Default.CheckForBetaUpdates
							? CoreImpl.Properties.Resources.ResourceManager.GetString("kstidAppcastSeBetasUrl")
							: CoreImpl.Properties.Resources.ResourceManager.GetString("kstidAppcastSeUrl"));

					var sparkle = SingletonsContainer.Get("Sparkle", () => new Sparkle(appCastUrl, app.ActiveMainWindow.Icon));
					sparkle.AboutToExitForInstallerRun += delegate(object sender, CancelEventArgs args)
						{
							CloseAllMainWindows();
							if(app.ActiveMainWindow != null)
							{
								args.Cancel = true;
							}
						};
					if (Settings.Default.AutoCheckForUpdates)
						sparkle.CheckOnFirstApplicationIdle();
#endif
					return true;
				}
			}
			catch (UnauthorizedAccessException uae)
			{
				if (MiscUtils.IsUnix)
				{
					// Tell Mono user he/she needs to logout and log back in
					MessageBox.Show(ResourceHelper.GetResourceString("ksNeedToJoinFwGroup"));
				}
				throw;
			}
			catch (FdoDataMigrationForbiddenException)
			{
				// tell the user to close all other applications using this project
				MessageBox.Show(ResourceHelper.GetResourceString("kstidDataMigrationProhibitedText"),
					ResourceHelper.GetResourceString("kstidDataMigrationProhibitedCaption"), MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			finally
			{
				CloseSplashScreen();
			}

			return false;
		}
예제 #16
0
		public void CleanUpNameForType_Default_NameWithPeriod_Exists()
		{
			string expectedPath = Path.Combine(Path.Combine(FwDirectoryFinder.ProjectsDirectory, "my.monkey"), "my.monkey");
			m_localCsSvcs.Stub(cs => cs.IdForLocalProject("my.monkey")).Return(expectedPath);
			m_mockFileOs.AddExistingFile(expectedPath);

			ProjectId proj = new ProjectId("my.monkey", null);
			Assert.AreEqual(expectedPath, proj.Path);
			Assert.AreEqual(FDOBackendProviderType.kXML, proj.Type);
			Assert.IsTrue(proj.IsValid);
		}
예제 #17
0
		public void CleanUpNameForType_XML_NameWithPeriod_FilesWithAndWithoutExtensionExist()
		{
			string myMonkeyProjectFolder = Path.Combine(FwDirectoryFinder.ProjectsDirectory, "my.monkey");
			string expectedPath = Path.Combine(myMonkeyProjectFolder, FdoFileHelper.GetXmlDataFileName("my.monkey"));
			m_mockFileOs.AddExistingFile(expectedPath);
			m_mockFileOs.AddExistingFile(Path.Combine(myMonkeyProjectFolder, "my.monkey"));

			var proj = new ProjectId("my.monkey", null);
			Assert.AreEqual(expectedPath, proj.Path);
			Assert.AreEqual(FDOBackendProviderType.kXML, proj.Type);
			Assert.IsTrue(proj.IsValid);
		}
예제 #18
0
		public void IsValid_NullType()
		{
			const string sProjectName = "monkey";
			string sFile = FdoFileHelper.GetXmlDataFileName(sProjectName);
			m_mockFileOs.AddExistingFile(GetXmlProjectFilename(sProjectName));
			ProjectId proj = new ProjectId(null, sFile, null);
			Assert.AreEqual(FDOBackendProviderType.kXML, proj.Type);
			Assert.IsTrue(proj.IsValid);
		}
예제 #19
0
		public void CleanUpNameForType_XML_onlyNameWithExtension()
		{
			string expectedPath = GetXmlProjectFilename("monkey");
			m_mockFileOs.AddExistingFile(expectedPath);

			var proj = new ProjectId(FdoFileHelper.GetXmlDataFileName("monkey"), null);
			Assert.AreEqual(expectedPath, proj.Path);
			Assert.AreEqual(FDOBackendProviderType.kXML, proj.Type);
			Assert.IsTrue(proj.IsValid);
		}
예제 #20
0
		public void IsValid_XML_False()
		{
			ProjectId proj = new ProjectId("xml", "notThere", null);
			Assert.IsFalse(proj.IsValid);
		}
예제 #21
0
		public void AssertValid_Valid()
		{
			string projFile = GetXmlProjectFilename("monkey");
			m_mockFileOs.AddExistingFile(projFile);

			var proj = new ProjectId(FdoFileHelper.GetXmlDataFileName("monkey"), null);
			proj.AssertValid(); // no exception should be thrown here for a valid project.
		}
예제 #22
0
		public void IsValid_XML_True()
		{
			const string sProjectName = "monkey";
			string sFile = FdoFileHelper.GetXmlDataFileName(sProjectName);
			m_mockFileOs.AddExistingFile(GetXmlProjectFilename(sProjectName));
			ProjectId proj = new ProjectId("xml", sFile, null);
			Assert.IsTrue(proj.IsValid);
		}
예제 #23
0
		public void AssertValid_Invalid_FileNotFound()
		{
			var proj = new ProjectId(FdoFileHelper.GetXmlDataFileName("notfound"), null);
			try
			{
				proj.AssertValid();
				Assert.Fail("FwStartupException expected");
			}
			catch (StartupException exception)
			{
				Assert.IsTrue(exception.ReportToUser);
			}
		}
예제 #24
0
		public void IsValid_XML_NullProjectName()
		{
			ProjectId proj = new ProjectId("xml", null, null);
			Assert.IsFalse(proj.IsValid);
		}
예제 #25
0
		public void AssertValid_Invalid_SharedFolderNotFound()
		{
			var proj = new ProjectId(FdoFileHelper.GetDb4oDataFileName("monkey"), FwLinkArgs.kLocalHost);
			try
			{
				proj.AssertValid();
				Assert.Fail("FwStartupException expected");
			}
			catch (StartupException exception)
			{
				Assert.IsTrue(exception.ReportToUser);
			}
		}
예제 #26
0
		public void CleanUpNameForType_EmptyName()
		{
			var proj = new ProjectId(string.Empty, null);
			Assert.IsNull(proj.Path);
			Assert.AreEqual(FDOBackendProviderType.kXML, proj.Type);
			Assert.IsFalse(proj.IsValid);
		}
예제 #27
0
		public void NameAndPath()
		{
			string myProjectFolder = Path.Combine(FwDirectoryFinder.ProjectsDirectory, "My.Project");
			ProjectId projId = new ProjectId(FDOBackendProviderType.kXML, "My.Project", null);
			Assert.AreEqual(Path.Combine(myProjectFolder, FdoFileHelper.GetXmlDataFileName("My.Project")), projId.Path);
			Assert.AreEqual("My.Project", projId.Name);

			projId = new ProjectId(FDOBackendProviderType.kDb4oClientServer, "My.Project", null);
			Assert.AreEqual(Path.Combine(myProjectFolder, FdoFileHelper.GetDb4oDataFileName("My.Project")), projId.Path);
			Assert.AreEqual("My.Project", projId.Name);
		}
예제 #28
0
		public void CleanUpNameForType_Default_onlyName()
		{
			m_defaultBepType = FDOBackendProviderType.kDb4oClientServer;
			string expectedPath = Path.Combine(Path.Combine(FwDirectoryFinder.ProjectsDirectory, "ape"),
				FdoFileHelper.GetDb4oDataFileName("ape"));
			m_localCsSvcs.Stub(cs => cs.IdForLocalProject("ape")).Return(expectedPath);
			m_mockFileOs.AddExistingFile(expectedPath);

			ProjectId proj = new ProjectId("ape", null);
			Assert.AreEqual(expectedPath, proj.Path);
			Assert.AreEqual(FDOBackendProviderType.kDb4oClientServer, proj.Type);
			Assert.IsTrue(proj.IsValid);
		}
예제 #29
0
		public void IsValid_BogusType()
		{
			ProjectId proj = new ProjectId("bogus", "rogus", null);
			Assert.AreEqual(FDOBackendProviderType.kInvalid, proj.Type);
			Assert.IsFalse(proj.IsValid);
		}
예제 #30
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Moves all of the projects in the specified old project folder (including any
		/// subfolders) to the specified new project folder.
		/// </summary>
		/// <param name="oldFolderForProjects">The old folder that held the projects.</param>
		/// <param name="newFolderForProjects">The new folder that will hold the projects.</param>
		/// <param name="projectPath">The project path.</param>
		/// <param name="oldProjectId">The Id of the old project (to use if it has not moved)</param>
		/// <returns>The ProjectId for the project to load after the move is completed</returns>
		/// ------------------------------------------------------------------------------------
		private static ProjectId MoveProjectFolders(string oldFolderForProjects, string newFolderForProjects,
			string projectPath, ProjectId oldProjectId)
		{
			List<string> rgErrors = new List<string>();
			bool fCopy = MustCopyFoldersAndFiles(oldFolderForProjects, newFolderForProjects);
			using (ProgressDialogWithTask progressDlg = new ProgressDialogWithTask(s_threadHelper))
			{
				string[] subDirs = Directory.GetDirectories(oldFolderForProjects);
				progressDlg.Maximum = subDirs.Length;
				progressDlg.AllowCancel = false;
				progressDlg.Title = string.Format(Properties.Resources.ksMovingProjectsCaption, oldFolderForProjects, newFolderForProjects);
				// Move all of the files and folders
				progressDlg.RunTask(true, (progressDialog, dummy) =>
				{
					foreach (string subdir in subDirs)
					{
						try
						{
							string sub = Path.GetFileName(subdir);
							// If the project folder is not known to be ours don't move the folder.
							// This is some protection against moving vital folders if the user does something silly like
							// making C:\ the project folder. See FWR-3371.
							var destDirName = Path.Combine(newFolderForProjects, sub);
							if (!IsFieldWorksProjectFolder(subdir))
							{
								// It might still be a folder which is the name of a remote server,
								// and contains local settings for projects on that server.
								// Check each of its subfolders, and move/copy the ones that appear to be settings, if any.
								bool movedSomething = false;
								foreach (string subsubdir in Directory.GetDirectories(subdir))
								{
									if (!IsFieldWorksSettingsFolder(subsubdir))
										continue;
									// We found a project folder one level down. This ought not so to be.
									// Maybe we are doing some bizarre move into one of our own subfolders, and this
									// was already moved earlier in the main loop? Anyway don't move it, it's not just a settings folder.
									if (IsFieldWorksProjectFolder(subsubdir))
										continue;
									movedSomething = true;
									var leafName = Path.GetFileName(subsubdir);
									var subDirDest = Path.Combine(destDirName, leafName);
									Directory.CreateDirectory(destDirName); // may be redundant (will be after first time) but cheap.
									if (fCopy)
									{
										CopyDirectory(subsubdir, subDirDest);
										Directory.Delete(subsubdir, true);
									}
									else
									{
										Directory.Move(subsubdir, subDirDest);
									}
								}
								// If we moved something and left nothing behind delete the parent folder.
								// (We do a fresh GetDirectories just in case bizarrely we are moving INTO one of our own subfolders,
								// and thus even though we moved everything something is still there).
								if (movedSomething && Directory.GetDirectories(subdir).Length == 0)
									Directory.Delete(subdir);
								continue; // with more top-level directories in the projects folder.
							}
							progressDialog.Message = string.Format(Properties.Resources.ksMovingProject, sub);
							progressDialog.Step(1);
							if (fCopy)
							{
								// Recursively copy each subfolder.
								CopyDirectory(subdir, destDirName);
								Directory.Delete(subdir, true);
							}
							else
							{
								Directory.Move(subdir, destDirName);
							}
						}
						catch (Exception e)
						{
							rgErrors.Add(String.Format("{0} - {1}", Path.GetFileNameWithoutExtension(subdir), e.Message));
						}
					}
					return null;
				});
			}
			if (rgErrors.Count > 0)
			{
				// Show the user any errors that occured while doing the move
				StringBuilder bldr = new StringBuilder();
				bldr.AppendLine(Properties.Resources.ksCannotMoveProjects);
				foreach (var err in rgErrors)
					bldr.AppendLine(err);
				bldr.Append(Properties.Resources.ksYouCanTryToMoveProjects);
				MessageBox.Show(bldr.ToString(), Properties.Resources.ksProblemsMovingProjects);
			}
			if (MiscUtils.IsUnix)
			{
				if (projectPath.StartsWith(oldFolderForProjects))
				{
					// This is perhaps a temporary workaround.  On Linux, FwDirectoryFinder.ProjectsDirectory
					// isn't returning the updated value, but rather the original value.  This seems to
					// last for the duration of the program, but if you exit and restart the program, it
					// gets the correct (updated) value!?
					// (On the other hand, I somewhat prefer this code which is fairly straightforward and
					// obvious to depending on calling some static method hidden in the depths of FDO.)
					string projFileName = Path.GetFileName(projectPath);
					string projName = Path.GetFileNameWithoutExtension(projectPath);
					string path = Path.Combine(Path.Combine(newFolderForProjects, projName), projFileName);
					return new ProjectId(path, null);
				}
			}
			else
			{
				if (projectPath.StartsWith(oldFolderForProjects, StringComparison.InvariantCultureIgnoreCase))
				{
					return new ProjectId(ClientServerServices.Current.Local.IdForLocalProject(
						Path.GetFileNameWithoutExtension(projectPath)), null);
				}
			}
			return oldProjectId;
		}