From 3be988c31721e63eb9fb6951e1bd2c7d0f9eaa41 Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Mon, 15 Mar 2021 08:27:56 +0100 Subject: [PATCH 01/17] chore: initial --- crates/core/src/config/wow.rs | 4 ++ src/gui/element/settings.rs | 102 +++++++++++++++++++++++++--------- src/gui/mod.rs | 17 ++++++ src/gui/update.rs | 47 ++++++++++------ 4 files changed, 129 insertions(+), 41 deletions(-) diff --git a/crates/core/src/config/wow.rs b/crates/core/src/config/wow.rs index b0dc23ae..659fbe1b 100644 --- a/crates/core/src/config/wow.rs +++ b/crates/core/src/config/wow.rs @@ -8,6 +8,9 @@ pub struct Wow { #[serde(default)] pub directory: Option, + #[serde(default)] + pub directories: Vec, + #[serde(default)] pub flavor: Flavor, } @@ -16,6 +19,7 @@ impl Default for Wow { fn default() -> Self { Wow { directory: None, + directories: vec![], flavor: Flavor::Retail, } } diff --git a/src/gui/element/settings.rs b/src/gui/element/settings.rs index bbe2ca86..f82010bd 100644 --- a/src/gui/element/settings.rs +++ b/src/gui/element/settings.rs @@ -4,7 +4,7 @@ use { crate::gui::{ style, BackupFolderKind, BackupState, CatalogColumnKey, CatalogColumnSettings, ColumnKey, ColumnSettings, DirectoryType, GlobalReleaseChannel, Interaction, Language, Message, - ScaleState, SelfUpdateChannelState, ThemeState, + ScaleState, SelfUpdateChannelState, ThemeState, WowDirectoryState, }, crate::localization::localized_string, ajour_core::{config::Config, theme::ColorPalette}, @@ -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) @@ -48,6 +49,78 @@ pub fn data_container<'a, 'b>( // 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)); + + // 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); + + let mut wow_dir_column = Column::new(); + for wow_dir in wow_directories { + let remove_button_title_container = + Container::new(Text::new(localized_string("delete")).size(DEFAULT_FONT_SIZE)) + .width(Length::FillPortion(1)) + .center_x() + .align_x(Align::Center); + + let remove_button: Element = Button::new( + &mut wow_dir.remove_button_state, + remove_button_title_container, + ) + .style(style::DefaultDeleteButton(color_palette)) + .on_press(Interaction::SelectDirectory(DirectoryType::Backup)) + .into(); + + let path_str = wow_dir + .path + .to_str() + .unwrap_or("Error converting path to UTF-8."); + + 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::BrightBackgroundContainer(color_palette)); + + let wow_dir_row = Row::new() + .push(directory_data_text_container) + .push(Space::new(Length::Units(DEFAULT_PADDING), Length::Units(0))) + .push(remove_button.map(Message::Interaction)); + + wow_dir_column = wow_dir_column.push(wow_dir_row); + } + let directory_button_title_container = Container::new(Text::new(localized_string("select-directory")).size(DEFAULT_FONT_SIZE)) .width(Length::FillPortion(1)) @@ -60,33 +133,12 @@ pub fn data_container<'a, 'b>( .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)); - - // 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); - Column::new() .push(direction_info_text_container) .push(Space::new(Length::Units(0), Length::Units(5))) - .push(directory_data_row) + .push(wow_dir_column) + .push(Space::new(Length::Units(0), Length::Units(5))) + .push(directory_button.map(Message::Interaction)) }; let theme_column = { diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 5c6fb093..16807bac 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -224,6 +224,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 { @@ -282,6 +283,7 @@ impl Default for Ajour { localization_picklist_state: Default::default(), flavor_picklist_state: Default::default(), addons_search_state: Default::default(), + wow_directories: Default::default(), } } } @@ -934,6 +936,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) @@ -1141,6 +1144,20 @@ impl Default for InstallFromSCMState { } } +pub struct WowDirectoryState { + pub path: PathBuf, + pub remove_button_state: button::State, +} + +impl Default for WowDirectoryState { + fn default() -> Self { + WowDirectoryState { + path: PathBuf::new(), + remove_button_state: Default::default(), + } + } +} + #[derive(Debug, Clone)] pub enum ExpandType { Details(Addon), diff --git a/src/gui/update.rs b/src/gui/update.rs index 85eb7f8d..4c452a0f 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -3,7 +3,7 @@ use { Ajour, AuraColumnKey, BackupFolderKind, CatalogCategory, CatalogColumnKey, CatalogRow, CatalogSource, ColumnKey, DirectoryType, DownloadReason, ExpandType, GlobalReleaseChannel, InstallAddon, InstallKind, InstallStatus, Interaction, Message, Mode, ReleaseChannel, - SelfUpdateStatus, SortDirection, State, + SelfUpdateStatus, SortDirection, State, WowDirectoryState, }, crate::localization::{localized_string, LANG}, crate::{log_error, Result}, @@ -69,6 +69,14 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result Result { log::debug!("Interaction::FlavorSelected({})", flavor); From 3c9b4f1f4fad36004ba2f0c59bdabfa96e9aaca2 Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Mon, 15 Mar 2021 17:43:26 +0100 Subject: [PATCH 02/17] chore: cleanup --- locale/en.json | 5 +++- src/gui/element/settings.rs | 49 +++++-------------------------------- src/gui/mod.rs | 1 + src/gui/update.rs | 48 ++++++++++++++++++++++++++++++++---- 4 files changed, 54 insertions(+), 49 deletions(-) diff --git a/locale/en.json b/locale/en.json index f8c499f2..558d2c3e 100644 --- a/locale/en.json +++ b/locale/en.json @@ -120,5 +120,8 @@ "select-catalog-source-title": "Select a source in the menu", "select-catalog-source-description": "Ajour has multiple catalog sources. Select a source to browse the addons.", "auto-update": "Automatically apply new updates when available", - "type": "Type" + "type": "Type", + "wow-directories": "World of Warcraft directories", + "add-directory": "Add Directory", + "remove": "Remove" } \ No newline at end of file diff --git a/src/gui/element/settings.rs b/src/gui/element/settings.rs index f82010bd..5d034461 100644 --- a/src/gui/element/settings.rs +++ b/src/gui/element/settings.rs @@ -41,53 +41,16 @@ pub fn data_container<'a, 'b>( .style(style::Scrollable(color_palette)); let wow_directory_column = { - // Title for the World of Warcraft directory selection. + // Title for the World of Warcraft directories selection. let directory_info_text = - Text::new(localized_string("wow-directory")).size(DEFAULT_FONT_SIZE); + Text::new(localized_string("wow-directories")).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)); - - // 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); - let mut wow_dir_column = Column::new(); + for wow_dir in wow_directories { let remove_button_title_container = - Container::new(Text::new(localized_string("delete")).size(DEFAULT_FONT_SIZE)) + Container::new(Text::new(localized_string("remove")).size(DEFAULT_FONT_SIZE)) .width(Length::FillPortion(1)) .center_x() .align_x(Align::Center); @@ -97,7 +60,7 @@ pub fn data_container<'a, 'b>( remove_button_title_container, ) .style(style::DefaultDeleteButton(color_palette)) - .on_press(Interaction::SelectDirectory(DirectoryType::Backup)) + .on_press(Interaction::RemoveDirectory(wow_dir.path.clone())) .into(); let path_str = wow_dir @@ -122,7 +85,7 @@ pub fn data_container<'a, 'b>( } let directory_button_title_container = - Container::new(Text::new(localized_string("select-directory")).size(DEFAULT_FONT_SIZE)) + Container::new(Text::new(localized_string("add-directory")).size(DEFAULT_FONT_SIZE)) .width(Length::FillPortion(1)) .center_x() .align_x(Align::Center); diff --git a/src/gui/mod.rs b/src/gui/mod.rs index d717c1d4..4bc7fcc3 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -125,6 +125,7 @@ pub enum Interaction { ToggleDeleteSavedVariables(bool), AddonsQuery(String), ToggleAutoUpdateAddons(bool), + RemoveDirectory(PathBuf), } #[derive(Debug)] diff --git a/src/gui/update.rs b/src/gui/update.rs index aa13804f..ca167266 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -70,13 +70,17 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result Result { + log::debug!("Interaction::RemoveDirectory({:?})", path); + + if let Some(index) = ajour.config.wow.directories.iter().position(|p| *p == path) { + // Remove path from config. + ajour.config.wow.directories.remove(index); + let _ = &ajour.config.save(); + + // Map WoW paths to WowDirectoryState. + ajour.wow_directories = ajour + .config + .wow + .directories + .iter() + .map(|path| WowDirectoryState { + path: path.clone(), + ..Default::default() + }) + .collect(); + } + } Message::Interaction(Interaction::ResetColumns) => { log::debug!("Interaction::ResetColumns"); @@ -281,8 +306,21 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result Date: Tue, 16 Mar 2021 09:13:46 +0100 Subject: [PATCH 03/17] chore: stepping back a few steps --- crates/core/src/config/mod.rs | 21 +++---- crates/core/src/config/wow.rs | 12 +++- src/gui/update.rs | 109 +++++++++++++++------------------- 3 files changed, 68 insertions(+), 74 deletions(-) diff --git a/crates/core/src/config/mod.rs b/crates/core/src/config/mod.rs index 278bbd7c..a0e37e1b 100644 --- a/crates/core/src/config/mod.rs +++ b/crates/core/src/config/mod.rs @@ -13,7 +13,7 @@ mod wow; use crate::fs::PersistentData; pub use crate::config::addons::Addons; -pub use crate::config::wow::{Flavor, Wow}; +pub use crate::config::wow::{Directory, Flavor, Wow}; /// Config struct. #[derive(Deserialize, Serialize, Debug, PartialEq, Default, Clone)] @@ -64,6 +64,14 @@ pub struct Config { } impl Config { + pub fn get_flavor_directory_for_flavor( + &self, + flavor: &Flavor, + path: &PathBuf, + ) -> Option { + Some(PathBuf::new()) + } + /// 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 { @@ -108,16 +116,9 @@ 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. diff --git a/crates/core/src/config/wow.rs b/crates/core/src/config/wow.rs index 659fbe1b..6e6babc9 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,10 +7,11 @@ use std::path::PathBuf; #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] pub struct Wow { #[serde(default)] + // TODO: Deprecate this. pub directory: Option, #[serde(default)] - pub directories: Vec, + pub directories: HashMap, #[serde(default)] pub flavor: Flavor, @@ -19,12 +21,18 @@ impl Default for Wow { fn default() -> Self { Wow { directory: None, - directories: vec![], + directories: HashMap::new(), flavor: Flavor::Retail, } } } +#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq)] +pub struct Directory { + pub path: PathBuf, + pub flavor: Flavor, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Hash, PartialOrd, Ord)] pub enum Flavor { #[serde(alias = "retail", alias = "wow_retail")] diff --git a/src/gui/update.rs b/src/gui/update.rs index ca167266..e30bc534 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -15,7 +15,7 @@ use { AddonCache, AddonCacheEntry, FingerprintCache, }, catalog, - config::{ColumnConfig, ColumnConfigV2, Flavor}, + config::{ColumnConfig, ColumnConfigV2, Directory, Flavor}, error::{DownloadError, FilesystemError, ParseError, RepositoryError}, fs::{delete_addons, delete_saved_variables, install_addon, PersistentData}, network::download_addon, @@ -70,17 +70,20 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result Result { log::debug!("Interaction::RemoveDirectory({:?})", path); - - if let Some(index) = ajour.config.wow.directories.iter().position(|p| *p == path) { - // Remove path from config. - ajour.config.wow.directories.remove(index); - let _ = &ajour.config.save(); - - // Map WoW paths to WowDirectoryState. - ajour.wow_directories = ajour - .config - .wow - .directories - .iter() - .map(|path| WowDirectoryState { - path: path.clone(), - ..Default::default() - }) - .collect(); - } } Message::Interaction(Interaction::ResetColumns) => { log::debug!("Interaction::ResetColumns"); @@ -303,41 +288,41 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result { log::debug!("Interaction::FlavorSelected({})", flavor); From 9e47ba6a319189ead6a18fd53e81432f8f30f9e6 Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Tue, 16 Mar 2021 14:56:11 +0100 Subject: [PATCH 04/17] chore: hashmap to keep flavor, path. --- crates/core/src/config/mod.rs | 29 +++++++++----------- src/gui/element/menu.rs | 7 ++++- src/gui/mod.rs | 3 --- src/gui/update.rs | 50 ++++++++++++++++------------------- 4 files changed, 42 insertions(+), 47 deletions(-) diff --git a/crates/core/src/config/mod.rs b/crates/core/src/config/mod.rs index a0e37e1b..d978766a 100644 --- a/crates/core/src/config/mod.rs +++ b/crates/core/src/config/mod.rs @@ -64,23 +64,19 @@ pub struct Config { } impl Config { - pub fn get_flavor_directory_for_flavor( - &self, - flavor: &Flavor, - path: &PathBuf, - ) -> Option { - Some(PathBuf::new()) + /// 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 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. @@ -92,7 +88,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 { @@ -104,7 +100,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); } @@ -124,10 +120,11 @@ impl Config { /// 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. @@ -139,7 +136,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/src/gui/element/menu.rs b/src/gui/element/menu.rs index 51eb2860..701023c2 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,12 @@ pub fn data_container<'a>( weak_auras_is_installed: bool, ) -> Container<'a, Message> { let flavor = config.wow.flavor; + let valid_flavors = config + .wow + .directories + .keys() + .map(|k| k.clone()) + .collect::>(); // State. let myaddons_state = state diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 4bc7fcc3..ea567cdd 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -184,7 +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, @@ -240,7 +239,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(), @@ -389,7 +387,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, diff --git a/src/gui/update.rs b/src/gui/update.rs index e30bc534..acc55e1c 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -3,7 +3,7 @@ use { Ajour, AuraColumnKey, BackupFolderKind, CatalogCategory, CatalogColumnKey, CatalogRow, CatalogSource, ColumnKey, DirectoryType, DownloadReason, ExpandType, GlobalReleaseChannel, InstallAddon, InstallKind, InstallStatus, Interaction, Message, Mode, ReleaseChannel, - SelfUpdateStatus, SortDirection, State, WowDirectoryState, + SelfUpdateStatus, SortDirection, State, }, crate::localization::{localized_string, LANG}, crate::{log_error, Result}, @@ -15,7 +15,7 @@ use { AddonCache, AddonCacheEntry, FingerprintCache, }, catalog, - config::{ColumnConfig, ColumnConfigV2, Directory, Flavor}, + config::{ColumnConfig, ColumnConfigV2, Flavor}, error::{DownloadError, FilesystemError, ParseError, RepositoryError}, fs::{delete_addons, delete_saved_variables, install_addon, PersistentData}, network::download_addon, @@ -72,20 +72,20 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result>(); for flavor in flavors { if let Some(addon_directory) = ajour.config.get_addon_directory_for_flavor(flavor) { log::debug!( @@ -93,13 +93,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()?; } } From 55f5f57d89cc77080d5938e7616b8ca3d34bd190 Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Tue, 16 Mar 2021 22:52:26 +0100 Subject: [PATCH 05/17] chore: logic and ui done --- crates/core/src/utility.rs | 9 ++- src/gui/element/settings.rs | 102 ++++++++++++++----------------- src/gui/element/status.rs | 4 +- src/gui/mod.rs | 31 +++++----- src/gui/update.rs | 116 ++++++++++++++++++++---------------- 5 files changed, 129 insertions(+), 133 deletions(-) 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/gui/element/settings.rs b/src/gui/element/settings.rs index 5d034461..54b65b48 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, WowDirectoryState, + 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, @@ -41,67 +41,43 @@ pub fn data_container<'a, 'b>( .style(style::Scrollable(color_palette)); let wow_directory_column = { - // Title for the World of Warcraft directories selection. - let directory_info_text = - Text::new(localized_string("wow-directories")).size(DEFAULT_FONT_SIZE); - let direction_info_text_container = Container::new(directory_info_text) - .style(style::NormalBackgroundContainer(color_palette)); let mut wow_dir_column = Column::new(); - - for wow_dir in wow_directories { - let remove_button_title_container = - Container::new(Text::new(localized_string("remove")).size(DEFAULT_FONT_SIZE)) - .width(Length::FillPortion(1)) - .center_x() - .align_x(Align::Center); - - let remove_button: Element = Button::new( - &mut wow_dir.remove_button_state, - remove_button_title_container, - ) - .style(style::DefaultDeleteButton(color_palette)) - .on_press(Interaction::RemoveDirectory(wow_dir.path.clone())) - .into(); - - let path_str = wow_dir - .path - .to_str() - .unwrap_or("Error converting path to UTF-8."); - - let directory_data_text = Text::new(path_str) + 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 directory_data_text_container = Container::new(directory_data_text) + let path_text_container = Container::new(path_text) .height(Length::Units(25)) .center_y() - .style(style::BrightBackgroundContainer(color_palette)); - - let wow_dir_row = Row::new() - .push(directory_data_text_container) + .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(remove_button.map(Message::Interaction)); + .push(path_text_container); - wow_dir_column = wow_dir_column.push(wow_dir_row); + wow_dir_column = wow_dir_column.push(flavor_row); } - let directory_button_title_container = - Container::new(Text::new(localized_string("add-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(); - - Column::new() - .push(direction_info_text_container) - .push(Space::new(Length::Units(0), Length::Units(5))) - .push(wow_dir_column) - .push(Space::new(Length::Units(0), Length::Units(5))) - .push(directory_button.map(Message::Interaction)) + wow_dir_column }; let theme_column = { @@ -224,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 @@ -448,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) @@ -527,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) @@ -538,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 ea567cdd..b06a17a1 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), @@ -125,7 +126,6 @@ pub enum Interaction { ToggleDeleteSavedVariables(bool), AddonsQuery(String), ToggleAutoUpdateAddons(bool), - RemoveDirectory(PathBuf), } #[derive(Debug)] @@ -152,7 +152,7 @@ pub enum Message { Result, FilesystemError>, ), ), - UpdateWowDirectory(Option), + UpdateWowDirectory((Option, Option)), UpdateBackupDirectory(Option), RuntimeEvent(iced_native::Event), LatestBackup(Option), @@ -184,7 +184,6 @@ pub struct Ajour { settings_scrollable_state: scrollable::State, about_scrollable_state: scrollable::State, config: Config, - directory_btn_state: button::State, expanded_type: ExpandType, self_update_state: SelfUpdateState, refresh_btn_state: button::State, @@ -239,7 +238,6 @@ impl Default for Ajour { settings_scrollable_state: Default::default(), about_scrollable_state: Default::default(), config: Config::default(), - directory_btn_state: Default::default(), expanded_type: ExpandType::None, self_update_state: Default::default(), refresh_btn_state: Default::default(), @@ -282,7 +280,13 @@ impl Default for Ajour { localization_picklist_state: Default::default(), flavor_picklist_state: Default::default(), addons_search_state: Default::default(), - wow_directories: Default::default(), + wow_directories: Flavor::ALL + .iter() + .map(|f| WowDirectoryState { + flavor: *f, + button_state: Default::default(), + }) + .collect::>(), } } } @@ -920,7 +924,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, @@ -1143,15 +1146,15 @@ impl Default for InstallFromSCMState { } pub struct WowDirectoryState { - pub path: PathBuf, - pub remove_button_state: button::State, + pub flavor: Flavor, + pub button_state: button::State, } impl Default for WowDirectoryState { fn default() -> Self { WowDirectoryState { - path: PathBuf::new(), - remove_button_state: Default::default(), + flavor: Default::default(), + button_state: Default::default(), } } } @@ -1166,12 +1169,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, diff --git a/src/gui/update.rs b/src/gui/update.rs index acc55e1c..78718bcd 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}, @@ -242,18 +242,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::RemoveDirectory(path)) => { - log::debug!("Interaction::RemoveDirectory({:?})", path); + Message::Interaction(Interaction::SelectBackupDirectory()) => { + log::debug!("Interaction::SelectBackupDirectory"); + return Ok(Command::perform( + select_directory(), + Message::UpdateBackupDirectory, + )); } Message::Interaction(Interaction::ResetColumns) => { log::debug!("Interaction::ResetColumns"); @@ -277,47 +278,47 @@ 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); - - println!("path selected {:?}", &path); - - // if let Some(path) = path { - // ajour.config.wow.directories.push(path); - // let _ = &ajour.config.save(); - - // // Map WoW paths to WowDirectoryState. - // ajour.wow_directories = ajour - // .config - // .wow - // .directories - // .iter() - // .map(|path| WowDirectoryState { - // path: path.clone(), - // ..Default::default() - // }) - // .collect(); - // } - - // TODO (casper): clean up. - 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. - let _ = &ajour.config.save(); - // Set loading state. - let state = ajour.state.clone(); - for (mode, _) in state { - if matches!(mode, Mode::MyAddons(_)) { - ajour.state.insert(mode, State::Loading); + 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); + } + } + + // Clear addons. + ajour.addons = HashMap::new(); + // Save config. + ajour.config.wow.directory = None; + let _ = &ajour.config.save(); - return Ok(Command::perform(async {}, Message::Parse)); + let state = ajour.state.clone(); + for (mode, _) in state { + if matches!(mode, Mode::MyAddons(_)) { + ajour.state.insert(mode, State::Loading); + } + } + + return Ok(Command::perform(async {}, Message::Parse)); + } } } Message::Interaction(Interaction::FlavorSelected(flavor)) => { @@ -2227,6 +2228,15 @@ async fn select_directory() -> 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>>, From a4e5204a23fbcfa374cb462f6b5c6ba2d4406fca Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Tue, 16 Mar 2021 22:58:22 +0100 Subject: [PATCH 06/17] Update menu.rs --- src/gui/element/menu.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/gui/element/menu.rs b/src/gui/element/menu.rs index 701023c2..f9c4b946 100644 --- a/src/gui/element/menu.rs +++ b/src/gui/element/menu.rs @@ -33,12 +33,7 @@ pub fn data_container<'a>( weak_auras_is_installed: bool, ) -> Container<'a, Message> { let flavor = config.wow.flavor; - let valid_flavors = config - .wow - .directories - .keys() - .map(|k| k.clone()) - .collect::>(); + let valid_flavors = config.wow.directories.keys().copied(); // State. let myaddons_state = state From b933d76df61f3c0619da5466c5883200f456f40c Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Tue, 16 Mar 2021 23:02:23 +0100 Subject: [PATCH 07/17] chore: deprecated directory --- crates/core/src/config/mod.rs | 2 +- crates/core/src/config/wow.rs | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/crates/core/src/config/mod.rs b/crates/core/src/config/mod.rs index d978766a..c87a0889 100644 --- a/crates/core/src/config/mod.rs +++ b/crates/core/src/config/mod.rs @@ -13,7 +13,7 @@ mod wow; use crate::fs::PersistentData; pub use crate::config::addons::Addons; -pub use crate::config::wow::{Directory, Flavor, Wow}; +pub use crate::config::wow::{Flavor, Wow}; /// Config struct. #[derive(Deserialize, Serialize, Debug, PartialEq, Default, Clone)] diff --git a/crates/core/src/config/wow.rs b/crates/core/src/config/wow.rs index 6e6babc9..3b5e61a7 100644 --- a/crates/core/src/config/wow.rs +++ b/crates/core/src/config/wow.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] pub struct Wow { #[serde(default)] - // TODO: Deprecate this. + #[deprecated(since = "0.8.0")] pub directory: Option, #[serde(default)] @@ -27,12 +27,6 @@ impl Default for Wow { } } -#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq)] -pub struct Directory { - pub path: PathBuf, - pub flavor: Flavor, -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Hash, PartialOrd, Ord)] pub enum Flavor { #[serde(alias = "retail", alias = "wow_retail")] From 9ea9dd38a8bebbac9f38a2b1c579d5691bea0687 Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Tue, 16 Mar 2021 23:23:58 +0100 Subject: [PATCH 08/17] chore: removing deprecated usage of directory --- src/gui/element/menu.rs | 7 ++++++- src/gui/element/settings.rs | 2 +- src/gui/update.rs | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/gui/element/menu.rs b/src/gui/element/menu.rs index f9c4b946..a9c9cdd4 100644 --- a/src/gui/element/menu.rs +++ b/src/gui/element/menu.rs @@ -33,7 +33,12 @@ pub fn data_container<'a>( weak_auras_is_installed: bool, ) -> Container<'a, Message> { let flavor = config.wow.flavor; - let valid_flavors = config.wow.directories.keys().copied(); + let valid_flavors = config + .wow + .directories + .keys() + .copied() + .collect::>(); // State. let myaddons_state = state diff --git a/src/gui/element/settings.rs b/src/gui/element/settings.rs index 54b65b48..f7b9799e 100644 --- a/src/gui/element/settings.rs +++ b/src/gui/element/settings.rs @@ -252,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().is_empty() && (config.backup_addons || config.backup_wtf) { backup_button = backup_button.on_press(Interaction::Backup); diff --git a/src/gui/update.rs b/src/gui/update.rs index 78718bcd..61049d1b 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -306,8 +306,8 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result Date: Tue, 16 Mar 2021 23:57:56 +0100 Subject: [PATCH 09/17] chore: updated cli --- crates/core/src/config/mod.rs | 9 +++++++++ crates/core/src/config/wow.rs | 2 +- src/command/backup.rs | 9 ++++++--- src/gui/element/settings.rs | 2 +- src/gui/update.rs | 32 ++++++++++++++++---------------- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/crates/core/src/config/mod.rs b/crates/core/src/config/mod.rs index c87a0889..5b56c87e 100644 --- a/crates/core/src/config/mod.rs +++ b/crates/core/src/config/mod.rs @@ -69,6 +69,15 @@ impl Config { 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(Box::new(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 { diff --git a/crates/core/src/config/wow.rs b/crates/core/src/config/wow.rs index 3b5e61a7..46d60724 100644 --- a/crates/core/src/config/wow.rs +++ b/crates/core/src/config/wow.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] pub struct Wow { #[serde(default)] - #[deprecated(since = "0.8.0")] + #[allow(deprecated)] pub directory: Option, #[serde(default)] diff --git a/src/command/backup.rs b/src/command/backup.rs index 41d0042d..6f2b8382 100644 --- a/src/command/backup.rs +++ b/src/command/backup.rs @@ -26,7 +26,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: {:?}", @@ -38,8 +40,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/settings.rs b/src/gui/element/settings.rs index f7b9799e..5f8563ea 100644 --- a/src/gui/element/settings.rs +++ b/src/gui/element/settings.rs @@ -252,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.directories.keys().is_empty() + && config.wow.directories.keys().next().is_some() && (config.backup_addons || config.backup_wtf) { backup_button = backup_button.on_press(Interaction::Backup); diff --git a/src/gui/update.rs b/src/gui/update.rs index 61049d1b..c1ef9933 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -1322,31 +1322,31 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result Date: Wed, 17 Mar 2021 00:06:40 +0100 Subject: [PATCH 10/17] Update en.json --- locale/en.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/locale/en.json b/locale/en.json index 558d2c3e..7f9e77cd 100644 --- a/locale/en.json +++ b/locale/en.json @@ -121,7 +121,5 @@ "select-catalog-source-description": "Ajour has multiple catalog sources. Select a source to browse the addons.", "auto-update": "Automatically apply new updates when available", "type": "Type", - "wow-directories": "World of Warcraft directories", - "add-directory": "Add Directory", - "remove": "Remove" + "wow-directories": "World of Warcraft directories" } \ No newline at end of file From 8015685f20a48b067e68b74064ad9700e1b726d8 Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm <2248455+casperstorm@users.noreply.github.com> Date: Wed, 17 Mar 2021 00:15:27 +0100 Subject: [PATCH 11/17] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f9fbfed..1609b0f1 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 From 89c78cec9ee8cec17a75bd342a88a40f6fdc5aa1 Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Wed, 17 Mar 2021 00:28:54 +0100 Subject: [PATCH 12/17] Update update.rs --- src/gui/update.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/gui/update.rs b/src/gui/update.rs index c1ef9933..72e3bb75 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -303,22 +303,22 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result { From 9a8de534d412dd8f6cf7fb7e31e17fd4242bfe6f Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Thu, 18 Mar 2021 08:29:27 +0100 Subject: [PATCH 13/17] chore: removed unnecessary box --- crates/core/src/config/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/src/config/mod.rs b/crates/core/src/config/mod.rs index 5b56c87e..755fbfeb 100644 --- a/crates/core/src/config/mod.rs +++ b/crates/core/src/config/mod.rs @@ -72,7 +72,7 @@ impl Config { /// 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(Box::new(flavor_dir.parent().unwrap()).to_path_buf()) + Some(flavor_dir.parent().unwrap().to_path_buf()) } else { None } From 2285ed23d91203b01522ba9e3b74b67be0dcf62f Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Thu, 18 Mar 2021 08:49:26 +0100 Subject: [PATCH 14/17] chore: moved migration to apply_config. --- src/gui/mod.rs | 17 +++++++++++++++++ src/gui/update.rs | 15 --------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/gui/mod.rs b/src/gui/mod.rs index b06a17a1..ab1f601d 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -2394,4 +2394,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 72e3bb75..3dcef10d 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -70,21 +70,6 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result>(); for flavor in flavors { if let Some(addon_directory) = ajour.config.get_addon_directory_for_flavor(flavor) { From 632a1db43ec856ab69ed80e87834181cb0dfa605 Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Fri, 19 Mar 2021 20:04:26 +0100 Subject: [PATCH 15/17] chore: add or deletes paths dynamically --- src/gui/update.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/gui/update.rs b/src/gui/update.rs index 3dcef10d..80e44d97 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -70,6 +70,49 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result = vec![]; + for flavor in Flavor::ALL.iter() { + if let None = ajour.config.wow.directories.get(flavor) { + 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) { From 5432cfde7ebed2e39e65c3f16a320a39f4192acb Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Fri, 19 Mar 2021 20:10:35 +0100 Subject: [PATCH 16/17] Update update.rs --- src/gui/update.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/update.rs b/src/gui/update.rs index 80e44d97..168c648f 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -74,7 +74,7 @@ pub fn handle_message(ajour: &mut Ajour, message: Message) -> Result = vec![]; for flavor in Flavor::ALL.iter() { - if let None = ajour.config.wow.directories.get(flavor) { + if ajour.config.wow.directories.get(flavor).is_none() { missing_flavors.push(flavor); } } From e20671c0ffe9f600094fe23508245e0b272c0c56 Mon Sep 17 00:00:00 2001 From: Casper Rogild Storm Date: Mon, 22 Mar 2021 18:56:04 +0100 Subject: [PATCH 17/17] Update menu.rs --- src/gui/element/menu.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/element/menu.rs b/src/gui/element/menu.rs index a9c9cdd4..c0ecbf5c 100644 --- a/src/gui/element/menu.rs +++ b/src/gui/element/menu.rs @@ -33,13 +33,15 @@ pub fn data_container<'a>( weak_auras_is_installed: bool, ) -> Container<'a, Message> { let flavor = config.wow.flavor; - let valid_flavors = config + let mut valid_flavors = config .wow .directories .keys() .copied() .collect::>(); + valid_flavors.sort(); + // State. let myaddons_state = state .get(&Mode::MyAddons(flavor))