edit_window: Add "Replace Original" option
This commit is contained in:
parent
01c25a8865
commit
84e58f28df
3 changed files with 156 additions and 43 deletions
|
|
@ -40,6 +40,8 @@ mod imp {
|
||||||
toolbar_view: TemplateChild<adw::ToolbarView>,
|
toolbar_view: TemplateChild<adw::ToolbarView>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
cancel: TemplateChild<gtk::Button>,
|
cancel: TemplateChild<gtk::Button>,
|
||||||
|
#[template_child]
|
||||||
|
done: TemplateChild<gtk::MenuButton>,
|
||||||
|
|
||||||
#[property(get, construct_only)]
|
#[property(get, construct_only)]
|
||||||
original_image: OnceCell<LpImage>,
|
original_image: OnceCell<LpImage>,
|
||||||
|
|
@ -60,7 +62,15 @@ mod imp {
|
||||||
glib::spawn_future_local(glib::clone!(
|
glib::spawn_future_local(glib::clone!(
|
||||||
#[weak]
|
#[weak]
|
||||||
obj,
|
obj,
|
||||||
async move { obj.imp().save_image().await }
|
async move { obj.imp().save_copy().await }
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
klass.install_action("edit.save-overwrite", None, |obj, _, _| {
|
||||||
|
glib::spawn_future_local(glib::clone!(
|
||||||
|
#[weak]
|
||||||
|
obj,
|
||||||
|
async move { obj.imp().save_overwrite().await }
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -87,6 +97,15 @@ mod imp {
|
||||||
obj.window().show_image();
|
obj.window().show_image();
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
|
glib::spawn_future_local(glib::clone!(
|
||||||
|
#[weak]
|
||||||
|
obj,
|
||||||
|
async move {
|
||||||
|
let can_trash = obj.original_image().can_trash().await;
|
||||||
|
obj.action_set_enabled("edit.save-overwrite", can_trash);
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,8 +113,9 @@ mod imp {
|
||||||
impl BinImpl for LpEditWindow {}
|
impl BinImpl for LpEditWindow {}
|
||||||
|
|
||||||
impl LpEditWindow {
|
impl LpEditWindow {
|
||||||
async fn save_image(&self) {
|
async fn save_copy(&self) {
|
||||||
let obj = self.obj();
|
let obj = self.obj();
|
||||||
|
self.done.popdown();
|
||||||
|
|
||||||
if let Some(current_file) = obj.original_image().file() {
|
if let Some(current_file) = obj.original_image().file() {
|
||||||
let file_dialog = gtk::FileDialog::new();
|
let file_dialog = gtk::FileDialog::new();
|
||||||
|
|
@ -107,55 +127,113 @@ mod imp {
|
||||||
log::error!("{}", err);
|
log::error!("{}", err);
|
||||||
}
|
}
|
||||||
Ok(new_file) => {
|
Ok(new_file) => {
|
||||||
let editor = glycin::Editor::new(current_file);
|
self.save(current_file, new_file).await;
|
||||||
if let Some(operations) = obj.operations() {
|
}
|
||||||
log::debug!("Computing edited image.");
|
}
|
||||||
let result = editor.apply_complete(&operations).await;
|
}
|
||||||
match result {
|
}
|
||||||
Err(err) => {
|
|
||||||
log::warn!("Failed to edit image: {err}");
|
async fn save_overwrite(&self) {
|
||||||
obj.window().show_error(
|
let obj = self.obj();
|
||||||
&gettext("Failed to edit image."),
|
self.done.popdown();
|
||||||
|
|
||||||
|
if let Some(current_file) = obj.original_image().file() {
|
||||||
|
if let Some(current_path) = current_file.path() {
|
||||||
|
if let Some(mut file_stem) = current_path.file_stem().map(|x| x.to_os_string())
|
||||||
|
{
|
||||||
|
let mut tmp_path = current_path.clone();
|
||||||
|
file_stem.push(".tmp");
|
||||||
|
if let Some(ext) = current_path.extension() {
|
||||||
|
file_stem.push(".");
|
||||||
|
file_stem.push(ext);
|
||||||
|
}
|
||||||
|
tmp_path.set_file_name(file_stem);
|
||||||
|
|
||||||
|
let new_file = gio::File::for_path(&tmp_path);
|
||||||
|
let written = self.save(current_file.clone(), new_file.clone()).await;
|
||||||
|
|
||||||
|
if written {
|
||||||
|
if let Err(err) =
|
||||||
|
current_file.trash_future(glib::Priority::DEFAULT).await
|
||||||
|
{
|
||||||
|
obj.window().show_error(
|
||||||
|
&gettext("Failed to save image."),
|
||||||
|
&format!("Failed to move image {current_path:?} to trash and therefore couldn't save image: {err}"),
|
||||||
|
ErrorType::General,
|
||||||
|
);
|
||||||
|
} else if let Err(err) = new_file
|
||||||
|
.move_future(
|
||||||
|
¤t_file,
|
||||||
|
gio::FileCopyFlags::NONE,
|
||||||
|
glib::Priority::DEFAULT,
|
||||||
|
)
|
||||||
|
.0
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
obj.window().show_error(
|
||||||
|
&gettext("Failed to save image."),
|
||||||
&format!(
|
&format!(
|
||||||
"Failed to edit image:\n\n{err}\n\n{operations:#?}"
|
"Failed to move image image to {current_path:?}: {err} move {:?} to {:?}", new_file.path().unwrap(), current_file.path().unwrap()
|
||||||
),
|
),
|
||||||
ErrorType::General,
|
ErrorType::General,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn save(&self, current_file: gio::File, new_file: gio::File) -> bool {
|
||||||
|
let obj = self.obj();
|
||||||
|
let editor = glycin::Editor::new(current_file);
|
||||||
|
if let Some(operations) = obj.operations() {
|
||||||
|
log::debug!("Computing edited image.");
|
||||||
|
let result = editor.apply_complete(&operations).await;
|
||||||
|
match result {
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to edit image: {err}");
|
||||||
|
obj.window().show_error(
|
||||||
|
&gettext("Failed to edit image."),
|
||||||
|
&format!("Failed to edit image:\n\n{err}\n\n{operations:#?}"),
|
||||||
|
ErrorType::General,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Ok(binary_data) => {
|
||||||
|
log::debug!("Saving edited image to '{}'", new_file.uri());
|
||||||
|
match binary_data.get() {
|
||||||
|
Err(err) => {
|
||||||
|
obj.window().show_error(
|
||||||
|
&gettext("Failed to Save Image"),
|
||||||
|
&format!("Failed to get binary data: {err}"),
|
||||||
|
ErrorType::General,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(data) => {
|
||||||
|
if let Err(err) = new_file
|
||||||
|
.replace_contents_future(
|
||||||
|
data,
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
gio::FileCreateFlags::NONE,
|
||||||
)
|
)
|
||||||
}
|
.await
|
||||||
Ok(binary_data) => {
|
{
|
||||||
log::debug!("Saving edited image to '{}'", new_file.uri());
|
obj.window().show_error(
|
||||||
match binary_data.get() {
|
&gettext("Failed to Save Image"),
|
||||||
Err(err) => {
|
&format!("Failed to write file:\n\n{err:?}"),
|
||||||
obj.window().show_error(
|
ErrorType::General,
|
||||||
&gettext("Failed to Save Image"),
|
);
|
||||||
&format!("Failed to get binary data: {err}"),
|
} else {
|
||||||
ErrorType::General,
|
return true;
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(data) => {
|
|
||||||
if let Err(err) = new_file
|
|
||||||
.replace_contents_future(
|
|
||||||
data,
|
|
||||||
None,
|
|
||||||
true,
|
|
||||||
gio::FileCreateFlags::NONE,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
obj.window().show_error(
|
|
||||||
&gettext("Failed to Save Image"),
|
|
||||||
&format!("Failed to write file:\n\n{err:?}"),
|
|
||||||
ErrorType::General,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child type="end">
|
<child type="end">
|
||||||
<object class="GtkMenuButton" id="save">
|
<object class="GtkMenuButton" id="done">
|
||||||
<property name="label" translatable="true">_Done</property>
|
<property name="label" translatable="true">_Done</property>
|
||||||
<property name="use-underline">true</property>
|
<property name="use-underline">true</property>
|
||||||
<property name="popover">
|
<property name="popover">
|
||||||
|
|
@ -57,12 +57,13 @@
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBoxRow" id="save_overwrite">
|
<object class="GtkListBoxRow" id="save_overwrite">
|
||||||
|
<property name="action-name">edit.save-overwrite</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel">
|
<object class="GtkLabel">
|
||||||
<property name="label" translatable="yes">_Replace Original…</property>
|
<property name="label" translatable="yes">_Replace Original</property>
|
||||||
<property name="use-underline">true</property>
|
<property name="use-underline">true</property>
|
||||||
<property name="xalign">0</property>
|
<property name="xalign">0</property>
|
||||||
<property name="mnemonic-widget">save_overwrite</property>
|
<property name="mnemonic-widget">save_overwrite</property>
|
||||||
|
|
|
||||||
|
|
@ -115,4 +115,38 @@ impl LpImage {
|
||||||
self.imp().untransformed_dimensions()
|
self.imp().untransformed_dimensions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn can_trash(&self) -> bool {
|
||||||
|
let Some(file) = self.file() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if file.path().is_none() {
|
||||||
|
// The trash portal only supports fds anyway
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_info = file
|
||||||
|
.query_info_future(
|
||||||
|
gio::FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
|
||||||
|
gio::FileQueryInfoFlags::NONE,
|
||||||
|
glib::Priority::DEFAULT,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match file_info {
|
||||||
|
Err(err) => {
|
||||||
|
log::error!(
|
||||||
|
"Failed to obtain CAN_TRASH attribute for '{}': {err}",
|
||||||
|
file.uri()
|
||||||
|
);
|
||||||
|
// Assume we can't trash if we don't have any infos
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Ok(file_info) => {
|
||||||
|
file_info.has_attribute(gio::FILE_ATTRIBUTE_ACCESS_CAN_TRASH)
|
||||||
|
&& file_info.boolean(gio::FILE_ATTRIBUTE_ACCESS_CAN_TRASH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue