diff --git a/CHANGELOG.md b/CHANGELOG.md index fc3c2243..d6599f26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ and `Removed`. ### Added +- Ability to choose a different directory for each flavor. + This means you can use a single instance of Ajour even if you have Retail and + Classic in two different directories. - Addon backup directory will be created if missing - Addon updates are automatically checked for every 30 minutes while the program is open. If new updates are available, they will be sorted to the top of the screen diff --git a/crates/core/src/config/mod.rs b/crates/core/src/config/mod.rs index 278bbd7c..755fbfeb 100644 --- a/crates/core/src/config/mod.rs +++ b/crates/core/src/config/mod.rs @@ -64,15 +64,28 @@ pub struct Config { } impl Config { + /// Returns a `PathBuf` to the flavor directory. + pub fn get_flavor_directory_for_flavor(&self, flavor: &Flavor, path: &PathBuf) -> PathBuf { + path.join(&flavor.folder_name()) + } + + /// Returns a `Option` to the root directory of the Flavor. + pub fn get_root_directory_for_flavor(&self, flavor: &Flavor) -> Option { + if let Some(flavor_dir) = self.wow.directories.get(flavor) { + Some(flavor_dir.parent().unwrap().to_path_buf()) + } else { + None + } + } + /// Returns a `Option` to the directory containing the addons. /// This will return `None` if no `wow_directory` is set in the config. pub fn get_addon_directory_for_flavor(&self, flavor: &Flavor) -> Option { - match &self.wow.directory { - Some(wow_dir) => { - // The path to the flavor directory - let flavor_dir = wow_dir.join(&flavor.folder_name()); + let dir = self.wow.directories.get(flavor); + match dir { + Some(dir) => { // The path to the addons directory - let mut addon_dir = flavor_dir.join("Interface/AddOns"); + let mut addon_dir = dir.join("Interface/AddOns"); // If path doesn't exist, it could have been modified by the user. // Check for a case-insensitive version and use that instead. @@ -84,7 +97,7 @@ impl Config { // For some reason the case insensitive pattern doesn't work // unless we add an actual pattern symbol, hence the `?`. - let pattern = format!("{}/?nterface/?ddons", flavor_dir.display()); + let pattern = format!("{}/?nterface/?ddons", dir.display()); for entry in glob::glob_with(&pattern, options).unwrap() { if let Ok(path) = entry { @@ -96,7 +109,7 @@ impl Config { // If flavor dir exists but not addon dir we try to create it. // This state can happen if you do a fresh install of WoW and // launch Ajour before you launch WoW. - if flavor_dir.exists() && !addon_dir.exists() { + if dir.exists() && !addon_dir.exists() { let _ = create_dir_all(&addon_dir); } @@ -108,25 +121,19 @@ impl Config { /// Returns a `Option` to the directory which will hold the /// temporary zip archives. - /// This will return `None` if no `wow_directory` is set in the config. + /// This will return `None` if flavor does not have a directory. pub fn get_download_directory_for_flavor(&self, flavor: Flavor) -> Option { - match self.get_addon_directory_for_flavor(&flavor) { - Some(dir) => { - // The path to the directory which hold the temporary zip archives - let dir = dir.parent().expect("Expected Addons folder has a parent."); - Some(dir.to_path_buf()) - } - None => None, - } + self.wow.directories.get(&flavor).cloned() } /// Returns a `Option` to the WTF directory. /// This will return `None` if no `wow_directory` is set in the config. pub fn get_wtf_directory_for_flavor(&self, flavor: &Flavor) -> Option { - match &self.wow.directory { + let dir = self.wow.directories.get(flavor); + match dir { Some(dir) => { // The path to the WTF directory - let mut addon_dir = dir.join(&flavor.folder_name()).join("WTF"); + let mut addon_dir = dir.join("WTF"); // If path doesn't exist, it could have been modified by the user. // Check for a case-insensitive version and use that instead. @@ -138,7 +145,7 @@ impl Config { // For some reason the case insensitive pattern doesn't work // unless we add an actual pattern symbol, hence the `?`. - let pattern = format!("{}/?tf", dir.join(&flavor.folder_name()).display()); + let pattern = format!("{}/?tf", dir.display()); for entry in glob::glob_with(&pattern, options).unwrap() { if let Ok(path) = entry { diff --git a/crates/core/src/config/wow.rs b/crates/core/src/config/wow.rs index b0dc23ae..46d60724 100644 --- a/crates/core/src/config/wow.rs +++ b/crates/core/src/config/wow.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::path::PathBuf; /// Struct for settings related to World of Warcraft. @@ -6,8 +7,12 @@ use std::path::PathBuf; #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] pub struct Wow { #[serde(default)] + #[allow(deprecated)] pub directory: Option, + #[serde(default)] + pub directories: HashMap, + #[serde(default)] pub flavor: Flavor, } @@ -16,6 +21,7 @@ impl Default for Wow { fn default() -> Self { Wow { directory: None, + directories: HashMap::new(), flavor: Flavor::Retail, } } diff --git a/crates/core/src/utility.rs b/crates/core/src/utility.rs index 1f3e0cee..288684e4 100644 --- a/crates/core/src/utility.rs +++ b/crates/core/src/utility.rs @@ -1,4 +1,4 @@ -use crate::config::SelfUpdateChannel; +use crate::config::{Flavor, SelfUpdateChannel}; use crate::error::DownloadError; #[cfg(target_os = "macos")] use crate::error::FilesystemError; @@ -199,11 +199,14 @@ fn extract_binary_from_tar( }) } -/// Logic to help pick the right World of Warcraft folder. We want the root folder. +/// Logic to help pick the right World of Warcraft folder. pub fn wow_path_resolution(path: Option) -> Option { if let Some(path) = path { // Known folders in World of Warcraft dir - let known_folders = ["_retail_", "_classic_", "_ptr_"]; + let known_folders = Flavor::ALL + .iter() + .map(|f| f.folder_name()) + .collect::>(); // If chosen path has any of the known Wow folders, we have the right one. for folder in known_folders.iter() { diff --git a/src/command/backup.rs b/src/command/backup.rs index b97e74ec..977abe66 100644 --- a/src/command/backup.rs +++ b/src/command/backup.rs @@ -31,7 +31,9 @@ pub fn backup( return Err(format_err!("destination must be a folder, not a file")); } - let wow_dir = config.wow.directory.as_ref().ok_or_else(|| format_err!("No WoW directory set. Launch Ajour and make sure a WoW directory is set before using the command line."))?; + if config.wow.directories.keys().next().is_none() { + return Err(format_err!("No WoW directories set. Launch Ajour and make sure a WoW directory is set before using the command line.")); + } log::info!( "Backing up:\n\tbackup folders: {:?}\n\tflavors: {:?}\n\tdestination: {:?}", @@ -43,8 +45,9 @@ pub fn backup( let mut src_folders = vec![]; for flavor in flavors { - let addon_directory = config.get_addon_directory_for_flavor(&flavor).ok_or_else(|| format_err!("No WoW directory set. Launch Ajour and make sure a WoW directory is set before using the command line."))?; - let wtf_directory = config.get_wtf_directory_for_flavor(&flavor).ok_or_else(|| format_err!("No WoW directory set. Launch Ajour and make sure a WoW directory is set before using the command line."))?; + let wow_dir = config.get_root_directory_for_flavor(&flavor).ok_or_else(|| format_err!("No WoW directories set. Launch Ajour and make sure a WoW directory is set before using the command line."))?; + let addon_directory = config.get_addon_directory_for_flavor(&flavor).ok_or_else(|| format_err!("No WoW directories set. Launch Ajour and make sure a WoW directory is set before using the command line."))?; + let wtf_directory = config.get_wtf_directory_for_flavor(&flavor).ok_or_else(|| format_err!("No WoW directories set. Launch Ajour and make sure a WoW directory is set before using the command line."))?; let addons_folder = backup::BackupFolder::new(&addon_directory, &wow_dir); let wtf_folder = backup::BackupFolder::new(&wtf_directory, &wow_dir); diff --git a/src/gui/element/menu.rs b/src/gui/element/menu.rs index 51eb2860..c0ecbf5c 100644 --- a/src/gui/element/menu.rs +++ b/src/gui/element/menu.rs @@ -22,7 +22,6 @@ pub fn data_container<'a>( state: &HashMap, error: &Option, config: &Config, - valid_flavors: &[Flavor], settings_button_state: &'a mut button::State, about_button_state: &'a mut button::State, addon_mode_button_state: &'a mut button::State, @@ -34,6 +33,14 @@ pub fn data_container<'a>( weak_auras_is_installed: bool, ) -> Container<'a, Message> { let flavor = config.wow.flavor; + let mut valid_flavors = config + .wow + .directories + .keys() + .copied() + .collect::>(); + + valid_flavors.sort(); // State. let myaddons_state = state diff --git a/src/gui/element/settings.rs b/src/gui/element/settings.rs index bbe2ca86..5f8563ea 100644 --- a/src/gui/element/settings.rs +++ b/src/gui/element/settings.rs @@ -1,10 +1,11 @@ #![allow(clippy::too_many_arguments)] + use { super::{DEFAULT_FONT_SIZE, DEFAULT_HEADER_FONT_SIZE, DEFAULT_PADDING}, crate::gui::{ style, BackupFolderKind, BackupState, CatalogColumnKey, CatalogColumnSettings, ColumnKey, - ColumnSettings, DirectoryType, GlobalReleaseChannel, Interaction, Language, Message, - ScaleState, SelfUpdateChannelState, ThemeState, + ColumnSettings, GlobalReleaseChannel, Interaction, Language, Message, ScaleState, + SelfUpdateChannelState, ThemeState, WowDirectoryState, }, crate::localization::localized_string, ajour_core::{config::Config, theme::ColorPalette}, @@ -19,7 +20,6 @@ use { pub fn data_container<'a, 'b>( color_palette: ColorPalette, scrollable_state: &'a mut scrollable::State, - directory_button_state: &'a mut button::State, config: &Config, theme_state: &'a mut ThemeState, scale_state: &'a mut ScaleState, @@ -33,6 +33,7 @@ pub fn data_container<'a, 'b>( default_addon_release_channel_picklist_state: &'a mut pick_list::State, reset_columns_button_state: &'a mut button::State, localization_picklist_state: &'a mut pick_list::State, + wow_directories: &'a mut Vec, ) -> Container<'a, Message> { let mut scrollable = Scrollable::new(scrollable_state) .spacing(1) @@ -40,53 +41,43 @@ pub fn data_container<'a, 'b>( .style(style::Scrollable(color_palette)); let wow_directory_column = { - // Title for the World of Warcraft directory selection. - let directory_info_text = - Text::new(localized_string("wow-directory")).size(DEFAULT_FONT_SIZE); - let direction_info_text_container = Container::new(directory_info_text) - .style(style::NormalBackgroundContainer(color_palette)); - - // Directory button for World of Warcraft directory selection. - - let directory_button_title_container = - Container::new(Text::new(localized_string("select-directory")).size(DEFAULT_FONT_SIZE)) - .width(Length::FillPortion(1)) - .center_x() - .align_x(Align::Center); - - let directory_button: Element = - Button::new(directory_button_state, directory_button_title_container) - .style(style::DefaultBoxedButton(color_palette)) - .on_press(Interaction::SelectDirectory(DirectoryType::Wow)) - .into(); - - // Directory text, written next to directory button to let the user - // know what has been selected.. - let no_directory_str = &localized_string("no-directory")[..]; - let path_str = config - .wow - .directory - .as_ref() - .and_then(|p| p.to_str()) - .unwrap_or(no_directory_str); - let directory_data_text = Text::new(path_str) - .size(14) - .vertical_alignment(VerticalAlignment::Center); - let directory_data_text_container = Container::new(directory_data_text) - .height(Length::Units(25)) - .center_y() - .style(style::NormalBackgroundContainer(color_palette)); + let mut wow_dir_column = Column::new(); + for wow_dir_state in wow_directories { + let flavor = wow_dir_state.flavor; + let path_str = config + .wow + .directories + .get(&flavor) + .map(|p| p.to_str()) + .flatten() + .unwrap_or("-"); + let flavor_text = Text::new(flavor.to_string()) + .size(14) + .vertical_alignment(VerticalAlignment::Center); + let flavor_text_container = Container::new(flavor_text) + .width(Length::Units(75)) + .center_y(); + let flavor_button: Element = + Button::new(&mut wow_dir_state.button_state, flavor_text_container) + .style(style::DefaultButton(color_palette)) + .on_press(Interaction::SelectWowDirectory(Some(flavor))) + .into(); + let path_text = Text::new(path_str) + .size(14) + .vertical_alignment(VerticalAlignment::Center); + let path_text_container = Container::new(path_text) + .height(Length::Units(25)) + .center_y() + .style(style::NormalBackgroundContainer(color_palette)); + let flavor_row = Row::new() + .push(flavor_button.map(Message::Interaction)) + .push(Space::new(Length::Units(DEFAULT_PADDING), Length::Units(0))) + .push(path_text_container); - // Data row for the World of Warcraft directory selection. - let directory_data_row = Row::new() - .push(directory_button.map(Message::Interaction)) - .push(Space::new(Length::Units(DEFAULT_PADDING), Length::Units(0))) - .push(directory_data_text_container); + wow_dir_column = wow_dir_column.push(flavor_row); + } - Column::new() - .push(direction_info_text_container) - .push(Space::new(Length::Units(0), Length::Units(5))) - .push(directory_data_row) + wow_dir_column }; let theme_column = { @@ -209,7 +200,7 @@ pub fn data_container<'a, 'b>( directory_button_title_container, ) .style(style::DefaultBoxedButton(color_palette)) - .on_press(Interaction::SelectDirectory(DirectoryType::Backup)) + .on_press(Interaction::SelectBackupDirectory()) .into(); // Directory text, written next to directory button to let the user @@ -261,7 +252,7 @@ pub fn data_container<'a, 'b>( // the wow folder is chosen and at least one of the folders is selected // for backup if !backup_state.backing_up - && config.wow.directory.is_some() + && config.wow.directories.keys().next().is_some() && (config.backup_addons || config.backup_wtf) { backup_button = backup_button.on_press(Interaction::Backup); @@ -433,6 +424,11 @@ pub fn data_container<'a, 'b>( let general_settings_title_container = Container::new(general_settings_title) .style(style::BrightBackgroundContainer(color_palette)); + let directories_settings_title = + Text::new(localized_string("wow-directories")).size(DEFAULT_HEADER_FONT_SIZE); + let directories_settings_title_container = Container::new(directories_settings_title) + .style(style::BrightBackgroundContainer(color_palette)); + let theme_scale_row = Row::new() .push(theme_column) .push(scale_column) @@ -512,8 +508,6 @@ pub fn data_container<'a, 'b>( .push(Space::new(Length::Units(0), Length::Units(5))) .push(language_container) .push(Space::new(Length::Units(0), Length::Units(10))) - .push(wow_directory_column) - .push(Space::new(Length::Units(0), Length::Units(10))) .push(theme_scale_row) .push(Space::new(Length::Units(0), Length::Units(10))) .push(alternate_row_color_column) @@ -523,6 +517,13 @@ pub fn data_container<'a, 'b>( .push(config_column) .push(Space::new(Length::Units(0), Length::Units(30))); + // Directories + scrollable = scrollable + .push(directories_settings_title_container) + .push(Space::new(Length::Units(0), Length::Units(5))) + .push(wow_directory_column) + .push(Space::new(Length::Units(0), Length::Units(20))); + // Backup scrollable = scrollable .push(backup_title_row) diff --git a/src/gui/element/status.rs b/src/gui/element/status.rs index ced1bba6..307d788d 100644 --- a/src/gui/element/status.rs +++ b/src/gui/element/status.rs @@ -1,6 +1,6 @@ use { super::{DEFAULT_FONT_SIZE, DEFAULT_PADDING}, - crate::gui::{style, DirectoryType, Interaction, Message, State}, + crate::gui::{style, Interaction, Message, State}, crate::localization::localized_string, ajour_core::theme::ColorPalette, iced::{ @@ -44,7 +44,7 @@ pub fn data_container<'a>( let onboarding_button: Element = Button::new(btn_state, onboarding_button_title_container) .style(style::DefaultButton(color_palette)) - .on_press(Interaction::SelectDirectory(DirectoryType::Wow)) + .on_press(Interaction::SelectWowDirectory(None)) .into(); colum = colum diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 4a0a53e9..01d2b538 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -85,7 +85,8 @@ pub enum Interaction { Delete(String), Expand(ExpandType), Ignore(String), - SelectDirectory(DirectoryType), + SelectBackupDirectory(), + SelectWowDirectory(Option), OpenDirectory(PathBuf), OpenLink(String), Refresh(Mode), @@ -151,7 +152,7 @@ pub enum Message { Result, FilesystemError>, ), ), - UpdateWowDirectory(Option), + UpdateWowDirectory((Option, Option)), UpdateBackupDirectory(Option), RuntimeEvent(iced_native::Event), LatestBackup(Option), @@ -183,8 +184,6 @@ pub struct Ajour { settings_scrollable_state: scrollable::State, about_scrollable_state: scrollable::State, config: Config, - valid_flavors: Vec, - directory_btn_state: button::State, expanded_type: ExpandType, self_update_state: SelfUpdateState, refresh_btn_state: button::State, @@ -223,6 +222,7 @@ pub struct Ajour { localization_picklist_state: pick_list::State, flavor_picklist_state: pick_list::State, addons_search_state: AddonsSearchState, + wow_directories: Vec, } impl Default for Ajour { @@ -237,8 +237,6 @@ impl Default for Ajour { settings_scrollable_state: Default::default(), about_scrollable_state: Default::default(), config: Config::default(), - valid_flavors: Vec::new(), - directory_btn_state: Default::default(), expanded_type: ExpandType::None, self_update_state: Default::default(), refresh_btn_state: Default::default(), @@ -280,6 +278,13 @@ impl Default for Ajour { localization_picklist_state: Default::default(), flavor_picklist_state: Default::default(), addons_search_state: Default::default(), + wow_directories: Flavor::ALL + .iter() + .map(|f| WowDirectoryState { + flavor: *f, + button_state: Default::default(), + }) + .collect::>(), } } } @@ -384,7 +389,6 @@ impl Application for Ajour { &self.state, &self.error, &self.config, - &self.valid_flavors, &mut self.settings_btn_state, &mut self.about_btn_state, &mut self.addon_mode_btn_state, @@ -918,7 +922,6 @@ impl Application for Ajour { let settings_container = element::settings::data_container( color_palette, &mut self.settings_scrollable_state, - &mut self.directory_btn_state, &self.config, &mut self.theme_state, &mut self.scale_state, @@ -932,6 +935,7 @@ impl Application for Ajour { &mut self.default_addon_release_channel_picklist_state, &mut self.reset_columns_btn_state, &mut self.localization_picklist_state, + &mut self.wow_directories, ); content = content.push(settings_container) @@ -1138,6 +1142,20 @@ impl Default for InstallFromSCMState { } } +pub struct WowDirectoryState { + pub flavor: Flavor, + pub button_state: button::State, +} + +impl Default for WowDirectoryState { + fn default() -> Self { + WowDirectoryState { + flavor: Default::default(), + button_state: Default::default(), + } + } +} + #[derive(Debug, Clone)] pub enum ExpandType { Details(Addon), @@ -1148,12 +1166,6 @@ pub enum ExpandType { None, } -#[derive(Debug, Clone, Copy)] -pub enum DirectoryType { - Wow, - Backup, -} - #[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)] pub enum ColumnKey { Title, @@ -2379,4 +2391,21 @@ fn apply_config(ajour: &mut Ajour, config: Config) { ajour.mode = Mode::MyAddons(config.wow.flavor); ajour.config = config; + + // @see (casperstorm): Migration from single World of Warcraft directory to multiple directories. + // This is essentially deprecrating `ajour.config.wow.directory`. + if ajour.config.wow.directory.is_some() { + for flavor in Flavor::ALL.iter() { + let path = ajour.config.wow.directory.as_ref().unwrap(); + let flavor_path = ajour.config.get_flavor_directory_for_flavor(flavor, path); + if flavor_path.exists() { + ajour.config.wow.directories.insert(*flavor, flavor_path); + } + } + + // Removing `directory`, so we don't end up here again. + ajour.config.wow.directory = None; + } + + let _ = &ajour.config.save(); } diff --git a/src/gui/update.rs b/src/gui/update.rs index dcacc589..168c648f 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -1,9 +1,9 @@ use { super::{ Ajour, AuraColumnKey, BackupFolderKind, CatalogCategory, CatalogColumnKey, CatalogRow, - CatalogSource, ColumnKey, DirectoryType, DownloadReason, ExpandType, GlobalReleaseChannel, - InstallAddon, InstallKind, InstallStatus, Interaction, Message, Mode, ReleaseChannel, - SelfUpdateStatus, SortDirection, State, + CatalogSource, ColumnKey, DownloadReason, ExpandType, GlobalReleaseChannel, InstallAddon, + InstallKind, InstallStatus, Interaction, Message, Mode, ReleaseChannel, SelfUpdateStatus, + SortDirection, State, }, crate::localization::{localized_string, LANG}, crate::{log_error, Result}, @@ -70,7 +70,50 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result = vec![]; + for flavor in Flavor::ALL.iter() { + if ajour.config.wow.directories.get(flavor).is_none() { + missing_flavors.push(flavor); + } + } + + let flavors = ajour + .config + .wow + .directories + .keys() + .copied() + .collect::>(); + for flavor in flavors { + // Find root dir of the flavor and check if any of the missing_flavor's is there. + // If it is, we added it to the directories. + if let Some(root_dir) = ajour.config.get_root_directory_for_flavor(&flavor) { + for missing_flavor in &missing_flavors { + let flavor_dir = ajour + .config + .get_flavor_directory_for_flavor(missing_flavor, &root_dir); + if flavor_dir.exists() { + ajour + .config + .wow + .directories + .insert(**missing_flavor, flavor_dir); + } + } + } + + // Check if the current flavor we are looping still exists. + // It might have been uninstalled since last time, if we can't find it we remove it. + if let Some(flavor_path) = ajour.config.wow.directories.get(&flavor) { + if !flavor_path.exists() { + ajour.config.wow.directories.remove(&flavor); + } + } + } + + let flavors = ajour.config.wow.directories.keys().collect::>(); for flavor in flavors { if let Some(addon_directory) = ajour.config.get_addon_directory_for_flavor(flavor) { log::debug!( @@ -78,13 +121,6 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result Result>() + .clone(); + if !flavors.iter().any(|f| *f == &flavor) { + if let Some(flavor) = flavors.first() { + ajour.config.wow.flavor = **flavor; + ajour.mode = Mode::MyAddons(**flavor); ajour.config.save()?; } } @@ -231,15 +270,19 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result { - log::debug!("Interaction::SelectDirectory({:?})", dir_type); - - let message = match dir_type { - DirectoryType::Wow => Message::UpdateWowDirectory, - DirectoryType::Backup => Message::UpdateBackupDirectory, - }; - - return Ok(Command::perform(select_directory(), message)); + Message::Interaction(Interaction::SelectWowDirectory(flavor)) => { + log::debug!("Interaction::SelectWowDirectory({:?})", flavor); + return Ok(Command::perform( + select_wow_directory(flavor), + Message::UpdateWowDirectory, + )); + } + Message::Interaction(Interaction::SelectBackupDirectory()) => { + log::debug!("Interaction::SelectBackupDirectory"); + return Ok(Command::perform( + select_directory(), + Message::UpdateBackupDirectory, + )); } Message::Interaction(Interaction::ResetColumns) => { log::debug!("Interaction::ResetColumns"); @@ -263,19 +306,39 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result { - log::debug!("Message::UpdateWowDirectory(Chosen({:?}))", &chosen_path); - let path = wow_path_resolution(chosen_path); - log::debug!("Message::UpdateWowDirectory(Resolution({:?}))", &path); + Message::UpdateWowDirectory((chosen_path, flavor)) => { + log::debug!( + "Message::UpdateWowDirectory(Chosen({:?} - {:?}))", + &chosen_path, + &flavor + ); + if let Some(path) = wow_path_resolution(chosen_path) { + log::debug!("Message::UpdateWowDirectory(Resolution({:?}))", &path); + + if let Some(flavor) = flavor { + // If a flavor is supplied we only update path for that specific flavor. + let flavor_path = ajour.config.get_flavor_directory_for_flavor(&flavor, &path); + if flavor_path.exists() { + ajour.config.wow.directories.insert(flavor, flavor_path); + } + } else { + // If no flavor is supplied it will find as many flavors as possible in the path. + let flavors = &Flavor::ALL[..]; + for flavor in flavors { + let flavor_path = + ajour.config.get_flavor_directory_for_flavor(flavor, &path); + if flavor_path.exists() { + ajour.config.wow.directories.insert(*flavor, flavor_path); + } + } + } - if path.is_some() { // Clear addons. ajour.addons = HashMap::new(); - // Update the path for World of Warcraft. - ajour.config.wow.directory = path; - // Persist the newly updated config. + + // Save config. let _ = &ajour.config.save(); - // Set loading state. + let state = ajour.state.clone(); for (mode, _) in state { if matches!(mode, Mode::MyAddons(_)) { @@ -1287,31 +1350,31 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result Option { None } +async fn select_wow_directory(flavor: Option) -> (Option, Option) { + let dialog = OpenSingleDir { dir: None }; + if let Ok(show) = dialog.show() { + return (show, flavor); + } + + (None, flavor) +} + async fn perform_read_addon_directory( addon_cache: Option>>, fingerprint_cache: Option>>,