Allow to set included/excluded directories by command line arguments (#677)

This commit is contained in:
Rafał Mikrut 2022-04-16 18:31:01 +02:00 committed by GitHub
parent aa07d73bca
commit c88d347e00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 188 additions and 124 deletions

View File

@ -53,7 +53,7 @@ pub fn connect_settings(gui_data: &GuiData) {
let button_settings_load_configuration = gui_data.settings.button_settings_load_configuration.clone();
let scrolled_window_errors = gui_data.scrolled_window_errors.clone();
button_settings_load_configuration.connect_clicked(move |_| {
load_configuration(true, &upper_notebook, &main_notebook, &settings, &text_view_errors, &scrolled_window_errors);
load_configuration(true, &upper_notebook, &main_notebook, &settings, &text_view_errors, &scrolled_window_errors, Vec::new());
});
}
// Connect reset configuration button

View File

@ -5,7 +5,11 @@
#![allow(clippy::type_complexity)]
#![allow(clippy::needless_late_init)]
use gtk::gio::ApplicationFlags;
use gtk::prelude::*;
use gtk::Application;
use std::env;
use std::ffi::OsString;
use czkawka_core::*;
@ -55,125 +59,134 @@ mod taskbar_progress_win;
mod tests;
fn main() {
let application = gtk::Application::builder().build();
application.connect_activate(move |application| {
let mut gui_data: GuiData = GuiData::new_with_application(application);
// Used for getting data from thread
let (glib_stop_sender, glib_stop_receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
// Futures progress report
let (futures_sender_duplicate_files, futures_receiver_duplicate_files): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_empty_files, futures_receiver_empty_files): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_empty_folder, futures_receiver_empty_folder): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_big_file, futures_receiver_big_file): (
futures::channel::mpsc::UnboundedSender<big_file::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<big_file::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_same_music, futures_receiver_same_music): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_similar_images, futures_receiver_similar_images): (
futures::channel::mpsc::UnboundedSender<similar_images::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<similar_images::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_similar_videos, futures_receiver_similar_videos): (
futures::channel::mpsc::UnboundedSender<similar_videos::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<similar_videos::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_temporary, futures_receiver_temporary): (
futures::channel::mpsc::UnboundedSender<temporary::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<temporary::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_invalid_symlinks, futures_receiver_invalid_symlinks): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_broken_files, futures_receiver_broken_files): (
futures::channel::mpsc::UnboundedSender<broken_files::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<broken_files::ProgressData>,
) = futures::channel::mpsc::unbounded();
initialize_gui(&mut gui_data);
validate_notebook_data(&gui_data); // Must be run after initialization of gui, to check if everything was properly setup
reset_configuration(false, &gui_data.upper_notebook, &gui_data.main_notebook, &gui_data.settings, &gui_data.text_view_errors); // Fallback for invalid loading setting project
load_system_language(&gui_data); // Check for default system language, must be loaded after initializing GUI and before loading settings from file
load_configuration(
false,
&gui_data.upper_notebook,
&gui_data.main_notebook,
&gui_data.settings,
&gui_data.text_view_errors,
&gui_data.scrolled_window_errors,
);
// Needs to run when entire GUI is initialized
connect_change_language(&gui_data);
connect_button_delete(&gui_data);
connect_button_save(&gui_data);
connect_button_search(
&gui_data,
glib_stop_sender,
futures_sender_duplicate_files,
futures_sender_empty_files,
futures_sender_empty_folder,
futures_sender_big_file,
futures_sender_same_music,
futures_sender_similar_images,
futures_sender_similar_videos,
futures_sender_temporary,
futures_sender_invalid_symlinks,
futures_sender_broken_files,
);
connect_button_select(&gui_data);
connect_button_stop(&gui_data);
connect_button_hardlink_symlink(&gui_data);
connect_button_move(&gui_data);
connect_button_compare(&gui_data);
connect_duplicate_combo_box(&gui_data);
connect_notebook_tabs(&gui_data);
connect_selection_of_directories(&gui_data);
connect_popovers(&gui_data);
connect_compute_results(&gui_data, glib_stop_receiver);
connect_progress_window(
&gui_data,
futures_receiver_duplicate_files,
futures_receiver_empty_files,
futures_receiver_empty_folder,
futures_receiver_big_file,
futures_receiver_same_music,
futures_receiver_similar_images,
futures_receiver_similar_videos,
futures_receiver_temporary,
futures_receiver_invalid_symlinks,
futures_receiver_broken_files,
);
connect_show_hide_ui(&gui_data);
connect_settings(&gui_data);
connect_button_about(&gui_data);
connect_about_buttons(&gui_data);
connect_similar_image_size_change(&gui_data);
let window_main = gui_data.window_main.clone();
let taskbar_state = gui_data.taskbar_state.clone();
window_main.connect_delete_event(move |_, _| {
save_configuration(false, &gui_data.upper_notebook, &gui_data.main_notebook, &gui_data.settings, &gui_data.text_view_errors); // Save configuration at exit
taskbar_state.borrow_mut().release();
Inhibit(false)
});
let application = gtk::Application::new(None, ApplicationFlags::HANDLES_OPEN | ApplicationFlags::HANDLES_COMMAND_LINE);
application.connect_command_line(move |app, cmdline| {
build_ui(app, cmdline.arguments());
0
});
application.run_with_args(&env::args().collect::<Vec<_>>());
}
fn build_ui(application: &Application, arguments: Vec<OsString>) {
let mut gui_data: GuiData = GuiData::new_with_application(application);
// Used for getting data from thread
let (glib_stop_sender, glib_stop_receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
// Futures progress report
let (futures_sender_duplicate_files, futures_receiver_duplicate_files): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_empty_files, futures_receiver_empty_files): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_empty_folder, futures_receiver_empty_folder): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_big_file, futures_receiver_big_file): (
futures::channel::mpsc::UnboundedSender<big_file::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<big_file::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_same_music, futures_receiver_same_music): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_similar_images, futures_receiver_similar_images): (
futures::channel::mpsc::UnboundedSender<similar_images::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<similar_images::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_similar_videos, futures_receiver_similar_videos): (
futures::channel::mpsc::UnboundedSender<similar_videos::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<similar_videos::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_temporary, futures_receiver_temporary): (
futures::channel::mpsc::UnboundedSender<temporary::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<temporary::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_invalid_symlinks, futures_receiver_invalid_symlinks): (
futures::channel::mpsc::UnboundedSender<common_dir_traversal::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<common_dir_traversal::ProgressData>,
) = futures::channel::mpsc::unbounded();
let (futures_sender_broken_files, futures_receiver_broken_files): (
futures::channel::mpsc::UnboundedSender<broken_files::ProgressData>,
futures::channel::mpsc::UnboundedReceiver<broken_files::ProgressData>,
) = futures::channel::mpsc::unbounded();
initialize_gui(&mut gui_data);
validate_notebook_data(&gui_data); // Must be run after initialization of gui, to check if everything was properly setup
reset_configuration(false, &gui_data.upper_notebook, &gui_data.main_notebook, &gui_data.settings, &gui_data.text_view_errors); // Fallback for invalid loading setting project
load_system_language(&gui_data); // Check for default system language, must be loaded after initializing GUI and before loading settings from file
load_configuration(
false,
&gui_data.upper_notebook,
&gui_data.main_notebook,
&gui_data.settings,
&gui_data.text_view_errors,
&gui_data.scrolled_window_errors,
arguments.clone(),
);
// Needs to run when entire GUI is initialized
connect_change_language(&gui_data);
connect_button_delete(&gui_data);
connect_button_save(&gui_data);
connect_button_search(
&gui_data,
glib_stop_sender,
futures_sender_duplicate_files,
futures_sender_empty_files,
futures_sender_empty_folder,
futures_sender_big_file,
futures_sender_same_music,
futures_sender_similar_images,
futures_sender_similar_videos,
futures_sender_temporary,
futures_sender_invalid_symlinks,
futures_sender_broken_files,
);
connect_button_select(&gui_data);
connect_button_stop(&gui_data);
connect_button_hardlink_symlink(&gui_data);
connect_button_move(&gui_data);
connect_button_compare(&gui_data);
connect_duplicate_combo_box(&gui_data);
connect_notebook_tabs(&gui_data);
connect_selection_of_directories(&gui_data);
connect_popovers(&gui_data);
connect_compute_results(&gui_data, glib_stop_receiver);
connect_progress_window(
&gui_data,
futures_receiver_duplicate_files,
futures_receiver_empty_files,
futures_receiver_empty_folder,
futures_receiver_big_file,
futures_receiver_same_music,
futures_receiver_similar_images,
futures_receiver_similar_videos,
futures_receiver_temporary,
futures_receiver_invalid_symlinks,
futures_receiver_broken_files,
);
connect_show_hide_ui(&gui_data);
connect_settings(&gui_data);
connect_button_about(&gui_data);
connect_about_buttons(&gui_data);
connect_similar_image_size_change(&gui_data);
let window_main = gui_data.window_main.clone();
let taskbar_state = gui_data.taskbar_state.clone();
let used_additional_arguments = arguments.len() > 1;
window_main.connect_delete_event(move |_, _| {
// Not save configuration when using non default arguments
if !used_additional_arguments {
save_configuration(false, &gui_data.upper_notebook, &gui_data.main_notebook, &gui_data.settings, &gui_data.text_view_errors);
// Save configuration at exit
}
taskbar_state.borrow_mut().release();
Inhibit(false)
});
application.run();
}

View File

@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::ffi::OsString;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
@ -20,7 +21,6 @@ use crate::help_functions::*;
use crate::language_functions::{get_language_from_combo_box_text, LANGUAGES_ALL};
use crate::localizer_core::generate_translation_hashmap;
// TODO organize this better, add specific functions that will allow to load from files specific strings
const SAVE_FILE_NAME: &str = "czkawka_gui_config_4.txt";
const DEFAULT_SAVE_ON_EXIT: bool = true;
@ -662,6 +662,7 @@ pub fn load_configuration(
settings: &GuiSettings,
text_view_errors: &TextView,
scrolled_window_errors: &ScrolledWindow,
arguments: Vec<OsString>,
) {
let text_view_errors = text_view_errors.clone();
@ -681,8 +682,8 @@ pub fn load_configuration(
// Loading data from hashmaps
let (hashmap_ls, _hashmap_sl) = create_hash_map();
let included_directories: Vec<String> = loaded_entries.get_vector_string(hashmap_ls.get(&LoadText::IncludedDirectories).unwrap().clone(), included_directories);
let excluded_directories: Vec<String> = loaded_entries.get_vector_string(hashmap_ls.get(&LoadText::ExcludedDirectories).unwrap().clone(), excluded_directories);
let mut included_directories: Vec<String> = loaded_entries.get_vector_string(hashmap_ls.get(&LoadText::IncludedDirectories).unwrap().clone(), included_directories);
let mut excluded_directories: Vec<String> = loaded_entries.get_vector_string(hashmap_ls.get(&LoadText::ExcludedDirectories).unwrap().clone(), excluded_directories);
let excluded_items: String = loaded_entries.get_string(
hashmap_ls.get(&LoadText::ExcludedItems).unwrap().clone(),
upper_notebook.entry_excluded_items.text().to_string(),
@ -752,6 +753,46 @@ pub fn load_configuration(
// Setting data
if manual_execution || loading_at_start {
{
// Handle here arguments that were added to app e.g. czkawka_gui /home --/home/roman
if loading_at_start && arguments.len() > 1 {
let iter_i = arguments.iter().skip(1);
let iter_e = iter_i.clone();
included_directories = iter_i
.filter_map(|e| {
let r = e.to_string_lossy().to_string();
if !r.starts_with("--") {
let path = Path::new(&r);
if !path.exists() {
return None;
}
match path.canonicalize() {
Ok(r) => Some(r.to_string_lossy().to_string()),
Err(_) => None,
}
} else {
None
}
})
.collect::<Vec<_>>();
excluded_directories = iter_e
.filter_map(|e| {
let r = e.to_string_lossy().to_string();
if let Some(r) = r.strip_prefix("--") {
let path = Path::new(&r);
if !path.exists() {
return None;
}
match path.canonicalize() {
Ok(r) => Some(r.to_string_lossy().to_string()),
Err(_) => None,
}
} else {
None
}
})
.collect::<Vec<_>>();
}
// Include Directories
let tree_view_included_directories = upper_notebook.tree_view_included_directories.clone();
let list_store = get_list_store(&tree_view_included_directories);

View File

@ -36,6 +36,16 @@ To open folder containing selected file, just click twice on it with right mouse
To invert a selection of files, click on a file with the middle mouse button and it will invert the selection of the other files in the same group.
### Adding directories
By default current path is loaded to included directory and excluded directories are filled with default paths.
It is possible to override this, by adding arguments when opening app e.g. `czkawka_gui /home /usr --/home/rafal --/home/zaba` which means that `/home` and `/usr` directories will be checked and `/home/rafal` and `/home/zaba` will be excluded.
When using additional command line arguments, saving at exit option become disabled, so this current info about directories will not be saved until user save it manually.
Both relative and absolute path are supported, so used can use both `../home` and `/home`.
## CLI
Czkawka CLI frontend is great to automate some tasks like removing empty directories.