Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 455bbf2d authored by camilo higuita's avatar camilo higuita

move to i18n fix issue #5 and start implementing the metadata info dialog with exiv2 for issue #4

parent 7c87e7f1
......@@ -10,9 +10,10 @@ find_package(ECM ${REQUIRED_KF5_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${ECM_MODULE_PATH})
set(CMAKE_CXX_STANDARD 17)
find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS Qml Quick Sql Svg QuickControls2 Widgets)
find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS Qml Quick Sql Svg QuickControls2 Positioning)
find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED COMPONENTS I18n Notifications Config KIO Attica)
find_package(MauiKit REQUIRED)
find_package(LibExiv2 0.21 REQUIRED)
include(KDEInstallDirs)
include(KDECompilerSettings NO_POLICY_SCOPE)
......@@ -31,6 +32,8 @@ if (ANDROID)
find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS AndroidExtras)
endif()
kde_enable_exceptions()
add_subdirectory(src)
install(FILES org.kde.pix.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
......
......@@ -3,6 +3,10 @@ set(pix_SRCS
pix.cpp
models/gallery/gallery.cpp
models/folders/folders.cpp
models/picinfomodel.cpp
utils/picinfo/exiv2extractor.cpp
utils/picinfo/reversegeocoder.cpp
utils/picinfo/kdtree.c
)
set(pix_HDRS
......@@ -11,6 +15,10 @@ set(pix_HDRS
utils/pic.h
models/gallery/gallery.h
models/folders/folders.h
models/picinfomodel.h
utils/picinfo/exiv2extractor.h
utils/picinfo/reversegeocoder.h
utils/picinfo/kdtree.h
)
set(pix_ASSETS
......@@ -24,7 +32,7 @@ add_executable(pix
${pix_ASSETS}
)
target_link_libraries(pix MauiKit Qt5::Sql Qt5::Qml Qt5::Widgets Qt5::Svg KF5::ConfigCore KF5::Notifications KF5::KIOCore KF5::I18n KF5::Attica)
target_link_libraries(pix MauiKit Qt5::Sql Qt5::Qml Qt5::Svg Qt5::Positioning KF5::I18n LibExiv2::LibExiv2)
if(ANDROID)
target_link_libraries(pix Qt5::AndroidExtras)
......@@ -36,3 +44,51 @@ install(FILES org.kde.pix.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
#TODO: port to ecm_install_icons()
install(FILES assets/pix.svg DESTINATION ${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps)
#
# Reverse GeoLookup Data
#
# Packagers can download the file and put it in the tarball
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/cities1000.zip)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/cities1000.zip DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
endif()
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/cities1000.zip)
file (DOWNLOAD "http://download.geonames.org/export/dump/cities1000.zip"
${CMAKE_CURRENT_BINARY_DIR}/cities1000.zip
SHOW_PROGRESS
)
endif()
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar -xzf ${CMAKE_CURRENT_BINARY_DIR}/cities1000.zip
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/admin1CodesASCII.txt)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/admin1CodesASCII.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
endif()
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/admin1CodesASCII.txt)
file (DOWNLOAD "http://download.geonames.org/export/dump/admin1CodesASCII.txt"
${CMAKE_CURRENT_BINARY_DIR}/admin1CodesASCII.txt
SHOW_PROGRESS
)
endif()
file(RENAME ${CMAKE_CURRENT_BINARY_DIR}/admin1CodesASCII.txt ${CMAKE_CURRENT_BINARY_DIR}/admin1Codes.txt)
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/admin2Codes.txt)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/admin2Codes.txt DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
endif()
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/admin2Codes.txt)
file (DOWNLOAD "http://download.geonames.org/export/dump/admin2Codes.txt"
${CMAKE_CURRENT_BINARY_DIR}/admin2Codes.txt
SHOW_PROGRESS
)
endif()
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/cities1000.txt DESTINATION ${DATA_INSTALL_DIR}/koko)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/admin1Codes.txt DESTINATION ${DATA_INSTALL_DIR}/koko)
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/admin2Codes.txt DESTINATION ${DATA_INSTALL_DIR}/koko)
install (FILES countries.csv DESTINATION ${DATA_INSTALL_DIR}/koko)
......@@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QQmlApplicationEngine>
#include <QCommandLineParser>
#include <QQmlContext>
#include <QIcon>
#include <QFileInfo>
#include "pix.h"
......@@ -48,11 +49,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "models/gallery/gallery.h"
//#include "models/cloud/cloud.h"
#include "models/folders/folders.h"
#include "models/picinfomodel.h"
#ifdef Q_OS_MACOS
#include "mauimacos.h"
#endif
#include <KI18n/KLocalizedContext>
static const QList<QUrl> getFolderImages(const QString &path)
{
QList<QUrl> urls;
......@@ -144,7 +148,10 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
});
qmlRegisterType<Gallery>("GalleryList", 1, 0, "GalleryList");
qmlRegisterType<Folders>("FoldersList", 1, 0, "FoldersList");
qmlRegisterType<Folders>("FoldersList", 1, 0, "FoldersList");
qmlRegisterType<PicInfoModel>("Pix", 1, 0, "PicInfoModel");
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
#ifdef STATIC_KIRIGAMI
KirigamiPlugin::getInstance().registerTypes();
......
This diff is collapsed.
#include "picinfomodel.h"
#include "utils/picinfo/exiv2extractor.h"
#include "utils/picinfo/reversegeocoder.h"
#include <QGeoAddress>
PicInfoModel::PicInfoModel(QObject *parent) : MauiList(parent)
, m_geoCoder( new ReverseGeoCoder)
{
}
PicInfoModel::~PicInfoModel()
{
delete m_geoCoder;
}
static FMH::MODEL_LIST basicInfo(const QUrl &url)
{
FMH::MODEL_LIST res;
const FMH::MODEL model = FMH::getFileInfoModel(url);
for(const auto key : model.keys())
{
FMH::MODEL m_model;
auto value = FMH::MODEL_NAME[key];
m_model.insert(FMH::MODEL_KEY::KEY, value.replace(0, 1, value[0].toUpper()));
m_model.insert(FMH::MODEL_KEY::VALUE, model[key]);
res << m_model;
}
return res;
}
void PicInfoModel::parse()
{
emit preListChanged();
m_data.clear();
m_data << basicInfo(m_url);
FMH::MODEL model;
Exiv2Extractor extractor;
extractor.extract(m_url.toLocalFile());
if (extractor.error())
{
return;
}
double latitude = extractor.gpsLatitude();
double longitude = extractor.gpsLongitude();
if (latitude != 0.0 && longitude != 0.0)
{
if (!m_geoCoder->initialized())
{
m_geoCoder->init();
}
QVariantMap map = m_geoCoder->lookup(latitude, longitude);
QGeoAddress addr;
addr.setCountry(map.value("country").toString());
addr.setState(map.value("admin1").toString());
addr.setCity(map.value("admin2").toString());
m_data << FMH::MODEL{{FMH::MODEL_KEY::KEY, "Location"}, {FMH::MODEL_KEY::VALUE, addr.text()}};
}
m_data << FMH::MODEL{{FMH::MODEL_KEY::KEY, "Origin"}, {FMH::MODEL_KEY::VALUE, extractor.dateTime().toString()}};
qDebug()<< "File info ready" << m_data;
emit postListChanged();
}
FMH::MODEL_LIST PicInfoModel::items() const
{
return m_data;
}
#ifndef PICINFOMODEL_H
#define PICINFOMODEL_H
#include <QObject>
#include <QAbstractListModel>
#include <MauiKit/mauilist.h>
class ReverseGeoCoder;
class PicInfoModel : public MauiList
{
Q_OBJECT
Q_PROPERTY (QUrl url READ url WRITE setUrl NOTIFY urlChanged)
public:
enum ROLES
{
KEY,
VALUE
};
explicit PicInfoModel(QObject *parent = nullptr);
~PicInfoModel();
QUrl url() const
{
return m_url;
}
public slots:
void setUrl(QUrl url)
{
if(!FMH::fileExists(url))
{
return;
}
if (m_url == url)
return;
m_url = url;
emit urlChanged(m_url);
this->parse();
}
private:
QUrl m_url;
FMH::MODEL_LIST m_data;
ReverseGeoCoder* m_geoCoder;
void parse();
signals:
void urlChanged(QUrl url);
// MauiList interface
public:
FMH::MODEL_LIST items() const override;
};
#endif // PICINFOMODEL_H
......@@ -19,5 +19,6 @@
<file>widgets/views/Cloud/CloudView.qml</file>
<file>widgets/views/Store/StoreView.qml</file>
<file>view_models/PixPicList.qml</file>
<file>widgets/InfoDialog.qml</file>
</qresource>
</RCC>
/*
Copyright (C) 2012-15 Vishesh Handa <vhanda@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "exiv2extractor.h"
#include <QDebug>
#include <QFile>
Exiv2Extractor::Exiv2Extractor()
: m_latitude(0)
, m_longitude(0)
, m_error(true)
{
}
static QDateTime dateTimeFromString(const QString& dateString)
{
QDateTime dateTime;
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy-MM-dd"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("dd-MM-yyyy"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy-MM"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("MM-yyyy"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy.MM.dd"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("dd.MM.yyyy"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("dd MMMM yyyy"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("MM.yyyy"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy.MM"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("yy"));
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, Qt::ISODate);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("dddd d MMM yyyy h':'mm':'ss AP"));
dateTime.setTimeSpec(Qt::LocalTime);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, QStringLiteral("yyyy:MM:dd hh:mm:ss"));
dateTime.setTimeSpec(Qt::LocalTime);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, Qt::SystemLocaleDate);
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, Qt::SystemLocaleShortDate);
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
dateTime = QDateTime::fromString(dateString, Qt::SystemLocaleLongDate);
dateTime.setTimeSpec(Qt::UTC);
}
if (!dateTime.isValid()) {
qWarning() << "Could not determine correct datetime format from:" << dateString;
return QDateTime();
}
return dateTime;
}
static QDateTime toDateTime(const Exiv2::Value& value)
{
if (value.typeId() == Exiv2::asciiString) {
QDateTime val = dateTimeFromString(value.toString().c_str());
if (val.isValid()) {
// Datetime is stored in exif as local time.
val.setOffsetFromUtc(0);
return val;
}
}
return QDateTime();
}
void Exiv2Extractor::extract(const QString& filePath)
{
QByteArray arr = QFile::encodeName(filePath);
std::string fileString(arr.data(), arr.length());
Exiv2::LogMsg::setLevel(Exiv2::LogMsg::mute);
#if EXIV2_TEST_VERSION(0, 27, 99)
Exiv2::Image::UniquePtr image;
#else
Exiv2::Image::AutoPtr image;
#endif
try {
image = Exiv2::ImageFactory::open(fileString);
} catch (const std::exception&) {
return;
}
if (!image.get()) {
return;
}
if (!image->good()) {
return;
}
try {
image->readMetadata();
} catch (const std::exception&) {
return;
}
const Exiv2::ExifData& data = image->exifData();
Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey("Exif.Photo.DateTimeOriginal"));
if (it != data.end()) {
m_dateTime = toDateTime(it->value());
}
if (m_dateTime.isNull()) {
it = data.findKey(Exiv2::ExifKey("Exif.Image.DateTime"));
if (it != data.end()) {
m_dateTime = toDateTime(it->value());
}
}
m_latitude = fetchGpsDouble(data, "Exif.GPSInfo.GPSLatitude");
m_longitude = fetchGpsDouble(data, "Exif.GPSInfo.GPSLongitude");
QByteArray latRef = fetchByteArray(data, "Exif.GPSInfo.GPSLatitudeRef");
if (!latRef.isEmpty() && latRef[0] == 'S')
m_latitude *= -1;
QByteArray longRef = fetchByteArray(data, "Exif.GPSInfo.GPSLongitudeRef");
if (!longRef.isEmpty() && longRef[0] == 'W')
m_longitude *= -1;
m_error = false;
}
double Exiv2Extractor::fetchGpsDouble(const Exiv2::ExifData& data, const char* name)
{
Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name));
if (it != data.end() && it->count() == 3) {
double n = 0.0;
double d = 0.0;
n = (*it).toRational(0).first;
d = (*it).toRational(0).second;
if (d == 0) {
return 0.0;
}
double deg = n / d;
n = (*it).toRational(1).first;
d = (*it).toRational(1).second;
if (d == 0) {
return deg;
}
double min = n / d;
if (min != -1.0) {
deg += min / 60.0;
}
n = (*it).toRational(2).first;
d = (*it).toRational(2).second;
if (d == 0) {
return deg;
}
double sec = n / d;
if (sec != -1.0) {
deg += sec / 3600.0;
}
return deg;
}
return 0.0;
}
QByteArray Exiv2Extractor::fetchByteArray(const Exiv2::ExifData& data, const char* name)
{
Exiv2::ExifData::const_iterator it = data.findKey(Exiv2::ExifKey(name));
if (it != data.end()) {
std::string str = it->value().toString();
return QByteArray(str.c_str(), str.size());
}
return QByteArray();
}
bool Exiv2Extractor::error() const
{
return m_error;
}
/*
Copyright (C) 2012-15 Vishesh Handa <vhanda@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef EXIV2EXTRACTOR_H
#define EXIV2EXTRACTOR_H
#include <exiv2/exiv2.hpp>
#include <QString>
#include <QDateTime>
class Exiv2Extractor
{
public:
Exiv2Extractor();
void extract(const QString& filePath);
double gpsLatitude() { return m_latitude; }
double gpsLongitude() { return m_longitude; }
QDateTime dateTime() { return m_dateTime; }
bool error() const;
private:
double fetchGpsDouble(const Exiv2::ExifData& data, const char* name);
QByteArray fetchByteArray(const Exiv2::ExifData& data, const char* name);
double m_latitude;
double m_longitude;
QDateTime m_dateTime;
bool m_error;
};
#endif // EXIV2EXTRACTOR_H
This diff is collapsed.
/*
This file is part of ``kdtree'', a library for working with kd-trees.
Copyright (C) 2007-2011 John Tsiombikas <nuclear@member.fsf.org>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#ifndef KOKO_KDTREE_H_
#define KOKO_KDTREE_H_
#ifdef __cplusplus
extern "C" {
#endif
struct kdtree;
struct kdres;
/* create a kd-tree for "k"-dimensional data */
struct kdtree *kd_create(int k);
/* free the struct kdtree */
void kd_free(struct kdtree *tree);
/* remove all the elements from the tree */
void kd_clear(struct kdtree *tree);
/* if called with non-null 2nd argument, the function provided
* will be called on data pointers (see kd_insert) when nodes
* are to be removed from the tree.
*/
void kd_data_destructor(struct kdtree *tree, void (*destr)(void*));
/* insert a node, specifying its position, and optional data */
int kd_insert(struct kdtree *tree, const double *pos, void *data);
int kd_insertf(struct kdtree *tree, const float *pos, void *data);
int kd_insert3(struct kdtree *tree, double x, double y, double z, void *data);
int kd_insert3f(struct kdtree *tree, float x, float y, float z, void *data);
/* Find the nearest node from a given point.
*
* This function returns a pointer to a result set with at most one element.
*/
struct kdres *kd_nearest(struct kdtree *tree, const double *pos);
struct kdres *kd_nearestf(struct kdtree *tree, const float *pos);
struct kdres *kd_nearest3(struct kdtree *tree, double x, double y, double z);
struct kdres *kd_nearest3f(struct kdtree *tree, float x, float y, float z);
/* Find the N nearest nodes from a given point.
*
* This function returns a pointer to a result set, with at most N elements,
* which can be manipulated with the kd_res_* functions.
* The returned pointer can be null as an indication of an error. Otherwise
* a valid result set is always returned which may contain 0 or more elements.
* The result set must be deallocated with kd_res_free after use.
*/
/*
struct kdres *kd_nearest_n(struct kdtree *tree, const double *pos, int num);
struct kdres *kd_nearest_nf(struct kdtree *tree, const float *pos, int num);
struct kdres *kd_nearest_n3(struct kdtree *tree, double x, double y, double z);
struct kdres *kd_nearest_n3f(struct kdtree *tree, float x, float y, float z);
*/
/* Find any nearest nodes from a given point within a range.
*
* This function returns a pointer to a result set, which can be manipulated
* by the kd_res_* functions.
* The returned pointer can be null as an indication of an error. Otherwise
* a valid result set is always returned which may contain 0 or more elements.
* The result set must be deallocated with kd_res_free after use.
*/
struct kdres *kd_nearest_range(struct kdtree *tree, const double *pos, double range);
struct kdres *kd_nearest_rangef(struct kdtree *tree, const float *pos, float range);
struct kdres *kd_nearest_range3(struct kdtree *tree, double x, double y, double z, double range);
struct kdres *kd_nearest_range3f(struct kdtree *tree, float x, float y, float z, float range);
/* frees a result set returned by kd_nearest_range() */
void kd_res_free(struct kdres *set);
/* returns the size of the result set (in elements) */
int kd_res_size(struct kdres *set);