core: remove filterconstraint C boilerplate code

Since all code can now directly access C++ structures these
accessor functions were not necessary.

Split out the table from the filterconstraint source file
and include it directly into the divelog.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2024-06-08 18:54:23 +02:00 committed by bstoeger
parent 2bdcdab391
commit 91968ac579
20 changed files with 212 additions and 250 deletions

View file

@ -55,6 +55,7 @@ SOURCES += subsurface-mobile-main.cpp \
core/eventtype.cpp \ core/eventtype.cpp \
core/filterconstraint.cpp \ core/filterconstraint.cpp \
core/filterpreset.cpp \ core/filterpreset.cpp \
core/filterpresettable.cpp \
core/divelist.cpp \ core/divelist.cpp \
core/divelog.cpp \ core/divelog.cpp \
core/gas-model.cpp \ core/gas-model.cpp \
@ -228,6 +229,7 @@ HEADERS += \
core/divefilter.h \ core/divefilter.h \
core/filterconstraint.h \ core/filterconstraint.h \
core/filterpreset.h \ core/filterpreset.h \
core/filterpresettable.h \
core/divelist.h \ core/divelist.h \
core/divelog.h \ core/divelog.h \
core/divelogexportlogic.h \ core/divelogexportlogic.h \

View file

@ -492,11 +492,11 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source)
// When encountering filter presets with equal names, check whether they are // When encountering filter presets with equal names, check whether they are
// the same. If they are, ignore them. // the same. If they are, ignore them.
for (const filter_preset &preset: *log->filter_presets) { for (const filter_preset &preset: log->filter_presets) {
std::string name = preset.name; std::string name = preset.name;
auto it = std::find_if(divelog.filter_presets->begin(), divelog.filter_presets->end(), auto it = std::find_if(divelog.filter_presets.begin(), divelog.filter_presets.end(),
[&name](const filter_preset &preset) { return preset.name == name; }); [&name](const filter_preset &preset) { return preset.name == name; });
if (it != divelog.filter_presets->end() && it->data == preset.data) if (it != divelog.filter_presets.end() && it->data == preset.data)
continue; continue;
filterPresetsToAdd.emplace_back(preset.name, preset.data); filterPresetsToAdd.emplace_back(preset.name, preset.data);
} }
@ -530,7 +530,7 @@ void ImportDives::redoit()
// Add new filter presets // Add new filter presets
for (auto &it: filterPresetsToAdd) { for (auto &it: filterPresetsToAdd) {
filterPresetsToRemove.push_back(filter_preset_add(it.first, it.second)); filterPresetsToRemove.push_back(divelog.filter_presets.add(it.first, it.second));
emit diveListNotifier.filterPresetAdded(filterPresetsToRemove.back()); emit diveListNotifier.filterPresetAdded(filterPresetsToRemove.back());
} }
filterPresetsToAdd.clear(); filterPresetsToAdd.clear();
@ -559,9 +559,10 @@ void ImportDives::undoit()
// Remove filter presets. Do this in reverse order. // Remove filter presets. Do this in reverse order.
for (auto it = filterPresetsToRemove.rbegin(); it != filterPresetsToRemove.rend(); ++it) { for (auto it = filterPresetsToRemove.rbegin(); it != filterPresetsToRemove.rend(); ++it) {
int index = *it; int index = *it;
std::string oldName = filter_preset_name(index); const filter_preset &preset = divelog.filter_presets[index];
FilterData oldData = filter_preset_get(index); std::string oldName = preset.name;
filter_preset_delete(index); FilterData oldData = preset.data;
divelog.filter_presets.remove(index);
emit diveListNotifier.filterPresetRemoved(index); emit diveListNotifier.filterPresetRemoved(index);
filterPresetsToAdd.emplace_back(oldName, oldData); filterPresetsToAdd.emplace_back(oldName, oldData);
} }

View file

@ -1,23 +1,26 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include "command_filter.h" #include "command_filter.h"
#include "core/divelog.h"
#include "core/filterpreset.h" #include "core/filterpreset.h"
#include "core/filterpresettable.h"
#include "core/subsurface-qt/divelistnotifier.h" #include "core/subsurface-qt/divelistnotifier.h"
namespace Command { namespace Command {
static int createFilterPreset(const std::string &name, const FilterData &data) static int createFilterPreset(const std::string &name, const FilterData &data)
{ {
int index = filter_preset_add(name, data); int index = divelog.filter_presets.add(name, data);
emit diveListNotifier.filterPresetAdded(index); emit diveListNotifier.filterPresetAdded(index);
return index; return index;
} }
static std::pair<std::string, FilterData> removeFilterPreset(int index) static std::pair<std::string, FilterData> removeFilterPreset(int index)
{ {
std::string oldName = filter_preset_name(index); const filter_preset &preset = divelog.filter_presets[index];
FilterData oldData = filter_preset_get(index); std::string oldName = preset.name;
filter_preset_delete(index); FilterData oldData = preset.data;
divelog.filter_presets.remove(index);
emit diveListNotifier.filterPresetRemoved(index); emit diveListNotifier.filterPresetRemoved(index);
return { oldName, oldData }; return { oldName, oldData };
} }
@ -46,7 +49,8 @@ void CreateFilterPreset::undo()
RemoveFilterPreset::RemoveFilterPreset(int indexIn) : index(indexIn) RemoveFilterPreset::RemoveFilterPreset(int indexIn) : index(indexIn)
{ {
setText(Command::Base::tr("Delete filter preset %1").arg(QString(filter_preset_name(index).c_str()))); const std::string &name = divelog.filter_presets[index].name;
setText(Command::Base::tr("Delete filter preset %1").arg(QString::fromStdString(name)));
} }
bool RemoveFilterPreset::workToBeDone() bool RemoveFilterPreset::workToBeDone()
@ -68,7 +72,8 @@ void RemoveFilterPreset::undo()
EditFilterPreset::EditFilterPreset(int indexIn, const FilterData &dataIn) : EditFilterPreset::EditFilterPreset(int indexIn, const FilterData &dataIn) :
index(indexIn), data(dataIn) index(indexIn), data(dataIn)
{ {
setText(Command::Base::tr("Edit filter preset %1").arg(QString(filter_preset_name(index).c_str()))); const std::string &name = divelog.filter_presets[index].name;
setText(Command::Base::tr("Edit filter preset %1").arg(QString::fromStdString(name)));
} }
bool EditFilterPreset::workToBeDone() bool EditFilterPreset::workToBeDone()
@ -78,9 +83,8 @@ bool EditFilterPreset::workToBeDone()
void EditFilterPreset::redo() void EditFilterPreset::redo()
{ {
FilterData oldData = filter_preset_get(index); filter_preset &preset = divelog.filter_presets[index];
filter_preset_set(index, data); std::swap(data, preset.data);
data = std::move(oldData);
} }
void EditFilterPreset::undo() void EditFilterPreset::undo()

View file

@ -95,6 +95,8 @@ set(SUBSURFACE_CORE_LIB_SRCS
filterconstraint.h filterconstraint.h
filterpreset.cpp filterpreset.cpp
filterpreset.h filterpreset.h
filterpresettable.cpp
filterpresettable.h
format.cpp format.cpp
format.h format.h
fulltext.cpp fulltext.cpp

View file

@ -6,16 +6,12 @@
#include "dive.h" #include "dive.h"
#include "errorhelper.h" #include "errorhelper.h"
#include "filterpreset.h" #include "filterpreset.h"
#include "filterpresettable.h"
#include "trip.h" #include "trip.h"
struct divelog divelog; struct divelog divelog;
divelog::divelog() : divelog::divelog() = default;
filter_presets(std::make_unique<filter_preset_table>()),
autogroup(false)
{
}
divelog::~divelog() = default; divelog::~divelog() = default;
divelog::divelog(divelog &&) = default; divelog::divelog(divelog &&) = default;
struct divelog &divelog::operator=(divelog &&) = default; struct divelog &divelog::operator=(divelog &&) = default;
@ -68,7 +64,7 @@ void divelog::clear()
sites.clear(); sites.clear();
trips.clear(); trips.clear();
devices.clear(); devices.clear();
filter_presets->clear(); filter_presets.clear();
} }
/* check if we have a trip right before / after this dive */ /* check if we have a trip right before / after this dive */

View file

@ -1,26 +1,24 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
// A structure that contains all the data we store in a divelog files // A structure that contains all the data we store in divelog files
#ifndef DIVELOG_H #ifndef DIVELOG_H
#define DIVELOG_H #define DIVELOG_H
#include "divelist.h" #include "divelist.h"
#include "divesitetable.h" #include "divesitetable.h"
#include "filterpresettable.h"
#include "triptable.h" #include "triptable.h"
#include <memory>
#include <vector> #include <vector>
struct trip_table;
struct device; struct device;
struct filter_preset_table;
struct divelog { struct divelog {
dive_table dives; dive_table dives;
trip_table trips; trip_table trips;
dive_site_table sites; dive_site_table sites;
std::vector<device> devices; std::vector<device> devices;
std::unique_ptr<filter_preset_table> filter_presets; filter_preset_table filter_presets;
bool autogroup; bool autogroup = false;
divelog(); divelog();
~divelog(); ~divelog();

View file

@ -134,33 +134,33 @@ static const range_mode_description *get_range_mode_description(enum filter_cons
return nullptr; return nullptr;
} }
static enum filter_constraint_type filter_constraint_type_from_string(const char *s) static enum filter_constraint_type filter_constraint_type_from_string(const std::string &s)
{ {
for (const auto &desc: type_descriptions) { for (const auto &desc: type_descriptions) {
if (same_string(desc.token, s)) if (desc.token == s)
return desc.type; return desc.type;
} }
report_error("unknown filter constraint type: %s", s); report_error("unknown filter constraint type: %s", s.c_str());
return FILTER_CONSTRAINT_DATE; return FILTER_CONSTRAINT_DATE;
} }
static enum filter_constraint_string_mode filter_constraint_string_mode_from_string(const char *s) static enum filter_constraint_string_mode filter_constraint_string_mode_from_string(const std::string &s)
{ {
for (const auto &desc: string_mode_descriptions) { for (const auto &desc: string_mode_descriptions) {
if (same_string(desc.token, s)) if (desc.token == s)
return desc.mode; return desc.mode;
} }
report_error("unknown filter constraint string mode: %s", s); report_error("unknown filter constraint string mode: %s", s.c_str());
return FILTER_CONSTRAINT_EXACT; return FILTER_CONSTRAINT_EXACT;
} }
static enum filter_constraint_range_mode filter_constraint_range_mode_from_string(const char *s) static enum filter_constraint_range_mode filter_constraint_range_mode_from_string(const std::string &s)
{ {
for (const auto &desc: range_mode_descriptions) { for (const auto &desc: range_mode_descriptions) {
if (same_string(desc.token, s)) if (desc.token == s)
return desc.mode; return desc.mode;
} }
report_error("unknown filter constraint range mode: %s", s); report_error("unknown filter constraint range mode: %s", s.c_str());
return FILTER_CONSTRAINT_EQUAL; return FILTER_CONSTRAINT_EQUAL;
} }
@ -551,14 +551,14 @@ filter_constraint::filter_constraint(const filter_constraint &c) :
data.numerical_range = c.data.numerical_range; data.numerical_range = c.data.numerical_range;
} }
filter_constraint::filter_constraint(const char *type_in, const char *string_mode_in, filter_constraint::filter_constraint(const std::string &type_in, const std::string &string_mode_in,
const char *range_mode_in, bool negate_in, const char *s_in) : const std::string &range_mode_in, bool negate_in, const std::string &s_in) :
type(filter_constraint_type_from_string(type_in)), type(filter_constraint_type_from_string(type_in)),
string_mode(FILTER_CONSTRAINT_STARTS_WITH), string_mode(FILTER_CONSTRAINT_STARTS_WITH),
range_mode(FILTER_CONSTRAINT_GREATER), range_mode(FILTER_CONSTRAINT_GREATER),
negate(negate_in) negate(negate_in)
{ {
QString s(s_in); QString s = QString::fromStdString(s_in);
if (filter_constraint_has_string_mode(type)) if (filter_constraint_has_string_mode(type))
string_mode = filter_constraint_string_mode_from_string(string_mode_in); string_mode = filter_constraint_string_mode_from_string(string_mode_in);
if (filter_constraint_has_range_mode(type)) if (filter_constraint_has_range_mode(type))
@ -622,22 +622,22 @@ filter_constraint::~filter_constraint()
delete data.string_list; delete data.string_list;
} }
std::string filter_constraint_data_to_string(const filter_constraint *c) std::string filter_constraint_data_to_string(const filter_constraint &c)
{ {
if (filter_constraint_is_timestamp(c->type)) { if (filter_constraint_is_timestamp(c.type)) {
std::string from_s = format_datetime(c->data.timestamp_range.from); std::string from_s = format_datetime(c.data.timestamp_range.from);
std::string to_s = format_datetime(c->data.timestamp_range.to); std::string to_s = format_datetime(c.data.timestamp_range.to);
return from_s + ',' + to_s; return from_s + ',' + to_s;
} else if (filter_constraint_is_string(c->type)) { } else if (filter_constraint_is_string(c.type)) {
// TODO: this obviously breaks if the strings contain ",". // TODO: this obviously breaks if the strings contain ",".
// That is currently not supported by the UI, but one day we might // That is currently not supported by the UI, but one day we might
// have to escape the strings. // have to escape the strings.
return c->data.string_list->join(",").toStdString(); return c.data.string_list->join(",").toStdString();
} else if (filter_constraint_is_multiple_choice(c->type)) { } else if (filter_constraint_is_multiple_choice(c.type)) {
return std::to_string(c->data.multiple_choice); return std::to_string(c.data.multiple_choice);
} else { } else {
return std::to_string(c->data.numerical_range.from) + ',' + return std::to_string(c.data.numerical_range.from) + ',' +
std::to_string(c->data.numerical_range.to); std::to_string(c.data.numerical_range.to);
} }
} }

View file

@ -78,8 +78,8 @@ struct filter_constraint {
} data; } data;
// For C++, define constructors, assignment operators and destructor to make our lives easier. // For C++, define constructors, assignment operators and destructor to make our lives easier.
filter_constraint(filter_constraint_type type); filter_constraint(filter_constraint_type type);
filter_constraint(const char *type, const char *string_mode, filter_constraint(const std::string &type, const std::string &string_mode,
const char *range_mode, bool negate, const char *data); // from parser data const std::string &range_mode, bool negate, const std::string &data); // from parser data
filter_constraint(const filter_constraint &); filter_constraint(const filter_constraint &);
filter_constraint &operator=(const filter_constraint &); filter_constraint &operator=(const filter_constraint &);
~filter_constraint(); ~filter_constraint();
@ -137,6 +137,6 @@ void filter_constraint_set_timestamp_from(filter_constraint &c, timestamp_t from
void filter_constraint_set_timestamp_to(filter_constraint &c, timestamp_t to); // convert according to current units (metric or imperial) void filter_constraint_set_timestamp_to(filter_constraint &c, timestamp_t to); // convert according to current units (metric or imperial)
void filter_constraint_set_multiple_choice(filter_constraint &c, uint64_t); void filter_constraint_set_multiple_choice(filter_constraint &c, uint64_t);
bool filter_constraint_match_dive(const filter_constraint &c, const struct dive *d); bool filter_constraint_match_dive(const filter_constraint &c, const struct dive *d);
std::string filter_constraint_data_to_string(const struct filter_constraint *constraint); // caller takes ownership of returned string std::string filter_constraint_data_to_string(const struct filter_constraint &constraint); // caller takes ownership of returned string
#endif #endif

View file

@ -4,24 +4,14 @@
#include "qthelper.h" #include "qthelper.h"
#include "subsurface-string.h" #include "subsurface-string.h"
static filter_preset_table &global_table() std::string filter_preset::fulltext_query() const
{ {
return *divelog.filter_presets; return data.fullText.originalQuery.toStdString();
} }
int filter_presets_count() const char *filter_preset::fulltext_mode() const
{ {
return (int)global_table().size(); switch (data.fulltextStringMode) {
}
extern std::string filter_preset_fulltext_query(int preset)
{
return global_table()[preset].data.fullText.originalQuery.toStdString();
}
const char *filter_preset_fulltext_mode(int preset)
{
switch (global_table()[preset].data.fulltextStringMode) {
default: default:
case StringFilterMode::SUBSTRING: case StringFilterMode::SUBSTRING:
return "substring"; return "substring";
@ -32,98 +22,19 @@ const char *filter_preset_fulltext_mode(int preset)
} }
} }
void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode) void filter_preset::set_fulltext(const std::string fulltext, const std::string &fulltext_string_mode)
{ {
if (same_string(fulltext_string_mode, "substring")) if (fulltext_string_mode == "substring")
preset->data.fulltextStringMode = StringFilterMode::SUBSTRING; data.fulltextStringMode = StringFilterMode::SUBSTRING;
else if (same_string(fulltext_string_mode, "startswith")) else if (fulltext_string_mode == "startswith")
preset->data.fulltextStringMode = StringFilterMode::STARTSWITH; data.fulltextStringMode = StringFilterMode::STARTSWITH;
else // if (same_string(fulltext_string_mode, "exact")) else // if (fulltext_string_mode == "exact"))
preset->data.fulltextStringMode = StringFilterMode::EXACT; data.fulltextStringMode = StringFilterMode::EXACT;
preset->data.fullText = fulltext; data.fullText = QString::fromStdString(std::move(fulltext));
} }
int filter_preset_constraint_count(int preset) void filter_preset::add_constraint(const std::string &type, const std::string &string_mode,
const std::string &range_mode, bool negate, const std::string &constraint_data)
{ {
return (int)global_table()[preset].data.constraints.size(); data.constraints.emplace_back(type, string_mode, range_mode, negate, constraint_data);
}
const filter_constraint *filter_preset_constraint(int preset, int constraint)
{
return &global_table()[preset].data.constraints[constraint];
}
void filter_preset_set_name(struct filter_preset *preset, const char *name)
{
preset->name = name;
}
static int filter_preset_add_to_table(const std::string name, const FilterData &d, struct filter_preset_table &table)
{
// std::lower_bound does a binary search - the vector must be sorted.
filter_preset newEntry { name, d };
auto it = std::lower_bound(table.begin(), table.end(), newEntry,
[](const filter_preset &p1, const filter_preset &p2)
{ return p1.name < p2.name; });
it = table.insert(it, newEntry);
return it - table.begin();
}
// Take care that the name doesn't already exist by adding numbers
static std::string get_unique_preset_name(const std::string &orig, const struct filter_preset_table &table)
{
std::string res = orig;
int count = 2;
while (std::find_if(table.begin(), table.end(),
[&res](const filter_preset &preset)
{ return preset.name == res; }) != table.end()) {
res = orig + "#" + std::to_string(count);
++count;
}
return res;
}
void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table)
{
std::string name = get_unique_preset_name(preset->name, *table);
filter_preset_add_to_table(name, preset->data, *table);
}
void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode,
const char *range_mode, bool negate, const char *data)
{
preset->data.constraints.emplace_back(type, string_mode, range_mode, negate, data);
}
int filter_preset_id(const std::string &name)
{
auto it = std::find_if(global_table().begin(), global_table().end(),
[&name] (filter_preset &p) { return p.name == name; });
return it != global_table().end() ? it - global_table().begin() : -1;
}
std::string filter_preset_name(int preset)
{
return global_table()[preset].name;
}
void filter_preset_set(int preset, const FilterData &data)
{
global_table()[preset].data = data;
}
FilterData filter_preset_get(int preset)
{
return global_table()[preset].data;
}
int filter_preset_add(const std::string &nameIn, const FilterData &d)
{
std::string name = get_unique_preset_name(nameIn, global_table());
return filter_preset_add_to_table(name, d, global_table());
}
void filter_preset_delete(int preset)
{
global_table().erase(global_table().begin() + preset);
} }

View file

@ -1,15 +1,8 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
// A list of filter settings with names. Every name is unique, which
// means that saving to an old name will overwrite the old preset.
//
// Even though the filter data itself is a C++/Qt class to simplify
// string manipulation and memory management, the data is accessible
// via pure C functions so that it can be written to the log (git or xml).
#ifndef FILTER_PRESETS_H #ifndef FILTER_PRESETS_H
#define FILTER_PRESETS_H #define FILTER_PRESETS_H
#include "divefilter.h" #include "divefilter.h"
#include <vector>
#include <string> #include <string>
struct dive; struct dive;
@ -19,32 +12,12 @@ struct FilterData;
struct filter_preset { struct filter_preset {
std::string name; std::string name;
FilterData data; FilterData data;
};
// Subclassing standard library containers is generally std::string fulltext_query() const; // Fulltext query of filter preset.
// not recommended. However, this makes interaction with const char *fulltext_mode() const; // String mode of fulltext query. Ownership is *not* passed to caller.
// C-code easier and since we don't add any member functions, void set_fulltext(const std::string fulltext, const std::string &fulltext_string_mode); // First argument is consumed.
// this is not a problem. void add_constraint(const std::string &type, const std::string &string_mode,
struct filter_preset_table : public std::vector<filter_preset> const std::string &range_mode, bool negate, const std::string &data); // called by the parser, therefore data passed as strings.
{
}; };
// The C IO code accesses the filter presets via integer indices.
extern int filter_presets_count();
extern const char *filter_preset_fulltext_mode(int preset); // string mode of fulltext query. ownership is *not* passed to caller.
extern int filter_preset_constraint_count(int preset); // number of constraints in the filter preset.
extern const struct filter_constraint *filter_preset_constraint(int preset, int constraint); // get constraint. ownership is *not* passed to caller.
extern void filter_preset_set_name(struct filter_preset *preset, const char *name);
extern void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode);
extern void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table);
extern void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode,
const char *range_mode, bool negate, const char *data); // called by the parser, therefore data passed as strings.
int filter_preset_id(const std::string &s); // for now, we assume that names are unique. returns -1 if no preset with that name.
void filter_preset_set(int preset, const FilterData &d); // this will override a preset if the name already exists.
FilterData filter_preset_get(int preset);
int filter_preset_add(const std::string &name, const FilterData &d); // returns point of insertion
void filter_preset_delete(int preset);
std::string filter_preset_name(int preset); // name of filter preset - caller must free the result.
std::string filter_preset_fulltext_query(int preset); // fulltext query of filter preset - caller must free the result.
#endif #endif

View file

@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0
#include "filterpresettable.h"
#include "filterpreset.h"
#include <algorithm>
int filter_preset_table::preset_id(const std::string &name) const
{
auto it = std::find_if(begin(), end(), [&name] (const filter_preset &p) { return p.name == name; });
return it != end() ? it - begin() : -1;
}
// Take care that the name doesn't already exist by adding numbers
static std::string get_unique_preset_name(const std::string &orig, const struct filter_preset_table &table)
{
std::string res = orig;
int count = 2;
while (std::find_if(table.begin(), table.end(),
[&res](const filter_preset &preset)
{ return preset.name == res; }) != table.end()) {
res = orig + "#" + std::to_string(count);
++count;
}
return res;
}
static int filter_preset_add_to_table(const std::string name, const FilterData &d, struct filter_preset_table &table)
{
// std::lower_bound does a binary search - the vector must be sorted.
filter_preset newEntry { name, d };
auto it = std::lower_bound(table.begin(), table.end(), newEntry,
[](const filter_preset &p1, const filter_preset &p2)
{ return p1.name < p2.name; });
it = table.insert(it, newEntry);
return it - table.begin();
}
void filter_preset_table::add(const filter_preset &preset)
{
std::string name = get_unique_preset_name(preset.name, *this);
filter_preset_add_to_table(name, preset.data, *this);
}
int filter_preset_table::add(const std::string &nameIn, const FilterData &d)
{
std::string name = get_unique_preset_name(nameIn, *this);
return filter_preset_add_to_table(name, d, *this);
}
void filter_preset_table::remove(int preset)
{
erase(begin() + preset);
}

25
core/filterpresettable.h Normal file
View file

@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0
// A list of filter settings with names. Every name is unique, which
// means that saving to an old name will overwrite the old preset.
#ifndef FILTER_PRESETSTABLE_H
#define FILTER_PRESETSTABLE_H
#include <string>
#include <vector>
struct filter_preset;
struct FilterData;
// Subclassing standard library containers is generally
// not recommended. However, this makes interaction with
// C-code easier and since we don't add any member functions,
// this is not a problem.
struct filter_preset_table : public std::vector<filter_preset>
{
void add(const filter_preset &preset);
int add(const std::string &name, const FilterData &d); // returns point of insertion
void remove(int iox);
int preset_id(const std::string &s) const; // for now, we assume that names are unique. returns -1 if no preset with that name
};
#endif

View file

@ -1183,10 +1183,10 @@ static void parse_filter_preset_constraint(char *line, struct git_parser_state *
line = parse_keyvalue_entry(parse_filter_preset_constraint_keyvalue, state, line, state); line = parse_keyvalue_entry(parse_filter_preset_constraint_keyvalue, state, line, state);
} }
filter_preset_add_constraint(state->active_filter.get(), state->filter_constraint_type.c_str(), state->active_filter->add_constraint(state->filter_constraint_type,
state->filter_constraint_string_mode.c_str(), state->filter_constraint_string_mode,
state->filter_constraint_range_mode.c_str(), state->filter_constraint_range_mode,
state->filter_constraint_negate, state->filter_constraint_data.c_str()); state->filter_constraint_negate, state->filter_constraint_data);
state->filter_constraint_type.clear(); state->filter_constraint_type.clear();
state->filter_constraint_string_mode.clear(); state->filter_constraint_string_mode.clear();
state->filter_constraint_range_mode.clear(); state->filter_constraint_range_mode.clear();
@ -1220,14 +1220,14 @@ static void parse_filter_preset_fulltext(char *line, struct git_parser_state *st
line = parse_keyvalue_entry(parse_filter_preset_fulltext_keyvalue, state, line, state); line = parse_keyvalue_entry(parse_filter_preset_fulltext_keyvalue, state, line, state);
} }
filter_preset_set_fulltext(state->active_filter.get(), state->fulltext_query.c_str(), state->fulltext_mode.c_str()); state->active_filter.get()->set_fulltext(std::move(state->fulltext_query), state->fulltext_mode);
state->fulltext_mode.clear(); state->fulltext_mode.clear();
state->fulltext_query.clear(); state->fulltext_query.clear();
} }
static void parse_filter_preset_name(char *, struct git_parser_state *state) static void parse_filter_preset_name(char *, struct git_parser_state *state)
{ {
filter_preset_set_name(state->active_filter.get(), get_first_converted_string_c(state)); state->active_filter->name = get_first_converted_string(state);
} }
/* These need to be sorted! */ /* These need to be sorted! */
@ -1774,7 +1774,7 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en
git_blob_free(blob); git_blob_free(blob);
add_filter_preset_to_table(state->active_filter.get(), state->log->filter_presets.get()); state->log->filter_presets.add(*state->active_filter);
state->active_filter.reset(); state->active_filter.reset();
return 0; return 0;

View file

@ -1440,11 +1440,8 @@ static void try_to_fill_filter(struct filter_preset *filter, const char *name, c
{ {
start_match("filterpreset", name, buf); start_match("filterpreset", name, buf);
std::string s; if (MATCH("name", utf8_string_std, &filter->name))
if (MATCH("name", utf8_string_std, &s)) {
filter_preset_set_name(filter, s.c_str());
return; return;
}
nonmatch("filterpreset", name, buf); nonmatch("filterpreset", name, buf);
} }

View file

@ -202,7 +202,7 @@ void filter_preset_start(struct parser_state *state)
void filter_preset_end(struct parser_state *state) void filter_preset_end(struct parser_state *state)
{ {
add_filter_preset_to_table(state->cur_filter.get(), state->log->filter_presets.get()); state->log->filter_presets.add(*state->cur_filter);
state->cur_filter.reset(); state->cur_filter.reset();
} }
@ -217,7 +217,7 @@ void fulltext_end(struct parser_state *state)
{ {
if (!state->in_fulltext) if (!state->in_fulltext)
return; return;
filter_preset_set_fulltext(state->cur_filter.get(), state->fulltext.c_str(), state->fulltext_string_mode.c_str()); state->cur_filter->set_fulltext(std::move(state->fulltext), state->fulltext_string_mode);
state->fulltext.clear(); state->fulltext.clear();
state->fulltext_string_mode.clear(); state->fulltext_string_mode.clear();
state->in_fulltext = false; state->in_fulltext = false;
@ -234,8 +234,8 @@ void filter_constraint_end(struct parser_state *state)
{ {
if (!state->in_filter_constraint) if (!state->in_filter_constraint)
return; return;
filter_preset_add_constraint(state->cur_filter.get(), state->filter_constraint_type.c_str(), state->filter_constraint_string_mode.c_str(), state->cur_filter->add_constraint(state->filter_constraint_type, state->filter_constraint_string_mode,
state->filter_constraint_range_mode.c_str(), state->filter_constraint_negate, state->filter_constraint.c_str()); state->filter_constraint_range_mode, state->filter_constraint_negate, state->filter_constraint);
state->filter_constraint_type.clear(); state->filter_constraint_type.clear();
state->filter_constraint_string_mode.clear(); state->filter_constraint_string_mode.clear();

View file

@ -33,6 +33,7 @@
#include "version.h" #include "version.h"
#include "picture.h" #include "picture.h"
#include "qthelper.h" #include "qthelper.h"
#include "range.h"
#include "gettext.h" #include "gettext.h"
#include "tag.h" #include "tag.h"
#include "subsurface-time.h" #include "subsurface-time.h"
@ -914,21 +915,20 @@ static void save_divesites(git_repository *repo, struct dir *tree)
* Whether stringmode or rangemode exist depends on the type of the constraint. * Whether stringmode or rangemode exist depends on the type of the constraint.
* Any constraint can be negated. * Any constraint can be negated.
*/ */
static void format_one_filter_constraint(int preset_id, int constraint_id, struct membuffer *b) static void format_one_filter_constraint(const filter_constraint &constraint, struct membuffer *b)
{ {
const struct filter_constraint *constraint = filter_preset_constraint(preset_id, constraint_id); const char *type = filter_constraint_type_to_string(constraint.type);
const char *type = filter_constraint_type_to_string(constraint->type);
show_utf8(b, "constraint type=", type, ""); show_utf8(b, "constraint type=", type, "");
if (filter_constraint_has_string_mode(constraint->type)) { if (filter_constraint_has_string_mode(constraint.type)) {
const char *mode = filter_constraint_string_mode_to_string(constraint->string_mode); const char *mode = filter_constraint_string_mode_to_string(constraint.string_mode);
show_utf8(b, " stringmode=", mode, ""); show_utf8(b, " stringmode=", mode, "");
} }
if (filter_constraint_has_range_mode(constraint->type)) { if (filter_constraint_has_range_mode(constraint.type)) {
const char *mode = filter_constraint_range_mode_to_string(constraint->range_mode); const char *mode = filter_constraint_range_mode_to_string(constraint.range_mode);
show_utf8(b, " rangemode=", mode, ""); show_utf8(b, " rangemode=", mode, "");
} }
if (constraint->negate) if (constraint.negate)
put_format(b, " negate"); put_format(b, " negate");
std::string data = filter_constraint_data_to_string(constraint); std::string data = filter_constraint_data_to_string(constraint);
show_utf8(b, " data=", data.c_str(), "\n"); show_utf8(b, " data=", data.c_str(), "\n");
@ -943,19 +943,18 @@ static void format_one_filter_constraint(int preset_id, int constraint_id, struc
* fulltext mode "fulltext mode" query "the query as entered by the user" * fulltext mode "fulltext mode" query "the query as entered by the user"
* The format of the "constraint" entry is described in the format_one_filter_constraint() function. * The format of the "constraint" entry is described in the format_one_filter_constraint() function.
*/ */
static void format_one_filter_preset(int preset_id, struct membuffer *b) static void format_one_filter_preset(const filter_preset &preset, struct membuffer *b)
{ {
std::string name = filter_preset_name(preset_id); show_utf8(b, "name ", preset.name.c_str(), "\n");
show_utf8(b, "name ", name.c_str(), "\n");
std::string fulltext = filter_preset_fulltext_query(preset_id); std::string fulltext = preset.fulltext_query();
if (!fulltext.empty()) { if (!fulltext.empty()) {
show_utf8(b, "fulltext mode=", filter_preset_fulltext_mode(preset_id), ""); show_utf8(b, "fulltext mode=", preset.fulltext_mode(), "");
show_utf8(b, " query=", fulltext.c_str(), "\n"); show_utf8(b, " query=", fulltext.c_str(), "\n");
} }
for (int i = 0; i < filter_preset_constraint_count(preset_id); i++) for (auto &constraint: preset.data.constraints)
format_one_filter_constraint(preset_id, i, b); format_one_filter_constraint(constraint, b);
} }
static void save_filter_presets(git_repository *repo, struct dir *tree) static void save_filter_presets(git_repository *repo, struct dir *tree)
@ -965,13 +964,13 @@ static void save_filter_presets(git_repository *repo, struct dir *tree)
put_format(&dirname, "02-Filterpresets"); put_format(&dirname, "02-Filterpresets");
filter_dir = new_directory(repo, tree, &dirname); filter_dir = new_directory(repo, tree, &dirname);
for (int i = 0; i < filter_presets_count(); i++) for (auto [i, filter_preset]: enumerated_range(divelog.filter_presets))
{ {
membuffer preset_name; membuffer preset_name;
membuffer preset_buffer; membuffer preset_buffer;
put_format(&preset_name, "Preset-%03d", i); put_format(&preset_name, "Preset-%03d", i);
format_one_filter_preset(i, &preset_buffer); format_one_filter_preset(filter_preset, &preset_buffer);
blob_insert(repo, filter_dir, &preset_buffer, mb_cstring(&preset_name)); blob_insert(repo, filter_dir, &preset_buffer, mb_cstring(&preset_name));
} }

View file

@ -591,38 +591,34 @@ int save_dives(const char *filename)
static void save_filter_presets(struct membuffer *b) static void save_filter_presets(struct membuffer *b)
{ {
int i; if (divelog.filter_presets.empty())
if (filter_presets_count() <= 0)
return; return;
put_format(b, "<filterpresets>\n"); put_format(b, "<filterpresets>\n");
for (i = 0; i < filter_presets_count(); i++) { for (auto &filter_preset: divelog.filter_presets) {
std::string name = filter_preset_name(i);
put_format(b, " <filterpreset"); put_format(b, " <filterpreset");
show_utf8(b, name.c_str(), " name='", "'", 1); show_utf8(b, filter_preset.name.c_str(), " name='", "'", 1);
put_format(b, ">\n"); put_format(b, ">\n");
std::string fulltext = filter_preset_fulltext_query(i); std::string fulltext = filter_preset.fulltext_query();
if (!fulltext.empty()) { if (!fulltext.empty()) {
const char *fulltext_mode = filter_preset_fulltext_mode(i); const char *fulltext_mode = filter_preset.fulltext_mode();
show_utf8(b, fulltext_mode, " <fulltext mode='", "'>", 1); show_utf8(b, fulltext_mode, " <fulltext mode='", "'>", 1);
show_utf8(b, fulltext.c_str(), "", "</fulltext>\n", 0); show_utf8(b, fulltext.c_str(), "", "</fulltext>\n", 0);
} }
for (int j = 0; j < filter_preset_constraint_count(i); j++) { for (auto &constraint: filter_preset.data.constraints) {
const struct filter_constraint *constraint = filter_preset_constraint(i, j); const char *type = filter_constraint_type_to_string(constraint.type);
const char *type = filter_constraint_type_to_string(constraint->type);
put_format(b, " <constraint"); put_format(b, " <constraint");
show_utf8(b, type, " type='", "'", 1); show_utf8(b, type, " type='", "'", 1);
if (filter_constraint_has_string_mode(constraint->type)) { if (filter_constraint_has_string_mode(constraint.type)) {
const char *mode = filter_constraint_string_mode_to_string(constraint->string_mode); const char *mode = filter_constraint_string_mode_to_string(constraint.string_mode);
show_utf8(b, mode, " string_mode='", "'", 1); show_utf8(b, mode, " string_mode='", "'", 1);
} }
if (filter_constraint_has_range_mode(constraint->type)) { if (filter_constraint_has_range_mode(constraint.type)) {
const char *mode = filter_constraint_range_mode_to_string(constraint->range_mode); const char *mode = filter_constraint_range_mode_to_string(constraint.range_mode);
show_utf8(b, mode, " range_mode='", "'", 1); show_utf8(b, mode, " range_mode='", "'", 1);
} }
if (constraint->negate) if (constraint.negate)
put_format(b, " negate='1'"); put_format(b, " negate='1'");
put_format(b, ">"); put_format(b, ">");
std::string data = filter_constraint_data_to_string(constraint); std::string data = filter_constraint_data_to_string(constraint);

View file

@ -97,7 +97,7 @@ void FilterWidget::selectPreset(int i)
void FilterWidget::loadPreset(int index) void FilterWidget::loadPreset(int index)
{ {
ignoreSignal = true; // When reloading the filter UI, we get numerous constraintChanged signals. Ignore them. ignoreSignal = true; // When reloading the filter UI, we get numerous constraintChanged signals. Ignore them.
FilterData filter = filter_preset_get(index); FilterData filter = divelog.filter_presets[index].data;
setFilterData(filter); setFilterData(filter);
ignoreSignal = false; ignoreSignal = false;
presetModified = false; presetModified = false;
@ -227,7 +227,8 @@ void FilterWidget::updatePresetLabel()
int presetId = selectedPreset(); int presetId = selectedPreset();
QString text; QString text;
if (presetId >= 0) { if (presetId >= 0) {
text = QString(filter_preset_name(presetId).c_str()); const std::string &name = divelog.filter_presets[presetId].name;
text = QString::fromStdString(name);
if (presetModified) if (presetModified)
text += " (" + tr("modified") + ")"; text += " (" + tr("modified") + ")";
} }
@ -240,13 +241,15 @@ void FilterWidget::on_addSetButton_clicked()
// Thus, if the user selects an item and modify the filter, // Thus, if the user selects an item and modify the filter,
// they can simply overwrite the preset. // they can simply overwrite the preset.
int presetId = selectedPreset(); int presetId = selectedPreset();
QString selectedPreset = presetId >= 0 ? QString(filter_preset_name(presetId).c_str()) : QString(); QString selectedPreset = presetId >= 0 ?
QString::fromStdString(divelog.filter_presets[presetId].name) :
QString();
AddFilterPresetDialog dialog(selectedPreset, this); AddFilterPresetDialog dialog(selectedPreset, this);
QString name = dialog.doit(); QString name = dialog.doit();
if (name.isEmpty()) if (name.isEmpty())
return; return;
int idx = filter_preset_id(name.toStdString()); int idx = divelog.filter_presets.preset_id(name.toStdString());
if (idx >= 0) if (idx >= 0)
Command::editFilterPreset(idx, createFilterData()); Command::editFilterPreset(idx, createFilterData());
else else

View file

@ -374,10 +374,9 @@ AddFilterPresetDialog::AddFilterPresetDialog(const QString &defaultName, QWidget
// Create a completer so that the user can easily overwrite existing presets. // Create a completer so that the user can easily overwrite existing presets.
QStringList presets; QStringList presets;
int count = filter_presets_count(); presets.reserve(divelog.filter_presets.size());
presets.reserve(count); for (auto &filter_preset: divelog.filter_presets)
for (int i = 0; i < count; ++i) presets.push_back(QString::fromStdString(filter_preset.name));
presets.push_back(QString(filter_preset_name(i).c_str()));
QCompleter *completer = new QCompleter(presets, this); QCompleter *completer = new QCompleter(presets, this);
completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setCaseSensitivity(Qt::CaseInsensitive);
ui.name->setCompleter(completer); ui.name->setCompleter(completer);
@ -387,7 +386,7 @@ void AddFilterPresetDialog::nameChanged(const QString &text)
{ {
QString trimmed = text.trimmed(); QString trimmed = text.trimmed();
bool isEmpty = trimmed.isEmpty(); bool isEmpty = trimmed.isEmpty();
bool exists = !isEmpty && filter_preset_id(trimmed.toStdString()) >= 0; bool exists = !isEmpty && divelog.filter_presets.preset_id(trimmed.toStdString()) >= 0;
ui.duplicateWarning->setVisible(exists); ui.duplicateWarning->setVisible(exists);
ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!isEmpty); ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!isEmpty);
} }

View file

@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include "filterpresetmodel.h" #include "filterpresetmodel.h"
#include "core/divelog.h"
#include "core/filterconstraint.h" #include "core/filterconstraint.h"
#include "core/filterpreset.h" #include "core/filterpreset.h"
#include "core/filterpresettable.h"
#include "core/qthelper.h" #include "core/qthelper.h"
#include "core/subsurface-qt/divelistnotifier.h" #include "core/subsurface-qt/divelistnotifier.h"
@ -26,13 +28,14 @@ FilterPresetModel *FilterPresetModel::instance()
QVariant FilterPresetModel::data(const QModelIndex &index, int role) const QVariant FilterPresetModel::data(const QModelIndex &index, int role) const
{ {
if (!index.isValid() || index.row() >= filter_presets_count()) if (index.row() < 0 || static_cast<size_t>(index.row()) >= divelog.filter_presets.size())
return QVariant(); return QVariant();
const auto &filter_preset = divelog.filter_presets[index.row()];
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole:
if (index.column() == NAME) if (index.column() == NAME)
return QString(filter_preset_name(index.row()).c_str()); return QString::fromStdString(filter_preset.name);
break; break;
case Qt::DecorationRole: case Qt::DecorationRole:
if (index.column() == REMOVE) if (index.column() == REMOVE)
@ -52,7 +55,7 @@ QVariant FilterPresetModel::data(const QModelIndex &index, int role) const
int FilterPresetModel::rowCount(const QModelIndex &parent) const int FilterPresetModel::rowCount(const QModelIndex &parent) const
{ {
return filter_presets_count(); return static_cast<int>(divelog.filter_presets.size());
} }
void FilterPresetModel::reset() void FilterPresetModel::reset()