/* * Copyright 2019 faddenSoft * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using CommonUtil; using Microsoft.Win32; namespace SourceGen.WpfGui { /// /// Export selection dialog. /// public partial class Export : Window, INotifyPropertyChanged { /// /// Indicates which type of file the user wants to generate. /// public enum GenerateFileType { Unknown = 0, Text, Html } /// /// Result: type of file to generate. /// public GenerateFileType GenType { get; private set; } /// /// Result: full pathname of output file. /// public string PathName { get; private set; } /// /// Result: flags indicating which of the optional columns should be shown. /// public Exporter.ActiveColumnFlags ColFlags { get; private set; } private bool mIncludeNotes; public bool IncludeNotes { get { return mIncludeNotes; } set { mIncludeNotes = value; OnPropertyChanged(); } } private bool mShowOffset; public bool ShowOffset { get { return mShowOffset; } set { mShowOffset = value; OnPropertyChanged(); } } private bool mShowAddress; public bool ShowAddress { get { return mShowAddress; } set { mShowAddress = value; OnPropertyChanged(); } } private bool mShowBytes; public bool ShowBytes { get { return mShowBytes; } set { mShowBytes = value; OnPropertyChanged(); } } private bool mShowFlags; public bool ShowFlags { get { return mShowFlags; } set { mShowFlags = value; OnPropertyChanged(); } } private bool mShowAttrs; public bool ShowAttr { get { return mShowAttrs; } set { mShowAttrs = value; OnPropertyChanged(); } } private bool mSelectionOnly; public bool SelectionOnly { get { return mSelectionOnly; } set { mSelectionOnly = value; OnPropertyChanged(); } } private bool mLongLabelNewLine; public bool LongLabelNewLine { get { return mLongLabelNewLine; } set { mLongLabelNewLine = value; OnPropertyChanged(); } } // // Numeric input fields, bound directly to TextBox.Text. These rely on a TextChanged // field to update the IsValid flag, because the "set" method is only called when the // field contains a valid integer. // private int mAsmLabelColWidth; public int AsmLabelColWidth { get { return mAsmLabelColWidth; } set { if (mAsmLabelColWidth != value) { mAsmLabelColWidth = value; OnPropertyChanged(); } } } private int mAsmOpcodeColWidth; public int AsmOpcodeColWidth { get { return mAsmOpcodeColWidth; } set { if (mAsmOpcodeColWidth != value) { mAsmOpcodeColWidth = value; OnPropertyChanged(); } } } private int mAsmOperandColWidth; public int AsmOperandColWidth { get { return mAsmOperandColWidth; } set { if (mAsmOperandColWidth != value) { mAsmOperandColWidth = value; OnPropertyChanged(); } } } private int mAsmCommentColWidth; public int AsmCommentColWidth { get { return mAsmCommentColWidth; } set { if (mAsmCommentColWidth != value) { mAsmCommentColWidth = value; OnPropertyChanged(); } } } private bool mTextModePlain; public bool TextModePlain { get { return mTextModePlain; } set { mTextModePlain = value; OnPropertyChanged(); } } private bool mTextModeCsv; public bool TextModeCsv { get { return mTextModeCsv; } set { mTextModeCsv = value; OnPropertyChanged(); } } private bool mOverwriteCss; public bool OverwriteCss { get { return mOverwriteCss; } set { mOverwriteCss = value; OnPropertyChanged(); } } private enum TextMode { Unknown = 0, PlainText, Csv } /// /// Valid flag, used to enable the "generate" buttons. /// public bool IsValid { get { return mIsValid; } set { mIsValid = value; OnPropertyChanged(); } } private bool mIsValid; // INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } /// /// Filename of project file. /// string mProjectFileName; public Export(Window owner, string projectFileName) { InitializeComponent(); Owner = owner; DataContext = this; mProjectFileName = projectFileName; } private void Window_Loaded(object sender, RoutedEventArgs e) { IncludeNotes = AppSettings.Global.GetBool(AppSettings.EXPORT_INCLUDE_NOTES, false); ShowOffset = AppSettings.Global.GetBool(AppSettings.EXPORT_SHOW_OFFSET, false); ShowAddress = AppSettings.Global.GetBool(AppSettings.EXPORT_SHOW_ADDR, false); ShowBytes = AppSettings.Global.GetBool(AppSettings.EXPORT_SHOW_BYTES, false); ShowFlags = AppSettings.Global.GetBool(AppSettings.EXPORT_SHOW_FLAGS, false); ShowAttr = AppSettings.Global.GetBool(AppSettings.EXPORT_SHOW_ATTR, false); SelectionOnly = AppSettings.Global.GetBool(AppSettings.EXPORT_SELECTION_ONLY, false); LongLabelNewLine = AppSettings.Global.GetBool(AppSettings.EXPORT_LONG_LABEL_NEW_LINE, false); int[] colWidths = new int[] { 9, 8, 11, 72 }; // 100-col output string colStr = AppSettings.Global.GetString(AppSettings.EXPORT_COL_WIDTHS, null); try { if (!string.IsNullOrEmpty(colStr)) { colWidths = TextUtil.DeserializeIntArray(colStr); if (colWidths.Length != 4) { throw new InvalidOperationException("Bad length " + colWidths.Length); } } } finally { AsmLabelColWidth = colWidths[0]; AsmOpcodeColWidth = colWidths[1]; AsmOperandColWidth = colWidths[2]; AsmCommentColWidth = colWidths[3]; } TextMode mode = (TextMode)AppSettings.Global.GetEnum(AppSettings.EXPORT_TEXT_MODE, typeof(TextMode), (int)TextMode.PlainText); if (mode == TextMode.PlainText) { TextModePlain = true; } else { TextModeCsv = true; } } /// /// Saves the settings to the global settings object. /// private void SaveSettings() { AppSettings.Global.SetBool(AppSettings.EXPORT_INCLUDE_NOTES, IncludeNotes); AppSettings.Global.SetBool(AppSettings.EXPORT_SHOW_OFFSET, ShowOffset); AppSettings.Global.SetBool(AppSettings.EXPORT_SHOW_ADDR, ShowAddress); AppSettings.Global.SetBool(AppSettings.EXPORT_SHOW_BYTES, ShowBytes); AppSettings.Global.SetBool(AppSettings.EXPORT_SHOW_FLAGS, ShowFlags); AppSettings.Global.SetBool(AppSettings.EXPORT_SHOW_ATTR, ShowAttr); AppSettings.Global.SetBool(AppSettings.EXPORT_SELECTION_ONLY, SelectionOnly); AppSettings.Global.SetBool(AppSettings.EXPORT_LONG_LABEL_NEW_LINE, LongLabelNewLine); int[] colWidths = new int[] { AsmLabelColWidth, AsmOpcodeColWidth, AsmOperandColWidth, AsmCommentColWidth }; string cereal = TextUtil.SerializeIntArray(colWidths); AppSettings.Global.SetString(AppSettings.EXPORT_COL_WIDTHS, cereal); // OverwriteCss is not saved, since there's generally no reason to replace it. // Forcing the user to check it every time is essentially the same as popping // up an "are you sure you want to overwrite" dialog, but less annoying for the // common case. TextMode mode; if (TextModePlain) { mode = TextMode.PlainText; } else { mode = TextMode.Csv; } AppSettings.Global.SetEnum(AppSettings.EXPORT_TEXT_MODE, typeof(TextMode), (int)mode); } /// /// Updates the state of the UI. /// private void UpdateControls() { bool isValid = true; isValid &= !Validation.GetHasError(asmLabelColWidthTextBox); isValid &= !Validation.GetHasError(asmOpcodeColWidthTextBox); isValid &= !Validation.GetHasError(asmOperandColWidthTextBox); isValid &= !Validation.GetHasError(asmCommentColWidthTextBox); IsValid = isValid; } /// /// Called whenever something is typed in one of the column width entry boxes. /// /// /// We need this because we're using validated int fields rather than strings. The /// "set" call doesn't fire if the user types garbage. /// private void AsmColWidthTextBox_TextChanged(object sender, TextChangedEventArgs e) { UpdateControls(); } private void GenerateHtmlButton_Click(object sender, RoutedEventArgs e) { GenType = GenerateFileType.Html; Finish(Res.Strings.FILE_FILTER_HTML, ".html"); } private void GenerateTextButton_Click(object sender, RoutedEventArgs e) { GenType = GenerateFileType.Text; if (TextModeCsv) { Finish(Res.Strings.FILE_FILTER_CSV, ".csv"); } else { Finish(Res.Strings.FILE_FILTER_TEXT, ".txt"); } } /// /// Handles a click on one of the "generate" buttons. /// private void Finish(string fileFilter, string fileExt) { Debug.Assert(mProjectFileName == Path.GetFileName(mProjectFileName)); string initialName = Path.GetFileNameWithoutExtension(mProjectFileName) + fileExt; if (GenType == GenerateFileType.Html) { // Can't link to a file with an unescaped '#' -- the browser will think // it's an anchor. initialName = initialName.Replace('#', '_'); } SaveFileDialog fileDlg = new SaveFileDialog() { Filter = fileFilter + "|" + Res.Strings.FILE_FILTER_ALL, FilterIndex = 1, ValidateNames = true, AddExtension = true, // doesn't add extension if non-ext file exists FileName = initialName }; if (fileDlg.ShowDialog() != true) { return; } PathName = Path.GetFullPath(fileDlg.FileName); ColFlags = Exporter.ActiveColumnFlags.None; if (ShowOffset) { ColFlags |= Exporter.ActiveColumnFlags.Offset; } if (ShowAddress) { ColFlags |= Exporter.ActiveColumnFlags.Address; } if (ShowBytes) { ColFlags |= Exporter.ActiveColumnFlags.Bytes; } if (ShowFlags) { ColFlags |= Exporter.ActiveColumnFlags.Flags; } if (ShowAttr) { ColFlags |= Exporter.ActiveColumnFlags.Attr; } SaveSettings(); DialogResult = true; } } }