1
0
Fork 0
mirror of synced 2024-06-02 02:24:44 +12:00
czkawka/czkawka_gui/src/gui_data.rs
krzysdz 4c205ce098
Windows taskbar progress support (#264)
* Initial Windows taskbar progress support

* Changes to COM (un)init

It turns out winapi exposes IIDs through a `uuidof()` function of interfaces, so the copied one can be removed.

* Don't return error codes

Now the `TaskbarProgress` functions fail silently.
The `TaskbarProgress` struct now will always be created (even in case of errors in initialisation), but it won't do anything.

* Fix builds for other systems

* Formatted code

* Fix progress shown after the operation finished

A progress update was received after the stop event.
Also `as_ref()` was removed in many places (I don't even know why it was there).

* Remove redundant call to hide

It's already called by the `glib_stop_receiver` receiver.

* Release the ITaskbarList3 and call CoUninitialize at exit

Because objects moved to closures used as fallbacks in GTK have [static lifetimes](https://gtk-rs.org/docs-src/tutorial/closures#closures), the `TaskbarProgress` will never be dropped.
To workaround this problem a `release` function is called when the main window is closed. This function behaves like `drop`, but sets the struct in a valid "empty" state, so that calling `release`/`drop` again won't cause problems.

* Don't set the NORMAL state manually

Because only NOPROGRESS and INDETERMINATE states are used, there is no need to set the NORMAL state when changing the progress value.

Now `set_progress_value` will also change the `TaskbarProgress::current_state` if such situation occurs.

> Unless [SetProgressState](https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressstate)
> has set a blocking state (TBPF_ERROR or TBPF_PAUSED) for the window, a call to **SetProgressValue** assumes the TBPF_NORMAL
> state even if it is not explicitly set. A call to **SetProgressValue** overrides and clears the TBPF_INDETERMINATE state.

See the [SetProgressValue documentation](https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressvalue#how-the-taskbar-button-chooses-the-progress-indicator-for-a-group)
2021-02-20 12:24:36 +01:00

191 lines
7.8 KiB
Rust

extern crate gtk;
use crate::gui_about::GUIAbout;
use crate::gui_bottom_buttons::GUIBottomButtons;
use crate::gui_header::GUIHeader;
use crate::gui_main_notebook::GUIMainNotebook;
use crate::gui_options::GUIOptions;
use crate::gui_popovers::GUIPopovers;
use crate::gui_progress_dialog::GUIProgressDialog;
use crate::gui_upper_notepad::GUIUpperNotebook;
use crate::notebook_enums::*;
use crate::taskbar_progress::TaskbarProgress;
use crossbeam_channel::unbounded;
use czkawka_core::big_file::BigFile;
use czkawka_core::broken_files::BrokenFiles;
use czkawka_core::duplicate::DuplicateFinder;
use czkawka_core::empty_files::EmptyFiles;
use czkawka_core::empty_folder::EmptyFolder;
use czkawka_core::invalid_symlinks::InvalidSymlinks;
use czkawka_core::same_music::SameMusic;
use czkawka_core::similar_images::SimilarImages;
use czkawka_core::temporary::Temporary;
use czkawka_core::zeroed::ZeroedFiles;
use gtk::prelude::*;
use gtk::Builder;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Clone)]
pub struct GuiData {
// Glade builder
pub glade_src: String,
pub builder: Builder,
// Windows
pub window_main: gtk::Window,
pub main_notebook: GUIMainNotebook,
pub upper_notebook: GUIUpperNotebook,
pub popovers: GUIPopovers,
pub bottom_buttons: GUIBottomButtons,
pub progress_window: GUIProgressDialog,
pub about: GUIAbout,
pub options: GUIOptions,
pub header: GUIHeader,
// Taskbar state
pub taskbar_state: Rc<RefCell<TaskbarProgress>>,
// Buttons state
pub shared_buttons: Rc<RefCell<HashMap<NotebookMainEnum, HashMap<String, bool>>>>,
// Upper Notebook state
pub shared_upper_notebooks: Rc<RefCell<HashMap<NotebookMainEnum, HashMap<NotebookUpperEnum, bool>>>>,
// State of search results
pub shared_duplication_state: Rc<RefCell<DuplicateFinder>>,
pub shared_empty_folders_state: Rc<RefCell<EmptyFolder>>,
pub shared_empty_files_state: Rc<RefCell<EmptyFiles>>,
pub shared_temporary_files_state: Rc<RefCell<Temporary>>,
pub shared_big_files_state: Rc<RefCell<BigFile>>,
pub shared_similar_images_state: Rc<RefCell<SimilarImages>>,
pub shared_zeroed_files_state: Rc<RefCell<ZeroedFiles>>,
pub shared_same_music_state: Rc<RefCell<SameMusic>>,
pub shared_same_invalid_symlinks: Rc<RefCell<InvalidSymlinks>>,
pub shared_broken_files_state: Rc<RefCell<BrokenFiles>>,
//// Entry
pub entry_info: gtk::Entry,
//// Bottom
pub text_view_errors: gtk::TextView,
pub scrolled_window_errors: gtk::ScrolledWindow,
// Used for sending stop signal to thread
pub stop_sender: crossbeam_channel::Sender<()>,
pub stop_receiver: crossbeam_channel::Receiver<()>,
}
impl GuiData {
pub fn new() -> Self {
//// Loading glade file content and build with it help UI
let glade_src = include_str!("../czkawka.glade").to_string();
let builder = Builder::from_string(glade_src.as_str());
//// Windows
let window_main: gtk::Window = builder.get_object("window_main").unwrap();
window_main.show_all();
window_main.set_title("Czkawka");
let main_notebook = GUIMainNotebook::create_from_builder(&builder);
let upper_notebook = GUIUpperNotebook::create_from_builder(&builder);
let popovers = GUIPopovers::create_from_builder(&builder);
let bottom_buttons = GUIBottomButtons::create_from_builder(&builder);
let progress_window = GUIProgressDialog::create_from_builder(&builder);
let options = GUIOptions::create_from_builder(&builder);
let about = GUIAbout::create_from_builder(&builder);
let header = GUIHeader::create_from_builder(&builder);
////////////////////////////////////////////////////////////////////////////////////////////////
// Taskbar state
let taskbar_state = Rc::new(RefCell::new(TaskbarProgress::new()));
// Buttons State - to remember existence of different buttons on pages
let shared_buttons: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::<NotebookMainEnum, HashMap<String, bool>>::new()));
// Show by default only search button
for i in get_all_main_tabs().iter() {
let mut temp_hashmap: HashMap<String, bool> = Default::default();
for button_name in bottom_buttons.buttons_names.iter() {
if *button_name == "search" {
temp_hashmap.insert(button_name.to_string(), true);
} else {
temp_hashmap.insert(button_name.to_string(), false);
}
}
shared_buttons.borrow_mut().insert(i.clone(), temp_hashmap);
}
// Upper Notebook state
let shared_upper_notebooks: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::<NotebookMainEnum, HashMap<NotebookUpperEnum, bool>>::new()));
for i in get_all_main_tabs().iter() {
let mut temp_hashmap: HashMap<NotebookUpperEnum, bool> = Default::default();
for j in get_all_upper_tabs().iter() {
temp_hashmap.insert(j.clone(), true);
}
shared_upper_notebooks.borrow_mut().insert(i.clone(), temp_hashmap);
}
// Some upper notebook tabs are disabled
*shared_upper_notebooks.borrow_mut().get_mut(&NotebookMainEnum::Temporary).unwrap().get_mut(&NotebookUpperEnum::AllowedExtensions).unwrap() = false;
// State of search results
let shared_duplication_state: Rc<RefCell<_>> = Rc::new(RefCell::new(DuplicateFinder::new()));
let shared_empty_folders_state: Rc<RefCell<_>> = Rc::new(RefCell::new(EmptyFolder::new()));
let shared_empty_files_state: Rc<RefCell<_>> = Rc::new(RefCell::new(EmptyFiles::new()));
let shared_temporary_files_state: Rc<RefCell<_>> = Rc::new(RefCell::new(Temporary::new()));
let shared_big_files_state: Rc<RefCell<_>> = Rc::new(RefCell::new(BigFile::new()));
let shared_similar_images_state: Rc<RefCell<_>> = Rc::new(RefCell::new(SimilarImages::new()));
let shared_zeroed_files_state: Rc<RefCell<_>> = Rc::new(RefCell::new(ZeroedFiles::new()));
let shared_same_music_state: Rc<RefCell<_>> = Rc::new(RefCell::new(SameMusic::new()));
let shared_same_invalid_symlinks: Rc<RefCell<_>> = Rc::new(RefCell::new(InvalidSymlinks::new()));
let shared_broken_files_state: Rc<RefCell<_>> = Rc::new(RefCell::new(BrokenFiles::new()));
//// Entry
let entry_info: gtk::Entry = builder.get_object("entry_info").unwrap();
//// Bottom
let text_view_errors: gtk::TextView = builder.get_object("text_view_errors").unwrap();
let scrolled_window_errors: gtk::ScrolledWindow = builder.get_object("scrolled_window_errors").unwrap();
scrolled_window_errors.show_all(); // Not sure why needed, but without it text view errors sometimes hide itself
// Used for sending stop signal to thread
let (stop_sender, stop_receiver): (crossbeam_channel::Sender<()>, crossbeam_channel::Receiver<()>) = unbounded();
Self {
glade_src,
builder,
window_main,
main_notebook,
upper_notebook,
popovers,
bottom_buttons,
progress_window,
about,
options,
header,
taskbar_state,
shared_buttons,
shared_upper_notebooks,
shared_duplication_state,
shared_empty_folders_state,
shared_empty_files_state,
shared_temporary_files_state,
shared_big_files_state,
shared_similar_images_state,
shared_zeroed_files_state,
shared_same_music_state,
shared_same_invalid_symlinks,
shared_broken_files_state,
entry_info,
text_view_errors,
scrolled_window_errors,
stop_sender,
stop_receiver,
}
}
}