aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.DEREK.yml6
-rw-r--r--CHANGELOG.md10
-rw-r--r--Documentation/user-manual.txt32
-rw-r--r--Documentation/user-manual_es.txt36
-rw-r--r--README.md8
-rw-r--r--ReleaseNotes/ReleaseNotes.txt12
-rw-r--r--core/plannernotes.c6
-rw-r--r--core/qt-init.cpp6
-rw-r--r--core/qthelper.h26
-rw-r--r--core/qtserialbluetooth.cpp11
-rw-r--r--core/serial_ftdi.c8
-rw-r--r--desktop-widgets/configuredivecomputerdialog.cpp2
-rw-r--r--desktop-widgets/diveplanner.cpp4
m---------libdivecomputer0
-rw-r--r--profile-widget/profilewidget2.cpp162
-rw-r--r--profile-widget/profilewidget2.h4
-rw-r--r--qt-models/divepicturemodel.cpp46
-rw-r--r--qt-models/divepicturemodel.h3
-rw-r--r--qt-models/diveplannermodel.cpp1
-rw-r--r--subsurface-helper.cpp2
-rw-r--r--translations/subsurface_bg_BG.ts24
-rw-r--r--translations/subsurface_ca.ts42
-rw-r--r--translations/subsurface_es_ES.ts24
23 files changed, 337 insertions, 138 deletions
diff --git a/.DEREK.yml b/.DEREK.yml
index fea6c57d2..d324aea2c 100644
--- a/.DEREK.yml
+++ b/.DEREK.yml
@@ -7,7 +7,13 @@ maintainers:
- mturkia
- janmulder
- tcanabrava
+ - bstoeger
+ - sfuchs79
+ - janiversen
+ - jbygdell
features:
- dco_check
- comments
+ - labels
+ - milestone
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 02ef2c079..83da2f5fc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,8 @@
- mobile: show developer menu is now saved on disk and remebered between start of Subsurface
-- mobile: show developer menu is now stored on disk and thus remembered
- tests: add qml test harness
-- Cloud storage: fix potential issue with credentials on Linux [#1346]
-- Mobile/iOS: fix missing translations
-- Dive media: support addition of videos
-- Dive media: locate moved files based on filename and path
-- Profile: Context menu entry to manually split a dive
-- BLE support: fix recognition of Heinrich Weikamp OSTC 2 and OSTC Plus
+- Dive media: sort thumbnails by timestamp
+- Dive media: don't recalculate all pictures on drag & drop
+- Profile: immediately update thumbnail positions on deletion
---
* Always add new entries at the very top of this file above other existing entries and this note.
* Use this layout for new entries: `[Area]: [Details about the change] [reference thread / issue]`
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 02379fe18..6d3ac4d8e 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1105,9 +1105,13 @@ using Microsoft Access databases, preventing the integration the importer into t
core application.
A stand alone tool for Linux has been developed to import the _.slg_ files
-generated by SmartTrak to Subsurface's _.xml_ format. It can be built together
-with _Subsurface_ for Linux systems. Two dependencies need to be installed in
-your system before building: _libglib2.0_ and _libmdb2_.
+generated by SmartTrak to Subsurface's _.xml_ format. It can be downloaded
+from https://subsurface-divelog.org/downloads[the usual _Subsurface_ repository],
+as a Windows installer or a Linux AppImage.
+The application is not currently supported on Mac.
+
+It can also be built for Linux systems. Two dependencies need to be met in your
+system before building: _glib2.0_ and _mdbtools_ (see below).
In addition, a web service is available for divelog convertions from SmartTrak to _Subsurface_ (see below).
@@ -1119,15 +1123,19 @@ for assistance in importing _SmartTrak_ dive logs.
Assuming the above dependencies
have been installed and the _Subsurface_ source tree is in the directory _~/src/subsurface_, then:
-- Move to the source tree directory.
-- Run " $ ccmake build " and set SMARTTRAK_IMPORT option to *on* (off by
- default).
-- Generate with [c] and save and exit with [g].
-- Build as you prefer, using the _build.sh_ script (recomended) or moving to build
- directory and running _make_.
-- After a successful build, there will be an executable named _smtk2ssrf_ in the
- _subsurface/build_ directory.
- Copy or move it to a directory in your $PATH, e.g. _~/bin_.
+- Move to the source tree directory out of _Subsurface_ (e.g. ~/src)
+- Run " $ ./subsurface/scripts/smtk2ssrf-build.sh ", if every thing has gone
+ fine, you will now have an executable named smtk2ssrf in
+ ~/src/subsurface/smtk-import/build
+- You can run it from this directory, copy it to another one, e.g. _~/bin_ or
+ simply run "sudo make install", and the binary will be installed in
+ /usr/local/bin (which is commonly included in every $PATH).
+- The script has some options mostly useful for development pourposes. If you
+ think you may need them, please read comments on script header itself.
+- *WARNING*: While building smtk2ssrf, a light version of _Subsurface_ is
+ built (usable but lacking a lot of features). So, if you commonly use the
+ built executable placed at ~/subsurface/build/, you will need to rebuild it as
+ explained in this manual above.
===== Running
diff --git a/Documentation/user-manual_es.txt b/Documentation/user-manual_es.txt
index 38b8b0b04..6ff52e7a8 100644
--- a/Documentation/user-manual_es.txt
+++ b/Documentation/user-manual_es.txt
@@ -1135,8 +1135,12 @@ otros.
Se ha desarrollado una pequeña herramienta independiente para importar los
archivos .slg generados por SmartTrak al formato .xml de _Subsurface_. Puede
-compilarse junto con _Subsurface_ en sistemas Linux. Se necesita instalar dos
-dependencias antes compilar: libglib2.0 y libmdb2.
+descargarse desde https://subsurface-divelog.org/downloads[el repositorio habitual de _Subsurface_],
+como instalador para Windows, o como AppImage para Linux.
+Actualmente la aplicación no está soportada en Mac.
+
+También puede compilarse en sistemas Linux. Se necesita instalar dos
+dependencias antes de compilar: _glib2.0_ y _mdbtools_ (ver a continuación).
Además, está disponible un servicio web para convertir divelogs desde
SmartTrak a _Subsurface_ (ver a continuación).
@@ -1144,20 +1148,26 @@ SmartTrak a _Subsurface_ (ver a continuación).
Si necesitas ayuda puedes contactar con el equipo de _Subsurface_ en
mailto:subsurface@subsurface-divelog.org[nuestra lista de correo]
+===== Compilar _smtk2ssrf_
+
Suponiendo que se hayan instalado las dependencias y que el directorio raíz
del código de _Subsurface_ sea ~/src/subsurface, entonces:
-===== Compilar _smtk2ssrf_
-
-- Sitúate en el directorio raíz del código de _Subsurface_
-- Ejecuta " $ ccmake build " y ajusta la opción SMARTTRAK_IMPORT a *ON* (por
- defecto en OFF).
-- Selecciona [c] y [g] para generar la configuración de cmake y guardarla.
-- Compila el código como prefieras, usando el script build.sh (recomendado) o
- yendo al directorio "build" y ejecutando "make".
-- Si se ha compilado el código con éxito, habrá un ejecutable en el directorio
- "build" llamado smtk2ssrf, cópialo o muévelo a un directorio que se
- encuentre incluido en tu $PATH, p.e. ~/bin
+- Sitúate en el directorio raíz del código, fuera de _Subsurface_ (~/src en el
+ ejemplo)
+- Ejecuta " $ ./subsurface/scripts/smtk2ssrf-build.sh " y, si todo va bien,
+ obtendrás un binario ejecutable en la carpeta
+ ~/src/subsurface/smtk-import/build
+- Puedes ejecutarlo desde allí, copiarlo a una carpeta que esté en tu $PATH o
+ moverte allí y correr "$ sudo make install" que. por defecto lo instalará en
+ /usr/local/bin, el cual suele estar incluido en el $PATH
+- El script tiene algunas opciones de ejecución en línea de comandos útiles
+ mayormente para desarrollo, si crees que puedes necesitarlas, por favor, lee
+ el encabezamiento del script.
+- *AVISO*: Al compilar smtk2ssrf, se recompila una versión aligerada de
+ _Subsurface_. Esto carecerá de importancia excepto si utilizas habitualmente
+ el binario compilado previamente que se encuentra en ~/src/subsurface/build.
+ En ese caso tendrás que recompilar _Subsurface_.
===== Ejecutar
diff --git a/README.md b/README.md
index f399838b1..772e0b935 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
# Subsurface [![Build Status](https://travis-ci.org/Subsurface-divelog/subsurface.svg?branch=master)](https://travis-ci.org/Subsurface-divelog/subsurface)
-This is the README file for Subsurface 4.8
+This is the README file for Subsurface 4.8.1
Please check the `ReleaseNotes.txt` for details about new features and
-changes since Subsurface 4.7.8 (and earlier versions).
+changes since Subsurface 4.8 (and earlier versions).
Subsurface can be found at http://subsurface-divelog.org
@@ -38,10 +38,10 @@ development version) you can either get this via git or the release tar
ball. After cloning run the following command:
```
-git checkout v4.8.0 (or whatever the last release is)
+git checkout v4.8.1 (or whatever the last release is)
```
-or download a tarball from http://subsurface-divelog.org/downloads/Subsurface-4.8.0.tgz
+or download a tarball from http://subsurface-divelog.org/downloads/Subsurface-4.8.1.tgz
Detailed build instructions can be found in the INSTALL file.
diff --git a/ReleaseNotes/ReleaseNotes.txt b/ReleaseNotes/ReleaseNotes.txt
index 1d1d7887b..78df36eea 100644
--- a/ReleaseNotes/ReleaseNotes.txt
+++ b/ReleaseNotes/ReleaseNotes.txt
@@ -2,6 +2,18 @@
= _Subsurface_ Release Notes
+New in _Subsurface_ 4.8.1
+~~~~~~~~~~~~~~~~~~~~~~~~~
+- Mac: fix crashes when opening the user manual or connecting to Facebook [#1471]
+- BLE support: fix recognition of Heinrich Weikamp OSTC 2 and OSTC Plus
+- BT support: fix downloading / configuring some OSTC models [#1474]
+- Cloud storage: fix potential issue with credentials on Linux [#1346]
+- Profile: Context menu entry to manually split a dive
+- Dive media: support addition of videos
+- Dive media: locate moved files based on filename and path
+- Planner: fix bug in dive mode drop-down and planner notes
+- Mobile/iOS: fix missing translations
+
New in _Subsurface_ 4.8
~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/core/plannernotes.c b/core/plannernotes.c
index 10b4a2ddb..c7d443324 100644
--- a/core/plannernotes.c
+++ b/core/plannernotes.c
@@ -245,7 +245,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d
FRACTION(dp->time - lasttime, 60),
FRACTION(dp->time, 60),
gasname(&gasmix),
- divemode_text_ui[dp->divemode]);
+ translate("gettextFromC", divemode_text_ui[dp->divemode]));
}
put_string(&buf, "<br>");
newdepth = dp->depth.mm;
@@ -312,7 +312,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d
free(temp);
} else {
put_format(&buf, "<td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td>",
- gasname(&newgasmix), lastdivemode == dp->divemode ? "" : divemode_text_ui[dp->divemode]);
+ gasname(&newgasmix), lastdivemode == UNDEF_COMP_TYPE || lastdivemode == dp->divemode ? "" : translate("gettextFromC", divemode_text_ui[dp->divemode]));
if (isascent && (get_he(&lastprintgasmix) > 0)) { // For a trimix gas change on ascent, save ICD info if previous cylinder had helium
if (isobaric_counterdiffusion(&lastprintgasmix, &newgasmix, &icdvalues)) // Do icd calulations
icdwarning = true;
@@ -334,7 +334,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d
free(temp);
} else {
put_format(&buf, "<td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td>", gasname(&gasmix),
- lastdivemode == dp->divemode ? "" : divemode_text_ui[dp->divemode]);
+ lastdivemode == UNDEF_COMP_TYPE || lastdivemode == dp->divemode ? "" : translate("gettextFromC", divemode_text_ui[dp->divemode]));
if (get_he(&lastprintgasmix) > 0) { // For a trimix gas change, save ICD info if previous cylinder had helium
if (isobaric_counterdiffusion(&lastprintgasmix, &gasmix, &icdvalues)) // Do icd calculations
icdwarning = true;
diff --git a/core/qt-init.cpp b/core/qt-init.cpp
index b1a235cb6..5596b3f95 100644
--- a/core/qt-init.cpp
+++ b/core/qt-init.cpp
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <QApplication>
+#include <Qt>
#include <QNetworkProxy>
#include <QLibraryInfo>
#include <QTextCodec>
@@ -42,6 +43,11 @@ void init_qt_late()
QCoreApplication::setApplicationName("Subsurface");
#endif
}
+ // Disables the WindowContextHelpButtonHint by default on Qt::Sheet and Qt::Dialog widgets.
+ // This hides the ? button on Windows, which only makes sense if you use QWhatsThis functionality.
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+ QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
+#endif
// find plugins installed in the application directory (without this SVGs don't work on Windows)
SettingsObjectWrapper::instance()->load();
diff --git a/core/qthelper.h b/core/qthelper.h
index a2decd527..e1517991c 100644
--- a/core/qthelper.h
+++ b/core/qthelper.h
@@ -88,6 +88,32 @@ QString getUserAgent();
#define TITLE_OR_TEXT(_t, _m) _t, _m
#endif
+// Move a range in a vector to a different position.
+// The parameters are given according to the usual STL-semantics:
+// v: a container with STL-like random access iterator via std::begin(...)
+// rangeBegin: index of first element
+// rangeEnd: index one *past* last element
+// destination: index to element before which the range will be moved
+// Owing to std::begin() magic, this function works with STL-like containers:
+// QVector<int> v{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+// moveInVector(v, 1, 4, 6);
+// as well as with C-style arrays:
+// int array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+// moveInVector(array, 1, 4, 6);
+// Both calls will have the following effect:
+// Before: 0 1 2 3 4 5 6 7 8 9
+// After: 0 4 5 1 2 3 6 7 8 9
+// No sanitizing of the input arguments is performed.
+template <typename Vector>
+void moveInVector(Vector &v, int rangeBegin, int rangeEnd, int destination)
+{
+ auto it = std::begin(v);
+ if (destination > rangeEnd)
+ std::rotate(it + rangeBegin, it + rangeEnd, it + destination);
+ else if (destination < rangeBegin)
+ std::rotate(it + destination, it + rangeBegin, it + rangeEnd);
+}
+
#endif
// 3) Functions visible to C and C++
diff --git a/core/qtserialbluetooth.cpp b/core/qtserialbluetooth.cpp
index 71a76187f..14ddacf2b 100644
--- a/core/qtserialbluetooth.cpp
+++ b/core/qtserialbluetooth.cpp
@@ -6,6 +6,7 @@
#include <QEventLoop>
#include <QTimer>
#include <QDebug>
+#include <QThread>
#include <libdivecomputer/version.h>
#include <libdivecomputer/context.h>
@@ -395,6 +396,12 @@ static dc_status_t qt_serial_set_timeout(void *io, int timeout)
return DC_STATUS_SUCCESS;
}
+static dc_status_t qt_custom_sleep(void *io, unsigned int timeout)
+{
+ QThread::msleep(timeout);
+ return DC_STATUS_SUCCESS;
+}
+
#ifdef BLE_SUPPORT
dc_status_t
ble_packet_open(dc_iostream_t **iostream, dc_context_t *context, const char* devaddr, void *userdata)
@@ -415,7 +422,7 @@ ble_packet_open(dc_iostream_t **iostream, dc_context_t *context, const char* dev
qt_ble_write, /* write */
NULL, /* flush */
NULL, /* purge */
- NULL, /* sleep */
+ qt_custom_sleep, /* sleep */
qt_ble_close, /* close */
};
@@ -448,7 +455,7 @@ rfcomm_stream_open(dc_iostream_t **iostream, dc_context_t *context, const char*
qt_serial_write, /* write */
NULL, /* flush */
qt_serial_purge, /* purge */
- NULL, /* sleep */
+ qt_custom_sleep, /* sleep */
qt_serial_close, /* close */
};
diff --git a/core/serial_ftdi.c b/core/serial_ftdi.c
index 6d99672f2..9bcf7aba0 100644
--- a/core/serial_ftdi.c
+++ b/core/serial_ftdi.c
@@ -98,12 +98,14 @@ static dc_status_t serial_ftdi_get_transmitted (ftdi_serial_t *device)
return DC_STATUS_UNSUPPORTED;
}
-static dc_status_t serial_ftdi_sleep (ftdi_serial_t *device, unsigned long timeout)
+static dc_status_t serial_ftdi_sleep (void *io, unsigned int timeout)
{
+ ftdi_serial_t *device = io;
+
if (device == NULL)
return DC_STATUS_INVALIDARGS;
- INFO (device->context, "Sleep: value=%lu", timeout);
+ INFO (device->context, "Sleep: value=%u", timeout);
struct timespec ts;
ts.tv_sec = (timeout / 1000);
@@ -541,7 +543,7 @@ dc_status_t ftdi_open(dc_iostream_t **iostream, dc_context_t *context)
serial_ftdi_write, /* write */
NULL, /* flush */
serial_ftdi_purge, /* purge */
- NULL, /* sleep */
+ serial_ftdi_sleep, /* sleep */
serial_ftdi_close, /* close */
};
diff --git a/desktop-widgets/configuredivecomputerdialog.cpp b/desktop-widgets/configuredivecomputerdialog.cpp
index bceaa98f9..47eced724 100644
--- a/desktop-widgets/configuredivecomputerdialog.cpp
+++ b/desktop-widgets/configuredivecomputerdialog.cpp
@@ -1429,7 +1429,7 @@ void ConfigureDiveComputerDialog::on_DiveComputerList_currentRowChanged(int curr
break;
case 1:
selected_vendor = "Heinrichs Weikamp";
- selected_product = "OSTC 3";
+ selected_product = "OSTC Plus";
fw_upgrade_possible = true;
break;
case 2:
diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp
index 6ec14795f..7d4f1a5f5 100644
--- a/desktop-widgets/diveplanner.cpp
+++ b/desktop-widgets/diveplanner.cpp
@@ -421,7 +421,6 @@ void DivePlannerWidget::printDecoPlan()
PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f)
{
ui.setupUi(this);
- QStringList rebreather_modes;
plannerModel->getDiveplan().bottomsac = prefs.bottomsac;
plannerModel->getDiveplan().decosac = prefs.decosac;
@@ -448,7 +447,8 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f)
disableDecoElements((int) prefs.planner_deco_mode);
// should be the same order as in dive_comp_type!
- for (int i=0; i < FREEDIVE; i++)
+ QStringList rebreather_modes = QStringList();
+ for (int i = 0; i < FREEDIVE; i++)
rebreather_modes.append(gettextFromC::tr(divemode_text_ui[i]));
ui.rebreathermode->insertItems(0, rebreather_modes);
diff --git a/libdivecomputer b/libdivecomputer
-Subproject 02560a7e7fe82919d584d3edbf3876f90382052
+Subproject 8f4945dc1e83c53ed9d2cdbaaa16e7b117df1f3
diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp
index 9af364f20..6c4f0f2cc 100644
--- a/profile-widget/profilewidget2.cpp
+++ b/profile-widget/profilewidget2.cpp
@@ -2081,10 +2081,17 @@ void ProfileWidget2::updateThumbnail(QString filename, QImage thumbnail)
}
}
-ProfileWidget2::PictureEntry::PictureEntry (offset_t offsetIn, const QString &filenameIn) : offset(offsetIn),
+// Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown.
+ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const QString &filenameIn, QGraphicsScene *scene) : offset(offsetIn),
filename(filenameIn),
thumbnail(new DivePictureItem)
{
+ int size = Thumbnailer::defaultThumbnailSize();
+ scene->addItem(thumbnail.get());
+ thumbnail->setVisible(prefs.show_pictures_in_profile);
+ QImage img = Thumbnailer::instance()->fetchThumbnail(filename).scaled(size, size, Qt::KeepAspectRatio);
+ thumbnail->setPixmap(QPixmap::fromImage(img));
+ thumbnail->setFileUrl(filename);
}
// Define a default sort order for picture-entries: sort lexicographically by timestamp and filename.
@@ -2094,6 +2101,40 @@ bool ProfileWidget2::PictureEntry::operator< (const PictureEntry &e) const
return std::tie(offset.seconds, filename) < std::tie(e.offset.seconds, e.filename);
}
+// Calculate the y-coordinates of the thumbnails, which are supposed to be sorted by x-coordinate.
+// This will also change the order in which the thumbnails are painted, to avoid weird effects,
+// when items are added later to the scene. This is simply done by increasing the Z-value.
+void ProfileWidget2::calculatePictureYPositions()
+{
+ double lastX = -1.0, lastY;
+ double z = 0.0;
+ for (PictureEntry &e: pictures) {
+ if (!e.thumbnail)
+ continue;
+ // let's put the picture at the correct time, but at a fixed "depth" on the profile
+ // not sure this is ideal, but it seems to look right.
+ double x = e.thumbnail->x();
+ double y;
+ if (lastX >= 0.0 && fabs(x - lastX) < 3 && lastY <= (10 + 14 * 3))
+ y = lastY + 3;
+ else
+ y = 10;
+ lastX = x;
+ lastY = y;
+ e.thumbnail->setY(y);
+ e.thumbnail->setZValue(z);
+ z += 1.0;
+ }
+}
+
+void ProfileWidget2::updateThumbnailXPos(PictureEntry &e)
+{
+ // Here, we only set the x-coordinate of the picture. The y-coordinate
+ // will be set later in calculatePictureYPositions().
+ double x = timeAxis->posAtValue(e.offset.seconds);
+ e.thumbnail->setX(x);
+}
+
// This function resets the picture thumbnails of the current dive.
void ProfileWidget2::plotPictures()
{
@@ -2104,9 +2145,10 @@ void ProfileWidget2::plotPictures()
// Fetch all pictures of the current dive, but consider only those that are within the dive time.
// For each picture, create a PictureEntry object in the pictures-vector.
// emplace_back() constructs an object at the end of the vector. The parameters are passed directly to the constructor.
+ // Note that FOR_EACH_PICTURE handles current_dive being null gracefully.
FOR_EACH_PICTURE(current_dive) {
if (picture->offset.seconds > 0 && picture->offset.seconds <= current_dive->duration.seconds)
- pictures.emplace_back(picture->offset, QString(picture->filename));
+ pictures.emplace_back(picture->offset, QString(picture->filename), scene());
}
if (pictures.empty())
return;
@@ -2114,29 +2156,10 @@ void ProfileWidget2::plotPictures()
// This will allow for proper location of the pictures on the profile plot.
std::sort(pictures.begin(), pictures.end());
- // Add the DivePictureItems to the scene, set their pixmaps and filenames
- // and finaly calculate their positions.
- double x, y, lastX = -1.0, lastY = -1.0;
- int size = Thumbnailer::defaultThumbnailSize();
- for (PictureEntry &e: pictures) {
- scene()->addItem(e.thumbnail.get());
- e.thumbnail->setVisible(prefs.show_pictures_in_profile);
- QImage thumbnail = Thumbnailer::instance()->fetchThumbnail(e.filename).scaled(size, size, Qt::KeepAspectRatio);
- e.thumbnail->setPixmap(QPixmap::fromImage(thumbnail));
- e.thumbnail->setFileUrl(e.filename);
- // let's put the picture at the correct time, but at a fixed "depth" on the profile
- // not sure this is ideal, but it seems to look right.
- x = timeAxis->posAtValue(e.offset.seconds);
- if (lastX < 0.0)
- y = 10;
- else if (fabs(x - lastX) < 3 && lastY <= (10 + 14 * 3))
- y = lastY + 3;
- else
- y = 10;
- lastX = x;
- lastY = y;
- e.thumbnail->setPos(x, y);
- }
+ // Calculate thumbnail positions. First the x-coordinates and and then the y-coordinates.
+ for (PictureEntry &e: pictures)
+ updateThumbnailXPos(e);
+ calculatePictureYPositions();
}
// Remove the pictures with the given filenames from the profile plot.
@@ -2155,6 +2178,7 @@ void ProfileWidget2::removePictures(const QVector<QString> &fileUrls)
// Check whether filename of entry is in list of provided filenames
{ return std::find(fileUrls.begin(), fileUrls.end(), e.filename) != fileUrls.end(); });
pictures.erase(it, pictures.end());
+ calculatePictureYPositions();
}
#endif
@@ -2166,23 +2190,87 @@ void ProfileWidget2::dropEvent(QDropEvent *event)
QDataStream dataStream(&itemData, QIODevice::ReadOnly);
QString filename;
- QPoint offset;
- dataStream >> filename >> offset;
+ QPoint pos;
+ dataStream >> filename >> pos;
+#ifndef SUBSURFACE_MOBILE
+ // Calculate time in dive where picture was dropped and whether the new position is during the dive.
QPointF mappedPos = mapToScene(event->pos());
+ offset_t offset { (int32_t)lrint(timeAxis->valueAt(mappedPos)) };
+ bool duringDive = current_dive && offset.seconds > 0 && offset.seconds < current_dive->duration.seconds;
+
+ // Flag which states whether the drag&dropped picture actually belongs to this dive.
+ // If this is not the case, the calculated offset makes no sense whatsoever and we must ignore the event.
+ bool belongsToDive = true;
+
+ // A picture was drag&dropped onto the profile: We have four cases to consider:
+ // 1a) The image was already shown on the profile and is moved to a different position on the profile.
+ // Calculate the new position and move the picture.
+ // 1b) The image was on the profile and is moved outside of the dive time.
+ // Remove the picture.
+ // 2a) The image was not on the profile, but belongs to the current dive.
+ // Add the picture to the profile if it is during the dive.
+ // 2b) The picture does not belong to the current dive.
+ // For now, do nothing. We may think about adding the picture to the dive.
+ auto oldPos = std::find_if(pictures.begin(), pictures.end(), [filename](const PictureEntry &e)
+ { return e.filename == filename; });
+ if (oldPos != pictures.end()) {
+ // Cases 1a) and 1b): picture is on profile
+ if (duringDive) {
+ // Case 1a): move to new position
+ // First, find new position. Note that we also have to compare filenames,
+ // because it is quite easy to generate equal offsets.
+ auto newPos = std::find_if(pictures.begin(), pictures.end(), [offset, &filename](const PictureEntry &e)
+ { return std::tie(e.offset.seconds, e.filename) > std::tie(offset.seconds, filename); });
+ // Set new offset
+ oldPos->offset.seconds = offset.seconds;
+ updateThumbnailXPos(*oldPos);
+
+ // Move image from old to new position
+ int oldIndex = oldPos - pictures.begin();
+ int newIndex = newPos - pictures.begin();
+ moveInVector(pictures, oldIndex, oldIndex + 1, newIndex);
+ } else {
+ // Case 1b): remove picture
+ pictures.erase(oldPos);
+ }
- FOR_EACH_PICTURE(current_dive) {
- if (QString(picture->filename) == filename) {
- picture->offset.seconds = lrint(timeAxis->valueAt(mappedPos));
- mark_divelist_changed(true);
-#ifndef SUBSURFACE_MOBILE
- DivePictureModel::instance()->updateDivePictureOffset(filename, picture->offset.seconds);
- plotPictures();
-#endif
- break;
+ // In both cases the picture list changed, therefore we must recalculate the y-coordinatesA.
+ calculatePictureYPositions();
+ } else {
+ // Cases 2a) and 2b): picture not on profile. Check if it belongs to current dive.
+ // Note that FOR_EACH_PICTURE handles current_dive being null gracefully.
+ bool found = false;
+ FOR_EACH_PICTURE(current_dive) {
+ if (picture->filename == filename) {
+ found = true;
+ break;
+ }
+ }
+ if (found && duringDive) {
+ // Case 2a): add the picture at the appropriate position.
+ // The case move from outside-to-outside of the profile plot was handled by
+ // the "&& duringDive" condition in the if above.
+ // As for case 1a), we have to also consider filenames in the case of equal offsets.
+ auto newPos = std::find_if(pictures.begin(), pictures.end(), [offset, &filename](const PictureEntry &e)
+ { return std::tie(e.offset.seconds, e.filename) > std::tie(offset.seconds, filename); });
+ // emplace() constructs the element at the given position in the vector.
+ // The parameters are passed directly to the contructor.
+ // The call returns an iterator to the new element (which might differ from
+ // the old iterator, since the buffer might have been reallocated).
+ newPos = pictures.emplace(newPos, offset, filename, scene());
+ updateThumbnailXPos(*newPos);
+ calculatePictureYPositions();
+ } else if (!found) {
+ // Case 2b): Unknown picture. Ignore.
+ belongsToDive = false;
}
}
- copy_dive(current_dive, &displayed_dive);
+
+ // Only signal the drag&drop action if the picture actually belongs to the dive.
+ if (belongsToDive)
+ DivePictureModel::instance()->updateDivePictureOffset(displayed_dive.id, filename, offset.seconds);
+#endif
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h
index c7db09a92..2640fb85a 100644
--- a/profile-widget/profilewidget2.h
+++ b/profile-widget/profilewidget2.h
@@ -236,10 +236,12 @@ private:
offset_t offset;
QString filename;
std::unique_ptr<DivePictureItem> thumbnail;
- PictureEntry (offset_t offsetIn, const QString &filenameIn);
+ PictureEntry (offset_t offsetIn, const QString &filenameIn, QGraphicsScene *scene);
bool operator< (const PictureEntry &e) const;
};
+ void updateThumbnailXPos(PictureEntry &e);
std::vector<PictureEntry> pictures;
+ void calculatePictureYPositions();
QList<DiveHandler *> handles;
void repositionDiveHandlers();
diff --git a/qt-models/divepicturemodel.cpp b/qt-models/divepicturemodel.cpp
index f080138cf..0aaf8f868 100644
--- a/qt-models/divepicturemodel.cpp
+++ b/qt-models/divepicturemodel.cpp
@@ -56,8 +56,14 @@ void DivePictureModel::updateDivePictures()
struct dive *dive;
for_each_dive (i, dive) {
if (dive->selected) {
+ int first = pictures.count();
FOR_EACH_PICTURE(dive)
- pictures.push_back({picture, picture->filename, {}, picture->offset.seconds});
+ pictures.push_back({ dive->id, picture, picture->filename, {}, picture->offset.seconds });
+
+ // Sort pictures of this dive by offset.
+ // Thus, the list will be sorted by (diveId, offset).
+ std::sort(pictures.begin() + first, pictures.end(),
+ [](const PictureEntry &a, const PictureEntry &b) { return a.offsetSeconds < b.offsetSeconds; });
}
}
@@ -166,11 +172,39 @@ void DivePictureModel::updateThumbnail(QString filename, QImage thumbnail)
}
}
-void DivePictureModel::updateDivePictureOffset(const QString &filename, int offsetSeconds)
+void DivePictureModel::updateDivePictureOffset(int diveId, const QString &filename, int offsetSeconds)
{
- int i = findPictureId(filename);
- if (i >= 0) {
- pictures[i].offsetSeconds = offsetSeconds;
- emit dataChanged(createIndex(i, 0), createIndex(i, 1));
+ // Find the pictures of the given dive.
+ auto from = std::find_if(pictures.begin(), pictures.end(), [diveId](const PictureEntry &e) { return e.diveId == diveId; });
+ auto to = std::find_if(from, pictures.end(), [diveId](const PictureEntry &e) { return e.diveId != diveId; });
+
+ // Find picture with the given filename
+ auto oldPos = std::find_if(from, to, [filename](const PictureEntry &e) { return e.filename == filename; });
+ if (oldPos == to)
+ return;
+
+ // Find new position
+ auto newPos = std::find_if(from, to, [offsetSeconds](const PictureEntry &e) { return e.offsetSeconds > offsetSeconds; });
+
+ // Update the offset here and in the backend
+ oldPos->offsetSeconds = offsetSeconds;
+ if (struct dive *dive = get_dive_by_uniq_id(diveId)) {
+ FOR_EACH_PICTURE(dive) {
+ if (picture->filename == filename) {
+ picture->offset.seconds = offsetSeconds;
+ mark_divelist_changed(true);
+ break;
+ }
+ }
+ copy_dive(current_dive, &displayed_dive);
}
+
+ // Henceforth we will work with indices instead of iterators
+ int oldIndex = oldPos - pictures.begin();
+ int newIndex = newPos - pictures.begin();
+ if (oldIndex == newIndex || oldIndex + 1 == newIndex)
+ return;
+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
+ moveInVector(pictures, oldIndex, oldIndex + 1, newIndex);
+ endMoveRows();
}
diff --git a/qt-models/divepicturemodel.h b/qt-models/divepicturemodel.h
index 427ab0158..6dc633fb2 100644
--- a/qt-models/divepicturemodel.h
+++ b/qt-models/divepicturemodel.h
@@ -7,6 +7,7 @@
#include <QFuture>
struct PictureEntry {
+ int diveId;
struct picture *picture;
QString filename;
QImage image;
@@ -22,7 +23,7 @@ public:
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual void updateDivePictures();
void removePictures(const QVector<QString> &fileUrls);
- void updateDivePictureOffset(const QString &filename, int offsetSeconds);
+ void updateDivePictureOffset(int diveId, const QString &filename, int offsetSeconds);
signals:
void picturesRemoved(const QVector<QString> &fileUrls);
public slots:
diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp
index 435571e46..52c87071e 100644
--- a/qt-models/diveplannermodel.cpp
+++ b/qt-models/diveplannermodel.cpp
@@ -141,6 +141,7 @@ void DivePlannerPointsModel::loadFromDive(dive *d)
if (!hasMarkedSamples && !dc->last_manual_time.seconds)
addStop(0, d->dc.duration.seconds,cylinderid, last_sp.mbar, true, current_divemode);
recalc = oldRec;
+ DiveTypeSelectionModel::instance()->repopulate();
emitDataChanged();
}
diff --git a/subsurface-helper.cpp b/subsurface-helper.cpp
index 88b8a0587..92409cb81 100644
--- a/subsurface-helper.cpp
+++ b/subsurface-helper.cpp
@@ -34,6 +34,7 @@ QObject *qqWindowObject = NULL;
void init_ui()
{
init_qt_late();
+ register_qml_types();
#ifndef SUBSURFACE_MOBILE
PluginManager::instance().loadPlugins();
@@ -59,7 +60,6 @@ double get_screen_dpi()
void run_ui()
{
- register_qml_types();
#ifdef SUBSURFACE_MOBILE
QQmlApplicationEngine engine;
diff --git a/translations/subsurface_bg_BG.ts b/translations/subsurface_bg_BG.ts
index bf0502de6..82f459b5f 100644
--- a/translations/subsurface_bg_BG.ts
+++ b/translations/subsurface_bg_BG.ts
@@ -3933,28 +3933,28 @@ p, li { white-space: pre-wrap; }
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.ui" line="23"/>
<source>Find moved images</source>
- <translation type="unfinished"/>
+ <translation>Намери преместени изображения</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.ui" line="37"/>
<source>Found images</source>
- <translation type="unfinished"/>
+ <translation>Намерени изображения</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.ui" line="71"/>
<source>Match only images in selected dive(s)</source>
- <translation type="unfinished"/>
+ <translation>Намери изображения в избраното гмуркане (гмуркания)</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.ui" line="95"/>
<source>Scanning:</source>
- <translation type="unfinished"/>
+ <translation>Сканиране:</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.ui" line="121"/>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="240"/>
<source>Select folder and scan</source>
- <translation type="unfinished"/>
+ <translation>Избери папка и сканирай</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="173"/>
@@ -3964,27 +3964,27 @@ p, li { white-space: pre-wrap; }
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="179"/>
<source>Stop scanning</source>
- <translation type="unfinished"/>
+ <translation>Спри сканирането</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="250"/>
<source>Scanning cancelled - results may be incomplete</source>
- <translation type="unfinished"/>
+ <translation>Сканирането спряно - резултатите може да са непълни</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="254"/>
<source>No matching images found</source>
- <translation type="unfinished"/>
+ <translation>Не могат да бъдат намерени изображения</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="266"/>
<source>Found &lt;b&gt;%1&lt;/b&gt; images at their current place.</source>
- <translation type="unfinished"/>
+ <translation>Намерени &lt;b&gt;%1&lt;/b&gt; изображения в тяхната текуща локация.</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="268"/>
<source>Found &lt;b&gt;%1&lt;/b&gt; images at new locations:</source>
- <translation type="unfinished"/>
+ <translation>Намерени &lt;b&gt;%1&lt;/b&gt; изображения в тяхната текуща локация.</translation>
</message>
</context>
<context>
@@ -6253,7 +6253,7 @@ Please export this template to a different file.</source>
<message>
<location filename="../profile-widget/profilewidget2.cpp" line="1432"/>
<source>Split dive into two</source>
- <translation type="unfinished"/>
+ <translation>Раздели гмуркане на две</translation>
</message>
<message>
<location filename="../profile-widget/profilewidget2.cpp" line="1442"/>
@@ -9911,7 +9911,7 @@ EADD: %d%s / %.1fгр./л.
<message numerus="yes">
<location filename="../core/qthelper.cpp" line="1010"/>
<source>(%n dive(s))</source>
- <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+ <translation><numerusform>(%n гмуркане (гмуркания))</numerusform><numerusform>(%n гмуркане (гмуркания))</numerusform></translation>
</message>
<message>
<location filename="../core/qthelper.cpp" line="1246"/>
diff --git a/translations/subsurface_ca.ts b/translations/subsurface_ca.ts
index e0066092f..a3d02f266 100644
--- a/translations/subsurface_ca.ts
+++ b/translations/subsurface_ca.ts
@@ -407,7 +407,7 @@
<message>
<location filename="../core/cloudstorage.cpp" line="65"/>
<source>Cloud account verification required, enter PIN in preferences</source>
- <translation>Cal verificació del compte del núvol, introduïu el PIN a les preferències</translation>
+ <translation>Cal verificació del compte en el núvol, introduïu el PIN a les preferències</translation>
</message>
</context>
<context>
@@ -4922,12 +4922,12 @@ directament, ja que l&apos;aplicació les pot sobreescriure quan s&apos;inicia.<
<source>You have unsaved changes. Do you want to commit them to the cloud storage?
If answering no, the cloud will only be synced on next call to &quot;Open cloud storage&quot; or &quot;Save to cloud storage&quot;.</source>
<translation>Teniu canvis sense desar. Els voleu publicar a l&apos;emmagatzematge al núvol?
-Si responeu no, el núvol només se sincronitzarà en la següent crida a «Obre l&apos;emmagatzematge en el núvol» o «Desar a l&apos;emmagatzematge en el núvol».</translation>
+Si responeu no, el núvol només se sincronitzarà en la següent crida a «Obre l&apos;emmagatzematge en el núvol» o «Desa a l&apos;emmagatzematge en el núvol».</translation>
</message>
<message>
<location filename="../desktop-widgets/mainwindow.cpp" line="700"/>
<source>Failure taking cloud storage online</source>
- <translation>Ha fallat en prendre l&apos;emmagatzematge al núvol en línia</translation>
+ <translation>Ha fallat en posar l&apos;emmagatzematge al núvol en línia</translation>
</message>
<message>
<location filename="../desktop-widgets/mainwindow.cpp" line="769"/>
@@ -5847,18 +5847,18 @@ Vegeu http://doc.qt.io/qt-5/qdatetime.html#toString</translation>
<message>
<location filename="../desktop-widgets/preferences/preferences_network.cpp" line="71"/>
<source>Change ignored. Cloud storage email and password can only consist of letters, numbers, and &apos;.&apos;, &apos;-&apos;, &apos;_&apos;, and &apos;+&apos;.</source>
- <translation>El canvi s&apos;ha ignorat. El correu electrònic i la contrasenya de l&apos;emmagatzematge al núvol només poden consistir en lletres, números i «.», «-», «_» i «+».</translation>
+ <translation>El canvi s&apos;ha ignorat. El correu electrònic i la contrasenya de l&apos;emmagatzematge en el núvol només poden consistir en lletres, números i «.», «-», «_» i «+».</translation>
</message>
<message>
<location filename="../desktop-widgets/preferences/preferences_network.cpp" line="75"/>
<source>Change ignored. Cloud storage email and new password can only consist of letters, numbers, and &apos;.&apos;, &apos;-&apos;, &apos;_&apos;, and &apos;+&apos;.</source>
- <translation>El canvi s&apos;ha ignorat. El correu electrònic i la contrasenya noves de l&apos;emmagatzematge al núvol només poden consistir en lletres, números i «.», «-», «_» i «+».</translation>
+ <translation>El canvi s&apos;ha ignorat. El correu electrònic i la contrasenya noves de l&apos;emmagatzematge en el núvol només poden consistir en lletres, números i «.», «-», «_» i «+».</translation>
</message>
<message>
<location filename="../desktop-widgets/preferences/preferences_network.cpp" line="98"/>
<location filename="../desktop-widgets/preferences/preferences_network.cpp" line="112"/>
<source>Cloud storage email and password can only consist of letters, numbers, and &apos;.&apos;, &apos;-&apos;, &apos;_&apos;, and &apos;+&apos;.</source>
- <translation>El correu i la contrasenya de l&apos;emmagatzematge al núvol només pot contenir lletres, números i els caràcters «.», «-», «_» i «+».</translation>
+ <translation>El correu i la contrasenya de l&apos;emmagatzematge en el núvol només pot contenir lletres, números i els caràcters «.», «-», «_» i «+».</translation>
</message>
<message>
<location filename="../desktop-widgets/preferences/preferences_network.cpp" line="138"/>
@@ -6377,7 +6377,7 @@ Exporteu aquesta plantilla a un fitxer diferent.</translation>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="445"/>
<source>Cloud storage email and password can only consist of letters, numbers, and &apos;.&apos;, &apos;-&apos;, &apos;_&apos;, and &apos;+&apos;.</source>
- <translation>El correu i la contrasenya de l&apos;emmagatzematge al núvol només pot contenir lletres, números i els caràcters «.», «-», «_» i «+».</translation>
+ <translation>El correu i la contrasenya de l&apos;emmagatzematge en el núvol només pot contenir lletres, números i els caràcters «.», «-», «_» i «+».</translation>
</message>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="451"/>
@@ -6407,7 +6407,7 @@ Exporteu aquesta plantilla a un fitxer diferent.</translation>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="553"/>
<source>Cannot connect to cloud storage - cloud account not verified</source>
- <translation>No s&apos;ha pogut connectar amb l&apos;emmagatzematge al núvol -el compte al núvol no ha estat verificat-</translation>
+ <translation>No s&apos;ha pogut connectar amb l&apos;emmagatzematge en el núvol -el compte al núvol no ha estat verificat-</translation>
</message>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="580"/>
@@ -6417,17 +6417,17 @@ Exporteu aquesta plantilla a un fitxer diferent.</translation>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="594"/>
<source>Cannot open cloud storage: Error creating https connection</source>
- <translation>No s&apos;ha pogut obrir l&apos;emmagatzematge al núvol: un error en crear la connexió HTTPS</translation>
+ <translation>No s&apos;ha pogut obrir l&apos;emmagatzematge en el núvol: un error en crear la connexió HTTPS</translation>
</message>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="608"/>
<source>Cannot open cloud storage: %1</source>
- <translation>No s&apos;ha pogut obrir l&apos;emmagatzematge al núvol: %1</translation>
+ <translation>No s&apos;ha pogut obrir l&apos;emmagatzematge en el núvol: %1</translation>
</message>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="621"/>
<source>Cannot connect to cloud storage</source>
- <translation>No s&apos;ha pogut connectar amb l&apos;emmagatzematge al núvol</translation>
+ <translation>No s&apos;ha pogut connectar amb l&apos;emmagatzematge en el núvol</translation>
</message>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="645"/>
@@ -6442,12 +6442,12 @@ Exporteu aquesta plantilla a un fitxer diferent.</translation>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="701"/>
<source>Loading dives from local storage (&apos;no cloud&apos; mode)</source>
- <translation>S&apos;estan carregant les immersions des de l&apos;emmagatzematge local (mode «sense el núvol»)</translation>
+ <translation>S&apos;estan carregant les immersions des de l&apos;emmagatzematge local («Mode sense el núvol»)</translation>
</message>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="747"/>
<source>Failed to connect to cloud server, reverting to no cloud status</source>
- <translation>Ha fallat en connectar amb el servidor del núvol, no s&apos;ha rebut l&apos;estat</translation>
+ <translation>Ha fallat en connectar amb el servidor en el núvol, no s&apos;ha rebut l&apos;estat</translation>
</message>
<message>
<location filename="../mobile-widgets/qmlmanager.cpp" line="772"/>
@@ -7057,7 +7057,7 @@ Fitxers amb una data/hora inapropiada</translation>
<message>
<location filename="../mobile-widgets/qml/StartPage.qml" line="50"/>
<source>Thank you for registering with Subsurface. We sent &lt;b&gt;%1&lt;/b&gt; a PIN code to complete the registration. If you do not receive an email from us within 15 minutes, please check the correct spelling of your email address and your spam box first.&lt;br/&gt;&lt;br/&gt;In case of any problems regarding cloud account setup, please contact us at our user forum (https://subsurface-divelog.org/user-forum/).&lt;br/&gt;&lt;br/&gt;</source>
- <translation>Gràcies per registrar-vos amb el Subsurface. Hem enviat a &lt;b&gt;%1&lt;/b&gt; un codi PIN per a completar el registre. Si no rebeu un correu electrònic nostre en 15 minuts, si us plau, primer comproveu la correcta ortografia de la vostra adreça de correu electrònic i la vostra bústia de correu brossa.&lt;br/&gt;&lt;br/&gt;En cas de problemes relacionats amb la configuració del compte del núvol, poseu-vos en contacte amb nosaltres al nostre fòrum d&apos;usuari (https://subsurface-divelog.org/user-forum/).&lt;br/&gt;&lt;br/&gt;q</translation>
+ <translation>Gràcies per registrar-vos amb el Subsurface. Hem enviat a &lt;b&gt;%1&lt;/b&gt; un codi PIN per a completar el registre. Si no rebeu un correu electrònic nostre en 15 minuts, si us plau, primer comproveu la correcta ortografia de la vostra adreça de correu electrònic i la vostra bústia de correu brossa.&lt;br/&gt;&lt;br/&gt;En cas de problemes relacionats amb la configuració del compte en el núvol, poseu-vos en contacte amb nosaltres al nostre fòrum d&apos;usuaris (https://subsurface-divelog.org/user-forum/).&lt;br/&gt;&lt;br/&gt;q</translation>
</message>
</context>
<context>
@@ -8883,7 +8883,7 @@ Màxim</translation>
<message>
<location filename="../core/git-access.c" line="152"/>
<source>Local cache directory %s corrupted - can&apos;t sync with Subsurface cloud storage</source>
- <translation>El directori del cau local %s és corrupte -no s&apos;ha pogut sincronitzar amb l&apos;emmagatzematge al núvol del Subsurface-</translation>
+ <translation>El directori del cau local %s està corrupte -no s&apos;ha pogut sincronitzar amb l&apos;emmagatzematge en el núvol de Subsurface-</translation>
</message>
<message>
<location filename="../core/git-access.c" line="175"/>
@@ -8894,12 +8894,12 @@ Màxim</translation>
<message>
<location filename="../core/git-access.c" line="188"/>
<source>Subsurface cloud storage corrupted</source>
- <translation>Emmagatzematge al núvol del Subsurface és corrupte</translation>
+ <translation>L&apos;emmagatzematge en el núvol de Subsurface està corrupte</translation>
</message>
<message>
<location filename="../core/git-access.c" line="324"/>
<source>Could not update Subsurface cloud storage, try again later</source>
- <translation>No s&apos;ha pogut actualitzar l&apos;emmagatzematge al núvol del Subsurface, proveu-ho més tard</translation>
+ <translation>No s&apos;ha pogut actualitzar l&apos;emmagatzematge en el núvol de Subsurface, proveu-ho més tard</translation>
</message>
<message>
<location filename="../core/git-access.c" line="384"/>
@@ -8924,7 +8924,7 @@ Màxim</translation>
<message>
<location filename="../core/git-access.c" line="460"/>
<source>Problems with local cache of Subsurface cloud data</source>
- <translation>Problemes amb el cau local de les dades al núvol del Subsurface</translation>
+ <translation>Problemes amb el cau local de les dades al núvol de Subsurface</translation>
</message>
<message>
<location filename="../core/git-access.c" line="461"/>
@@ -8934,7 +8934,7 @@ Màxim</translation>
<message>
<location filename="../core/git-access.c" line="508"/>
<source>Update local storage to match cloud storage</source>
- <translation>Actualitza l&apos;emmagatzematge local per fer-lo coincidir amb l&apos;emmagatzematge al núvol</translation>
+ <translation>Actualitza l&apos;emmagatzematge local per a fer-lo coincidir amb l&apos;emmagatzematge en el núvol</translation>
</message>
<message>
<location filename="../core/git-access.c" line="516"/>
@@ -8974,7 +8974,7 @@ Màxim</translation>
<message>
<location filename="../core/git-access.c" line="794"/>
<source>Error connecting to Subsurface cloud storage</source>
- <translation>Error en connectar amb l&apos;emmagatzematge al núvol del Subsurface</translation>
+ <translation>Error en connectar amb l&apos;emmagatzematge en el núvol de Subsurface</translation>
</message>
<message>
<location filename="../core/git-access.c" line="797"/>
@@ -10803,7 +10803,7 @@ Està connectat correctament el Uemis Zurich?</translation>
<message>
<location filename="../mobile-widgets/qml/main.qml" line="280"/>
<source>Turning off automatic sync to cloud causes all data to only be stored locally. This can be very useful in situations with limited or no network access. Please choose &apos;Manual sync with cloud&apos; if you have network connectivity and want to sync your data to cloud storage.</source>
- <translation>Desactivar la sincronització automàtica amb el núvol fa que totes les dades només es puguin emmagatzemar localment. Això pot ser molt útil en situacions amb accés limitat o sense a la xarxa. Si us plau, seleccioneu «Sincronitza manualment amb el núvol» si teniu connectivitat a la xarxa i voleu sincronitzar les vostres dades amb l&apos;emmagatzematge en el núvol.</translation>
+ <translation>Desactivar la sincronització automàtica amb el núvol fa que totes les dades només es puguin emmagatzemar localment. Això pot ser molt útil en situacions amb accés limitat o sense la xarxa. Si us plau, seleccioneu «Sincronitza manualment amb el núvol» si teniu connectivitat a la xarxa i voleu sincronitzar les vostres dades amb l&apos;emmagatzematge en el núvol.</translation>
</message>
<message>
<location filename="../mobile-widgets/qml/main.qml" line="291"/>
diff --git a/translations/subsurface_es_ES.ts b/translations/subsurface_es_ES.ts
index 5e1a426e7..6a427b59e 100644
--- a/translations/subsurface_es_ES.ts
+++ b/translations/subsurface_es_ES.ts
@@ -3934,28 +3934,28 @@ p, li { white-space: pre-wrap; }
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.ui" line="23"/>
<source>Find moved images</source>
- <translation type="unfinished"/>
+ <translation>Encontrar imágenes movidas</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.ui" line="37"/>
<source>Found images</source>
- <translation type="unfinished"/>
+ <translation>Imágenes encontradas</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.ui" line="71"/>
<source>Match only images in selected dive(s)</source>
- <translation type="unfinished"/>
+ <translation>Buscar imágenes solo en la(s) inmersión(es) seleccionada(s)</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.ui" line="95"/>
<source>Scanning:</source>
- <translation type="unfinished"/>
+ <translation>Escaneando:</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.ui" line="121"/>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="240"/>
<source>Select folder and scan</source>
- <translation type="unfinished"/>
+ <translation>Seleccionar carpeta y escanear</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="173"/>
@@ -3965,27 +3965,27 @@ p, li { white-space: pre-wrap; }
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="179"/>
<source>Stop scanning</source>
- <translation type="unfinished"/>
+ <translation>Detener escaneo</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="250"/>
<source>Scanning cancelled - results may be incomplete</source>
- <translation type="unfinished"/>
+ <translation>Escaneo cancelado - los resultados pueden ser incompletos</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="254"/>
<source>No matching images found</source>
- <translation type="unfinished"/>
+ <translation>No se han encontrado imágenes que coincidan</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="266"/>
<source>Found &lt;b&gt;%1&lt;/b&gt; images at their current place.</source>
- <translation type="unfinished"/>
+ <translation>Encontradas &lt;b&gt;%1&lt;/b&gt; imágenes en su ubicación actual.</translation>
</message>
<message>
<location filename="../desktop-widgets/findmovedimagesdialog.cpp" line="268"/>
<source>Found &lt;b&gt;%1&lt;/b&gt; images at new locations:</source>
- <translation type="unfinished"/>
+ <translation>Encontradas &lt;b&gt;%1&lt;/b&gt; imágenes en nuevas ubicaciones:</translation>
</message>
</context>
<context>
@@ -6254,7 +6254,7 @@ Por favor, exporta esta plantilla a un archivo distinto.</translation>
<message>
<location filename="../profile-widget/profilewidget2.cpp" line="1432"/>
<source>Split dive into two</source>
- <translation type="unfinished"/>
+ <translation>Dividir la inmersión en dos</translation>
</message>
<message>
<location filename="../profile-widget/profilewidget2.cpp" line="1442"/>
@@ -9912,7 +9912,7 @@ EADD: %d%s / %.1fg/ℓ
<message numerus="yes">
<location filename="../core/qthelper.cpp" line="1010"/>
<source>(%n dive(s))</source>
- <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+ <translation><numerusform>(%n inmersión(es))</numerusform><numerusform>(%n inmersión(es))</numerusform></translation>
</message>
<message>
<location filename="../core/qthelper.cpp" line="1246"/>