Skip to content

Commit ca59b7d

Browse files
committed
Context menu for files in navbar, fixes #187
1 parent cf70c16 commit ca59b7d

File tree

1 file changed

+130
-83
lines changed

1 file changed

+130
-83
lines changed

src/app.rs

Lines changed: 130 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ pub enum PreviewKind {
234234

235235
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
236236
pub enum NavMenuAction {
237+
Open(segmented_button::Entity),
238+
OpenWith(segmented_button::Entity),
237239
OpenInNewTab(segmented_button::Entity),
238240
OpenInNewWindow(segmented_button::Entity),
239241
Preview(segmented_button::Entity),
@@ -525,6 +527,69 @@ pub struct App {
525527
}
526528

527529
impl App {
530+
fn open_file(&mut self, path: &PathBuf) {
531+
let mut found_desktop_exec = false;
532+
let mime = mime_icon::mime_for_path(path);
533+
if mime == "application/x-desktop" {
534+
match freedesktop_entry_parser::parse_entry(path) {
535+
Ok(entry) => match entry.section("Desktop Entry").attr("Exec") {
536+
Some(exec) => match mime_app::exec_to_command(exec, None) {
537+
Some(mut command) => match spawn_detached(&mut command) {
538+
Ok(()) => {
539+
found_desktop_exec = true;
540+
}
541+
Err(err) => {
542+
log::warn!("failed to execute {:?}: {}", path, err);
543+
}
544+
},
545+
None => {
546+
log::warn!("failed to parse {:?}: invalid Desktop Entry/Exec", path);
547+
}
548+
},
549+
None => {
550+
log::warn!("failed to parse {:?}: missing Desktop Entry/Exec", path);
551+
}
552+
},
553+
Err(err) => {
554+
log::warn!("failed to parse {:?}: {}", path, err);
555+
}
556+
}
557+
} else if mime == "application/x-executable" || mime == "application/vnd.appimage" {
558+
let mut command = std::process::Command::new(path);
559+
match spawn_detached(&mut command) {
560+
Ok(()) => {}
561+
Err(err) => match err.kind() {
562+
io::ErrorKind::PermissionDenied => {
563+
// If permission is denied, try marking as executable, then running
564+
self.dialog_pages
565+
.push_back(DialogPage::SetExecutableAndLaunch {
566+
path: path.to_path_buf(),
567+
});
568+
}
569+
_ => {
570+
log::warn!("failed to execute {:?}: {}", path, err);
571+
}
572+
},
573+
}
574+
found_desktop_exec = true;
575+
}
576+
if !found_desktop_exec {
577+
match open::that_detached(path) {
578+
Ok(()) => {
579+
let _ = recently_used_xbel::update_recently_used(
580+
path,
581+
App::APP_ID.to_string(),
582+
"cosmic-files".to_string(),
583+
None,
584+
);
585+
}
586+
Err(err) => {
587+
log::warn!("failed to open {:?}: {}", path, err);
588+
}
589+
}
590+
}
591+
}
592+
528593
fn open_tab_entity(
529594
&mut self,
530595
location: Location,
@@ -1364,14 +1429,28 @@ impl Application for App {
13641429

13651430
let mut items = Vec::new();
13661431

1367-
items.push(cosmic::widget::menu::Item::Button(
1368-
fl!("open-in-new-tab"),
1369-
NavMenuAction::OpenInNewTab(entity),
1370-
));
1371-
items.push(cosmic::widget::menu::Item::Button(
1372-
fl!("open-in-new-window"),
1373-
NavMenuAction::OpenInNewWindow(entity),
1374-
));
1432+
if location_opt
1433+
.and_then(|x| x.path_opt())
1434+
.map_or(false, |x| x.is_file())
1435+
{
1436+
items.push(cosmic::widget::menu::Item::Button(
1437+
fl!("open"),
1438+
NavMenuAction::Open(entity),
1439+
));
1440+
items.push(cosmic::widget::menu::Item::Button(
1441+
fl!("open-with"),
1442+
NavMenuAction::OpenWith(entity),
1443+
));
1444+
} else {
1445+
items.push(cosmic::widget::menu::Item::Button(
1446+
fl!("open-in-new-tab"),
1447+
NavMenuAction::OpenInNewTab(entity),
1448+
));
1449+
items.push(cosmic::widget::menu::Item::Button(
1450+
fl!("open-in-new-window"),
1451+
NavMenuAction::OpenInNewWindow(entity),
1452+
));
1453+
}
13751454
items.push(cosmic::widget::menu::Item::Divider);
13761455
items.push(cosmic::widget::menu::Item::Button(
13771456
fl!("show-details"),
@@ -2538,81 +2617,7 @@ impl Application for App {
25382617
tab::Command::MoveToTrash(paths) => {
25392618
self.operation(Operation::Delete { paths });
25402619
}
2541-
tab::Command::OpenFile(path) => {
2542-
let mut found_desktop_exec = false;
2543-
let mime = mime_icon::mime_for_path(&path);
2544-
if mime == "application/x-desktop" {
2545-
match freedesktop_entry_parser::parse_entry(&path) {
2546-
Ok(entry) => {
2547-
match entry.section("Desktop Entry").attr("Exec") {
2548-
Some(exec) => {
2549-
match mime_app::exec_to_command(exec, None) {
2550-
Some(mut command) => {
2551-
match spawn_detached(&mut command) {
2552-
Ok(()) => {
2553-
found_desktop_exec = true;
2554-
}
2555-
Err(err) => {
2556-
log::warn!(
2557-
"failed to execute {:?}: {}",
2558-
path,
2559-
err
2560-
);
2561-
}
2562-
}
2563-
}
2564-
None => {
2565-
log::warn!("failed to parse {:?}: invalid Desktop Entry/Exec", path);
2566-
}
2567-
}
2568-
}
2569-
None => {
2570-
log::warn!("failed to parse {:?}: missing Desktop Entry/Exec", path);
2571-
}
2572-
}
2573-
}
2574-
Err(err) => {
2575-
log::warn!("failed to parse {:?}: {}", path, err);
2576-
}
2577-
}
2578-
} else if mime == "application/x-executable"
2579-
|| mime == "application/vnd.appimage"
2580-
{
2581-
let mut command = std::process::Command::new(&path);
2582-
match spawn_detached(&mut command) {
2583-
Ok(()) => {}
2584-
Err(err) => match err.kind() {
2585-
io::ErrorKind::PermissionDenied => {
2586-
// If permission is denied, try marking as executable, then running
2587-
self.dialog_pages.push_back(
2588-
DialogPage::SetExecutableAndLaunch {
2589-
path: path.clone(),
2590-
},
2591-
);
2592-
}
2593-
_ => {
2594-
log::warn!("failed to execute {:?}: {}", path, err);
2595-
}
2596-
},
2597-
}
2598-
found_desktop_exec = true;
2599-
}
2600-
if !found_desktop_exec {
2601-
match open::that_detached(&path) {
2602-
Ok(()) => {
2603-
let _ = recently_used_xbel::update_recently_used(
2604-
&path,
2605-
App::APP_ID.to_string(),
2606-
"cosmic-files".to_string(),
2607-
None,
2608-
);
2609-
}
2610-
Err(err) => {
2611-
log::warn!("failed to open {:?}: {}", path, err);
2612-
}
2613-
}
2614-
}
2615-
}
2620+
tab::Command::OpenFile(path) => self.open_file(&path),
26162621
tab::Command::OpenInNewTab(path) => {
26172622
commands.push(self.open_tab(Location::Path(path.clone()), false, None));
26182623
}
@@ -2937,6 +2942,48 @@ impl Application for App {
29372942

29382943
// Applies selected nav bar context menu operation.
29392944
Message::NavMenuAction(action) => match action {
2945+
NavMenuAction::Open(entity) => {
2946+
match self
2947+
.nav_model
2948+
.data::<Location>(entity)
2949+
.and_then(|x| x.path_opt())
2950+
.map(|x| x.to_path_buf())
2951+
{
2952+
Some(path) => {
2953+
self.open_file(&path);
2954+
}
2955+
None => {}
2956+
}
2957+
}
2958+
NavMenuAction::OpenWith(entity) => {
2959+
match self
2960+
.nav_model
2961+
.data::<Location>(entity)
2962+
.and_then(|x| x.path_opt())
2963+
.map(|x| x.to_path_buf())
2964+
{
2965+
Some(path) => match tab::item_from_path(&path, IconSizes::default()) {
2966+
Ok(item) => {
2967+
return self.update(Message::DialogPush(DialogPage::OpenWith {
2968+
path: path.to_path_buf(),
2969+
mime: item.mime.clone(),
2970+
apps: item.open_with.clone(),
2971+
selected: 0,
2972+
store_opt: "x-scheme-handler/mime"
2973+
.parse::<mime_guess::Mime>()
2974+
.ok()
2975+
.and_then(|mime| {
2976+
mime_app::mime_apps(&mime).first().cloned()
2977+
}),
2978+
}));
2979+
}
2980+
Err(err) => {
2981+
log::warn!("failed to get item for path {:?}: {}", path, err);
2982+
}
2983+
},
2984+
None => {}
2985+
}
2986+
}
29402987
NavMenuAction::OpenInNewTab(entity) => {
29412988
match self.nav_model.data::<Location>(entity) {
29422989
Some(Location::Path(ref path)) => {

0 commit comments

Comments
 (0)