/// <summary> /// Attempts to find the specified XML scenario description file, either at its original /// location or below <see cref="ScenarioFolder"/>.</summary> /// <param name="file"> /// An absolute or relative file path to an XML scenario description file.</param> /// <returns> /// A <see cref="RootedPath"/> wrapping the absolute path at which the specified <paramref /// name="file"/> was found.</returns> /// <exception cref="FileNotFoundException"> /// The specified scenario <paramref name="file"/> could not be found anywhere.</exception> /// <exception cref="ArgumentNullOrEmptyException"> /// <paramref name="file"/> is a null reference or an empty string.</exception> /// <remarks> /// <b>SearchScenarioTree</b> prepends <see cref="ScenarioFolder"/> to the specified /// <paramref name="file"/> if it contains a relative path. If no file exists at the /// resulting absolute path, <b>SearchScenarioTree</b> then attempts to find the file name /// anywhere in the subdirectory tree below <see cref="ScenarioFolder"/>.</remarks> public static RootedPath SearchScenarioTree(string file) { if (String.IsNullOrEmpty(file)) { ThrowHelper.ThrowArgumentNullOrEmptyException("file"); } // prepend default scenario path to relative paths RootedPath path = CreateCommonPath(ScenarioFolder, file); // check if file exists at specified location if (!File.Exists(path.AbsolutePath)) { // look for file name in scenario directory tree string search = IOUtility.SearchDirectoryTree(ScenarioFolder, path.FileName); path = path.Change(search); // file not found anywhere, abort operation if (path.IsEmpty) { ThrowHelper.ThrowFileNotFoundException(file, Strings.ErrorGameScenario); } } Debug.Assert(File.Exists(path.AbsolutePath)); return(path); }
/// <summary> /// Updates the "Subsection Locations" display for the specified <see /// cref="ScenarioSection"/>, which must be a subsection.</summary> /// <param name="section"> /// A <see cref="ScenarioSection"/> value indicating the scenario subsection to update. /// </param> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="section"/> equals <see cref="ScenarioSection.Master"/>.</exception> /// <exception cref="InvalidEnumArgumentException"> /// <paramref name="section"/> is not a valid <see cref="ScenarioSection"/> value. /// </exception> /// <remarks> /// <b>UpdatePath</b> updates the "Subsection Locations" line corresponding to the specified /// <paramref name="section"/> to reflect its <see cref="SectionTabItem.DataChanged"/> flag /// and its file path within the current <see cref="MasterSection"/>.</remarks> public void UpdateSubsection(ScenarioSection section) { switch (section) { case ScenarioSection.Master: ThrowHelper.ThrowArgumentOutOfRangeExceptionWithFormat("section", section, Tektosyne.Strings.ArgumentEquals, ScenarioSection.Master); break; case ScenarioSection.Images: case ScenarioSection.Variables: case ScenarioSection.Entities: case ScenarioSection.Factions: case ScenarioSection.Areas: // show or hide change marker for section int index = (int)section; var tabPage = MainWindow.Instance.GetTabPage(section); this._dataChangedLabels[index].Visibility = (tabPage.DataChanged ? Visibility.Visible : Visibility.Hidden); // show file path or "(inline)" for section RootedPath path = MasterSection.Instance.SectionPaths.GetPath(section); this._sectionPathBoxes[index].Text = (path.IsEmpty ? Global.Strings.LabelInline : path.RelativePath); break; default: ThrowHelper.ThrowInvalidEnumArgumentException( "section", (int)section, typeof(ScenarioSection)); break; } }
/// <summary> /// Shows a <see cref="FileDialogs.SaveSectionDialog"/> allowing the user to enter or select /// an <see cref="AreaSection"/> file to save the current <see cref="WorldState"/> to. /// </summary> /// <param name="file"> /// The file initially selected in the dialog.</param> /// <remarks><para> /// The specified <paramref name="file"/> may be a null reference or an empty string to /// indicate that no file should be initially selected. Otherwise, any directory prefix it /// contains overrides the specified <paramref name="file"/>. Files without an absolute path /// are interpreted as relative to the <see cref="ScenarioSection.Areas"/> section folder. /// </para><para> /// <b>SaveAreas</b> attempts to create a new <see cref="AreaSection"/> object from the <see /// cref="WorldState"/> of the current session, using the latter's <see /// cref="WorldState.CreateAreaSection"/> method, and then invokes <see /// cref="ScenarioElement.Save"/> on the new <see cref="AreaSection"/> object. If either /// operation fails, an error message is shown but no exception is thrown.</para></remarks> public static void SaveAreas(string file) { // abort if no world state to save if (Session.Instance == null) { return; } // let user select file path for Areas section RootedPath path = FilePaths.GetSectionFile(ScenarioSection.Areas, file); path = FileDialogs.SaveSectionDialog(ScenarioSection.Areas, path.AbsolutePath); if (path.IsEmpty) { return; } try { // create Areas section from world state AreaSection areas = Session.Instance.WorldState.CreateAreaSection(0, 0, 0, 0); areas.Save(path.AbsolutePath); } catch (Exception e) { string message = String.Format(ApplicationInfo.Culture, Global.Strings.DialogSectionSaveError, path.AbsolutePath); MessageDialog.Show(MainWindow.Instance, message, Global.Strings.TitleSectionError, e, MessageBoxButton.OK, Images.Error); } }
/// <summary> /// Handles the <see cref="ButtonBase.Click"/> event for the "Browse…" <see cref="Button"/>. /// </summary> /// <param name="sender"> /// The <see cref="Object"/> where the event handler is attached.</param> /// <param name="args"> /// An <see cref="EventArgs"/> or <see cref="RoutedEventArgs"/> object containing event /// data.</param> /// <remarks><para> /// <b>OnPathBrowse</b> automatically calls <see cref="Load"/> if the user specifies an /// existing file, and <see cref="Save"/> if the user specifies a non-existent file. /// </para><para> /// <b>OnPathBrowse</b> does nothing if the user cancels the dialog, or enters an empty path /// or the current <see cref="Path"/>.</para></remarks> private void OnPathBrowse(object sender, EventArgs args) { RoutedEventArgs routedArgs = args as RoutedEventArgs; if (routedArgs != null) { routedArgs.Handled = true; } // ask user to enter an existing or new file RootedPath path = FileDialogs.OpenSectionDialog(Section, Path, true); // quit if user cancelled or entered old path if (path == null || path.IsEmpty || path.Equals(this._path)) { return; } // save to or load from new path if (!File.Exists(path.AbsolutePath)) { Save(path.AbsolutePath); } else { // ask for confirmation to load file if (MainWindow.Instance.ClearData(Section)) { Load(path.AbsolutePath); } } }
/// <summary> /// Handles the <see cref="ButtonBase.Click"/> event for the "Browse…" <see cref="Button"/>. /// </summary> /// <param name="sender"> /// The <see cref="Object"/> where the event handler is attached.</param> /// <param name="args"> /// A <see cref="RoutedEventArgs"/> object containing event data.</param> /// <remarks> /// <b>OnPathBrowse</b> shows an <see cref="FileDialogs.OpenImageDialog"/> allowing the user /// to select a new <see cref="OverlayImage.Path"/>, and sets the <see cref="DataChanged"/> /// flag if the user selects a valid new file path.</remarks> private void OnPathBrowse(object sender, RoutedEventArgs args) { args.Handled = true; // ask user to enter an existing image RootedPath path = FileDialogs.OpenImageDialog(this._overlay.Path); // quit if user cancelled, or entered old or invalid path if (path == null || path.IsEmpty || path.Equals(this._overlay.Path) || !File.Exists(path.AbsolutePath)) { return; } // disable dialog while image loads IsEnabled = false; Dispatcher.DoEvents(); // try loading overlay image this._overlay.Path = path.AbsolutePath; if (!AreasTabContent.MapView.ShowOverlay(this, this._editing)) { path.Clear(); } // broadcast data changes ShowImagePath(); ShowImageSize(); DataChanged = true; IsEnabled = true; // reenable dialog }
public void Clone() { RootedPath path = new RootedPath(@"C:\Windows", "win.ini"); RootedPath clone = (RootedPath)path.Clone(); Assert.AreNotSame(path, clone); Assert.AreEqual(path, clone); Assert.AreEqual(@"C:\Windows\win.ini", clone.AbsolutePath); }
public void EquivalentPath() { RootedPath path = new RootedPath(@"C:\Windows\", @"C:/Windows"); Assert.AreEqual(@"C:\Windows\", path.AbsolutePath); Assert.AreEqual("", path.RelativePath); Assert.IsTrue(path.IsEmpty); Assert.AreEqual(@"C:\Windows", path.DirectoryName); Assert.AreEqual("", path.FileName); }
public void DifferentPath() { RootedPath path = new RootedPath(@"C:\Windows", @"C:\Temp\win.ini"); Assert.AreEqual(@"C:\Windows\", path.RootFolder); Assert.AreEqual(@"C:\Temp\win.ini", path.AbsolutePath); Assert.AreEqual(@"C:\Temp\win.ini", path.RelativePath); Assert.IsFalse(path.IsEmpty); Assert.AreEqual(@"C:\Temp", path.DirectoryName); Assert.AreEqual("win.ini", path.FileName); }
/// <summary> /// Handles the <see cref="ButtonBase.Click"/> event for the "Browse" <see cref="Button"/> /// next to the "Rule Script" text box.</summary> /// <param name="sender"> /// The <see cref="Object"/> where the event handler is attached.</param> /// <param name="args"> /// A <see cref="RoutedEventArgs"/> object containing event data.</param> /// <remarks><para> /// <b>OnRulesBrowse</b> displays an <see cref="FileDialogs.OpenRulesDialog"/> with the /// current contents of the "Rule Script" text box. /// </para><para> /// If the user selects a new path, <b>OnRulesBrowse</b> updates the "Rule Script" text box /// and the <see cref="RuleScript.Path"/> of the current <see cref="MasterSection.Rules"/>, /// and sets the <see cref="SectionTabItem.DataChanged"/> flag.</para></remarks> private void OnRulesBrowse(object sender, RoutedEventArgs args) { args.Handled = true; // ask user to select an existing file RootedPath path = FileDialogs.OpenRulesDialog(RulesPathBox.Text); // set new path (triggers OnRulesChanged) if (!path.IsEmpty) { RulesPathBox.Text = path.RelativePath; } }
public void Equals() { Assert.IsTrue(RootedPath.Equals(null, null)); RootedPath path = new RootedPath(@"C:\Windows", "win.ini"); Assert.AreEqual(new RootedPath(@"C:\Windows"), new RootedPath(@"C:\Windows")); Assert.AreEqual(path, new RootedPath(@"C:\Windows", "win.ini")); Assert.AreNotEqual(path, null); Assert.AreNotEqual(path, new RootedPath(@"C:\Temp", "win.ini")); Assert.AreNotEqual(path, new RootedPath(@"C:\Windows", "system.ini")); Assert.AreNotEqual(path, new RootedPath(@"C:\", @"Windows\win.ini")); }
/// <summary> /// Handles the <see cref="ButtonBase.Click"/> event for the "Add File" <see /// cref="Button"/>.</summary> /// <param name="sender"> /// The <see cref="Object"/> where the event handler is attached.</param> /// <param name="args"> /// A <see cref="RoutedEventArgs"/> object containing event data.</param> /// <remarks><para> /// <b>OnFileAdd</b> displays a <see cref="Dialog.ChangeIdentifier"/> dialog, followed by an /// <see cref="FileDialogs.OpenImageDialog"/>, allowing the user to define a new image file. /// </para><para> /// If the user confirmed both dialogs, <b>OnFileAdd</b> adds the new image file to the /// "Image File" list view and to the current <see cref="ImageSection"/>, redisplays all /// images, and sets the <see cref="SectionTabItem.DataChanged"/> flag.</para></remarks> private void OnFileAdd(object sender, RoutedEventArgs args) { args.Handled = true; // ask user for new image file ID var files = MasterSection.Instance.Images.ImageFiles; var dialog = new Dialog.ChangeIdentifier("file-id", Global.Strings.TitleImageFileIdEnter, files.ContainsKey, false); dialog.Owner = MainWindow.Instance; if (dialog.ShowDialog() != true) { return; } // retrieve new image file ID string id = String.Intern(dialog.Identifier); // ask user to select an existing image file RootedPath path = FileDialogs.OpenImageDialog(null); // return immediately if user cancelled if (path.IsEmpty) { return; } // construct new image file ImageFile file = new ImageFile(id, path.AbsolutePath); // try to load image file from disk if (!LoadImageFile(file, MasterSection.Instance.Images.MaskColor)) { return; } // add image file to section table files.Add(id, file); // update list view and select new item FileList.Items.Refresh(); FileList.SelectAndShow(file); // broadcast data changes ImageList.Redraw(); EnableListButtons(); SectionTab.DataChanged = true; }
/// <summary> /// Handles the <see cref="ButtonBase.Click"/> event for the "Change Path" <see /// cref="Button"/>.</summary> /// <param name="sender"> /// The <see cref="Object"/> where the event handler is attached.</param> /// <param name="args"> /// A <see cref="RoutedEventArgs"/> object containing event data.</param> /// <remarks><para> /// <b>OnFileChange</b> displays an <see cref="FileDialogs.OpenImageDialog"/> for the first /// selected item in the "Image File" list view. /// </para><para> /// If the user made any changes, <b>OnFileChange</b> propagates them to the current <see /// cref="ImageSection"/>, redisplays all class images, and sets the <see /// cref="SectionTabItem.DataChanged"/> flag.</para></remarks> private void OnFileChange(object sender, RoutedEventArgs args) { args.Handled = true; // retrieve selected image, if any ImageFile file = FileList.SelectedItem as ImageFile; if (file == null) { return; } // ask user to select an existing image file RootedPath path = FileDialogs.OpenImageDialog(file.Path); // return immediately if user cancelled if (path.IsEmpty) { return; } // construct new image file object ImageFile newFile = new ImageFile(file.Id, path.AbsolutePath); // try to load image file from disk var imageSection = MasterSection.Instance.Images; if (!LoadImageFile(newFile, imageSection.MaskColor)) { return; } // replace image file in scenario file.Unload(); Debug.Assert(file.Id == newFile.Id); imageSection.ImageFiles[newFile.Id] = newFile; // broadcast data changes FileList.Items.Refresh(); ImageList.Redraw(); SectionTab.DataChanged = true; }
/// <summary> /// Shows an <see cref="SaveFileDialog"/> allowing the user to enter or select a file to /// save.</summary> /// <param name="title"> /// The title bar text for the <see cref="SaveFileDialog"/>.</param> /// <param name="filter"> /// The file type filter for displayed files.</param> /// <param name="index"> /// The index of the initially selected file type filter.</param> /// <param name="extension"> /// The default extension for files entered by the user.</param> /// <param name="path"> /// A <see cref="RootedPath"/> wrapping the absolute file path initially selected in the /// dialog.</param> /// <param name="directory"> /// The directory initially shown in the dialog if <paramref name="path"/> is empty.</param> /// <returns> /// A <see cref="RootedPath"/> wrapping the absolute file path selected by the user, or an /// empty path if the user cancelled the dialog.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="path"/> is a null reference.</exception> /// <remarks> /// The specified <paramref name="path"/> may be empty to indicate that no file should be /// initially selected. In this case, the dialog initially shows the specified <paramref /// name="directory"/>, which is otherwise ignored.</remarks> private static RootedPath SaveDialog(string title, string filter, int index, string extension, RootedPath path, string directory) { if (path == null) { ThrowHelper.ThrowArgumentNullException("path"); } SaveFileDialog dialog = new SaveFileDialog(); dialog.DefaultExt = extension; dialog.Filter = filter; dialog.FilterIndex = index; dialog.InitialDirectory = directory ?? ""; dialog.RestoreDirectory = true; dialog.Title = title; dialog.ValidateNames = true; // pre-select specified file path, if any if (!path.IsEmpty) { dialog.InitialDirectory = path.DirectoryName; dialog.FileName = path.FileName; } // allow creation of new files dialog.CheckFileExists = false; dialog.CheckPathExists = false; // return empty path if user cancels var owner = System.Windows.Application.Current.MainWindow; if (dialog.ShowDialog(owner) != true) { return(path.Clear()); } // return selected file path return(path.Change(dialog.FileName)); }
/// <summary> /// Handles the <see cref="Application.DispatcherUnhandledException"/> event.</summary> /// <param name="information"> /// Information about the <see cref="Exception"/> and the current application state, or a /// null reference for no available information.</param> /// <param name="rulesFile"> /// The file path to the current scenario rule script, or a null reference if no such file /// exists.</param> /// <param name="saveScenario"> /// An optional <see cref="Action{String}"/> delegate that is invoked to save the current /// scenario data to an XML file.</param> /// <param name="saveSession"> /// An optional <see cref="Action{String}"/> delegate that is invoked to save the current /// session data to an XML file.</param> /// <remarks><para> /// <b>OnUnhandledException</b> creates a text file describing the error which includes the /// specified <paramref name="information"/>. Additionally, up to two debug XML files /// containing scenario and session data are created by invoking any of the supplied <see /// cref="Action{String}"/> delegates that are not null references. /// </para><para> /// The user is also asked to e-mail all created files, plus the specified <paramref /// name="rulesFile"/> if it exists, to the e-mail address specified by the <see /// cref="ApplicationInfo.Email"/> property via Simple MAPI, or if this fails, to send basic /// error information to the same address using the Explorer "mailto" method. /// </para><para> /// Finally, <b>OnUnhandledException</b> terminates the application using <see /// cref="Environment.Exit"/> and returns an error code of -1.</para></remarks> public static void OnUnhandledException(string information, string rulesFile, Action <String> saveScenario, Action <String> saveSession) { // check for original distribution package bool original = ApplicationInfo.IsSigned; // determine error and debug file names string errorFile = FilePaths.CreateUserPath("FatalError.txt").AbsolutePath; string scenarioFile = FilePaths.GetScenarioFile(ScenarioFileType.Debug).AbsolutePath; string sessionFile = FilePaths.GetSessionFile(SessionFileType.Debug).AbsolutePath; // create an error e-mail? bool wantEmail = false; if (original) { // ask user to create e-mail with attachments string message = String.Format(ApplicationInfo.Culture, Strings.DialogFatalErrorOriginal, errorFile, scenarioFile, sessionFile); wantEmail = ShowFatalError(message, null, true); } else { // just notify user that files are being saved string message = String.Format(ApplicationInfo.Culture, Strings.DialogFatalErrorModified, errorFile, scenarioFile, sessionFile); ShowFatalError(message, null, false); } // create state information string StringBuilder info = new StringBuilder(); info.AppendFormat("{0} ", ApplicationInfo.Signature); info.Append(Environment.NewLine); info.AppendFormat("Kynosarges Signature: {0}", original); info.Append(Environment.NewLine); info.AppendFormat("Public Key Token: {0}", StringUtility.Validate(ApplicationInfo.PublicKeyToken)); info.Append(Environment.NewLine); info.AppendFormat("Home: \"{0}\" ", FilePaths.ApplicationFolder); info.Append(Environment.NewLine); info.Append(Environment.NewLine); info.AppendFormat("{0} ", Environment.OSVersion); info.Append(Environment.NewLine); info.AppendFormat("{0} ", WindowsUtility.GetMemoryStatus()); info.Append(Environment.NewLine); info.Append(Environment.NewLine); // append additional information if specified if (!String.IsNullOrEmpty(information)) { info.AppendFormat("{0} ", information); info.Append(Environment.NewLine); } // create subject for e-mail message string subject = String.Concat(ApplicationInfo.Signature, " ", Strings.LabelError); // collection to hold file attachment data List <MapiAddress> files = new List <MapiAddress>(); try { try { // always create error information file files.Add(new MapiAddress(Path.GetFileName(errorFile), errorFile)); using (StreamWriter writer = new StreamWriter(errorFile, false, Encoding.UTF8)) writer.WriteLine(info.ToString()); // attach rule script file if specified RootedPath rulesPath = FilePaths.CreateCommonPath(rulesFile); if (!rulesPath.IsEmpty && File.Exists(rulesPath.AbsolutePath)) { files.Add(new MapiAddress(rulesPath.FileName, rulesPath.AbsolutePath)); } // create scenario debug file if possible if (saveScenario != null) { saveScenario(scenarioFile); files.Add(new MapiAddress(Path.GetFileName(scenarioFile), scenarioFile)); } // create session debug file if possible if (saveSession != null) { saveSession(sessionFile); files.Add(new MapiAddress(Path.GetFileName(sessionFile), sessionFile)); } } catch (Exception e) { // only proceed if user wanted e-mail if (wantEmail) { // ask user to try simpler e-mail format wantEmail = ShowFatalError(Strings.DialogFatalErrorSaveMail, e, true); throw; // rethrow to try mailto: } // user declined e-mail, just show error ShowFatalError(Strings.DialogFatalErrorSave, e, false); return; // quit, nothing else to do } // quit now if user declined e-mail if (!wantEmail) { return; } try { // address e-mail to application author MapiAddress recipient = new MapiAddress( ApplicationInfo.Company, ApplicationInfo.Email); // try sending e-mail with attached files MapiMail.SendMail(subject, "", new[] { recipient }, files.ToArray()); } catch (Exception e) { // quit if user cancelled MAPI session MapiException me = e as MapiException; if (me != null && me.Code == MapiException.Abort) { return; } // ask user to try simpler e-mail format wantEmail = ShowFatalError(Strings.DialogFatalErrorMail, e, true); throw; // rethrow to try mailto: } } catch { // quit now if user declined e-mail if (!wantEmail) { return; } // construct shell command to create e-mail string mailto = String.Format(CultureInfo.InvariantCulture, "mailto:{0}?subject={1}&body={2}", ApplicationInfo.Email, subject, info); try { // request default e-mail client Process.Start(mailto); } catch (Exception e) { ShowExplorerError(null, Strings.DialogExplorerMailError, e); } } finally { // quit application upon return Environment.Exit(-1); } }
public void ConstructorFailed() { RootedPath path = new RootedPath("Windows", "win.ini"); }