From d480fdb02b593a07c0ad3cbc738966d6a6be2f82 Mon Sep 17 00:00:00 2001 From: Nefo Fortressia Date: Wed, 15 Dec 2021 19:45:19 +0700 Subject: [PATCH 1/4] refactor: move saving file code to save_file_as() save_file_as() can now be run independently. --- src/components/window/file.rs | 77 +++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/src/components/window/file.rs b/src/components/window/file.rs index 3a51ce0..7989465 100644 --- a/src/components/window/file.rs +++ b/src/components/window/file.rs @@ -16,6 +16,7 @@ pub trait FileImplementedEditor { fn action_open_file(&self); fn open_file(notebook: >k::Notebook, file: gio::File); fn action_save_file_as(&self); + fn save_file_as(&self, file: &gio::File); } impl FileImplementedEditor for super::EchidnaWindow { @@ -79,7 +80,48 @@ impl FileImplementedEditor for super::EchidnaWindow { ))), ); } + fn save_file_as(&self, file: &gio::File) { + let window_imp = self.to_imp(); + let page: EchidnaCoreEditor; + match window_imp.notebook + .nth_page( + Some(window_imp.notebook + .current_page() + .expect( + "No tabs is the current tab, probably all tabs closed. No files to save" + ) + ) + ).expect( + "Couldn't get the page of the current index. Try again." + ).downcast::() { + Ok(res) => page = res, + Err(e) => panic!(format!("We got an error when trying to downcast the current tab page into EchidnaCoreEditor:\n{}", e)) + } + + let buffer: Buffer = page + .to_imp() + .sourceview + .buffer() + .downcast() + .expect("Could not downcast the editor's buffer to GtkSourceBuffer."); + let cancellable = Cancellable::new(); + + let file_saver = FileSaver::with_target(&buffer, &page.file(), file); + file_saver.save_async( + Priority::default(), + Some(&cancellable), + |_, _| {}, + |result| { + if result.is_err() { + panic!(format!( + "Found an error while saving the file:\n{}", + result.err().expect("No error") + )) + } + }, + ); + } fn action_save_file_as(&self) { let dialog = FileChooserDialog::new( Some("Save File As"), @@ -99,40 +141,7 @@ impl FileImplementedEditor for super::EchidnaWindow { move |dialog, response| { if response == ResponseType::Accept { let file = dialog.file().expect(""); - let window_imp = window.to_imp(); - let page: EchidnaCoreEditor; - - match window_imp.notebook - .nth_page( - Some(window_imp.notebook - .current_page() - .expect( - "No tabs is the current tab, probably all tabs closed. No files to save" - ) - ) - ).expect( - "Couldn't get the page of the current index. Try again." - ).downcast::() { - Ok(res) => page = res, - Err(e) => panic!(format!("We got an error when trying to downcast the current tab page into EchidnaCoreEditor:\n{}", e)) - } - - let buffer: Buffer = page.to_imp().sourceview.buffer().downcast().expect("Could not downcast the editor's buffer to GtkSourceBuffer."); - let cancellable = Cancellable::new(); - - let file_saver = FileSaver::with_target( - &buffer, - &page.file(), &file); - file_saver.save_async( - Priority::default(), - Some(&cancellable), - |_, _| {}, - |result| { - if result.is_err() { - panic!(format!("Found an error while saving the file:\n{}", result.err().expect("No error"))) - } - }); - + window.save_file_as(&file); } dialog.destroy(); From ce8fb5dd12a721604c9abad66de0cbc56a73a735 Mon Sep 17 00:00:00 2001 From: Nefo Fortressia Date: Thu, 16 Dec 2021 11:08:42 +0700 Subject: [PATCH 2/4] refactor: separate get_current_tab() from save_file_as() EchidnaWindow.get_current_tab() can now be used independently. This will help with reusing it in another places. I think it will be used a lot. Additionally, it will return Result instead of panicking, as it's a utils function now and should never panic. --- src/components/window/file.rs | 19 +++---------------- src/components/window/mod.rs | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/components/window/file.rs b/src/components/window/file.rs index 7989465..bf0dcb9 100644 --- a/src/components/window/file.rs +++ b/src/components/window/file.rs @@ -82,22 +82,9 @@ impl FileImplementedEditor for super::EchidnaWindow { } fn save_file_as(&self, file: &gio::File) { let window_imp = self.to_imp(); - let page: EchidnaCoreEditor; - - match window_imp.notebook - .nth_page( - Some(window_imp.notebook - .current_page() - .expect( - "No tabs is the current tab, probably all tabs closed. No files to save" - ) - ) - ).expect( - "Couldn't get the page of the current index. Try again." - ).downcast::() { - Ok(res) => page = res, - Err(e) => panic!(format!("We got an error when trying to downcast the current tab page into EchidnaCoreEditor:\n{}", e)) - } + let page: EchidnaCoreEditor = self + .get_current_tab() + .expect("Can't find the current tab because there are no tabs."); let buffer: Buffer = page .to_imp() diff --git a/src/components/window/mod.rs b/src/components/window/mod.rs index 40ed731..d692722 100644 --- a/src/components/window/mod.rs +++ b/src/components/window/mod.rs @@ -6,7 +6,7 @@ pub mod file; pub mod imp; pub mod menubar; -use glib::object::IsA; +use glib::object::{Cast, IsA}; use gtk::subclass::prelude::*; glib::wrapper! { @@ -28,4 +28,25 @@ impl EchidnaWindow { pub fn to_imp(&self) -> &imp::EchidnaWindow { imp::EchidnaWindow::from_instance(self) } + + pub fn get_current_tab>(&self) -> Result { + let window_imp = self.to_imp(); + let nth = window_imp.notebook.current_page(); + + match nth { + None => Err("No tabs are currently opened, maybe there are no tabs."), + Some(nth) => { + let page = window_imp + .notebook + .nth_page(Some(nth)) + .expect("Couldn't get the page of the current index."); + + match page.downcast::() + { + Ok(page) => Ok(page), + Err(e) => Err("Cannot downcast to type parameter A. Maybe it's not in the type you are looking for."), + } + } + } + } } From 52d1bdf7db2ad9316c01f719ac81e7b25f70649c Mon Sep 17 00:00:00 2001 From: Nefo Fortressia Date: Thu, 16 Dec 2021 14:04:53 +0700 Subject: [PATCH 3/4] refactor: move the code for saving files to EchidnaCoreEditor Additionally add support for normal saving ("Save File"). This change gives support for saving files other than the current one. EchidnaWindow.save_file_as() was only able to save the current tab opened. --- src/components/editor/mod.rs | 51 ++++++++++++++++++++++++++++++++++- src/components/window/file.rs | 43 +++++------------------------ 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/src/components/editor/mod.rs b/src/components/editor/mod.rs index dc218b9..4086b0a 100644 --- a/src/components/editor/mod.rs +++ b/src/components/editor/mod.rs @@ -3,9 +3,12 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ pub mod imp; +use gio::Cancellable; use gtk::prelude::*; use gtk::subclass::prelude::*; -use sourceview::{prelude::*, Buffer, FileExt as SourceFileExt, FileLoader, LanguageManager}; +use sourceview::{ + prelude::*, Buffer, FileExt as SourceFileExt, FileLoader, FileSaver, LanguageManager, +}; glib::wrapper! { pub struct EchidnaCoreEditor(ObjectSubclass) @@ -83,4 +86,50 @@ impl EchidnaCoreEditor { pub fn file(&self) -> sourceview::File { self.property("file").expect("Could not get property 'file' of EchidnaCoreEditor").get::().expect("Could not get property 'file' of EchidnaCoreEditor because its type is not IsA") } + + pub fn save_file(&self, save_as: Option<&gio::File>) -> Result<(), &str> { + let window_imp = self.to_imp(); + let buffer = self.to_imp().sourceview.buffer().downcast::(); + + match buffer { + Ok(buffer) => { + let cancellable = Cancellable::new(); + let mut file_saver: Option = None; + let result: Result<(), &str> = match save_as { + Some(file) => { + file_saver = Some(FileSaver::with_target(&buffer, &self.file(), file)); + Ok(()) + } + None => match self.file().location() { + Some(_) => { + file_saver = Some(FileSaver::new(&buffer, &self.file())); + Ok(()) + } + None => Err("The file location must exist. Please do \"Save As\""), + }, + }; + + match result { + Err(result) => Err(result), + Ok(_) => { + file_saver.unwrap().save_async( + glib::Priority::default(), + Some(&cancellable), + |_, _| {}, + |result| { + if result.is_err() { + panic!(format!( + "Found an error while saving the file:\n{}", + result.err().expect("No error") + )) + } + }, + ); + Ok(()) + } + } + } + Err(_) => Err("Can't downcast the buffer to GtkSourceBuffer."), + } + } } diff --git a/src/components/window/file.rs b/src/components/window/file.rs index bf0dcb9..ad44f7a 100644 --- a/src/components/window/file.rs +++ b/src/components/window/file.rs @@ -2,21 +2,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::lib::prelude::*; - use crate::components::editor::EchidnaCoreEditor; -use gio::Cancellable; -use glib::{clone, Priority}; +use crate::lib::prelude::*; +use glib::clone; use gtk::{ prelude::*, subclass::prelude::*, FileChooserAction, FileChooserDialog, Label, ResponseType, }; -use sourceview::{prelude::*, Buffer, File, FileSaver}; +use sourceview::File; pub trait FileImplementedEditor { fn action_open_file(&self); fn open_file(notebook: >k::Notebook, file: gio::File); fn action_save_file_as(&self); - fn save_file_as(&self, file: &gio::File); } impl FileImplementedEditor for super::EchidnaWindow { @@ -80,35 +77,6 @@ impl FileImplementedEditor for super::EchidnaWindow { ))), ); } - fn save_file_as(&self, file: &gio::File) { - let window_imp = self.to_imp(); - let page: EchidnaCoreEditor = self - .get_current_tab() - .expect("Can't find the current tab because there are no tabs."); - - let buffer: Buffer = page - .to_imp() - .sourceview - .buffer() - .downcast() - .expect("Could not downcast the editor's buffer to GtkSourceBuffer."); - let cancellable = Cancellable::new(); - - let file_saver = FileSaver::with_target(&buffer, &page.file(), file); - file_saver.save_async( - Priority::default(), - Some(&cancellable), - |_, _| {}, - |result| { - if result.is_err() { - panic!(format!( - "Found an error while saving the file:\n{}", - result.err().expect("No error") - )) - } - }, - ); - } fn action_save_file_as(&self) { let dialog = FileChooserDialog::new( Some("Save File As"), @@ -128,8 +96,9 @@ impl FileImplementedEditor for super::EchidnaWindow { move |dialog, response| { if response == ResponseType::Accept { let file = dialog.file().expect(""); - window.save_file_as(&file); - } + let tab: EchidnaCoreEditor = window.get_current_tab().expect("error"); + tab.save_file(Some(&file)); + } dialog.destroy(); From d8c7f104abb93fba53f1966cf123b47f5f96335a Mon Sep 17 00:00:00 2001 From: Nefo Fortressia Date: Thu, 16 Dec 2021 14:06:33 +0700 Subject: [PATCH 4/4] feat: implement "Save" menu --- src/components/window/file.rs | 15 ++++++++++++++- src/components/window/menubar.rs | 11 +++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/components/window/file.rs b/src/components/window/file.rs index ad44f7a..8b83cb9 100644 --- a/src/components/window/file.rs +++ b/src/components/window/file.rs @@ -8,12 +8,13 @@ use glib::clone; use gtk::{ prelude::*, subclass::prelude::*, FileChooserAction, FileChooserDialog, Label, ResponseType, }; -use sourceview::File; +use sourceview::{File, FileExt as SourceFileExt}; pub trait FileImplementedEditor { fn action_open_file(&self); fn open_file(notebook: >k::Notebook, file: gio::File); fn action_save_file_as(&self); + fn action_save_file(&self); } impl FileImplementedEditor for super::EchidnaWindow { @@ -104,4 +105,16 @@ impl FileImplementedEditor for super::EchidnaWindow { })); } + + fn action_save_file(&self) { + let page: EchidnaCoreEditor = self + .get_current_tab() + .expect("Can't find the current tab because there are no tabs."); + match page.file().location() { + Some(_) => { + page.save_file(None); + } + None => self.action_save_file_as(), + } + } } diff --git a/src/components/window/menubar.rs b/src/components/window/menubar.rs index 69b3a41..3296694 100644 --- a/src/components/window/menubar.rs +++ b/src/components/window/menubar.rs @@ -122,5 +122,16 @@ impl MenubarImplementedEditor for EchidnaWindow { window.action_save_file_as(); })); } + { + let action_save = SimpleAction::new("save", None); + + self.add_action(&action_save); + + action_save.connect_activate(clone!(@weak self as window => + move |_, _| { + window.action_save_file(); + } + )); + } } }