From 5bd26632cab351e4fed2f2b0e51b78627cfa4562 Mon Sep 17 00:00:00 2001 From: Benjamin Klum Date: Wed, 19 May 2021 00:11:25 +0200 Subject: [PATCH] #332 Fix non-deactivating preset save button by applying project independence transformation onto current mappings, not just the ones in the preset itself --- main/src/application/preset.rs | 78 +--------------- main/src/application/session.rs | 93 ++++++++++++++++++- main/src/infrastructure/ui/dialog_util.rs | 3 +- main/src/infrastructure/ui/header_panel.rs | 102 ++++++++------------- 4 files changed, 131 insertions(+), 145 deletions(-) diff --git a/main/src/application/preset.rs b/main/src/application/preset.rs index 40e4e42b1..419a01817 100644 --- a/main/src/application/preset.rs +++ b/main/src/application/preset.rs @@ -1,7 +1,4 @@ -use crate::application::{ - GroupModel, MappingModel, ParameterSetting, SharedGroup, SharedMapping, TargetCategory, -}; -use crate::domain::{ExtendedProcessorContext, VirtualFx, VirtualTrack}; +use crate::application::{GroupModel, MappingModel, ParameterSetting, SharedGroup, SharedMapping}; use std::collections::HashMap; use std::fmt; use std::fmt::Debug; @@ -35,76 +32,3 @@ pub trait PresetManager: fmt::Debug { groups: &[SharedGroup], ) -> bool; } - -pub fn mappings_have_project_references(mappings: &[MappingModel]) -> bool { - mappings.iter().any(mapping_has_project_references) -} - -pub fn make_mappings_project_independent( - mappings: &mut [MappingModel], - context: ExtendedProcessorContext, -) { - mappings - .iter_mut() - .for_each(|m| make_mapping_project_independent(m, context)); -} - -/// Checks if the given mapping has references to a project, e.g. refers to track or FX by ID. -fn mapping_has_project_references(mapping: &MappingModel) -> bool { - let target = &mapping.target_model; - match target.category.get() { - TargetCategory::Reaper => { - if target.r#type.get().supports_track() && target.track_type.get().refers_to_project() { - return true; - } - target.supports_fx() && target.fx_type.get().refers_to_project() - } - TargetCategory::Virtual => false, - } -} - -fn make_mapping_project_independent(mapping: &mut MappingModel, context: ExtendedProcessorContext) { - let compartment = mapping.compartment(); - let target = &mut mapping.target_model; - match target.category.get() { - TargetCategory::Reaper => { - let changed_to_track_ignore_fx = if target.supports_fx() { - let refers_to_project = target.fx_type.get().refers_to_project(); - if refers_to_project { - let target_with_context = target.with_context(context, compartment); - let virtual_fx = if target_with_context.fx().ok().as_ref() - == Some(context.context().containing_fx()) - { - // This is ourselves! - VirtualFx::This - } else { - VirtualFx::Focused - }; - target.set_virtual_fx(virtual_fx); - true - } else { - false - } - } else { - false - }; - if target.r#type.get().supports_track() && target.track_type.get().refers_to_project() { - let new_virtual_track = if changed_to_track_ignore_fx { - // Track doesn't matter at all. We change it to . Looks nice. - Some(VirtualTrack::This) - } else if let Ok(t) = target - .with_context(context, compartment) - .first_effective_track() - { - t.index().map(VirtualTrack::ByIndex) - } else { - None - }; - if let Some(t) = new_virtual_track { - target.set_virtual_track(t); - } - } - } - TargetCategory::Virtual => {} - } -} diff --git a/main/src/application/session.rs b/main/src/application/session.rs index c1a9312fb..c1f7bb562 100644 --- a/main/src/application/session.rs +++ b/main/src/application/session.rs @@ -11,7 +11,8 @@ use crate::domain::{ MappingId, MidiControlInput, MidiDestination, NormalMainTask, NormalRealTimeTask, OscDeviceId, ParameterArray, ProcessorContext, ProjectionFeedbackValue, QualifiedMappingId, RealSource, RealTimeSender, RealearnTarget, ReaperTarget, SharedInstanceState, TargetValueChangedEvent, - VirtualControlElementId, VirtualSource, COMPARTMENT_PARAMETER_COUNT, ZEROED_PLUGIN_PARAMETERS, + VirtualControlElementId, VirtualFx, VirtualSource, VirtualTrack, COMPARTMENT_PARAMETER_COUNT, + ZEROED_PLUGIN_PARAMETERS, }; use derivative::Derivative; use enum_map::{enum_map, EnumMap}; @@ -285,6 +286,17 @@ impl Session { }) } + pub fn mappings_have_project_references(&self, compartment: MappingCompartment) -> bool { + mappings_have_project_references(self.mappings[compartment].iter()) + } + + pub fn make_mappings_project_independent(&self, compartment: MappingCompartment) { + make_mappings_project_independent( + self.mappings[compartment].iter(), + self.extended_context(), + ); + } + pub fn get_parameter_settings( &self, compartment: MappingCompartment, @@ -1907,10 +1919,6 @@ impl Session { } } - pub fn parameters(&self) -> &ParameterArray { - &self.parameters - } - /// Just syncs whether control globally enabled or not. fn sync_control_is_globally_enabled(&self) { let enabled = self.control_is_globally_enabled(); @@ -2129,3 +2137,78 @@ pub enum InputDescriptor { pub fn empty_parameter_settings() -> Vec { vec![Default::default(); COMPARTMENT_PARAMETER_COUNT as usize] } + +fn mappings_have_project_references<'a>( + mut mappings: impl Iterator, +) -> bool { + mappings.any(mapping_has_project_references) +} + +fn make_mappings_project_independent<'a>( + mappings: impl Iterator, + context: ExtendedProcessorContext, +) { + mappings.for_each(|m| make_mapping_project_independent(m, context)); +} + +/// Checks if the given mapping has references to a project, e.g. refers to track or FX by ID. +fn mapping_has_project_references(mapping: &SharedMapping) -> bool { + let mapping = mapping.borrow(); + let target = &mapping.target_model; + match target.category.get() { + TargetCategory::Reaper => { + if target.r#type.get().supports_track() && target.track_type.get().refers_to_project() { + return true; + } + target.supports_fx() && target.fx_type.get().refers_to_project() + } + TargetCategory::Virtual => false, + } +} + +fn make_mapping_project_independent(mapping: &SharedMapping, context: ExtendedProcessorContext) { + let mut mapping = mapping.borrow_mut(); + let compartment = mapping.compartment(); + let target = &mut mapping.target_model; + match target.category.get() { + TargetCategory::Reaper => { + let changed_to_track_ignore_fx = if target.supports_fx() { + let refers_to_project = target.fx_type.get().refers_to_project(); + if refers_to_project { + let target_with_context = target.with_context(context, compartment); + let virtual_fx = if target_with_context.fx().ok().as_ref() + == Some(context.context().containing_fx()) + { + // This is ourselves! + VirtualFx::This + } else { + VirtualFx::Focused + }; + target.set_virtual_fx(virtual_fx); + true + } else { + false + } + } else { + false + }; + if target.r#type.get().supports_track() && target.track_type.get().refers_to_project() { + let new_virtual_track = if changed_to_track_ignore_fx { + // Track doesn't matter at all. We change it to . Looks nice. + Some(VirtualTrack::This) + } else if let Ok(t) = target + .with_context(context, compartment) + .first_effective_track() + { + t.index().map(VirtualTrack::ByIndex) + } else { + None + }; + if let Some(t) = new_virtual_track { + target.set_virtual_track(t); + } + } + } + TargetCategory::Virtual => {} + } +} diff --git a/main/src/infrastructure/ui/dialog_util.rs b/main/src/infrastructure/ui/dialog_util.rs index dccb54e97..396df9436 100644 --- a/main/src/infrastructure/ui/dialog_util.rs +++ b/main/src/infrastructure/ui/dialog_util.rs @@ -6,5 +6,6 @@ pub fn prompt_for(caption: &str, initial_value: &str) -> Option { Reaper::get() .medium_reaper() .get_user_inputs("ReaLearn", 1, caption, initial_value, 256) - .map(|r| r.into_string()) + .map(|r| r.to_str().trim().to_owned()) + .filter(|r| !r.is_empty()) } diff --git a/main/src/infrastructure/ui/header_panel.rs b/main/src/infrastructure/ui/header_panel.rs index ed40ea74b..90ff9e3b0 100644 --- a/main/src/infrastructure/ui/header_panel.rs +++ b/main/src/infrastructure/ui/header_panel.rs @@ -16,13 +16,12 @@ use slog::debug; use swell_ui::{MenuBar, Pixels, Point, SharedView, View, ViewContext, Window}; use crate::application::{ - make_mappings_project_independent, mappings_have_project_references, ControllerPreset, FxId, - MainPreset, MainPresetAutoLoadMode, MappingModel, ParameterSetting, Preset, PresetManager, - SharedMapping, SharedSession, VirtualControlElementType, WeakSession, + ControllerPreset, FxId, MainPreset, MainPresetAutoLoadMode, ParameterSetting, Preset, + PresetManager, SharedMapping, SharedSession, VirtualControlElementType, WeakSession, }; use crate::base::when; use crate::domain::{ - ControlInput, ExtendedProcessorContext, GroupId, MappingCompartment, OscDeviceId, ReaperTarget, + ControlInput, GroupId, MappingCompartment, OscDeviceId, ReaperTarget, COMPARTMENT_PARAMETER_COUNT, }; use crate::domain::{MidiControlInput, MidiDestination}; @@ -159,9 +158,6 @@ impl HeaderPanel { fn add_group(&self) { if let Some(name) = dialog_util::prompt_for("Group name", "") { - if name.is_empty() { - return; - } let id = self .session() .borrow_mut() @@ -1677,34 +1673,34 @@ impl HeaderPanel { let _ = App::get().main_preset_manager().borrow_mut().load_presets(); } + fn make_mappings_project_independent_if_desired(&self) { + let session = self.session(); + let compartment = self.active_compartment(); + if session + .borrow() + .mappings_have_project_references(compartment) + && self.ask_user_if_project_independence_desired() + { + session + .borrow() + .make_mappings_project_independent(compartment); + } + } + fn save_active_preset(&self) -> Result<(), &'static str> { + self.make_mappings_project_independent_if_desired(); let session = self.session(); - let (context, params, mut mappings, preset_id, compartment) = { - let session = session.borrow(); - let compartment = self.active_compartment(); - let preset_id = match compartment { - MappingCompartment::ControllerMappings => session.active_controller_preset_id(), - MappingCompartment::MainMappings => session.active_main_preset_id(), - }; - let preset_id = match preset_id { - None => return Err("no active preset"), - Some(id) => id, - }; - let mappings: Vec<_> = session - .mappings(compartment) - .map(|ptr| ptr.borrow().clone()) - .collect(); - ( - session.context().clone(), - *session.parameters(), - mappings, - preset_id.to_owned(), - compartment, - ) - }; - let extended_context = ExtendedProcessorContext::new(&context, ¶ms); - self.make_mappings_project_independent_if_desired(extended_context, &mut mappings); let session = session.borrow(); + let compartment = self.active_compartment(); + let preset_id = match compartment { + MappingCompartment::ControllerMappings => session.active_controller_preset_id(), + MappingCompartment::MainMappings => session.active_main_preset_id(), + } + .ok_or("no active preset")?; + let mappings: Vec<_> = session + .mappings(compartment) + .map(|ptr| ptr.borrow().clone()) + .collect(); let default_group = session.default_group(compartment).borrow().clone(); let parameter_settings = session.non_default_parameter_settings_by_compartment(compartment); let groups = session @@ -1743,7 +1739,7 @@ impl HeaderPanel { let current_session_id = { self.session().borrow().id.get_ref().clone() }; let new_session_id = match dialog_util::prompt_for("Session ID", ¤t_session_id) { None => return, - Some(n) => n.trim().to_string(), + Some(n) => n, }; if new_session_id == current_session_id { return; @@ -1765,44 +1761,26 @@ impl HeaderPanel { } /// Don't borrow the session while calling this! - fn make_mappings_project_independent_if_desired( - &self, - context: ExtendedProcessorContext, - mut mappings: &mut [MappingModel], - ) { + fn ask_user_if_project_independence_desired(&self) -> bool { let msg = "Some of the mappings have references to this particular project. This usually doesn't make too much sense for a preset that's supposed to be reusable among different projects. Do you want ReaLearn to automatically adjust the mappings so that track targets refer to tracks by their position and FX targets relate to whatever FX is currently focused?"; - if mappings_have_project_references(&mappings) - && self.view.require_window().confirm("ReaLearn", msg) - { - make_mappings_project_independent(&mut mappings, context); - } + self.view.require_window().confirm("ReaLearn", msg) } fn save_as_preset(&self) -> Result<(), &'static str> { - let session = self.session(); - let (context, params, mut mappings, compartment, param_settings) = { - let session = session.borrow_mut(); - let compartment = self.active_compartment(); - let mappings: Vec<_> = session - .mappings(compartment) - .map(|ptr| ptr.borrow().clone()) - .collect(); - ( - session.context().clone(), - *session.parameters(), - mappings, - compartment, - session.non_default_parameter_settings_by_compartment(compartment), - ) - }; - let extended_context = ExtendedProcessorContext::new(&context, ¶ms); - self.make_mappings_project_independent_if_desired(extended_context, &mut mappings); let preset_name = match dialog_util::prompt_for("Preset name", "") { None => return Ok(()), Some(n) => n, }; - let preset_id = slug::slugify(&preset_name); + self.make_mappings_project_independent_if_desired(); + let session = self.session(); let mut session = session.borrow_mut(); + let compartment = self.active_compartment(); + let mappings: Vec<_> = session + .mappings(compartment) + .map(|ptr| ptr.borrow().clone()) + .collect(); + let param_settings = session.non_default_parameter_settings_by_compartment(compartment); + let preset_id = slug::slugify(&preset_name); let default_group = session.default_group(compartment).borrow().clone(); let groups = session .groups(compartment)