diff options
Diffstat (limited to 'qthelper.cpp')
-rw-r--r-- | qthelper.cpp | 673 |
1 files changed, 621 insertions, 52 deletions
diff --git a/qthelper.cpp b/qthelper.cpp index acb1e103a..bbe908e6f 100644 --- a/qthelper.cpp +++ b/qthelper.cpp @@ -1,6 +1,15 @@ #include "qthelper.h" +#include "helpers.h" #include "gettextfromc.h" #include "statistics.h" +#include "usersurvey.h" +#include "membuffer.h" +#include "subsurfacesysinfo.h" +#include "version.h" +#include "divecomputer.h" +#include "time.h" +#include "gettextfromc.h" +#include <sys/time.h> #include <exif.h> #include "file.h" #include <QFile> @@ -8,8 +17,42 @@ #include <QDir> #include <QDebug> #include <QSettings> +#include <QStandardPaths> +#include <QJsonDocument> +#include <QJsonArray> +#include <QJsonObject> +#include <QNetworkReply> +#include <QNetworkRequest> +#include <QNetworkAccessManager> +#include <QUrlQuery> +#include <QEventLoop> +#include <QDateTime> +#include <QSaveFile> +#include <QDir> +#include <QImageReader> +#include <QtConcurrent> +#include "divepicturewidget.h" + #include <libxslt/documents.h> +const char *existing_filename; +static QString shortDateFormat; +static QString dateFormat; +static QString timeFormat; +static QLocale loc; + +#if defined(Q_OS_WIN) && QT_VERSION < 0x050000 +static QByteArray encodeUtf8(const QString &fname) +{ + return fname.toUtf8(); +} + +static QString decodeUtf8(const QByteArray &fname) +{ + return QString::fromUtf8(fname); +} +#endif + #define translate(_context, arg) trGettext(arg) static const QString DEGREE_SIGNS("dD" UTF8_DEGREE); @@ -31,7 +74,7 @@ QString weight_string(int weight_in_grams) return (str); } -QString printGPSCoords(int lat, int lon) +extern "C" const char *printGPSCoords(int lat, int lon) { unsigned int latdeg, londeg; unsigned int latmin, lonmin; @@ -39,24 +82,31 @@ QString printGPSCoords(int lat, int lon) QString lath, lonh, result; if (!lat && !lon) - return QString(); - - lath = lat >= 0 ? translate("gettextFromC", "N") : translate("gettextFromC", "S"); - lonh = lon >= 0 ? translate("gettextFromC", "E") : translate("gettextFromC", "W"); - lat = abs(lat); - lon = abs(lon); - latdeg = lat / 1000000U; - londeg = lon / 1000000U; - latmin = (lat % 1000000U) * 60U; - lonmin = (lon % 1000000U) * 60U; - latsec = (latmin % 1000000) * 60; - lonsec = (lonmin % 1000000) * 60; - result.sprintf("%u%s%02d\'%06.3f\"%s %u%s%02d\'%06.3f\"%s", - latdeg, UTF8_DEGREE, latmin / 1000000, latsec / 1000000, lath.toUtf8().data(), - londeg, UTF8_DEGREE, lonmin / 1000000, lonsec / 1000000, lonh.toUtf8().data()); - return result; + return strdup(""); + + if (prefs.coordinates_traditional) { + lath = lat >= 0 ? translate("gettextFromC", "N") : translate("gettextFromC", "S"); + lonh = lon >= 0 ? translate("gettextFromC", "E") : translate("gettextFromC", "W"); + lat = abs(lat); + lon = abs(lon); + latdeg = lat / 1000000U; + londeg = lon / 1000000U; + latmin = (lat % 1000000U) * 60U; + lonmin = (lon % 1000000U) * 60U; + latsec = (latmin % 1000000) * 60; + lonsec = (lonmin % 1000000) * 60; + result.sprintf("%u%s%02d\'%06.3f\"%s %u%s%02d\'%06.3f\"%s", + latdeg, UTF8_DEGREE, latmin / 1000000, latsec / 1000000, lath.toUtf8().data(), + londeg, UTF8_DEGREE, lonmin / 1000000, lonsec / 1000000, lonh.toUtf8().data()); + } else { + result.sprintf("%f %f", (double) lat / 1000000.0, (double) lon / 1000000.0); + } + return strdup(result.toUtf8().data()); } +/** +* Try to parse in a generic manner a coordinate. +*/ static bool parseCoord(const QString& txt, int& pos, const QString& positives, const QString& negatives, const QString& others, double& value) @@ -78,7 +128,7 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives, numberDefined = true; posBeforeNumber = pos; pos += numberRe.cap(1).size() - 1; - } else if (positives.indexOf(txt[pos].toUpper()) >= 0) { + } else if (positives.indexOf(txt[pos]) >= 0) { if (sign != 0) return false; sign = 1; @@ -88,7 +138,7 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives, ++pos; break; } - } else if (negatives.indexOf(txt[pos].toUpper()) >= 0) { + } else if (negatives.indexOf(txt[pos]) >= 0) { if (sign != 0) { if (others.indexOf(txt[pos]) >= 0) //special case for the '-' sign => next coordinate @@ -102,10 +152,11 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives, ++pos; break; } - } else if (others.indexOf(txt[pos].toUpper()) >= 0) { + } else if (others.indexOf(txt[pos]) >= 0) { //we are at the next coordinate. break; - } else if (DEGREE_SIGNS.indexOf(txt[pos]) >= 0) { + } else if (DEGREE_SIGNS.indexOf(txt[pos]) >= 0 || + (txt[pos].isSpace() && !degreesDefined && numberDefined)) { if (!numberDefined) return false; if (degreesDefined) { @@ -117,20 +168,18 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives, value += number; numberDefined = false; degreesDefined = true; - } else if (txt[pos] == '\'') { + } else if (txt[pos] == '\'' || (txt[pos].isSpace() && !minutesDefined && numberDefined)) { if (!numberDefined || minutesDefined) return false; value += number / 60.0; numberDefined = false; minutesDefined = true; - } else if (txt[pos] == '"') { + } else if (txt[pos] == '"' || (txt[pos].isSpace() && !secondsDefined && numberDefined)) { if (!numberDefined || secondsDefined) return false; value += number / 3600.0; numberDefined = false; secondsDefined = true; - } else if (txt[pos] == ' ' || txt[pos] == '\t') { - //ignore spaces } else { return false; } @@ -138,33 +187,56 @@ static bool parseCoord(const QString& txt, int& pos, const QString& positives, } if (!degreesDefined && numberDefined) { value = number; //just a single number => degrees - numberDefined = false; - degreesDefined = true; - } - if (!degreesDefined || numberDefined) + } else if (!minutesDefined && numberDefined) { + value += number / 60.0; + } else if (!secondsDefined && numberDefined) { + value += number / 3600.0; + } else if (numberDefined) { return false; + } if (sign == -1) value *= -1.0; return true; } +/** +* Parse special coordinate formats that cannot be handled by parseCoord. +*/ +static bool parseSpecialCoords(const QString& txt, double& latitude, double& longitude) { + QRegExp xmlFormat("(-?\\d+(?:\\.\\d+)?)\\s+(-?\\d+(?:\\.\\d+)?)"); + if (xmlFormat.exactMatch(txt)) { + latitude = xmlFormat.cap(1).toDouble(); + longitude = xmlFormat.cap(2).toDouble(); + return true; + } + return false; +} + bool parseGpsText(const QString &gps_text, double *latitude, double *longitude) { - const QString trimmed = gps_text.trimmed(); - if (trimmed.isEmpty()) { + static const QString POS_LAT = QString("+N") + translate("gettextFromC", "N"); + static const QString NEG_LAT = QString("-S") + translate("gettextFromC", "S"); + static const QString POS_LON = QString("+E") + translate("gettextFromC", "E"); + static const QString NEG_LON = QString("-W") + translate("gettextFromC", "W"); + + //remove the useless spaces (but keep the ones separating numbers) + static const QRegExp SPACE_CLEANER("\\s*([" + POS_LAT + NEG_LAT + POS_LON + + NEG_LON + DEGREE_SIGNS + "'\"\\s])\\s*"); + const QString normalized = gps_text.trimmed().toUpper().replace(SPACE_CLEANER, "\\1"); + + if (normalized.isEmpty()) { *latitude = 0.0; *longitude = 0.0; return true; } + if (parseSpecialCoords(normalized, *latitude, *longitude)) + return true; int pos = 0; - static const QString POS_LAT = QString("+N") + translate("gettextFromC", "N"); - static const QString NEG_LAT = QString("-S") + translate("gettextFromC", "S"); - static const QString POS_LON = QString("+E") + translate("gettextFromC", "E"); - static const QString NEG_LON = QString("-W") + translate("gettextFromC", "W"); - return parseCoord(gps_text, pos, POS_LAT, NEG_LAT, POS_LON + NEG_LON, *latitude) && - parseCoord(gps_text, pos, POS_LON, NEG_LON, "", *longitude) && - pos == gps_text.size(); + return parseCoord(normalized, pos, POS_LAT, NEG_LAT, POS_LON + NEG_LON, *latitude) && + parseCoord(normalized, pos, POS_LON, NEG_LON, "", *longitude) && + pos == normalized.size(); } +#if 0 // we'll need something like this for the dive site management, eventually bool gpsHasChanged(struct dive *dive, struct dive *master, const QString &gps_text, bool *parsed_out) { double latitude, longitude; @@ -193,6 +265,7 @@ bool gpsHasChanged(struct dive *dive, struct dive *master, const QString &gps_te dive->longitude.udeg = longudeg; return true; } +#endif QList<int> getDivesInTrip(dive_trip_t *trip) { @@ -235,9 +308,12 @@ int dive_getUniqID(struct dive *d) static xmlDocPtr get_stylesheet_doc(const xmlChar *uri, xmlDictPtr, int, void *, xsltLoadType) { QFile f(QLatin1String(":/xslt/") + (const char *)uri); - if (!f.open(QIODevice::ReadOnly)) - return NULL; - + if (!f.open(QIODevice::ReadOnly)) { + if (verbose > 0) { + qDebug() << "cannot open stylesheet" << QLatin1String(":/xslt/") + (const char *)uri; + return NULL; + } + } /* Load and parse the data */ QByteArray source = f.readAll(); @@ -265,22 +341,41 @@ extern "C" xsltStylesheetPtr get_stylesheet(const char *name) return xslt; } -extern "C" void picture_load_exif_data(struct picture *p, timestamp_t *timestamp) + +extern "C" timestamp_t picture_get_timestamp(char *filename) { EXIFInfo exif; memblock mem; + int retval; - if (readfile(p->filename, &mem) <= 0) - goto picture_load_exit; - if (exif.parseFrom((const unsigned char *)mem.buffer, (unsigned)mem.size) != PARSE_EXIF_SUCCESS) - goto picture_load_exit; - *timestamp = exif.epoch(); - p->longitude.udeg= lrint(1000000.0 * exif.GeoLocation.Longitude); - p->latitude.udeg = lrint(1000000.0 * exif.GeoLocation.Latitude); - -picture_load_exit: + // filename might not be the actual filename, so let's go via the hash. + if (readfile(localFilePath(QString(filename)).toUtf8().data(), &mem) <= 0) + return 0; + retval = exif.parseFrom((const unsigned char *)mem.buffer, (unsigned)mem.size); free(mem.buffer); - return; + if (retval != PARSE_EXIF_SUCCESS) + return 0; + return exif.epoch(); +} + +extern "C" const char *system_default_directory(void) +{ + static char filename[PATH_MAX]; + + if (!*filename) { + enum QStandardPaths::StandardLocation location; +#if QT_VERSION >= 0x050400 + location = QStandardPaths::AppDataLocation; +#else + location = QStandardPaths::DataLocation; +#endif + QString name = QStandardPaths::standardLocations(location).first(); + QDir dir(name); + dir.mkpath(name); + // Why no "dir.encodeName()"? Crazy Qt + strncpy(filename, QFile::encodeName(name), PATH_MAX-1); + } + return filename; } extern "C" char *get_file_name(const char *fileName) @@ -340,3 +435,477 @@ void selectedDivesGasUsed(QVector<QPair<QString, int> > &gasUsedOrdered) } qSort(gasUsedOrdered.begin(), gasUsedOrdered.end(), lessThan); } + +QString getUserAgent() +{ + QString arch; + // fill in the system data - use ':' as separator + // replace all other ':' with ' ' so that this is easy to parse + QString userAgent = QString("Subsurface:%1:").arg(subsurface_version()); + userAgent.append(SubsurfaceSysInfo::prettyOsName().replace(':', ' ') + ":"); + arch = SubsurfaceSysInfo::buildCpuArchitecture().replace(':', ' '); + userAgent.append(arch); + if (arch == "i386") + userAgent.append("/" + SubsurfaceSysInfo::currentCpuArchitecture()); + userAgent.append(":" + uiLanguage(NULL)); + return userAgent; + +} + +QString uiLanguage(QLocale *callerLoc) +{ + QSettings s; + s.beginGroup("Language"); + + if (!s.value("UseSystemLanguage", true).toBool()) { + loc = QLocale(s.value("UiLanguage", QLocale().uiLanguages().first()).toString()); + } else { + loc = QLocale(QLocale().uiLanguages().first()); + } + + QString uiLang = loc.uiLanguages().first(); + s.endGroup(); + + // there's a stupid Qt bug on MacOS where uiLanguages doesn't give us the country info + if (!uiLang.contains('-') && uiLang != loc.bcp47Name()) { + QLocale loc2(loc.bcp47Name()); + loc = loc2; + uiLang = loc2.uiLanguages().first(); + } + if (callerLoc) + *callerLoc = loc; + + // the short format is fine + // the long format uses long weekday and month names, so replace those with the short ones + // for time we don't want the time zone designator and don't want leading zeroes on the hours + shortDateFormat = loc.dateFormat(QLocale::ShortFormat); + dateFormat = loc.dateFormat(QLocale::LongFormat); + dateFormat.replace("dddd,", "ddd").replace("dddd", "ddd").replace("MMMM", "MMM"); + // special hack for Swedish as our switching from long weekday names to short weekday names + // messes things up there + dateFormat.replace("'en' 'den' d:'e'", " d"); + timeFormat = loc.timeFormat(); + timeFormat.replace("(t)", "").replace(" t", "").replace("t", "").replace("hh", "h").replace("HH", "H").replace("'kl'.", ""); + timeFormat.replace(".ss", "").replace(":ss", "").replace("ss", ""); + return uiLang; +} + +QLocale getLocale() +{ + return loc; +} + +QString getDateFormat() +{ + return dateFormat; +} +void set_filename(const char *filename, bool force) +{ + if (!force && existing_filename) + return; + free((void *)existing_filename); + if (filename) + existing_filename = strdup(filename); + else + existing_filename = NULL; +} + +const QString get_dc_nickname(const char *model, uint32_t deviceid) +{ + const DiveComputerNode *existNode = dcList.getExact(model, deviceid); + + if (existNode && !existNode->nickName.isEmpty()) + return existNode->nickName; + else + return model; +} + +QString get_depth_string(int mm, bool showunit, bool showdecimal) +{ + if (prefs.units.length == units::METERS) { + double meters = mm / 1000.0; + return QString("%1%2").arg(meters, 0, 'f', (showdecimal && meters < 20.0) ? 1 : 0).arg(showunit ? translate("gettextFromC", "m") : ""); + } else { + double feet = mm_to_feet(mm); + return QString("%1%2").arg(feet, 0, 'f', 0).arg(showunit ? translate("gettextFromC", "ft") : ""); + } +} + +QString get_depth_string(depth_t depth, bool showunit, bool showdecimal) +{ + return get_depth_string(depth.mm, showunit, showdecimal); +} + +QString get_depth_unit() +{ + if (prefs.units.length == units::METERS) + return QString("%1").arg(translate("gettextFromC", "m")); + else + return QString("%1").arg(translate("gettextFromC", "ft")); +} + +QString get_weight_string(weight_t weight, bool showunit) +{ + QString str = weight_string(weight.grams); + if (get_units()->weight == units::KG) { + str = QString("%1%2").arg(str).arg(showunit ? translate("gettextFromC", "kg") : ""); + } else { + str = QString("%1%2").arg(str).arg(showunit ? translate("gettextFromC", "lbs") : ""); + } + return (str); +} + +QString get_weight_unit() +{ + if (prefs.units.weight == units::KG) + return QString("%1").arg(translate("gettextFromC", "kg")); + else + return QString("%1").arg(translate("gettextFromC", "lbs")); +} + +/* these methods retrieve used gas per cylinder */ +static unsigned start_pressure(cylinder_t *cyl) +{ + return cyl->start.mbar ?: cyl->sample_start.mbar; +} + +static unsigned end_pressure(cylinder_t *cyl) +{ + return cyl->end.mbar ?: cyl->sample_end.mbar; +} + +QString get_cylinder_used_gas_string(cylinder_t *cyl, bool showunit) +{ + int decimals; + const char *unit; + double gas_usage; + /* Get the cylinder gas use in mbar */ + gas_usage = start_pressure(cyl) - end_pressure(cyl); + /* Can we turn it into a volume? */ + if (cyl->type.size.mliter) { + gas_usage = bar_to_atm(gas_usage / 1000); + gas_usage *= cyl->type.size.mliter; + gas_usage = get_volume_units(gas_usage, &decimals, &unit); + } else { + gas_usage = get_pressure_units(gas_usage, &unit); + decimals = 0; + } + // translate("gettextFromC","%.*f %s" + return QString("%1 %2").arg(gas_usage, 0, 'f', decimals).arg(showunit ? unit : ""); +} + +QString get_temperature_string(temperature_t temp, bool showunit) +{ + if (temp.mkelvin == 0) { + return ""; //temperature not defined + } else if (prefs.units.temperature == units::CELSIUS) { + double celsius = mkelvin_to_C(temp.mkelvin); + return QString("%1%2%3").arg(celsius, 0, 'f', 1).arg(showunit ? (UTF8_DEGREE) : "").arg(showunit ? translate("gettextFromC", "C") : ""); + } else { + double fahrenheit = mkelvin_to_F(temp.mkelvin); + return QString("%1%2%3").arg(fahrenheit, 0, 'f', 1).arg(showunit ? (UTF8_DEGREE) : "").arg(showunit ? translate("gettextFromC", "F") : ""); + } +} + +QString get_temp_unit() +{ + if (prefs.units.temperature == units::CELSIUS) + return QString(UTF8_DEGREE "C"); + else + return QString(UTF8_DEGREE "F"); +} + +QString get_volume_string(volume_t volume, bool showunit, int mbar) +{ + const char *unit; + int decimals; + double value = get_volume_units(volume.mliter, &decimals, &unit); + if (mbar) { + // we are showing a tank size + // fix the weird imperial way of denominating size and provide + // reasonable number of decimals + if (prefs.units.volume == units::CUFT) + value *= bar_to_atm(mbar / 1000.0); + decimals = (value > 20.0) ? 0 : (value > 2.0) ? 1 : 2; + } + return QString("%1%2").arg(value, 0, 'f', decimals).arg(showunit ? unit : ""); +} + +QString get_volume_unit() +{ + const char *unit; + (void) get_volume_units(0, NULL, &unit); + return QString(unit); +} + +QString get_pressure_string(pressure_t pressure, bool showunit) +{ + if (prefs.units.pressure == units::BAR) { + double bar = pressure.mbar / 1000.0; + return QString("%1%2").arg(bar, 0, 'f', 1).arg(showunit ? translate("gettextFromC", "bar") : ""); + } else { + double psi = mbar_to_PSI(pressure.mbar); + return QString("%1%2").arg(psi, 0, 'f', 0).arg(showunit ? translate("gettextFromC", "psi") : ""); + } +} + +QString getSubsurfaceDataPath(QString folderToFind) +{ + QString execdir; + QDir folder; + + // first check if we are running in the build dir, so the path that we + // are looking for is just a subdirectory of the execution path; + // this also works on Windows as there we install the dirs + // under the application path + execdir = QCoreApplication::applicationDirPath(); + folder = QDir(execdir.append(QDir::separator()).append(folderToFind)); + if (folder.exists()) + return folder.absolutePath(); + + // next check for the Linux typical $(prefix)/share/subsurface + execdir = QCoreApplication::applicationDirPath(); + if (execdir.contains("bin")) { + folder = QDir(execdir.replace("bin", "share/subsurface/").append(folderToFind)); + if (folder.exists()) + return folder.absolutePath(); + } + // then look for the usual locations on a Mac + execdir = QCoreApplication::applicationDirPath(); + folder = QDir(execdir.append("/../Resources/share/").append(folderToFind)); + if (folder.exists()) + return folder.absolutePath(); + execdir = QCoreApplication::applicationDirPath(); + folder = QDir(execdir.append("/../Resources/").append(folderToFind)); + if (folder.exists()) + return folder.absolutePath(); + return QString(""); +} + +int gettimezoneoffset(timestamp_t when) +{ + QDateTime dt1, dt2; + if (when == 0) + dt1 = QDateTime::currentDateTime(); + else + dt1 = QDateTime::fromMSecsSinceEpoch(when * 1000); + dt2 = dt1.toUTC(); + dt1.setTimeSpec(Qt::UTC); + return dt2.secsTo(dt1); +} + +int parseTemperatureToMkelvin(const QString &text) +{ + int mkelvin; + QString numOnly = text; + numOnly.replace(",", ".").remove(QRegExp("[^-0-9.]")); + if (numOnly.isEmpty()) + return 0; + double number = numOnly.toDouble(); + switch (prefs.units.temperature) { + case units::CELSIUS: + mkelvin = C_to_mkelvin(number); + break; + case units::FAHRENHEIT: + mkelvin = F_to_mkelvin(number); + break; + default: + mkelvin = 0; + } + return mkelvin; +} + +QString get_dive_duration_string(timestamp_t when, QString hourText, QString minutesText) +{ + int hrs, mins; + mins = (when + 59) / 60; + hrs = mins / 60; + mins -= hrs * 60; + + QString displayTime; + if (hrs) + displayTime = QString("%1%2%3%4").arg(hrs).arg(hourText).arg(mins, 2, 10, QChar('0')).arg(minutesText); + else + displayTime = QString("%1%2").arg(mins).arg(minutesText); + + return displayTime; +} + +QString get_dive_date_string(timestamp_t when) +{ + QDateTime ts; + ts.setMSecsSinceEpoch(when * 1000L); + return loc.toString(ts.toUTC(), dateFormat + " " + timeFormat); +} + +QString get_short_dive_date_string(timestamp_t when) +{ + QDateTime ts; + ts.setMSecsSinceEpoch(when * 1000L); + return loc.toString(ts.toUTC(), shortDateFormat + " " + timeFormat); +} + +const char *get_dive_date_c_string(timestamp_t when) +{ + QString text = get_dive_date_string(when); + return strdup(text.toUtf8().data()); +} + +QString get_trip_date_string(timestamp_t when, int nr) +{ + struct tm tm; + utc_mkdate(when, &tm); + if (nr != 1) { + QString ret = translate("gettextFromC", "%1 %2 (%3 dives)"); + return ret.arg(monthname(tm.tm_mon)) + .arg(tm.tm_year + 1900) + .arg(nr); + } else { + QString ret = translate("gettextFromC", "%1 %2 (1 dive)"); + return ret.arg(monthname(tm.tm_mon)) + .arg(tm.tm_year + 1900); + } +} + +extern "C" void reverseGeoLookup(degrees_t latitude, degrees_t longitude, uint32_t uuid) +{ + QNetworkRequest request; + QNetworkAccessManager *rgl = new QNetworkAccessManager(); + request.setUrl(QString("http://open.mapquestapi.com/nominatim/v1/reverse.php?format=json&accept-language=%1&lat=%2&lon=%3") + .arg(uiLanguage(NULL)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0)); + request.setRawHeader("Accept", "text/json"); + request.setRawHeader("User-Agent", getUserAgent().toUtf8()); + QNetworkReply *reply = rgl->get(request); + QEventLoop loop; + QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + QJsonParseError errorObject; + QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &errorObject); + if (errorObject.error != QJsonParseError::NoError) { + qDebug() << errorObject.errorString(); + } else { + QJsonObject obj = jsonDoc.object(); + QJsonObject address = obj.value("address").toObject(); + qDebug() << "found country:" << address.value("country").toString(); + struct dive_site *ds = get_dive_site_by_uuid(uuid); + ds->notes = add_to_string(ds->notes, "countrytag: %s", address.value("country").toString().toUtf8().data()); + } +} + +QHash<QString, QByteArray> hashOf; +QMutex hashOfMutex; +QHash<QByteArray, QString> localFilenameOf; + +extern "C" char * hashstring(char * filename) +{ + return hashOf[QString(filename)].toHex().data(); +} + +void read_hashes() +{ + QFile hashfile(QString(system_default_directory()).append("/hashes")); + if (hashfile.open(QIODevice::ReadOnly)) { + QDataStream stream(&hashfile); + stream >> localFilenameOf; + hashfile.close(); + } +} + +void write_hashes() +{ + QSaveFile hashfile(QString(system_default_directory()).append("/hashes")); + if (hashfile.open(QIODevice::WriteOnly)) { + QDataStream stream(&hashfile); + stream << localFilenameOf; + hashfile.commit(); + } else { + qDebug() << "cannot open" << hashfile.fileName(); + } +} + +void add_hash(const QString filename, QByteArray hash) +{ + QMutexLocker locker(&hashOfMutex); + hashOf[filename] = hash; + localFilenameOf[hash] = filename; +} + +QByteArray hashFile(const QString filename) +{ + QCryptographicHash hash(QCryptographicHash::Sha1); + QFile imagefile(filename); + imagefile.open(QIODevice::ReadOnly); + hash.addData(&imagefile); + add_hash(filename, hash.result()); + return hash.result(); +} + +void learnHash(struct picture *picture, QByteArray hash) +{ + if (picture->hash) + free(picture->hash); + QMutexLocker locker(&hashOfMutex); + hashOf[QString(picture->filename)] = hash; + picture->hash = strdup(hash.toHex()); +} + +QString localFilePath(const QString originalFilename) +{ + if (hashOf.contains(originalFilename) && localFilenameOf.contains(hashOf[originalFilename])) + return localFilenameOf[hashOf[originalFilename]]; + else + return originalFilename; +} + +QString fileFromHash(char *hash) +{ + return localFilenameOf[QByteArray::fromHex(hash)]; +} + +void updateHash(struct picture *picture) { + QByteArray hash = hashFile(fileFromHash(picture->hash)); + QMutexLocker locker(&hashOfMutex); + hashOf[QString(picture->filename)] = hash; + char *old = picture->hash; + picture->hash = strdup(hash.toHex()); + free(old); +} + +void learnImages(const QDir dir, int max_recursions, bool recursed) +{ + QDir current(dir); + QStringList filters, files; + + if (max_recursions) { + foreach (QString dirname, dir.entryList(QStringList(), QDir::NoDotAndDotDot | QDir::Dirs)) { + learnImages(QDir(dir.filePath(dirname)), max_recursions - 1, true); + } + } + + foreach (QString format, QImageReader::supportedImageFormats()) { + filters.append(QString("*.").append(format)); + } + + foreach (QString file, dir.entryList(filters, QDir::Files)) { + files.append(dir.absoluteFilePath(file)); + } + + QtConcurrent::blockingMap(files, hashFile); +} + +extern "C" void picture_load_exif_data(struct picture *p) +{ + EXIFInfo exif; + memblock mem; + + if (readfile(localFilePath(QString(p->filename)).toUtf8().data(), &mem) <= 0) + goto picture_load_exit; + if (exif.parseFrom((const unsigned char *)mem.buffer, (unsigned)mem.size) != PARSE_EXIF_SUCCESS) + goto picture_load_exit; + p->longitude.udeg= lrint(1000000.0 * exif.GeoLocation.Longitude); + p->latitude.udeg = lrint(1000000.0 * exif.GeoLocation.Latitude); + +picture_load_exit: + free(mem.buffer); + return; +} |