summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--CMakeLists.txt2
-rw-r--r--Documentation/mobile-manual.txt7
-rw-r--r--android-mobile/build.gradle64
-rw-r--r--core/btdiscovery.cpp5
-rw-r--r--core/dive.c158
-rw-r--r--core/dive.h28
-rw-r--r--core/divelist.c149
-rw-r--r--core/divelist.h5
-rw-r--r--core/downloadfromdcthread.cpp23
-rw-r--r--core/downloadfromdcthread.h5
-rw-r--r--core/libdivecomputer.c2
-rw-r--r--core/qthelper.cpp14
-rw-r--r--core/statistics.c1
-rw-r--r--core/statistics.h1
-rw-r--r--core/subsurface-qt/DiveObjectHelper.cpp43
-rw-r--r--desktop-widgets/CMakeLists.txt3
-rw-r--r--desktop-widgets/command_divelist.cpp26
-rw-r--r--desktop-widgets/divelistview.cpp12
-rw-r--r--desktop-widgets/divelogimportdialog.cpp1
-rw-r--r--desktop-widgets/downloadfromdivecomputer.cpp24
-rw-r--r--desktop-widgets/filterwidget.ui140
-rw-r--r--desktop-widgets/filterwidget2.cpp105
-rw-r--r--desktop-widgets/filterwidget2.h35
-rw-r--r--desktop-widgets/filterwidget2.ui260
-rw-r--r--desktop-widgets/listfilter.ui4
-rw-r--r--desktop-widgets/locationinformation.cpp2
-rw-r--r--desktop-widgets/mainwindow.cpp24
-rw-r--r--desktop-widgets/mainwindow.h2
-rw-r--r--desktop-widgets/mainwindow.ui60
-rw-r--r--desktop-widgets/modeldelegates.cpp5
-rw-r--r--desktop-widgets/printdialog.cpp1
-rw-r--r--desktop-widgets/simplewidgets.cpp113
-rw-r--r--desktop-widgets/simplewidgets.h54
-rw-r--r--desktop-widgets/subsurfacewebservices.cpp4
-rw-r--r--desktop-widgets/tab-widgets/TabDiveStatistics.cpp14
-rw-r--r--desktop-widgets/tab-widgets/TabDiveStatistics.ui2
-rw-r--r--desktop-widgets/tab-widgets/maintab.cpp2
m---------libdivecomputer0
-rw-r--r--mobile-widgets/qml/CopySettings.qml168
-rw-r--r--mobile-widgets/qml/DiveList.qml5
-rw-r--r--mobile-widgets/qml/DownloadFromDiveComputer.qml43
-rw-r--r--mobile-widgets/qml/main.qml5
-rw-r--r--mobile-widgets/qml/mobile-resources.qrc1
-rw-r--r--mobile-widgets/qmlmanager.cpp161
-rw-r--r--mobile-widgets/qmlmanager.h12
-rwxr-xr-xpackaging/android/android-build-wrapper.sh1
-rwxr-xr-xpackaging/android/build.sh1
-rw-r--r--qt-models/diveimportedmodel.cpp41
-rw-r--r--qt-models/diveimportedmodel.h7
-rw-r--r--qt-models/divetripmodel.cpp5
-rw-r--r--qt-models/filtermodels.cpp652
-rw-r--r--qt-models/filtermodels.h129
-rw-r--r--qt-models/yearlystatisticsmodel.cpp8
-rw-r--r--qt-models/yearlystatisticsmodel.h1
-rw-r--r--scripts/windows-container/before_install.sh1
-rw-r--r--tests/testpicture.cpp6
57 files changed, 1162 insertions, 1489 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9cc29adb8..3021da2d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
+- Core: shift dive time in correct direction [#1893]
+- Include average max depth in statistics
+- Fix bug in cloud save after removing dives from a trip
- Dive: Perform more accurate OTU calculations, and include
OTU calculations for rebreather dives [#1851 & #1865].
+- Mobile: UI for copy-paste
- Mobile: add initial copy-paste support
- Desktop: translate trip date
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0cc383d98..1556707e6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -411,14 +411,12 @@ if(ANDROID)
if((DEFINED ENV{KEYSTORE}) AND (DEFINED ENV{KEYSTORE_PASSWORD}))
add_qt_android_apk(${SUBSURFACE_TARGET}.apk ${SUBSURFACE_TARGET}
PACKAGE_SOURCES ${CMAKE_BINARY_DIR}/android-mobile DEPENDS ${ANDROID_NATIVE_LIBSSL} ${ANDROID_NATIVE_LIBCRYPT}
- BUILDTOOLS_REVISION ${BUILDTOOLS_REVISION}
KEYSTORE $ENV{KEYSTORE} Subsurface-mobile KEYSTORE_PASSWORD $ENV{KEYSTORE_PASSWORD}
)
message(STATUS "KEYSTORE=$ENV{KEYSTORE} KEYSTORE_PASSWORD=$ENV{KEYSTORE_PASSWORD}")
else()
add_qt_android_apk(${SUBSURFACE_TARGET}.apk ${SUBSURFACE_TARGET}
PACKAGE_SOURCES ${CMAKE_BINARY_DIR}/android-mobile DEPENDS ${ANDROID_NATIVE_LIBSSL} ${ANDROID_NATIVE_LIBCRYPT}
- BUILDTOOLS_REVISION ${BUILDTOOLS_REVISION}
)
message(STATUS "no KEYSTORE")
endif()
diff --git a/Documentation/mobile-manual.txt b/Documentation/mobile-manual.txt
index 5b48cf920..2a869674d 100644
--- a/Documentation/mobile-manual.txt
+++ b/Documentation/mobile-manual.txt
@@ -280,10 +280,9 @@ The dive is deleted without asking any confirmation because _Subsurface-mobile_
combination of a long tap on the dive with another tap on the red dustbin is an unambiguous
instruction to delete the dive.
-A dive can also be deleted from the Details View which has an Action Bar with a dustbin. If this is tapped,
-the dive shown in the _Details View_ is deleted. You have a brief opportunity to undo
-the delete by tapping the grey _Undo_ button in the message that appears at the bottom of
-the screen (see image below).
+To choose what dive details to copy, long-press the copy button. This will open
+up a configuration page where you can toggle the details you want to copy over
+to the destination. By default, the following fields are copied:
image::mobile-images/Delete_undo.jpg["FIGURE: Undo delete dive",align="center"]
diff --git a/android-mobile/build.gradle b/android-mobile/build.gradle
new file mode 100644
index 000000000..4dd4a9f32
--- /dev/null
+++ b/android-mobile/build.gradle
@@ -0,0 +1,64 @@
+/*******************************************************
+ * SPDX-License-Identifier: GPL-2.0
+ * Subsurface-Mobile own Gradle build spec. Derived from
+ * the one supplied by Qt.
+ *******************************************************/
+buildscript {
+ repositories {
+ jcenter()
+ maven { url "https://dl.bintray.com/android/android-tools/" }
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.3'
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ maven { url "https://dl.bintray.com/android/android-tools/" }
+ }
+}
+
+apply plugin: 'com.android.application'
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+}
+
+android {
+ /*******************************************************
+ * The following variables:
+ * - androidBuildToolsVersion,
+ * - androidCompileSdkVersion
+ * - qt5AndroidDir - holds the path to qt android files
+ * needed to build any Qt application
+ * on Android.
+ *
+ * are defined in gradle.properties file. This file is
+ * updated by QtCreator and androiddeployqt tools.
+ * Changing them manually might break the compilation!
+ *******************************************************/
+
+ compileSdkVersion androidCompileSdkVersion.toInteger()
+
+ buildToolsVersion androidBuildToolsVersion
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
+ aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
+ res.srcDirs = [qt5AndroidDir + '/res', 'res']
+ resources.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ assets.srcDirs = ['assets']
+ jniLibs.srcDirs = ['libs']
+ }
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+}
diff --git a/core/btdiscovery.cpp b/core/btdiscovery.cpp
index 6ec4bba7f..207dfacf8 100644
--- a/core/btdiscovery.cpp
+++ b/core/btdiscovery.cpp
@@ -74,6 +74,11 @@ static dc_descriptor_t *getDeviceType(QString btName)
product = "i770R";
}
+ if (btName.contains(QRegularExpression("^ER\\d{6}$"))) {
+ vendor = "Oceanic";
+ product = "Pro Plus X";
+ }
+
if (!vendor.isEmpty() && !product.isEmpty())
return descriptorLookup.value(vendor + product);
diff --git a/core/dive.c b/core/dive.c
index 47143aaa3..49431bffe 100644
--- a/core/dive.c
+++ b/core/dive.c
@@ -3357,70 +3357,6 @@ void dump_taglist(const char *intro, struct tag_entry *tl)
fprintf(stderr, "\n");
}
-// count the dives where the tag list contains the given tag
-int count_dives_with_tag(const char *tag)
-{
- int i, counter = 0;
- struct dive *d;
-
- for_each_dive (i, d) {
- if (empty_string(tag)) {
- // count dives with no tags
- if (d->tag_list == NULL)
- counter++;
- } else if (taglist_contains(d->tag_list, tag)) {
- counter++;
- }
- }
- return counter;
-}
-
-extern bool string_sequence_contains(const char *string_sequence, const char *text);
-
-// count the dives where the person is included in the comma separated string sequences of buddies or divemasters
-int count_dives_with_person(const char *person)
-{
- int i, counter = 0;
- struct dive *d;
-
- for_each_dive (i, d) {
- if (empty_string(person)) {
- // solo dive
- if (empty_string(d->buddy) && empty_string(d->divemaster))
- counter++;
- } else if (string_sequence_contains(d->buddy, person) || string_sequence_contains(d->divemaster, person)) {
- counter++;
- }
- }
- return counter;
-}
-
-// count the dives with exactly the location
-int count_dives_with_location(const char *location)
-{
- int i, counter = 0;
- struct dive *d;
-
- for_each_dive (i, d) {
- if (same_string(get_dive_location(d), location))
- counter++;
- }
- return counter;
-}
-
-// count the dives with exactly the suit
-int count_dives_with_suit(const char *suit)
-{
- int i, counter = 0;
- struct dive *d;
-
- for_each_dive (i, d) {
- if (same_string(d->suit, suit))
- counter++;
- }
- return counter;
-}
-
/*
* Merging two dives can be subtle, because there's two different ways
* of merging:
@@ -3686,17 +3622,6 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou
return nr;
}
-static void finish_split(int nr, struct dive *old, struct dive *d1, struct dive *d2)
-{
- if (old->divetrip) {
- add_dive_to_trip(d1, old->divetrip);
- add_dive_to_trip(d2, old->divetrip);
- }
- delete_single_dive(nr);
- add_single_dive(nr, d1);
- add_single_dive(nr + 1, d2);
-}
-
/* in freedive mode we split for as little as 10 seconds on the surface,
* otherwise we use a minute */
static bool should_split(const struct divecomputer *dc, int t1, int t2)
@@ -3716,7 +3641,7 @@ static bool should_split(const struct divecomputer *dc, int t1, int t2)
*
* In other words, this is a (simplified) reversal of the dive merging.
*/
-int split_dive_dont_insert(const struct dive *dive, struct dive **new1, struct dive **new2)
+int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2)
{
int i;
int at_surface, surface_start;
@@ -3758,16 +3683,7 @@ int split_dive_dont_insert(const struct dive *dive, struct dive **new1, struct d
return -1;
}
-void split_dive(struct dive *dive)
-{
- int nr;
- struct dive *new1, *new2;
-
- if ((nr = split_dive_dont_insert(dive, &new1, &new2)) >= 0)
- finish_split(nr, dive, new1, new2);
-}
-
-int split_dive_at_time_dont_insert(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2)
+int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2)
{
int i = 0;
struct sample *sample = dive->dc.sample;
@@ -3783,15 +3699,6 @@ int split_dive_at_time_dont_insert(const struct dive *dive, duration_t time, str
return split_dive_at(dive, i, i - 1, new1, new2);
}
-void split_dive_at_time(struct dive *dive, duration_t time)
-{
- int nr;
- struct dive *new1, *new2;
-
- if ((nr = split_dive_at_time_dont_insert(dive, time, &new1, &new2)) >= 0)
- finish_split(nr, dive, new1, new2);
-}
-
/*
* "dc_maxtime()" is how much total time this dive computer
* has for this dive. Note that it can differ from "duration"
@@ -4004,20 +3911,51 @@ static bool new_picture_for_dive(struct dive *d, const char *filename)
return true;
}
+/* Return distance of timestamp to time of dive. Result is always positive, 0 means during dive. */
+static timestamp_t time_from_dive(const struct dive *d, timestamp_t timestamp)
+{
+ timestamp_t end_time = dive_endtime(d);
+ if (timestamp < d->when)
+ return d->when - timestamp;
+ else if (timestamp > end_time)
+ return timestamp - end_time;
+ else
+ return 0;
+}
+
// only add pictures that have timestamps between 30 minutes before the dive and
// 30 minutes after the dive ends
#define D30MIN (30 * 60)
-bool dive_check_picture_time(const struct dive *d, int shift_time, timestamp_t timestamp)
+static bool dive_check_picture_time(const struct dive *d, timestamp_t timestamp)
{
- offset_t offset;
- if (timestamp) {
- offset.seconds = timestamp - d->when + shift_time;
- if (offset.seconds > -D30MIN && offset.seconds < dive_totaltime(d) + D30MIN) {
- // this picture belongs to this dive
- return true;
+ return time_from_dive(d, timestamp) < D30MIN;
+}
+
+/* Return dive closest selected dive to given timestamp or NULL if no dives are selected. */
+static struct dive *nearest_selected_dive(timestamp_t timestamp)
+{
+ struct dive *d, *res = NULL;
+ int i;
+ timestamp_t offset, min = 0;
+
+ for_each_dive(i, d) {
+ if (!d->selected)
+ continue;
+ offset = time_from_dive(d, timestamp);
+ if (!res || offset < min) {
+ res = d;
+ min = offset;
}
+
+ /* We suppose that dives are sorted chronologically. Thus
+ * if the offset starts to increase, we can end. This ignores
+ * pathological cases such as overlapping dives. In such a
+ * case the user will have to add pictures manually.
+ */
+ if (offset == 0 || offset > min)
+ break;
}
- return false;
+ return res;
}
bool picture_check_valid_time(timestamp_t timestamp, int shift_time)
@@ -4026,18 +3964,26 @@ bool picture_check_valid_time(timestamp_t timestamp, int shift_time)
struct dive *dive;
for_each_dive (i, dive)
- if (dive->selected && dive_check_picture_time(dive, shift_time, timestamp))
+ if (dive->selected && dive_check_picture_time(dive, timestamp + shift_time))
return true;
return false;
}
-void dive_create_picture(struct dive *dive, const char *filename, int shift_time, bool match_all)
+void create_picture(const char *filename, int shift_time, bool match_all)
{
struct metadata metadata;
+ struct dive *dive;
+ timestamp_t timestamp;
+
get_metadata(filename, &metadata);
+ timestamp = metadata.timestamp + shift_time;
+ dive = nearest_selected_dive(timestamp);
+
+ if (!dive)
+ return;
if (!new_picture_for_dive(dive, filename))
return;
- if (!match_all && !dive_check_picture_time(dive, shift_time, metadata.timestamp))
+ if (!match_all && !dive_check_picture_time(dive, timestamp))
return;
struct picture *picture = alloc_picture();
diff --git a/core/dive.h b/core/dive.h
index 92bb1bb33..6bc0f6fae 100644
--- a/core/dive.h
+++ b/core/dive.h
@@ -227,12 +227,7 @@ void taglist_cleanup(struct tag_entry **tag_list);
void taglist_init_global();
void taglist_free(struct tag_entry *tag_list);
-
bool taglist_contains(struct tag_entry *tag_list, const char *tag);
-int count_dives_with_tag(const char *tag);
-int count_dives_with_person(const char *person);
-int count_dives_with_location(const char *location);
-int count_dives_with_suit(const char *suit);
struct extra_data {
const char *key;
@@ -278,10 +273,10 @@ struct divecomputer {
#define W_IDX_PRIMARY 0
#define W_IDX_SECONDARY 1
-struct dive_table {
+typedef struct dive_table {
int nr, allocated;
struct dive **dives;
-};
+} dive_table_t;
typedef struct dive_trip
{
@@ -376,8 +371,7 @@ struct picture {
extern struct picture *alloc_picture();
extern void free_picture(struct picture *picture);
-extern bool dive_check_picture_time(const struct dive *d, int shift_time, timestamp_t timestamp);
-extern void dive_create_picture(struct dive *d, const char *filename, int shift_time, bool match_all);
+extern void create_picture(const char *filename, int shift_time, bool match_all);
extern void dive_add_picture(struct dive *d, struct picture *newpic);
extern bool dive_remove_picture(struct dive *d, const char *filename);
extern unsigned int dive_get_picture_count(struct dive *d);
@@ -425,7 +419,7 @@ extern const struct units SI_units, IMPERIAL_units;
extern const struct units *get_units(void);
extern int run_survey, verbose, quit, force_root;
-extern struct dive_table dive_table, downloadTable;
+extern struct dive_table dive_table;
extern struct dive displayed_dive;
extern unsigned int dc_number;
extern struct dive *current_dive;
@@ -553,10 +547,8 @@ extern void fixup_dc_duration(struct divecomputer *dc);
extern int dive_getUniqID();
extern unsigned int dc_airtemp(const struct divecomputer *dc);
extern unsigned int dc_watertemp(const struct divecomputer *dc);
-extern int split_dive_dont_insert(const struct dive *dive, struct dive **new1, struct dive **new2);
-extern void split_dive(struct dive *);
-extern int split_dive_at_time_dont_insert(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2);
-extern void split_dive_at_time(struct dive *dive, duration_t time);
+extern int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2);
+extern int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2);
extern struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, bool prefer_downloaded, struct dive_trip **trip);
extern struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded);
extern struct event *clone_event(const struct event *src_ev);
@@ -759,10 +751,14 @@ extern void average_max_depth(struct diveplan *dive, int *avg_depth, int *max_de
#ifdef __cplusplus
}
-/* Make pointers to dive and dive_trip "Qt metatypes" so that they can
- * be passed through QVariants. */
+/* Make pointers to dive, dive_trip and dive_table "Qt metatypes" so that they can
+ * be passed through QVariants and through QML.
+ * Note: we have to use the typedef "dive_table_t" instead of "struct dive_table",
+ * because MOC removes the "struct", but dive_table is already the name of a global
+ * variable, leading to compilation errors. */
Q_DECLARE_METATYPE(struct dive *);
Q_DECLARE_METATYPE(struct dive_trip *);
+Q_DECLARE_METATYPE(dive_table_t *);
#endif
diff --git a/core/divelist.c b/core/divelist.c
index ee7986466..4659261cd 100644
--- a/core/divelist.c
+++ b/core/divelist.c
@@ -19,7 +19,6 @@
* void insert_trip(dive_trip_t *dive_trip_p)
* void unregister_trip(dive_trip_t *trip)
* void free_trip(dive_trip_t *trip)
- * void remove_dive_from_trip(struct dive *dive)
* void remove_dive_from_trip(struct dive *dive, bool was_autogen)
* void add_dive_to_trip(struct dive *dive, dive_trip_t *trip)
* dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive)
@@ -32,12 +31,10 @@
* void delete_single_dive(int idx)
* void add_dive_to_table(struct dive_table *table, int idx, struct dive *dive)
* void add_single_dive(int idx, struct dive *dive)
- * struct dive *merge_two_dives(struct dive *a, struct dive *b)
* void select_dive(struct dive *dive)
* void deselect_dive(struct dive *dive)
* void mark_divelist_changed(int changed)
* int unsaved_changes()
- * void remove_autogen_trips()
* bool dive_less_than(const struct dive *a, const struct dive *b)
* bool trip_less_than(const struct dive_trip *a, const struct dive_trip *b)
* bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b)
@@ -77,9 +74,6 @@ dive_trip_t *dive_trip_list;
unsigned int amount_selected;
-// We need to stop using globals, really.
-struct dive_table downloadTable;
-
#if DEBUG_SELECTION_TRACKING
void dump_selection(void)
{
@@ -912,11 +906,14 @@ void remove_dive_from_trip(struct dive *dive, short was_autogen)
delete_trip(trip);
}
+/* Add dive to a trip. Caller is responsible for removing dive
+ * from trip beforehand. */
void add_dive_to_trip(struct dive *dive, dive_trip_t *trip)
{
if (dive->divetrip == trip)
return;
- remove_dive_from_trip(dive, false);
+ if (dive->divetrip)
+ fprintf(stderr, "Warning: adding dive to trip that has trip set\n");
add_dive_to_table(&trip->dives, -1, dive);
dive->divetrip = trip;
}
@@ -1075,16 +1072,15 @@ void delete_dive_from_table(struct dive_table *table, int idx)
unregister_dive_from_table(table, idx);
}
-/* this removes a dive from the dive table and trip-list but doesn't
- * free the resources associated with the dive. It returns a pointer
- * to the unregistered dive. The returned dive has the selection-
- * and hidden-flags cleared. */
+/* This removes a dive from the global dive table but doesn't free the
+ * resources associated with the dive. The caller must removed the dive
+ * from the trip-list. Returns a pointer to the unregistered dive.
+ * The unregistered dive has the selection- and hidden-flags cleared. */
struct dive *unregister_dive(int idx)
{
struct dive *dive = get_dive(idx);
if (!dive)
return NULL; /* this should never happen */
- remove_dive_from_trip(dive, false);
unregister_dive_from_table(&dive_table, idx);
if (dive->selected)
amount_selected--;
@@ -1092,8 +1088,8 @@ struct dive *unregister_dive(int idx)
return dive;
}
-/* this implements the mechanics of removing the dive from the table,
- * but doesn't deal with updating dive trips, etc */
+/* this implements the mechanics of removing the dive from the global
+ * dive table and the trip, but doesn't deal with updating dive trips, etc */
void delete_single_dive(int idx)
{
struct dive *dive = get_dive(idx);
@@ -1101,8 +1097,8 @@ void delete_single_dive(int idx)
return; /* this should never happen */
if (dive->selected)
deselect_dive(dive);
- dive = unregister_dive(idx);
- free_dive(dive);
+ remove_dive_from_trip(dive, false);
+ delete_dive_from_table(&dive_table, idx);
}
struct dive **grow_dive_table(struct dive_table *table)
@@ -1184,87 +1180,6 @@ bool consecutive_selected()
return consecutive;
}
-/*
- * Merge two dives. 'a' is always before 'b' in the dive list
- * (and thus in time).
- */
-struct dive *merge_two_dives(struct dive *a, struct dive *b)
-{
- struct dive *res;
- int i, j, nr, nrdiff;
- int id;
-
- if (!a || !b)
- return NULL;
-
- id = a->id;
- i = get_divenr(a);
- j = get_divenr(b);
- if (i < 0 || j < 0)
- // something is wrong with those dives. Bail
- return NULL;
- res = merge_dives(a, b, b->when - a->when, false, NULL);
- if (!res)
- return NULL;
-
- /*
- * If 'a' and 'b' were numbered, and in proper order,
- * then the resulting dive will get the first number,
- * and the subsequent dives will be renumbered by the
- * difference.
- *
- * So if you had a dive list 1 3 6 7 8, and you
- * merge 1 and 3, the resulting numbered list will
- * be 1 4 5 6, because we assume that there were
- * some missing dives (originally dives 4 and 5),
- * that now will still be missing (dives 2 and 3
- * in the renumbered world).
- *
- * Obviously the normal case is that everything is
- * consecutive, and the difference will be 1, so the
- * above example is not supposed to be normal.
- */
- nrdiff = 0;
- nr = a->number;
- if (a->number && b->number > a->number) {
- res->number = nr;
- nrdiff = b->number - nr;
- }
-
- add_single_dive(i, res);
- delete_single_dive(i + 1);
- delete_single_dive(j);
- // now make sure that we keep the id of the first dive.
- // why?
- // because this way one of the previously selected ids is still around
- res->id = id;
-
- // renumber dives from merged one in advance by difference between
- // merged dives numbers. Do not renumber if actual number is zero.
- for (; j < dive_table.nr; j++) {
- struct dive *dive = dive_table.dives[j];
- int newnr;
-
- if (!dive->number)
- continue;
- newnr = dive->number - nrdiff;
-
- /*
- * Don't renumber stuff that isn't in order!
- *
- * So if the new dive number isn't larger than the
- * previous dive number, just stop here.
- */
- if (newnr <= nr)
- break;
- dive->number = newnr;
- nr = newnr;
- }
-
- mark_divelist_changed(true);
- return res;
-}
-
void select_dive(struct dive *dive)
{
if (!dive)
@@ -1336,34 +1251,15 @@ void filter_dive(struct dive *d, bool shown)
}
-/* This only gets called with non-NULL trips.
- * It does not combine notes or location, just picks the first one
- * (or the second one if the first one is empty */
-void combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b)
-{
- if (empty_string(trip_a->location) && trip_b->location) {
- free(trip_a->location);
- trip_a->location = strdup(trip_b->location);
- }
- if (empty_string(trip_a->notes) && trip_b->notes) {
- free(trip_a->notes);
- trip_a->notes = strdup(trip_b->notes);
- }
- /* this also removes the dives from trip_b and eventually
- * calls delete_trip(trip_b) when the last dive has been moved */
- while (trip_b->dives.nr > 0)
- add_dive_to_trip(trip_b->dives.dives[0], trip_a);
-}
-
/* Out of two strings, copy the string that is not empty (if any). */
static char *copy_non_empty_string(const char *a, const char *b)
{
return copy_string(empty_string(b) ? a : b);
}
-/* Combine trips new. This combines two trips, generating a
+/* This combines the information of two trips, generating a
* new trip. To support undo, we have to preserve the old trips. */
-dive_trip_t *combine_trips_create(struct dive_trip *trip_a, struct dive_trip *trip_b)
+dive_trip_t *combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b)
{
dive_trip_t *trip;
@@ -1387,19 +1283,6 @@ int unsaved_changes()
return dive_list_changed;
}
-void remove_autogen_trips()
-{
- int i;
- struct dive *dive;
-
- for_each_dive(i, dive) {
- dive_trip_t *trip = dive->divetrip;
-
- if (trip && trip->autogen)
- remove_dive_from_trip(dive, true);
- }
-}
-
/*
* When adding dives to the dive table, we try to renumber
* the new dives based on any old dives in the dive table.
@@ -1522,10 +1405,8 @@ static bool try_to_merge_into(struct dive *dive_to_add, int idx, bool prefer_imp
merged->id = old_dive->id;
merged->selected = old_dive->selected;
dive_table.dives[idx] = merged;
- if (trip) {
+ if (trip)
remove_dive_from_trip(old_dive, false);
- add_dive_to_trip(merged, trip);
- }
free_dive(old_dive);
remove_dive_from_trip(dive_to_add, false);
free_dive(dive_to_add);
diff --git a/core/divelist.h b/core/divelist.h
index 11fa75f50..ce4943660 100644
--- a/core/divelist.h
+++ b/core/divelist.h
@@ -14,7 +14,6 @@ struct dive;
extern void update_cylinder_related_info(struct dive *);
extern void mark_divelist_changed(bool);
extern int unsaved_changes(void);
-extern void remove_autogen_trips(void);
extern int init_decompression(struct deco_state *ds, struct dive *dive);
/* divelist core logic functions */
@@ -37,15 +36,13 @@ extern dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive);
extern dive_trip_t *get_dives_to_autogroup(int start, int *from, int *to, bool *allocated);
extern dive_trip_t *get_trip_for_new_dive(struct dive *new_dive, bool *allocated);
extern void autogroup_dives(void);
-extern struct dive *merge_two_dives(struct dive *a, struct dive *b);
extern bool consecutive_selected();
extern void select_dive(struct dive *dive);
extern void deselect_dive(struct dive *dive);
extern void select_dives_in_trip(struct dive_trip *trip);
extern void deselect_dives_in_trip(struct dive_trip *trip);
extern void filter_dive(struct dive *d, bool shown);
-extern void combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b);
-extern dive_trip_t *combine_trips_create(struct dive_trip *trip_a, struct dive_trip *trip_b);
+extern dive_trip_t *combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b);
extern struct dive *first_selected_dive();
extern struct dive *last_selected_dive();
extern bool is_trip_before_after(const struct dive *dive, bool before);
diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp
index 8e937346d..2fdebf129 100644
--- a/core/downloadfromdcthread.cpp
+++ b/core/downloadfromdcthread.cpp
@@ -24,7 +24,6 @@ static QString str_error(const char *fmt, ...)
return str;
}
-
static void updateRememberedDCs()
{
QString current = qPrefDiveComputer::vendor() + " - " + qPrefDiveComputer::product() + " - " + qPrefDiveComputer::device();
@@ -60,9 +59,9 @@ static void updateRememberedDCs()
}
-DownloadThread::DownloadThread()
+DownloadThread::DownloadThread() : downloadTable({ 0 }),
+ m_data(DCDeviceData::instance())
{
- m_data = DCDeviceData::instance();
}
void DownloadThread::run()
@@ -81,7 +80,7 @@ void DownloadThread::run()
internalData->devname = "ftdi";
#endif
qDebug() << "Starting download from " << (internalData->bluetooth_mode ? "BT" : internalData->devname);
- downloadTable.nr = 0;
+ clear_table(&downloadTable);
Q_ASSERT(internalData->download_table != nullptr);
const char *errorText;
@@ -254,7 +253,6 @@ void show_computer_list()
qDebug() << msg;
}
}
-DCDeviceData *DCDeviceData::m_instance = NULL;
DCDeviceData::DCDeviceData()
{
@@ -276,18 +274,12 @@ DCDeviceData::DCDeviceData()
#else
data.libdc_log = false;
#endif
- if (m_instance) {
- qDebug() << "already have an instance of DCDevieData";
- return;
- }
- m_instance = this;
}
DCDeviceData *DCDeviceData::instance()
{
- if (!m_instance)
- m_instance = new DCDeviceData;
- return m_instance;
+ static DCDeviceData self;
+ return &self;
}
QStringList DCDeviceData::getProductListFromVendor(const QString &vendor)
@@ -310,6 +302,11 @@ DCDeviceData *DownloadThread::data()
return m_data;
}
+struct dive_table *DownloadThread::table()
+{
+ return &downloadTable;
+}
+
QString DCDeviceData::vendor() const
{
return data.vendor;
diff --git a/core/downloadfromdcthread.h b/core/downloadfromdcthread.h
index 3e2d7ddc0..b380a88a1 100644
--- a/core/downloadfromdcthread.h
+++ b/core/downloadfromdcthread.h
@@ -6,6 +6,7 @@
#include <QHash>
#include <QLoggingCategory>
+#include "dive.h"
#include "libdivecomputer.h"
#include "connectionlistmodel.h"
#if BT_SUPPORT
@@ -51,7 +52,6 @@ public:
void setSaveDump(bool dumpMode);
void setSaveLog(bool saveLog);
private:
- static DCDeviceData *m_instance;
device_data_t data;
// Bluetooth name is managed outside of libdivecomputer
@@ -60,15 +60,18 @@ private:
class DownloadThread : public QThread {
Q_OBJECT
+ Q_PROPERTY(dive_table_t *table READ table CONSTANT)
public:
DownloadThread();
void run() override;
DCDeviceData *data();
+ struct dive_table *table();
QString error;
private:
+ struct dive_table downloadTable;
DCDeviceData *m_data;
};
diff --git a/core/libdivecomputer.c b/core/libdivecomputer.c
index a2e2890b8..4d2d29bad 100644
--- a/core/libdivecomputer.c
+++ b/core/libdivecomputer.c
@@ -1387,7 +1387,7 @@ const char *do_libdivecomputer_import(device_data_t *data)
/* TODO: Show the logfile to the user on error. */
dc_device_close(data->device);
data->device = NULL;
- if (!downloadTable.nr)
+ if (!data->download_table->nr)
dev_info(data, translate("gettextFromC", "No new dives downloaded from dive computer"));
}
dc_iostream_close(data->iostream);
diff --git a/core/qthelper.cpp b/core/qthelper.cpp
index 8fbaa31af..d47a39fdf 100644
--- a/core/qthelper.cpp
+++ b/core/qthelper.cpp
@@ -362,20 +362,6 @@ extern "C" void copy_image_and_overwrite(const char *cfileName, const char *path
qDebug() << "copy of" << fileName << "to" << newName << "failed";
}
-extern "C" bool string_sequence_contains(const char *string_sequence, const char *text)
-{
- if (empty_string(text) || empty_string(string_sequence))
- return false;
-
- QString stringSequence(string_sequence);
- QStringList strings = stringSequence.split(",", QString::SkipEmptyParts);
- Q_FOREACH (const QString& string, strings) {
- if (string.trimmed().compare(QString(text).trimmed(), Qt::CaseInsensitive) == 0)
- return true;
- }
- return false;
-}
-
static bool lessThan(const QPair<QString, int> &a, const QPair<QString, int> &b)
{
return a.second < b.second;
diff --git a/core/statistics.c b/core/statistics.c
index 6f5efe64f..d6d9418d7 100644
--- a/core/statistics.c
+++ b/core/statistics.c
@@ -53,6 +53,7 @@ static void process_dive(struct dive *dive, stats_t *stats)
stats->max_depth.mm = dive->maxdepth.mm;
if (stats->min_depth.mm == 0 || dive->maxdepth.mm < stats->min_depth.mm)
stats->min_depth.mm = dive->maxdepth.mm;
+ stats->combined_max_depth.mm += dive->maxdepth.mm;
process_temperatures(dive, stats);
diff --git a/core/statistics.h b/core/statistics.h
index d3707b9cb..6072f93b2 100644
--- a/core/statistics.h
+++ b/core/statistics.h
@@ -24,6 +24,7 @@ typedef struct
depth_t max_depth;
depth_t min_depth;
depth_t avg_depth;
+ depth_t combined_max_depth;
volume_t max_sac;
volume_t min_sac;
volume_t avg_sac;
diff --git a/core/subsurface-qt/DiveObjectHelper.cpp b/core/subsurface-qt/DiveObjectHelper.cpp
index e8e11e7d9..c85a3475e 100644
--- a/core/subsurface-qt/DiveObjectHelper.cpp
+++ b/core/subsurface-qt/DiveObjectHelper.cpp
@@ -8,14 +8,13 @@
#include "core/subsurface-string.h"
#include "qt-models/tankinfomodel.h"
-static QString EMPTY_DIVE_STRING = QStringLiteral("");
enum returnPressureSelector {START_PRESSURE, END_PRESSURE};
static QString getFormattedWeight(struct dive *dive, unsigned int idx)
{
weightsystem_t *weight = &dive->weightsystem[idx];
if (!weight->description)
- return QString(EMPTY_DIVE_STRING);
+ return QString();
QString fmt = QString(weight->description);
fmt += ", " + get_weight_string(weight->weight, true);
return fmt;
@@ -26,7 +25,7 @@ static QString getFormattedCylinder(struct dive *dive, unsigned int idx)
cylinder_t *cyl = &dive->cylinder[idx];
const char *desc = cyl->type.description;
if (!desc && idx > 0)
- return QString(EMPTY_DIVE_STRING);
+ return QString();
QString fmt = desc ? QString(desc) : gettextFromC::tr("unknown");
fmt += ", " + get_volume_string(cyl->type.size, true);
fmt += ", " + get_pressure_string(cyl->type.workingpressure, true);
@@ -107,7 +106,7 @@ QString DiveObjectHelper::time() const
QString DiveObjectHelper::location() const
{
- return get_dive_location(m_dive) ? QString::fromUtf8(get_dive_location(m_dive)) : EMPTY_DIVE_STRING;
+ return get_dive_location(m_dive) ? QString::fromUtf8(get_dive_location(m_dive)) : QString();
}
QString DiveObjectHelper::gps() const
@@ -149,35 +148,27 @@ QString DiveObjectHelper::depth() const
QString DiveObjectHelper::divemaster() const
{
- return m_dive->divemaster ? m_dive->divemaster : EMPTY_DIVE_STRING;
+ return m_dive->divemaster ? m_dive->divemaster : QString();
}
QString DiveObjectHelper::buddy() const
{
- return m_dive->buddy ? m_dive->buddy : EMPTY_DIVE_STRING;
+ return m_dive->buddy ? m_dive->buddy : QString();
}
QString DiveObjectHelper::airTemp() const
{
- QString temp = get_temperature_string(m_dive->airtemp, true);
- if (temp.isEmpty()) {
- temp = EMPTY_DIVE_STRING;
- }
- return temp;
+ return get_temperature_string(m_dive->airtemp, true);
}
QString DiveObjectHelper::waterTemp() const
{
- QString temp = get_temperature_string(m_dive->watertemp, true);
- if (temp.isEmpty()) {
- temp = EMPTY_DIVE_STRING;
- }
- return temp;
+ return get_temperature_string(m_dive->watertemp, true);
}
QString DiveObjectHelper::notes() const
{
- QString tmp = m_dive->notes ? QString::fromUtf8(m_dive->notes) : EMPTY_DIVE_STRING;
+ QString tmp = m_dive->notes ? QString::fromUtf8(m_dive->notes) : QString();
if (same_string(m_dive->dc.model, "planned dive")) {
QTextDocument notes;
#define _NOTES_BR "&#92n"
@@ -238,7 +229,7 @@ QString DiveObjectHelper::weightList() const
QString weights;
for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
QString w = getFormattedWeight(m_dive, i);
- if (w == EMPTY_DIVE_STRING)
+ if (w.isEmpty())
continue;
weights += w + "; ";
}
@@ -250,7 +241,7 @@ QStringList DiveObjectHelper::weights() const
QStringList weights;
for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
QString w = getFormattedWeight(m_dive, i);
- if (w == EMPTY_DIVE_STRING)
+ if (w.isEmpty())
continue;
weights << w;
}
@@ -265,13 +256,13 @@ bool DiveObjectHelper::singleWeight() const
QString DiveObjectHelper::weight(int idx) const
{
if ( (idx < 0) || idx > MAX_WEIGHTSYSTEMS )
- return QString(EMPTY_DIVE_STRING);
+ return QString();
return getFormattedWeight(m_dive, idx);
}
QString DiveObjectHelper::suit() const
{
- return m_dive->suit ? m_dive->suit : EMPTY_DIVE_STRING;
+ return m_dive->suit ? m_dive->suit : QString();
}
QStringList DiveObjectHelper::cylinderList() const
@@ -282,7 +273,7 @@ QStringList DiveObjectHelper::cylinderList() const
for_each_dive (i, d) {
for (int j = 0; j < MAX_CYLINDERS; j++) {
QString cyl = d->cylinder[j].type.description;
- if (cyl == EMPTY_DIVE_STRING)
+ if (cyl.isEmpty())
continue;
cylinders << cyl;
}
@@ -290,7 +281,7 @@ QStringList DiveObjectHelper::cylinderList() const
for (unsigned long ti = 0; ti < MAX_TANK_INFO && tank_info[ti].name != NULL; ti++) {
QString cyl = tank_info[ti].name;
- if (cyl == EMPTY_DIVE_STRING)
+ if (cyl.isEmpty())
continue;
cylinders << cyl;
}
@@ -305,7 +296,7 @@ QStringList DiveObjectHelper::cylinders() const
QStringList cylinders;
for (int i = 0; i < MAX_CYLINDERS; i++) {
QString cyl = getFormattedCylinder(m_dive, i);
- if (cyl == EMPTY_DIVE_STRING)
+ if (cyl.isEmpty())
continue;
cylinders << cyl;
}
@@ -315,7 +306,7 @@ QStringList DiveObjectHelper::cylinders() const
QString DiveObjectHelper::cylinder(int idx) const
{
if ( (idx < 0) || idx > MAX_CYLINDERS)
- return QString(EMPTY_DIVE_STRING);
+ return QString();
return getFormattedCylinder(m_dive, idx);
}
@@ -412,6 +403,6 @@ QString DiveObjectHelper::fullText() const
QString DiveObjectHelper::fullTextNoNotes() const
{
- QString tripLocation = m_dive->divetrip ? m_dive->divetrip->location : EMPTY_DIVE_STRING;
+ QString tripLocation = m_dive->divetrip ? m_dive->divetrip->location : QString();
return tripLocation + ":-:" + location() + ":-:" + buddy() + ":-:" + divemaster() + ":-:" + suit() + ":-:" + tags();
}
diff --git a/desktop-widgets/CMakeLists.txt b/desktop-widgets/CMakeLists.txt
index 519061ac0..629c0507e 100644
--- a/desktop-widgets/CMakeLists.txt
+++ b/desktop-widgets/CMakeLists.txt
@@ -33,7 +33,7 @@ set (SUBSURFACE_UI
diveplanner.ui
diveshareexportdialog.ui
downloadfromdivecomputer.ui
- filterwidget.ui
+ filterwidget2.ui
findmovedimagesdialog.ui
listfilter.ui
locationInformation.ui
@@ -90,6 +90,7 @@ set(SUBSURFACE_INTERFACE
command_divelist.cpp
locationinformation.cpp
qtwaitingspinner.cpp
+ filterwidget2.cpp
tab-widgets/TabDiveStatistics.cpp
tab-widgets/TabDiveInformation.cpp
tab-widgets/TabDivePhotos.cpp
diff --git a/desktop-widgets/command_divelist.cpp b/desktop-widgets/command_divelist.cpp
index 2c474cf94..b85b9a65f 100644
--- a/desktop-widgets/command_divelist.cpp
+++ b/desktop-widgets/command_divelist.cpp
@@ -92,6 +92,7 @@ dive *DiveListBase::addDive(DiveToAdd &d)
res->hidden_by_filter = !show;
add_single_dive(d.idx, res); // Return ownership to backend
+ invalidate_dive_cache(res); // Ensure that dive is written in git_save()
// If the dive to be removed is selected, we will inform the frontend
// later via a signal that the dive changed.
@@ -109,11 +110,6 @@ std::vector<DiveToAdd> DiveListBase::removeDives(std::vector<dive *> &divesToDel
std::vector<DiveToAdd> res;
res.reserve(divesToDelete.size());
- // First, tell the filters that dives are removed. This could
- // be done later using the emitted signals, but we do this here
- // for symmetry with addDives()
- MultiFilterSortModel::instance()->divesDeleted(QVector<dive *>::fromStdVector(divesToDelete));
-
for (dive *d: divesToDelete)
res.push_back(removeDive(d));
divesToDelete.clear();
@@ -153,7 +149,6 @@ std::vector<dive *> DiveListBase::addDives(std::vector<DiveToAdd> &divesToAdd)
QVector<dive *> divesForFilter;
for (const DiveToAdd &entry: divesToAdd)
divesForFilter.push_back(entry.dive.get());
- MultiFilterSortModel::instance()->divesAdded(divesForFilter);
// At the end of the function, to send the proper dives-added signals,
// we the the list of added trips. Create this list now.
@@ -191,7 +186,7 @@ std::vector<dive *> DiveListBase::addDives(std::vector<DiveToAdd> &divesToAdd)
// This helper function renumbers dives according to an array of id/number pairs.
// The old numbers are stored in the array, thus calling this function twice has no effect.
-// TODO: switch from uniq-id to indexes once all divelist-actions are controlled by undo-able commands
+// TODO: switch from uniq-id to indices once all divelist-actions are controlled by undo-able commands
static void renumberDives(QVector<QPair<dive *, int>> &divesToRenumber)
{
for (auto &pair: divesToRenumber) {
@@ -199,6 +194,7 @@ static void renumberDives(QVector<QPair<dive *, int>> &divesToRenumber)
if (!d)
continue;
std::swap(d->number, pair.second);
+ invalidate_dive_cache(d);
}
// Emit changed signals per trip.
@@ -239,6 +235,7 @@ static OwningTripPtr moveDiveToTrip(DiveToTrip &diveToTrip)
// Store old trip and get new trip we should associate this dive with
std::swap(trip, diveToTrip.trip);
add_dive_to_trip(diveToTrip.dive, trip);
+ invalidate_dive_cache(diveToTrip.dive); // Ensure that dive is written in git_save()
return res;
}
@@ -302,9 +299,12 @@ static void moveDivesBetweenTrips(DivesToTrip &dives)
for (size_t k = i; k < j; ++k)
divesInTrip[k - i] = divesMoved[k].d;
- // Check if the from-trip was deleted: If yes, it was recorded in the tripsToAdd structure
+ // Check if the from-trip was deleted: If yes, it was recorded in the tripsToAdd structure.
+ // Only set the flag if this is that last time this trip is featured.
bool deleteFrom = from &&
- std::find_if(dives.tripsToAdd.begin(), dives.tripsToAdd.end(),
+ std::find_if(divesMoved.begin() + j, divesMoved.end(), // Is this the last occurence of "from"?
+ [from](const DiveMoved &entry) { return entry.from == from; }) == divesMoved.end() &&
+ std::find_if(dives.tripsToAdd.begin(), dives.tripsToAdd.end(), // Is "from" in tripsToAdd?
[from](const OwningTripPtr &trip) { return trip.get() == from; }) != dives.tripsToAdd.end();
// Check if the to-trip has to be created. For this purpose, we saved an array of trips to be created.
bool createTo = false;
@@ -600,7 +600,7 @@ ShiftTime::ShiftTime(const QVector<dive *> &changedDives, int amount)
void ShiftTime::redoit()
{
for (dive *d: diveList)
- d->when -= timeChanged;
+ d->when += timeChanged;
// Changing times may have unsorted the dive table
sort_table(&dive_table);
@@ -737,7 +737,7 @@ MergeTrips::MergeTrips(dive_trip *trip1, dive_trip *trip2)
{
if (trip1 == trip2)
return;
- dive_trip *newTrip = combine_trips_create(trip1, trip2);
+ dive_trip *newTrip = combine_trips(trip1, trip2);
divesToMove.tripsToAdd.emplace_back(newTrip);
for (int i = 0; i < trip1->dives.nr; ++i)
divesToMove.divesToMove.push_back( { trip1->dives.dives[i], newTrip } );
@@ -752,8 +752,8 @@ SplitDives::SplitDives(dive *d, duration_t time)
// Split the dive
dive *new1, *new2;
int idx = time.seconds < 0 ?
- split_dive_dont_insert(d, &new1, &new2) :
- split_dive_at_time_dont_insert(d, time, &new1, &new2);
+ split_dive(d, &new1, &new2) :
+ split_dive_at_time(d, time, &new1, &new2);
// If this didn't work, simply return. Empty arrays indicate that nothing is to be done.
if (idx < 0)
diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp
index 9167bea5e..c340b556b 100644
--- a/desktop-widgets/divelistview.cpp
+++ b/desktop-widgets/divelistview.cpp
@@ -25,6 +25,7 @@
#include "qt-models/divepicturemodel.h"
#include "core/metrics.h"
#include "core/subsurface-qt/DiveListNotifier.h"
+#include "desktop-widgets/simplewidgets.h"
DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelection(false),
currentLayout(DiveTripModel::TREE), dontEmitDiveChangedSignal(false), selectionSaved(false),
@@ -969,15 +970,8 @@ void DiveListView::matchImagesToDives(QStringList fileNames)
return;
updateLastImageTimeOffset(shiftDialog.amount());
- Q_FOREACH (const QString &fileName, fileNames) {
- int j = 0;
- struct dive *dive;
- for_each_dive (j, dive) {
- if (!dive->selected)
- continue;
- dive_create_picture(dive, copy_qstring(fileName), shiftDialog.amount(), shiftDialog.matchAll());
- }
- }
+ for (const QString &fileName: fileNames)
+ create_picture(qPrintable(fileName), shiftDialog.amount(), shiftDialog.matchAll());
mark_divelist_changed(true);
copy_dive(current_dive, &displayed_dive);
diff --git a/desktop-widgets/divelogimportdialog.cpp b/desktop-widgets/divelogimportdialog.cpp
index 668b230a8..f4f9311ff 100644
--- a/desktop-widgets/divelogimportdialog.cpp
+++ b/desktop-widgets/divelogimportdialog.cpp
@@ -9,6 +9,7 @@
#include <QMimeData>
#include <QRegExp>
#include <QUndoStack>
+#include <QPainter>
#include "core/qthelper.h"
#include "core/import-csv.h"
diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp
index 3c70d8686..f92fdbb70 100644
--- a/desktop-widgets/downloadfromdivecomputer.cpp
+++ b/desktop-widgets/downloadfromdivecomputer.cpp
@@ -29,14 +29,12 @@ DownloadFromDCWidget::DownloadFromDCWidget(QWidget *parent, Qt::WindowFlags f) :
currentState(INITIAL)
{
diveImportedModel = new DiveImportedModel(this);
- diveImportedModel->setDiveTable(&downloadTable);
vendorModel.setStringList(vendorList);
QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), this);
QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this);
int startingWidth = defaultModelFont().pointSize();
- clear_table(&downloadTable);
ui.setupUi(this);
ui.progressBar->hide();
ui.progressBar->setMinimum(0);
@@ -254,7 +252,7 @@ void DownloadFromDCWidget::updateState(states state)
markChildrenAsEnabled();
progress_bar_text = "";
} else {
- if (downloadTable.nr != 0)
+ if (thread.table()->nr != 0)
progress_bar_text = "";
ui.progressBar->setValue(100);
markChildrenAsEnabled();
@@ -349,7 +347,7 @@ void DownloadFromDCWidget::on_downloadCancelRetryButton_clicked()
// this means we are retrying - so we better clean out the partial
// list of downloaded dives from the last attempt
diveImportedModel->clearTable();
- clear_table(&downloadTable);
+ clear_table(thread.table());
}
updateState(DOWNLOADING);
@@ -492,10 +490,7 @@ void DownloadFromDCWidget::onDownloadThreadFinished()
}
ui.downloadCancelRetryButton->setText(tr("Retry download"));
ui.downloadCancelRetryButton->setEnabled(true);
- // regardless, if we got dives, we should show them to the user
- if (downloadTable.nr) {
- diveImportedModel->setImportedDivesIndexes(0, downloadTable.nr - 1);
- }
+ diveImportedModel->repopulate(thread.table());
}
void DownloadFromDCWidget::on_cancel_clicked()
@@ -504,7 +499,7 @@ void DownloadFromDCWidget::on_cancel_clicked()
return;
// now discard all the dives
- clear_table(&downloadTable);
+ clear_table(thread.table());
done(-1);
}
@@ -512,23 +507,24 @@ void DownloadFromDCWidget::on_ok_clicked()
{
if (currentState != DONE && currentState != ERROR)
return;
+ struct dive_table *table = thread.table();
// delete non-selected dives
- int total = downloadTable.nr;
+ int total = table->nr;
int j = 0;
for (int i = 0; i < total; i++) {
if (diveImportedModel->data(diveImportedModel->index(i, 0), Qt::CheckStateRole) == Qt::Checked)
j++;
else
- delete_dive_from_table(&downloadTable, j);
+ delete_dive_from_table(thread.table(), j);
}
- if (downloadTable.nr > 0) {
+ if (table->nr > 0) {
MainWindow::instance()->diveList->unselectDives();
// remember the last downloaded dive (on most dive computers this will be the chronologically
// first new dive) and select it again after processing all the dives
- int uniqId = downloadTable.dives[downloadTable.nr - 1]->id;
- process_imported_dives(&downloadTable, preferDownloaded(), true);
+ int uniqId = table->dives[table->nr - 1]->id;
+ process_imported_dives(table, preferDownloaded(), true);
autogroup_dives();
Command::clear();
// after process_imported_dives does any merging or resorting needed, we need
diff --git a/desktop-widgets/filterwidget.ui b/desktop-widgets/filterwidget.ui
deleted file mode 100644
index 7f548a931..000000000
--- a/desktop-widgets/filterwidget.ui
+++ /dev/null
@@ -1,140 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>FilterWidget2</class>
- <widget class="QWidget" name="FilterWidget2">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>594</width>
- <height>362</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string></string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <item>
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QLabel" name="filterText">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QToolButton" name="clear">
- <property name="toolTip">
- <string>Reset filters</string>
- </property>
- <property name="icon">
- <iconset resource="../subsurface.qrc">
- <normaloff>:edit-clear-icon</normaloff>:edit-clear-icon</iconset>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="maximize">
- <property name="toolTip">
- <string>Show/hide filters</string>
- </property>
- <property name="icon">
- <iconset resource="../subsurface.qrc">
- <normaloff>:hide-icon</normaloff>:hide-icon</iconset>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="close">
- <property name="toolTip">
- <string>Close and reset filters</string>
- </property>
- <property name="icon">
- <iconset resource="../subsurface.qrc">
- <normaloff>:filter-close</normaloff>:filter-close</iconset>
- </property>
- <property name="autoRaise">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QScrollArea" name="scrollArea">
- <property name="frameShape">
- <enum>QFrame::NoFrame</enum>
- </property>
- <property name="widgetResizable">
- <bool>true</bool>
- </property>
- <widget class="QWidget" name="scrollAreaWidgetContents">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>594</width>
- <height>337</height>
- </rect>
- </property>
- </widget>
- </widget>
- </item>
- </layout>
- </widget>
- <resources>
- <include location="../subsurface.qrc"/>
- </resources>
- <connections/>
-</ui>
diff --git a/desktop-widgets/filterwidget2.cpp b/desktop-widgets/filterwidget2.cpp
new file mode 100644
index 000000000..d120ffe4d
--- /dev/null
+++ b/desktop-widgets/filterwidget2.cpp
@@ -0,0 +1,105 @@
+#include "desktop-widgets/filterwidget2.h"
+#include "desktop-widgets/simplewidgets.h"
+
+#include <QDoubleSpinBox>
+
+FilterWidget2::FilterWidget2(QWidget* parent)
+: QWidget(parent)
+, ui(new Ui::FilterWidget2())
+{
+ ui->setupUi(this);
+
+ FilterData data;
+ ui->minRating->setCurrentStars(data.minRating);
+ ui->maxRating->setCurrentStars(data.maxRating);
+ ui->minVisibility->setCurrentStars(data.minVisibility);
+ ui->maxVisibility->setCurrentStars(data.maxVisibility);
+ ui->minAirTemp->setValue(data.minAirTemp);
+ ui->maxAirTemp->setValue(data.maxAirTemp);
+ ui->minWaterTemp->setValue(data.minWaterTemp);
+ ui->maxWaterTemp->setValue(data.maxWaterTemp);
+
+ // TODO: unhide this when we discover how to search for equipment.
+ ui->equipment->hide();
+ ui->labelEquipment->hide();
+ ui->invertFilter->hide();
+
+ ui->to->setDate(data.to.date());
+
+ connect(ui->maxRating, &StarWidget::valueChanged,
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->minRating, &StarWidget::valueChanged,
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->maxVisibility, &StarWidget::valueChanged,
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->minVisibility, &StarWidget::valueChanged,
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->maxAirTemp, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->minAirTemp, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->maxWaterTemp, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->minWaterTemp, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->from, &QDateTimeEdit::dateTimeChanged,
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->to, &QDateTimeEdit::dateTimeChanged,
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->tags, &QLineEdit::textChanged,
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->people, &QLineEdit::textChanged,
+ this, &FilterWidget2::updateFilter);
+
+ connect(ui->location, &QLineEdit::textChanged,
+ this, &FilterWidget2::updateFilter);
+}
+
+void FilterWidget2::updateFilter()
+{
+ FilterData data;
+
+ data.validFilter = true;
+ data.minVisibility = ui->minVisibility->currentStars();
+ data.maxVisibility = ui->maxVisibility->currentStars();
+ data.minRating = ui->minRating->currentStars();
+ data.maxRating = ui->maxRating->currentStars();
+ data.minWaterTemp = ui->minWaterTemp->value();
+ data.maxWaterTemp = ui->maxWaterTemp->value();
+ data.minAirTemp = ui->minAirTemp->value();
+ data.maxWaterTemp = ui->maxWaterTemp->value();
+ data.from = ui->from->dateTime();
+ data.to = ui->to->dateTime();
+ data.tags = ui->tags->text().split(",", QString::SkipEmptyParts);
+ data.people = ui->people->text().split(",", QString::SkipEmptyParts);
+ data.location = ui->location->text().split(",", QString::SkipEmptyParts);
+ data.equipment = ui->equipment->text().split(",", QString::SkipEmptyParts);
+ data.invertFilter = ui->invertFilter->isChecked();
+
+ filterData = data;
+ emit filterDataChanged(data);
+}
+
+void FilterWidget2::showEvent(QShowEvent *event)
+{
+ QWidget::showEvent(event);
+ emit filterDataChanged(filterData);
+}
+
+void FilterWidget2::hideEvent(QHideEvent *event)
+{
+ QWidget::hideEvent(event);
+ FilterData data;
+ emit filterDataChanged(data);
+}
diff --git a/desktop-widgets/filterwidget2.h b/desktop-widgets/filterwidget2.h
new file mode 100644
index 000000000..80629f0cc
--- /dev/null
+++ b/desktop-widgets/filterwidget2.h
@@ -0,0 +1,35 @@
+#ifndef FILTERWIDGET_2_H
+#define FILTERWIDGET_2_H
+
+#include <QWidget>
+#include <QHideEvent>
+#include <QShowEvent>
+
+#include <memory>
+
+#include "ui_filterwidget2.h"
+#include "qt-models/filtermodels.h"
+
+namespace Ui {
+ class FilterWidget2;
+}
+
+class FilterWidget2 : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit FilterWidget2(QWidget *parent = 0);
+ void updateFilter();
+protected:
+ void hideEvent(QHideEvent *event) override;
+ void showEvent(QShowEvent *event) override;
+
+signals:
+ void filterDataChanged(const FilterData& data);
+
+private:
+ std::unique_ptr<Ui::FilterWidget2> ui;
+ FilterData filterData;
+};
+
+#endif
diff --git a/desktop-widgets/filterwidget2.ui b/desktop-widgets/filterwidget2.ui
new file mode 100644
index 000000000..978dafc6e
--- /dev/null
+++ b/desktop-widgets/filterwidget2.ui
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FilterWidget2</class>
+ <widget class="QWidget" name="FilterWidget2">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>510</width>
+ <height>320</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="3" column="1">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>Min</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="4">
+ <widget class="StarWidget" name="maxVisibility" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::TabFocus</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QDoubleSpinBox" name="minWaterTemp"/>
+ </item>
+ <item row="2" column="2">
+ <widget class="StarWidget" name="minVisibility" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::TabFocus</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Tags</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string> Rating</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="StarWidget" name="minRating" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::TabFocus</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>People</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="3">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>Max</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Min</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1" colspan="4">
+ <widget class="QLineEdit" name="tags"/>
+ </item>
+ <item row="11" column="1" colspan="4">
+ <widget class="QCheckBox" name="invertFilter">
+ <property name="toolTip">
+ <string>Display dives that will not match the search, only applies to tags, people, location and equipment</string>
+ </property>
+ <property name="text">
+ <string>Invert filter</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Max</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" colspan="4">
+ <widget class="QDateTimeEdit" name="from"/>
+ </item>
+ <item row="8" column="1" colspan="4">
+ <widget class="QLineEdit" name="people"/>
+ </item>
+ <item row="1" column="4">
+ <widget class="StarWidget" name="maxRating" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::TabFocus</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="4">
+ <widget class="QDoubleSpinBox" name="maxWaterTemp"/>
+ </item>
+ <item row="10" column="0">
+ <widget class="QLabel" name="labelEquipment">
+ <property name="text">
+ <string>Equipment</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Max</string>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Location</string>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1" colspan="4">
+ <widget class="QLineEdit" name="location"/>
+ </item>
+ <item row="6" column="1" colspan="4">
+ <widget class="QDateTimeEdit" name="to"/>
+ </item>
+ <item row="10" column="1" colspan="4">
+ <widget class="QLineEdit" name="equipment"/>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>From</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>To</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Visibility</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Water Temp</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Min</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Air Temp</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="label_17">
+ <property name="text">
+ <string>Min</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3">
+ <widget class="QLabel" name="label_18">
+ <property name="text">
+ <string>Max</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QDoubleSpinBox" name="minAirTemp"/>
+ </item>
+ <item row="4" column="4">
+ <widget class="QDoubleSpinBox" name="maxAirTemp"/>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>StarWidget</class>
+ <extends>QWidget</extends>
+ <header location="global">desktop-widgets/starwidget.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>minRating</tabstop>
+ <tabstop>maxRating</tabstop>
+ <tabstop>minVisibility</tabstop>
+ <tabstop>maxVisibility</tabstop>
+ <tabstop>from</tabstop>
+ <tabstop>to</tabstop>
+ <tabstop>tags</tabstop>
+ <tabstop>people</tabstop>
+ <tabstop>location</tabstop>
+ <tabstop>equipment</tabstop>
+ <tabstop>invertFilter</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/desktop-widgets/listfilter.ui b/desktop-widgets/listfilter.ui
index 06f1889a8..91d430617 100644
--- a/desktop-widgets/listfilter.ui
+++ b/desktop-widgets/listfilter.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>FilterWidget</class>
- <widget class="QWidget" name="FilterWidget">
+ <class>ListFilter</class>
+ <widget class="QWidget" name="ListFilter">
<property name="geometry">
<rect>
<x>0</x>
diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp
index 2d0807474..7356f8370 100644
--- a/desktop-widgets/locationinformation.cpp
+++ b/desktop-widgets/locationinformation.cpp
@@ -34,8 +34,6 @@ LocationInformationWidget::LocationInformationWidget(QWidget *parent) : QGroupBo
ui.diveSiteMessage->addAction(rejectAction);
connect(ui.geoCodeButton, SIGNAL(clicked()), this, SLOT(reverseGeocode()));
- connect(this, SIGNAL(nameChanged(const QString &, const QString &)),
- LocationFilterModel::instance(), SLOT(changeName(const QString &, const QString &)));
connect(ui.updateLocationButton, SIGNAL(clicked()), this, SLOT(updateLocationOnMap()));
connect(ui.diveSiteCoordinates, SIGNAL(returnPressed()), this, SLOT(updateLocationOnMap()));
ui.diveSiteCoordinates->installEventFilter(this);
diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp
index 99942ec56..6c0b42c9d 100644
--- a/desktop-widgets/mainwindow.cpp
+++ b/desktop-widgets/mainwindow.cpp
@@ -53,6 +53,8 @@
#include "desktop-widgets/tab-widgets/maintab.h"
#include "desktop-widgets/updatemanager.h"
#include "desktop-widgets/usersurvey.h"
+#include "desktop-widgets/filterwidget2.h"
+#include "desktop-widgets/simplewidgets.h"
#include "profile-widget/profilewidget2.h"
@@ -141,10 +143,10 @@ MainWindow::MainWindow() : QMainWindow(),
diveList = new DiveListView(this);
graphics = new ProfileWidget2(this);
MapWidget *mapWidget = MapWidget::instance();
-
divePlannerSettingsWidget = new PlannerSettingsWidget(this);
divePlannerWidget = new DivePlannerWidget(this);
plannerDetails = new PlannerDetails(this);
+ auto *filterWidget2 = new FilterWidget2();
// what is a sane order for those icons? we should have the ones the user is
// most likely to want towards the top so they are always visible
@@ -193,6 +195,7 @@ MainWindow::MainWindow() : QMainWindow(),
registerApplicationState("PlanDive", divePlannerWidget, profileContainer, divePlannerSettingsWidget, plannerDetails );
registerApplicationState("EditPlannedDive", divePlannerWidget, profileContainer, diveList, mapWidget );
registerApplicationState("EditDiveSite", diveSiteEdit, profileContainer, diveList, mapWidget);
+ registerApplicationState("FilterDive", mainTab, profileContainer, diveList, filterWidget2);
setStateProperties("Default", enabledList, enabledList, enabledList,enabledList);
setStateProperties("AddDive", enabledList, enabledList, enabledList,enabledList);
@@ -200,11 +203,9 @@ MainWindow::MainWindow() : QMainWindow(),
setStateProperties("PlanDive", enabledList, enabledList, enabledList,enabledList);
setStateProperties("EditPlannedDive", enabledList, enabledList, enabledList,enabledList);
setStateProperties("EditDiveSite", enabledList, disabledList, disabledList, enabledList);
-
+ setStateProperties("FilterDive", enabledList, enabledList, enabledList, enabledList);
setApplicationState("Default");
- ui.multiFilter->hide();
-
setWindowIcon(QIcon(":subsurface-icon"));
if (!QIcon::hasThemeIcon("window-close")) {
QIcon::setThemeName("subsurface");
@@ -495,10 +496,6 @@ void MainWindow::refreshDisplay(bool doRecreateDiveList)
void MainWindow::recreateDiveList()
{
diveList->reload();
- TagFilterModel::instance()->repopulate();
- BuddyFilterModel::instance()->repopulate();
- LocationFilterModel::instance()->repopulate();
- SuitsFilterModel::instance()->repopulate();
MultiFilterSortModel::instance()->myInvalidate();
}
@@ -759,9 +756,8 @@ void MainWindow::on_actionClose_triggered()
{
if (okToClose(tr("Please save or cancel the current dive edit before closing the file."))) {
closeCurrentFile();
- // hide any pictures and the filter
DivePictureModel::instance()->updateDivePictures();
- ui.multiFilter->closeFilter();
+ setApplicationState("Default");
recreateDiveList();
}
}
@@ -1874,13 +1870,7 @@ void MainWindow::on_paste_triggered()
void MainWindow::on_actionFilterTags_triggered()
{
- if (ui.multiFilter->isVisible()) {
- ui.multiFilter->closeFilter();
- ui.actionFilterTags->setChecked(false);
- } else {
- ui.multiFilter->setVisible(true);
- ui.actionFilterTags->setChecked(true);
- }
+ setApplicationState(getCurrentAppState() == "FilterDive" ? "Default" : "FilterDive");
}
void MainWindow::setCheckedActionFilterTags(bool checked)
diff --git a/desktop-widgets/mainwindow.h b/desktop-widgets/mainwindow.h
index be35ecf1e..a18738de4 100644
--- a/desktop-widgets/mainwindow.h
+++ b/desktop-widgets/mainwindow.h
@@ -18,6 +18,7 @@
#include "ui_plannerDetails.h"
#include "desktop-widgets/notificationwidget.h"
#include "core/gpslocation.h"
+#include "core/dive.h"
#define NUM_RECENT_FILES 4
@@ -35,6 +36,7 @@ class ProfileWidget2;
class PlannerDetails;
class PlannerSettingsWidget;
class LocationInformationWidget;
+class FilterWidget2;
typedef std::pair<QByteArray, QVariant> WidgetProperty;
typedef QVector<WidgetProperty> PropertyList;
diff --git a/desktop-widgets/mainwindow.ui b/desktop-widgets/mainwindow.ui
index 1263b4093..2dcd3e8a6 100644
--- a/desktop-widgets/mainwindow.ui
+++ b/desktop-widgets/mainwindow.ui
@@ -15,12 +15,18 @@
<property name="spacing">
<number>0</number>
</property>
- <property name="margin">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
<number>0</number>
</property>
- <item>
- <widget class="MultiFilter" name="multiFilter" native="true"/>
- </item>
<item>
<widget class="QSplitter" name="mainSplitter">
<property name="orientation">
@@ -53,7 +59,7 @@
<x>0</x>
<y>0</y>
<width>861</width>
- <height>23</height>
+ <height>29</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@@ -133,7 +139,7 @@
</widget>
<widget class="QMenu" name="menuShare_on">
<property name="title">
- <string>Share on</string>
+ <string>Share o&amp;n</string>
</property>
<addaction name="separator"/>
</widget>
@@ -438,7 +444,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:pp-o2-icon</normaloff>:pp-o2-icon</iconset>
</property>
<property name="text">
@@ -450,7 +456,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:pp-n2-icon</normaloff>:pp-n2-icon</iconset>
</property>
<property name="text">
@@ -462,7 +468,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:pp-he-icon</normaloff>:pp-he-icon</iconset>
</property>
<property name="text">
@@ -474,7 +480,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:ceiling-dc-icon</normaloff>:ceiling-dc-icon</iconset>
</property>
<property name="text">
@@ -486,7 +492,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:ceiling-calculated-icon</normaloff>:ceiling-calculated-icon</iconset>
</property>
<property name="text">
@@ -498,7 +504,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:ceiling-tissues-icon</normaloff>:ceiling-tissues-icon</iconset>
</property>
<property name="text">
@@ -510,7 +516,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:ceiling-increments-icon</normaloff>:ceiling-increments-icon</iconset>
</property>
<property name="text">
@@ -522,7 +528,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:rate-heart-icon</normaloff>:rate-heart-icon</iconset>
</property>
<property name="text">
@@ -534,7 +540,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:depth-mod-icon</normaloff>:depth-mod-icon</iconset>
</property>
<property name="text">
@@ -546,7 +552,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:depth-ead-icon</normaloff>:depth-ead-icon</iconset>
</property>
<property name="text">
@@ -558,7 +564,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:depth-ndl-icon</normaloff>:depth-ndl-icon</iconset>
</property>
<property name="text">
@@ -570,7 +576,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:rate-sac-icon</normaloff>:rate-sac-icon</iconset>
</property>
<property name="text">
@@ -582,7 +588,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:ruler-icon</normaloff>:ruler-icon</iconset>
</property>
<property name="text">
@@ -594,7 +600,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:scale-graph-icon</normaloff>:scale-graph-icon</iconset>
</property>
<property name="text">
@@ -606,7 +612,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:photo-icon</normaloff>:photo-icon</iconset>
</property>
<property name="text">
@@ -618,7 +624,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:gaschange-icon</normaloff>:gaschange-icon</iconset>
</property>
<property name="text">
@@ -641,7 +647,7 @@
<bool>true</bool>
</property>
<property name="icon">
- <iconset resource="../subsurface.qrc">
+ <iconset>
<normaloff>:heatmap-icon</normaloff>:heatmap-icon</iconset>
</property>
<property name="text">
@@ -704,7 +710,7 @@
<bool>true</bool>
</property>
<property name="text">
- <string>Cloud storage online</string>
+ <string>Cloud stora&amp;ge online</string>
</property>
</action>
</widget>
@@ -715,12 +721,6 @@
<header>desktop-widgets/notificationwidget.h</header>
<container>1</container>
</customwidget>
- <customwidget>
- <class>MultiFilter</class>
- <extends>QWidget</extends>
- <header>desktop-widgets/simplewidgets.h</header>
- <container>1</container>
- </customwidget>
</customwidgets>
<resources>
<include location="../subsurface.qrc"/>
diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp
index f008f4bc2..41fe1c083 100644
--- a/desktop-widgets/modeldelegates.cpp
+++ b/desktop-widgets/modeldelegates.cpp
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+
#include "desktop-widgets/modeldelegates.h"
#include "core/subsurface-string.h"
#include "core/gettextfromc.h"
@@ -13,6 +14,7 @@
#include "qt-models/divetripmodel.h"
#include "qt-models/divelocationmodel.h"
#include "core/qthelper.h"
+#include "desktop-widgets/simplewidgets.h"
#include <QCompleter>
#include <QKeyEvent>
@@ -22,6 +24,9 @@
#include <QBrush>
#include <QColor>
#include <QAbstractProxyModel>
+#include <QLineEdit>
+#include <QAbstractItemView>
+#include <QSpinBox>
QSize DiveListDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const
{
diff --git a/desktop-widgets/printdialog.cpp b/desktop-widgets/printdialog.cpp
index bbbade2e4..4c863fbea 100644
--- a/desktop-widgets/printdialog.cpp
+++ b/desktop-widgets/printdialog.cpp
@@ -10,6 +10,7 @@
#include <QShortcut>
#include <QSettings>
#include <QMessageBox>
+#include <QDialogButtonBox>
#define SETTINGS_GROUP "PrintDialog"
diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp
index 42747cd3e..42569a270 100644
--- a/desktop-widgets/simplewidgets.cpp
+++ b/desktop-widgets/simplewidgets.cpp
@@ -200,7 +200,7 @@ void SetpointDialog::setpointData(struct divecomputer *divecomputer, int second)
void SetpointDialog::buttonClicked(QAbstractButton *button)
{
if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole && dc) {
- add_event(dc, time, SAMPLE_EVENT_PO2, 0, (int)(1000.0 * ui.spinbox->value()),
+ add_event(dc, time, SAMPLE_EVENT_PO2, 0, (int)(1000.0 * ui.spinbox->value()),
QT_TRANSLATE_NOOP("gettextFromC", "SP change"));
invalidate_dive_cache(current_dive);
}
@@ -493,117 +493,6 @@ void DiveComponentSelection::buttonClicked(QAbstractButton *button)
}
}
-void FilterBase::addContextMenuEntry(const QString &s, void (FilterModelBase::*fn)())
-{
- QAction *act = new QAction(s, this);
- connect(act, &QAction::triggered, model, fn);
- ui.filterList->addAction(act);
-}
-
-FilterBase::FilterBase(FilterModelBase *model_, QWidget *parent) : QWidget(parent),
- model(model_)
-{
- ui.setupUi(this);
-#if QT_VERSION >= 0x050200
- ui.filterInternalList->setClearButtonEnabled(true);
-#endif
- QSortFilterProxyModel *filter = new QSortFilterProxyModel();
- filter->setSourceModel(model);
- filter->setFilterCaseSensitivity(Qt::CaseInsensitive);
- connect(ui.filterInternalList, SIGNAL(textChanged(QString)), filter, SLOT(setFilterFixedString(QString)));
- connect(ui.notButton, &QToolButton::toggled, model, &FilterModelBase::setNegate);
- ui.filterList->setModel(filter);
-
- addContextMenuEntry(tr("Select All"), &FilterModelBase::selectAll);
- addContextMenuEntry(tr("Unselect All"), &FilterModelBase::clearFilter);
- addContextMenuEntry(tr("Invert Selection"), &FilterModelBase::invertSelection);
- ui.filterList->setContextMenuPolicy(Qt::ActionsContextMenu);
-}
-
-void FilterBase::showEvent(QShowEvent *event)
-{
- MultiFilterSortModel::instance()->addFilterModel(model);
- QWidget::showEvent(event);
-}
-
-void FilterBase::hideEvent(QHideEvent *event)
-{
- MultiFilterSortModel::instance()->removeFilterModel(model);
- QWidget::hideEvent(event);
-}
-
-TagFilter::TagFilter(QWidget *parent) : FilterBase(TagFilterModel::instance(), parent)
-{
- ui.label->setText(tr("Tags") + QStringLiteral(": "));
-}
-
-BuddyFilter::BuddyFilter(QWidget *parent) : FilterBase(BuddyFilterModel::instance(), parent)
-{
- ui.label->setText(tr("Person") + QStringLiteral(": "));
- ui.label->setToolTip(tr("Searches for buddies and divemasters"));
-}
-
-LocationFilter::LocationFilter(QWidget *parent) : FilterBase(LocationFilterModel::instance(), parent)
-{
- ui.label->setText(tr("Location") + QStringLiteral(": "));
-}
-
-SuitFilter::SuitFilter(QWidget *parent) : FilterBase(SuitsFilterModel::instance(), parent)
-{
- ui.label->setText(tr("Suits") + QStringLiteral(": "));
-}
-
-MultiFilter::MultiFilter(QWidget *parent) : QWidget(parent)
-{
- ui.setupUi(this);
-
- QWidget *expandedWidget = new QWidget();
- QHBoxLayout *l = new QHBoxLayout();
-
- TagFilter *tagFilter = new TagFilter(this);
- int minimumHeight = tagFilter->ui.filterInternalList->height() +
- tagFilter->ui.verticalLayout->spacing() * tagFilter->ui.verticalLayout->count();
-
- QListView *dummyList = new QListView();
- QStringListModel *dummy = new QStringListModel(QStringList() << "Dummy Text");
- dummyList->setModel(dummy);
-
- connect(ui.close, SIGNAL(clicked(bool)), this, SLOT(closeFilter()));
- connect(ui.clear, SIGNAL(clicked(bool)), MultiFilterSortModel::instance(), SLOT(clearFilter()));
- connect(ui.maximize, SIGNAL(clicked(bool)), this, SLOT(adjustHeight()));
-
- l->addWidget(tagFilter);
- l->addWidget(new BuddyFilter());
- l->addWidget(new LocationFilter());
- l->addWidget(new SuitFilter());
- l->setContentsMargins(0, 0, 0, 0);
- l->setSpacing(0);
- expandedWidget->setLayout(l);
-
- ui.scrollArea->setWidget(expandedWidget);
- expandedWidget->resize(expandedWidget->width(), minimumHeight + dummyList->sizeHintForRow(0) * 5);
- ui.scrollArea->setMinimumHeight(expandedWidget->height() + 5);
-
- connect(MultiFilterSortModel::instance(), SIGNAL(filterFinished()), this, SLOT(filterFinished()));
-}
-
-void MultiFilter::filterFinished()
-{
- ui.filterText->setText(tr("Filter shows %1 (of %2) dives").arg(MultiFilterSortModel::instance()->divesDisplayed).arg(dive_table.nr));
-}
-
-void MultiFilter::adjustHeight()
-{
- ui.scrollArea->setVisible(!ui.scrollArea->isVisible());
-}
-
-void MultiFilter::closeFilter()
-{
- MultiFilterSortModel::instance()->clearFilter();
- hide();
- MainWindow::instance()->setCheckedActionFilterTags(false);
-}
-
TextHyperlinkEventFilter::TextHyperlinkEventFilter(QTextEdit *txtEdit) : QObject(txtEdit),
textEdit(txtEdit),
scrollView(textEdit->viewport())
diff --git a/desktop-widgets/simplewidgets.h b/desktop-widgets/simplewidgets.h
index 48e5e7e3d..a814519c4 100644
--- a/desktop-widgets/simplewidgets.h
+++ b/desktop-widgets/simplewidgets.h
@@ -20,7 +20,6 @@ class FilterModelBase;
#include "ui_urldialog.h"
#include "ui_divecomponentselection.h"
#include "ui_listfilter.h"
-#include "ui_filterwidget.h"
#include "core/exif.h"
#include "core/dive.h"
@@ -150,59 +149,6 @@ private:
struct dive_components *what;
};
-namespace Ui{
- class FilterWidget2;
-};
-
-class MultiFilter : public QWidget {
- Q_OBJECT
-public
-slots:
- void closeFilter();
- void adjustHeight();
- void filterFinished();
-
-public:
- MultiFilter(QWidget *parent);
- Ui::FilterWidget2 ui;
-};
-
-class FilterBase : public QWidget {
- Q_OBJECT
- void addContextMenuEntry(const QString &s, void (FilterModelBase::*)());
-protected:
- FilterBase(FilterModelBase *model, QWidget *parent = 0);
- FilterModelBase *model;
- Ui::FilterWidget ui;
- void showEvent(QShowEvent *) override;
- void hideEvent(QHideEvent *) override;
- friend class MultiFilter;
-};
-
-class TagFilter : public FilterBase {
- Q_OBJECT
-public:
- TagFilter(QWidget *parent = 0);
-};
-
-class BuddyFilter : public FilterBase {
- Q_OBJECT
-public:
- BuddyFilter(QWidget *parent = 0);
-};
-
-class SuitFilter : public FilterBase {
- Q_OBJECT
-public:
- SuitFilter(QWidget *parent = 0);
-};
-
-class LocationFilter : public FilterBase {
- Q_OBJECT
-public:
- LocationFilter(QWidget *parent = 0);
-};
-
class TextHyperlinkEventFilter : public QObject {
Q_OBJECT
public:
diff --git a/desktop-widgets/subsurfacewebservices.cpp b/desktop-widgets/subsurfacewebservices.cpp
index 2acc7451e..d62cd8552 100644
--- a/desktop-widgets/subsurfacewebservices.cpp
+++ b/desktop-widgets/subsurfacewebservices.cpp
@@ -484,6 +484,10 @@ void DivelogsDeWebServices::prepareDivesForUpload(bool selected)
{
/* generate a random filename and create/open that file with zip_open */
QString filename = QDir::tempPath() + "/import-" + QString::number(qrand() % 99999999) + ".dld";
+ if (!amount_selected) {
+ report_error(tr("no dives were selected").toUtf8());
+ return;
+ }
if (prepare_dives_for_divelogs(filename, selected)) {
QFile f(filename);
if (f.open(QIODevice::ReadOnly)) {
diff --git a/desktop-widgets/tab-widgets/TabDiveStatistics.cpp b/desktop-widgets/tab-widgets/TabDiveStatistics.cpp
index 67e80ed24..1b3fe9522 100644
--- a/desktop-widgets/tab-widgets/TabDiveStatistics.cpp
+++ b/desktop-widgets/tab-widgets/TabDiveStatistics.cpp
@@ -48,17 +48,13 @@ void TabDiveStatistics::updateData()
calculate_stats_selected(&stats_selection);
clear();
ui->depthLimits->setMaximum(get_depth_string(stats_selection.max_depth, true));
- if (amount_selected > 1)
+ if (amount_selected > 1) {
ui->depthLimits->setMinimum(get_depth_string(stats_selection.min_depth, true));
- else
+ ui->depthLimits->setAverage(get_depth_string(stats_selection.combined_max_depth.mm / stats_selection.selection_size, true));
+ } else {
ui->depthLimits->setMinimum("");
- // the overall average depth is really confusing when listed between the
- // deepest and shallowest dive - let's just not set it
- // ui->depthLimits->setAverage(get_depth_string(stats_selection.avg_depth, true));
-
- // Also hide the avgIco, so its clear that its not there.
- ui->depthLimits->overrideAvgToolTipText("");
- ui->depthLimits->setAvgVisibility(false);
+ ui->depthLimits->setAverage("");
+ }
if (stats_selection.max_sac.mliter && (stats_selection.max_sac.mliter != stats_selection.avg_sac.mliter))
ui->sacLimits->setMaximum(get_volume_string(stats_selection.max_sac, true).append(tr("/min")));
diff --git a/desktop-widgets/tab-widgets/TabDiveStatistics.ui b/desktop-widgets/tab-widgets/TabDiveStatistics.ui
index d954dca3b..1be4bbcfc 100644
--- a/desktop-widgets/tab-widgets/TabDiveStatistics.ui
+++ b/desktop-widgets/tab-widgets/TabDiveStatistics.ui
@@ -70,7 +70,7 @@
<item row="1" column="0">
<widget class="QGroupBox" name="groupBoxb">
<property name="title">
- <string>Depth</string>
+ <string>Max. depth</string>
</property>
<layout class="QHBoxLayout" name="statsDepthLayout">
<item>
diff --git a/desktop-widgets/tab-widgets/maintab.cpp b/desktop-widgets/tab-widgets/maintab.cpp
index 567e1737f..f22b7cf1e 100644
--- a/desktop-widgets/tab-widgets/maintab.cpp
+++ b/desktop-widgets/tab-widgets/maintab.cpp
@@ -27,6 +27,7 @@
#include "core/gettextfromc.h"
#include "desktop-widgets/locationinformation.h"
#include "desktop-widgets/command.h"
+#include "desktop-widgets/simplewidgets.h"
#include "TabDiveExtraInfo.h"
#include "TabDiveInformation.h"
@@ -688,7 +689,6 @@ struct dive_site *MainTab::updateDiveSite(struct dive_site *pickedDs, dive *d)
QString name = ui.location->text().isEmpty() ? tr("New dive site") : ui.location->text();
pickedDs = create_dive_site(qPrintable(name), displayed_dive.when);
createdNewDive = true;
- LocationFilterModel::instance()->addName(name);
}
if (origDs) {
diff --git a/libdivecomputer b/libdivecomputer
-Subproject abde311d3a6ea97c7a586e4cc879e07d4ce0fd4
+Subproject e4c96e93caf68cf59e601da798e5dbaaf5bad60
diff --git a/mobile-widgets/qml/CopySettings.qml b/mobile-widgets/qml/CopySettings.qml
new file mode 100644
index 000000000..172a0173c
--- /dev/null
+++ b/mobile-widgets/qml/CopySettings.qml
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+import QtQuick 2.6
+import QtQuick.Controls 2.2 as Controls
+import QtQuick.Window 2.2
+import QtQuick.Dialogs 1.2
+import QtQuick.Layouts 1.2
+import org.kde.kirigami 2.4 as Kirigami
+import org.subsurfacedivelog.mobile 1.0
+
+Kirigami.ScrollablePage {
+ objectName: "CopySettings"
+ id: settingsCopy
+
+ title: qsTr("Copy Settings")
+ background: Rectangle { color: subsurfaceTheme.backgroundColor }
+
+ property real gridWidth: settingsCopy.width - Kirigami.Units.gridUnit
+
+ ColumnLayout {
+ width: gridWidth
+
+ GridLayout {
+ id: copy_settings
+ columns: 2
+ Controls.Label {
+ text: qsTr("Selection for copy-paste")
+ font.pointSize: subsurfaceTheme.headingPointSize
+ font.weight: Font.Light
+ color: subsurfaceTheme.textColor
+ Layout.topMargin: Kirigami.Units.largeSpacing
+ Layout.bottomMargin: Kirigami.Units.largeSpacing / 2
+ Layout.columnSpan: 2
+ }
+
+ Controls.Label {
+ text: qsTr("Dive site")
+ font.pointSize: subsurfaceTheme.regularPointSize
+ Layout.preferredWidth: gridWidth * 0.75
+ }
+ SsrfSwitch {
+ checked: manager.toggleDiveSite(false)
+ Layout.preferredWidth: gridWidth * 0.25
+ onClicked: {
+ manager.toggleDiveSite(true)
+ }
+ }
+ Controls.Label {
+ text: qsTr("Notes")
+ font.pointSize: subsurfaceTheme.regularPointSize
+ Layout.preferredWidth: gridWidth * 0.75
+ }
+ SsrfSwitch {
+ checked: manager.toggleNotes(false)
+ Layout.preferredWidth: gridWidth * 0.25
+ onClicked: {
+ manager.toggleNotes(true)
+ }
+ }
+ Controls.Label {
+ text: qsTr("Dive master")
+ font.pointSize: subsurfaceTheme.regularPointSize
+ Layout.preferredWidth: gridWidth * 0.75
+ }
+ SsrfSwitch {
+ checked: manager.toggleDiveMaster(false)
+ Layout.preferredWidth: gridWidth * 0.25
+ onClicked: {
+ manager.toggleDiveMaster(true)
+ }
+ }
+ Controls.Label {
+ text: qsTr("Buddy")
+ font.pointSize: subsurfaceTheme.regularPointSize
+ Layout.preferredWidth: gridWidth * 0.75
+ }
+ SsrfSwitch {
+ checked: manager.toggleBuddy(false)
+ Layout.preferredWidth: gridWidth * 0.25
+ onClicked: {
+ manager.toggleBuddy(true)
+ }
+ }
+ Controls.Label {
+ text: qsTr("Suit")
+ font.pointSize: subsurfaceTheme.regularPointSize
+ Layout.preferredWidth: gridWidth * 0.75
+ }
+ SsrfSwitch {
+ checked: manager.toggleSuit(false)
+ Layout.preferredWidth: gridWidth * 0.25
+ onClicked: {
+ manager.toggleSuit(true)
+ }
+ }
+ Controls.Label {
+ text: qsTr("Rating")
+ font.pointSize: subsurfaceTheme.regularPointSize
+ Layout.preferredWidth: gridWidth * 0.75
+ }
+ SsrfSwitch {
+ checked: manager.toggleRating(false)
+ Layout.preferredWidth: gridWidth * 0.25
+ onClicked: {
+ manager.toggleRating(true)
+ }
+ }
+ Controls.Label {
+ text: qsTr("Visibility")
+ font.pointSize: subsurfaceTheme.regularPointSize
+ Layout.preferredWidth: gridWidth * 0.75
+ }
+ SsrfSwitch {
+ checked: manager.toggleVisibility(false)
+ Layout.preferredWidth: gridWidth * 0.25
+ onClicked: {
+ manager.toggleVisibility(true)
+ }
+ }
+ Controls.Label {
+ text: qsTr("Tags")
+ font.pointSize: subsurfaceTheme.regularPointSize
+ Layout.preferredWidth: gridWidth * 0.75
+ }
+ SsrfSwitch {
+ checked: manager.toggleTags(false)
+ Layout.preferredWidth: gridWidth * 0.25
+ onClicked: {
+ manager.toggleTags(true)
+ }
+ }
+ Controls.Label {
+ text: qsTr("Cylinders")
+ font.pointSize: subsurfaceTheme.regularPointSize
+ Layout.preferredWidth: gridWidth * 0.75
+ }
+ SsrfSwitch {
+ checked: manager.toggleCylinders(false)
+ Layout.preferredWidth: gridWidth * 0.25
+ onClicked: {
+ manager.toggleCylinders(true)
+ }
+ }
+ Controls.Label {
+ text: qsTr("Weights")
+ font.pointSize: subsurfaceTheme.regularPointSize
+ Layout.preferredWidth: gridWidth * 0.75
+ }
+ SsrfSwitch {
+ checked: manager.toggleWeights(false)
+ Layout.preferredWidth: gridWidth * 0.25
+ onClicked: {
+ manager.toggleWeights(true)
+ }
+ }
+ }
+
+ Rectangle {
+ color: subsurfaceTheme.darkerPrimaryColor
+ height: 1
+ opacity: 0.5
+ Layout.fillWidth: true
+ }
+
+ Item {
+ height: Kirigami.Units.gridUnit * 6
+ }
+ }
+}
diff --git a/mobile-widgets/qml/DiveList.qml b/mobile-widgets/qml/DiveList.qml
index 3d29a1ec4..aecd2f003 100644
--- a/mobile-widgets/qml/DiveList.qml
+++ b/mobile-widgets/qml/DiveList.qml
@@ -232,6 +232,11 @@ Kirigami.ScrollablePage {
timer.stop()
manager.copyDiveData(dive.id)
}
+ onPressAndHold: {
+ globalDrawer.close()
+ manager.copyDiveData(dive.id)
+ pageStack.push(settingsCopyWindow)
+ }
}
}
Rectangle {
diff --git a/mobile-widgets/qml/DownloadFromDiveComputer.qml b/mobile-widgets/qml/DownloadFromDiveComputer.qml
index 142465f87..a1403b07d 100644
--- a/mobile-widgets/qml/DownloadFromDiveComputer.qml
+++ b/mobile-widgets/qml/DownloadFromDiveComputer.qml
@@ -28,7 +28,7 @@ Kirigami.Page {
id: downloadThread
onFinished : {
- importModel.repopulate()
+ importModel.repopulate(table)
progressBar.visible = false
if (dcImportModel.rowCount() > 0) {
console.log(dcImportModel.rowCount() + " dive downloaded")
@@ -167,18 +167,6 @@ Kirigami.Page {
elide: Text.ElideRight
}
onCurrentTextChanged: {
- // pattern that matches BT addresses
- var btAddr = /[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]/ ;
-
- // On iOS we store UUID instead of device address.
- if (Qt.platform.os === 'ios')
- btAddr = /\{?[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}/;
-
- if (btAddr.test(currentText))
- manager.DC_bluetoothMode = true
- else
- manager.DC_bluetoothMode = false
- manager.DC_devName = currentText
dc1.enabled = dc2.enabled = dc3.enabled = dc4.enabled = true
for (var i = 1; i < 5; i++) {
if (comboProduct.currentIndex === -1 && currentText === "FTDI"){
@@ -198,7 +186,6 @@ Kirigami.Page {
}
}
download.text = qsTr("Download")
-
}
}
}
@@ -277,10 +264,28 @@ Kirigami.Page {
comboConnection.currentIndex != -1
onClicked: {
text = qsTr("Retry")
- // strip any BT Name from the address
- var devName = manager.DC_devName
- if (devName != qsTr("USB device"))
- manager.DC_devName = devName.replace(/^(.*) /, "")
+
+ var connectionString = comboConnection.currentText
+ // separate BT address and BT name (if applicable)
+ // pattern that matches BT addresses
+ var btAddr = "(LE:)?([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}";
+
+ // On iOS we store UUID instead of device address.
+ if (Qt.platform.os === 'ios')
+ btAddr = "(LE:)?\{?[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}";
+
+ var pattern = new RegExp(btAddr);
+ var devAddress = "";
+ devAddress = pattern.exec(connectionString);
+ if (devAddress !== null) {
+ manager.DC_bluetoothMode = true;
+ manager.DC_devName = devAddress[0]; // exec returns an array with the matched text in element 0
+ manager.retrieveBluetoothName();
+ manager.appendTextToLog("setting btName to " + manager.DC_devBluetoothName);
+ } else {
+ manager.DC_bluetoothMode = false;
+ manager.DC_devName = connectionString;
+ }
manager.appendTextToLog("DCDownloadThread started for " + manager.DC_vendor + " " + manager.DC_product + " on "+ manager.DC_devName)
progressBar.visible = true
downloadThread.start()
@@ -392,7 +397,7 @@ Kirigami.Page {
comboVendor.currentIndex = manager.getDetectedVendorIndex()
comboProduct.currentIndex = manager.getDetectedProductIndex(comboVendor.currentText)
comboConnection.currentIndex = manager.getMatchingAddress(comboVendor.currentText, comboProduct.currentText)
-
+
}
}
}
diff --git a/mobile-widgets/qml/main.qml b/mobile-widgets/qml/main.qml
index a954dfbc4..8c67c9947 100644
--- a/mobile-widgets/qml/main.qml
+++ b/mobile-widgets/qml/main.qml
@@ -592,6 +592,11 @@ if you have network connectivity and want to sync your data to cloud storage."),
visible: false
}
+ CopySettings {
+ id: settingsCopyWindow
+ visible: false
+ }
+
About {
id: aboutWindow
visible: false
diff --git a/mobile-widgets/qml/mobile-resources.qrc b/mobile-widgets/qml/mobile-resources.qrc
index beb1d93f0..989a7e936 100644
--- a/mobile-widgets/qml/mobile-resources.qrc
+++ b/mobile-widgets/qml/mobile-resources.qrc
@@ -15,6 +15,7 @@
<file>main.qml</file>
<file>MapPage.qml</file>
<file>Settings.qml</file>
+ <file>CopySettings.qml</file>
<file>ThemeTest.qml</file>
<file>StartPage.qml</file>
<file>SsrfButton.qml</file>
diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp
index 70ab87ea5..232b50cfa 100644
--- a/mobile-widgets/qmlmanager.cpp
+++ b/mobile-widgets/qmlmanager.cpp
@@ -144,7 +144,6 @@ QMLManager::QMLManager() : m_locationServiceEnabled(false),
m_updateSelectedDive(-1),
m_selectedDiveTimestamp(0),
alreadySaving(false),
- m_device_data(new DCDeviceData),
m_pluggedInDeviceName("")
{
LOG_STP("qmlmgr starting");
@@ -225,6 +224,18 @@ QMLManager::QMLManager() : m_locationServiceEnabled(false),
// make sure we know if the current cloud repo has been successfully synced
syncLoadFromCloud();
LOG_STP("qmlmgr sync load cloud");
+
+ memset(&m_copyPasteDive, 0, sizeof(m_copyPasteDive));
+ memset(&what, 0, sizeof(what));
+
+ // Let's set some defaults to be copied so users don't necessarily need
+ // to know how to configure this
+ what.divemaster = true;
+ what.buddy = true;
+ what.suit = true;
+ what.tags = true;
+ what.cylinders = true;
+ what.weights = true;
}
void QMLManager::applicationStateChanged(Qt::ApplicationState state)
@@ -1318,6 +1329,86 @@ void QMLManager::deleteDive(int id)
changesNeedSaving();
}
+bool QMLManager::toggleDiveSite(bool toggle)
+{
+ if (toggle)
+ what.divesite = what.divesite ? false : true;
+
+ return what.divesite;
+}
+
+bool QMLManager::toggleNotes(bool toggle)
+{
+ if (toggle)
+ what.notes = what.notes ? false : true;
+
+ return what.notes;
+}
+
+bool QMLManager::toggleDiveMaster(bool toggle)
+{
+ if (toggle)
+ what.divemaster = what.divemaster ? false : true;
+
+ return what.divemaster;
+}
+
+bool QMLManager::toggleBuddy(bool toggle)
+{
+ if (toggle)
+ what.buddy = what.buddy ? false : true;
+
+ return what.buddy;
+}
+
+bool QMLManager::toggleSuit(bool toggle)
+{
+ if (toggle)
+ what.suit = what.suit ? false : true;
+
+ return what.suit;
+}
+
+bool QMLManager::toggleRating(bool toggle)
+{
+ if (toggle)
+ what.rating = what.rating ? false : true;
+
+ return what.rating;
+}
+
+bool QMLManager::toggleVisibility(bool toggle)
+{
+ if (toggle)
+ what.visibility = what.visibility ? false : true;
+
+ return what.visibility;
+}
+
+bool QMLManager::toggleTags(bool toggle)
+{
+ if (toggle)
+ what.tags = what.tags ? false : true;
+
+ return what.tags;
+}
+
+bool QMLManager::toggleCylinders(bool toggle)
+{
+ if (toggle)
+ what.cylinders = what.cylinders ? false : true;
+
+ return what.cylinders;
+}
+
+bool QMLManager::toggleWeights(bool toggle)
+{
+ if (toggle)
+ what.weights = what.weights ? false : true;
+
+ return what.weights;
+}
+
void QMLManager::copyDiveData(int id)
{
m_copyPasteDive = get_dive_by_uniq_id(id);
@@ -1326,14 +1417,6 @@ void QMLManager::copyDiveData(int id)
return;
}
- // TODO: selection dialog for the data to be copied
- what.divemaster = true;
- what.buddy = true;
- what.suit = true;
- what.tags = true;
- what.cylinders = true;
- what.weights = true;
-
setNotificationText("Copy");
}
@@ -1354,6 +1437,9 @@ void QMLManager::pasteDiveData(int id)
mark_divelist_changed(true);
changesNeedSaving();
setNotificationText("Paste");
+
+ int modelIdx = DiveListModel::instance()->getDiveIdx(id);
+ DiveListModel::instance()->updateDive(modelIdx, d);
}
void QMLManager::cancelDownloadDC()
@@ -1671,119 +1757,130 @@ void QMLManager::setStatusbarColor(QColor)
#endif
+void QMLManager::retrieveBluetoothName()
+{
+ QString name = DC_devName();
+ QList<BTDiscovery::btVendorProduct> btDCs = BTDiscovery::instance()->getBtDcs();
+ foreach (BTDiscovery::btVendorProduct btDC, btDCs) {
+ qDebug() << "compare" <<name << btDC.btpdi.address;
+ if (name.contains(btDC.btpdi.address))
+ DC_setDevBluetoothName(btDC.btpdi.name);
+ }
+}
+
QString QMLManager::DC_vendor() const
{
- return m_device_data->vendor();
+ return DCDeviceData::instance()->vendor();
}
QString QMLManager::DC_product() const
{
- return m_device_data->product();
+ return DCDeviceData::instance()->product();
}
QString QMLManager::DC_devName() const
{
- return m_device_data->devName();
+ return DCDeviceData::instance()->devName();
}
QString QMLManager::DC_devBluetoothName() const
{
- return m_device_data->devBluetoothName();
+ return DCDeviceData::instance()->devBluetoothName();
}
QString QMLManager::DC_descriptor() const
{
- return m_device_data->descriptor();
+ return DCDeviceData::instance()->descriptor();
}
bool QMLManager::DC_forceDownload() const
{
- return m_device_data->forceDownload();
+ return DCDeviceData::instance()->forceDownload();
}
bool QMLManager::DC_bluetoothMode() const
{
- return m_device_data->bluetoothMode();
+ return DCDeviceData::instance()->bluetoothMode();
}
bool QMLManager::DC_createNewTrip() const
{
- return m_device_data->createNewTrip();
+ return DCDeviceData::instance()->createNewTrip();
}
bool QMLManager::DC_saveDump() const
{
- return m_device_data->saveDump();
+ return DCDeviceData::instance()->saveDump();
}
int QMLManager::DC_deviceId() const
{
- return m_device_data->deviceId();
+ return DCDeviceData::instance()->deviceId();
}
void QMLManager::DC_setDeviceId(int deviceId)
{
- m_device_data->setDeviceId(deviceId);
+ DCDeviceData::instance()->setDeviceId(deviceId);
}
void QMLManager::DC_setVendor(const QString& vendor)
{
- m_device_data->setVendor(vendor);
+ DCDeviceData::instance()->setVendor(vendor);
}
void QMLManager::DC_setProduct(const QString& product)
{
- m_device_data->setProduct(product);
+ DCDeviceData::instance()->setProduct(product);
}
void QMLManager::DC_setDevName(const QString& devName)
{
- m_device_data->setDevName(devName);
+ DCDeviceData::instance()->setDevName(devName);
}
void QMLManager::DC_setDevBluetoothName(const QString& devBluetoothName)
{
- m_device_data->setDevBluetoothName(devBluetoothName);
+ DCDeviceData::instance()->setDevBluetoothName(devBluetoothName);
}
void QMLManager::DC_setBluetoothMode(bool mode)
{
- m_device_data->setBluetoothMode(mode);
+ DCDeviceData::instance()->setBluetoothMode(mode);
}
void QMLManager::DC_setForceDownload(bool force)
{
- m_device_data->setForceDownload(force);
+ DCDeviceData::instance()->setForceDownload(force);
}
void QMLManager::DC_setCreateNewTrip(bool create)
{
- m_device_data->setCreateNewTrip(create);
+ DCDeviceData::instance()->setCreateNewTrip(create);
}
void QMLManager::DC_setSaveDump(bool dumpMode)
{
- m_device_data->setSaveDump(dumpMode);
+ DCDeviceData::instance()->setSaveDump(dumpMode);
}
QStringList QMLManager::getProductListFromVendor(const QString &vendor)
{
- return m_device_data->getProductListFromVendor(vendor);
+ return DCDeviceData::instance()->getProductListFromVendor(vendor);
}
int QMLManager::getMatchingAddress(const QString &vendor, const QString &product)
{
- return m_device_data->getMatchingAddress(vendor, product);
+ return DCDeviceData::instance()->getMatchingAddress(vendor, product);
}
int QMLManager::getDetectedVendorIndex()
{
- return m_device_data->getDetectedVendorIndex();
+ return DCDeviceData::instance()->getDetectedVendorIndex();
}
int QMLManager::getDetectedProductIndex(const QString &currentVendorText)
{
- return m_device_data->getDetectedProductIndex(currentVendorText);
+ return DCDeviceData::instance()->getDetectedProductIndex(currentVendorText);
}
int QMLManager::getConnectionIndex(const QString &deviceSubstr)
diff --git a/mobile-widgets/qmlmanager.h b/mobile-widgets/qmlmanager.h
index 1f2790854..2812ae08f 100644
--- a/mobile-widgets/qmlmanager.h
+++ b/mobile-widgets/qmlmanager.h
@@ -62,6 +62,8 @@ public:
QString DC_devName() const;
void DC_setDevName(const QString& devName);
+ Q_INVOKABLE void retrieveBluetoothName();
+
QString DC_devBluetoothName() const;
void DC_setDevBluetoothName(const QString& devBluetoothName);
@@ -165,6 +167,16 @@ public slots:
void deleteDive(int id);
void copyDiveData(int id);
void pasteDiveData(int id);
+ bool toggleDiveSite(bool toggle);
+ bool toggleNotes(bool toggle);
+ bool toggleDiveMaster(bool toggle);
+ bool toggleBuddy(bool toggle);
+ bool toggleSuit(bool toggle);
+ bool toggleRating(bool toggle);
+ bool toggleVisibility(bool toggle);
+ bool toggleTags(bool toggle);
+ bool toggleCylinders(bool toggle);
+ bool toggleWeights(bool toggle);
bool undoDelete(int id);
QString addDive();
void addDiveAborted(int id);
diff --git a/packaging/android/android-build-wrapper.sh b/packaging/android/android-build-wrapper.sh
index 16d50ce82..999cca221 100755
--- a/packaging/android/android-build-wrapper.sh
+++ b/packaging/android/android-build-wrapper.sh
@@ -83,6 +83,7 @@ if [ ! -d "$ANDROID_SDK"/build-tools/"${ANDROID_BUILDTOOLS_REVISION}" ] ; then
unzip -q ../"$SDK_TOOLS"
yes | tools/bin/sdkmanager --licenses > /dev/null 2>&1 || echo "d56f5187479451eabf01fb78af6dfcb131a6481e" > licenses/android-sdk-license
cat licenses/android-sdk-license
+ tools/bin/sdkmanager tools platform-tools 'platforms;'"${ANDROID_PLATFORMS}" 'build-tools;'"${ANDROID_BUILDTOOLS_REVISION}"
echo ""
else
pushd "$ANDROID_SDK"
diff --git a/packaging/android/build.sh b/packaging/android/build.sh
index 99c33b1bc..81db27488 100755
--- a/packaging/android/build.sh
+++ b/packaging/android/build.sh
@@ -416,7 +416,6 @@ cmake $MOBILE_CMAKE \
-DFTDISUPPORT=${FTDI} \
-DANDROID_NATIVE_LIBSSL="$BUILDROOT/ndk-$ARCH/sysroot/usr/lib/libssl.so" \
-DANDROID_NATIVE_LIBCRYPT="$BUILDROOT/ndk-$ARCH/sysroot/usr/lib/libcrypto.so" \
- -DBUILDTOOLS_REVISION="$ANDROID_BUILDTOOLS_REVISION" \
-DCMAKE_MAKE_PROGRAM="make" \
"$SUBSURFACE_SOURCE"
diff --git a/qt-models/diveimportedmodel.cpp b/qt-models/diveimportedmodel.cpp
index e988a8678..3e8386b94 100644
--- a/qt-models/diveimportedmodel.cpp
+++ b/qt-models/diveimportedmodel.cpp
@@ -4,11 +4,8 @@
DiveImportedModel::DiveImportedModel(QObject *o) : QAbstractTableModel(o),
firstIndex(0),
lastIndex(-1),
- checkStates(nullptr),
diveTable(nullptr)
{
- // Defaults to downloadTable, can be changed later.
- diveTable = &downloadTable;
}
int DiveImportedModel::columnCount(const QModelIndex&) const
@@ -46,11 +43,6 @@ QVariant DiveImportedModel::headerData(int section, Qt::Orientation orientation,
return QVariant();
}
-void DiveImportedModel::setDiveTable(struct dive_table* table)
-{
- diveTable = table;
-}
-
QVariant DiveImportedModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
@@ -97,7 +89,7 @@ void DiveImportedModel::changeSelected(QModelIndex clickedIndex)
void DiveImportedModel::selectAll()
{
- memset(checkStates, true, lastIndex - firstIndex + 1);
+ std::fill(checkStates.begin(), checkStates.end(), true);
dataChanged(index(0, 0), index(lastIndex - firstIndex, 0), QVector<int>() << Qt::CheckStateRole << Selected);
}
@@ -109,7 +101,7 @@ void DiveImportedModel::selectRow(int row)
void DiveImportedModel::selectNone()
{
- memset(checkStates, false, lastIndex - firstIndex + 1);
+ std::fill(checkStates.begin(), checkStates.end(), false);
dataChanged(index(0, 0), index(lastIndex - firstIndex,0 ), QVector<int>() << Qt::CheckStateRole << Selected);
}
@@ -135,26 +127,17 @@ void DiveImportedModel::clearTable()
endRemoveRows();
}
-void DiveImportedModel::setImportedDivesIndexes(int first, int last)
+void DiveImportedModel::repopulate(dive_table_t *table)
{
- if (lastIndex >= firstIndex) {
- beginRemoveRows(QModelIndex(), 0, lastIndex - firstIndex);
- endRemoveRows();
- }
- if (last >= first)
- beginInsertRows(QModelIndex(), 0, last - first);
- lastIndex = last;
- firstIndex = first;
- delete[] checkStates;
- checkStates = new bool[last - first + 1];
- memset(checkStates, true, last - first + 1);
- if (last >= first)
- endInsertRows();
-}
+ beginResetModel();
-void DiveImportedModel::repopulate()
-{
- setImportedDivesIndexes(0, diveTable->nr-1);
+ diveTable = table;
+ firstIndex = 0;
+ lastIndex = diveTable->nr - 1;
+ checkStates.resize(diveTable->nr);
+ std::fill(checkStates.begin(), checkStates.end(), true);
+
+ endResetModel();
}
void DiveImportedModel::recordDives()
@@ -170,7 +153,7 @@ void DiveImportedModel::recordDives()
if (checkStates[i])
j++;
else
- delete_dive_from_table(&downloadTable, j);
+ delete_dive_from_table(diveTable, j);
}
process_imported_dives(diveTable, true, true);
diff --git a/qt-models/diveimportedmodel.h b/qt-models/diveimportedmodel.h
index eeea0b82c..0c5ba34cd 100644
--- a/qt-models/diveimportedmodel.h
+++ b/qt-models/diveimportedmodel.h
@@ -2,6 +2,8 @@
#define DIVEIMPORTEDMODEL_H
#include <QAbstractTableModel>
+#include <vector>
+#include "core/dive.h"
class DiveImportedModel : public QAbstractTableModel
{
@@ -10,7 +12,6 @@ public:
enum roleTypes { DateTime = Qt::UserRole + 1, Duration, Depth, Selected};
DiveImportedModel(QObject *parent = 0);
- void setDiveTable(struct dive_table *table);
int columnCount(const QModelIndex& index = QModelIndex()) const;
int rowCount(const QModelIndex& index = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role) const;
@@ -19,7 +20,7 @@ public:
Qt::ItemFlags flags(const QModelIndex &index) const;
Q_INVOKABLE void clearTable();
QHash<int, QByteArray> roleNames() const;
- Q_INVOKABLE void repopulate();
+ Q_INVOKABLE void repopulate(dive_table_t *table);
Q_INVOKABLE void recordDives();
public
slots:
@@ -31,7 +32,7 @@ slots:
private:
int firstIndex;
int lastIndex;
- bool *checkStates;
+ std::vector<char> checkStates; // char instead of bool to avoid silly pessimization of std::vector.
struct dive_table *diveTable;
};
diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp
index ae7641ba2..7a76ffbdb 100644
--- a/qt-models/divetripmodel.cpp
+++ b/qt-models/divetripmodel.cpp
@@ -696,12 +696,13 @@ void DiveTripModel::topLevelChanged(int idx)
// If that didn't change, try to move forward
if (newIdx == idx) {
- while (newIdx <= (int)items.size() && !dive_or_trip_less_than(items[idx].d_or_t, items[newIdx + 1].d_or_t))
+ ++newIdx;
+ while (newIdx < (int)items.size() && !dive_or_trip_less_than(items[idx].d_or_t, items[newIdx].d_or_t))
++newIdx;
}
// If index changed, move items
- if (newIdx != idx) {
+ if (newIdx != idx && newIdx != idx + 1) {
beginMoveRows(QModelIndex(), idx, idx, QModelIndex(), newIdx);
moveInVector(items, idx, idx + 1, newIdx);
endMoveRows();
diff --git a/qt-models/filtermodels.cpp b/qt-models/filtermodels.cpp
index 4063ba149..25344b712 100644
--- a/qt-models/filtermodels.cpp
+++ b/qt-models/filtermodels.cpp
@@ -15,547 +15,71 @@
#include <QDebug>
#include <algorithm>
-#define CREATE_INSTANCE_METHOD(CLASS) \
- CLASS *CLASS::instance() \
- { \
- static CLASS *self = new CLASS(); \
- return self; \
- }
-
-CREATE_INSTANCE_METHOD(TagFilterModel)
-CREATE_INSTANCE_METHOD(BuddyFilterModel)
-CREATE_INSTANCE_METHOD(LocationFilterModel)
-CREATE_INSTANCE_METHOD(SuitsFilterModel)
-CREATE_INSTANCE_METHOD(MultiFilterSortModel)
-
-FilterModelBase::FilterModelBase(QObject *parent) : QAbstractListModel(parent),
- anyChecked(false),
- negate(false)
-{
-}
-
-// Get index of item with given name, but ignore last item, as this
-// is the "Show Empty Tags" item. Return -1 for not found.
-int FilterModelBase::indexOf(const QString &name) const
-{
- for (int i = 0; i < rowCount() - 1; i++) {
- if (name == items[i].name)
- return i;
- }
- return -1;
-}
-
-int FilterModelBase::findInsertionIndex(const QString &name)
-{
- // Find insertion position. Note: we search only up to the last
- // item, because the last item is the "Show Empty Tags" item.
- // N.B: We might do a binary search using std::lower_bound()
- int i;
- for (i = 0; i < rowCount() - 1; i++) {
- if (name < items[i].name)
- return i;
- }
- return i;
-}
-
-void FilterModelBase::addItem(const QString &name, bool checked, int count)
-{
- int idx = findInsertionIndex(name);
- beginInsertRows(QModelIndex(), idx, idx);
- items.insert(items.begin() + idx, { name, checked, count });
- endInsertRows();
-}
-
-void FilterModelBase::changeName(const QString &oldName, const QString &newName)
-{
- if (oldName.isEmpty() || newName.isEmpty() || oldName == newName)
- return;
- int oldIndex = indexOf(oldName);
- if (oldIndex < 0)
- return;
- int newIndex = indexOf(newName);
-
- if (newIndex >= 0) {
- // If there was already an entry with the new name, we are merging entries.
- // Thus, if the old entry was selected, also select the new entry.
- if (items[oldIndex].checked && !items[newIndex].checked) {
- items[newIndex].checked = true;
- dataChanged(createIndex(newIndex, 0), createIndex(newIndex, 0));
- }
- // Now, delete the old item
- beginRemoveRows(QModelIndex(), oldIndex, oldIndex);
- items.erase(items.begin() + oldIndex);
- endRemoveRows();
- } else {
- // There was no entry of the same name. We might have to move the item.
- newIndex = findInsertionIndex(newName);
- if (oldIndex != newIndex && oldIndex + 1 != newIndex) {
- beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
- moveInVector(items, oldIndex, oldIndex + 1, newIndex);
- endMoveRows();
- }
-
- // The item was moved, but the name still has to be modified
- items[newIndex].name = newName;
- dataChanged(createIndex(newIndex, 0), createIndex(newIndex, 0));
- }
-}
-
-// Update the the items array.
-// The last item is supposed to be the "Show Empty Tags" entry.
-// All other items will be sorted alphabetically. Attention: the passed-in list is modified!
-void FilterModelBase::updateList(QStringList &newList)
-{
- // Sort list, but leave out last element by using std::prev()
- if (!newList.empty())
- std::sort(newList.begin(), std::prev(newList.end()));
-
- beginResetModel();
-
- // Keep copy of the old items array to reimport the checked state later.
- // Note that by using std::move(), this is an essentially free operation:
- // The data is moved from the old array to the new one and the old array
- // is reset to zero size.
- std::vector<Item> oldItems = std::move(items);
-
- // Resize the cleared array to the new size. This leaves the checked
- // flag in an undefined state (since we didn't define a default constructor).
- items.resize(newList.count());
-
- // First, reset all checked states to false and set the names
- anyChecked = false;
- for (int i = 0; i < rowCount(); ++i) {
- items[i].name = newList[i];
- items[i].checked = false;
- }
-
- // Then, restore the checked state. Ignore the last item, since
- // this is the "Show Empty Tags" entry.
- for (int i = 0; i < (int)oldItems.size() - 1; i++) {
- if (oldItems[i].checked) {
- int ind = newList.indexOf(oldItems[i].name);
- if (ind >= 0 && ind < newList.count() - 1) {
- items[ind].checked = true;
- anyChecked = true;
- }
- }
- }
-
- // Reset the state of the "Show Empty Tags" entry. But be careful:
- // on program startup, the old list is empty.
- if (!oldItems.empty() && !items.empty() && oldItems.back().checked) {
- items.back().checked = true;
- anyChecked = true;
- }
-
- // Finally, calculate and cache the counts. Ignore the last item, since
- // this is the "Show Empty Tags" entry.
- for (int i = 0; i < (int)newList.size() - 1; i++)
- items[i].count = countDives(qPrintable(newList[i]));
-
- // Calculate count of "Empty Tags".
- if (!items.empty())
- items.back().count = countDives("");
-
- endResetModel();
-}
-
-// Decrease count of entry with given name. Remove if count reaches zero.
-// Exception: Don't remove the "Show Empty Tags" entry.
-void FilterModelBase::decreaseCount(const QString &name)
-{
- if (name.isEmpty()) {
- // Decrease the "Show Empty Tags" entry. Keep it even if count reaches 0.
- if (items.empty() || items.back().count <= 0)
- return; // Shouldn't happen!
- --items.back().count;
- int idx = items.size() - 1;
- dataChanged(createIndex(idx, 0), createIndex(idx, 0));
- return;
- }
-
- int idx = indexOf(name);
- if (idx < 0 || items[idx].count <= 0)
- return; // Shouldn't happen
-
- if(--items[idx].count == 0) {
- beginRemoveRows(QModelIndex(), idx, idx);
- items.erase(items.begin() + idx);
- endRemoveRows();
- } else {
- dataChanged(createIndex(idx, 0), createIndex(idx, 0));
- }
-}
-
-// Increase count of entry with given name. If entry doesn't yet exist, add it.
-void FilterModelBase::increaseCount(const QString &name)
-{
- if (name.isEmpty()) {
- // Increase the "Show Empty Tags" entry. Keep it even if count reaches 0.
- if (items.empty())
- return; // Shouldn't happen!
- ++items.back().count;
- int idx = items.size() - 1;
- dataChanged(createIndex(idx, 0), createIndex(idx, 0));
- return;
- }
-
- int idx = indexOf(name);
- if (idx < 0) {
- idx = findInsertionIndex(name);
- beginInsertRows(QModelIndex(), idx, idx);
- items.insert(items.begin() + idx, { name, anyChecked, 1 });
- endInsertRows();
- } else {
- ++items[idx].count;
- dataChanged(createIndex(idx, 0), createIndex(idx, 0));
- }
-}
-
-Qt::ItemFlags FilterModelBase::flags(const QModelIndex &index) const
-{
- return QAbstractListModel::flags(index) | Qt::ItemIsUserCheckable;
-}
-
-bool FilterModelBase::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- if (role == Qt::CheckStateRole) {
- items[index.row()].checked = value.toBool();
- anyChecked = false;
- for (const Item &item: items) {
- if (item.checked) {
- anyChecked = true;
- break;
- }
+namespace {
+ bool hasTag(const QStringList tags, const struct dive *d)
+ {
+ if (!tags.isEmpty()) {
+ auto dive_tags = get_taglist_string(d->tag_list).split(",");
+ bool found_tag = false;
+ for (const auto& filter_tag : tags)
+ for (const auto& dive_tag : dive_tags)
+ if (dive_tag.trimmed().toUpper().contains(filter_tag.trimmed().toUpper()))
+ found_tag = true;
+
+ return found_tag;
}
- dataChanged(index, index, { role });
- return true;
- }
- return false;
-}
-
-int FilterModelBase::rowCount(const QModelIndex &) const
-{
- return items.size();
-}
-
-QVariant FilterModelBase::data(const QModelIndex &index, int role) const
-{
- if (role == Qt::CheckStateRole) {
- return items[index.row()].checked ? Qt::Checked : Qt::Unchecked;
- } else if (role == Qt::DisplayRole) {
- const Item &item = items[index.row()];
- return QStringLiteral("%1 (%2)").arg(item.name, QString::number(item.count));
- }
- return QVariant();
-}
-
-void FilterModelBase::clearFilter()
-{
- for (Item &item: items)
- item.checked = false;
- anyChecked = false;
- emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
-}
-
-void FilterModelBase::selectAll()
-{
- for (Item &item: items)
- item.checked = true;
- anyChecked = true;
- emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
-}
-
-void FilterModelBase::invertSelection()
-{
- for (Item &item: items)
- item.checked = !item.checked;
- anyChecked = std::any_of(items.begin(), items.end(), [](Item &item) { return !!item.checked; });
- emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
-}
-
-void FilterModelBase::setNegate(bool negateParam)
-{
- negate = negateParam;
- emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
-}
-
-SuitsFilterModel::SuitsFilterModel(QObject *parent) : FilterModelBase(parent)
-{
-}
-
-int SuitsFilterModel::countDives(const char *s) const
-{
- return count_dives_with_suit(s);
-}
-
-bool SuitsFilterModel::doFilter(const dive *d) const
-{
- // rowCount() == 0 should never happen, because we have the "no suits" row
- // let's handle it gracefully anyway.
- if (!anyChecked || rowCount() == 0)
return true;
-
- // Checked means 'Show', Unchecked means 'Hide'.
- QString suit(d->suit);
- // only show empty suit dives if the user checked that.
- if (suit.isEmpty())
- return items[rowCount() - 1].checked != negate;
-
- // there is a suit selected
- // Ignore last item, since this is the "Show Empty Tags" entry
- for (int i = 0; i < rowCount() - 1; i++) {
- if (items[i].checked && suit == items[i].name)
- return !negate;
}
- return negate;
-}
-void SuitsFilterModel::diveAdded(const dive *d)
-{
- increaseCount(QString(d->suit));
-}
+ bool hasPerson(const QStringList people, const struct dive *d)
+ {
+ if (!people.isEmpty()) {
+ QStringList dive_people = QString(d->buddy).split(",", QString::SkipEmptyParts)
+ + QString(d->divemaster).split(",", QString::SkipEmptyParts);
-void SuitsFilterModel::diveDeleted(const dive *d)
-{
- decreaseCount(QString(d->suit));
-}
+ bool found_person = false;
+ for(const auto& filter_person : people)
+ for(const auto& dive_person : dive_people)
+ if (dive_person.trimmed().toUpper().contains(filter_person.trimmed().toUpper()))
+ found_person = true;
-void SuitsFilterModel::repopulate()
-{
- QStringList list;
- struct dive *dive;
- int i = 0;
- for_each_dive (i, dive) {
- QString suit(dive->suit);
- if (!suit.isEmpty() && !list.contains(suit)) {
- list.append(suit);
+ return found_person;
}
- }
- list << tr("No suit set");
- updateList(list);
-}
-
-TagFilterModel::TagFilterModel(QObject *parent) : FilterModelBase(parent)
-{
-}
-
-int TagFilterModel::countDives(const char *s) const
-{
- return count_dives_with_tag(s);
-}
-
-void TagFilterModel::repopulate()
-{
- if (g_tag_list == NULL)
- return;
- QStringList list;
- struct tag_entry *current_tag_entry = g_tag_list;
- while (current_tag_entry != NULL) {
- if (count_dives_with_tag(current_tag_entry->tag->name) > 0)
- list.append(QString(current_tag_entry->tag->name));
- current_tag_entry = current_tag_entry->next;
- }
- list << tr("Empty tags");
- updateList(list);
-}
-
-bool TagFilterModel::doFilter(const dive *d) const
-{
- // If there's nothing checked, this should show everything
- // rowCount() == 0 should never happen, because we have the "no tags" row
- // let's handle it gracefully anyway.
- if (!anyChecked || rowCount() == 0)
return true;
-
- // Checked means 'Show', Unchecked means 'Hide'.
- struct tag_entry *head = d->tag_list;
-
- if (!head) // last tag means "Show empty tags";
- return items[rowCount() - 1].checked != negate;
-
- // have at least one tag.
- while (head) {
- QString tagName(head->tag->name);
- int index = indexOf(tagName);
- if (index >= 0 && items[index].checked)
- return !negate;
- head = head->next;
- }
- return negate;
-}
-
-void TagFilterModel::diveAdded(const dive *d)
-{
- struct tag_entry *head = d->tag_list;
- if (!head) {
- increaseCount(QString());
- return;
- }
- while (head) {
- increaseCount(QString());
- increaseCount(QString(head->tag->name));
- head = head->next;
}
-}
-
-void TagFilterModel::diveDeleted(const dive *d)
-{
- struct tag_entry *head = d->tag_list;
- if (!head) {
- decreaseCount(QString());
- return;
- }
- while (head) {
- decreaseCount(QString(head->tag->name));
- head = head->next;
- }
-}
-BuddyFilterModel::BuddyFilterModel(QObject *parent) : FilterModelBase(parent)
-{
-}
-
-int BuddyFilterModel::countDives(const char *s) const
-{
- return count_dives_with_person(s);
-}
-
-static QStringList getDiveBuddies(const dive *d)
-{
- QString persons = QString(d->buddy) + "," + QString(d->divemaster);
- QStringList personsList = persons.split(',', QString::SkipEmptyParts);
- for (QString &s: personsList)
- s = s.trimmed();
- return personsList;
-}
-
-bool BuddyFilterModel::doFilter(const dive *d) const
-{
- // If there's nothing checked, this should show everything
- // rowCount() == 0 should never happen, because we have the "no tags" row
- // let's handle it gracefully anyway.
- if (!anyChecked || rowCount() == 0)
- return true;
-
- QStringList personsList = getDiveBuddies(d);
- // only show empty buddie dives if the user checked that.
- if (personsList.isEmpty())
- return items[rowCount() - 1].checked != negate;
+ bool hasLocation(const QStringList locations, const struct dive *d)
+ {
+ if (!locations.isEmpty()) {
+ QStringList diveLocations;
+ if (d->divetrip)
+ diveLocations.push_back(QString(d->divetrip->location));
- // have at least one buddy
- // Ignore last item, since this is the "Show Empty Tags" entry
- for (int i = 0; i < rowCount() - 1; i++) {
- if (items[i].checked && personsList.contains(items[i].name, Qt::CaseInsensitive))
- return !negate;
- }
- return negate;
-}
-
-void BuddyFilterModel::diveAdded(const dive *d)
-{
- QStringList buddies = getDiveBuddies(d);
- if (buddies.empty()) {
- increaseCount(QString());
- return;
- }
- for(const QString &buddy: buddies)
- increaseCount(buddy);
-}
+ if (d->dive_site)
+ diveLocations.push_back(QString(d->dive_site->name));
-void BuddyFilterModel::diveDeleted(const dive *d)
-{
- QStringList buddies = getDiveBuddies(d);
- if (buddies.empty()) {
- decreaseCount(QString());
- return;
- }
- for(const QString &buddy: buddies)
- decreaseCount(buddy);
-}
+ bool found_location = false;
+ for (const auto& filter_location : locations)
+ for (const auto& dive_location : diveLocations)
+ if (dive_location.trimmed().toUpper().contains(filter_location.trimmed().toUpper()))
+ found_location = true;
-void BuddyFilterModel::repopulate()
-{
- QStringList list;
- struct dive *dive;
- int i = 0;
- for_each_dive (i, dive) {
- QString persons = QString(dive->buddy) + "," + QString(dive->divemaster);
- Q_FOREACH (const QString &person, persons.split(',', QString::SkipEmptyParts)) {
- // Remove any leading spaces
- if (!list.contains(person.trimmed())) {
- list.append(person.trimmed());
- }
+ return found_location;
}
- }
- list << tr("No buddies");
- updateList(list);
-}
-
-LocationFilterModel::LocationFilterModel(QObject *parent) : FilterModelBase(parent)
-{
-}
-
-int LocationFilterModel::countDives(const char *s) const
-{
- return count_dives_with_location(s);
-}
-
-bool LocationFilterModel::doFilter(const dive *d) const
-{
- // rowCount() == 0 should never happen, because we have the "no location" row
- // let's handle it gracefully anyway.
- if (!anyChecked || rowCount() == 0)
return true;
-
- // Checked means 'Show', Unchecked means 'Hide'.
- QString location(get_dive_location(d));
- // only show empty location dives if the user checked that.
- if (location.isEmpty())
- return items[rowCount() - 1].checked != negate;
-
- // There is a location selected
- // Ignore last item, since this is the "Show Empty Tags" entry
- for (int i = 0; i < rowCount() - 1; i++) {
- if (items[i].checked && location == items[i].name)
- return !negate;
}
- return negate;
-}
-
-void LocationFilterModel::diveAdded(const dive *d)
-{
- increaseCount(get_dive_location(d));
-}
-
-void LocationFilterModel::diveDeleted(const dive *d)
-{
- decreaseCount(get_dive_location(d));
-}
-void LocationFilterModel::repopulate()
-{
- QStringList list;
- struct dive *dive;
- int i = 0;
- for_each_dive (i, dive) {
- QString location(get_dive_location(dive));
- if (!location.isEmpty() && !list.contains(location)) {
- list.append(location);
- }
+ // TODO: Finish this iimplementation.
+ bool hasEquipment(const QStringList& equipment, const struct dive *d)
+ {
+ return true;
}
- list << tr("No location set");
- updateList(list);
}
-void LocationFilterModel::addName(const QString &newName)
+MultiFilterSortModel *MultiFilterSortModel::instance()
{
- if (newName.isEmpty() || indexOf(newName) >= 0)
- return;
- int count = countDives(qPrintable(newName));
- // If any old item was checked, also check the new one so that
- // dives with the added dive site are shown.
- addItem(newName, anyChecked, count);
+ static MultiFilterSortModel self;
+ return &self;
}
MultiFilterSortModel::MultiFilterSortModel(QObject *parent) : QSortFilterProxyModel(parent),
@@ -574,44 +98,45 @@ void MultiFilterSortModel::setLayout(DiveTripModel::Layout layout)
tripModel->setLayout(layout); // Note: setLayout() resets the whole model
}
-void MultiFilterSortModel::divesAdded(const QVector<dive *> &dives)
+bool MultiFilterSortModel::showDive(const struct dive *d) const
{
- // TODO: We call diveAdded for every dive and model.
- // If multiple dives are added (e.g. import dive) this will lead to a large
- // number of model changes and might be a pessimization compared to a full
- // model reload. Instead, the models should take the vector, calculate the
- // new fields and add them at once.
- for (FilterModelBase *model: models) {
- for (const dive *d: dives)
- model->diveAdded(d);
- }
-}
+ if (!filterData.validFilter)
+ return true;
-void MultiFilterSortModel::divesDeleted(const QVector<dive *> &dives)
-{
- // TODO: See comment for divesDeleted
- for (FilterModelBase *model: models) {
- for (const dive *d: dives)
- model->diveDeleted(d);
- }
-}
+ if (d->visibility < filterData.minVisibility || d->visibility > filterData.maxVisibility)
+ return false;
-bool MultiFilterSortModel::showDive(const struct dive *d) const
-{
- if (curr_dive_site) {
- dive_site *ds = d->dive_site;
- if (!ds)
- return false;
- return ds == curr_dive_site || same_string(ds->name, curr_dive_site->name);
- }
+ if (d->rating < filterData.minRating || d->rating > filterData.maxRating)
+ return false;
- if (models.isEmpty())
- return true;
+ // TODO: get the preferences for the imperial vs metric data.
+ // ignore the check if it doesn't makes sense.
+ if (d->watertemp.mkelvin < C_to_mkelvin(filterData.minWaterTemp) || d->watertemp.mkelvin > C_to_mkelvin((filterData.maxWaterTemp)))
+ return false;
- for (const FilterModelBase *model: models) {
- if (!model->doFilter(d))
- return false;
- }
+ if (d->airtemp.mkelvin < C_to_mkelvin(filterData.minAirTemp) || d->airtemp.mkelvin > C_to_mkelvin(filterData.maxAirTemp))
+ return false;
+
+ if (filterData.from.isValid() && d->when < filterData.from.toTime_t())
+ return false;
+
+ if (filterData.to.isValid() && d->when > filterData.to.toTime_t())
+ return false;
+
+ // tags.
+ if (!hasTag(filterData.tags, d))
+ return false;
+
+ // people
+ if (!hasPerson(filterData.people, d))
+ return false;
+
+ // Location
+ if (!hasLocation(filterData.location, d))
+ return false;
+
+ if (!hasEquipment(filterData.equipment, d))
+ return false;
return true;
}
@@ -674,23 +199,8 @@ void MultiFilterSortModel::myInvalidate()
#endif
}
-void MultiFilterSortModel::addFilterModel(FilterModelBase *model)
-{
- models.append(model);
- connect(model, &FilterModelBase::dataChanged, this, &MultiFilterSortModel::filterChanged);
-}
-
-void MultiFilterSortModel::removeFilterModel(FilterModelBase *model)
-{
- models.removeAll(model);
- disconnect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(myInvalidate()));
-}
-
void MultiFilterSortModel::clearFilter()
{
- Q_FOREACH (FilterModelBase *iface, models) {
- iface->clearFilter();
- }
myInvalidate();
}
@@ -711,3 +221,9 @@ bool MultiFilterSortModel::lessThan(const QModelIndex &i1, const QModelIndex &i2
// Hand sorting down to the source model.
return model->lessThan(i1, i2);
}
+
+void MultiFilterSortModel::filterDataChanged(const FilterData& data)
+{
+ filterData = data;
+ myInvalidate();
+}
diff --git a/qt-models/filtermodels.h b/qt-models/filtermodels.h
index 8830e99c3..61b82be33 100644
--- a/qt-models/filtermodels.h
+++ b/qt-models/filtermodels.h
@@ -6,6 +6,8 @@
#include <QStringListModel>
#include <QSortFilterProxyModel>
+#include <QDateTime>
+
#include <stdint.h>
#include <vector>
@@ -13,108 +15,23 @@ struct dive;
struct dive_trip;
class DiveTripModel;
-class FilterModelBase : public QAbstractListModel {
- Q_OBJECT
-private:
- int findInsertionIndex(const QString &name);
-protected:
- struct Item {
- QString name;
- bool checked;
- int count;
- };
- std::vector<Item> items;
- int indexOf(const QString &name) const;
- void addItem(const QString &name, bool checked, int count);
- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- void decreaseCount(const QString &d);
- void increaseCount(const QString &d);
-public:
- virtual bool doFilter(const dive *d) const = 0;
- virtual void diveAdded(const dive *d) = 0;
- virtual void diveDeleted(const dive *d) = 0;
- void clearFilter();
- void selectAll();
- void invertSelection();
- bool anyChecked;
- bool negate;
-public
-slots:
- void setNegate(bool negate);
- void changeName(const QString &oldName, const QString &newName);
-protected:
- explicit FilterModelBase(QObject *parent = 0);
- void updateList(QStringList &new_list);
- virtual int countDives(const char *) const = 0;
-private:
- Qt::ItemFlags flags(const QModelIndex &index) const;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
-};
-
-class TagFilterModel : public FilterModelBase {
- Q_OBJECT
-public:
- static TagFilterModel *instance();
- bool doFilter(const dive *d) const;
-public
-slots:
- void repopulate();
-
-private:
- explicit TagFilterModel(QObject *parent = 0);
- int countDives(const char *) const;
- void diveAdded(const dive *d);
- void diveDeleted(const dive *d);
-};
-
-class BuddyFilterModel : public FilterModelBase {
- Q_OBJECT
-public:
- static BuddyFilterModel *instance();
- bool doFilter(const dive *d) const;
-public
-slots:
- void repopulate();
-
-private:
- explicit BuddyFilterModel(QObject *parent = 0);
- int countDives(const char *) const;
- void diveAdded(const dive *d);
- void diveDeleted(const dive *d);
-};
-
-class LocationFilterModel : public FilterModelBase {
- Q_OBJECT
-public:
- static LocationFilterModel *instance();
- bool doFilter(const dive *d) const;
-public
-slots:
- void repopulate();
- void addName(const QString &newName);
-
-private:
- explicit LocationFilterModel(QObject *parent = 0);
- int countDives(const char *) const;
- void diveAdded(const dive *d);
- void diveDeleted(const dive *d);
-};
-
-class SuitsFilterModel : public FilterModelBase {
- Q_OBJECT
-public:
- static SuitsFilterModel *instance();
- bool doFilter(const dive *d) const;
-public
-slots:
- void repopulate();
-
-private:
- explicit SuitsFilterModel(QObject *parent = 0);
- int countDives(const char *) const;
- void diveAdded(const dive *d);
- void diveDeleted(const dive *d);
+struct FilterData {
+ bool validFilter = false;
+ int minVisibility = 0;
+ int maxVisibility = 5;
+ int minRating = 0;
+ int maxRating = 5;
+ double minWaterTemp = 0;
+ double maxWaterTemp = 100;
+ double minAirTemp = 0;
+ double maxAirTemp = 100;
+ QDateTime from;
+ QDateTime to = QDateTime::currentDateTime();
+ QStringList tags;
+ QStringList people;
+ QStringList location;
+ QStringList equipment;
+ bool invertFilter;
};
class MultiFilterSortModel : public QSortFilterProxyModel {
@@ -122,10 +39,6 @@ class MultiFilterSortModel : public QSortFilterProxyModel {
public:
static MultiFilterSortModel *instance();
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
- void addFilterModel(FilterModelBase *model);
- void removeFilterModel(FilterModelBase *model);
- void divesAdded(const QVector<dive *> &dives);
- void divesDeleted(const QVector<dive *> &dives);
bool showDive(const struct dive *d) const;
int divesDisplayed;
bool lessThan(const QModelIndex &, const QModelIndex &) const override;
@@ -137,14 +50,16 @@ slots:
void stopFilterDiveSite();
void filterChanged(const QModelIndex &from, const QModelIndex &to, const QVector<int> &roles);
void setLayout(DiveTripModel::Layout layout);
+ void filterDataChanged(const FilterData& data);
signals:
void filterFinished();
+
private:
MultiFilterSortModel(QObject *parent = 0);
- QList<FilterModelBase *> models;
struct dive_site *curr_dive_site;
DiveTripModel *model;
+ FilterData filterData;
};
#endif
diff --git a/qt-models/yearlystatisticsmodel.cpp b/qt-models/yearlystatisticsmodel.cpp
index c8aa2639b..fa86cffbe 100644
--- a/qt-models/yearlystatisticsmodel.cpp
+++ b/qt-models/yearlystatisticsmodel.cpp
@@ -15,6 +15,7 @@ public:
SHORTEST_TIME,
LONGEST_TIME,
AVG_DEPTH,
+ AVG_MAX_DEPTH,
MIN_DEPTH,
MAX_DEPTH,
AVG_SAC,
@@ -74,6 +75,10 @@ QVariant YearStatisticsItem::data(int column, int role) const
case AVG_DEPTH:
ret = get_depth_string(stats_interval.avg_depth);
break;
+ case AVG_MAX_DEPTH:
+ if (stats_interval.selection_size)
+ ret = get_depth_string(stats_interval.combined_max_depth.mm / stats_interval.selection_size);
+ break;
case MIN_DEPTH:
ret = get_depth_string(stats_interval.min_depth);
break;
@@ -143,6 +148,9 @@ QVariant YearlyStatisticsModel::headerData(int section, Qt::Orientation orientat
case AVG_DEPTH:
val = QString(tr("Depth (%1)\n Average")).arg(get_depth_unit());
break;
+ case AVG_MAX_DEPTH:
+ val = tr("\nAverage maximum");
+ break;
case MIN_DEPTH:
val = tr("\nMinimum");
break;
diff --git a/qt-models/yearlystatisticsmodel.h b/qt-models/yearlystatisticsmodel.h
index 77a5ae074..99e646388 100644
--- a/qt-models/yearlystatisticsmodel.h
+++ b/qt-models/yearlystatisticsmodel.h
@@ -15,6 +15,7 @@ public:
SHORTEST_TIME,
LONGEST_TIME,
AVG_DEPTH,
+ AVG_MAX_DEPTH,
MIN_DEPTH,
MAX_DEPTH,
AVG_SAC,
diff --git a/scripts/windows-container/before_install.sh b/scripts/windows-container/before_install.sh
index bcfcf761a..b0d0e5f13 100644
--- a/scripts/windows-container/before_install.sh
+++ b/scripts/windows-container/before_install.sh
@@ -38,6 +38,7 @@ docker run -v $PWD/win32:/win/win32 -v $PWD/subsurface:/win/subsurface --name=bu
# for some reason this package was installed but still isn't there?
# hmmmm. The container doesn't seem to have libtool installed
+docker exec -t builder apt-get update
docker exec -t builder apt-get install -y ca-certificates libtool
# now set up our other dependencies
diff --git a/tests/testpicture.cpp b/tests/testpicture.cpp
index 695c965b0..a84925c72 100644
--- a/tests/testpicture.cpp
+++ b/tests/testpicture.cpp
@@ -25,13 +25,15 @@ void TestPicture::addPicture()
QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test44.xml", &dive_table), 0);
dive = get_dive(0);
+ // Pictures will be added to selected dives
+ dive->selected = true;
QVERIFY(dive != NULL);
pic1 = dive->picture_list;
// So far no picture in dive
QVERIFY(pic1 == NULL);
- dive_create_picture(dive, SUBSURFACE_TEST_DATA "/dives/images/wreck.jpg", 0, false);
- dive_create_picture(dive, SUBSURFACE_TEST_DATA "/dives/images/data_after_EOI.jpg", 0, false);
+ create_picture(SUBSURFACE_TEST_DATA "/dives/images/wreck.jpg", 0, false);
+ create_picture(SUBSURFACE_TEST_DATA "/dives/images/data_after_EOI.jpg", 0, false);
pic1 = dive->picture_list;
pic2 = pic1->next;
// Now there are two picture2