/* * 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.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using Asm65; using PluginCommon; namespace SourceGen.WpfGui { /// /// Visualization set editor. /// public partial class EditVisualizationSet : Window, INotifyPropertyChanged { /// /// Modified visualization set. Only valid after OK is hit. /// public VisualizationSet NewVisSet { get; private set; } /// /// List of Visualization serial numbers that were removed from the set. The caller /// can use this to update animations in other sets that referred to the removed items. /// /// /// We have to use serial numbers because the user might have edited the Visualization /// before removing it. /// public List RemovedSerials { get; private set; } private DisasmProject mProject; private Formatter mFormatter; private VisualizationSet mOrigSet; private int mOffset; /// /// ItemsSource for visualizationGrid. /// public ObservableCollection VisualizationList { get; private set; } = new ObservableCollection(); /// /// True if there are plugins that implement the visualization generation interface. /// public bool HasVisPlugins { get { return mHasVisPlugins; } set { mHasVisPlugins = value; OnPropertyChanged(); } } private bool mHasVisPlugins; public Visibility ScriptWarningVisible { get { return mHasVisPlugins ? Visibility.Collapsed : Visibility.Visible; } // this can't change while the dialog is open, so don't need OnPropertyChanged } // // Every action causes a selection change, so we don't explicitly call an "update // controls" function. // public bool IsEditEnabled { get { return mIsEditEnabled; } set { mIsEditEnabled = value; OnPropertyChanged(); } } private bool mIsEditEnabled; public bool IsRemoveEnabled { get { return mIsRemoveEnabled; } set { mIsRemoveEnabled = value; OnPropertyChanged(); } } private bool mIsRemoveEnabled; public bool IsUpEnabled { get { return mIsUpEnabled; } set { mIsUpEnabled = value; OnPropertyChanged(); } } private bool mIsUpEnabled; public bool IsDownEnabled { get { return mIsDownEnabled; } set { mIsDownEnabled = value; OnPropertyChanged(); } } private bool mIsDownEnabled; // INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public EditVisualizationSet(Window owner, DisasmProject project, Formatter formatter, VisualizationSet curSet, int offset) { InitializeComponent(); Owner = owner; DataContext = this; mProject = project; mFormatter = formatter; mOrigSet = curSet; mOffset = offset; RemovedSerials = new List(); if (curSet != null) { // Populate the data grid ItemsSource. foreach (Visualization vis in curSet) { VisualizationList.Add(vis); } } if (VisualizationList.Count > 0) { visualizationGrid.SelectedIndex = 0; } // Check to see if we have any relevant plugins. If not, disable New/Edit. Dictionary plugins = project.GetActivePlugins(); foreach (IPlugin chkPlug in plugins.Values) { if (chkPlug is IPlugin_Visualizer) { HasVisPlugins = true; break; } } } private void OkButton_Click(object sender, RoutedEventArgs e) { NewVisSet = MakeVisSet(); DialogResult = true; } private void Window_Closing(object sender, CancelEventArgs e) { if (DialogResult == true) { return; } // Check to see if changes have been made. VisualizationSet newSet; if (VisualizationList.Count == 0) { newSet = null; } else { newSet = MakeVisSet(); } if (newSet != mOrigSet) { string msg = (string)FindResource("str_ConfirmDiscardChanges"); string caption = (string)FindResource("str_ConfirmDiscardChangesCaption"); MessageBoxResult result = MessageBox.Show(msg, caption, MessageBoxButton.OKCancel, MessageBoxImage.Question); if (result == MessageBoxResult.Cancel) { e.Cancel = true; } } } private void VisualizationList_SelectionChanged(object sender, SelectionChangedEventArgs e) { bool isItemSelected = (visualizationGrid.SelectedItem != null); IsEditEnabled = HasVisPlugins && isItemSelected; IsRemoveEnabled = isItemSelected; IsUpEnabled = isItemSelected && visualizationGrid.SelectedIndex != 0; IsDownEnabled = isItemSelected && visualizationGrid.SelectedIndex != VisualizationList.Count - 1; } private void VisualizationList_MouseDoubleClick(object sender, MouseButtonEventArgs e) { EditSelectedItem(); } private void NewVisualizationButton_Click(object sender, RoutedEventArgs e) { EditVisualization dlg = new EditVisualization(this, mProject, mFormatter, mOffset, CreateEditedSetList(), null); if (dlg.ShowDialog() != true) { return; } VisualizationList.Add(dlg.NewVis); visualizationGrid.SelectedIndex = VisualizationList.Count - 1; okButton.Focus(); } private void NewWireframeAnimationButton_Click(object sender, RoutedEventArgs e) { // TODO(xyzzy) } private void NewBitmapAnimationButton_Click(object sender, RoutedEventArgs e) { EditBitmapAnimation dlg = new EditBitmapAnimation(this, mOffset, CreateEditedSetList(), null); if (dlg.ShowDialog() != true) { return; } VisualizationList.Add(dlg.NewAnim); visualizationGrid.SelectedIndex = VisualizationList.Count - 1; okButton.Focus(); } private void EditButton_Click(object sender, RoutedEventArgs e) { EditSelectedItem(); } private void EditSelectedItem() { if (!IsEditEnabled) { // can get called here by a double-click return; } Visualization item = (Visualization)visualizationGrid.SelectedItem; Visualization newVis; if (item is VisBitmapAnimation) { EditBitmapAnimation dlg = new EditBitmapAnimation(this, mOffset, CreateEditedSetList(), (VisBitmapAnimation)item); if (dlg.ShowDialog() != true) { return; } newVis = dlg.NewAnim; } else { EditVisualization dlg = new EditVisualization(this, mProject, mFormatter, mOffset, CreateEditedSetList(), item); if (dlg.ShowDialog() != true) { return; } newVis = dlg.NewVis; } int index = VisualizationList.IndexOf(item); VisualizationList.Remove(item); VisualizationList.Insert(index, newVis); visualizationGrid.SelectedIndex = index; okButton.Focus(); } private void RemoveButton_Click(object sender, RoutedEventArgs e) { Visualization item = (Visualization)visualizationGrid.SelectedItem; int index = visualizationGrid.SelectedIndex; VisualizationList.RemoveAt(index); // Keep selection at same index, unless we just removed the item at the end. if (index == VisualizationList.Count) { index--; } if (index >= 0) { visualizationGrid.SelectedIndex = index; } RemovedSerials.Add(item.SerialNumber); // Update any animations in this set. Animations in other sets will be updated later. // (This is a bit awkward because we can't modify VisualizationList while iterating // through it, and there's no simple "replace entry" operation on an observable // collection. Fortunately we don't do this often and the data sets are small.) List needsUpdate = new List(); foreach (Visualization vis in VisualizationList) { if (vis is VisBitmapAnimation) { VisBitmapAnimation visAnim = (VisBitmapAnimation)vis; if (visAnim.ContainsSerial(item.SerialNumber)) { needsUpdate.Add(visAnim); } } } foreach (VisBitmapAnimation visAnim in needsUpdate) { VisBitmapAnimation newAnim; if (VisBitmapAnimation.StripEntries(visAnim, new List(1) { item.SerialNumber }, out newAnim)) { if (newAnim.Count == 0) { VisualizationList.Remove(visAnim); } else { newAnim.GenerateImage(CreateEditedSetList()); index = VisualizationList.IndexOf(visAnim); VisualizationList.Remove(visAnim); VisualizationList.Insert(index, newAnim); } } } } private void UpButton_Click(object sender, RoutedEventArgs e) { Visualization item = (Visualization)visualizationGrid.SelectedItem; int index = visualizationGrid.SelectedIndex; Debug.Assert(index > 0); VisualizationList.RemoveAt(index); VisualizationList.Insert(index - 1, item); visualizationGrid.SelectedIndex = index - 1; visualizationGrid.ScrollIntoView(item); } private void DownButton_Click(object sender, RoutedEventArgs e) { Visualization item = (Visualization)visualizationGrid.SelectedItem; int index = visualizationGrid.SelectedIndex; Debug.Assert(index >= 0 && index < VisualizationList.Count - 1); VisualizationList.RemoveAt(index); VisualizationList.Insert(index + 1, item); visualizationGrid.SelectedIndex = index + 1; visualizationGrid.ScrollIntoView(item); } /// /// Creates a VisualizationSet from the current list of Visualizations. /// /// New VisualizationSet. private VisualizationSet MakeVisSet() { VisualizationSet newSet = new VisualizationSet(VisualizationList.Count); foreach (Visualization vis in VisualizationList) { newSet.Add(vis); } return newSet; } /// /// Generates a list of VisualizationSet references. This is the list from the /// DisasmProject, but with the set we're editing added or substituted. /// /// /// The editors sometimes need access to the full collection of Visualization objects, /// such as when testing a tag for uniqueness or getting a list of all bitmap /// frames for an animation. The editor needs access to recent edits that have not /// been pushed to the project yet. /// /// List of VisualizationSet. private SortedList CreateEditedSetList() { SortedList mixList = new SortedList(mProject.VisualizationSets.Count); mixList[mOffset] = MakeVisSet(); foreach (KeyValuePair kvp in mProject.VisualizationSets) { // Skip the entry for mOffset (if it exists). if (kvp.Key != mOffset) { mixList[kvp.Key] = kvp.Value; } } return mixList; } /// /// Finds a Visualization with a matching tag, searching across all sets in the /// edited list. /// /// Tag to search for. /// Matching Visualization, or null if not found. public static Visualization FindVisualizationByTag(SortedList list, string tag) { foreach (KeyValuePair kvp in list) { foreach (Visualization vis in kvp.Value) { if (vis.Tag == tag) { return vis; } } } return null; } } }