1
0
Fork 0
mirror of synced 2024-04-28 17:42:26 +12:00

Add option to save file, store settings and load them (#108)

This commit is contained in:
Rafał Mikrut 2020-12-07 15:25:44 +01:00 committed by GitHub
parent a047380dbe
commit 6fab1a9368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 580 additions and 76 deletions

58
Cargo.lock generated
View file

@ -23,9 +23,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.34"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7"
checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4"
[[package]]
name = "approx"
@ -239,9 +239,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.65"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
[[package]]
name = "cfg-if"
@ -438,6 +438,7 @@ dependencies = [
"chrono",
"crossbeam-channel 0.4.4",
"czkawka_core",
"directories-next",
"futures",
"gdk",
"gio",
@ -511,6 +512,16 @@ dependencies = [
"generic-array",
]
[[package]]
name = "directories-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
dependencies = [
"cfg-if 1.0.0",
"dirs-sys-next",
]
[[package]]
name = "dirs"
version = "3.0.1"
@ -531,6 +542,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99de365f605554ae33f115102a02057d4fc18b01f3284d6870be0938743cfe7d"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "discard"
version = "1.0.4"
@ -1099,15 +1121,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.80"
version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
[[package]]
name = "libloading"
version = "0.6.5"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1090080fe06ec2648d0da3881d9453d97e71a45f00eb179af7fdd7e3f686fdb0"
checksum = "e9367bdfa836b7e3cf895867f7a570283444da90562980ec2263d6e1569b16bc"
dependencies = [
"cfg-if 1.0.0",
"winapi",
@ -1474,9 +1496,9 @@ dependencies = [
[[package]]
name = "ordered-float"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3741934be594d77de1c8461ebcbbe866f585ea616a9753aa78f2bdc69f0e4579"
checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7"
dependencies = [
"num-traits",
]
@ -2020,18 +2042,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.117"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.117"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
dependencies = [
"proc-macro2 1.0.24",
"quote 1.0.7",
@ -2040,9 +2062,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.59"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
dependencies = [
"itoa",
"ryu",
@ -2063,9 +2085,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85"
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
[[package]]
name = "spin_sleep"

View file

@ -16,6 +16,7 @@ But the most important thing for me was to learn Rust and create a program usefu
## Features
- Written in memory safe Rust
- Amazingly fast - due using more or less advanced algorithms and multithreading support
- Free, Open Source without ads
- CLI frontend, very fast and powerful with rich help
- GUI GTK frontend - uses modern GTK 3 and looks similar to FSlint
- Light/Dark theme match the appearance of the system(Linux only)

View file

@ -17,10 +17,15 @@ glib = "0.10.1"
humansize = "1"
chrono = "0.4"
# Used for sending stop signal across threads
crossbeam-channel = "0.4.4"
# To get informations about progress
futures = "0.3.8"
# For saving/loading config files to specific directories
directories-next = "2.0.0"
# For opening files
open = "1.4.0"

View file

@ -881,6 +881,104 @@ Author: Rafał Mikrut
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="notebook_upper_settings">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_start">5</property>
<property name="margin_end">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkCheckButton" id="check_button_settings_save_at_exit">
<property name="label" translatable="yes">Save configuration at exit</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_settings_save_configuration">
<property name="label" translatable="yes">Save current configuration</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_settings_load_configuration">
<property name="label" translatable="yes">Load configuration</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_settings_reset_configuration">
<property name="label" translatable="yes">Reset configuration</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="check_button_settings_load_at_start">
<property name="label" translatable="yes">Load configuration at start</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="position">4</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Settings</property>
</object>
<packing>
<property name="position">4</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>

View file

@ -34,13 +34,12 @@ pub fn connect_notebook_tabs(gui_data: &GuiData) {
set_buttons(&mut *shared_buttons.borrow_mut().get_mut(page).unwrap(), &buttons_array, &buttons_names);
// Upper notebook
{
//let upper_notebooks_labels = [/*"general",*/"included_directories","excluded_directories","excluded_items","allowed_extensions"];
let mut hashmap: HashMap<&str, &str> = Default::default();
//hashmap.insert("notebook_upper_general","general");
hashmap.insert("notebook_upper_included_directories", "included_directories");
hashmap.insert("notebook_upper_excluded_directories", "excluded_directories");
hashmap.insert("notebook_upper_excluded_items", "excluded_items");
hashmap.insert("notebook_upper_allowed_extensions", "allowed_extensions");
hashmap.insert("notebook_upper_settings", "settings");
for tab in &notebook_upper_children_names {
let name = hashmap.get(tab.as_str()).unwrap().to_string();

View file

@ -2,7 +2,7 @@ extern crate gtk;
use crate::gui_data::GuiData;
use gtk::prelude::*;
pub fn connect_upper_notebook(gui_data: &GuiData) {
pub fn connect_selection_of_directories(gui_data: &GuiData) {
// Add included directory
{
let scrolled_window_included_directories = gui_data.scrolled_window_included_directories.clone();

View file

@ -0,0 +1,31 @@
extern crate gtk;
use crate::gui_data::GuiData;
use crate::saving_loading::{load_configuration, reset_configuration, save_configuration};
use gtk::prelude::*;
pub fn connect_settings(gui_data: &GuiData) {
// Connect save configuration button
{
let gui_data = gui_data.clone();
let button_settings_save_configuration = gui_data.button_settings_save_configuration.clone();
button_settings_save_configuration.connect_clicked(move |_| {
save_configuration(&gui_data, true);
});
}
// Connect load configuration button
{
let gui_data = gui_data.clone();
let button_settings_load_configuration = gui_data.button_settings_load_configuration.clone();
button_settings_load_configuration.connect_clicked(move |_| {
load_configuration(&gui_data, true);
});
}
// Connect reset configuration button
{
let gui_data = gui_data.clone();
let button_settings_reset_configuration = gui_data.button_settings_reset_configuration.clone();
button_settings_reset_configuration.connect_clicked(move |_| {
reset_configuration(&gui_data, true);
});
}
}

View file

@ -26,7 +26,7 @@ pub struct GuiData {
// States
pub main_notebooks_labels: [String; 8],
pub upper_notebooks_labels: [String; 4],
pub upper_notebooks_labels: [String; 5],
pub buttons_labels: [String; 4],
// Buttons state
pub shared_buttons: Rc<RefCell<HashMap<String, HashMap<String, bool>>>>,
@ -145,6 +145,14 @@ pub struct GuiData {
pub grid_progress_stages: gtk::Grid,
pub button_stop_in_dialog: gtk::Button,
//// Settings
pub check_button_settings_save_at_exit: gtk::CheckButton,
pub check_button_settings_load_at_start: gtk::CheckButton,
pub button_settings_save_configuration: gtk::Button,
pub button_settings_load_configuration: gtk::Button,
pub button_settings_reset_configuration: gtk::Button,
//// Threads
// Used for sending stop signal to thread
@ -176,10 +184,11 @@ impl GuiData {
"same_music".to_string(),
];
let upper_notebooks_labels = [
/*"general",*/ "included_directories".to_string(),
"included_directories".to_string(),
"excluded_directories".to_string(),
"excluded_items".to_string(),
"allowed_extensions".to_string(),
"settings".to_string(),
];
let buttons_labels = ["search".to_string(), "select".to_string(), "delete".to_string(), "save".to_string()];
@ -337,6 +346,14 @@ impl GuiData {
let button_stop_in_dialog: gtk::Button = builder.get_object("button_stop_in_dialog").unwrap();
//// Settings
let check_button_settings_save_at_exit: gtk::CheckButton = builder.get_object("check_button_settings_save_at_exit").unwrap();
let check_button_settings_load_at_start: gtk::CheckButton = builder.get_object("check_button_settings_load_at_start").unwrap();
let button_settings_save_configuration: gtk::Button = builder.get_object("button_settings_save_configuration").unwrap();
let button_settings_load_configuration: gtk::Button = builder.get_object("button_settings_load_configuration").unwrap();
let button_settings_reset_configuration: gtk::Button = builder.get_object("button_settings_reset_configuration").unwrap();
//// Threads
// Types of messages to send to main thread where gui can be draw.
@ -426,6 +443,11 @@ impl GuiData {
label_stage,
grid_progress_stages,
button_stop_in_dialog,
check_button_settings_save_at_exit,
check_button_settings_load_at_start,
button_settings_save_configuration,
button_settings_load_configuration,
button_settings_reset_configuration,
stop_sender,
stop_receiver,
}

View file

@ -4,9 +4,8 @@ use crate::gui_data::*;
use crate::help_functions::*;
use gtk::prelude::*;
use gtk::{SelectionMode, TreeView};
use std::env;
pub fn startup_configuration(gui_data: &GuiData) {
pub fn initialize_gui(gui_data: &GuiData) {
//// Setup default look(duplicate finder)
{
let entry_info = gui_data.entry_info.clone();
@ -24,7 +23,6 @@ pub fn startup_configuration(gui_data: &GuiData) {
let scrolled_window_zeroed_files_finder = gui_data.scrolled_window_zeroed_files_finder.clone();
let scrolled_window_included_directories = gui_data.scrolled_window_included_directories.clone();
let scrolled_window_excluded_directories = gui_data.scrolled_window_excluded_directories.clone();
let entry_excluded_items = gui_data.entry_excluded_items.clone();
entry_info.set_text("Duplicated Files");
@ -201,7 +199,7 @@ pub fn startup_configuration(gui_data: &GuiData) {
// Set Included Directory
{
let col_types: [glib::types::Type; 2] = [glib::types::Type::String, glib::types::Type::String];
let col_types: [glib::types::Type; 1] = [glib::types::Type::String];
let list_store: gtk::ListStore = gtk::ListStore::new(&col_types);
let mut tree_view_included_directory: gtk::TreeView = TreeView::with_model(&list_store);
@ -210,32 +208,12 @@ pub fn startup_configuration(gui_data: &GuiData) {
create_tree_view_directories(&mut tree_view_included_directory);
let col_indices = [0, 1];
let current_dir: String = match env::current_dir() {
Ok(t) => t.to_str().unwrap().to_string(),
Err(_) => {
if cfg!(target_family = "unix") {
println!("Failed to read current directory, setting /home instead");
"/home".to_string()
} else if cfg!(target_family = "windows") {
println!("Failed to read current directory, setting C:\\ instead");
"C:\\".to_string()
} else {
"".to_string()
}
}
};
let values: [&dyn ToValue; 2] = [&current_dir, &(MAIN_ROW_COLOR.to_string())];
list_store.set(&list_store.append(), &col_indices, &values);
scrolled_window_included_directories.add(&tree_view_included_directory);
scrolled_window_included_directories.show_all();
}
// Set Excluded Directory
{
let col_types: [glib::types::Type; 2] = [glib::types::Type::String, glib::types::Type::String];
let col_types: [glib::types::Type; 1] = [glib::types::Type::String];
let list_store: gtk::ListStore = gtk::ListStore::new(&col_types);
let mut tree_view_excluded_directory: gtk::TreeView = TreeView::with_model(&list_store);
@ -244,26 +222,8 @@ pub fn startup_configuration(gui_data: &GuiData) {
create_tree_view_directories(&mut tree_view_excluded_directory);
let col_indices = [0, 1];
if cfg!(target_family = "unix") {
for i in ["/proc", "/dev", "/sys", "/run", "/snap"].iter() {
let values: [&dyn ToValue; 2] = [&i, &(MAIN_ROW_COLOR.to_string())];
list_store.set(&list_store.append(), &col_indices, &values);
}
}
scrolled_window_excluded_directories.add(&tree_view_excluded_directory);
scrolled_window_excluded_directories.show_all();
}
// Set Excluded Items
{
if cfg!(target_family = "unix") {
entry_excluded_items.set_text("*/.git/*,*/node_modules/*,*/lost+found/*,*/Trash/*");
}
if cfg!(target_family = "windows") {
entry_excluded_items.set_text("*/.git/*,*/node_modules/*,*/lost+found/*,*:/windows/*");
}
}
}
}

View file

@ -7,12 +7,14 @@ mod connect_compute_results;
mod connect_notebook_tabs;
mod connect_popovers;
mod connect_progress_window;
mod connect_upper_notebook;
mod connect_selection_of_directories;
mod connect_settings;
mod create_tree_view;
mod double_click_opening;
mod gui_data;
mod help_functions;
mod startup_configuration;
mod initialize_gui;
mod saving_loading;
use czkawka_core::*;
@ -26,9 +28,11 @@ use crate::connect_compute_results::*;
use crate::connect_notebook_tabs::*;
use crate::connect_popovers::*;
use crate::connect_progress_window::*;
use crate::connect_upper_notebook::*;
use crate::connect_selection_of_directories::*;
use crate::connect_settings::*;
use crate::gui_data::*;
use crate::startup_configuration::*;
use crate::initialize_gui::*;
use crate::saving_loading::{load_configuration, reset_configuration, save_configuration};
use gtk::prelude::*;
use std::{env, process};
@ -66,7 +70,10 @@ fn main() {
let (futures_sender_temporary, futures_receiver_temporary): (futures::channel::mpsc::Sender<temporary::ProgressData>, futures::channel::mpsc::Receiver<temporary::ProgressData>) = futures::channel::mpsc::channel(20);
let (futures_sender_zeroed, futures_receiver_zeroed): (futures::channel::mpsc::Sender<zeroed::ProgressData>, futures::channel::mpsc::Receiver<zeroed::ProgressData>) = futures::channel::mpsc::channel(20);
startup_configuration(&gui_data);
initialize_gui(&gui_data);
reset_configuration(&gui_data, false); // Fallback for invalid loading setting project
load_configuration(&gui_data, false);
connect_button_delete(&gui_data);
connect_button_save(&gui_data);
connect_button_search(
@ -84,7 +91,7 @@ fn main() {
connect_button_select(&gui_data);
connect_button_stop(&gui_data);
connect_notebook_tabs(&gui_data);
connect_upper_notebook(&gui_data);
connect_selection_of_directories(&gui_data);
connect_popovers(&gui_data);
connect_compute_results(&gui_data, glib_stop_receiver);
connect_progress_window(
@ -98,12 +105,17 @@ fn main() {
futures_receiver_temporary,
futures_receiver_zeroed,
);
connect_settings(&gui_data);
// Quit the program when X in main window was clicked
gui_data.window_main.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(false)
});
{
let window_main = gui_data.window_main.clone();
window_main.connect_delete_event(move |_, _| {
save_configuration(&gui_data, false); // Save configuration at exit
gtk::main_quit();
Inhibit(false)
});
}
// We start the gtk main loop.
gtk::main();

View file

@ -0,0 +1,354 @@
use crate::gui_data::*;
use crate::help_functions::{get_list_store, ColumnsDirectory};
use directories_next::ProjectDirs;
use gtk::prelude::*;
use gtk::{EntryExt, GtkListStoreExt, ToggleButtonExt};
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::{env, fs};
const SAVE_FILE_NAME: &str = "czkawka_gui_config.txt";
pub fn save_configuration(gui_data: &GuiData, manual_execution: bool) {
let check_button_settings_save_at_exit = gui_data.check_button_settings_save_at_exit.clone();
let text_view_errors = gui_data.text_view_errors.clone();
if !manual_execution && !check_button_settings_save_at_exit.get_active() {
// When check button is deselected, not save configuration at exit
return;
}
if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") {
// Lin: /home/alice/.config/barapp
// Win: C:\Users\Alice\AppData\Roaming\Foo Corp\Bar App\config
// Mac: /Users/Alice/Library/Application Support/com.Foo-Corp.Bar-App
let config_dir = proj_dirs.config_dir();
if config_dir.exists() {
if !config_dir.is_dir() {
text_view_errors
.get_buffer()
.unwrap()
.set_text(format!("Cannot create save file inside {} because this isn't a folder.", config_dir.display()).as_str());
return;
}
} else if fs::create_dir(config_dir).is_err() {
text_view_errors.get_buffer().unwrap().set_text(format!("Failed configuration to create configuration folder {}", config_dir.display()).as_str());
return;
}
let mut data_to_save: Vec<String> = Vec::new();
//// Included Directories
data_to_save.push("--included_directories:".to_string());
let scrolled_window_included_directories = gui_data.scrolled_window_included_directories.clone();
let list_store = get_list_store(&scrolled_window_included_directories);
if let Some(iter) = list_store.get_iter_first() {
loop {
data_to_save.push(list_store.get_value(&iter, ColumnsDirectory::Path as i32).get::<String>().unwrap().unwrap());
if !list_store.iter_next(&iter) {
break;
}
}
}
//// Excluded Directories
data_to_save.push("--excluded_directories:".to_string());
let scrolled_window_excluded_directories = gui_data.scrolled_window_excluded_directories.clone();
let list_store = get_list_store(&scrolled_window_excluded_directories);
if let Some(iter) = list_store.get_iter_first() {
loop {
data_to_save.push(list_store.get_value(&iter, ColumnsDirectory::Path as i32).get::<String>().unwrap().unwrap());
if !list_store.iter_next(&iter) {
break;
}
}
}
//// Excluded Items
data_to_save.push("--excluded_items:".to_string());
let entry_excluded_items = gui_data.entry_excluded_items.clone();
for item in entry_excluded_items.get_text().split(',') {
if item.trim().is_empty() {
continue;
}
data_to_save.push(item.to_string());
}
//// Allowed extensions
data_to_save.push("--allowed_extensions:".to_string());
let entry_allowed_extensions = gui_data.entry_allowed_extensions.clone();
for extension in entry_allowed_extensions.get_text().split(',') {
if extension.trim().is_empty() {
continue;
}
data_to_save.push(extension.to_string());
}
//// Save at exit
data_to_save.push("--save_at_exit:".to_string());
let check_button_settings_save_at_exit = gui_data.check_button_settings_save_at_exit.clone();
data_to_save.push(check_button_settings_save_at_exit.get_active().to_string());
//// Load at start
data_to_save.push("--load_at_start:".to_string());
let check_button_settings_load_at_start = gui_data.check_button_settings_load_at_start.clone();
data_to_save.push(check_button_settings_load_at_start.get_active().to_string());
// Creating/Opening config file
let config_file = config_dir.join(Path::new(SAVE_FILE_NAME));
let mut config_file_handler = match File::create(&config_file) {
Ok(t) => t,
Err(_) => {
text_view_errors.get_buffer().unwrap().set_text(format!("Failed to create config file {}", config_file.display()).as_str());
return;
}
};
for data in data_to_save {
match writeln!(config_file_handler, "{}", data) {
Ok(_) => {
text_view_errors.get_buffer().unwrap().set_text(format!("Saved configuration to file {}", config_file.display()).as_str());
}
Err(_) => {
text_view_errors.get_buffer().unwrap().set_text(format!("Failed to save configuration data to file {}", config_file.display()).as_str());
return;
}
}
}
} else {
text_view_errors.get_buffer().unwrap().set_text("Failed to get home directory, so can't save file.");
}
}
enum TypeOfLoadedData {
None,
IncludedDirectories,
ExcludedDirectories,
ExcludedItems,
AllowedExtensions,
LoadingAtStart,
SavingAtExit,
}
pub fn load_configuration(gui_data: &GuiData, manual_execution: bool) {
let text_view_errors = gui_data.text_view_errors.clone();
if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") {
// Lin: /home/alice/.config/barapp
// Win: C:\Users\Alice\AppData\Roaming\Foo Corp\Bar App\config
// Mac: /Users/Alice/Library/Application Support/com.Foo-Corp.Bar-App
let config_dir = proj_dirs.config_dir();
let config_file = config_dir.join(Path::new(SAVE_FILE_NAME));
if !config_file.exists() || !config_file.is_file() {
if manual_execution {
// Don't show errors when there is no configuration file when starting app
text_view_errors.get_buffer().unwrap().set_text(format!("Cannot load configuration from file {:?}.", config_file.display()).as_str());
}
return;
}
// Loading Data
let loaded_data: String = match fs::read_to_string(&config_file) {
Ok(t) => t,
Err(_) => {
text_view_errors.get_buffer().unwrap().set_text(format!("Failed to read data from file {:?}.", config_file).as_str());
return;
}
};
// Parsing Data
let mut included_directories: Vec<String> = Vec::new();
let mut excluded_directories: Vec<String> = Vec::new();
let mut excluded_items: Vec<String> = Vec::new();
let mut allowed_extensions: Vec<String> = Vec::new();
let mut loading_at_start: bool = true;
let mut saving_at_exit: bool = true;
let mut current_type = TypeOfLoadedData::None;
for (line_number, line) in loaded_data.replace("\r\n", "\n").split('\n').enumerate() {
let line: String = line.trim().to_string();
if line.trim().is_empty() {
continue; // Empty line, so we just skip it
}
if line.starts_with("--included_directories") {
current_type = TypeOfLoadedData::IncludedDirectories;
} else if line.starts_with("--excluded_directories") {
current_type = TypeOfLoadedData::ExcludedDirectories;
} else if line.starts_with("--excluded_items") {
current_type = TypeOfLoadedData::ExcludedItems;
} else if line.starts_with("--allowed_extensions") {
current_type = TypeOfLoadedData::AllowedExtensions;
} else if line.starts_with("--load_at_start") {
current_type = TypeOfLoadedData::LoadingAtStart;
} else if line.starts_with("--save_at_exit") {
current_type = TypeOfLoadedData::SavingAtExit;
} else {
match current_type {
TypeOfLoadedData::None => {
text_view_errors
.get_buffer()
.unwrap()
.set_text(format!("Found orphan data in line {} \"\"\"{}\"\"\" when loading file {:?}", line_number, line, config_file).as_str());
return;
}
TypeOfLoadedData::IncludedDirectories => {
included_directories.push(line);
}
TypeOfLoadedData::ExcludedDirectories => {
excluded_directories.push(line);
}
TypeOfLoadedData::ExcludedItems => {
excluded_items.push(line);
}
TypeOfLoadedData::AllowedExtensions => {
allowed_extensions.push(line);
}
TypeOfLoadedData::LoadingAtStart => {
let line = line.to_lowercase();
if line == "1" || line == "true" {
loading_at_start = true;
} else if line == "0" || line == "false" {
loading_at_start = false;
} else {
text_view_errors
.get_buffer()
.unwrap()
.set_text(format!("Found invalid data in line {} \"\"\"{}\"\"\" isn't proper value(0/1/true/false) when loading file {:?}", line_number, line, config_file).as_str());
}
}
TypeOfLoadedData::SavingAtExit => {
let line = line.to_lowercase();
if line == "1" || line == "true" {
saving_at_exit = true;
} else if line == "0" || line == "false" {
saving_at_exit = false;
} else {
text_view_errors
.get_buffer()
.unwrap()
.set_text(format!("Found invalid data in line {} \"\"\"{}\"\"\" isn't proper value(0/1/true/false) when loading file {:?}", line_number, line, config_file).as_str());
}
}
}
}
}
// Setting data
if manual_execution || loading_at_start {
//// Included Directories
let scrolled_window_included_directories = gui_data.scrolled_window_included_directories.clone();
let list_store = get_list_store(&scrolled_window_included_directories);
list_store.clear();
let col_indices = [0];
for directory in included_directories {
let values: [&dyn ToValue; 1] = [&directory];
list_store.set(&list_store.append(), &col_indices, &values);
}
//// Exclude Directories
let scrolled_window_excluded_directories = gui_data.scrolled_window_excluded_directories.clone();
let list_store = get_list_store(&scrolled_window_excluded_directories);
list_store.clear();
let col_indices = [0];
for directory in excluded_directories {
let values: [&dyn ToValue; 1] = [&directory];
list_store.set(&list_store.append(), &col_indices, &values);
}
//// Excluded Items
let entry_excluded_items = gui_data.entry_excluded_items.clone();
entry_excluded_items.set_text(excluded_items.iter().map(|e| e.to_string() + ",").collect::<String>().as_str());
//// Allowed extensions
let entry_allowed_extensions = gui_data.entry_allowed_extensions.clone();
entry_allowed_extensions.set_text(allowed_extensions.iter().map(|e| e.to_string() + ",").collect::<String>().as_str());
//// Buttons
gui_data.check_button_settings_load_at_start.set_active(loading_at_start);
gui_data.check_button_settings_save_at_exit.set_active(saving_at_exit);
} else {
gui_data.check_button_settings_load_at_start.set_active(false);
}
if manual_execution {
text_view_errors.get_buffer().unwrap().set_text(format!("Properly loaded configuration from file {:?}", config_file).as_str());
}
} else {
text_view_errors.get_buffer().unwrap().set_text("Failed to get home directory, so can't load file.");
}
}
pub fn reset_configuration(gui_data: &GuiData, manual_clearing: bool) {
// TODO Maybe add popup dialog to confirm resetting
let text_view_errors = gui_data.text_view_errors.clone();
// Resetting included directories
{
let col_indices = [0];
let scrolled_window_included_directories = gui_data.scrolled_window_included_directories.clone();
let list_store = get_list_store(&scrolled_window_included_directories);
list_store.clear();
let current_dir: String = match env::current_dir() {
Ok(t) => t.to_str().unwrap().to_string(),
Err(_) => {
if cfg!(target_family = "unix") {
println!("Failed to read current directory, setting /home instead");
"/home".to_string()
} else if cfg!(target_family = "windows") {
println!("Failed to read current directory, setting C:\\ instead");
"C:\\".to_string()
} else {
"".to_string()
}
}
};
let values: [&dyn ToValue; 1] = [&current_dir];
list_store.set(&list_store.append(), &col_indices, &values);
}
// Resetting excluded directories
{
let col_indices = [0];
let scrolled_window_excluded_directories = gui_data.scrolled_window_excluded_directories.clone();
let list_store = get_list_store(&scrolled_window_excluded_directories);
list_store.clear();
if cfg!(target_family = "unix") {
for i in ["/proc", "/dev", "/sys", "/run", "/snap"].iter() {
let values: [&dyn ToValue; 1] = [&i];
list_store.set(&list_store.append(), &col_indices, &values);
}
}
}
// Resetting excluded items
{
let entry_excluded_items = gui_data.entry_excluded_items.clone();
if cfg!(target_family = "unix") {
entry_excluded_items.set_text("*/.git/*,*/node_modules/*,*/lost+found/*,*/Trash/*");
}
if cfg!(target_family = "windows") {
entry_excluded_items.set_text("*/.git/*,*/node_modules/*,*/lost+found/*,*:/windows/*");
}
}
// Resetting allowed extensions
{
let entry_allowed_extensions = gui_data.entry_allowed_extensions.clone();
entry_allowed_extensions.set_text("");
}
// Set settings
{
gui_data.check_button_settings_save_at_exit.set_active(true);
gui_data.check_button_settings_load_at_start.set_active(true);
}
if manual_clearing {
text_view_errors.get_buffer().unwrap().set_text("Current configuration was cleared.");
}
}