Adjust executable permission to use Operation and adjust text per UX
This commit is contained in:
parent
00857511ca
commit
5624f37a36
3 changed files with 107 additions and 74 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
133
src/app.rs
133
src/app.rs
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue