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 keep-both = Keep both
skip = Skip skip = Skip
## Execution permission Dialog ## Set as Executable and Launch Dialog
add-permission-title = Add execution permission set-executable-and-launch = Set as executable and launch
add-permission-control = Add execution permission to {$path} set-executable-and-launch-description = Do you want to set "{$name}" as executable and launch it?
add-permission = Add permission set-and-launch = Set and launch
## Metadata Dialog ## Metadata Dialog
owner = Owner owner = Owner
@ -145,6 +145,8 @@ extracted = Extracted {$items} {$items ->
[one] item [one] item
*[other] items *[other] items
} from {$from} to {$to} } 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 -> moving = Moving {$items} {$items ->
[one] item [one] item
*[other] items *[other] items

View file

@ -43,13 +43,10 @@ use slotmap::Key as SlotMapKey;
use std::{ use std::{
any::TypeId, any::TypeId,
collections::{BTreeMap, HashMap, HashSet, VecDeque}, collections::{BTreeMap, HashMap, HashSet, VecDeque},
env, env, fmt, fs,
ffi::OsStr,
fmt, fs,
future::pending, future::pending,
io::{BufRead, BufReader}, io,
num::NonZeroU16, num::NonZeroU16,
os::unix::fs::PermissionsExt,
path::PathBuf, path::PathBuf,
process, process,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
@ -415,10 +412,6 @@ pub enum DialogPage {
name: String, name: String,
dir: bool, dir: bool,
}, },
AddExecutablePermission {
file_path: PathBuf,
run: bool,
},
Replace { Replace {
from: tab::Item, from: tab::Item,
to: tab::Item, to: tab::Item,
@ -426,6 +419,9 @@ pub enum DialogPage {
apply_to_all: bool, apply_to_all: bool,
tx: mpsc::Sender<ReplaceResult>, tx: mpsc::Sender<ReplaceResult>,
}, },
SetExecutableAndLaunch {
path: PathBuf,
},
} }
pub struct FavoriteIndex(usize); pub struct FavoriteIndex(usize);
@ -1465,25 +1461,12 @@ impl Application for App {
let to = parent.join(name); let to = parent.join(name);
self.operation(Operation::Rename { from, to }); 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 { .. } => { DialogPage::Replace { .. } => {
log::warn!("replace dialog should be completed with replace result"); 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) => { tab::Command::OpenFile(path) => {
let mut found_desktop_exec = false; 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) { match freedesktop_entry_parser::parse_entry(&path) {
Ok(entry) => { Ok(entry) => {
match entry.section("Desktop Entry").attr("Exec") { match entry.section("Desktop Entry").attr("Exec") {
@ -2275,37 +2259,43 @@ impl Application for App {
Err(err) => { Err(err) => {
log::warn!("failed to parse {:?}: {}", path, err); log::warn!("failed to parse {:?}: {}", path, err);
} }
}; }
} } else if mime == "application/x-executable"
if !found_desktop_exec { || mime == "application/vnd.appimage"
let file_extension = path.extension(); {
match file_extension { let mut command = std::process::Command::new(&path);
Some(ext) if ext == OsStr::new("AppImage") => { match spawn_detached(&mut command) {
let mut perms = fs::metadata(&path) Ok(()) => {}
.expect("Failed to get metadata") Err(err) => match err.kind() {
.permissions(); io::ErrorKind::PermissionDenied => {
// If permission is denied, try marking as executable, then running
self.dialog_pages.push_back( self.dialog_pages.push_back(
DialogPage::AddExecutablePermission { DialogPage::SetExecutableAndLaunch {
file_path: path.clone(), 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,
); );
} }
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) => { tab::Command::OpenInNewTab(path) => {
@ -3088,24 +3078,6 @@ impl Application for App {
.spacing(space_xxs), .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 { DialogPage::RenameItem {
from, from,
parent, 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()) Some(dialog.into())

View file

@ -14,6 +14,7 @@ use crate::{
config::IconSizes, config::IconSizes,
err_str, fl, err_str, fl,
mime_icon::mime_for_path, mime_icon::mime_for_path,
spawn_detached::spawn_detached,
tab, tab,
}; };
@ -176,6 +177,10 @@ pub enum Operation {
Restore { Restore {
paths: Vec<trash::TrashItem>, paths: Vec<trash::TrashItem>,
}, },
/// Set executable and launch
SetExecutableAndLaunch {
path: PathBuf,
},
} }
async fn copy_or_move( async fn copy_or_move(
@ -473,6 +478,9 @@ impl Operation {
fl!("renaming", from = file_name(from), to = file_name(to)) fl!("renaming", from = file_name(from), to = file_name(to))
} }
Self::Restore { paths } => fl!("restoring", items = paths.len()), 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::Rename { from, to } => fl!("renamed", from = file_name(from), to = file_name(to)),
Self::Restore { paths } => fl!("restored", items = paths.len()), 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; .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 let _ = msg_tx