Use gio to read display names for gvfs mounts, fixes #174
This commit is contained in:
parent
f07a106d0f
commit
d0fb166cf6
1 changed files with 116 additions and 43 deletions
159
src/tab.rs
159
src/tab.rs
|
|
@ -524,11 +524,18 @@ fn hidden_attribute(metadata: &Metadata) -> bool {
|
||||||
metadata.file_attributes() & FILE_ATTRIBUTE_HIDDEN == FILE_ATTRIBUTE_HIDDEN
|
metadata.file_attributes() & FILE_ATTRIBUTE_HIDDEN == FILE_ATTRIBUTE_HIDDEN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum FsKind {
|
||||||
|
Local,
|
||||||
|
Remote,
|
||||||
|
Gvfs,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn remote_fs(metadata: &Metadata) -> bool {
|
fn fs_kind(metadata: &Metadata) -> FsKind {
|
||||||
//TODO: method to reload remote filesystems dynamically
|
//TODO: method to reload remote filesystems dynamically
|
||||||
//TODO: fix for https://github.com/eminence/procfs/issues/262
|
//TODO: fix for https://github.com/eminence/procfs/issues/262
|
||||||
static DEVICES: Lazy<HashMap<u64, bool>> = Lazy::new(|| {
|
static DEVICES: Lazy<HashMap<u64, FsKind>> = Lazy::new(|| {
|
||||||
let mut devices = HashMap::new();
|
let mut devices = HashMap::new();
|
||||||
match procfs::process::Process::myself() {
|
match procfs::process::Process::myself() {
|
||||||
Ok(process) => match process.mountinfo() {
|
Ok(process) => match process.mountinfo() {
|
||||||
|
|
@ -549,19 +556,13 @@ fn remote_fs(metadata: &Metadata) -> bool {
|
||||||
};
|
};
|
||||||
let dev = libc::makedev(major, minor);
|
let dev = libc::makedev(major, minor);
|
||||||
//TODO: make sure this list is exhaustive
|
//TODO: make sure this list is exhaustive
|
||||||
let remote = [
|
let kind = match mount_info.fs_type.as_str() {
|
||||||
"cifs",
|
"cifs" | "fuse.rclone" | "fuse.sshfs" | "nfs" | "nfs4" | "smb"
|
||||||
//TODO: check with GVFS?
|
| "smb2" => FsKind::Remote,
|
||||||
"fuse.gvfsd-fuse",
|
"fuse.gvfsd-fuse" => FsKind::Gvfs,
|
||||||
"fuse.rclone",
|
_ => FsKind::Local,
|
||||||
"fuse.sshfs",
|
};
|
||||||
"nfs",
|
devices.insert(dev, kind);
|
||||||
"nfs4",
|
|
||||||
"smb",
|
|
||||||
"smb2",
|
|
||||||
]
|
|
||||||
.contains(&mount_info.fs_type.as_str());
|
|
||||||
devices.insert(dev, remote);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
@ -574,13 +575,13 @@ fn remote_fs(metadata: &Metadata) -> bool {
|
||||||
}
|
}
|
||||||
devices
|
devices
|
||||||
});
|
});
|
||||||
DEVICES.get(&metadata.dev()).map_or(false, |x| *x)
|
DEVICES.get(&metadata.dev()).map_or(FsKind::Local, |x| *x)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
fn remote_fs(_metadata: &Metadata) -> bool {
|
fn fs_kind(_metadata: &Metadata) -> FsKind {
|
||||||
//TODO: support BSD, macOS, Windows?
|
//TODO: support BSD, macOS, Windows?
|
||||||
false
|
FsKind::Local
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_desktop_file(path: &Path) -> (Option<String>, Option<String>) {
|
pub fn parse_desktop_file(path: &Path) -> (Option<String>, Option<String>) {
|
||||||
|
|
@ -613,7 +614,47 @@ pub fn item_from_entry(
|
||||||
|
|
||||||
let hidden = name.starts_with(".") || hidden_attribute(&metadata);
|
let hidden = name.starts_with(".") || hidden_attribute(&metadata);
|
||||||
|
|
||||||
let remote = remote_fs(&metadata);
|
let remote = match fs_kind(&metadata) {
|
||||||
|
FsKind::Local => false,
|
||||||
|
FsKind::Remote => true,
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
FsKind::Gvfs => {
|
||||||
|
let file = gio::File::for_path(&path);
|
||||||
|
match gio::prelude::FileExt::query_info(
|
||||||
|
&file,
|
||||||
|
gio::FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
|
||||||
|
gio::FileQueryInfoFlags::NONE,
|
||||||
|
gio::Cancellable::NONE,
|
||||||
|
) {
|
||||||
|
Ok(info) => {
|
||||||
|
display_name = Item::display_name(&info.display_name());
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("failed to get GIO info for {:?}: {}", path, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match gio::prelude::FileExt::query_filesystem_info(
|
||||||
|
&file,
|
||||||
|
gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE,
|
||||||
|
gio::Cancellable::NONE,
|
||||||
|
) {
|
||||||
|
Ok(info) => info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE),
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("failed to get GIO filesystem info for {:?}: {}", path, err);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "gvfs"))]
|
||||||
|
FsKind::Gvfs => {
|
||||||
|
log::info!(
|
||||||
|
"gvfs feature not enabled, info may be inaccurate for {:?}",
|
||||||
|
path
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let (mime, icon_handle_grid, icon_handle_list, icon_handle_list_condensed) =
|
let (mime, icon_handle_grid, icon_handle_list, icon_handle_list_condensed) =
|
||||||
if metadata.is_dir() {
|
if metadata.is_dir() {
|
||||||
|
|
@ -1198,6 +1239,20 @@ impl Location {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ancestors(&self) -> Vec<(Location, String)> {
|
||||||
|
let mut ancestors = Vec::new();
|
||||||
|
if let Some(path) = self.path_opt() {
|
||||||
|
for ancestor in path.ancestors() {
|
||||||
|
let (name, found_home) = folder_name(ancestor);
|
||||||
|
ancestors.push((self.with_path(ancestor.to_path_buf()), name));
|
||||||
|
if found_home {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ancestors
|
||||||
|
}
|
||||||
|
|
||||||
pub fn path_opt(&self) -> Option<&PathBuf> {
|
pub fn path_opt(&self) -> Option<&PathBuf> {
|
||||||
match self {
|
match self {
|
||||||
Self::Desktop(path, ..) => Some(path),
|
Self::Desktop(path, ..) => Some(path),
|
||||||
|
|
@ -1247,6 +1302,31 @@ impl Location {
|
||||||
};
|
};
|
||||||
(parent_item_opt, items)
|
(parent_item_opt, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn title(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Desktop(path, _, _) => {
|
||||||
|
let (name, _) = folder_name(path);
|
||||||
|
name
|
||||||
|
}
|
||||||
|
Self::Path(path) => {
|
||||||
|
let (name, _) = folder_name(path);
|
||||||
|
name
|
||||||
|
}
|
||||||
|
Self::Search(path, term, ..) => {
|
||||||
|
//TODO: translate
|
||||||
|
let (name, _) = folder_name(path);
|
||||||
|
format!("Search \"{}\": {}", term, name)
|
||||||
|
}
|
||||||
|
Self::Trash => {
|
||||||
|
fl!("trash")
|
||||||
|
}
|
||||||
|
Self::Recents => {
|
||||||
|
fl!("recents")
|
||||||
|
}
|
||||||
|
Self::Network(_uri, display_name) => display_name.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TaskWrapper(pub cosmic::Task<Message>);
|
pub struct TaskWrapper(pub cosmic::Task<Message>);
|
||||||
|
|
@ -1946,6 +2026,8 @@ impl fmt::Debug for SearchContextWrapper {
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
//TODO: make more items private
|
//TODO: make more items private
|
||||||
pub location: Location,
|
pub location: Location,
|
||||||
|
pub location_ancestors: Vec<(Location, String)>,
|
||||||
|
pub location_title: String,
|
||||||
pub location_context_menu_point: Option<Point>,
|
pub location_context_menu_point: Option<Point>,
|
||||||
pub location_context_menu_index: Option<usize>,
|
pub location_context_menu_index: Option<usize>,
|
||||||
pub context_menu: Option<Point>,
|
pub context_menu: Option<Point>,
|
||||||
|
|
@ -2011,7 +2093,11 @@ fn folder_name<P: AsRef<Path>>(path: P) -> (String, bool) {
|
||||||
found_home = true;
|
found_home = true;
|
||||||
fl!("home")
|
fl!("home")
|
||||||
} else {
|
} else {
|
||||||
name.to_string_lossy().to_string()
|
// This is not optimized but it helps ensure the same display names
|
||||||
|
match item_from_path(path, IconSizes::default()) {
|
||||||
|
Ok(item) => item.display_name,
|
||||||
|
Err(_err) => name.to_string_lossy().to_string(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -2040,9 +2126,14 @@ fn parse_hidden_file(path: &PathBuf) -> Vec<String> {
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn new(location: Location, config: TabConfig) -> Self {
|
pub fn new(location: Location, config: TabConfig) -> Self {
|
||||||
|
let location = location.normalize();
|
||||||
|
let location_ancestors = location.ancestors();
|
||||||
|
let location_title = location.title();
|
||||||
let history = vec![location.clone()];
|
let history = vec![location.clone()];
|
||||||
Self {
|
Self {
|
||||||
location: location.normalize(),
|
location,
|
||||||
|
location_ancestors,
|
||||||
|
location_title,
|
||||||
context_menu: None,
|
context_menu: None,
|
||||||
location_context_menu_point: None,
|
location_context_menu_point: None,
|
||||||
location_context_menu_index: None,
|
location_context_menu_index: None,
|
||||||
|
|
@ -2081,28 +2172,8 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn title(&self) -> String {
|
pub fn title(&self) -> String {
|
||||||
match &self.location {
|
//TODO: is it possible to return a &str?
|
||||||
Location::Desktop(path, _, _) => {
|
self.location_title.clone()
|
||||||
let (name, _) = folder_name(path);
|
|
||||||
name
|
|
||||||
}
|
|
||||||
Location::Path(path) => {
|
|
||||||
let (name, _) = folder_name(path);
|
|
||||||
name
|
|
||||||
}
|
|
||||||
Location::Search(path, term, ..) => {
|
|
||||||
//TODO: translate
|
|
||||||
let (name, _) = folder_name(path);
|
|
||||||
format!("Search \"{}\": {}", term, name)
|
|
||||||
}
|
|
||||||
Location::Trash => {
|
|
||||||
fl!("trash")
|
|
||||||
}
|
|
||||||
Location::Recents => {
|
|
||||||
fl!("recents")
|
|
||||||
}
|
|
||||||
Location::Network(_uri, display_name) => display_name.clone(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn items_opt(&self) -> Option<&Vec<Item>> {
|
pub fn items_opt(&self) -> Option<&Vec<Item>> {
|
||||||
|
|
@ -2395,6 +2466,8 @@ impl Tab {
|
||||||
|
|
||||||
pub fn change_location(&mut self, location: &Location, history_i_opt: Option<usize>) {
|
pub fn change_location(&mut self, location: &Location, history_i_opt: Option<usize>) {
|
||||||
self.location = location.normalize();
|
self.location = location.normalize();
|
||||||
|
self.location_ancestors = self.location.ancestors();
|
||||||
|
self.location_title = self.location.title();
|
||||||
self.context_menu = None;
|
self.context_menu = None;
|
||||||
self.edit_location = None;
|
self.edit_location = None;
|
||||||
self.items_opt = None;
|
self.items_opt = None;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue