Adjust executable permission to use Operation and adjust text per UX

This commit is contained in:
Jeremy Soller 2024-10-03 11:53:01 -06:00
parent 00857511ca
commit 5624f37a36
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
3 changed files with 107 additions and 74 deletions

View file

@ -66,10 +66,10 @@ apply-to-all = Apply to all
keep-both = Keep both
skip = Skip
## Execution permission Dialog
add-permission-title = Add execution permission
add-permission-control = Add execution permission to {$path}
add-permission = Add permission
## Set as Executable and Launch Dialog
set-executable-and-launch = Set as executable and launch
set-executable-and-launch-description = Do you want to set "{$name}" as executable and launch it?
set-and-launch = Set and launch
## Metadata Dialog
owner = Owner
@ -145,6 +145,8 @@ extracted = Extracted {$items} {$items ->
[one] item
*[other] items
} from {$from} to {$to}
setting-executable-and-launching = Setting "{$name}" as executable and launching
set-executable-and-launched = Set "{$name}" as executable and launched
moving = Moving {$items} {$items ->
[one] item
*[other] items

View file

@ -43,13 +43,10 @@ use slotmap::Key as SlotMapKey;
use std::{
any::TypeId,
collections::{BTreeMap, HashMap, HashSet, VecDeque},
env,
ffi::OsStr,
fmt, fs,
env, fmt, fs,
future::pending,
io::{BufRead, BufReader},
io,
num::NonZeroU16,
os::unix::fs::PermissionsExt,
path::PathBuf,
process,
sync::{Arc, Mutex},
@ -415,10 +412,6 @@ pub enum DialogPage {
name: String,
dir: bool,
},
AddExecutablePermission {
file_path: PathBuf,
run: bool,
},
Replace {
from: tab::Item,
to: tab::Item,
@ -426,6 +419,9 @@ pub enum DialogPage {
apply_to_all: bool,
tx: mpsc::Sender<ReplaceResult>,
},
SetExecutableAndLaunch {
path: PathBuf,
},
}
pub struct FavoriteIndex(usize);
@ -1465,25 +1461,12 @@ impl Application for App {
let to = parent.join(name);
self.operation(Operation::Rename { from, to });
}
DialogPage::AddExecutablePermission { file_path, run } => {
let mut perms = fs::metadata(&file_path)
.expect("Failed to get metadata")
.permissions();
let current_mode = perms.mode();
let new_mode = current_mode | 0o100;
perms.set_mode(new_mode);
fs::set_permissions(&file_path, perms)
.expect("Failed to set permissions");
if run {
log::info!("running app: {:?}", file_path);
let _ = std::process::Command::new(file_path).spawn();
}
}
DialogPage::Replace { .. } => {
log::warn!("replace dialog should be completed with replace result");
}
DialogPage::SetExecutableAndLaunch { path } => {
self.operation(Operation::SetExecutableAndLaunch { path });
}
}
}
}
@ -2242,7 +2225,8 @@ impl Application for App {
}
tab::Command::OpenFile(path) => {
let mut found_desktop_exec = false;
if mime_icon::mime_for_path(&path) == "application/x-desktop" {
let mime = mime_icon::mime_for_path(&path);
if mime == "application/x-desktop" {
match freedesktop_entry_parser::parse_entry(&path) {
Ok(entry) => {
match entry.section("Desktop Entry").attr("Exec") {
@ -2275,37 +2259,43 @@ impl Application for App {
Err(err) => {
log::warn!("failed to parse {:?}: {}", path, err);
}
};
}
if !found_desktop_exec {
let file_extension = path.extension();
match file_extension {
Some(ext) if ext == OsStr::new("AppImage") => {
let mut perms = fs::metadata(&path)
.expect("Failed to get metadata")
.permissions();
self.dialog_pages.push_back(
DialogPage::AddExecutablePermission {
file_path: path.clone(),
run: true,
},
);
}
_ => match open::that_detached(&path) {
Ok(()) => {
let _ = recently_used_xbel::update_recently_used(
&path,
App::APP_ID.to_string(),
"cosmic-files".to_string(),
None,
}
} else if mime == "application/x-executable"
|| mime == "application/vnd.appimage"
{
let mut command = std::process::Command::new(&path);
match spawn_detached(&mut command) {
Ok(()) => {}
Err(err) => match err.kind() {
io::ErrorKind::PermissionDenied => {
// If permission is denied, try marking as executable, then running
self.dialog_pages.push_back(
DialogPage::SetExecutableAndLaunch {
path: path.clone(),
},
);
}
Err(err) => {
log::warn!("failed to open {:?}: {}", path, err);
_ => {
log::warn!("failed to execute {:?}: {}", path, err);
}
},
}
found_desktop_exec = true;
}
if !found_desktop_exec {
match open::that_detached(&path) {
Ok(()) => {
let _ = recently_used_xbel::update_recently_used(
&path,
App::APP_ID.to_string(),
"cosmic-files".to_string(),
None,
);
}
Err(err) => {
log::warn!("failed to open {:?}: {}", path, err);
}
}
}
}
tab::Command::OpenInNewTab(path) => {
@ -3088,24 +3078,6 @@ impl Application for App {
.spacing(space_xxs),
)
}
DialogPage::AddExecutablePermission { file_path, run } => {
let mut dialog = widget::dialog(fl!("add-permission-title"))
.primary_action(
widget::button::text(fl!("add-permission"))
.style(theme::Button::Suggested)
.on_press(Message::DialogComplete),
)
.secondary_action(
widget::button::text(fl!("cancel"))
.style(theme::Button::Destructive)
.on_press(Message::DialogCancel),
)
.control(widget::column().push(widget::text::text(fl!(
"add-permission-control",
path = file_path.as_os_str().to_str()
))));
dialog
}
DialogPage::RenameItem {
from,
parent,
@ -3231,6 +3203,27 @@ impl Application for App {
)
}
}
DialogPage::SetExecutableAndLaunch { path } => {
let name = match path.file_name() {
Some(file_name) => file_name.to_str(),
None => path.as_os_str().to_str(),
};
widget::dialog(fl!("set-executable-and-launch"))
.primary_action(
widget::button::text(fl!("set-and-launch"))
.style(theme::Button::Suggested)
.on_press(Message::DialogComplete),
)
.secondary_action(
widget::button::text(fl!("cancel"))
.style(theme::Button::Standard)
.on_press(Message::DialogCancel),
)
.control(widget::text::text(fl!(
"set-executable-and-launch-description",
name = name
)))
}
};
Some(dialog.into())

View file

@ -14,6 +14,7 @@ use crate::{
config::IconSizes,
err_str, fl,
mime_icon::mime_for_path,
spawn_detached::spawn_detached,
tab,
};
@ -176,6 +177,10 @@ pub enum Operation {
Restore {
paths: Vec<trash::TrashItem>,
},
/// Set executable and launch
SetExecutableAndLaunch {
path: PathBuf,
},
}
async fn copy_or_move(
@ -473,6 +478,9 @@ impl Operation {
fl!("renaming", from = file_name(from), to = file_name(to))
}
Self::Restore { paths } => fl!("restoring", items = paths.len()),
Self::SetExecutableAndLaunch { path } => {
fl!("setting-executable-and-launching", name = file_name(path))
}
}
}
@ -521,6 +529,9 @@ impl Operation {
),
Self::Rename { from, to } => fl!("renamed", from = file_name(from), to = file_name(to)),
Self::Restore { paths } => fl!("restored", items = paths.len()),
Self::SetExecutableAndLaunch { path } => {
fl!("set-executable-and-launched", name = file_name(path))
}
}
}
@ -841,6 +852,33 @@ impl Operation {
.await;
}
}
Self::SetExecutableAndLaunch { path } => {
tokio::task::spawn_blocking(move || -> io::Result<()> {
//TODO: what to do on non-Unix systems?
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&path)?.permissions();
let current_mode = perms.mode();
let new_mode = current_mode | 0o111;
perms.set_mode(new_mode);
fs::set_permissions(&path, perms)?;
}
let mut command = std::process::Command::new(path);
spawn_detached(&mut command)?;
Ok(())
})
.await
.map_err(err_str)?
.map_err(err_str)?;
let _ = msg_tx
.lock()
.await
.send(Message::PendingProgress(id, 100.0))
.await;
}
}
let _ = msg_tx