private void MainTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs <object> e) { if (sender is TreeView tree && tree.SelectedItem is ProjectTreeItemModel itemModel) { ProjectTreeItemModel item = Model.CurMainTree = itemModel; Dispatcher.Invoke(() => { Stopwatch watch = new Stopwatch(); watch.Start(); Model.DisplayScript(item.Script); watch.Stop(); double msec = watch.Elapsed.TotalMilliseconds; string filename = Path.GetFileName(Model.CurMainTree.Script.TreePath); Model.StatusBarText = $"{filename} rendered ({msec:0}ms)"; }); } }
private void DirectoryCollapseTreeCommand_Executed(object sender, ExecutedRoutedEventArgs e) { ProjectTreeItemModel selectedItem = Model.CurMainTree; selectedItem.IsExpanded = false; Queue <ProjectTreeItemModel> q = new Queue <ProjectTreeItemModel>(selectedItem.Children.Where(x => x.Script.Type == ScriptType.Directory)); while (0 < q.Count) { ProjectTreeItemModel dirItem = q.Dequeue(); dirItem.IsExpanded = false; foreach (ProjectTreeItemModel subItem in dirItem.Children.Where(x => x.Script.Type == ScriptType.Directory)) { q.Enqueue(subItem); } } }
private async void ProjectBuildStartCommand_Executed(object sender, ExecutedRoutedEventArgs e) { // Force update of script interface ProjectBuildStartButton.Focus(); if (!Engine.TryEnterLock()) { return; } try { // Get current project Project p = Model.CurMainTree.Script.Project; Model.BuildTreeItems.Clear(); ProjectTreeItemModel treeRoot = MainViewModel.PopulateOneTreeItem(p.MainScript, null, null); MainViewModel.ScriptListToTreeViewModel(p, p.ActiveScripts, false, treeRoot); Model.BuildTreeItems.Add(treeRoot); Model.CurBuildTree = null; EngineState s = new EngineState(p, Logger, Model); s.SetOptions(Global.Setting); s.SetCompat(p.Compat); Engine.WorkingEngine = new Engine(s); // Build Start, Switch to Build View Model.SwitchNormalBuildInterface = false; // Turn on progress ring Model.WorkInProgress = true; // Set StatusBar Text using (CancellationTokenSource ct = new CancellationTokenSource()) { Task printStatus = MainViewModel.PrintBuildElapsedStatus($"Building {p.ProjectName}...", s, ct.Token); // Run int buildId = await Engine.WorkingEngine.Run($"Project {p.ProjectName}"); #if DEBUG Logger.ExportBuildLog(LogExportType.Text, Path.Combine(s.BaseDir, "LogDebugDump.txt"), buildId, new BuildLogOptions { IncludeComments = true, IncludeMacros = true, ShowLogFlags = true, }); #endif // Cancel and wait until PrintBuildElapsedStatus stops ct.Cancel(); await printStatus; } // Turn off progress ring Model.WorkInProgress = false; // Build ended, Switch to Normal View Model.SwitchNormalBuildInterface = true; Model.BuildTreeItems.Clear(); Model.DisplayScript(Model.CurMainTree.Script); // Report elapsed time string reason = s.RunResultReport(); if (reason != null) { Model.StatusBarText = $"{p.ProjectName} build stopped by {reason}. ({s.Elapsed:h\\:mm\\:ss})"; } else { Model.StatusBarText = $"{p.ProjectName} build finished. ({s.Elapsed:h\\:mm\\:ss})"; } if (Global.Setting.General.ShowLogAfterBuild && LogWindow.Count == 0) { // Open BuildLogWindow LogDialog = new LogWindow(1); LogDialog.Show(); } } finally { Engine.WorkingEngine = null; Engine.ExitLock(); CommandManager.InvalidateRequerySuggested(); } }
private async void CreateScriptMetaFilesCommand_Executed(object sender, ExecutedRoutedEventArgs e) { // Force update of script interface controls (if changed) ScriptUpdateButton.Focus(); // Must be filtered by ScriptCommand_CanExecute before if (Model.WorkInProgress) { Global.Logger.SystemWrite(new LogInfo(LogState.CriticalError, $"Race condition with {nameof(Model.WorkInProgress)} happened in {nameof(ScriptUpdateCommand_Executed)}")); return; } // Get instances of Script and Project Script targetScript = Model.CurMainTree.Script; Project p = Model.CurMainTree.Script.Project; // Define local variables Script[] targetScripts; List <LogInfo> logs = new List <LogInfo>(); // Turn on progress ring Model.WorkInProgress = true; int successCount = 0; int errorCount = 0; try { // Populate BuildTree ProjectTreeItemModel treeRoot = MainViewModel.PopulateOneTreeItem(targetScript, null, null); Model.BuildTreeItems.Clear(); if (targetScript.Type == ScriptType.Directory || targetScript.IsMainScript) { // Update a list of scripts // We have to search in p.AllScripts rather than in ProjectTreeItemModel to find hidden scripts // (ProjectTreeItemModel only contains visible scripts) if (targetScript.IsMainScript) { targetScripts = p.AllScripts.ToArray(); } else { targetScripts = p.AllScripts .Where(x => x.TreePath.StartsWith(targetScript.TreePath, StringComparison.OrdinalIgnoreCase)) .ToArray(); } MainViewModel.ScriptListToTreeViewModel(p, targetScripts, false, treeRoot); targetScripts = targetScripts.Where(x => x.Type != ScriptType.Directory).ToArray(); if (targetScripts.Length == 0) { // Ask user for confirmation MessageBox.Show(this, $"Directory [{targetScript.Title}] does not contain any scripts.", "No child scripts", MessageBoxButton.OK, MessageBoxImage.Warning); return; } } else { targetScripts = new Script[] { targetScript }; } Model.BuildTreeItems.Add(treeRoot); Model.CurBuildTree = null; // Switch to Build View Model.BuildScriptProgressVisibility = Visibility.Collapsed; Model.BuildFullProgressMax = targetScripts.Length; Model.BuildFullProgressValue = 0; Model.SwitchNormalBuildInterface = false; // I do not know why, but this line must come after SwitchNormalBuildInterface. Model.BuildEchoMessage = "Creating meta files..."; Stopwatch watch = Stopwatch.StartNew(); // Run Updater int idx = 0; foreach (Script sc in targetScripts) { // Display script information idx += 1; Model.BuildFullProgressValue = idx; Model.DisplayScriptTexts(sc, null); Model.ScriptTitleText = Model.ScriptTitleText; Model.BuildEchoMessage = $"Creating meta files... ({idx * 100 / targetScripts.Length}%)"; Application.Current?.Dispatcher?.BeginInvoke((Action)(() => { Model.DisplayScriptLogo(sc); // BuildTree is empty -> return if (Model.BuildTreeItems.Count == 0) { return; } if (Model.CurBuildTree != null) { Model.CurBuildTree.Focus = false; } Model.CurBuildTree = ProjectTreeItemModel.FindScriptByRealPath(Model.BuildTreeItems[0], sc.RealPath); if (Model.CurBuildTree != null) { Model.CurBuildTree.Focus = true; } })); // Do the real job string destJsonFile = Path.ChangeExtension(sc.RealPath, ".meta.json"); try { await UpdateJson.CreateScriptUpdateJsonAsync(sc, destJsonFile); logs.Add(new LogInfo(LogState.Success, $"Created meta file for [{sc.Title}]")); successCount += 1; } catch (Exception ex) { logs.Add(new LogInfo(LogState.Error, $"Unable to create meta file for [{sc.Title}] - {Logger.LogExceptionMessage(ex)}")); errorCount += 1; } } // Log messages Logger.SystemWrite(logs); watch.Stop(); TimeSpan t = watch.Elapsed; Model.StatusBarText = $"Updated {targetScript.Title} ({t:h\\:mm\\:ss})"; } finally { // Turn off progress ring Model.WorkInProgress = false; // Build Ended, Switch to Normal View Model.BuildScriptProgressVisibility = Visibility.Visible; Model.BuildEchoMessage = string.Empty; Model.SwitchNormalBuildInterface = true; Model.DisplayScript(Model.CurMainTree.Script); } PackIconMaterialKind msgBoxIcon = PackIconMaterialKind.Information; StringBuilder b = new StringBuilder(targetScripts.Length + 4); b.AppendLine($"Created [{successCount}] script meta files."); foreach (LogInfo log in logs.Where(x => x.State == LogState.Success)) { b.AppendLine($"- {log.Message}"); } if (0 < errorCount) { // Failure b.AppendLine(); b.AppendLine($"Failed to create [{errorCount}] script meta files"); foreach (LogInfo log in logs.Where(x => x.State == LogState.Error)) { b.AppendLine($"- {log.Message}"); } msgBoxIcon = PackIconMaterialKind.Alert; } const string msgTitle = "Script Meta Files Report"; TextViewDialog dialog = new TextViewDialog(this, msgTitle, msgTitle, b.ToString(), msgBoxIcon); dialog.ShowDialog(); }
private async void ScriptUpdateCommand_Executed(object sender, ExecutedRoutedEventArgs e) { // Force update of script interface controls (if changed) ScriptUpdateButton.Focus(); // Must be filtered by ScriptCommand_CanExecute before if (Model.WorkInProgress) { Global.Logger.SystemWrite(new LogInfo(LogState.CriticalError, $"Race condition with {nameof(Model.WorkInProgress)} happened in {nameof(ScriptUpdateCommand_Executed)}")); return; } // Get instances of Script and Project Script targetScript = Model.CurMainTree.Script; Project p = Model.CurMainTree.Script.Project; // Do not apply updateMultipleScript to MainScript, because users should use project update for this job. bool updateMultipleScript = targetScript.Type == ScriptType.Directory; if (!updateMultipleScript && !targetScript.IsUpdateable) { Global.Logger.SystemWrite(new LogInfo(LogState.CriticalError, $"Race condition with {nameof(Script.IsUpdateable)} happened in {nameof(ScriptUpdateCommand_Executed)}")); return; } // Define local variables Script[] targetScripts = null; // Update one script Script newScript = null; LogInfo updaterLog = null; // Update scripts Script[] newScripts = null; LogInfo[] updaterLogs = null; // Turn on progress ring Model.WorkInProgress = true; try { // Populate BuildTree ProjectTreeItemModel treeRoot = MainViewModel.PopulateOneTreeItem(targetScript, null, null); Model.BuildTreeItems.Clear(); if (updateMultipleScript) { // Update a list of scripts // We have to search in p.AllScripts rather than in ProjectTreeItemModel to find hidden scripts // (ProjectTreeItemModel only contains visible scripts) targetScripts = p.AllScripts .Where(x => x.TreePath.StartsWith(targetScript.TreePath, StringComparison.OrdinalIgnoreCase) && x.IsUpdateable) .ToArray(); MainViewModel.ScriptListToTreeViewModel(p, targetScripts, false, treeRoot); targetScripts = targetScripts.Where(x => x.Type != ScriptType.Directory).ToArray(); if (targetScripts.Length == 0) { // Ask user for confirmation MessageBox.Show(this, $"Directory [{targetScript.Title}] does not contain any scripts that are able to be updated.", "No updateable scripts", MessageBoxButton.OK, MessageBoxImage.Warning); return; } } Model.BuildTreeItems.Add(treeRoot); Model.CurBuildTree = null; Debug.Assert(updateMultipleScript && targetScript != null && targetScripts != null || !updateMultipleScript && targetScript != null && targetScripts == null, $"Check {updateMultipleScript}"); // Ask user for confirmation string targetScriptCountStr; if (updateMultipleScript) { targetScriptCountStr = targetScripts.Length == 1 ? "1 script" : $"{targetScripts.Length} scripts"; } else { targetScriptCountStr = $"script [{targetScript.Title}]"; } MessageBoxResult result = MessageBox.Show(this, $"Are you sure you want to update {targetScriptCountStr}?", "Continue?", MessageBoxButton.YesNo, MessageBoxImage.Question); if (result == MessageBoxResult.No) { return; } // Switch to Build View Model.BuildScriptFullProgressVisibility = Visibility.Collapsed; Model.SwitchNormalBuildInterface = false; Stopwatch watch = Stopwatch.StartNew(); // Run Updater string customUserAgent = Global.Setting.General.UseCustomUserAgent ? Global.Setting.General.CustomUserAgent : null; FileUpdater updater = new FileUpdater(p, Model, customUserAgent); if (updateMultipleScript) // Update a list of scripts { (newScripts, updaterLogs) = await updater.UpdateScriptsAsync(targetScripts, true); Logger.SystemWrite(updaterLogs); } else { (newScript, updaterLog) = await updater.UpdateScriptAsync(targetScript, true); Logger.SystemWrite(updaterLog); } watch.Stop(); TimeSpan t = watch.Elapsed; Model.StatusBarText = $"Updated {targetScript.Title} ({t:h\\:mm\\:ss})"; } finally { // Turn off progress ring Model.BuildScriptFullProgressVisibility = Visibility.Visible; Model.WorkInProgress = false; // Build Ended, Switch to Normal View Model.SwitchNormalBuildInterface = true; Model.DisplayScript(Model.CurMainTree.Script); } // Report results if (updateMultipleScript) { // Updated multiple scripts PackIconMaterialKind msgBoxIcon = PackIconMaterialKind.Information; StringBuilder b = new StringBuilder(updaterLogs.Length + 6); if (0 < newScripts.Length) { b.AppendLine($"Successfully updated [{newScripts.Length}] scripts"); } foreach (Script newSc in newScripts) { ProjectTreeItemModel node = Model.CurMainTree.FindScriptByRealPath(newSc.RealPath); Debug.Assert(node != null, "Internal error with MainTree management"); Model.PostRefreshScript(node, newSc); b.AppendLine($"- {newSc.Title}"); } LogInfo[] errorLogs = updaterLogs.Where(x => x.State == LogState.Error).ToArray(); if (0 < errorLogs.Length) { // Failure if (0 < newScripts.Length) { b.AppendLine(); } b.AppendLine($"Failed to update [{targetScripts.Length - newScripts.Length}] scripts"); foreach (LogInfo log in errorLogs) { b.AppendLine($"- {log.Message}"); } msgBoxIcon = PackIconMaterialKind.Alert; } const string msgTitle = "Script Update Report"; TextViewDialog dialog = new TextViewDialog(this, msgTitle, msgTitle, b.ToString(), msgBoxIcon); dialog.ShowDialog(); } else { // Updated single script if (newScript != null) { // Success ProjectTreeItemModel node = Model.CurMainTree.FindScriptByRealPath(newScript.RealPath); Debug.Assert(node != null, "Internal error with MainTree management"); Model.PostRefreshScript(node, newScript); MessageBox.Show(this, $"Successfully updated script {newScript.Title}", "Script Update Success", MessageBoxButton.OK, MessageBoxImage.Information); } else { // Failure StringBuilder b = new StringBuilder(updaterLogs.Length + 6); LogInfo[] errorLogs = updaterLogs.Where(x => x.State == LogState.Error).ToArray(); foreach (LogInfo log in errorLogs) { b.AppendLine($"- {log.Message}"); } MessageBox.Show(this, b.ToString(), "Script Update Failure", MessageBoxButton.OK, MessageBoxImage.Error); } } }
private async void ScriptRunCommand_Executed(object sender, ExecutedRoutedEventArgs e) { // Force update of script interface ScriptRunButton.Focus(); Script sc = Model.CurMainTree.Script; if (!sc.Sections.ContainsKey(ScriptSection.Names.Process)) { Model.StatusBarText = $"Section [Process] does not exist in {sc.Title}"; return; } if (Engine.TryEnterLock()) { // Engine is not running, so we can start new engine try { Model.BuildTreeItems.Clear(); ProjectTreeItemModel rootItem = MainViewModel.PopulateOneTreeItem(sc, null, null); Model.BuildTreeItems.Add(rootItem); Model.CurBuildTree = null; EngineState s = new EngineState(sc.Project, Logger, Model, EngineMode.RunMainAndOne, sc); s.SetOptions(Global.Setting); s.SetCompat(sc.Project.Compat); Engine.WorkingEngine = new Engine(s); // Switch to Build View Model.SwitchNormalBuildInterface = false; TimeSpan t; using (CancellationTokenSource ct = new CancellationTokenSource()) { Task printStatus = MainViewModel.PrintBuildElapsedStatus($"Running {sc.Title}...", s, ct.Token); // Run int buildId = await Engine.WorkingEngine.Run($"{sc.Title} - Run"); #if DEBUG Logger.ExportBuildLog(LogExportType.Text, Path.Combine(s.BaseDir, "LogDebugDump.txt"), buildId, new BuildLogOptions { IncludeComments = true, IncludeMacros = true, ShowLogFlags = true, }); #endif // Cancel and Wait until PrintBuildElapsedStatus stops // Report elapsed time t = s.Elapsed; ct.Cancel(); await printStatus; } Model.StatusBarText = $"{sc.Title} processed in {t:h\\:mm\\:ss}"; // Build Ended, Switch to Normal View Model.SwitchNormalBuildInterface = true; Model.BuildTreeItems.Clear(); Model.DisplayScript(Model.CurMainTree.Script); if (Global.Setting.General.ShowLogAfterBuild && LogWindow.Count == 0) { // Open BuildLogWindow LogDialog = new LogWindow(1); LogDialog.Show(); } } finally { Engine.WorkingEngine = null; Engine.ExitLock(); } } else // Stop Build { // Request engine to stop the build. ForceStopBuild(); } }