[niepce] rust+engine: port thumbnail cache to Rust



commit cb0a81d31726b96f75a2c707f224e14d171d4719
Author: Hubert Figuière <hub figuiere net>
Date:   Tue Feb 4 00:57:39 2020 -0500

    rust+engine: port thumbnail cache to Rust
    
    - remove thumbnail notifications and merge them with lib notifications

 Cargo.lock                                         |   3 +
 crates/npc-engine/Cargo.toml                       |   2 +
 crates/npc-engine/build.rs                         |   1 +
 crates/npc-engine/src/lib.rs                       |   2 +
 crates/npc-engine/src/library/mod.rs               |   1 +
 crates/npc-engine/src/library/notification.rs      |  31 +++
 crates/npc-engine/src/library/queriedcontent.rs    |   4 +
 crates/npc-engine/src/library/thumbnail_cache.rs   | 238 +++++++++++++++++++++
 crates/npc-fwk/Cargo.toml                          |   3 +-
 crates/npc-fwk/build.rs                            |   1 +
 crates/npc-fwk/src/lib.rs                          |   1 +
 crates/npc-fwk/src/toolkit/gdk_utils.rs            | 142 ++++++++++++
 crates/npc-fwk/src/toolkit/mimetype.rs             |   7 +-
 crates/npc-fwk/src/toolkit/mod.rs                  |   2 +
 .../npc-fwk/src/toolkit/movieutils.rs              |  34 +--
 crates/npc-fwk/src/toolkit/thumbnail.rs            | 227 ++++++++++++++++++++
 src/Makefile.am                                    |   2 +
 src/engine/Makefile.am                             |   1 -
 src/engine/importer/cameraimporter.cpp             |   4 +-
 src/engine/importer/directoryimporter.cpp          |   3 +-
 src/engine/importer/iimporter.hpp                  |   6 +-
 src/engine/library/thumbnailcache.cpp              | 154 -------------
 src/engine/library/thumbnailcache.hpp              |  86 --------
 src/fwk/toolkit/Makefile.am                        |   1 -
 src/fwk/toolkit/gphoto.cpp                         |   7 +-
 src/fwk/toolkit/gphoto.hpp                         |   5 +-
 src/fwk/toolkit/movieutils.cpp                     |  49 -----
 src/fwk/toolkit/movieutils.hpp                     |  43 ----
 src/fwk/toolkit/notificationcenter.cpp             |  26 +--
 src/fwk/toolkit/notificationcenter.hpp             |   7 +-
 src/fwk/toolkit/thumbnail.cpp                      | 219 +------------------
 src/fwk/toolkit/thumbnail.hpp                      |  34 +--
 src/libraryclient/libraryclient.cpp                |   9 +-
 src/libraryclient/libraryclient.hpp                |   8 +-
 src/niepce/Makefile.am                             |   1 -
 src/niepce/notificationcenter.cpp                  |  40 +---
 src/niepce/notificationcenter.hpp                  |  11 +-
 src/niepce/notifications.hpp                       |  34 ---
 src/niepce/ui/dialogs/importdialog.cpp             |   6 +-
 src/niepce/ui/dialogs/importdialog.hpp             |   3 +-
 src/niepce/ui/filmstripcontroller.cpp              |   2 -
 src/niepce/ui/imageliststore.cpp                   |  38 ++--
 src/niepce/ui/imageliststore.hpp                   |   4 +-
 src/niepce/ui/moduleshell.cpp                      |   1 -
 src/niepce/ui/niepcewindow.cpp                     |  10 +-
 45 files changed, 742 insertions(+), 771 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index af6b017..6a4bf62 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -606,6 +606,8 @@ dependencies = [
  "cbindgen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "exempi 2.5.0 (git+https://github.com/hfiguiere/exempi-rs.git?rev=53cfc05)",
+ "gdk-pixbuf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "glib 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "npc-fwk 0.1.0",
@@ -624,6 +626,7 @@ dependencies = [
  "exempi 2.5.0 (git+https://github.com/hfiguiere/exempi-rs.git?rev=53cfc05)",
  "gdk 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "gdk-pixbuf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk-pixbuf-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gio 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "glib 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/crates/npc-engine/Cargo.toml b/crates/npc-engine/Cargo.toml
index 0b22aab..0f1125a 100644
--- a/crates/npc-engine/Cargo.toml
+++ b/crates/npc-engine/Cargo.toml
@@ -10,6 +10,8 @@ build = "build.rs"
 [dependencies]
 chrono = "0.4.0"
 exempi = { version = "2.5.0", git = "https://github.com/hfiguiere/exempi-rs.git";, rev="53cfc05" }
+gdk-pixbuf-sys = "*"
+gdk-pixbuf = "*"
 glib = "*"
 libc = "0.2.39"
 rusqlite = { version = "0.14.0", features = ["functions"] }
diff --git a/crates/npc-engine/build.rs b/crates/npc-engine/build.rs
index ae2ae9c..63776b0 100644
--- a/crates/npc-engine/build.rs
+++ b/crates/npc-engine/build.rs
@@ -70,6 +70,7 @@ fn main() {
             .with_parse_deps(true)
             .with_parse_exclude(&["exempi", "chrono", "multimap"])
             .include_item("Managed")
+            .exclude_item("GdkPixbuf")
             .exclude_item("GtkWindow")
             .exclude_item("GtkToolbar")
             .exclude_item("GFileInfo")
diff --git a/crates/npc-engine/src/lib.rs b/crates/npc-engine/src/lib.rs
index 6602e78..e489fa4 100644
--- a/crates/npc-engine/src/lib.rs
+++ b/crates/npc-engine/src/lib.rs
@@ -20,6 +20,8 @@
 extern crate glib;
 #[macro_use]
 extern crate try_opt;
+extern crate gdk_pixbuf;
+extern crate gdk_pixbuf_sys;
 
 #[macro_use]
 extern crate npc_fwk;
diff --git a/crates/npc-engine/src/library/mod.rs b/crates/npc-engine/src/library/mod.rs
index ead2f6c..f65967b 100644
--- a/crates/npc-engine/src/library/mod.rs
+++ b/crates/npc-engine/src/library/mod.rs
@@ -2,3 +2,4 @@ pub mod commands;
 pub mod notification;
 pub mod op;
 pub mod queriedcontent;
+pub mod thumbnail_cache;
diff --git a/crates/npc-engine/src/library/notification.rs b/crates/npc-engine/src/library/notification.rs
index 72531e6..4da84b0 100644
--- a/crates/npc-engine/src/library/notification.rs
+++ b/crates/npc-engine/src/library/notification.rs
@@ -17,10 +17,15 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+use gdk_pixbuf;
+use gdk_pixbuf_sys;
+use glib::translate::*;
+
 use super::queriedcontent::QueriedContent;
 use crate::db::libfile::FileStatus;
 use crate::db::{Keyword, Label, LibFolder, LibMetadata, LibraryId};
 use npc_fwk::base::PropertyIndex;
+use npc_fwk::toolkit::thumbnail;
 use npc_fwk::toolkit::PortableChannel;
 use npc_fwk::PropertyValue;
 
@@ -50,6 +55,7 @@ pub enum NotificationType {
     XMP_NEEDS_UPDATE,
     FILE_MOVED,
     FILE_STATUS_CHANGED,
+    ThumbnailLoaded,
 }
 
 #[repr(C)]
@@ -87,6 +93,15 @@ impl MetadataChange {
     }
 }
 
+#[repr(C)]
+#[derive(Clone)]
+pub struct Thumbnail {
+    pub id: LibraryId,
+    pub width: i32,
+    pub height: i32,
+    pub pix: thumbnail::Thumbnail,
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn metadatachange_get_id(meta: *const MetadataChange) -> LibraryId {
     (*meta).id
@@ -126,6 +141,7 @@ pub enum LibNotification {
     MetadataChanged(MetadataChange),
     MetadataQueried(LibMetadata),
     XmpNeedsUpdate,
+    ThumbnailLoaded(Thumbnail),
 }
 
 /// Send a notification for the file status change.
@@ -183,6 +199,7 @@ pub unsafe extern "C" fn engine_library_notification_type(
         Some(&LibNotification::MetadataChanged(_)) => NotificationType::METADATA_CHANGED,
         Some(&LibNotification::MetadataQueried(_)) => NotificationType::METADATA_QUERIED,
         Some(&LibNotification::XmpNeedsUpdate) => NotificationType::XMP_NEEDS_UPDATE,
+        Some(&LibNotification::ThumbnailLoaded(_)) => NotificationType::ThumbnailLoaded,
         None => unreachable!(),
     }
 }
@@ -196,6 +213,7 @@ pub unsafe extern "C" fn engine_library_notification_get_id(
         Some(&LibNotification::FolderDeleted(id)) => id,
         Some(&LibNotification::LabelDeleted(id)) => id,
         Some(&LibNotification::FileStatusChanged(ref changed)) => changed.id,
+        Some(&LibNotification::ThumbnailLoaded(ref thumbnail)) => thumbnail.id,
         _ => unreachable!(),
     }
 }
@@ -295,3 +313,16 @@ pub unsafe extern "C" fn engine_library_notification_get_content(
         _ => unreachable!(),
     }
 }
+
+#[no_mangle]
+pub unsafe extern "C" fn engine_library_notification_get_pixbuf(
+    n: *const LibNotification,
+) -> *mut gdk_pixbuf_sys::GdkPixbuf {
+    match n.as_ref() {
+        Some(&LibNotification::ThumbnailLoaded(ref thumbnail)) => {
+            let pixbuf: gdk_pixbuf::Pixbuf = thumbnail.pix.clone().into();
+            pixbuf.to_glib_full()
+        }
+        _ => unreachable!(),
+    }
+}
diff --git a/crates/npc-engine/src/library/queriedcontent.rs b/crates/npc-engine/src/library/queriedcontent.rs
index 09ddfed..6a84430 100644
--- a/crates/npc-engine/src/library/queriedcontent.rs
+++ b/crates/npc-engine/src/library/queriedcontent.rs
@@ -38,6 +38,10 @@ impl QueriedContent {
     pub fn push(&mut self, f: LibFile) {
         self.content.push(f);
     }
+
+    pub fn get_content(&self) -> &Vec<LibFile> {
+        &self.content
+    }
 }
 
 #[no_mangle]
diff --git a/crates/npc-engine/src/library/thumbnail_cache.rs 
b/crates/npc-engine/src/library/thumbnail_cache.rs
new file mode 100644
index 0000000..0b5067b
--- /dev/null
+++ b/crates/npc-engine/src/library/thumbnail_cache.rs
@@ -0,0 +1,238 @@
+/*
+ * niepce - library/thumbnail_cache.rs
+ *
+ * Copyright (C) 2020 Hubert Figuière
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+use libc::c_char;
+use std::cmp;
+use std::collections::VecDeque;
+use std::ffi::CStr;
+use std::fs;
+use std::path::{Path, PathBuf};
+use std::sync;
+use std::sync::atomic;
+use std::thread;
+
+use gdk_pixbuf;
+use glib;
+
+use crate::db::libfile::{FileStatus, LibFile};
+use crate::db::LibraryId;
+use crate::library::notification;
+use crate::library::notification::LibNotification::{FileStatusChanged, ThumbnailLoaded};
+use crate::library::notification::{FileStatusChange, LcChannel, LibNotification};
+use crate::library::queriedcontent::QueriedContent;
+use npc_fwk::toolkit::thumbnail::Thumbnail;
+
+/// Thumbnail task
+struct ThumbnailTask {
+    /// Requested width.
+    width: i32,
+    /// Requested height.
+    height: i32,
+    /// File to generate thumbnail for.
+    file: LibFile,
+}
+
+impl ThumbnailTask {
+    /// Create a new ThumbnailTask
+    pub fn new(file: LibFile, width: i32, height: i32) -> Self {
+        ThumbnailTask {
+            file,
+            width,
+            height,
+        }
+    }
+}
+
+fn get_thumbnail(f: &LibFile, w: i32, h: i32, cached: &Path) -> Thumbnail {
+    let filename = f.path();
+    if ThumbnailCache::is_thumbnail_cached(filename, cached) {
+        dbg_out!("thumbnail for {:?} is cached!", filename);
+        return Thumbnail::from(gdk_pixbuf::Pixbuf::new_from_file(cached).ok());
+    }
+
+    dbg_out!("creating thumbnail for {:?}", filename);
+    if let Some(cached_dir) = cached.parent() {
+        if let Err(err) = fs::create_dir_all(cached_dir) {
+            err_out!("Coudln't create directories for {:?}: {}", cached, err);
+        }
+    }
+
+    let thumbnail = Thumbnail::thumbnail_file(filename, w, h, f.orientation());
+    if thumbnail.ok() {
+        thumbnail.save(cached, "png");
+    } else {
+        dbg_out!("couldn't get the thumbnail for {:?}", filename);
+    }
+    return thumbnail;
+}
+
+type Tasks = sync::Arc<(sync::Mutex<VecDeque<ThumbnailTask>>, sync::Condvar)>;
+type Running = sync::Arc<atomic::AtomicBool>;
+
+pub struct ThumbnailCache {
+    cache_dir: PathBuf,
+    tasks: Tasks,
+    running: Running,
+    sender: glib::Sender<LibNotification>,
+}
+
+impl ThumbnailCache {
+    fn new(dir: &Path, sender: glib::Sender<LibNotification>) -> Self {
+        Self {
+            cache_dir: PathBuf::from(dir),
+            tasks: sync::Arc::new((sync::Mutex::new(VecDeque::new()), sync::Condvar::new())),
+            running: sync::Arc::new(atomic::AtomicBool::new(false)),
+            sender,
+        }
+    }
+
+    fn execute(task: ThumbnailTask, cache_dir: &Path, sender: glib::Sender<LibNotification>) {
+        let w = task.width;
+        let h = task.height;
+        let libfile = task.file;
+
+        let path = libfile.path();
+        let id = libfile.id();
+        let dest = Self::path_for_thumbnail(path, id, cmp::max(w, h), cache_dir);
+
+        let pix = get_thumbnail(&libfile, w, h, &dest);
+        if !path.is_file() {
+            dbg_out!("file doesn't exist");
+            if let Err(err) = sender.send(FileStatusChanged(FileStatusChange {
+                id,
+                status: FileStatus::Missing,
+            })) {
+                err_out!("Sending file status change notification failed: {}", err);
+            }
+        }
+
+        if !pix.ok() {
+            return;
+        }
+        // notify the thumbnail
+        if let Err(err) = sender.send(ThumbnailLoaded(notification::Thumbnail {
+            id,
+            width: pix.get_width(),
+            height: pix.get_height(),
+            pix,
+        })) {
+            err_out!("Sending thumbnail notification failed: {}", err);
+        }
+    }
+
+    fn main(
+        running: &Running,
+        tasks: &Tasks,
+        cache_dir: PathBuf,
+        sender: glib::Sender<LibNotification>,
+    ) {
+        while running.load(atomic::Ordering::Relaxed) {
+            let elem: Option<ThumbnailTask>;
+            {
+                let mut queue = tasks.0.lock().unwrap();
+                if !queue.is_empty() {
+                    elem = queue.pop_front();
+                } else {
+                    elem = None;
+                }
+            }
+
+            if let Some(task) = elem {
+                Self::execute(task, &cache_dir, sender.clone());
+            }
+
+            let queue = tasks.0.lock().unwrap();
+            if queue.is_empty() {
+                running.store(false, atomic::Ordering::Relaxed);
+            }
+        }
+    }
+
+    fn schedule(&self, task: ThumbnailTask) {
+        if let Ok(ref mut q) = self.tasks.0.lock() {
+            q.push_back(task);
+            if !self.running.load(atomic::Ordering::Relaxed) {
+                self.running.store(true, atomic::Ordering::Relaxed);
+                let running = self.running.clone();
+                let tasks = self.tasks.clone();
+                let cache_dir = self.cache_dir.clone();
+                let sender = self.sender.clone();
+                thread::spawn(move || {
+                    Self::main(&running, &tasks, cache_dir, sender);
+                });
+            }
+        }
+    }
+
+    pub fn request(&self, fl: &Vec<LibFile>) {
+        for f in fl {
+            self.schedule(ThumbnailTask::new(f.clone(), 160, 160));
+        }
+    }
+
+    pub fn is_thumbnail_cached(_file: &Path, thumb: &Path) -> bool {
+        thumb.is_file()
+    }
+
+    pub fn path_for_thumbnail(
+        filename: &Path,
+        id: LibraryId,
+        size: i32,
+        cache_dir: &Path,
+    ) -> PathBuf {
+        let base_name = filename.file_name().and_then(|f| f.to_str()).unwrap(); // XXX fatal if fails.
+        let thumb_name = format!("{}-{}.png", id, base_name);
+        let mut path = PathBuf::from(Self::dir_for_thumbnail(size, cache_dir));
+        path.push(thumb_name);
+        path
+    }
+
+    pub fn dir_for_thumbnail(size: i32, cache_dir: &Path) -> PathBuf {
+        let subdir = if size == 0 {
+            "full".to_string()
+        } else {
+            size.to_string()
+        };
+        let mut dir = PathBuf::from(cache_dir);
+        dir.push(subdir);
+        dir
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn engine_library_thumbnail_cache_new(
+    dir: *const c_char,
+    channel: *const LcChannel,
+) -> *mut ThumbnailCache {
+    let path = PathBuf::from(&*CStr::from_ptr(dir).to_string_lossy());
+    Box::into_raw(Box::new(ThumbnailCache::new(&path, (*channel).0.clone())))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn engine_library_thumbnail_cache_delete(obj: *mut ThumbnailCache) {
+    Box::from_raw(obj);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn engine_library_thumbnail_cache_request(
+    self_: &mut ThumbnailCache,
+    content: &QueriedContent,
+) {
+    self_.request(content.get_content())
+}
diff --git a/crates/npc-fwk/Cargo.toml b/crates/npc-fwk/Cargo.toml
index 33df4f6..c64c25e 100644
--- a/crates/npc-fwk/Cargo.toml
+++ b/crates/npc-fwk/Cargo.toml
@@ -17,7 +17,8 @@ glib-sys = "*"
 glib = { version = "^0.9.0" }
 gtk-sys = "*"
 gdk = "^0.12.0"
-gdk-pixbuf = "0.8.0"
+gdk-pixbuf-sys = "*"
+gdk-pixbuf = { version = "0.8.0", features = [ "v2_32" ] }
 gtk = { version = "^0.8.0", git = "https://github.com/hfiguiere/gtk.git";, branch = "0.8.0-p1" }
 libc = "0.2.39"
 multimap = "0.4.0"
diff --git a/crates/npc-fwk/build.rs b/crates/npc-fwk/build.rs
index 1850b30..2f11988 100644
--- a/crates/npc-fwk/build.rs
+++ b/crates/npc-fwk/build.rs
@@ -16,6 +16,7 @@ fn main() {
             .with_language(cbindgen::Language::Cxx)
             .with_parse_deps(true)
             .with_parse_exclude(&["exempi", "chrono", "multimap"])
+            .exclude_item("GdkPixbuf")
             .exclude_item("GtkDrawingArea")
             .exclude_item("GtkWidget")
             .exclude_item("GtkWindow")
diff --git a/crates/npc-fwk/src/lib.rs b/crates/npc-fwk/src/lib.rs
index 1c2899d..8ddd2af 100644
--- a/crates/npc-fwk/src/lib.rs
+++ b/crates/npc-fwk/src/lib.rs
@@ -21,6 +21,7 @@ extern crate chrono;
 extern crate exempi;
 extern crate gdk;
 extern crate gdk_pixbuf;
+extern crate gdk_pixbuf_sys;
 extern crate gio;
 extern crate gio_sys;
 #[macro_use]
diff --git a/crates/npc-fwk/src/toolkit/gdk_utils.rs b/crates/npc-fwk/src/toolkit/gdk_utils.rs
index 2ff9da6..6f1bbd4 100644
--- a/crates/npc-fwk/src/toolkit/gdk_utils.rs
+++ b/crates/npc-fwk/src/toolkit/gdk_utils.rs
@@ -17,10 +17,16 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+use libc::c_char;
 use std::cmp;
+use std::path::Path;
+use std::slice;
 
 use gdk_pixbuf;
+use gdk_pixbuf::prelude::*;
+use glib::translate::*;
 
+/// Scale the pixbuf to fit in a square of %dim pixels
 pub fn gdkpixbuf_scale_to_fit(
     pix: Option<&gdk_pixbuf::Pixbuf>,
     dim: i32,
@@ -41,3 +47,139 @@ pub fn gdkpixbuf_scale_to_fit(
         None
     }
 }
+
+/// Rotate the pixbuf according to the Exif orientation value
+pub fn gdkpixbuf_exif_rotate(
+    pix: Option<&gdk_pixbuf::Pixbuf>,
+    orientation: i32,
+) -> Option<gdk_pixbuf::Pixbuf> {
+    match orientation {
+        0 | 1 => pix.map(|p| p.clone()),
+        2 => pix.and_then(|p| p.flip(true)),
+        3 => pix.and_then(|p| p.rotate_simple(gdk_pixbuf::PixbufRotation::Upsidedown)),
+        4 => pix
+            .and_then(|p| p.rotate_simple(gdk_pixbuf::PixbufRotation::Upsidedown))
+            .and_then(|p| p.flip(true)),
+        5 => pix
+            .and_then(|p| p.rotate_simple(gdk_pixbuf::PixbufRotation::Clockwise))
+            .and_then(|p| p.flip(false)),
+        6 => pix.and_then(|p| p.rotate_simple(gdk_pixbuf::PixbufRotation::Clockwise)),
+        7 => pix
+            .and_then(|p| p.rotate_simple(gdk_pixbuf::PixbufRotation::Counterclockwise))
+            .and_then(|p| p.flip(false)),
+        8 => pix.and_then(|p| p.rotate_simple(gdk_pixbuf::PixbufRotation::Counterclockwise)),
+        _ => None,
+    }
+}
+
+// XXX bindgen
+#[repr(i32)]
+#[allow(dead_code)]
+enum ORDataType {
+    None = 0,
+    Pixmap8RGB,
+    Pixmap16RGB,
+    JPEG,
+    TIFF,
+    PNG,
+    RAW,
+    CompressedRAW,
+    Unknown,
+}
+
+#[repr(C)]
+struct ORThumbnail {
+    a: i32,
+}
+#[repr(C)]
+struct ORRawFile {
+    a: i32,
+}
+
+// XXX bindgen these too
+extern "C" {
+    fn or_rawfile_new(path: *const c_char, type_: i32) -> *mut ORRawFile;
+    fn or_rawfile_get_orientation(rawfile: *const ORRawFile) -> i32;
+    fn or_rawfile_get_thumbnail(
+        rawfile: *const ORRawFile,
+        size: u32,
+        thumbnail: *mut ORThumbnail,
+    ) -> i32;
+    fn or_rawfile_release(rawfile: *mut ORRawFile) -> i32;
+    fn or_thumbnail_new() -> *mut ORThumbnail;
+    fn or_thumbnail_release(thumbnail: *mut ORThumbnail) -> i32;
+    fn or_thumbnail_format(thumbnail: *const ORThumbnail) -> ORDataType;
+    fn or_thumbnail_data(thumbnail: *const ORThumbnail) -> *const u8;
+    fn or_thumbnail_data_size(thumbnail: *const ORThumbnail) -> usize;
+    fn or_thumbnail_dimensions(thumbnail: *const ORThumbnail, x: *mut u32, y: *mut u32);
+}
+
+fn thumbnail_to_pixbuf(
+    thumbnail: *const ORThumbnail,
+    orientation: i32,
+) -> Option<gdk_pixbuf::Pixbuf> {
+    let format = unsafe { or_thumbnail_format(thumbnail) };
+    let buf_size = unsafe { or_thumbnail_data_size(thumbnail) };
+    let buf = unsafe { slice::from_raw_parts(or_thumbnail_data(thumbnail), buf_size) };
+
+    let pixbuf = match format {
+        ORDataType::Pixmap8RGB => {
+            let mut x: u32 = 0;
+            let mut y: u32 = 0;
+            unsafe { or_thumbnail_dimensions(thumbnail, &mut x, &mut y) };
+
+            let bytes = glib::Bytes::from(buf);
+            Some(gdk_pixbuf::Pixbuf::new_from_bytes(
+                &bytes,
+                gdk_pixbuf::Colorspace::Rgb,
+                false,
+                8,
+                x as i32,
+                y as i32,
+                x as i32 * 3,
+            ))
+        }
+        ORDataType::JPEG | ORDataType::TIFF | ORDataType::PNG => {
+            let loader = gdk_pixbuf::PixbufLoader::new();
+
+            if let Err(err) = loader.write(buf) {
+                err_out!("loader write error: {}", err);
+            }
+
+            if let Err(err) = loader.close() {
+                err_out!("loader close error: {}", err);
+            }
+            loader.get_pixbuf()
+        }
+        _ => None,
+    };
+
+    gdkpixbuf_exif_rotate(pixbuf.as_ref(), orientation)
+}
+
+/// Extract the thumbnail from libopenraw
+pub fn openraw_extract_rotated_thumbnail<P: AsRef<Path>>(
+    path: P,
+    dim: u32,
+) -> Option<gdk_pixbuf::Pixbuf> {
+    let mut pixbuf = None;
+    let rf = unsafe { or_rawfile_new(path.as_ref().to_glib_none().0, 0) };
+    if !rf.is_null() {
+        let orientation = unsafe { or_rawfile_get_orientation(rf) };
+
+        let thumbnail = unsafe { or_thumbnail_new() };
+        let err = unsafe { or_rawfile_get_thumbnail(rf, dim, thumbnail) };
+        if err == 0 {
+            pixbuf = thumbnail_to_pixbuf(thumbnail, orientation);
+        } else {
+            err_out!("or_get_extract_thumbnail() failed with {}.", err);
+        }
+        let err = unsafe { or_thumbnail_release(thumbnail) };
+        if err != 0 {
+            err_out!("or_thumbnail_release() failed with {}", err);
+        }
+        unsafe { or_rawfile_release(rf) };
+    }
+
+    pixbuf
+}
diff --git a/crates/npc-fwk/src/toolkit/mimetype.rs b/crates/npc-fwk/src/toolkit/mimetype.rs
index 4d47033..c03b187 100644
--- a/crates/npc-fwk/src/toolkit/mimetype.rs
+++ b/crates/npc-fwk/src/toolkit/mimetype.rs
@@ -20,8 +20,8 @@
 use gio;
 use gio::prelude::*;
 
-use std::path::Path;
 use std::convert::AsRef;
+use std::path::Path;
 
 #[derive(PartialEq, Copy, Clone, Debug)]
 pub enum IsRaw {
@@ -56,8 +56,7 @@ pub fn guess_type(gmtype: &str) -> MType {
 }
 
 /// Guess the type from a file
-fn guess_type_for_file<P: AsRef<Path>>(p: P) -> MType
-{
+fn guess_type_for_file<P: AsRef<Path>>(p: P) -> MType {
     let path = p.as_ref();
     let file = gio::File::new_for_path(path);
     let cancellable: Option<&gio::Cancellable> = None;
@@ -129,7 +128,7 @@ mod tests {
         assert_eq!(
             guess_type_for_file("/foo/bar/img_0001.cr2"),
             MType::Image(IsRaw::Yes)
-                );
+        );
         assert!(mimetype.is_image());
         assert!(mimetype.is_digicam_raw());
     }
diff --git a/crates/npc-fwk/src/toolkit/mod.rs b/crates/npc-fwk/src/toolkit/mod.rs
index fa78c5d..f3d6331 100644
--- a/crates/npc-fwk/src/toolkit/mod.rs
+++ b/crates/npc-fwk/src/toolkit/mod.rs
@@ -20,6 +20,8 @@
 pub mod clickable_cell_renderer;
 pub mod gdk_utils;
 pub mod mimetype;
+pub mod movieutils;
+pub mod thumbnail;
 pub mod widgets;
 
 pub type Sender<T> = glib::Sender<T>;
diff --git a/src/engine/library/thumbnailnotification.hpp b/crates/npc-fwk/src/toolkit/movieutils.rs
similarity index 53%
rename from src/engine/library/thumbnailnotification.hpp
rename to crates/npc-fwk/src/toolkit/movieutils.rs
index 0877a9a..42a0e8d 100644
--- a/src/engine/library/thumbnailnotification.hpp
+++ b/crates/npc-fwk/src/toolkit/movieutils.rs
@@ -1,7 +1,7 @@
 /*
- * niepce - engine/library/thumbnailnotification.hpp
+ * niepce - npc-fwk/toolkit/movieutils.rs
  *
- * Copyright (C) 2007-2017 Hubert Figuiere
+ * Copyright (C) 2020 Hubert Figuière
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,21 +17,21 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+use std::cmp;
+use std::path::Path;
+use std::process::Command;
 
-#pragma once
-
-#include "fwk/toolkit/thumbnail.hpp"
-
-#include "rust_bindings.hpp"
-
-namespace eng {
-
-struct ThumbnailNotification
+pub fn thumbnail_movie<S, D>(source: S, w: i32, h: i32, dest: D) -> bool
+where
+    S: AsRef<Path> + std::fmt::Debug,
+    D: AsRef<Path>,
 {
-  eng::library_id_t  id;
-  int  width;
-  int  height;
-  fwk::Thumbnail pixmap;
-};
-
+    let status = Command::new("totem-video-thumbnailer")
+        .arg("-s")
+        .arg(format!("{}", cmp::max(w, h)))
+        .arg(source.as_ref().as_os_str())
+        .arg(dest.as_ref().as_os_str())
+        .status()
+        .expect(&format!("Failed to thumbnail {:?}", source));
+    status.success()
 }
diff --git a/crates/npc-fwk/src/toolkit/thumbnail.rs b/crates/npc-fwk/src/toolkit/thumbnail.rs
new file mode 100644
index 0000000..9b2a6af
--- /dev/null
+++ b/crates/npc-fwk/src/toolkit/thumbnail.rs
@@ -0,0 +1,227 @@
+/*
+ * niepce - toolkit/thumbnail.rs
+ *
+ * Copyright (C) 2020 Hubert Figuière
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+use libc::c_char;
+use std::cmp;
+use std::convert::From;
+use std::ffi::CStr;
+use std::fs;
+use std::path::{Path, PathBuf};
+
+use gdk_pixbuf;
+use gdk_pixbuf::Colorspace;
+use glib;
+use glib::translate::*;
+
+use super::gdk_utils;
+use super::mimetype::MimeType;
+use super::movieutils;
+
+#[derive(Clone)]
+pub struct Thumbnail {
+    bytes: Vec<u8>,
+    width: i32,
+    height: i32,
+    stride: i32,
+    bits_per_sample: i32,
+    has_alpha: bool,
+    colorspace: Colorspace,
+}
+
+impl Default for Thumbnail {
+    fn default() -> Self {
+        Self {
+            bytes: vec![],
+            width: 0,
+            height: 0,
+            stride: 0,
+            bits_per_sample: 0,
+            has_alpha: false,
+            colorspace: Colorspace::Rgb,
+        }
+    }
+}
+
+impl Thumbnail {
+    /// Return true if there is a pixbuf
+    pub fn ok(&self) -> bool {
+        self.bytes.len() > 0
+    }
+
+    /// Get the width of the pixbuf. 0 if None
+    pub fn get_width(&self) -> i32 {
+        self.width
+    }
+
+    /// Get the height of the pixbuf. 0 if None
+    pub fn get_height(&self) -> i32 {
+        self.height
+    }
+
+    /// Make a gdk_pixbuf::Pixbuf out of the Thumbnail
+    fn make_pixbuf(&self) -> Option<gdk_pixbuf::Pixbuf> {
+        if self.ok() {
+            // XXX figure out the allocation here.
+            // Ideally we should have this shared
+            // Also this is duplicated with Into()
+            Some(gdk_pixbuf::Pixbuf::new_from_bytes(
+                &glib::Bytes::from(&self.bytes),
+                self.colorspace,
+                self.has_alpha,
+                self.bits_per_sample,
+                self.width,
+                self.height,
+                self.stride,
+            ))
+        } else {
+            None
+        }
+    }
+
+    pub fn save<P: AsRef<Path> + std::fmt::Debug>(&self, path: P, format: &str) {
+        if let Some(pixbuf) = &self.make_pixbuf() {
+            if let Err(err) = pixbuf.savev(&path, format, &[]) {
+                err_out!("Failed to save pixbuf {:?}: {}", &path, err);
+            }
+        }
+    }
+
+    pub fn thumbnail_file<P: AsRef<Path>>(path: P, w: i32, h: i32, orientation: i32) -> Self {
+        let filename = path.as_ref();
+        let mime_type = MimeType::new(filename);
+        dbg_out!("MIME type {:?}", mime_type);
+
+        let mut pix: Option<gdk_pixbuf::Pixbuf> = None;
+
+        if mime_type.is_unknown() {
+            dbg_out!("unknown file type {:?}", filename);
+        } else if mime_type.is_movie() {
+            // XXX FIXME
+            dbg_out!("video thumbnail");
+            if let Some(mut cached) = glib::get_tmp_dir() {
+                cached.push("temp-1234");
+                if movieutils::thumbnail_movie(filename, w, h, &cached) {
+                    pix = gdk_pixbuf::Pixbuf::new_from_file_at_size(&cached, w, h).ok();
+                    if let Err(err) = fs::remove_file(&cached) {
+                        err_out!("Remove temporary file {:?} failed: {}", &cached, err);
+                    }
+                }
+            }
+            if pix.is_none() {
+                err_out!("exception thumbnailing video ");
+            }
+        } else if !mime_type.is_image() {
+            dbg_out!("not an image type");
+        } else if !mime_type.is_digicam_raw() {
+            dbg_out!("not a raw type, trying GdkPixbuf loaders");
+            match gdk_pixbuf::Pixbuf::new_from_file_at_size(filename, w, h) {
+                Ok(ref pixbuf) => {
+                    pix = gdk_utils::gdkpixbuf_exif_rotate(Some(pixbuf), orientation);
+                }
+                Err(err) => err_out!("exception thumbnailing image: {}", err),
+            }
+        } else {
+            dbg_out!("trying raw loader");
+            pix = gdk_utils::openraw_extract_rotated_thumbnail(filename, cmp::min(w, h) as u32);
+            if let Some(ref pixbuf) = pix {
+                if (w < pixbuf.get_width()) || (h < pixbuf.get_height()) {
+                    pix = gdk_utils::gdkpixbuf_scale_to_fit(Some(pixbuf), cmp::min(w, h));
+                }
+            } else {
+                err_out!("raw loader failed");
+            }
+        }
+
+        Thumbnail::from(pix)
+    }
+}
+
+impl From<Option<gdk_pixbuf::Pixbuf>> for Thumbnail {
+    fn from(pixbuf: Option<gdk_pixbuf::Pixbuf>) -> Self {
+        if let Some(pixbuf) = pixbuf {
+            if let Some(bytes) = pixbuf.read_pixel_bytes() {
+                let width = pixbuf.get_width();
+                let height = pixbuf.get_height();
+                let stride = pixbuf.get_rowstride();
+                let bits_per_sample = pixbuf.get_bits_per_sample();
+                let colorspace = pixbuf.get_colorspace();
+                let has_alpha = pixbuf.get_has_alpha();
+                return Self {
+                    width,
+                    height,
+                    stride,
+                    bits_per_sample,
+                    colorspace,
+                    has_alpha,
+                    bytes: Vec::from(bytes.as_ref()),
+                };
+            }
+        }
+        Self::default()
+    }
+}
+
+impl Into<gdk_pixbuf::Pixbuf> for Thumbnail {
+    fn into(self) -> gdk_pixbuf::Pixbuf {
+        gdk_pixbuf::Pixbuf::new_from_bytes(
+            &glib::Bytes::from(&self.bytes),
+            self.colorspace,
+            self.has_alpha,
+            self.bits_per_sample,
+            self.width,
+            self.height,
+            self.stride,
+        )
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fwk_toolkit_thumbnail_file(
+    filename: *const c_char,
+    w: i32,
+    h: i32,
+    orientation: i32,
+) -> *mut Thumbnail {
+    Box::into_raw(Box::new(Thumbnail::thumbnail_file(
+        &PathBuf::from(&*CStr::from_ptr(filename).to_string_lossy()),
+        w,
+        h,
+        orientation,
+    )))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fwk_toolkit_thumbnail_delete(obj: *mut Thumbnail) {
+    Box::from_raw(obj);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fwk_toolkit_thumbnail_from_pixbuf(
+    pixbuf: *mut gdk_pixbuf_sys::GdkPixbuf,
+) -> *mut Thumbnail {
+    let pixbuf: Option<gdk_pixbuf::Pixbuf> = from_glib_none(pixbuf);
+    Box::into_raw(Box::new(Thumbnail::from(pixbuf)))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fwk_toolkit_thumbnail_to_pixbuf(
+    self_: &Thumbnail,
+) -> *mut gdk_pixbuf_sys::GdkPixbuf {
+    self_.make_pixbuf().to_glib_full()
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index 04e156a..8a99b80 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,6 +24,7 @@ RUST_SOURCES = \
        @top_srcdir@/crates/npc-engine/src/library/notification.rs \
        @top_srcdir@/crates/npc-engine/src/library/queriedcontent.rs \
        @top_srcdir@/crates/npc-engine/src/library/op.rs \
+       @top_srcdir@/crates/npc-engine/src/library/thumbnail_cache.rs \
        @top_srcdir@/crates/npc-engine/src/lib.rs \
        @top_srcdir@/crates/npc-fwk/Cargo.toml \
        @top_srcdir@/crates/npc-fwk/build.rs \
@@ -39,6 +40,7 @@ RUST_SOURCES = \
        @top_srcdir@/crates/npc-fwk/src/toolkit/gdk_utils.rs \
        @top_srcdir@/crates/npc-fwk/src/toolkit/mimetype.rs \
        @top_srcdir@/crates/npc-fwk/src/toolkit/mod.rs \
+       @top_srcdir@/crates/npc-fwk/src/toolkit/thumbnail.rs \
        @top_srcdir@/crates/npc-fwk/src/toolkit/widgets/mod.rs \
        @top_srcdir@/crates/npc-fwk/src/toolkit/widgets/rating_label.rs \
        @top_srcdir@/crates/npc-fwk/src/utils/exempi.rs \
diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am
index fbbd293..98a3c00 100644
--- a/src/engine/Makefile.am
+++ b/src/engine/Makefile.am
@@ -28,7 +28,6 @@ libniepceengine_a_SOURCES = \
        db/properties.hpp db/properties.cpp \
        db/properties-def.hpp \
        library/clienttypes.hpp \
-       library/thumbnailcache.hpp library/thumbnailcache.cpp \
        library/thumbnailnotification.hpp \
        importer/iimporter.hpp \
        importer/directoryimporter.hpp \
diff --git a/src/engine/importer/cameraimporter.cpp b/src/engine/importer/cameraimporter.cpp
index 2dfefe4..e17e855 100644
--- a/src/engine/importer/cameraimporter.cpp
+++ b/src/engine/importer/cameraimporter.cpp
@@ -2,7 +2,7 @@
 /*
  * niepce - engine/importer/cameraimporter.cpp
  *
- * Copyright (C) 2017 Hubert Figuière
+ * Copyright (C) 2017-2020 Hubert Figuière
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -94,7 +94,7 @@ bool CameraImporter::get_previews_for(const std::string& source,
     if (ensure_camera_open(source)) {
         for (auto path: paths) {
             DBG_OUT("want thumbnail %s", path.c_str());
-            fwk::Thumbnail thumbnail = m_camera->get_preview(path);
+            fwk::ThumbnailPtr thumbnail = m_camera->get_preview(path);
             callback(path, thumbnail);
         }
 
diff --git a/src/engine/importer/directoryimporter.cpp b/src/engine/importer/directoryimporter.cpp
index 2a031ba..cf0e628 100644
--- a/src/engine/importer/directoryimporter.cpp
+++ b/src/engine/importer/directoryimporter.cpp
@@ -84,7 +84,8 @@ bool DirectoryImporter::get_previews_for(const std::string& /*source*/,
 {
     for (auto path : paths) {
         DBG_OUT("path %s", path.c_str());
-        auto thumbnail = fwk::Thumbnail::thumbnail_file(path, 160, 160, 0);
+        auto thumbnail =
+            fwk::thumbnail_wrap(ffi::fwk_toolkit_thumbnail_file(path.c_str(), 160, 160, 0));
         callback(path, thumbnail);
     }
     return true;
diff --git a/src/engine/importer/iimporter.hpp b/src/engine/importer/iimporter.hpp
index 5db673f..e2236d1 100644
--- a/src/engine/importer/iimporter.hpp
+++ b/src/engine/importer/iimporter.hpp
@@ -1,7 +1,7 @@
 /*
  * niepce - engine/importer/iimporter.hpp
  *
- * Copyright (C) 2014-2017 Hubert Figuière
+ * Copyright (C) 2014-2020 Hubert Figuière
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -28,8 +28,6 @@
 #include "fwk/utils/files.hpp"
 #include "engine/importer/importedfile.hpp"
 
-#include "rust_bindings.hpp"
-
 namespace eng {
 
 /**
@@ -49,7 +47,7 @@ public:
                                      const SourceContentReady& callback) = 0;
 
     typedef std::function<void (const std::string& path,
-                                const fwk::Thumbnail&)> PreviewReady;
+                                const fwk::ThumbnailPtr&)> PreviewReady;
     virtual bool get_previews_for(const std::string& source,
                                   const std::list<std::string>& paths,
                                   const PreviewReady& callback) = 0;
diff --git a/src/fwk/toolkit/Makefile.am b/src/fwk/toolkit/Makefile.am
index b7b135f..9a07269 100644
--- a/src/fwk/toolkit/Makefile.am
+++ b/src/fwk/toolkit/Makefile.am
@@ -41,7 +41,6 @@ libniepceframework_a_SOURCES = configuration.hpp configuration.cpp \
        mapcontroller.hpp mapcontroller.cpp \
        notification.hpp \
        mimetype.hpp mimetype.cpp \
-       movieutils.hpp movieutils.cpp \
        imageloader.hpp imageloader.cpp \
        notificationcenter.hpp notificationcenter.cpp \
        configdatabinder.hpp configdatabinder.cpp \
diff --git a/src/fwk/toolkit/gphoto.cpp b/src/fwk/toolkit/gphoto.cpp
index 67ff051..c2b7eea 100644
--- a/src/fwk/toolkit/gphoto.cpp
+++ b/src/fwk/toolkit/gphoto.cpp
@@ -39,7 +39,6 @@
 #include "fwk/utils/files.hpp"
 #include "fwk/utils/pathutils.hpp"
 #include "fwk/toolkit/gdkutils.hpp"
-#include "fwk/toolkit/thumbnail.hpp"
 #include "gphoto.hpp"
 
 
@@ -360,9 +359,9 @@ std::list<std::pair<std::string, std::string>> GpCamera::list_content() const
     return files;
 }
 
-fwk::Thumbnail GpCamera::get_preview(const std::string& path) const
+fwk::ThumbnailPtr GpCamera::get_preview(const std::string& path) const
 {
-    fwk::Thumbnail thumbnail;
+    fwk::ThumbnailPtr thumbnail;
     std::string folder = fwk::path_dirname(path);
     std::string name = fwk::path_basename(path);
 
@@ -407,7 +406,7 @@ fwk::Thumbnail GpCamera::get_preview(const std::string& path) const
             unlink(exif_path.c_str());
         }
 #endif
-        thumbnail = fwk::Thumbnail(pix);
+        thumbnail = fwk::thumbnail_wrap(ffi::fwk_toolkit_thumbnail_from_pixbuf(pix->gobj()));
     }
 
     return thumbnail;
diff --git a/src/fwk/toolkit/gphoto.hpp b/src/fwk/toolkit/gphoto.hpp
index 615cae0..b10a7b5 100644
--- a/src/fwk/toolkit/gphoto.hpp
+++ b/src/fwk/toolkit/gphoto.hpp
@@ -31,11 +31,10 @@
 #include "fwk/base/option.hpp"
 #include "fwk/base/singleton.hpp"
 #include "fwk/base/util.hpp"
+#include "fwk/toolkit/thumbnail.hpp"
 
 namespace fwk {
 
-class Thumbnail;
-
 namespace gp {
 typedef std::unique_ptr<CameraFile, decltype(&gp_file_unref)> CameraFilePtr;
 
@@ -111,7 +110,7 @@ public:
     bool close();
     bool try_unmount_camera();
     std::list<std::pair<std::string, std::string>> list_content() const;
-    fwk::Thumbnail get_preview(const std::string& path) const;
+    fwk::ThumbnailPtr get_preview(const std::string& path) const;
     bool download_file(const std::string& folder, const std::string& file,
                        const std::string& dest);
 private:
diff --git a/src/fwk/toolkit/notificationcenter.cpp b/src/fwk/toolkit/notificationcenter.cpp
index e495c3e..329175c 100644
--- a/src/fwk/toolkit/notificationcenter.cpp
+++ b/src/fwk/toolkit/notificationcenter.cpp
@@ -31,47 +31,23 @@ namespace fwk {
 class NotificationCenter::Priv
 {
 public:
-    uint64_t m_id;
     Glib::Dispatcher                     m_dispatcher;
     fwk::MtQueue< Notification::Ptr >    m_notificationQueue;
     std::map< int, subscription_t >      m_subscribers;
 };
 
-typedef fwk::Singleton<std::map<uint64_t, std::weak_ptr<NotificationCenter>>> NotificationCenterRegistry;
-
-std::weak_ptr<NotificationCenter> NotificationCenter::get_nc(uint64_t notif_id)
-{
-    auto iter = NotificationCenterRegistry::obj().find(notif_id);
-    if (iter == NotificationCenterRegistry::obj().end()) {
-        return NotificationCenter::Ptr();
-    }
-    return iter->second;
-}
-
-NotificationCenter::NotificationCenter(uint64_t notif_id)
+NotificationCenter::NotificationCenter()
     : p( new Priv )
 {
-    p->m_id = notif_id;
     p->m_dispatcher.connect(
         sigc::mem_fun(this, &NotificationCenter::_dispatch));
 }
 
 NotificationCenter::~NotificationCenter()
 {
-    NotificationCenterRegistry::obj().erase(p->m_id);
     delete p;
 }
 
-void NotificationCenter::attach()
-{
-    NotificationCenterRegistry::obj()[p->m_id] = shared_from_this();
-}
-
-uint64_t NotificationCenter::id() const
-{
-    return p->m_id;
-}
-
 void NotificationCenter::subscribe(int type, const subscriber_t & s)
 {
     // TODO make sure it is not yet subscribed
diff --git a/src/fwk/toolkit/notificationcenter.hpp b/src/fwk/toolkit/notificationcenter.hpp
index f75fd2f..bf259aa 100644
--- a/src/fwk/toolkit/notificationcenter.hpp
+++ b/src/fwk/toolkit/notificationcenter.hpp
@@ -39,19 +39,14 @@ public:
 
     ~NotificationCenter();
 
-    uint64_t id() const;
-
     // called from out of thread
     void post(Notification::Ptr && n);
 
     void subscribe(int type, const subscriber_t & );
     void unsubscribe(int type, const subscriber_t & );
 
-    static std::weak_ptr<NotificationCenter> get_nc(uint64_t notif_id);
-
 protected:
-    NotificationCenter(uint64_t notif_id);
-    void attach();
+    NotificationCenter();
 
 private:
     typedef sigc::signal<void, Notification::Ptr> subscription_t;
diff --git a/src/fwk/toolkit/thumbnail.cpp b/src/fwk/toolkit/thumbnail.cpp
index 8ff1f0d..0a7f63f 100644
--- a/src/fwk/toolkit/thumbnail.cpp
+++ b/src/fwk/toolkit/thumbnail.cpp
@@ -2,7 +2,7 @@
 /*
  * niepce - fwk/toolkit/thumbnail.cpp
  *
- * Copyright (C) 2017-2019 Hubert Figuière
+ * Copyright (C) 2017-2020 Hubert Figuière
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,224 +20,11 @@
 
 #include "thumbnail.hpp"
 
-#include <glibmm/miscutils.h>
-#include <libopenraw/libopenraw.h>
-
-#include "fwk/base/debug.hpp"
-#include "fwk/toolkit/mimetype.hpp"
-#include "fwk/toolkit/gdkutils.hpp"
-#include "fwk/toolkit/movieutils.hpp"
-
 namespace fwk {
 
-namespace {
-
-static void pixbuf_free(guchar* data, gpointer u)
-{
-    (void)u;
-    free(data);
-}
-
-/**
- * Returns a retained GdkPixbuf
- */
-static GdkPixbuf* rotate_pixbuf(GdkPixbuf* tmp, int32_t orientation)
-{
-    GdkPixbuf* pixbuf = nullptr;
-    switch (orientation) {
-    case 0:
-    case 1:
-        pixbuf = (GdkPixbuf*)g_object_ref(tmp);
-        break;
-    case 2:
-        pixbuf = gdk_pixbuf_flip(tmp, TRUE);
-        break;
-    case 3:
-        pixbuf = gdk_pixbuf_rotate_simple(tmp, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
-        break;
-    case 4: {
-        GdkPixbuf* rotated =
-            gdk_pixbuf_rotate_simple(tmp, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
-        pixbuf = gdk_pixbuf_flip(rotated, TRUE);
-        g_object_unref(rotated);
-        break;
-    }
-    case 5: {
-        GdkPixbuf* rotated =
-            gdk_pixbuf_rotate_simple(tmp, GDK_PIXBUF_ROTATE_CLOCKWISE);
-        pixbuf = gdk_pixbuf_flip(rotated, FALSE);
-        g_object_unref(rotated);
-        break;
-    }
-    case 6:
-        pixbuf = gdk_pixbuf_rotate_simple(tmp, GDK_PIXBUF_ROTATE_CLOCKWISE);
-        break;
-    case 7: {
-        GdkPixbuf* rotated =
-            gdk_pixbuf_rotate_simple(tmp, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
-        pixbuf = gdk_pixbuf_flip(rotated, FALSE);
-        g_object_unref(rotated);
-        break;
-    }
-    case 8:
-        pixbuf =
-            gdk_pixbuf_rotate_simple(tmp, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
-        break;
-    default:
-        break;
-    }
-    return pixbuf;
-}
-
-static GdkPixbuf* thumbnail_to_pixbuf(ORThumbnailRef thumbnail,
-                                      int32_t orientation)
-{
-    GdkPixbuf* tmp = nullptr;
-    GdkPixbuf* pixbuf = nullptr;
-
-    const guchar* buf;
-    or_data_type format = or_thumbnail_format(thumbnail);
-    buf = (const guchar*)or_thumbnail_data(thumbnail);
-
-    switch (format) {
-    case OR_DATA_TYPE_PIXMAP_8RGB: {
-        uint32_t x, y;
-        size_t buf_size;
-        guchar* data;
-
-        buf_size = or_thumbnail_data_size(thumbnail);
-        data = (guchar*)malloc(buf_size);
-        memcpy(data, buf, buf_size);
-        or_thumbnail_dimensions(thumbnail, &x, &y);
-
-        tmp = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, FALSE, 8, x, y,
-                                       x * 3, pixbuf_free, nullptr);
-        break;
-    }
-    case OR_DATA_TYPE_JPEG:
-    case OR_DATA_TYPE_TIFF:
-    case OR_DATA_TYPE_PNG: {
-        GdkPixbufLoader* loader = nullptr;
-        size_t count = or_thumbnail_data_size(thumbnail);
-        loader = gdk_pixbuf_loader_new();
-        if (loader != nullptr) {
-            GError* error = nullptr;
-            if (!gdk_pixbuf_loader_write(loader, buf, count, &error) && error) {
-                ERR_OUT("loader write error: %s", error->message);
-                g_error_free(error);
-                error = nullptr;
-            }
-            if (!gdk_pixbuf_loader_close(loader, &error) && error) {
-                ERR_OUT("loader close error: %s", error->message);
-                g_error_free(error);
-            }
-            tmp = gdk_pixbuf_loader_get_pixbuf(loader);
-            g_object_ref(tmp);
-            g_object_unref(loader);
-        }
-        break;
-    }
-    default:
-        break;
-    }
-    pixbuf = rotate_pixbuf(tmp, orientation);
-    g_object_unref(tmp);
-    return pixbuf;
-}
-
-static GdkPixbuf* gdkpixbuf_extract_rotated_thumbnail(const char* path,
-                                                      uint32_t preferred_size)
+ThumbnailPtr thumbnail_wrap(ffi::Thumbnail* t)
 {
-    ORRawFileRef rf;
-    int32_t orientation = 0;
-    GdkPixbuf* pixbuf = nullptr;
-    or_error err = OR_ERROR_NONE;
-    ORThumbnailRef thumbnail = nullptr;
-    gboolean rotate = TRUE;
-
-    rf = or_rawfile_new(path, OR_RAWFILE_TYPE_UNKNOWN);
-    if (rf) {
-        if (rotate) {
-            orientation = or_rawfile_get_orientation(rf);
-        }
-        thumbnail = or_thumbnail_new();
-        err = or_rawfile_get_thumbnail(rf, preferred_size, thumbnail);
-        if (err == OR_ERROR_NONE) {
-            pixbuf = thumbnail_to_pixbuf(thumbnail, orientation);
-        } else {
-            ERR_OUT("or_get_extract_thumbnail() failed with %d.", err);
-        }
-        err = or_thumbnail_release(thumbnail);
-        if (err != OR_ERROR_NONE) {
-            ERR_OUT("or_thumbnail_release() failed with %d", err);
-        }
-        or_rawfile_release(rf);
-    }
-
-    return pixbuf;
-}
-
-}
-
-Thumbnail::Thumbnail(const Glib::RefPtr<Gdk::Pixbuf>& pixbuf)
-    : m_pixbuf(pixbuf)
-{
-}
-
-void Thumbnail::save(const std::string& path, const std::string& format)
-{
-    if (m_pixbuf) {
-        m_pixbuf->save(path, format);
-    }
-}
-
-Thumbnail Thumbnail::thumbnail_file(const std::string& filename,
-                                    int w, int h, int32_t orientation)
-{
-    fwk::MimeType mime_type(filename);
-    DBG_OUT("MIME type %s", mime_type.string().c_str());
-
-    Glib::RefPtr<Gdk::Pixbuf> pix;
-
-    if(mime_type.isUnknown()) {
-        DBG_OUT("unknown file type %s", filename.c_str());
-    } else if(mime_type.isMovie()) {
-        try {
-            // XXX FIXME
-            std::string cached = Glib::get_tmp_dir() + "/temp-1234";
-            if(fwk::thumbnail_movie(filename, w, h, cached)) {
-                pix = Gdk::Pixbuf::create_from_file(cached, w, h, true);
-                unlink(cached.c_str());
-            }
-        }
-        catch(const Glib::Error & e) {
-            ERR_OUT("exception thumbnailing video %s", e.what().c_str());
-        }
-    } else if(!mime_type.isImage()) {
-        DBG_OUT("not an image type");
-    } else if(!mime_type.isDigicamRaw()) {
-        DBG_OUT("not a raw type, trying GdkPixbuf loaders");
-        try {
-            pix = Gdk::Pixbuf::create_from_file(filename, w, h, true);
-            if(pix) {
-                pix = fwk::gdkpixbuf_exif_rotate(pix, orientation);
-            }
-        }
-        catch(const Glib::Error & e) {
-            ERR_OUT("exception thumbnailing image %s", e.what().c_str());
-        }
-    } else {
-        GdkPixbuf *pixbuf = gdkpixbuf_extract_rotated_thumbnail(filename.c_str(),
-                                                                std::min(w, h));
-        if(pixbuf) {
-            pix = Glib::wrap(pixbuf, true); // take ownership
-            if((w < pix->get_width()) || (h < pix->get_height())) {
-                pix = fwk::gdkpixbuf_scale_to_fit(pix, std::min(w,h));
-            }
-        }
-    }
-
-    return Thumbnail(pix);
+    return ThumbnailPtr(t, &ffi::fwk_toolkit_thumbnail_delete);
 }
 
 }
diff --git a/src/fwk/toolkit/thumbnail.hpp b/src/fwk/toolkit/thumbnail.hpp
index dc6c384..bb576f6 100644
--- a/src/fwk/toolkit/thumbnail.hpp
+++ b/src/fwk/toolkit/thumbnail.hpp
@@ -20,39 +20,13 @@
 
 #pragma once
 
-#include <stdint.h>
-
-#include <string>
-
-#include <gdkmm/pixbuf.h>
+#include <memory>
+#include "rust_bindings.hpp"
 
 namespace fwk {
 
-class Thumbnail {
-public:
-    Thumbnail() = default;
-    Thumbnail(const Glib::RefPtr<Gdk::Pixbuf>& pixbuf);
-
-    bool ok() const {
-        return !!m_pixbuf;
-    }
-
-    int get_width() const {
-        return m_pixbuf->get_width();
-    }
-    int get_height() const {
-        return m_pixbuf->get_height();
-    }
-    const Glib::RefPtr<Gdk::Pixbuf>& pixbuf() const {
-        return m_pixbuf;
-    }
-
-    void save(const std::string& path, const std::string& format);
-
-    static Thumbnail thumbnail_file(const std::string& path, int w, int h, int32_t orientation);
+typedef std::shared_ptr<ffi::Thumbnail> ThumbnailPtr;
 
-private:
-    Glib::RefPtr<Gdk::Pixbuf> m_pixbuf;
-};
+ThumbnailPtr thumbnail_wrap(ffi::Thumbnail *);
 
 }
diff --git a/src/libraryclient/libraryclient.cpp b/src/libraryclient/libraryclient.cpp
index 0f8b9d9..67018c2 100644
--- a/src/libraryclient/libraryclient.cpp
+++ b/src/libraryclient/libraryclient.cpp
@@ -1,7 +1,7 @@
 /*
  * niepce - libraryclient/libraryclient.cpp
  *
- * Copyright (C) 2007-2019 Hubert Figuière
+ * Copyright (C) 2007-2020 Hubert Figuière
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -32,17 +32,20 @@ namespace libraryclient {
 const char * s_thumbcacheDirname = "thumbcache";
 
 LibraryClient::LibraryClient(const fwk::Moniker & moniker,
-                             const std::shared_ptr<ffi::LcChannel>& channel, uint64_t notif_id)
+                             const std::shared_ptr<ffi::LcChannel>& channel)
     : m_client(
         ffi::libraryclient_new(moniker.path().c_str(), channel.get()),
         ffi::libraryclient_delete)
-    , m_thumbnailCache(moniker.path() + "/" + s_thumbcacheDirname, notif_id)
+    , m_thumbnailCache(ffi::engine_library_thumbnail_cache_new(
+                           std::string(moniker.path() + "/" + s_thumbcacheDirname).c_str(),
+                           channel.get()))
     , m_uidataprovider(new UIDataProvider())
 {
 }
 
 LibraryClient::~LibraryClient()
 {
+    ffi::engine_library_thumbnail_cache_delete(m_thumbnailCache);
 }
 
 }
diff --git a/src/libraryclient/libraryclient.hpp b/src/libraryclient/libraryclient.hpp
index 6a6b2df..a169530 100644
--- a/src/libraryclient/libraryclient.hpp
+++ b/src/libraryclient/libraryclient.hpp
@@ -22,7 +22,6 @@
 #include <string>
 #include <memory>
 
-#include "engine/library/thumbnailcache.hpp"
 #include "uidataprovider.hpp"
 
 #include "rust_bindings.hpp"
@@ -39,11 +38,10 @@ public:
     LibraryClient() = delete;
     LibraryClient& operator=(const LibraryClient&) = delete;
 
-    LibraryClient(const fwk::Moniker & moniker, const std::shared_ptr<ffi::LcChannel>& channel,
-                  uint64_t notif_id);
+    LibraryClient(const fwk::Moniker & moniker, const std::shared_ptr<ffi::LcChannel>& channel);
     virtual ~LibraryClient();
 
-    eng::ThumbnailCache & thumbnailCache()
+    ffi::ThumbnailCache* thumbnailCache()
         { return m_thumbnailCache; }
 
     const UIDataProviderPtr& getDataProvider() const
@@ -55,7 +53,7 @@ public:
 private:
     std::shared_ptr<ffi::LibraryClientWrapper> m_client;
 
-    eng::ThumbnailCache m_thumbnailCache;
+    ffi::ThumbnailCache* m_thumbnailCache;
     UIDataProviderPtr m_uidataprovider;
 };
 
diff --git a/src/niepce/Makefile.am b/src/niepce/Makefile.am
index 90a476a..8cda28a 100644
--- a/src/niepce/Makefile.am
+++ b/src/niepce/Makefile.am
@@ -48,7 +48,6 @@ BUILT_SOURCES = \
 
 niepce_SOURCES = \
        notificationcenter.hpp notificationcenter.cpp \
-        notifications.hpp \
         npc-resources.c \
         npc-resources.h \
        main.cpp
diff --git a/src/niepce/notificationcenter.cpp b/src/niepce/notificationcenter.cpp
index c1fcee4..c1b375d 100644
--- a/src/niepce/notificationcenter.cpp
+++ b/src/niepce/notificationcenter.cpp
@@ -20,7 +20,6 @@
 #include <boost/any.hpp>
 
 #include "fwk/base/debug.hpp"
-#include "niepce/notifications.hpp"
 #include "niepce/notificationcenter.hpp"
 
 namespace niepce {
@@ -36,12 +35,9 @@ int32_t NotificationCenter::channel_callback(const eng::LibNotification *notific
     return 1;
 }
 
-NotificationCenter::NotificationCenter(uint64_t notif_id)
-    : fwk::NotificationCenter(notif_id)
-    , m_channel(ffi::lcchannel_new(&channel_callback, this), ffi::lcchannel_delete)
+NotificationCenter::NotificationCenter()
+    : m_channel(ffi::lcchannel_new(&channel_callback, this), ffi::lcchannel_delete)
 {
-    subscribe(NOTIFICATION_THUMBNAIL,
-              sigc::mem_fun(*this, &NotificationCenter::dispatch_notification));
 }
 
 NotificationCenter::~NotificationCenter()
@@ -54,38 +50,6 @@ void NotificationCenter::dispatch_lib_notification(const eng::LibNotification *n
     signal_lib_notification(*n);
 }
 
-void NotificationCenter::dispatch_notification(const fwk::Notification::Ptr &n) const
-{
-    try {
-        switch(n->type()) {
-        case NOTIFICATION_THUMBNAIL:
-        {
-            eng::ThumbnailNotification tn
-                = boost::any_cast<eng::ThumbnailNotification>(n->data());
-            signal_thumbnail_notification(tn);
-            break;
-        }
-        default:
-            ERR_OUT("Unhandled notification type %d", n->type());
-            break;
-        }
-    }
-    catch(const boost::bad_any_cast & e)
-    {
-        ERR_OUT("improper notification data: %s", e.what());
-    }
-    catch(const std::exception & e)
-    {
-        ERR_OUT("other exception notification type %d of %s: %s", n->type(),
-                n->data().type().name(),
-                e.what());
-    }
-    catch(...)
-    {
-        ERR_OUT("unknown exception");
-    }
-}
-
 }
 /*
   Local Variables:
diff --git a/src/niepce/notificationcenter.hpp b/src/niepce/notificationcenter.hpp
index 69ffa19..f96f807 100644
--- a/src/niepce/notificationcenter.hpp
+++ b/src/niepce/notificationcenter.hpp
@@ -23,7 +23,8 @@
 #include <sigc++/signal.h>
 
 #include "fwk/toolkit/notificationcenter.hpp"
-#include "engine/library/thumbnailnotification.hpp"
+
+#include "rust_bindings.hpp"
 
 namespace niepce {
 
@@ -34,20 +35,18 @@ public:
   ~NotificationCenter();
 
   typedef std::shared_ptr<NotificationCenter> Ptr;
-  static Ptr make(uint64_t notif_id)
+  static Ptr make()
     {
-      Ptr nc = Ptr(new NotificationCenter(notif_id));
-      nc->attach();
+      Ptr nc = Ptr(new NotificationCenter());
       return nc;
     }
 
   sigc::signal<void, const eng::LibNotification &> signal_lib_notification;
-  sigc::signal<void, const eng::ThumbnailNotification &> signal_thumbnail_notification;
 
   const std::shared_ptr<ffi::LcChannel>& get_channel() const
     { return m_channel; };
 protected:
-  NotificationCenter(uint64_t notif_id);
+  NotificationCenter();
 
 private:
   static int32_t channel_callback(const eng::LibNotification *notification, void *data);
diff --git a/src/niepce/ui/dialogs/importdialog.cpp b/src/niepce/ui/dialogs/importdialog.cpp
index 7480a50..354ce30 100644
--- a/src/niepce/ui/dialogs/importdialog.cpp
+++ b/src/niepce/ui/dialogs/importdialog.cpp
@@ -210,7 +210,7 @@ void ImportDialog::append_files_to_import()
         [this, importer, source, paths] () {
             return importer->get_previews_for(
                 source, paths,
-                [this] (const std::string& path, const fwk::Thumbnail& thumbnail) {
+                [this] (const std::string& path, const fwk::ThumbnailPtr& thumbnail) {
                     this->m_previews_to_import.send_data(
                         std::make_pair(path, thumbnail));
                 });
@@ -224,7 +224,9 @@ void ImportDialog::preview_received()
         auto preview = result.unwrap();
         auto iter = m_images_list_map.find(preview.first);
         if (iter != m_images_list_map.end()) {
-            iter->second->set_value(m_grid_columns.pixbuf, preview.second.pixbuf());
+            iter->second->set_value(m_grid_columns.pixbuf,
+                                    Glib::wrap(ffi::fwk_toolkit_thumbnail_to_pixbuf(
+                                                   preview.second.get())));
         }
     }
 }
diff --git a/src/niepce/ui/dialogs/importdialog.hpp b/src/niepce/ui/dialogs/importdialog.hpp
index c66bd79..d4cc2b7 100644
--- a/src/niepce/ui/dialogs/importdialog.hpp
+++ b/src/niepce/ui/dialogs/importdialog.hpp
@@ -46,7 +46,6 @@ class Stack;
 
 namespace fwk {
 class ImageGridView;
-class Thumbnail;
 }
 
 namespace eng {
@@ -120,7 +119,7 @@ private:
     MetaDataPaneController::Ptr m_metadata_pane;
 
     fwk::UIResultSingle<std::list<eng::ImportedFilePtr>> m_files_to_import;
-    fwk::UIResults<std::pair<std::string, fwk::Thumbnail>> m_previews_to_import;
+    fwk::UIResults<std::pair<std::string, fwk::ThumbnailPtr>> m_previews_to_import;
 };
 
 }
diff --git a/src/niepce/ui/filmstripcontroller.cpp b/src/niepce/ui/filmstripcontroller.cpp
index 1d63895..555fff1 100644
--- a/src/niepce/ui/filmstripcontroller.cpp
+++ b/src/niepce/ui/filmstripcontroller.cpp
@@ -20,8 +20,6 @@
 
 #include <gtkmm/iconview.h>
 
-#include "niepce/notifications.hpp"
-#include "engine/library/thumbnailnotification.hpp"
 #include "fwk/base/debug.hpp"
 
 #include "filmstripcontroller.hpp"
diff --git a/src/niepce/ui/imageliststore.cpp b/src/niepce/ui/imageliststore.cpp
index 800b7cc..678a8f6 100644
--- a/src/niepce/ui/imageliststore.cpp
+++ b/src/niepce/ui/imageliststore.cpp
@@ -26,7 +26,6 @@
 #include "fwk/base/debug.hpp"
 #include "fwk/toolkit/application.hpp"
 #include "fwk/toolkit/gdkutils.hpp"
-#include "niepce/notifications.hpp"
 #include "niepcewindow.hpp"
 
 #include "rust_bindings.hpp"
@@ -136,7 +135,7 @@ void ImageListStore::on_lib_notification(const eng::LibNotification &ln)
     case eng::NotificationType::FOLDER_CONTENT_QUERIED:
     case eng::NotificationType::KEYWORD_CONTENT_QUERIED:
     {
-        auto content = engine_library_notification_get_content(&ln);
+        auto content = ffi::engine_library_notification_get_content(&ln);
         if (type == eng::NotificationType::FOLDER_CONTENT_QUERIED) {
             m_current_folder = ffi::engine_queried_content_id(content);
             m_current_keyword = 0;
@@ -153,7 +152,7 @@ void ImageListStore::on_lib_notification(const eng::LibNotification &ln)
             l->push_back(f);
         }
         // at that point clear the cache because the icon view is populated.
-        getLibraryClient()->thumbnailCache().request(*l);
+        ffi::engine_library_thumbnail_cache_request(getLibraryClient()->thumbnailCache(), content);
         break;
     }
     case eng::NotificationType::FILE_MOVED:
@@ -218,27 +217,28 @@ void ImageListStore::on_lib_notification(const eng::LibNotification &ln)
         ffi::libraryclient_process_xmp_update_queue(getLibraryClient()->client(), write_xmp);
         break;
     }
+    case eng::NotificationType::ThumbnailLoaded:
+    {
+        auto id = engine_library_notification_get_id(&ln);
+        auto iter = m_idmap.find(id);
+        if(iter != m_idmap.end()) {
+            // found the icon view item
+            auto pixbuf = engine_library_notification_get_pixbuf(&ln);
+            // About the ownership:
+            // The Glib::wrap below will claim it.
+            ffi::npc_image_list_store_set_tnail(
+                m_store, iter->second.gobj(), pixbuf,
+                fwk::gdkpixbuf_scale_to_fit(Glib::wrap(pixbuf), 100)->gobj());
+        }
+        else {
+            DBG_OUT("row %Ld not found", (long long)id);
+        }
+    }
     default:
         break;
     }
 }
 
-void ImageListStore::on_tnail_notification(const eng::ThumbnailNotification &tn)
-{
-    std::map<eng::library_id_t, Gtk::TreeIter>::iterator iter
-        = m_idmap.find( tn.id );
-    if(iter != m_idmap.end()) {
-        // found the icon view item
-        auto pixbuf = tn.pixmap.pixbuf();
-        ffi::npc_image_list_store_set_tnail(
-            m_store, iter->second.gobj(), pixbuf->gobj(),
-            fwk::gdkpixbuf_scale_to_fit(pixbuf, 100)->gobj());
-    }
-    else {
-        DBG_OUT("row %Ld not found", (long long)tn.id);
-    }
-}
-
 libraryclient::LibraryClientPtr ImageListStore::getLibraryClient()
 {
     ModuleShell::Ptr shell = std::dynamic_pointer_cast<ModuleShell>(m_controller.lock());
diff --git a/src/niepce/ui/imageliststore.hpp b/src/niepce/ui/imageliststore.hpp
index 0276f1f..1c51b87 100644
--- a/src/niepce/ui/imageliststore.hpp
+++ b/src/niepce/ui/imageliststore.hpp
@@ -1,7 +1,7 @@
 /*
  * niepce - ui/imageliststore.h
  *
- * Copyright (C) 2008-2020 Hubert Figuiere
+ * Copyright (C) 2008-2020 Hubert Figuière
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,7 +25,6 @@
 #include "fwk/base/propertybag.hpp"
 #include "fwk/toolkit/controller.hpp"
 #include "engine/db/libfile.hpp"
-#include "engine/library/thumbnailnotification.hpp"
 #include "libraryclient/libraryclient.hpp"
 
 namespace ui {
@@ -74,7 +73,6 @@ public:
     // Should be called when the content will change
     void clear_content();
     void on_lib_notification(const eng::LibNotification &n);
-    void on_tnail_notification(const eng::ThumbnailNotification &n);
 
 private:
     /// Add the LibFile to the model
diff --git a/src/niepce/ui/moduleshell.cpp b/src/niepce/ui/moduleshell.cpp
index a91e6b0..bc4604f 100644
--- a/src/niepce/ui/moduleshell.cpp
+++ b/src/niepce/ui/moduleshell.cpp
@@ -25,7 +25,6 @@
 #include <gtkmm/cellrenderer.h>
 
 #include "fwk/base/debug.hpp"
-#include "niepce/notifications.hpp"
 #include "engine/db/libfile.hpp"
 #include "fwk/toolkit/application.hpp"
 #include "fwk/toolkit/gtkutils.hpp"
diff --git a/src/niepce/ui/niepcewindow.cpp b/src/niepce/ui/niepcewindow.cpp
index 7de3b77..e9ab2b9 100644
--- a/src/niepce/ui/niepcewindow.cpp
+++ b/src/niepce/ui/niepcewindow.cpp
@@ -28,7 +28,6 @@
 #include <gtkmm/separator.h>
 #include <gtkmm/filechooserdialog.h>
 
-#include "niepce/notifications.hpp"
 #include "niepce/notificationcenter.hpp"
 #include "fwk/base/debug.hpp"
 #include "fwk/base/moniker.hpp"
@@ -126,11 +125,6 @@ NiepceWindow::_createModuleShell()
         .connect(sigc::mem_fun(
                      *m_moduleshell->get_map_module(),
                      &mapm::MapModule::on_lib_notification));
-    m_notifcenter->signal_thumbnail_notification
-        .connect(sigc::mem_fun(
-                     m_moduleshell->get_list_store().get(),
-                     &ImageListStore::on_tnail_notification));
-
 
     // workspace treeview
     auto workspace_actions = Gio::SimpleActionGroup::create();
@@ -190,7 +184,7 @@ NiepceWindow::buildWidget()
 
     init_actions();
 
-    m_notifcenter = niepce::NotificationCenter::make(reinterpret_cast<uint64_t>(this));
+    m_notifcenter = niepce::NotificationCenter::make();
 
     Glib::ustring name("org.gnome.Niepce");
     set_icon_from_theme(name);
@@ -376,7 +370,7 @@ bool NiepceWindow::open_library(const std::string & libMoniker)
     fwk::Moniker mon = fwk::Moniker(libMoniker);
     m_libClient
         = LibraryClientPtr(new LibraryClient(
-                               mon, m_notifcenter->get_channel(), m_notifcenter->id()));
+                               mon, m_notifcenter->get_channel()));
     // XXX ensure the library is open.
     set_title(libMoniker);
     m_library_cfg


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]