fileModel: Use GFile with URI as key

Part-of: <https://gitlab.gnome.org/Incubator/loupe/-/merge_requests/174>
This commit is contained in:
Sophie Herold 2023-04-12 01:11:59 +02:00 • committed by Marge Bot
parent 3b0c32ecee
commit 032707a210
10 changed files with 164 additions and 188 deletions

View file

@ -21,12 +21,11 @@ use crate::deps::*;
use crate::util;
use crate::util::gettext::*;
use std::path::{Path, PathBuf};
use anyhow::Context;
use gio::prelude::*;
use gio::subclass::prelude::*;
use indexmap::IndexSet;
use glib::GString;
use indexmap::{IndexMap, IndexSet};
use once_cell::sync::{Lazy, OnceCell};
use std::cell::RefCell;
@ -38,8 +37,8 @@ mod imp {
pub struct LpFileModel {
/// Use and IndexSet such that we can put the elements into an arbitrary order
/// while still having fast hash access via `PathBuf`.
pub(super) files: RefCell<IndexSet<PathBuf>>,
pub(super) directory: OnceCell<PathBuf>,
pub(super) files: RefCell<IndexMap<GString, gio::File>>,
pub(super) directory: OnceCell<gio::File>,
/// Track file changes.
pub(super) monitor: OnceCell<gio::FileMonitor>,
}
@ -71,12 +70,12 @@ impl Default for LpFileModel {
impl LpFileModel {
/// Create with a single element
pub fn from_path(path: &Path) -> Self {
pub fn from_file(file: &gio::File) -> Self {
let model = Self::default();
{
let mut vec = model.imp().files.borrow_mut();
vec.insert(path.to_path_buf());
vec.insert(file.uri(), file.clone());
}
model
@ -85,10 +84,10 @@ impl LpFileModel {
/// Load all file from the given directory
///
/// This can be used if there are already files present
pub async fn load_directory(&self, directory: PathBuf) -> anyhow::Result<()> {
pub async fn load_directory(&self, directory: gio::File) -> anyhow::Result<()> {
self.imp().directory.set(directory.clone()).unwrap();
let monitor = gio::File::for_path(&directory)
let monitor = directory
.monitor_directory(gio::FileMonitorFlags::WATCH_MOVES, gio::Cancellable::NONE)?;
monitor.connect_changed(
@ -99,9 +98,9 @@ impl LpFileModel {
self.imp().monitor.set(monitor).unwrap();
let new_files_result = gio::spawn_blocking(move || {
let mut files = IndexSet::new();
let mut files = IndexMap::new();
let enumerator = gio::File::for_path(&directory)
let enumerator = directory
.enumerate_children(
&format!(
"{},{}",
@ -118,9 +117,8 @@ impl LpFileModel {
if let Some(content_type) = info.content_type().map(|t| t.to_string()) {
// Filter out non-images; For now we support "all" image types.
if content_type.starts_with("image/") {
let path = PathBuf::from_iter([directory.clone(), info.name()]);
log::debug!("{path:?} is an image, adding to the list");
files.insert(path);
let file = directory.child(info.name());
files.insert(file.uri(), file);
}
}
}
@ -141,8 +139,8 @@ impl LpFileModel {
// Here we use a nested scope so that the mutable borrow only lasts as long as we need it
let mut files = self.imp().files.borrow_mut();
for path in files.iter() {
new_files.insert(path.clone());
for (uri, file) in files.iter() {
new_files.insert(uri.clone(), file.clone());
}
*files = new_files;
// Then sort by name.
@ -152,47 +150,49 @@ impl LpFileModel {
Ok(())
}
pub fn directory(&self) -> Option<PathBuf> {
pub fn directory(&self) -> Option<gio::File> {
self.imp().directory.get().cloned()
}
pub fn index_of(&self, path: &Path) -> Option<usize> {
self.imp().files.borrow().get_index_of(path)
pub fn index_of(&self, file: &gio::File) -> Option<usize> {
self.imp().files.borrow().get_index_of(&file.uri())
}
pub fn n_files(&self) -> usize {
self.imp().files.borrow().len()
}
pub fn contains(&self, path: &Path) -> bool {
self.imp().files.borrow().contains(path)
pub fn contains(&self, file: &gio::File) -> bool {
self.imp().files.borrow().contains_key(&file.uri())
}
pub fn before(&self, path: &Path) -> Option<PathBuf> {
let index = self.index_of(path)?;
pub fn before(&self, file: &gio::File) -> Option<gio::File> {
let index = self.index_of(&file)?;
self.imp()
.files
.borrow()
.get_index(index.checked_sub(1)?)
.map(|x| x.1)
.cloned()
}
pub fn after(&self, path: &Path) -> Option<PathBuf> {
let index = self.index_of(path)?;
pub fn after(&self, file: &gio::File) -> Option<gio::File> {
let index = self.index_of(file)?;
self.imp()
.files
.borrow()
.get_index(index.checked_add(1)?)
.map(|x| x.1)
.cloned()
}
/// Returns `n` elements before and after given path
pub fn files_around(&self, path: &Path, n: usize) -> IndexSet<PathBuf> {
let Some(index) = self.index_of(path) else {
log::error!("Path not in model: {path:?}");
return IndexSet::new();
pub fn files_around(&self, file: &gio::File, n: usize) -> IndexMap<GString, gio::File> {
let Some(index) = self.index_of(file) else {
log::error!("URI not in model: {}", file.uri());
return IndexMap::new();
};
let reduce = if index < n { n - index } else { 0 };
@ -203,23 +203,23 @@ impl LpFileModel {
.iter()
.skip(index.saturating_sub(n))
.take(2 * n + 1 - reduce)
.cloned()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
}
/// Return first path
pub fn first(&self) -> Option<PathBuf> {
self.imp().files.borrow().first().cloned()
pub fn first(&self) -> Option<gio::File> {
self.imp().files.borrow().first().map(|x| x.1).cloned()
}
/// Returns last path
pub fn last(&self) -> Option<PathBuf> {
self.imp().files.borrow().last().cloned()
pub fn last(&self) -> Option<gio::File> {
self.imp().files.borrow().last().map(|x| x.1).cloned()
}
/// Currently sorts by name
fn sort(files: &mut IndexSet<PathBuf>) {
files.sort_by(|x, y| util::compare_by_name(x, y));
fn sort(files: &mut IndexMap<GString, gio::File>) {
files.sort_by(|_, x, _, y| util::compare_by_name(&x.uri(), &y.uri()));
}
/// Determines if a file is added
@ -253,12 +253,6 @@ impl LpFileModel {
file_a: &gio::File,
file_b: Option<&gio::File>,
) {
let Some(path_a) = file_a.path() else {
log::error!("File has no path: {event}");
return;
};
let path_b = file_b.and_then(|x| x.path());
let changed = match event {
gio::FileMonitorEvent::Created
| gio::FileMonitorEvent::MovedIn
@ -268,7 +262,7 @@ impl LpFileModel {
if Self::is_image_file(file_a) =>
{
let mut files = self.imp().files.borrow_mut();
let changed = files.insert(path_a);
let changed = files.insert(file_a.uri(),file_a.clone()).is_some();
if changed {
Self::sort(&mut files);
}
@ -276,20 +270,20 @@ impl LpFileModel {
}
gio::FileMonitorEvent::Deleted | gio::FileMonitorEvent::MovedOut | gio::FileMonitorEvent::Unmounted => {
let mut files = self.imp().files.borrow_mut();
let changed = files.remove(&path_a);
let changed = files.remove(&file_a.uri()).is_some();
if changed {
Self::sort(&mut files);
}
changed
}
gio::FileMonitorEvent::Renamed => {
if let (Some(path_b), Some(file_b)) = (path_b, file_b) {
if let Some(file_b) = file_b {
let mut changed = false;
{
let mut files = self.imp().files.borrow_mut();
changed |= files.remove(&path_a);
changed |= files.remove(&file_a.uri()).is_some();
if Self::is_image_file(file_b) {
changed |= files.insert(path_b);
changed |= files.insert(file_b.uri(), file_b.clone()).is_some();
if changed {
Self::sort(&mut files);
}