[niepce] npc-fwk: Port Configuration to Rust



commit 19edfe5071624fc2a480bd2ad4160476d0645028
Author: Hubert Figuière <hub figuiere net>
Date:   Sat Oct 15 12:05:36 2022 -0400

    npc-fwk: Port Configuration to Rust

 .gitignore                                         |   2 +
 Cargo.lock                                         |  46 +++++++++
 crates/npc-fwk/Cargo.toml                          |   2 +
 crates/npc-fwk/src/lib.rs                          |  38 +++++++-
 crates/npc-fwk/src/toolkit.rs                      |   3 +
 crates/npc-fwk/src/toolkit/configuration.rs        | 104 ++++++++++++++++++++
 src/Makefile.am                                    |  14 ++-
 src/engine/Makefile.am                             |   2 +-
 src/fwk/Makefile.am                                |  20 +++-
 src/fwk/base/Makefile.am                           |   2 +-
 src/fwk/base/string.hpp                            |   8 +-
 src/fwk/toolkit/Makefile.am                        |  14 ++-
 src/fwk/toolkit/application.cpp                    |   6 +-
 src/fwk/toolkit/application.hpp                    |   6 +-
 src/fwk/toolkit/configdatabinder.cpp               |   8 +-
 src/fwk/toolkit/configdatabinder.hpp               |  24 ++---
 src/fwk/toolkit/configuration.cpp                  | 106 ---------------------
 src/fwk/toolkit/configuration.hpp                  |  62 ------------
 src/fwk/toolkit/frame.cpp                          |  11 +--
 src/fwk/toolkit/t/testconfigdatabinder.cpp         |  16 ++--
 src/fwk/utils/Makefile.am                          |   2 +-
 src/libraryclient/Makefile.am                      |   2 +-
 src/niepce/Makefile.am                             |   2 +-
 src/niepce/modules/darkroom/Makefile.am            |   2 +-
 src/niepce/modules/interfaces/Makefile.am          |   2 +-
 src/niepce/modules/map/Makefile.am                 |   2 +-
 src/niepce/ui/Makefile.am                          |   2 +-
 src/niepce/ui/dialogs/importdialog.cpp             |  15 ++-
 .../ui/dialogs/importers/directoryimporterui.cpp   |   5 +-
 src/niepce/ui/imageliststore.cpp                   |   6 +-
 src/niepce/ui/niepcewindow.cpp                     |  16 ++--
 src/niepce/ui/niepcewindow.hpp                     |   4 +-
 src/niepce/ui/workspacecontroller.cpp              |  15 ++-
 src/niepce/ui/workspacecontroller.hpp              |   2 +-
 src/rust_bindings.hpp                              |   7 +-
 35 files changed, 317 insertions(+), 261 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 0eac4fde..941714c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,6 +91,8 @@ m4/xsize.m4
 /src/fwk/utils/testufrawmeta
 /src/fwk/utils/testxmp
 /src/fwk/toolkit/testconfigdatabinder
+/src/fwk/cxx_fwk_bindings.cpp
+/src/fwk/cxx_fwk_bindings.hpp
 /src/libraryclient/test_worker
 /src/engine/test_library
 /src/engine/test_filebundle
diff --git a/Cargo.lock b/Cargo.lock
index 0cea1466..41782ec3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -111,6 +111,12 @@ dependencies = [
  "toml",
 ]
 
+[[package]]
+name = "cc"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+
 [[package]]
 name = "cfg-expr"
 version = "0.10.3"
@@ -163,6 +169,35 @@ dependencies = [
  "cache-padded",
 ]
 
+[[package]]
+name = "cxx"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "event-listener"
 version = "2.5.2"
@@ -659,6 +694,15 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "link-cplusplus"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "log"
 version = "0.4.17"
@@ -748,6 +792,7 @@ dependencies = [
  "cairo-rs",
  "cbindgen",
  "chrono",
+ "cxx",
  "exempi",
  "gdk-pixbuf",
  "gdk-pixbuf-sys",
@@ -765,6 +810,7 @@ dependencies = [
  "multimap",
  "once_cell",
  "rexiv2",
+ "tempfile",
 ]
 
 [[package]]
diff --git a/crates/npc-fwk/Cargo.toml b/crates/npc-fwk/Cargo.toml
index ff25ede7..1a979dcf 100644
--- a/crates/npc-fwk/Cargo.toml
+++ b/crates/npc-fwk/Cargo.toml
@@ -11,6 +11,7 @@ build = "build.rs"
 async-channel = "1.6.1"
 cairo-rs = "*"
 chrono = "0.4.0"
+cxx = "1.0"
 exempi = { version = "2.6.0", git = "https://github.com/hfiguiere/exempi-rs.git";, rev="99e8ba5" }
 gio-sys = "*"
 gio = "^0.15.7"
@@ -28,6 +29,7 @@ libopenraw-rs = { git = "https://gitlab.freedesktop.org/libopenraw/libopenraw-rs
 multimap = "0.4.0"
 once_cell = "^1.12.0"
 rexiv2 = "^0.9.1"
+tempfile = "3.3.0"
 
 
 [build-dependencies]
diff --git a/crates/npc-fwk/src/lib.rs b/crates/npc-fwk/src/lib.rs
index 80502c45..236f04a2 100644
--- a/crates/npc-fwk/src/lib.rs
+++ b/crates/npc-fwk/src/lib.rs
@@ -1,7 +1,7 @@
 /*
  * niepce - fwk/lib.rs
  *
- * Copyright (C) 2017-2020 Hubert Figuière
+ * Copyright (C) 2017-2022 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
@@ -75,3 +75,39 @@ pub unsafe extern "C" fn fwk_fraction_to_decimal(cvalue: *const c_char) -> f64 {
 pub fn init() {
     rexiv2::initialize().expect("Unable to initialize rexiv2");
 }
+
+use crate::toolkit::Configuration;
+
+fn make_config_path(file: &str) -> String {
+    Configuration::make_config_path(file)
+        .to_string_lossy()
+        .into()
+}
+
+fn configuration_new(file: &str) -> cxx::SharedPtr<ffi::SharedConfiguration> {
+    cxx::SharedPtr::new(ffi::SharedConfiguration {
+        cfg: Box::new(Configuration::from_file(file)),
+    })
+}
+
+#[cxx::bridge(namespace = "fwk")]
+mod ffi {
+    struct SharedConfiguration {
+        cfg: Box<Configuration>,
+    }
+
+    extern "Rust" {
+        type Configuration;
+
+        #[cxx_name = "Configuration_new"]
+        fn configuration_new(file: &str) -> SharedPtr<SharedConfiguration>;
+        #[cxx_name = "Configuration_make_config_path"]
+        fn make_config_path(file: &str) -> String;
+        #[cxx_name = "hasKey"]
+        fn has(&self, key: &str) -> bool;
+        #[cxx_name = "getValue"]
+        fn value(&self, key: &str, def: &str) -> String;
+        #[cxx_name = "setValue"]
+        fn set_value(&self, key: &str, value: &str);
+    }
+}
diff --git a/crates/npc-fwk/src/toolkit.rs b/crates/npc-fwk/src/toolkit.rs
index ba377d4f..c31abed7 100644
--- a/crates/npc-fwk/src/toolkit.rs
+++ b/crates/npc-fwk/src/toolkit.rs
@@ -18,12 +18,15 @@
  */
 
 pub mod clickable_cell_renderer;
+mod configuration;
 pub mod gdk_utils;
 pub mod mimetype;
 pub mod movieutils;
 pub mod thumbnail;
 pub mod widgets;
 
+pub use configuration::Configuration;
+
 pub type Sender<T> = async_channel::Sender<T>;
 
 /// Wrapper type for the channel tuple to get passed down to the unsafe C++ code.
diff --git a/crates/npc-fwk/src/toolkit/configuration.rs b/crates/npc-fwk/src/toolkit/configuration.rs
new file mode 100644
index 00000000..57fdc14a
--- /dev/null
+++ b/crates/npc-fwk/src/toolkit/configuration.rs
@@ -0,0 +1,104 @@
+/*
+ * niepce - fwk/toolkit/configuration.rs
+ *
+ * Copyright (C) 2022 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 gtk4::glib;
+
+/// A configuration, backed by a `glib::Keyfile`
+pub struct Configuration {
+    filename: std::path::PathBuf,
+    keyfile: glib::KeyFile,
+    root: String,
+}
+
+impl Configuration {
+    /// New configuration from a file.
+    pub fn from_file<P: AsRef<std::path::Path>>(file: P) -> Configuration {
+        let keyfile = glib::KeyFile::new();
+        on_err_out!(keyfile.load_from_file(&file, glib::KeyFileFlags::NONE));
+        Configuration {
+            filename: file.as_ref().to_path_buf(),
+            keyfile,
+            root: "main".to_string(),
+        }
+    }
+
+    /// New XDG compliant config path from `app_name`.
+    pub fn make_config_path(app_name: &str) -> std::path::PathBuf {
+        let mut filename = glib::user_config_dir();
+        filename.push(app_name);
+        on_err_out!(std::fs::create_dir_all(&filename));
+        filename.push("config");
+
+        filename
+    }
+
+    /// Return true if it has `key`.
+    pub fn has(&self, key: &str) -> bool {
+        self.keyfile.has_group(&self.root) && self.keyfile.has_key(&self.root, key).unwrap_or(false)
+    }
+
+    /// Return string value for `key`, or `def` if not found.
+    pub fn value(&self, key: &str, def: &str) -> String {
+        if !self.has(key) {
+            return def.to_string();
+        }
+        self.keyfile
+            .string(&self.root, key)
+            .map(|v| v.as_str().to_string())
+            .unwrap_or_else(|_| def.to_string())
+    }
+
+    /// Set `value` for `key`
+    pub fn set_value(&self, key: &str, value: &str) {
+        self.keyfile.set_string(&self.root, key, value);
+        on_err_out!(self.save());
+    }
+
+    /// Save
+    fn save(&self) -> Result<(), glib::Error> {
+        glib::file_set_contents(&self.filename, self.keyfile.to_data().as_gstr().to_bytes())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::Configuration;
+
+    #[test]
+    fn test_configuration() {
+        let tmpdir = tempfile::tempdir().expect("Tmp directory failed");
+
+        let mut test_file = tmpdir.path().to_path_buf();
+        test_file.push("test_file.ini");
+
+        assert!(!test_file.exists());
+        let cfg = Configuration::from_file(&test_file);
+        assert!(!test_file.exists());
+
+        assert!(!cfg.has("foobar"));
+        assert_eq!(cfg.value("foobar", "some_default"), "some_default");
+
+        cfg.set_value("foobar", "some_value");
+        assert!(test_file.exists());
+
+        assert!(cfg.has("foobar"));
+        assert_eq!(cfg.value("foobar", "some_default"), "some_value");
+    }
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index 0053453c..57407842 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,6 +39,7 @@ RUST_SOURCES = \
        @top_srcdir@/crates/npc-fwk/src/lib.rs \
        @top_srcdir@/crates/npc-fwk/src/toolkit.rs \
        @top_srcdir@/crates/npc-fwk/src/toolkit/clickable_cell_renderer.rs \
+       @top_srcdir@/crates/npc-fwk/src/toolkit/configuration.rs \
        @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/thumbnail.rs \
@@ -85,7 +86,18 @@ endif
 
 CARGO_TARGET_DIR=@abs_top_builddir@/target
 
-all-local: $(CARGO_TARGET_DIR)/@CARGO_TARGET_SUBDIR@/libniepce_rust.a
+RUST_BIN_DIR=$(CARGO_TARGET_DIR)/bin
+RUST_CXXBRIDGE=$(RUST_BIN_DIR)/cxxbridge
+
+# This might be a problem offline
+$(RUST_CXXBRIDGE):
+       mkdir -p $(RUST_BIN_DIR)
+       cargo install --root $(CARGO_TARGET_DIR) cxxbridge-cmd
+
+CLEANFILES = \
+       $(RUST_CXXBRIDGE)
+
+all-local: $(CARGO_TARGET_DIR)/@CARGO_TARGET_SUBDIR@/libniepce_rust.a $(RUST_CXXBRIDGE)
 
 @abs_top_builddir@/target/bindings.h: $(CARGO_TARGET_DIR)/@CARGO_TARGET_SUBDIR@/libniepce_rust.a
 
diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am
index 98a3c001..5a7abe2f 100644
--- a/src/engine/Makefile.am
+++ b/src/engine/Makefile.am
@@ -1,5 +1,5 @@
 
-AM_CPPFLAGS = -I$(top_srcdir)/src/  @FRAMEWORK_CFLAGS@ \
+AM_CPPFLAGS = -I$(top_srcdir)/src/ -I$(top_builddir)/src  @FRAMEWORK_CFLAGS@ \
        @GPHOTO_CFLAGS@ \
        $(NULL)
 
diff --git a/src/fwk/Makefile.am b/src/fwk/Makefile.am
index 167f3d65..86472adc 100644
--- a/src/fwk/Makefile.am
+++ b/src/fwk/Makefile.am
@@ -1,2 +1,20 @@
-SUBDIRS = base utils toolkit
+SUBDIRS = . base utils toolkit
 
+CARGO_TARGET_DIR=@abs_top_builddir@/target
+
+RUST_BIN_DIR=$(CARGO_TARGET_DIR)/bin
+RUST_CXXBRIDGE=$(RUST_BIN_DIR)/cxxbridge
+
+cxx_fwk_bindings.cpp: $(top_srcdir)/crates/npc-fwk/src/lib.rs
+       @echo "Generating bindings $@..."
+       @$(RUST_CXXBRIDGE) $< > $@
+
+cxx_fwk_bindings.hpp: $(top_srcdir)/crates/npc-fwk/src/lib.rs
+       @echo "Generating bindings header $@..."
+       @$(RUST_CXXBRIDGE) --header $< > $@
+
+BUILT_SOURCES = \
+        cxx_fwk_bindings.cpp \
+        cxx_fwk_bindings.hpp
+
+CLEANFILES = $(BUILD_SOURCES)
diff --git a/src/fwk/base/Makefile.am b/src/fwk/base/Makefile.am
index 538837b0..e3b7a0b6 100644
--- a/src/fwk/base/Makefile.am
+++ b/src/fwk/base/Makefile.am
@@ -1,5 +1,5 @@
 
-niepce_cppflags = -I$(top_srcdir)/src \
+niepce_cppflags = -I$(top_srcdir)/src -I$(top_builddir)/src \
        @BOOST_CPPFLAGS@ \
        @FRAMEWORK_CFLAGS@ \
        $(NULL)
diff --git a/src/fwk/base/string.hpp b/src/fwk/base/string.hpp
index 9e614fb1..cc52ac84 100644
--- a/src/fwk/base/string.hpp
+++ b/src/fwk/base/string.hpp
@@ -1,7 +1,7 @@
 /*
  * niepce - fwk/base/string.hpp
  *
- * Copyright (C) 2020 Hubert Figuière
+ * Copyright (C) 2020-2022 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
@@ -19,6 +19,8 @@
 
 #pragma once
 
+#include <string.h>
+
 #include <memory>
 #include <string>
 
@@ -29,6 +31,10 @@ class RustFfiString {
 public:
   RustFfiString(char* p);
 
+  bool empty() const {
+    return !ptr || strlen(c_str()) == 0;
+  }
+
   const char* c_str() const {
     return ptr.get();
   }
diff --git a/src/fwk/toolkit/Makefile.am b/src/fwk/toolkit/Makefile.am
index 3742756a..d070f783 100644
--- a/src/fwk/toolkit/Makefile.am
+++ b/src/fwk/toolkit/Makefile.am
@@ -1,12 +1,17 @@
 
 
-niepce_cppflags = -I$(top_srcdir)/src -I$(top_srcdir)/src/ext \
+niepce_cppflags = -I$(top_srcdir)/src -I$(top_builddir)/src -I$(top_srcdir)/src/ext \
        -DDATADIR=\"$(datadir)\" \
        @FRAMEWORK_CFLAGS@ \
        @GPHOTO_CFLAGS@ \
        @OPENRAW_CFLAGS@ \
        $(NULL)
 
+rust_ldadd = $(top_builddir)/target/@CARGO_TARGET_SUBDIR@/libniepce_rust.a \
+       @OPENRAW_LIBS@ \
+       @GEXIV2_LIBS@ \
+       $(NULL)
+
 AM_CPPFLAGS = $(niepce_cppflags)
 
 noinst_LIBRARIES = libniepceframework.a
@@ -24,9 +29,12 @@ testing_ldadd = @top_builddir@/third_party/libgtest.a \
 
 testconfigdatabinder_SOURCES = t/testconfigdatabinder.cpp
 testconfigdatabinder_CPPFLAGS = $(testing_cppflags)
-testconfigdatabinder_LDADD = $(testing_ldadd)
+testconfigdatabinder_LDADD = $(testing_ldadd) \
+       $(rust_ldadd) \
+       $(NULL)
 
-libniepceframework_a_SOURCES = configuration.hpp configuration.cpp \
+libniepceframework_a_SOURCES = \
+       ../cxx_fwk_bindings.cpp \
        application.hpp application.cpp \
        appframe.hpp appframe.cpp \
        dialog.hpp dialog.cpp \
diff --git a/src/fwk/toolkit/application.cpp b/src/fwk/toolkit/application.cpp
index b5479f7f..deddc50e 100644
--- a/src/fwk/toolkit/application.cpp
+++ b/src/fwk/toolkit/application.cpp
@@ -36,7 +36,7 @@ Application::Ptr Application::m_application;
 
 Application::Application(int & argc, char** &argv, const char* app_id,
                          const char * name)
-    : m_config(Configuration::make_config_path(name))
+    : m_config(Configuration_new(Configuration_make_config_path(name)))
     , m_module_manager(new ModuleManager())
     , m_gtkapp(Gtk::Application::create(app_id))
 {
@@ -68,7 +68,7 @@ bool Application::get_use_dark_theme() const
 {
     bool v;
     try {
-        v = std::stoi(m_config.getValue("ui_dark_theme", "0"));
+        v = std::stoi(std::string(m_config->cfg->getValue("ui_dark_theme", "0")));
     }
     catch(...) {
         v = false;
@@ -78,7 +78,7 @@ bool Application::get_use_dark_theme() const
 
 void Application::set_use_dark_theme(bool value)
 {
-    m_config.setValue("ui_dark_theme",
+    m_config->cfg->setValue("ui_dark_theme",
                       std::to_string(value));
 }
 
diff --git a/src/fwk/toolkit/application.hpp b/src/fwk/toolkit/application.hpp
index 14f9eacf..ea44ba50 100644
--- a/src/fwk/toolkit/application.hpp
+++ b/src/fwk/toolkit/application.hpp
@@ -25,10 +25,10 @@
 #include <gtkmm/application.h>
 #include <gtkmm/icontheme.h>
 
-#include "fwk/toolkit/configuration.hpp"
 #include "fwk/toolkit/appframe.hpp"
 #include "fwk/toolkit/undo.hpp"
 
+#include "rust_bindings.hpp"
 
 namespace fwk {
 
@@ -51,7 +51,7 @@ public:
     const Glib::RefPtr<Gtk::Application> & gtkApp() const
         { return m_gtkapp; }
 
-    Configuration & config()
+    const ConfigurationPtr& config() const
         { return m_config; }
 
     virtual void quit();
@@ -94,7 +94,7 @@ protected:
 
     AppFrame::WeakPtr            m_main_frame;
 private:
-    Configuration                m_config;
+    ConfigurationPtr m_config;
     UndoHistory                  m_undo;
     ModuleManager               *m_module_manager;
     Glib::RefPtr<Gtk::Application> m_gtkapp;
diff --git a/src/fwk/toolkit/configdatabinder.cpp b/src/fwk/toolkit/configdatabinder.cpp
index a80d3e1f..84b70380 100644
--- a/src/fwk/toolkit/configdatabinder.cpp
+++ b/src/fwk/toolkit/configdatabinder.cpp
@@ -1,7 +1,7 @@
 /*
  * niepce - fwk/toolkit/configdatabinder.cpp
  *
- * Copyright (C) 2007-2009 Hubert Figuiere
+ * Copyright (C) 2007-2022 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
@@ -21,10 +21,9 @@
 
 namespace fwk {
 
-
 ConfigDataBinderBase::ConfigDataBinderBase(const property_t & property,
-                                                                                  Configuration & config, 
-                                                                                  const std::string & key)
+                                          const ConfigurationPtr& config,
+                                          const std::string & key)
        : DataBinderBase(),
          m_property(property),
          m_config_key(key),
@@ -34,6 +33,5 @@ ConfigDataBinderBase::ConfigDataBinderBase(const property_t & property,
                sigc::mem_fun(*this, &ConfigDataBinderBase::on_changed));
 }
 
-
 }
 
diff --git a/src/fwk/toolkit/configdatabinder.hpp b/src/fwk/toolkit/configdatabinder.hpp
index acbb04d8..74b3a8b7 100644
--- a/src/fwk/toolkit/configdatabinder.hpp
+++ b/src/fwk/toolkit/configdatabinder.hpp
@@ -1,7 +1,7 @@
 /*
  * niepce - fwk/toolkit/configdatabinder.h
  *
- * Copyright (C) 2007-2009 Hubert Figuiere
+ * Copyright (C) 2007-2022 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,9 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
-#ifndef _FRAMEWORK_CONFIGDATABINDER_H_
-#define _FRAMEWORK_CONFIGDATABINDER_H_
+#pragma once
 
 #include <exception>
 #include <string>
@@ -30,7 +28,8 @@
 
 #include "fwk/base/debug.hpp"
 #include "fwk/utils/databinder.hpp"
-#include "fwk/toolkit/configuration.hpp"
+
+#include "rust_bindings.hpp"
 
 namespace fwk {
 
@@ -41,13 +40,13 @@ public:
        typedef Glib::PropertyProxy_Base property_t;
 
        ConfigDataBinderBase(const property_t & property,
-                             Configuration & config, const std::string & key);
+                             const ConfigurationPtr& config, const std::string & key);
 
        virtual void on_changed(void) = 0;
 protected:
        property_t        m_property;
        std::string       m_config_key;
-       Configuration   & m_config;
+       ConfigurationPtr m_config;
        sigc::connection  m_conn;
 };
 
@@ -59,11 +58,10 @@ public:
     typedef Glib::PropertyProxy<T> property_t;
 
     ConfigDataBinder(const property_t & property,
-                     Configuration & config, const std::string & key)
+                     const ConfigurationPtr & config, const std::string & key)
         : ConfigDataBinderBase(property, config, key)
         {
-            Glib::ustring value;
-            value = m_config.getValue(m_config_key, "");
+            std::string value = std::string(m_config->cfg->getValue(m_config_key, ""));
             if(!value.empty()) {
                 try {
                     T real_value;
@@ -81,7 +79,7 @@ public:
     virtual ~ConfigDataBinder()
         {
             try {
-                m_config.setValue(m_config_key,
+                m_config->cfg->setValue(m_config_key,
                                   boost::lexical_cast<std::string>(m_value));
             }
             catch(const boost::bad_lexical_cast &)
@@ -105,11 +103,7 @@ private:
     T m_value;
 };
 
-
 }
-
-
-#endif
 /*
   Local Variables:
   mode:c++
diff --git a/src/fwk/toolkit/frame.cpp b/src/fwk/toolkit/frame.cpp
index 32232ae6..6d2d365b 100644
--- a/src/fwk/toolkit/frame.cpp
+++ b/src/fwk/toolkit/frame.cpp
@@ -171,10 +171,9 @@ void Frame::frameRectFromConfig()
 {
     DBG_OUT("loading frame rect (%s)", m_layout_cfg_key.c_str());
     if(!m_layout_cfg_key.empty()) {
-        Configuration & cfg = Application::app()->config();
-        std::string val;
-        val = cfg.getValue(m_layout_cfg_key, "");
-        if(!val.empty()) {
+        auto& cfg = Application::app()->config()->cfg;
+        auto val = std::string(cfg->getValue(m_layout_cfg_key, ""));
+        if (!val.empty()) {
             try {
                 fwk::Rect r(val);
                 // XXX the position is now ignored
@@ -192,13 +191,13 @@ void Frame::frameRectToConfig()
 {
     DBG_OUT("saving frame rect (%s)", m_layout_cfg_key.c_str());
     if(!m_layout_cfg_key.empty()) {
-        Configuration & cfg = Application::app()->config();
+        auto& cfg = Application::app()->config()->cfg;
         int x, y, w, h;
         x = y = w = h = 0;
         // XXX the position is now ignored
         m_window->get_default_size(w, h);
         fwk::Rect r(x, y, w, h);
-        cfg.setValue(m_layout_cfg_key, std::to_string(r));
+        cfg->setValue(m_layout_cfg_key, std::to_string(r));
     }
 }
 
diff --git a/src/fwk/toolkit/t/testconfigdatabinder.cpp b/src/fwk/toolkit/t/testconfigdatabinder.cpp
index 0f26df13..e80a5c76 100644
--- a/src/fwk/toolkit/t/testconfigdatabinder.cpp
+++ b/src/fwk/toolkit/t/testconfigdatabinder.cpp
@@ -52,11 +52,11 @@ TEST(testConfigDataBinder, testConfigDataBinderSanity)
   PropertyFixture fixture;
 
   // get tmp file
-  Glib::ustring cfg_file("/tmp/tmp-cfg.ini");
+  std::string cfg_file("/tmp/tmp-cfg.ini");
   Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(cfg_file);
 
   {
-    fwk::Configuration cfg(cfg_file);
+    auto cfg = fwk::Configuration_new(cfg_file);
 
     {
       // the binder only write to the preference when
@@ -69,8 +69,8 @@ TEST(testConfigDataBinder, testConfigDataBinderSanity)
     }
     ASSERT_TRUE(file->query_exists());
 
-    ASSERT_TRUE(cfg.hasKey("int"));
-    Glib::ustring val = cfg.getValue("int", "0");
+    ASSERT_TRUE(cfg->cfg->hasKey("int"));
+    rust::String val = cfg->cfg->getValue("int", "0");
     ASSERT_EQ(val, "1");
 
     {
@@ -81,8 +81,8 @@ TEST(testConfigDataBinder, testConfigDataBinderSanity)
     }
     ASSERT_TRUE(file->query_exists());
 
-    ASSERT_TRUE(cfg.hasKey("string"));
-    val = cfg.getValue("string", "");
+    ASSERT_TRUE(cfg->cfg->hasKey("string"));
+    val = cfg->cfg->getValue("string", "");
     ASSERT_EQ(val, "foo");
 
     {
@@ -93,8 +93,8 @@ TEST(testConfigDataBinder, testConfigDataBinderSanity)
     }
     ASSERT_TRUE(file->query_exists());
 
-    ASSERT_TRUE(cfg.hasKey("bool"));
-    val = cfg.getValue("bool", "");
+    ASSERT_TRUE(cfg->cfg->hasKey("bool"));
+    val = cfg->cfg->getValue("bool", "");
     ASSERT_EQ(val, "1");
   }
 
diff --git a/src/fwk/utils/Makefile.am b/src/fwk/utils/Makefile.am
index f3392725..a61d736e 100644
--- a/src/fwk/utils/Makefile.am
+++ b/src/fwk/utils/Makefile.am
@@ -1,5 +1,5 @@
 
-niepce_cppflags = -I$(top_srcdir)/src/ \
+niepce_cppflags = -I$(top_srcdir)/src/ -I$(top_builddir)/src \
        @FRAMEWORK_CFLAGS@
 
 AM_CPPFLAGS = $(niepce_cppflags)
diff --git a/src/libraryclient/Makefile.am b/src/libraryclient/Makefile.am
index 5fdb9593..48b67640 100644
--- a/src/libraryclient/Makefile.am
+++ b/src/libraryclient/Makefile.am
@@ -1,6 +1,6 @@
 
 
-AM_CPPFLAGS = -I$(top_srcdir)/src @FRAMEWORK_CFLAGS@
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src @FRAMEWORK_CFLAGS@
 
 noinst_LIBRARIES = liblibraryclient.a
 
diff --git a/src/niepce/Makefile.am b/src/niepce/Makefile.am
index 67188a59..62b8cea9 100644
--- a/src/niepce/Makefile.am
+++ b/src/niepce/Makefile.am
@@ -2,7 +2,7 @@
 SUBDIRS = ui modules
 
 AM_CPPFLAGS = -DDATADIR=\"$(datadir)\" \
-       -I$(top_srcdir)/src/niepce -I$(top_srcdir)/src  \
+       -I$(top_srcdir)/src/niepce -I$(top_srcdir)/src -I$(top_builddir)/src \
        @FRAMEWORK_CFLAGS@ \
        -DNIEPCE_LOCALEDIR=\"@NIEPCE_LOCALEDIR@\"
 
diff --git a/src/niepce/modules/darkroom/Makefile.am b/src/niepce/modules/darkroom/Makefile.am
index b76fa915..2273f57b 100644
--- a/src/niepce/modules/darkroom/Makefile.am
+++ b/src/niepce/modules/darkroom/Makefile.am
@@ -1,6 +1,6 @@
 
 
-AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/niepce \
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src -I$(top_srcdir)/src/niepce \
        -I$(top_srcdir)/src/ext \
        -DDATADIR=\"$(datadir)\" \
        @FRAMEWORK_CFLAGS@ \
diff --git a/src/niepce/modules/interfaces/Makefile.am b/src/niepce/modules/interfaces/Makefile.am
index 636db343..47495c8f 100644
--- a/src/niepce/modules/interfaces/Makefile.am
+++ b/src/niepce/modules/interfaces/Makefile.am
@@ -1,6 +1,6 @@
 
 
-AM_CPPFLAGS = -I$(top_srcdir)/src/ \
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src \
        @FRAMEWORK_CFLAGS@
 
 noinst_HEADERS = ipostimportprocessing.hpp \
diff --git a/src/niepce/modules/map/Makefile.am b/src/niepce/modules/map/Makefile.am
index 80e99fdb..6bce6ba0 100644
--- a/src/niepce/modules/map/Makefile.am
+++ b/src/niepce/modules/map/Makefile.am
@@ -1,6 +1,6 @@
 
 
-AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/niepce \
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src -I$(top_srcdir)/src/niepce \
        -I$(top_srcdir)/src/ext \
        -DDATADIR=\"$(datadir)\" \
        @FRAMEWORK_CFLAGS@
diff --git a/src/niepce/ui/Makefile.am b/src/niepce/ui/Makefile.am
index 79618364..6e2e197e 100644
--- a/src/niepce/ui/Makefile.am
+++ b/src/niepce/ui/Makefile.am
@@ -1,5 +1,5 @@
 
-AM_CPPFLAGS = -I$(top_srcdir)/src/ -I$(srcdir)/.. \
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src -I$(srcdir)/.. \
        -I$(top_srcdir)/src/ext \
        -DDATADIR=\"$(datadir)\" \
        @FRAMEWORK_CFLAGS@ @GPHOTO_CFLAGS@ \
diff --git a/src/niepce/ui/dialogs/importdialog.cpp b/src/niepce/ui/dialogs/importdialog.cpp
index 8c099063..e76701b3 100644
--- a/src/niepce/ui/dialogs/importdialog.cpp
+++ b/src/niepce/ui/dialogs/importdialog.cpp
@@ -31,7 +31,6 @@
 #include "fwk/base/debug.hpp"
 #include "fwk/utils/pathutils.hpp"
 #include "fwk/toolkit/application.hpp"
-#include "fwk/toolkit/configuration.hpp"
 #include "engine/importer/directoryimporter.hpp"
 #include "engine/importer/importedfile.hpp"
 #include "importdialog.hpp"
@@ -52,10 +51,10 @@ ImportDialog::ImportDialog()
   , m_attributes_scrolled(nullptr)
   , m_images_list_scrolled(nullptr)
 {
-    fwk::Configuration & cfg = fwk::Application::app()->config();
-    m_base_dest_dir = cfg.getValue("base_import_dest_dir",
+    auto& cfg = fwk::Application::app()->config()->cfg;
+    m_base_dest_dir = std::string(cfg->getValue("base_import_dest_dir",
                                    Glib::get_user_special_dir(
-                                       Glib::UserDirectory::PICTURES));
+                                       Glib::UserDirectory::PICTURES)));
     DBG_OUT("base_dest_dir set to %s", m_base_dest_dir.c_str());
 }
 
@@ -81,7 +80,7 @@ void ImportDialog::setup_widget()
         return;
     }
 
-    fwk::Configuration & cfg = fwk::Application::app()->config();
+    auto& cfg = fwk::Application::app()->config()->cfg;
 
     Glib::RefPtr<Gtk::Builder> a_builder = builder();
     m_date_tz_combo = a_builder->get_widget<Gtk::ComboBox>("date_tz_combo");
@@ -104,7 +103,7 @@ void ImportDialog::setup_widget()
     m_importers[importer->id()] = importer;
     add_importer_ui(*importer);
 
-    auto last_importer = cfg.getValue("last_importer", "DirectoryImporter");
+    auto last_importer = std::string(cfg->getValue("last_importer", "DirectoryImporter"));
     m_import_source_combo->set_active_id(last_importer);
 
     // Metadata pane.
@@ -161,8 +160,8 @@ void ImportDialog::import_source_changed()
 
     clear_import_list();
 
-    fwk::Configuration & cfg = fwk::Application::app()->config();
-    cfg.setValue("last_importer", id);
+    auto& cfg = fwk::Application::app()->config()->cfg;
+    cfg->setValue("last_importer", id.c_str());
 }
 
 void ImportDialog::set_source(const std::string& source, const std::string& dest_dir)
diff --git a/src/niepce/ui/dialogs/importers/directoryimporterui.cpp 
b/src/niepce/ui/dialogs/importers/directoryimporterui.cpp
index 58bb3553..66976260 100644
--- a/src/niepce/ui/dialogs/importers/directoryimporterui.cpp
+++ b/src/niepce/ui/dialogs/importers/directoryimporterui.cpp
@@ -24,7 +24,6 @@
 
 #include "fwk/utils/pathutils.hpp"
 #include "fwk/toolkit/application.hpp"
-#include "fwk/toolkit/configuration.hpp"
 #include "engine/importer/directoryimporter.hpp"
 #include "directoryimporterui.hpp"
 
@@ -51,7 +50,7 @@ Gtk::Widget* DirectoryImporterUI::setup_widget(const fwk::Frame::Ptr& frame)
 
 void DirectoryImporterUI::do_select_directories()
 {
-    fwk::Configuration & cfg = fwk::Application::app()->config();
+    auto& cfg = fwk::Application::app()->config()->cfg;
 
     auto frame = m_frame.lock();
     auto dialog = new Gtk::FileChooserDialog(frame->gtkWindow(), _("Import picture folder"),
@@ -61,7 +60,7 @@ void DirectoryImporterUI::do_select_directories()
     dialog->add_button(_("Select"), Gtk::ResponseType::OK);
     dialog->set_select_multiple(false);
 
-    std::string last_import_location = cfg.getValue("last_import_location", "");
+    std::string last_import_location(cfg->getValue("last_import_location", ""));
     if (!last_import_location.empty()) {
         auto file = Gio::File::create_for_path(last_import_location);
         dialog->set_current_folder(file);
diff --git a/src/niepce/ui/imageliststore.cpp b/src/niepce/ui/imageliststore.cpp
index b88b99be..515ed562 100644
--- a/src/niepce/ui/imageliststore.cpp
+++ b/src/niepce/ui/imageliststore.cpp
@@ -96,11 +96,11 @@ void ImageListStore::on_lib_notification(const eng::LibNotification &ln)
     switch (type) {
     case eng::NotificationType::XMP_NEEDS_UPDATE:
     {
-        fwk::Configuration & cfg = fwk::Application::app()->config();
+        auto& cfg = fwk::Application::app()->config()->cfg;
         int write_xmp = false;
-        Glib::ustring xmp_pref;
+        std::string xmp_pref;
         try {
-            xmp_pref = cfg.getValue("write_xmp_automatically", "0");
+            xmp_pref = std::string(cfg->getValue("write_xmp_automatically", "0"));
             write_xmp = std::stoi(xmp_pref);
         }
         catch(const std::exception & e)
diff --git a/src/niepce/ui/niepcewindow.cpp b/src/niepce/ui/niepcewindow.cpp
index 6f5f1ac5..eefb5cf9 100644
--- a/src/niepce/ui/niepcewindow.cpp
+++ b/src/niepce/ui/niepcewindow.cpp
@@ -32,7 +32,6 @@
 #include "fwk/base/moniker.hpp"
 #include "fwk/utils/boost.hpp"
 #include "fwk/toolkit/application.hpp"
-#include "fwk/toolkit/configuration.hpp"
 #include "fwk/toolkit/notificationcenter.hpp"
 #include "fwk/toolkit/configdatabinder.hpp"
 #include "fwk/toolkit/undo.hpp"
@@ -254,17 +253,17 @@ void NiepceWindow::init_actions()
 
 void NiepceWindow::on_open_library()
 {
-    Configuration & cfg = Application::app()->config();
+    auto& cfg = Application::app()->config()->cfg;
     std::string libMoniker;
     int reopen = 0;
     try {
-        reopen = std::stoi(cfg.getValue("reopen_last_catalog", "0"));
+        reopen = std::stoi(std::string(cfg->getValue("reopen_last_catalog", "0")));
     }
     catch(...)
     {
     }
     if(reopen) {
-        libMoniker = cfg.getValue("last_open_catalog", "");
+        libMoniker = std::string(cfg->getValue("last_open_catalog", ""));
     }
     if (libMoniker.empty()) {
         prompt_open_library();
@@ -345,13 +344,13 @@ void NiepceWindow::prompt_open_library()
             DBG_OUT("response %d", response);
             if (response == Gtk::ResponseType::OK) {
                 DBG_OUT("Accepted");
-                Configuration & cfg = Application::app()->config();
+                auto& cfg = Application::app()->config()->cfg;
                 auto file = dialog->get_file();
                 Glib::ustring libraryToCreate = file->get_path();
                 // pass it to the library
                 std::string libMoniker = "local:";
                 libMoniker += libraryToCreate.c_str();
-                cfg.setValue("last_open_catalog", libMoniker);
+                cfg->setValue("last_open_catalog", libMoniker);
                 DBG_OUT("created catalog %s", libMoniker.c_str());
                 this->open_library(libMoniker);
             }
@@ -368,10 +367,7 @@ bool NiepceWindow::open_library(const std::string & libMoniker)
                                mon, m_notifcenter->get_channel()));
     // XXX ensure the library is open.
     set_title(libMoniker);
-    m_library_cfg
-        = fwk::Configuration::Ptr(
-            new fwk::Configuration(
-                Glib::build_filename(mon.path(), "config.ini")));
+    m_library_cfg = fwk::Configuration_new(Glib::build_filename(mon.path(), "config.ini"));
     ffi::libraryclient_get_all_labels(m_libClient->client());
     if(!m_moduleshell) {
         _createModuleShell();
diff --git a/src/niepce/ui/niepcewindow.hpp b/src/niepce/ui/niepcewindow.hpp
index 5fc8f2ae..fb1fce8f 100644
--- a/src/niepce/ui/niepcewindow.hpp
+++ b/src/niepce/ui/niepcewindow.hpp
@@ -49,7 +49,7 @@ public:
 
     libraryclient::LibraryClientPtr getLibraryClient() const
         { return m_libClient; }
-    fwk::Configuration::Ptr getLibraryConfig() const
+    const fwk::ConfigurationPtr& getLibraryConfig() const
         { return m_library_cfg; }
 
 protected:
@@ -83,7 +83,7 @@ private:
     Gtk::Statusbar                 m_statusBar;
     EditLabels::Ptr m_editlabel_dialog;
     libraryclient::LibraryClientPtr m_libClient;
-    fwk::Configuration::Ptr        m_library_cfg;
+    fwk::ConfigurationPtr m_library_cfg;
 };
 
 }
diff --git a/src/niepce/ui/workspacecontroller.cpp b/src/niepce/ui/workspacecontroller.cpp
index 000e050f..10098be6 100644
--- a/src/niepce/ui/workspacecontroller.cpp
+++ b/src/niepce/ui/workspacecontroller.cpp
@@ -29,7 +29,6 @@
 #include "fwk/base/debug.hpp"
 #include "fwk/base/string.hpp"
 #include "fwk/toolkit/application.hpp"
-#include "fwk/toolkit/configuration.hpp"
 #include "fwk/toolkit/gtkutils.hpp"
 #include "engine/importer/iimporter.hpp"
 #include "libraryclient/libraryclient.hpp"
@@ -88,7 +87,7 @@ libraryclient::LibraryClientPtr WorkspaceController::getLibraryClient() const
     return std::dynamic_pointer_cast<NiepceWindow>(m_parent.lock())->getLibraryClient();
 }
 
-fwk::Configuration::Ptr WorkspaceController::getLibraryConfig() const
+const fwk::ConfigurationPtr& WorkspaceController::getLibraryConfig() const
 {
     return std::dynamic_pointer_cast<NiepceWindow>(m_parent.lock())->getLibraryConfig();
 }
@@ -117,7 +116,7 @@ void WorkspaceController::action_delete_folder()
 
 void WorkspaceController::perform_file_import(ImportDialog::Ptr dialog)
 {
-    auto& cfg = Application::app()->config(); // XXX change to getLibraryConfig()
+    auto& cfg = Application::app()->config()->cfg; // XXX change to getLibraryConfig()
     // as the last import should be part of the library not the application.
 
     // import
@@ -128,7 +127,7 @@ void WorkspaceController::perform_file_import(ImportDialog::Ptr dialog)
     }
     // XXX this should be a different config key
     // specific to the importer.
-    cfg.setValue("last_import_location", source);
+    cfg->setValue("last_import_location", source);
 
     auto importer = dialog->get_importer();
     DBG_ASSERT(!!importer, "Import can't be null if we clicked import");
@@ -296,7 +295,7 @@ void WorkspaceController::on_row_expanded_collapsed(const Gtk::TreeModel::iterat
                                                     bool expanded)
 {
     int type = (*iter)[m_librarycolumns.m_type];
-    fwk::Configuration::Ptr cfg = getLibraryConfig();
+    auto& cfg = getLibraryConfig()->cfg;
     const char* key = nullptr;
     switch(type) {
     case FOLDERS_ITEM:
@@ -309,7 +308,7 @@ void WorkspaceController::on_row_expanded_collapsed(const Gtk::TreeModel::iterat
         key = "workspace_keywords_expanded";
         break;
     }
-    if(cfg && key) {
+    if (key) {
         cfg->setValue(key, std::to_string(expanded));
     }
 }
@@ -508,9 +507,9 @@ Gtk::Widget * WorkspaceController::buildWidget()
 void WorkspaceController::expand_from_cfg(const char* key,
                                           const Gtk::TreeModel::iterator& treenode)
 {
-    fwk::Configuration::Ptr cfg = getLibraryConfig();
+    auto& cfg = getLibraryConfig()->cfg;
 
-    bool expanded = std::stoi(cfg->getValue(key, "1"));
+    bool expanded = std::stoi(std::string(cfg->getValue(key, "1")));
     if(expanded) {
         m_librarytree.expand_row(m_treestore->get_path(treenode),
                                  false);
diff --git a/src/niepce/ui/workspacecontroller.hpp b/src/niepce/ui/workspacecontroller.hpp
index eae0882a..6bf622c5 100644
--- a/src/niepce/ui/workspacecontroller.hpp
+++ b/src/niepce/ui/workspacecontroller.hpp
@@ -105,7 +105,7 @@ private:
     void on_button_press_event(double x, double y);
 
     libraryclient::LibraryClientPtr getLibraryClient() const;
-    fwk::Configuration::Ptr getLibraryConfig() const;
+    const fwk::ConfigurationPtr& getLibraryConfig() const;
 
     /** add a folder item to the treeview */
     void add_folder_item(const eng::LibFolder* f);
diff --git a/src/rust_bindings.hpp b/src/rust_bindings.hpp
index 571a0056..15052fc7 100644
--- a/src/rust_bindings.hpp
+++ b/src/rust_bindings.hpp
@@ -1,7 +1,7 @@
 /*
  * niepce - rust_bindings.hpp
  *
- * Copyright (C) 2017-2021 Hubert Figuiere
+ * Copyright (C) 2017-2022 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
@@ -19,6 +19,8 @@
 
 #pragma once
 
+#include <memory>
+
 #include <gtk/gtk.h>
 
 namespace ffi {
@@ -34,9 +36,10 @@ struct NiepcePropertySet;
 #include "target/fwk_bindings.h"
 #include "target/eng_bindings.h"
 #include "target/bindings.h"
+#include "fwk/cxx_fwk_bindings.hpp"
 
 namespace fwk {
-
+typedef std::shared_ptr<SharedConfiguration> ConfigurationPtr;
 typedef ffi::ExempiManager ExempiManager;
 typedef ffi::PropertyValue PropertyValue;
 typedef ffi::NiepcePropertyBag PropertyBag;


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