[niepce] importer: camera importer (part 1)



commit fe38dcf6e43d9c623cbe3068031d298dce9f78f3
Author: Hubert Figuière <hub figuiere net>
Date:   Thu May 25 00:08:30 2017 -0400

    importer: camera importer (part 1)
    
    - implement more of the gphoto wrapper.
    - tweak the importer interface.

 camerawire/src/Makefile.am                         |    5 +-
 camerawire/src/cwwindow.hpp                        |    2 +-
 configure.ac                                       |    2 +-
 src/engine/Makefile.am                             |    1 +
 src/engine/importer/cameraimporter.cpp             |   71 +++++-
 src/engine/importer/cameraimporter.hpp             |    5 +
 src/engine/importer/directoryimporter.cpp          |   12 +-
 src/engine/importer/importedfile.hpp               |    3 +-
 src/fwk/base/option.hpp                            |   68 +++---
 src/fwk/utils/gphoto.cpp                           |  285 ++++++++++++++++----
 src/fwk/utils/gphoto.hpp                           |  102 +++++---
 src/niepce/Makefile.am                             |    1 +
 src/niepce/ui/Makefile.am                          |    4 +-
 src/niepce/ui/dialogs/importdialog.cpp             |    4 +-
 .../ui/dialogs/importers/cameraimporterui.cpp      |   46 +++-
 .../ui/dialogs/importers/cameraimporterui.hpp      |   15 +-
 .../ui/dialogs/importers/cameraimporterui.ui       |   50 ++++
 src/niepce/ui/dialogs/importers/importerui.cpp     |   29 ++-
 src/niepce/ui/dialogs/importers/importerui.hpp     |   26 +-
 19 files changed, 561 insertions(+), 170 deletions(-)
---
diff --git a/camerawire/src/Makefile.am b/camerawire/src/Makefile.am
index 1a8fba9..41debe6 100644
--- a/camerawire/src/Makefile.am
+++ b/camerawire/src/Makefile.am
@@ -13,9 +13,10 @@ camerawire_SOURCES = \
        cwwindow.hpp cwwindow.cpp \
        main.cpp
 
-camerawire_LDADD = @FRAMEWORK_LIBS@ \
-       @GPHOTO_LIBS@ \
+camerawire_LDADD = \
        $(top_builddir)/src/fwk/toolkit/libniepceframework.a \
        $(top_builddir)/src/fwk/utils/libniepceutils.a \
        $(top_builddir)/src/fwk/base/libfwkbase.a \
+       @FRAMEWORK_LIBS@ \
+       @GPHOTO_LIBS@ \
        $(NULL)
diff --git a/camerawire/src/cwwindow.hpp b/camerawire/src/cwwindow.hpp
index 08fca8f..a2177d5 100644
--- a/camerawire/src/cwwindow.hpp
+++ b/camerawire/src/cwwindow.hpp
@@ -56,7 +56,7 @@ private:
 
     Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > m_icon;
     Gtk::TreeModelColumn<std::string>        m_label;
-    Gtk::TreeModelColumn<fwk::GpDevice::Ptr> m_camera;
+    Gtk::TreeModelColumn<fwk::GpDevicePtr> m_camera;
     Gtk::TreeModelColumn<bool>               m_persistent;
   };
 
diff --git a/configure.ac b/configure.ac
index 152e5a8..1981c19 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,7 +18,7 @@ EXEMPI_VERSION=2.2.0
 SQLITE_VERSION=3.0
 GEGL_VERSION=0.3.0
 LIBOPENRAW_VERSION=0.1.0
-LIBGPHOTO_VERSION=2.4
+LIBGPHOTO_VERSION=2.5
 dnl need at least 2.5.0 because of xmlTextReader
 LIBXML2_VERSION=2.5.0
 BOOST_VERSION=1.34
diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am
index b26bd29..f71d219 100644
--- a/src/engine/Makefile.am
+++ b/src/engine/Makefile.am
@@ -1,6 +1,7 @@
 
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/  @FRAMEWORK_CFLAGS@ \
+       @GPHOTO_CFLAGS@ \
        $(NULL)
 
 TESTS = test_library test_filebundle test_opqueue
diff --git a/src/engine/importer/cameraimporter.cpp b/src/engine/importer/cameraimporter.cpp
index b6b8727..96466d2 100644
--- a/src/engine/importer/cameraimporter.cpp
+++ b/src/engine/importer/cameraimporter.cpp
@@ -20,14 +20,44 @@
 
 #include "cameraimporter.hpp"
 
+#include "fwk/base/debug.hpp"
+
 namespace eng {
 
+
+class CameraImportedFile
+    : public ImportedFile
+{
+public:
+    CameraImportedFile(const std::pair<std::string, std::string>& desc)
+        : CameraImportedFile(desc.first, desc.second)
+        { }
+
+    CameraImportedFile(const std::string& folder, const std::string& filename)
+        : m_folder(folder)
+        , m_filename(filename)
+        , m_path(m_folder + "/" + m_filename)
+        { }
+    const std::string& name() const override
+        { return m_filename; }
+    const std::string& path() const override
+        { return m_path; }
+private:
+    std::string m_folder;
+    std::string m_filename;
+    std::string m_path;
+};
+
+
 CameraImporter::CameraImporter()
 {
 }
 
 CameraImporter::~CameraImporter()
 {
+    if (m_camera) {
+        m_camera->close();
+    }
 }
 
 const std::string& CameraImporter::id() const
@@ -36,20 +66,59 @@ const std::string& CameraImporter::id() const
     return _id;
 }
 
-bool CameraImporter::list_source_content(const std::string & source,
+bool CameraImporter::list_source_content(const std::string& source,
                                          const SourceContentReady& callback)
 {
+    if (ensure_camera_open(source)) {
+        auto content = m_camera->list_content();
+        std::list<ImportedFilePtr> file_list;
+        for (auto item : content) {
+            file_list.push_back(ImportedFilePtr(new CameraImportedFile(item)));
+        }
+
+        callback(std::move(file_list));
+        return true;
+    }
+    return false;
 }
 
 bool CameraImporter::get_previews_for(const std::string& source,
                                       const std::list<std::string>& paths,
                                       const PreviewReady& callback)
 {
+    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);
+            callback(path, thumbnail);
+        }
+
+        return true;
+    }
+    return false;
 }
 
 bool CameraImporter::do_import(const std::string & source,
                                const FileImporter & importer)
 {
+    if (ensure_camera_open(source)) {
+
+    }
+    return false;
+}
+
+bool CameraImporter::ensure_camera_open(const std::string& source)
+{
+    if (!m_camera || m_camera->path() != source) {
+        auto result = fwk::GpDeviceList::obj().get_device(source);
+        if (result.ok()) {
+            m_camera.reset(new fwk::GpCamera(result.unwrap()));
+        }
+    }
+    if (m_camera) {
+        m_camera->open();
+    }
+    return !!m_camera;
 }
 
 }
diff --git a/src/engine/importer/cameraimporter.hpp b/src/engine/importer/cameraimporter.hpp
index 61bb59d..61f0fe7 100644
--- a/src/engine/importer/cameraimporter.hpp
+++ b/src/engine/importer/cameraimporter.hpp
@@ -24,6 +24,7 @@
 #include <list>
 
 #include "fwk/utils/files.hpp"
+#include "fwk/utils/gphoto.hpp"
 #include "engine/importer/iimporter.hpp"
 
 namespace eng {
@@ -43,6 +44,10 @@ public:
 
     bool do_import(const std::string & source,
                    const FileImporter & importer) override;
+private:
+    bool ensure_camera_open(const std::string&);
+    std::string m_camera_desc;
+    fwk::GpCameraPtr m_camera;
 };
 
 }
diff --git a/src/engine/importer/directoryimporter.cpp b/src/engine/importer/directoryimporter.cpp
index 767f318..76ce623 100644
--- a/src/engine/importer/directoryimporter.cpp
+++ b/src/engine/importer/directoryimporter.cpp
@@ -37,10 +37,10 @@ public:
         {
             m_name = fwk::path_basename(path);
         }
-    const std::string & name() const override
-        {
-            return m_name;
-        }
+    const std::string& name() const override
+        { return m_name; }
+    const std::string& path() const override
+        { return m_path; }
 private:
     std::string m_name;
     std::string m_path;
@@ -83,8 +83,8 @@ bool DirectoryImporter::get_previews_for(const std::string& source,
                                          const PreviewReady& callback)
 {
     for (auto path : paths) {
-        auto full_path = Glib::build_filename(source, path);
-        auto thumbnail = fwk::Thumbnail::thumbnail_file(full_path, 160, 160, 0);
+        DBG_OUT("path %s", path.c_str());
+        auto thumbnail = fwk::Thumbnail::thumbnail_file(path, 160, 160, 0);
         callback(path, thumbnail);
     }
     return true;
diff --git a/src/engine/importer/importedfile.hpp b/src/engine/importer/importedfile.hpp
index af3c2fb..2114900 100644
--- a/src/engine/importer/importedfile.hpp
+++ b/src/engine/importer/importedfile.hpp
@@ -34,7 +34,8 @@ public:
     virtual ~ImportedFile()
         {}
 
-    virtual const std::string & name() const = 0;
+    virtual const std::string& name() const = 0;
+    virtual const std::string& path() const = 0;
 };
 
 typedef std::shared_ptr<ImportedFile> ImportedFilePtr;
diff --git a/src/fwk/base/option.hpp b/src/fwk/base/option.hpp
index 76df967..2bdd7fc 100644
--- a/src/fwk/base/option.hpp
+++ b/src/fwk/base/option.hpp
@@ -30,42 +30,44 @@ template<class T>
 class Option
 {
 public:
-  typedef T data_type;
+    typedef T data_type;
 
-  Option()
-    : m_none(true)
-  {
-  }
-  Option(T&& data)
-    : m_none(false)
-    , m_data(data)
-  {
-  }
-  Option(const T& data)
-    : m_none(false)
-    , m_data(data)
-  {
-  }
-  template<class... Args>
-  Option(Args&&... args)
-    : m_none(false)
-    , m_data(args...)
-  {
-  }
+    Option()
+        : m_none(true)
+        {
+        }
+    Option(T&& data)
+        : m_none(false)
+        , m_data(data)
+        {
+        }
+    Option(const T& data)
+        : m_none(false)
+        , m_data(data)
+        {
+        }
+    template<class... Args>
+    Option(Args&&... args)
+        : m_none(false)
+        , m_data(args...)
+        {
+        }
 
-  T&& unwrap()
-  {
-    if (m_none) {
-      throw std::runtime_error("none option value");
-    }
-    m_none = true;
-    return std::move(m_data);
-  }
-  bool empty() const
-  { return m_none; }
+    T&& unwrap()
+        {
+            if (m_none) {
+                throw std::runtime_error("none option value");
+            }
+            m_none = true;
+            return std::move(m_data);
+        }
+    bool ok() const
+        { return !empty(); }
+    bool empty() const
+        { return m_none; }
 private:
-  bool m_none;
-  T m_data;
+    bool m_none;
+    T m_data;
 };
 
 
diff --git a/src/fwk/utils/gphoto.cpp b/src/fwk/utils/gphoto.cpp
index 8d302a3..3c2a24c 100644
--- a/src/fwk/utils/gphoto.cpp
+++ b/src/fwk/utils/gphoto.cpp
@@ -1,7 +1,8 @@
+/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode:nil; -*- */
 /*
  * niepce - fwk/utils/gphoto.cpp
  *
- * Copyright (C) 2009 Hubert Figuiere
+ * Copyright (C) 2009-2017 Hubert Figuiere
  *
  * 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
@@ -18,102 +19,276 @@
  */
 
 #include <string.h>
+#include <string>
+#include <vector>
 
-extern "C" {
+#include <gphoto2-port-info-list.h>
+#include <gphoto2-abilities-list.h>
+#include <gphoto2-camera.h>
+#include <gphoto2-context.h>
 #include <gphoto2-port-result.h>
-}
+
+#include <gdkmm/pixbufloader.h>
 
 #include "fwk/base/debug.hpp"
+#include "fwk/utils/pathutils.hpp"
+#include "fwk/toolkit/thumbnail.hpp"
 #include "gphoto.hpp"
 
 
 namespace fwk {
 
 
-#define GP_CHECK(x,op)   if (x < GP_OK) {         \
-    DBG_OUT("%s failed with %d",#op,x); }
+#define GP_CHECK(x, op)   if (x < GP_OK) { \
+    DBG_OUT("%s failed with %d", #op, x); }
 
 
-GpDevice::GpDevice(const std::string & model, const std::string & path)
-  : m_model(model)
-  , m_path(path)
+GpDevice::GpDevice(const std::string& model, const std::string& path)
+    : m_model(model)
+    , m_path(path)
 {
 }
 
 
 
 GpDeviceList::GpDeviceList()
-  : m_abilities(NULL)
-  , m_ports(NULL)
+    : m_abilities(nullptr)
+    , m_ports(nullptr)
 {
-  reload();
+    reload();
 }
 
 GpDeviceList::~GpDeviceList()
 {
-  _gp_cleanup();
+    gp_cleanup();
 }
 
 
-void GpDeviceList::_gp_cleanup()
+void GpDeviceList::gp_cleanup()
 {
-  if (m_abilities) {
-    ::gp_abilities_list_free(m_abilities);
-    m_abilities = NULL;
-  }
-  if (m_ports) {
-    ::gp_port_info_list_free(m_ports);
-    m_ports = NULL;
-  }
+    if (m_abilities) {
+        ::gp_abilities_list_free(m_abilities);
+        m_abilities = nullptr;
+    }
+    if (m_ports) {
+        ::gp_port_info_list_free(m_ports);
+        m_ports = nullptr;
+    }
 }
 
 void GpDeviceList::reload()
 {
-  _gp_cleanup();
+    gp_cleanup();
 
-  int ret;
-  ret = ::gp_port_info_list_new(&m_ports);
-  GP_CHECK(ret, gp_port_info_list_new);
-  ret = ::gp_port_info_list_load(m_ports);
-  GP_CHECK(ret, gp_port_list_load);
+    int ret;
+    ret = ::gp_port_info_list_new(&m_ports);
+    GP_CHECK(ret, gp_port_info_list_new);
+    ret = ::gp_port_info_list_load(m_ports);
+    GP_CHECK(ret, gp_port_list_load);
        ret = ::gp_abilities_list_new(&m_abilities);
-  GP_CHECK(ret, gp_abilities_list_new);
-  ret = ::gp_abilities_list_load(m_abilities, NULL);
-  GP_CHECK(ret, gp_abilities_list_load);
+    GP_CHECK(ret, gp_abilities_list_new);
+    ret = ::gp_abilities_list_load(m_abilities, NULL);
+    GP_CHECK(ret, gp_abilities_list_load);
 }
 
 
 void GpDeviceList::detect()
 {
-  if((!m_ports) || (!m_abilities)) {
-    reload();
-  }
-  ::CameraList *cameraList;
-  int ret;
-
-  ret = ::gp_list_new(&cameraList);
-  GP_CHECK(ret, gp_list_new);
-  ret = ::gp_abilities_list_detect(m_abilities, m_ports, cameraList, NULL);
-  GP_CHECK(ret, gp_abilities_list_detect);
-       
-  int count = ::gp_list_count(cameraList);
-  for (int i = 0; i < count; i++)      {
-    const char * name;
-    const char * value;
-    ret = ::gp_list_get_name(cameraList, i, &name);
-    GP_CHECK(ret, gp_list_get_name);
-    ret = ::gp_list_get_value(cameraList, i, &value);
-    GP_CHECK(ret, gp_list_get_value);
-    if ((count > 1) && (strcmp(value, "usb:") == 0)) {
-      continue;
+    if((!m_ports) || (!m_abilities)) {
+        reload();
+    }
+    ::CameraList *camera_list;
+    int ret;
+
+    ret = ::gp_list_new(&camera_list);
+    GP_CHECK(ret, gp_list_new);
+    ret = ::gp_abilities_list_detect(m_abilities, m_ports, camera_list, nullptr);
+    GP_CHECK(ret, gp_abilities_list_detect);
+
+    int count = ::gp_list_count(camera_list);
+    for (int i = 0; i < count; i++) {
+        const char * name = nullptr;
+        const char * value = nullptr;
+
+        ret = ::gp_list_get_name(camera_list, i, &name);
+        GP_CHECK(ret, gp_list_get_name);
+
+        ret = ::gp_list_get_value(camera_list, i, &value);
+        GP_CHECK(ret, gp_list_get_value);
+
+        if ((count > 1) && (strcmp(value, "usb:") == 0)) {
+            continue;
+        }
+        DBG_OUT("found %s %s", name, value);
+        push_back(GpDevicePtr(new GpDevice(name, value)));
+    }
+    ::gp_list_free(camera_list);
+}
+
+fwk::Option<GpDevicePtr> GpDeviceList::get_device(const std::string& device)
+{
+    for (auto d : fwk::GpDeviceList::obj()) {
+        if (d->get_path() == device) {
+            return fwk::Option<GpDevicePtr>(d);
+        }
     }
-    DBG_OUT("found %s %s", name, value);
-    push_back(GpDevice::Ptr(new GpDevice(name,value)));
-  }
-  ::gp_list_free(cameraList);
+    return fwk::Option<GpDevicePtr>();
+}
+
+class GpCamera::Priv {
+public:
+    Priv()
+        : camera(nullptr)
+        , context(::gp_context_new())
+        {
+        }
+    ~Priv()
+        {
+            if (camera) {
+                ::gp_camera_unref(camera);
+            }
+            ::gp_context_unref(context);
+        }
+    ::Camera* camera;
+    ::GPContext* context;
+};
+
+GpCamera::GpCamera(const GpDevicePtr& device)
+    : m_device(device)
+    , m_priv(new Priv)
+{
 }
 
+GpCamera::~GpCamera()
+{
+    delete m_priv;
+}
+
+bool GpCamera::open()
+{
+    bool success = false;
+    if (m_priv->camera) {
+        close();
+    }
+    ::gp_camera_new(&m_priv->camera);
+
+    CameraAbilities abilities;
+    auto al = fwk::GpDeviceList::obj().get_abilities_list();
+    int model_index = ::gp_abilities_list_lookup_model(al, m_device->get_model().c_str());
+       ::gp_abilities_list_get_abilities(al, model_index, &abilities);
+    ::gp_camera_set_abilities(m_priv->camera, abilities);
 
+       GPPortInfo info;
+    auto info_list = ::fwk::GpDeviceList::obj().get_port_info_list();
+    DBG_OUT("looking up port %s",  m_device->get_path().c_str());
+    int port_index = ::gp_port_info_list_lookup_path(info_list, m_device->get_path().c_str());
+    DBG_OUT("port index = %d", port_index);
+    if (port_index >= 0) {
+        ::gp_port_info_list_get_info(info_list, port_index, &info);
+        ::gp_camera_set_port_info(m_priv->camera, info);
 
+        int result = ::gp_camera_init(m_priv->camera, m_priv->context);
+        DBG_OUT("camera init returned %d", result);
+        switch (result) {
+        case GP_OK:
+            DBG_OUT("camera open");
+            success = true;
+            break;
+        case GP_ERROR_CANCEL:
+            break;
+        default:
+            close();
+        }
+    }
+
+    return success;
+}
+
+bool GpCamera::close()
+{
+    ::gp_camera_unref(m_priv->camera);
+    m_priv->camera = nullptr;
+    return true;
+}
+
+void GpCamera::process_folders(const std::vector<std::string>& folders,
+                               std::list<std::pair<std::string, std::string>>& files) const
+{
+    for (auto folder : folders) {
+        CameraList *flist;
+        ::gp_list_new(&flist);
+        int result = ::gp_camera_folder_list_files(m_priv->camera, folder.c_str(), flist,
+                                                m_priv->context);
+        DBG_OUT("listed folder %d", result);
+        if (result == GP_OK) {
+            int count = ::gp_list_count(flist);
+            DBG_OUT("processing folder %s, count %d", folder.c_str(), count);
+            for (int i = 0; i < count; i++) {
+                const char *name = nullptr;
+                ::gp_list_get_name(flist, i, &name);
+                files.push_back(std::make_pair(folder, name));
+            }
+        }
+        gp_list_unref(flist);
+    }
+}
+
+std::list<std::pair<std::string, std::string>> GpCamera::list_content() const
+{
+    std::list<std::pair<std::string, std::string>> files;
+
+    DBG_OUT("list content");
+    CameraList *list = nullptr;
+
+    // XXX fixme this should not be hardcoded.
+    std::string root_folder = "/store_00010001/DCIM";
+    ::gp_list_new(&list);
+    int result = gp_camera_folder_list_folders(m_priv->camera, root_folder.c_str(), list,
+                                               m_priv->context);
+
+    DBG_OUT("initial folder list %d", result);
+    if (result == GP_OK) {
+        std::vector<std::string> folders;
+        int count = gp_list_count(list);
+        DBG_OUT("list count %d", count);
+
+        for (int i = 0; i < count; i++) {
+            const char* name = nullptr;
+            ::gp_list_get_name(list, i, &name);
+            DBG_OUT("found folder %s", name);
+            folders.push_back(root_folder + "/" + name);
+        }
+
+        process_folders(folders, files);
+    }
+    ::gp_list_unref(list);
+    return files;
+}
+
+fwk::Thumbnail GpCamera::get_preview(const std::string& path) const
+{
+    fwk::Thumbnail thumbnail;
+    CameraFile* file;
+    std::string folder = fwk::path_dirname(path);
+    std::string name = fwk::path_basename(path);
+
+    ::gp_file_new(&file);
+    int result = ::gp_camera_file_get (m_priv->camera, folder.c_str(), name.c_str(),
+                                       GP_FILE_TYPE_PREVIEW, file,
+                                       m_priv->context);
+    DBG_OUT("file_get %s %d", path.c_str(), result);
+    if (result >= 0) {
+        const char *fd;
+        unsigned long fs;
+        ::gp_file_get_data_and_size(file, &fd, &fs);
+
+        Glib::RefPtr<Gdk::PixbufLoader> loader = Gdk::PixbufLoader::create();
+        loader->write(reinterpret_cast<const guint8*>(fd), fs);
+        loader->close();
+        thumbnail = fwk::Thumbnail(loader->get_pixbuf());
+    }
+    ::gp_file_unref(file);
+    return thumbnail;
+}
 
 }
diff --git a/src/fwk/utils/gphoto.hpp b/src/fwk/utils/gphoto.hpp
index 8a069d1..95f694f 100644
--- a/src/fwk/utils/gphoto.hpp
+++ b/src/fwk/utils/gphoto.hpp
@@ -1,3 +1,4 @@
+/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode:nil; -*- */
 /*
  * niepce - fwk/utils/gphoto.hpp
  *
@@ -17,79 +18,98 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
-#ifndef __FWK_GPHOTO_HPP_
-#define __FWK_GPHOTO_HPP_
+#pragma once
 
 #include <list>
 #include <string>
 #include <memory>
 
-extern "C" {
 #include <gphoto2-port-info-list.h>
 #include <gphoto2-abilities-list.h>
-}
 
-#include "fwk/base/util.hpp"
+#include "fwk/base/option.hpp"
 #include "fwk/base/singleton.hpp"
+#include "fwk/base/util.hpp"
 
 namespace fwk {
 
+class Thumbnail;
+
+/** Describe a gphoto device: model + port (path)
+ */
 class GpDevice
 {
 public:
-  NON_COPYABLE(GpDevice);
-
-  typedef std::shared_ptr<GpDevice> Ptr;
-
-  GpDevice(const std::string & model, const std::string & path);
-
-  const std::string & get_model() const
-    {
-      return m_model;
-    }
-  const std::string & get_path() const
-    {
-      return m_path;
-    }
+    NON_COPYABLE(GpDevice);
+
+    GpDevice(const std::string& model, const std::string& path);
+
+    const std::string& get_model() const
+        {
+            return m_model;
+        }
+    const std::string& get_path() const
+        {
+            return m_path;
+        }
 private:
-  std::string m_model;
-  std::string m_path;
+    std::string m_model;
+    std::string m_path;
 };
 
+typedef std::shared_ptr<GpDevice> GpDevicePtr;
 
-
+/** The device list from gphoto.
+ */
 class GpDeviceList
-  : public fwk::Singleton<GpDeviceList>
-  , public std::list<GpDevice::Ptr>
+    : public fwk::Singleton<GpDeviceList>
+    , public std::list<GpDevicePtr>
 {
 public:
-  ~GpDeviceList();
-
-  void reload();
-  void detect();
+    ~GpDeviceList();
+
+    void reload();
+    void detect();
+    ::CameraAbilitiesList* get_abilities_list() const
+        { return m_abilities; }
+    ::GPPortInfoList* get_port_info_list() const
+        { return m_ports; }
+    fwk::Option<GpDevicePtr> get_device(const std::string& device);
 protected:
-  friend class fwk::Singleton<GpDeviceList>;
-  GpDeviceList();
+    friend class fwk::Singleton<GpDeviceList>;
+    GpDeviceList();
 private:
 
-  void _gp_cleanup();
-  ::CameraAbilitiesList *m_abilities;
-  ::GPPortInfoList      *m_ports;
+    void gp_cleanup();
+    ::CameraAbilitiesList* m_abilities;
+    ::GPPortInfoList* m_ports;
 };
 
-
-
+/** A gphoto camera.
+ */
 class GpCamera
 {
 public:
-  GpCamera(const GpDevice::Ptr & device);
+    NON_COPYABLE(GpCamera);
+
+    GpCamera(const GpDevicePtr& device);
+    virtual ~GpCamera();
+
+    const std::string& path() const
+        { return m_device->get_path(); }
+    bool open();
+    bool close();
+    std::list<std::pair<std::string, std::string>> list_content() const;
+    fwk::Thumbnail get_preview(const std::string& path) const;
 
 private:
-  GpDevice::Ptr m_device;
+    void process_folders(const std::vector<std::string>& folders,
+                         std::list<std::pair<std::string, std::string>>& files) const;
+    class Priv;
+    GpDevicePtr m_device;
+    Priv* m_priv;
 };
 
-}
+typedef std::unique_ptr<GpCamera> GpCameraPtr;
 
-
-#endif
+}
diff --git a/src/niepce/Makefile.am b/src/niepce/Makefile.am
index 0802144..196424a 100644
--- a/src/niepce/Makefile.am
+++ b/src/niepce/Makefile.am
@@ -22,6 +22,7 @@ niepce_LDADD = \
        $(top_builddir)/src/ncr/libncr.a \
        $(top_builddir)/src/ext/libview/libview.a \
        @FRAMEWORK_LIBS@ \
+       @GPHOTO_LIBS@ \
        @BABL_LIBS@ \
        @GEGL_LIBS@ @OPENRAW_LIBS@
 
diff --git a/src/niepce/ui/Makefile.am b/src/niepce/ui/Makefile.am
index b659540..fad15e1 100644
--- a/src/niepce/ui/Makefile.am
+++ b/src/niepce/ui/Makefile.am
@@ -4,6 +4,7 @@ gladefiles = dialogs/preferences.ui \
        dialogs/importdialog.ui \
        dialogs/editlabels.ui \
        dialogs/importers/directoryimporterui.ui \
+       dialogs/importers/cameraimporterui.ui \
        $(NULL)
 
 gladedir = @datarootdir@/niepce/glade/
@@ -13,7 +14,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/ -I$(srcdir)/.. \
        -I$(top_srcdir)/src/ext \
        -DGLADEDIR=\"$(gladedir)\" \
        -DDATADIR=\"$(datadir)\" \
-       @FRAMEWORK_CFLAGS@
+       @FRAMEWORK_CFLAGS@ @GPHOTO_CFLAGS@ \
+       $(NULL)
 
 EXTRA_DIST = $(gladefiles)
 
diff --git a/src/niepce/ui/dialogs/importdialog.cpp b/src/niepce/ui/dialogs/importdialog.cpp
index a238adb..f3b2c33 100644
--- a/src/niepce/ui/dialogs/importdialog.cpp
+++ b/src/niepce/ui/dialogs/importdialog.cpp
@@ -172,9 +172,9 @@ void ImportDialog::append_files_to_import()
     std::list<std::string> paths;
     for(const auto & f : files_to_import) {
         DBG_OUT("selected %s", f->name().c_str());
-        paths.push_back(f->name());
+        paths.push_back(f->path());
         Gtk::TreeIter iter = m_images_list_model->append();
-        m_images_list_map.insert(std::make_pair(f->name(), iter));
+        m_images_list_map.insert(std::make_pair(f->path(), iter));
         iter->set_value(m_grid_columns.filename, Glib::ustring(f->name()));
         iter->set_value(m_grid_columns.file, std::move(f));
     }
diff --git a/src/niepce/ui/dialogs/importers/cameraimporterui.cpp 
b/src/niepce/ui/dialogs/importers/cameraimporterui.cpp
index 7b4d708..75c1f54 100644
--- a/src/niepce/ui/dialogs/importers/cameraimporterui.cpp
+++ b/src/niepce/ui/dialogs/importers/cameraimporterui.cpp
@@ -1,3 +1,4 @@
+/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode:nil; -*- */
 /*
  * niepce - ui/dialogs/importer/cameraimporterui.cpp
  *
@@ -19,7 +20,10 @@
 
 #include <glibmm/i18n.h>
 #include <gtkmm/label.h>
+#include <gtkmm/grid.h>
+#include <gtkmm/comboboxtext.h>
 
+#include "fwk/base/debug.hpp"
 #include "fwk/toolkit/frame.hpp"
 #include "engine/importer/cameraimporter.hpp"
 #include "cameraimporterui.hpp"
@@ -27,14 +31,50 @@
 namespace ui {
 
 CameraImporterUI::CameraImporterUI()
-  : ImporterUI(std::make_shared<eng::CameraImporter>(), _("Camera"))
+    : ImporterUI(std::make_shared<eng::CameraImporter>(), _("Camera"))
+    , m_camera_list_combo(nullptr)
+    , m_select_camera_btn(nullptr)
 {
 }
 
 Gtk::Widget* CameraImporterUI::setup_widget(const fwk::Frame::Ptr&)
 {
-  Gtk::Label* label = Gtk::manage(new Gtk::Label("camera! camera! camera!"));
-  return label;
+    Gtk::Grid* main_widget;
+    m_builder = Gtk::Builder::create_from_file(GLADEDIR"cameraimporterui.ui",
+                                               "main_widget");
+    m_builder->get_widget("main_widget", main_widget);
+    m_builder->get_widget("select_camera_btn", m_select_camera_btn);
+    m_select_camera_btn->signal_clicked()
+        .connect(sigc::mem_fun(*this, &CameraImporterUI::select_camera));
+    m_builder->get_widget("camera_list_combo", m_camera_list_combo);
+
+    fwk::GpDeviceList::obj().detect();
+
+    // XXX restore the selection from the preferences.
+    int i = 0;
+    for (auto device : fwk::GpDeviceList::obj()) {
+        m_camera_list_combo->append(device->get_path(), device->get_model());
+        if (i == 0) {
+            m_camera_list_combo->set_active_id(device->get_path());
+        }
+        i++;
+    }
+    if (i == 0) {
+        m_camera_list_combo->append("", _("No camera found"));
+        m_camera_list_combo->set_active_id("");
+        m_camera_list_combo->set_sensitive(false);
+        m_select_camera_btn->set_sensitive(false);
+    }
+
+    return main_widget;
+}
+
+void CameraImporterUI::select_camera()
+{
+    std::string source = m_camera_list_combo->get_active_id();
+    if (!source.empty() && m_source_selected_cb) {
+        m_source_selected_cb(source);
+    }
 }
 
 }
diff --git a/src/niepce/ui/dialogs/importers/cameraimporterui.hpp 
b/src/niepce/ui/dialogs/importers/cameraimporterui.hpp
index c72be2b..3fc8a35 100644
--- a/src/niepce/ui/dialogs/importers/cameraimporterui.hpp
+++ b/src/niepce/ui/dialogs/importers/cameraimporterui.hpp
@@ -1,3 +1,4 @@
+/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode:nil; -*- */
 /*
  * niepce - ui/dialogs/importer/cameraimporterui.hpp
  *
@@ -24,6 +25,7 @@
 
 namespace Gtk {
 class Widget;
+class ComboBoxText;
 }
 
 namespace fwk {
@@ -33,16 +35,17 @@ class Frame;
 namespace ui {
 
 class CameraImporterUI
-  : public ImporterUI
+    : public ImporterUI
 {
 public:
-  CameraImporterUI();
-
-  Gtk::Widget* setup_widget(const fwk::Frame::Ptr&) override;
+    CameraImporterUI();
 
+    Gtk::Widget* setup_widget(const fwk::Frame::Ptr&) override;
+    void select_camera();
 private:
-  std::string select_source();
-  void do_select_directories();
+
+    Gtk::ComboBoxText* m_camera_list_combo;
+    Gtk::Button* m_select_camera_btn;
 };
 
 }
diff --git a/src/niepce/ui/dialogs/importers/cameraimporterui.ui 
b/src/niepce/ui/dialogs/importers/cameraimporterui.ui
new file mode 100644
index 0000000..8144192
--- /dev/null
+++ b/src/niepce/ui/dialogs/importers/cameraimporterui.ui
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkOffscreenWindow">
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkGrid" id="main_widget">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkComboBoxText" id="camera_list_combo">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">start</property>
+            <property name="hexpand">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">start</property>
+            <property name="label" translatable="yes">Available cameras:</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="select_camera_btn">
+            <property name="label" translatable="yes">Select</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/src/niepce/ui/dialogs/importers/importerui.cpp b/src/niepce/ui/dialogs/importers/importerui.cpp
index ce146d8..ce7df85 100644
--- a/src/niepce/ui/dialogs/importers/importerui.cpp
+++ b/src/niepce/ui/dialogs/importers/importerui.cpp
@@ -1,3 +1,22 @@
+/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode:nil; -*- */
+/*
+ * niepce - ui/dialogs/importer/importerui.cpp
+ *
+ * Copyright (C) 2017 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/>.
+ */
 
 
 #include "fwk/toolkit/frame.hpp"
@@ -8,25 +27,25 @@ namespace ui {
 
 ImporterUI::ImporterUI(std::shared_ptr<eng::IImporter>&& importer,
                        const std::string& name)
-  : m_importer(importer)
-  , m_name(name)
+    : m_importer(importer)
+    , m_name(name)
 {
 }
 
 std::shared_ptr<eng::IImporter> ImporterUI::get_importer()
 {
-  return m_importer;
+    return m_importer;
 }
 
 const std::string& ImporterUI::id() const
 {
-  return m_importer->id();
+    return m_importer->id();
 }
 
 
 const std::string& ImporterUI::name() const
 {
-  return m_name;
+    return m_name;
 }
 
 
diff --git a/src/niepce/ui/dialogs/importers/importerui.hpp b/src/niepce/ui/dialogs/importers/importerui.hpp
index 5016673..8925b5f 100644
--- a/src/niepce/ui/dialogs/importers/importerui.hpp
+++ b/src/niepce/ui/dialogs/importers/importerui.hpp
@@ -1,3 +1,4 @@
+/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode:nil; -*- */
 /*
  * niepce - ui/dialogs/importer/importerui.hpp
  *
@@ -28,25 +29,26 @@ class Frame;
 namespace ui {
 
 class ImporterUI
-  : public IImporterUI
+    : public IImporterUI
+    , public sigc::trackable
 {
 public:
-  ImporterUI(std::shared_ptr<eng::IImporter>&& importer,
-             const std::string& name);
+    ImporterUI(std::shared_ptr<eng::IImporter>&& importer,
+               const std::string& name);
 
-  std::shared_ptr<eng::IImporter> get_importer() override;
+    std::shared_ptr<eng::IImporter> get_importer() override;
 
-  const std::string& name() const override;
-  const std::string& id() const override;
+    const std::string& name() const override;
+    const std::string& id() const override;
 
-  void set_source_selected_callback(const SourceSelected&) override;
+    void set_source_selected_callback(const SourceSelected&) override;
 
 protected:
-  std::shared_ptr<eng::IImporter> m_importer;
-  std::string m_name;
-  std::weak_ptr<fwk::Frame> m_frame;
-  Glib::RefPtr<Gtk::Builder> m_builder;
-  SourceSelected m_source_selected_cb;
+    std::shared_ptr<eng::IImporter> m_importer;
+    std::string m_name;
+    std::weak_ptr<fwk::Frame> m_frame;
+    Glib::RefPtr<Gtk::Builder> m_builder;
+    SourceSelected m_source_selected_cb;
 };
 
 }


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