Skip to content

Commit

Permalink
Implemented multifolder support for "Add from Folder".
Browse files Browse the repository at this point in the history
  • Loading branch information
Frodo45127 committed Feb 14, 2024
1 parent 948de24 commit 7f6468c
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 7 deletions.
3 changes: 3 additions & 0 deletions locale/English_en.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1568,3 +1568,6 @@ update_anim_ids_instructions = <p>This allows you to update the Anim Ids of all
starting_id = Starting ID:
offset = Offset:
instructions = Instructions
enable_multifolder_filepicker = Enable Multi-Folder FilePicker
settings_enable_multifolder_filepicker = This replace the System File-Picker that appears when using "Add from Folder" with a generic Qt FilePicker that allows importing multiple folders at once.
70 changes: 63 additions & 7 deletions rpfm_ui/src/packfile_contents_ui/slots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@
Module with all the code related to the main `PackFileContentsSlots`.
!*/

use qt_widgets::{QFileDialog, q_file_dialog::FileMode};
use qt_widgets::{QFileDialog, q_file_dialog::{FileMode, Option as FileDialogOption}};
use qt_widgets::QListView;
use qt_widgets::SlotOfQPoint;
use qt_widgets::QTreeView;

use qt_gui::QCursor;
use qt_gui::QGuiApplication;

use qt_core::QBox;
use qt_core::{SlotNoArgs, SlotOfBool, SlotOfQModelIndexInt, SlotOfQString};
use qt_core::QFlags;
use qt_core::QPtr;
use qt_core::QString;
use qt_core::{SlotNoArgs, SlotOfBool, SlotOfQModelIndexInt, SlotOfQString};

use std::collections::HashSet;
use std::fs::DirBuilder;
Expand Down Expand Up @@ -662,7 +664,22 @@ impl PackFileContentsSlots {
app_ui.main_window(),
&qtr("context_menu_add_folders"),
);
file_dialog.set_file_mode(FileMode::Directory);

file_dialog.set_file_mode(FileMode::DirectoryOnly);

// Wonky workaround to allow multiple folder selection.
if setting_bool("enable_multifolder_filepicker") {
file_dialog.set_options(QFlags::from(FileDialogOption::DontUseNativeDialog.to_int() | file_dialog.options().to_int()));

if let Ok(list_view) = file_dialog.find_child::<QListView>("listView") {
list_view.set_selection_mode(qt_widgets::q_abstract_item_view::SelectionMode::MultiSelection);
}

if let Ok(tree_view) = file_dialog.find_child::<QTreeView>("treeView") {
tree_view.set_selection_mode(qt_widgets::q_abstract_item_view::SelectionMode::MultiSelection);
}
}

match UI_STATE.get_operational_mode() {

// If we have a "MyMod" selected...
Expand Down Expand Up @@ -691,11 +708,31 @@ impl PackFileContentsSlots {
// Get the Paths of the folders we want to add.
let mut folder_paths: Vec<PathBuf> = vec![];
let paths_qt = file_dialog.selected_files();
for index in 0..paths_qt.size() { folder_paths.push(PathBuf::from(paths_qt.at(index).to_std_string())); }
for index in 0..paths_qt.size() {
folder_paths.push(PathBuf::from(paths_qt.at(index).to_std_string()));
}

// Make sure all folders are part of the same subfolder. The multifolder selector can accidentally add folders with different base paths,
// and we need to avoid that.
if let Some(base_path) = folder_paths.get(0) {
let mut base_path = base_path.to_path_buf();
base_path.pop();

for folder_path in &folder_paths {
let mut second_path = folder_path.to_path_buf();
second_path.pop();

if base_path != second_path {
return show_dialog(app_ui.main_window(), format!("Error: adding multiple folders from different parent folders is not supported."), false);
}
}
}

// Get the Paths of the files inside the folders we want to add.
let mut paths: Vec<PathBuf> = vec![];
for path in &folder_paths { paths.append(&mut files_from_subdir(path, true).unwrap()); }
for path in &folder_paths {
paths.append(&mut files_from_subdir(path, true).unwrap());
}

// Check to ensure we actually have a path, as you may try to add empty folders.
if let Some(path) = paths.get(0) {
Expand All @@ -712,8 +749,10 @@ impl PackFileContentsSlots {

// Otherwise, they are added like normal files.
else if let Some(selection) = pack_file_contents_ui.packfile_contents_tree_view.get_path_from_selection().get(0) {
let destination_paths = (0..folder_paths.len()).map(|_| ContainerPath::Folder(selection.to_string())).collect::<Vec<_>>();

app_ui.toggle_main_window(false);
PackFileContentsUI::add_files(&app_ui, &pack_file_contents_ui, &folder_paths, &[ContainerPath::Folder(selection.to_string())], None);
PackFileContentsUI::add_files(&app_ui, &pack_file_contents_ui, &folder_paths, &destination_paths, None);
app_ui.toggle_main_window(true);
}
}
Expand All @@ -739,11 +778,28 @@ impl PackFileContentsSlots {
folder_paths.push(PathBuf::from(paths_qt.at(index).to_std_string()));
}

// Make sure all folders are part of the same subfolder. The multifolder selector can accidentally add folders with different base paths,
// and we need to avoid that.
if let Some(base_path) = folder_paths.get(0) {
let mut base_path = base_path.to_path_buf();
base_path.pop();

for folder_path in &folder_paths {
let mut second_path = folder_path.to_path_buf();
second_path.pop();

if base_path != second_path {
return show_dialog(app_ui.main_window(), format!("Error: adding multiple folders from different parent folders is not supported."), false);
}
}
}

// Get the Paths of the files inside the folders we want to add.
if let Some(selection) = pack_file_contents_ui.packfile_contents_tree_view.get_path_from_selection().get(0) {
let destination_paths = (0..folder_paths.len()).map(|_| ContainerPath::Folder(selection.to_string())).collect::<Vec<_>>();

app_ui.toggle_main_window(false);
PackFileContentsUI::add_files(&app_ui, &pack_file_contents_ui, &folder_paths, &[ContainerPath::Folder(selection.to_string())], None);
PackFileContentsUI::add_files(&app_ui, &pack_file_contents_ui, &folder_paths, &destination_paths, None);
app_ui.toggle_main_window(true);
}
}
Expand Down
1 change: 1 addition & 0 deletions rpfm_ui/src/settings_ui/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ pub unsafe fn init_settings(main_window: &QPtr<QMainWindow>) {
set_setting_if_new_bool(&q_settings, "delete_empty_folders_on_delete", true);
set_setting_if_new_bool(&q_settings, "autosave_folder_size_warning_triggered", false);
set_setting_if_new_bool(&q_settings, "ignore_game_files_in_ak", false);
set_setting_if_new_bool(&q_settings, "enable_multifolder_filepicker", false);

// Table Settings.
set_setting_if_new_bool(&q_settings, "adjust_columns_to_content", true);
Expand Down
12 changes: 12 additions & 0 deletions rpfm_ui/src/settings_ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ pub struct SettingsUI {
include_base_folder_on_add_from_folder_label: QBox<QLabel>,
delete_empty_folders_on_delete_label: QBox<QLabel>,
ignore_game_files_in_ak_label: QBox<QLabel>,
enable_multifolder_filepicker_label: QBox<QLabel>,

general_language_combobox: QBox<QComboBox>,
extra_global_default_game_combobox: QBox<QComboBox>,
Expand All @@ -130,6 +131,7 @@ pub struct SettingsUI {
include_base_folder_on_add_from_folder_checkbox: QBox<QCheckBox>,
delete_empty_folders_on_delete_checkbox: QBox<QCheckBox>,
ignore_game_files_in_ak_checkbox: QBox<QCheckBox>,
enable_multifolder_filepicker_checkbox: QBox<QCheckBox>,

font_data: Rc<RefCell<(String, i32)>>,

Expand Down Expand Up @@ -427,6 +429,9 @@ impl SettingsUI {
let ignore_game_files_in_ak_label = QLabel::from_q_string_q_widget(&qtr("ignore_game_files_in_ak"), &general_frame);
let ignore_game_files_in_ak_checkbox = QCheckBox::from_q_widget(&general_frame);

let enable_multifolder_filepicker_label = QLabel::from_q_string_q_widget(&qtr("enable_multifolder_filepicker"), &general_frame);
let enable_multifolder_filepicker_checkbox = QCheckBox::from_q_widget(&general_frame);

// Adding to the grid.
general_grid.add_widget_5a(&general_language_label, 0, 0, 1, 1);
general_grid.add_widget_5a(&general_language_combobox, 0, 1, 1, 1);
Expand Down Expand Up @@ -488,6 +493,9 @@ impl SettingsUI {
general_grid.add_widget_5a(&ignore_game_files_in_ak_label, 20, 0, 1, 1);
general_grid.add_widget_5a(&ignore_game_files_in_ak_checkbox, 20, 1, 1, 1);

general_grid.add_widget_5a(&enable_multifolder_filepicker_label, 21, 0, 1, 1);
general_grid.add_widget_5a(&enable_multifolder_filepicker_checkbox, 21, 1, 1, 1);

settings_grid.add_widget_5a(&general_frame, 2, 0, 2, 1);

//-----------------------------------------------//
Expand Down Expand Up @@ -766,6 +774,7 @@ impl SettingsUI {
include_base_folder_on_add_from_folder_label,
delete_empty_folders_on_delete_label,
ignore_game_files_in_ak_label,
enable_multifolder_filepicker_label,

general_language_combobox,
extra_global_default_game_combobox,
Expand All @@ -789,6 +798,7 @@ impl SettingsUI {
include_base_folder_on_add_from_folder_checkbox,
delete_empty_folders_on_delete_checkbox,
ignore_game_files_in_ak_checkbox,
enable_multifolder_filepicker_checkbox,

font_data: Rc::new(RefCell::new((String::new(), -1))),

Expand Down Expand Up @@ -931,6 +941,7 @@ impl SettingsUI {
self.include_base_folder_on_add_from_folder_checkbox.set_checked(setting_bool_from_q_setting(&q_settings, "include_base_folder_on_add_from_folder"));
self.delete_empty_folders_on_delete_checkbox.set_checked(setting_bool_from_q_setting(&q_settings, "delete_empty_folders_on_delete"));
self.ignore_game_files_in_ak_checkbox.set_checked(setting_bool_from_q_setting(&q_settings, "ignore_game_files_in_ak"));
self.enable_multifolder_filepicker_checkbox.set_checked(setting_bool_from_q_setting(&q_settings, "enable_multifolder_filepicker"));

// Load the Table Stuff.
self.ui_table_adjust_columns_to_content_checkbox.set_checked(setting_bool_from_q_setting(&q_settings, "adjust_columns_to_content"));
Expand Down Expand Up @@ -1046,6 +1057,7 @@ impl SettingsUI {
set_setting_bool_to_q_setting(&q_settings, "include_base_folder_on_add_from_folder", self.include_base_folder_on_add_from_folder_checkbox.is_checked());
set_setting_bool_to_q_setting(&q_settings, "delete_empty_folders_on_delete", self.delete_empty_folders_on_delete_checkbox.is_checked());
set_setting_bool_to_q_setting(&q_settings, "ignore_game_files_in_ak", self.ignore_game_files_in_ak_checkbox.is_checked());
set_setting_bool_to_q_setting(&q_settings, "enable_multifolder_filepicker", self.enable_multifolder_filepicker_checkbox.is_checked());

// Get the Table Settings.
set_setting_bool_to_q_setting(&q_settings, "adjust_columns_to_content", self.ui_table_adjust_columns_to_content_checkbox.is_checked());
Expand Down
3 changes: 3 additions & 0 deletions rpfm_ui/src/settings_ui/tips.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub unsafe fn set_tips(settings_ui: &Rc<SettingsUI>) {
let include_base_folder_on_add_from_folder = qtr("settings_include_base_folder_on_add_from_folder");
let delete_empty_folders_on_delete = qtr("settings_delete_empty_folders_on_delete");
let ignore_game_files_in_ak = qtr("settings_ignore_game_files_in_ak");
let enable_multifolder_filepicker = qtr("settings_enable_multifolder_filepicker");

settings_ui.ui_global_use_dark_theme_label.set_tool_tip(&ui_global_use_dark_theme_tip);
settings_ui.ui_global_use_dark_theme_checkbox.set_tool_tip(&ui_global_use_dark_theme_tip);
Expand All @@ -60,6 +61,8 @@ pub unsafe fn set_tips(settings_ui: &Rc<SettingsUI>) {
settings_ui.delete_empty_folders_on_delete_checkbox.set_tool_tip(&delete_empty_folders_on_delete);
settings_ui.ignore_game_files_in_ak_label.set_tool_tip(&ignore_game_files_in_ak);
settings_ui.ignore_game_files_in_ak_checkbox.set_tool_tip(&ignore_game_files_in_ak);
settings_ui.enable_multifolder_filepicker_label.set_tool_tip(&enable_multifolder_filepicker);
settings_ui.enable_multifolder_filepicker_checkbox.set_tool_tip(&enable_multifolder_filepicker);

//-----------------------------------------------//
// `Extra` tips.
Expand Down

0 comments on commit 7f6468c

Please sign in to comment.