edit_window: Add "Replace Original" option

This commit is contained in:
Sophie Herold 2025-01-15 21:45:13 +01:00
parent 01c25a8865
commit 84e58f28df
3 changed files with 156 additions and 43 deletions

View file

@ -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,6 +127,65 @@ mod imp {
log::error!("{}", err); log::error!("{}", err);
} }
Ok(new_file) => { Ok(new_file) => {
self.save(current_file, new_file).await;
}
}
}
}
async fn save_overwrite(&self) {
let obj = self.obj();
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(
&current_file,
gio::FileCopyFlags::NONE,
glib::Priority::DEFAULT,
)
.0
.await
{
obj.window().show_error(
&gettext("Failed to save image."),
&format!(
"Failed to move image image to {current_path:?}: {err} move {:?} to {:?}", new_file.path().unwrap(), current_file.path().unwrap()
),
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); let editor = glycin::Editor::new(current_file);
if let Some(operations) = obj.operations() { if let Some(operations) = obj.operations() {
log::debug!("Computing edited image."); log::debug!("Computing edited image.");
@ -116,9 +195,7 @@ mod imp {
log::warn!("Failed to edit image: {err}"); log::warn!("Failed to edit image: {err}");
obj.window().show_error( obj.window().show_error(
&gettext("Failed to edit image."), &gettext("Failed to edit image."),
&format!( &format!("Failed to edit image:\n\n{err}\n\n{operations:#?}"),
"Failed to edit image:\n\n{err}\n\n{operations:#?}"
),
ErrorType::General, ErrorType::General,
) )
} }
@ -147,15 +224,16 @@ mod imp {
&format!("Failed to write file:\n\n{err:?}"), &format!("Failed to write file:\n\n{err:?}"),
ErrorType::General, ErrorType::General,
); );
} else {
return true;
} }
} }
} }
} }
} }
} }
}
} false
}
} }
} }
} }

View file

@ -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>

View file

@ -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)
}
}
}
} }