mirror of
				https://github.com/subsurface/subsurface.git
				synced 2025-02-19 22:16:15 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/bstoeger-range'
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
		
						commit
						46365b3199
					
				
					 13 changed files with 175 additions and 167 deletions
				
			
		|  | @ -206,6 +206,7 @@ HEADERS += \ | ||||||
| 	core/pref.h \ | 	core/pref.h \ | ||||||
| 	core/profile.h \ | 	core/profile.h \ | ||||||
| 	core/qthelper.h \ | 	core/qthelper.h \ | ||||||
|  | 	core/range.h \ | ||||||
| 	core/save-html.h \ | 	core/save-html.h \ | ||||||
| 	core/statistics.h \ | 	core/statistics.h \ | ||||||
| 	core/units.h \ | 	core/units.h \ | ||||||
|  |  | ||||||
|  | @ -147,6 +147,7 @@ set(SUBSURFACE_CORE_LIB_SRCS | ||||||
| 	qt-init.cpp | 	qt-init.cpp | ||||||
| 	qthelper.cpp | 	qthelper.cpp | ||||||
| 	qthelper.h | 	qthelper.h | ||||||
|  | 	range.h | ||||||
| 	sample.c | 	sample.c | ||||||
| 	sample.h | 	sample.h | ||||||
| 	save-git.c | 	save-git.c | ||||||
|  |  | ||||||
|  | @ -104,26 +104,11 @@ static const char *negate_description[2] { | ||||||
| 	QT_TRANSLATE_NOOP("gettextFromC", "is not"), | 	QT_TRANSLATE_NOOP("gettextFromC", "is not"), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static constexpr size_t type_descriptions_count() |  | ||||||
| { |  | ||||||
| 	return std::size(type_descriptions); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static constexpr size_t string_mode_descriptions_count() |  | ||||||
| { |  | ||||||
| 	return std::size(string_mode_descriptions); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static constexpr size_t range_mode_descriptions_count() |  | ||||||
| { |  | ||||||
| 	return std::size(range_mode_descriptions); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const type_description *get_type_description(enum filter_constraint_type type) | static const type_description *get_type_description(enum filter_constraint_type type) | ||||||
| { | { | ||||||
| 	for (size_t i = 0; i < type_descriptions_count(); ++i) { | 	for (const auto &desc: type_descriptions) { | ||||||
| 		if (type_descriptions[i].type == type) | 		if (desc.type == type) | ||||||
| 			return &type_descriptions[i]; | 			return &desc; | ||||||
| 	} | 	} | ||||||
| 	report_error("unknown filter constraint type: %d", type); | 	report_error("unknown filter constraint type: %d", type); | ||||||
| 	return nullptr; | 	return nullptr; | ||||||
|  | @ -131,9 +116,9 @@ static const type_description *get_type_description(enum filter_constraint_type | ||||||
| 
 | 
 | ||||||
| static const string_mode_description *get_string_mode_description(enum filter_constraint_string_mode mode) | static const string_mode_description *get_string_mode_description(enum filter_constraint_string_mode mode) | ||||||
| { | { | ||||||
| 	for (size_t i = 0; i < string_mode_descriptions_count(); ++i) { | 	for (const auto &desc: string_mode_descriptions) { | ||||||
| 		if (string_mode_descriptions[i].mode == mode) | 		if (desc.mode == mode) | ||||||
| 			return &string_mode_descriptions[i]; | 			return &desc; | ||||||
| 	} | 	} | ||||||
| 	report_error("unknown filter constraint string mode: %d", mode); | 	report_error("unknown filter constraint string mode: %d", mode); | ||||||
| 	return nullptr; | 	return nullptr; | ||||||
|  | @ -141,9 +126,9 @@ static const string_mode_description *get_string_mode_description(enum filter_co | ||||||
| 
 | 
 | ||||||
| static const range_mode_description *get_range_mode_description(enum filter_constraint_range_mode mode) | static const range_mode_description *get_range_mode_description(enum filter_constraint_range_mode mode) | ||||||
| { | { | ||||||
| 	for (size_t i = 0; i < range_mode_descriptions_count(); ++i) { | 	for (const auto &desc: range_mode_descriptions) { | ||||||
| 		if (range_mode_descriptions[i].mode == mode) | 		if (desc.mode == mode) | ||||||
| 			return &range_mode_descriptions[i]; | 			return &desc; | ||||||
| 	} | 	} | ||||||
| 	report_error("unknown filter constraint range mode: %d", mode); | 	report_error("unknown filter constraint range mode: %d", mode); | ||||||
| 	return nullptr; | 	return nullptr; | ||||||
|  | @ -151,9 +136,9 @@ static const range_mode_description *get_range_mode_description(enum filter_cons | ||||||
| 
 | 
 | ||||||
| static enum filter_constraint_type filter_constraint_type_from_string(const char *s) | static enum filter_constraint_type filter_constraint_type_from_string(const char *s) | ||||||
| { | { | ||||||
| 	for (size_t i = 0; i < type_descriptions_count(); ++i) { | 	for (const auto &desc: type_descriptions) { | ||||||
| 		if (same_string(type_descriptions[i].token, s)) | 		if (same_string(desc.token, s)) | ||||||
| 			return type_descriptions[i].type; | 			return desc.type; | ||||||
| 	} | 	} | ||||||
| 	report_error("unknown filter constraint type: %s", s); | 	report_error("unknown filter constraint type: %s", s); | ||||||
| 	return FILTER_CONSTRAINT_DATE; | 	return FILTER_CONSTRAINT_DATE; | ||||||
|  | @ -161,9 +146,9 @@ static enum filter_constraint_type filter_constraint_type_from_string(const char | ||||||
| 
 | 
 | ||||||
| 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 char *s) | ||||||
| { | { | ||||||
| 	for (size_t i = 0; i < string_mode_descriptions_count(); ++i) { | 	for (const auto &desc: string_mode_descriptions) { | ||||||
| 		if (same_string(string_mode_descriptions[i].token, s)) | 		if (same_string(desc.token, s)) | ||||||
| 			return string_mode_descriptions[i].mode; | 			return desc.mode; | ||||||
| 	} | 	} | ||||||
| 	report_error("unknown filter constraint string mode: %s", s); | 	report_error("unknown filter constraint string mode: %s", s); | ||||||
| 	return FILTER_CONSTRAINT_EXACT; | 	return FILTER_CONSTRAINT_EXACT; | ||||||
|  | @ -171,9 +156,9 @@ static enum filter_constraint_string_mode filter_constraint_string_mode_from_str | ||||||
| 
 | 
 | ||||||
| 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 char *s) | ||||||
| { | { | ||||||
| 	for (size_t i = 0; i < range_mode_descriptions_count(); ++i) { | 	for (const auto &desc: range_mode_descriptions) { | ||||||
| 		if (same_string(range_mode_descriptions[i].token, s)) | 		if (same_string(desc.token, s)) | ||||||
| 			return range_mode_descriptions[i].mode; | 			return desc.mode; | ||||||
| 	} | 	} | ||||||
| 	report_error("unknown filter constraint range mode: %s", s); | 	report_error("unknown filter constraint range mode: %s", s); | ||||||
| 	return FILTER_CONSTRAINT_EQUAL; | 	return FILTER_CONSTRAINT_EQUAL; | ||||||
|  | @ -217,21 +202,21 @@ extern "C" int filter_constraint_range_mode_to_index(enum filter_constraint_rang | ||||||
| 
 | 
 | ||||||
| extern "C" enum filter_constraint_type filter_constraint_type_from_index(int index) | extern "C" enum filter_constraint_type filter_constraint_type_from_index(int index) | ||||||
| { | { | ||||||
| 	if (index >= 0 && index < (int)type_descriptions_count()) | 	if (index >= 0 && index < (int)std::size(type_descriptions)) | ||||||
| 		return type_descriptions[index].type; | 		return type_descriptions[index].type; | ||||||
| 	return (enum filter_constraint_type)-1; | 	return (enum filter_constraint_type)-1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern "C" enum filter_constraint_string_mode filter_constraint_string_mode_from_index(int index) | extern "C" enum filter_constraint_string_mode filter_constraint_string_mode_from_index(int index) | ||||||
| { | { | ||||||
| 	if (index >= 0 && index < (int)string_mode_descriptions_count()) | 	if (index >= 0 && index < (int)std::size(string_mode_descriptions)) | ||||||
| 		return string_mode_descriptions[index].mode; | 		return string_mode_descriptions[index].mode; | ||||||
| 	return (enum filter_constraint_string_mode)-1; | 	return (enum filter_constraint_string_mode)-1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extern "C" enum filter_constraint_range_mode filter_constraint_range_mode_from_index(int index) | extern "C" enum filter_constraint_range_mode filter_constraint_range_mode_from_index(int index) | ||||||
| { | { | ||||||
| 	if (index >= 0 && index < (int)range_mode_descriptions_count()) | 	if (index >= 0 && index < (int)std::size(range_mode_descriptions)) | ||||||
| 		return range_mode_descriptions[index].mode; | 		return range_mode_descriptions[index].mode; | ||||||
| 	return (enum filter_constraint_range_mode)-1; | 	return (enum filter_constraint_range_mode)-1; | ||||||
| } | } | ||||||
|  | @ -347,32 +332,32 @@ static double base_to_display_unit(int i, enum filter_constraint_type type) | ||||||
| QStringList filter_constraint_type_list_translated() | QStringList filter_constraint_type_list_translated() | ||||||
| { | { | ||||||
| 	QStringList res; | 	QStringList res; | ||||||
| 	for (size_t i = 0; i < type_descriptions_count(); ++i) | 	for (const auto &desc: type_descriptions) | ||||||
| 		res.push_back(gettextFromC::tr(type_descriptions[i].text_ui)); | 		res.push_back(gettextFromC::tr(desc.text_ui)); | ||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QStringList filter_constraint_string_mode_list_translated() | QStringList filter_constraint_string_mode_list_translated() | ||||||
| { | { | ||||||
| 	QStringList res; | 	QStringList res; | ||||||
| 	for (size_t i = 0; i < string_mode_descriptions_count(); ++i) | 	for (const auto &desc: string_mode_descriptions) | ||||||
| 		res.push_back(gettextFromC::tr(string_mode_descriptions[i].text_ui)); | 		res.push_back(gettextFromC::tr(desc.text_ui)); | ||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QStringList filter_constraint_range_mode_list_translated() | QStringList filter_constraint_range_mode_list_translated() | ||||||
| { | { | ||||||
| 	QStringList res; | 	QStringList res; | ||||||
| 	for (size_t i = 0; i < range_mode_descriptions_count(); ++i) | 	for (const auto &desc: range_mode_descriptions) | ||||||
| 		res.push_back(gettextFromC::tr(range_mode_descriptions[i].text_ui)); | 		res.push_back(gettextFromC::tr(desc.text_ui)); | ||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QStringList filter_constraint_range_mode_list_translated_date() | QStringList filter_constraint_range_mode_list_translated_date() | ||||||
| { | { | ||||||
| 	QStringList res; | 	QStringList res; | ||||||
| 	for (size_t i = 0; i < range_mode_descriptions_count(); ++i) | 	for (const auto &desc: range_mode_descriptions) | ||||||
| 		res.push_back(gettextFromC::tr(range_mode_descriptions[i].text_ui_date)); | 		res.push_back(gettextFromC::tr(desc.text_ui_date)); | ||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -92,7 +92,7 @@ int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_ | ||||||
| 	return cylinder_idx; | 	return cylinder_idx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int get_gasidx(struct dive *dive, struct gasmix mix) | static int get_gasidx(struct dive *dive, struct gasmix mix) | ||||||
| { | { | ||||||
| 	return find_best_gasmix_match(mix, &dive->cylinders); | 	return find_best_gasmix_match(mix, &dive->cylinders); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -42,22 +42,13 @@ extern "C" { | ||||||
| 
 | 
 | ||||||
| extern int validate_gas(const char *text, struct gasmix *gas); | extern int validate_gas(const char *text, struct gasmix *gas); | ||||||
| extern int validate_po2(const char *text, int *mbar_po2); | extern int validate_po2(const char *text, int *mbar_po2); | ||||||
| extern timestamp_t current_time_notz(void); |  | ||||||
| extern void set_last_stop(bool last_stop_6m); |  | ||||||
| extern void set_verbatim(bool verbatim); |  | ||||||
| extern void set_display_runtime(bool display); |  | ||||||
| extern void set_display_duration(bool display); |  | ||||||
| extern void set_display_transitions(bool display); |  | ||||||
| extern int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_t time); | extern int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_t time); | ||||||
| extern int get_gasidx(struct dive *dive, struct gasmix mix); |  | ||||||
| extern bool diveplan_empty(struct diveplan *diveplan); | extern bool diveplan_empty(struct diveplan *diveplan); | ||||||
| extern void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, int error); | extern void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, int error); | ||||||
| extern const char *get_planner_disclaimer(); | extern const char *get_planner_disclaimer(); | ||||||
| extern char *get_planner_disclaimer_formatted(); | extern char *get_planner_disclaimer_formatted(); | ||||||
| 
 | 
 | ||||||
| extern void free_dps(struct diveplan *diveplan); | extern void free_dps(struct diveplan *diveplan); | ||||||
| extern struct dive *planned_dive; |  | ||||||
| extern char *cache_data; |  | ||||||
| 
 | 
 | ||||||
| struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode); | struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode); | ||||||
| struct divedatapoint *create_dp(int time_incr, int depth, int cylinderid, int po2); | struct divedatapoint *create_dp(int time_incr, int depth, int cylinderid, int po2); | ||||||
|  |  | ||||||
|  | @ -36,7 +36,6 @@ | ||||||
| #include <QApplication> | #include <QApplication> | ||||||
| #include <QTextDocument> | #include <QTextDocument> | ||||||
| #include <QPainter> | #include <QPainter> | ||||||
| #include <QProgressDialog>	// TODO: remove with convertThumbnails()
 |  | ||||||
| #include <QSvgRenderer> | #include <QSvgRenderer> | ||||||
| #include <cstdarg> | #include <cstdarg> | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
|  | @ -676,7 +675,7 @@ QString get_water_type_string(int salinity) | ||||||
| QStringList getWaterTypesAsString() | QStringList getWaterTypesAsString() | ||||||
| { | { | ||||||
| 	QStringList res; | 	QStringList res; | ||||||
| 	res.reserve(std::end(waterTypes) - std::begin(waterTypes)); // Waiting for C++17's std::size()
 | 	res.reserve(std::size(waterTypes)); | ||||||
| 	for (const char *t: waterTypes) | 	for (const char *t: waterTypes) | ||||||
| 		res.push_back(gettextFromC::tr(t)); | 		res.push_back(gettextFromC::tr(t)); | ||||||
| 	return res; | 	return res; | ||||||
|  | @ -719,17 +718,17 @@ static const char *printing_templates = "printing_templates"; | ||||||
| 
 | 
 | ||||||
| QString getPrintingTemplatePathUser() | QString getPrintingTemplatePathUser() | ||||||
| { | { | ||||||
| 	static QString path = QString(); | 	// Function-local statics are initialized on first invocation
 | ||||||
| 	if (path.isEmpty()) | 	static QString path(QString(system_default_directory()) + | ||||||
| 		path = QString(system_default_directory()) + QDir::separator() + QString(printing_templates); | 			    QDir::separator() + | ||||||
|  | 			    QString(printing_templates)); | ||||||
| 	return path; | 	return path; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QString getPrintingTemplatePathBundle() | QString getPrintingTemplatePathBundle() | ||||||
| { | { | ||||||
| 	static QString path = QString(); | 	// Function-local statics are initialized on first invocation
 | ||||||
| 	if (path.isEmpty()) | 	static QString path(getSubsurfaceDataPath(printing_templates)); | ||||||
| 		path = getSubsurfaceDataPath(printing_templates); |  | ||||||
| 	return path; | 	return path; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1072,45 +1071,6 @@ extern "C" char *hashfile_name_string() | ||||||
| 	return copy_qstring(hashfile_name()); | 	return copy_qstring(hashfile_name()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // During a transition period, convert old thumbnail-hashes to individual files
 |  | ||||||
| // TODO: remove this code in due course
 |  | ||||||
| static void convertThumbnails(const QHash <QString, QImage> &thumbnails) |  | ||||||
| { |  | ||||||
| 	if (thumbnails.empty()) |  | ||||||
| 		return; |  | ||||||
| 	// This is a singular occurrence, therefore translating the strings seems not worth it
 |  | ||||||
| 	QProgressDialog progress("Convert thumbnails...", "Abort", 0, thumbnails.size()); |  | ||||||
| 	progress.setWindowModality(Qt::WindowModal); |  | ||||||
| 
 |  | ||||||
| 	int count = 0; |  | ||||||
| 	for (const QString &name: thumbnails.keys()) { |  | ||||||
| 		const QImage thumbnail = thumbnails[name]; |  | ||||||
| 
 |  | ||||||
| 		if (thumbnail.isNull()) |  | ||||||
| 			continue; |  | ||||||
| 
 |  | ||||||
| 		// This is duplicate code (see qt-models/divepicturemodel.cpp)
 |  | ||||||
| 		// Not a problem, since this routine will be removed in due course.
 |  | ||||||
| 		QString filename = thumbnailFileName(name); |  | ||||||
| 		if (filename.isEmpty()) |  | ||||||
| 			continue; |  | ||||||
| 
 |  | ||||||
| 		QSaveFile file(filename); |  | ||||||
| 		if (!file.open(QIODevice::WriteOnly)) |  | ||||||
| 			return; |  | ||||||
| 		QDataStream stream(&file); |  | ||||||
| 
 |  | ||||||
| 		quint32 type = MEDIATYPE_PICTURE; |  | ||||||
| 		stream << type; |  | ||||||
| 		stream << thumbnail; |  | ||||||
| 		file.commit(); |  | ||||||
| 
 |  | ||||||
| 		progress.setValue(++count); |  | ||||||
| 		if (progress.wasCanceled()) |  | ||||||
| 			break; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // TODO: This is a temporary helper struct. Remove in due course with convertLocalFilename().
 | // TODO: This is a temporary helper struct. Remove in due course with convertLocalFilename().
 | ||||||
| struct HashToFile { | struct HashToFile { | ||||||
| 	QByteArray hash; | 	QByteArray hash; | ||||||
|  | @ -1129,7 +1089,7 @@ static void convertLocalFilename(const QHash<QString, QByteArray> &hashOf, const | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	// Create a vector of hash/filename pairs and sort by hash.
 | 	// Create a vector of hash/filename pairs and sort by hash.
 | ||||||
| 	// Elements can than be accessed with binary search.
 | 	// Elements can then be accessed with binary search.
 | ||||||
| 	QHash<QByteArray, QString> canonicalFilenameByHash; | 	QHash<QByteArray, QString> canonicalFilenameByHash; | ||||||
| 	QVector<HashToFile> h2f; | 	QVector<HashToFile> h2f; | ||||||
| 	h2f.reserve(hashOf.size()); | 	h2f.reserve(hashOf.size()); | ||||||
|  | @ -1166,7 +1126,6 @@ void read_hashes() | ||||||
| 		stream >> localFilenameOf; | 		stream >> localFilenameOf; | ||||||
| 		locker.unlock(); | 		locker.unlock(); | ||||||
| 		hashfile.close(); | 		hashfile.close(); | ||||||
| 		convertThumbnails(thumbnailCache); |  | ||||||
| 		convertLocalFilename(hashOf, localFilenameByHash); | 		convertLocalFilename(hashOf, localFilenameByHash); | ||||||
| 	} | 	} | ||||||
| 	QMutexLocker locker(&hashOfMutex); | 	QMutexLocker locker(&hashOfMutex); | ||||||
|  |  | ||||||
|  | @ -108,31 +108,6 @@ void uiNotification(const QString &msg); | ||||||
| #define TITLE_OR_TEXT(_t, _m) _t, _m | #define TITLE_OR_TEXT(_t, _m) _t, _m | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // Move a range in a vector to a different position.
 |  | ||||||
| // The parameters are given according to the usual STL-semantics:
 |  | ||||||
| //	v: a container with STL-like random access iterator via std::begin(...)
 |  | ||||||
| //	rangeBegin: index of first element
 |  | ||||||
| //	rangeEnd: index one *past* last element
 |  | ||||||
| //	destination: index to element before which the range will be moved
 |  | ||||||
| // Owing to std::begin() magic, this function works with STL-like containers:
 |  | ||||||
| //	QVector<int> v{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 |  | ||||||
| //	moveInVector(v, 1, 4, 6);
 |  | ||||||
| // as well as with C-style arrays:
 |  | ||||||
| //	int array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 |  | ||||||
| //	moveInVector(array, 1, 4, 6);
 |  | ||||||
| // Both calls will have the following effect:
 |  | ||||||
| //	Before: 0 1 2 3 4 5 6 7 8 9
 |  | ||||||
| //	After:  0 4 5 1 2 3 6 7 8 9
 |  | ||||||
| // No sanitizing of the input arguments is performed.
 |  | ||||||
| template <typename Vector> |  | ||||||
| void moveInVector(Vector &v, int rangeBegin, int rangeEnd, int destination) |  | ||||||
| { |  | ||||||
| 	auto it = std::begin(v); |  | ||||||
| 	if (destination > rangeEnd) |  | ||||||
| 		std::rotate(it + rangeBegin, it + rangeEnd, it + destination); |  | ||||||
| 	else if (destination < rangeBegin) |  | ||||||
| 		std::rotate(it + destination, it + rangeBegin, it + rangeEnd); |  | ||||||
| } |  | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // 3) Functions visible to C and C++
 | // 3) Functions visible to C and C++
 | ||||||
|  |  | ||||||
							
								
								
									
										101
									
								
								core/range.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								core/range.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,101 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0
 | ||||||
|  | // Helper functions for range manipulations
 | ||||||
|  | #ifndef RANGE_H | ||||||
|  | #define RANGE_H | ||||||
|  | 
 | ||||||
|  | #include <utility>	// for std::pair | ||||||
|  | #include <vector>	// we need a declaration of std::begin() and std::end() | ||||||
|  | 
 | ||||||
|  | // Move a range in a vector to a different position.
 | ||||||
|  | // The parameters are given according to the usual STL-semantics:
 | ||||||
|  | //	v: a container with STL-like random access iterator via std::begin(...)
 | ||||||
|  | //	rangeBegin: index of first element
 | ||||||
|  | //	rangeEnd: index one *past* last element
 | ||||||
|  | //	destination: index to element before which the range will be moved
 | ||||||
|  | // Owing to std::begin() magic, this function works with STL-like containers:
 | ||||||
|  | //	QVector<int> v{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 | ||||||
|  | //	move_in_range(v, 1, 4, 6);
 | ||||||
|  | // as well as with C-style arrays:
 | ||||||
|  | //	int array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 | ||||||
|  | //	move_in_range(array, 1, 4, 6);
 | ||||||
|  | // Both calls will have the following effect:
 | ||||||
|  | //	Before: 0 1 2 3 4 5 6 7 8 9
 | ||||||
|  | //	After:  0 4 5 1 2 3 6 7 8 9
 | ||||||
|  | // No sanitizing of the input arguments is performed.
 | ||||||
|  | template <typename Range> | ||||||
|  | void move_in_range(Range &v, int rangeBegin, int rangeEnd, int destination) | ||||||
|  | { | ||||||
|  | 	auto it = std::begin(v); | ||||||
|  | 	if (destination > rangeEnd) | ||||||
|  | 		std::rotate(it + rangeBegin, it + rangeEnd, it + destination); | ||||||
|  | 	else if (destination < rangeBegin) | ||||||
|  | 		std::rotate(it + destination, it + rangeBegin, it + rangeEnd); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // A rudimentary adaptor for looping over ranges with an index:
 | ||||||
|  | //	for (auto [idx, item]: enumerated_range(v)) ...
 | ||||||
|  | // The index is a signed integer, since this is what we use more often.
 | ||||||
|  | template <typename Range> | ||||||
|  | class enumerated_range | ||||||
|  | { | ||||||
|  | 	Range &base; | ||||||
|  | public: | ||||||
|  | 	using base_iterator = decltype(std::begin(std::declval<Range>())); | ||||||
|  | 	class iterator { | ||||||
|  | 		int idx; | ||||||
|  | 		base_iterator it; | ||||||
|  | 	public: | ||||||
|  | 		std::pair<int, decltype(*it)> operator*() const | ||||||
|  | 		{ | ||||||
|  | 			return { idx, *it }; | ||||||
|  | 		} | ||||||
|  | 		iterator &operator++() | ||||||
|  | 		{ | ||||||
|  | 			++idx; | ||||||
|  | 			++it; | ||||||
|  | 			return *this; | ||||||
|  | 		} | ||||||
|  | 		iterator(int idx, base_iterator it) : idx(idx), it(it) | ||||||
|  | 		{ | ||||||
|  | 		} | ||||||
|  | 		bool operator==(const iterator &it2) const | ||||||
|  | 		{ | ||||||
|  | 			return it == it2.it; | ||||||
|  | 		} | ||||||
|  | 		bool operator!=(const iterator &it2) const | ||||||
|  | 		{ | ||||||
|  | 			return it != it2.it; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	iterator begin() | ||||||
|  | 	{ | ||||||
|  | 		return iterator(0, std::begin(base)); | ||||||
|  | 	} | ||||||
|  | 	iterator end() | ||||||
|  | 	{ | ||||||
|  | 		return iterator(-1, std::end(base)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	enumerated_range(Range &base) : base(base) | ||||||
|  | 	{ | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Find the index of an element in a range. Return -1 if not found
 | ||||||
|  | // Range must have a random access iterator.
 | ||||||
|  | template <typename Range, typename Element> | ||||||
|  | int index_of(const Range &range, const Element &e) | ||||||
|  | { | ||||||
|  | 	auto it = std::find(std::begin(range), std::end(range), e); | ||||||
|  | 	return it == std::end(range) ? -1 : it - std::begin(range); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <typename Range, typename Func> | ||||||
|  | int index_of_if(const Range &range, Func f) | ||||||
|  | { | ||||||
|  | 	auto it = std::find_if(std::begin(range), std::end(range), f); | ||||||
|  | 	return it == std::end(range) ? -1 : it - std::begin(range); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| #include "templateedit.h" | #include "templateedit.h" | ||||||
| #include "templatelayout.h" | #include "templatelayout.h" | ||||||
| #include "core/qthelper.h" | #include "core/qthelper.h" | ||||||
|  | #include "core/range.h" | ||||||
| 
 | 
 | ||||||
| #include <QDebug> | #include <QDebug> | ||||||
| #include <QFileDialog> | #include <QFileDialog> | ||||||
|  | @ -63,10 +64,10 @@ void PrintOptions::setupTemplates() | ||||||
| 	currList.sort(); | 	currList.sort(); | ||||||
| 	int current_index = 0; | 	int current_index = 0; | ||||||
| 	ui.printTemplate->clear(); | 	ui.printTemplate->clear(); | ||||||
| 	Q_FOREACH(const QString& theme, currList) { | 	for (auto [idx, theme]: enumerated_range(currList)) { | ||||||
| 		 // find the stored template in the list
 | 		 // find the stored template in the list
 | ||||||
| 		if (theme == storedTemplate || theme == lastImportExportTemplate) | 		if (theme == storedTemplate || theme == lastImportExportTemplate) | ||||||
| 			current_index = currList.indexOf(theme); | 			current_index = idx; | ||||||
| 		ui.printTemplate->addItem(theme.split('.')[0], theme); | 		ui.printTemplate->addItem(theme.split('.')[0], theme); | ||||||
| 	} | 	} | ||||||
| 	ui.printTemplate->setCurrentIndex(current_index); | 	ui.printTemplate->setCurrentIndex(current_index); | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #include "core/event.h" | #include "core/event.h" | ||||||
| #include "core/subsurface-string.h" | #include "core/subsurface-string.h" | ||||||
| #include "core/qthelper.h" | #include "core/qthelper.h" | ||||||
|  | #include "core/range.h" | ||||||
| #include "core/settings/qPrefTechnicalDetails.h" | #include "core/settings/qPrefTechnicalDetails.h" | ||||||
| #include "core/settings/qPrefPartialPressureGas.h" | #include "core/settings/qPrefPartialPressureGas.h" | ||||||
| #include "profile-widget/diveeventitem.h" | #include "profile-widget/diveeventitem.h" | ||||||
|  | @ -870,8 +871,8 @@ void ProfileWidget2::pointsRemoved(const QModelIndex &, int start, int end) | ||||||
| 
 | 
 | ||||||
| void ProfileWidget2::pointsMoved(const QModelIndex &, int start, int end, const QModelIndex &, int row) | void ProfileWidget2::pointsMoved(const QModelIndex &, int start, int end, const QModelIndex &, int row) | ||||||
| { | { | ||||||
| 	moveInVector(handles, start, end + 1, row); | 	move_in_range(handles, start, end + 1, row); | ||||||
| 	moveInVector(gases, start, end + 1, row); | 	move_in_range(gases, start, end + 1, row); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ProfileWidget2::repositionDiveHandlers() | void ProfileWidget2::repositionDiveHandlers() | ||||||
|  | @ -1319,7 +1320,7 @@ void ProfileWidget2::pictureOffsetChanged(dive *dIn, QString filename, offset_t | ||||||
| 			// Move image from old to new position
 | 			// Move image from old to new position
 | ||||||
| 			int oldIndex = oldPos - pictures.begin(); | 			int oldIndex = oldPos - pictures.begin(); | ||||||
| 			int newIndex = newPos - pictures.begin(); | 			int newIndex = newPos - pictures.begin(); | ||||||
| 			moveInVector(pictures, oldIndex, oldIndex + 1, newIndex); | 			move_in_range(pictures, oldIndex, oldIndex + 1, newIndex); | ||||||
| 		} else { | 		} else { | ||||||
| 			// Case 1b): remove picture
 | 			// Case 1b): remove picture
 | ||||||
| 			pictures.erase(oldPos); | 			pictures.erase(oldPos); | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #include "core/imagedownloader.h" | #include "core/imagedownloader.h" | ||||||
| #include "core/picture.h" | #include "core/picture.h" | ||||||
| #include "core/qthelper.h" | #include "core/qthelper.h" | ||||||
|  | #include "core/range.h" | ||||||
| #include "core/selection.h" | #include "core/selection.h" | ||||||
| #include "core/subsurface-qt/divelistnotifier.h" | #include "core/subsurface-qt/divelistnotifier.h" | ||||||
| #include "commands/command.h" | #include "commands/command.h" | ||||||
|  | @ -205,8 +206,8 @@ void DivePictureModel::picturesAdded(dive *d, QVector<PictureObj> picsIn) | ||||||
| 	// Convert the picture-data into our own format
 | 	// Convert the picture-data into our own format
 | ||||||
| 	std::vector<PictureEntry> pics; | 	std::vector<PictureEntry> pics; | ||||||
| 	pics.reserve(picsIn.size()); | 	pics.reserve(picsIn.size()); | ||||||
| 	for (int i = 0; i < picsIn.size(); ++i) | 	for (const PictureObj &pic: picsIn) | ||||||
| 		pics.push_back(PictureEntry(d, picsIn[i])); | 		pics.push_back(PictureEntry(d, pic)); | ||||||
| 
 | 
 | ||||||
| 	// Insert batch-wise to avoid too many reloads
 | 	// Insert batch-wise to avoid too many reloads
 | ||||||
| 	pictures.reserve(pictures.size() + pics.size()); | 	pictures.reserve(pictures.size() + pics.size()); | ||||||
|  | @ -241,10 +242,8 @@ int DivePictureModel::rowCount(const QModelIndex&) const | ||||||
| 
 | 
 | ||||||
| int DivePictureModel::findPictureId(const std::string &filename) | int DivePictureModel::findPictureId(const std::string &filename) | ||||||
| { | { | ||||||
| 	for (int i = 0; i < (int)pictures.size(); ++i) | 	return index_of_if(pictures, [&filename](const PictureEntry &p) | ||||||
| 		if (pictures[i].filename == filename) | 				     { return p.filename == filename; }); | ||||||
| 			return i; |  | ||||||
| 	return -1; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void addDurationToThumbnail(QImage &img, duration_t duration) | static void addDurationToThumbnail(QImage &img, duration_t duration) | ||||||
|  | @ -312,6 +311,6 @@ void DivePictureModel::pictureOffsetChanged(dive *d, const QString filenameIn, o | ||||||
| 	if (oldIndex == newIndex || oldIndex + 1 == newIndex) | 	if (oldIndex == newIndex || oldIndex + 1 == newIndex) | ||||||
| 		return; | 		return; | ||||||
| 	beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex); | 	beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex); | ||||||
| 	moveInVector(pictures, oldIndex, oldIndex + 1, newIndex); | 	move_in_range(pictures, oldIndex, oldIndex + 1, newIndex); | ||||||
| 	endMoveRows(); | 	endMoveRows(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
| #include "qt-models/models.h" | #include "qt-models/models.h" | ||||||
| #include "core/device.h" | #include "core/device.h" | ||||||
| #include "core/qthelper.h" | #include "core/qthelper.h" | ||||||
|  | #include "core/range.h" | ||||||
| #include "core/sample.h" | #include "core/sample.h" | ||||||
| #include "core/settings/qPrefDivePlanner.h" | #include "core/settings/qPrefDivePlanner.h" | ||||||
| #include "core/settings/qPrefUnit.h" | #include "core/settings/qPrefUnit.h" | ||||||
|  | @ -877,7 +878,7 @@ void DivePlannerPointsModel::editStop(int row, divedatapoint newData) | ||||||
| 
 | 
 | ||||||
| 	if (newRow != row && newRow != row + 1) { | 	if (newRow != row && newRow != row + 1) { | ||||||
| 		beginMoveRows(QModelIndex(), row, row, QModelIndex(), newRow); | 		beginMoveRows(QModelIndex(), row, row, QModelIndex(), newRow); | ||||||
| 		moveInVector(divepoints, row, row + 1, newRow); | 		move_in_range(divepoints, row, row + 1, newRow); | ||||||
| 		endMoveRows(); | 		endMoveRows(); | ||||||
| 
 | 
 | ||||||
| 		// Account for moving the row backwards in the array.
 | 		// Account for moving the row backwards in the array.
 | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include "core/string-format.h" | #include "core/string-format.h" | ||||||
| #include "core/trip.h" | #include "core/trip.h" | ||||||
| #include "core/qthelper.h" | #include "core/qthelper.h" | ||||||
|  | #include "core/range.h" | ||||||
| #include "core/divesite.h" | #include "core/divesite.h" | ||||||
| #include "core/picture.h" | #include "core/picture.h" | ||||||
| #include "core/subsurface-string.h" | #include "core/subsurface-string.h" | ||||||
|  | @ -205,8 +206,8 @@ static QPixmap &getPhotoIcon(int idx) | ||||||
| 	if (!icons) { | 	if (!icons) { | ||||||
| 		const IconMetrics &im = defaultIconMetrics(); | 		const IconMetrics &im = defaultIconMetrics(); | ||||||
| 		icons = std::make_unique<QPixmap[]>(std::size(icon_names)); | 		icons = std::make_unique<QPixmap[]>(std::size(icon_names)); | ||||||
| 		for (size_t i = 0; i < std::size(icon_names); ++i) | 		for (auto [i, name]: enumerated_range(icon_names)) | ||||||
| 			icons[i] = QIcon(icon_names[i]).pixmap(im.sz_small, im.sz_small); | 			icons[i] = QIcon(name).pixmap(im.sz_small, im.sz_small); | ||||||
| 	} | 	} | ||||||
| 	return icons[idx]; | 	return icons[idx]; | ||||||
| } | } | ||||||
|  | @ -974,7 +975,7 @@ void DiveTripModelTree::topLevelChanged(int idx) | ||||||
| 	// If index changed, move items
 | 	// If index changed, move items
 | ||||||
| 	if (newIdx != idx && newIdx != idx + 1) { | 	if (newIdx != idx && newIdx != idx + 1) { | ||||||
| 		beginMoveRows(QModelIndex(), idx, idx, QModelIndex(), newIdx); | 		beginMoveRows(QModelIndex(), idx, idx, QModelIndex(), newIdx); | ||||||
| 		moveInVector(items, idx, idx + 1, newIdx); | 		move_in_range(items, idx, idx + 1, newIdx); | ||||||
| 		endMoveRows(); | 		endMoveRows(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -1007,34 +1008,26 @@ void DiveTripModelTree::addDivesToTrip(int trip, const QVector<dive *> &dives) | ||||||
| 
 | 
 | ||||||
| int DiveTripModelTree::findTripIdx(const dive_trip *trip) const | int DiveTripModelTree::findTripIdx(const dive_trip *trip) const | ||||||
| { | { | ||||||
| 	for (int i = 0; i < (int)items.size(); ++i) | 	return index_of_if(items, [trip] (const Item &item) | ||||||
| 		if (items[i].d_or_t.trip == trip) | 				  { return item.d_or_t.trip == trip; }); | ||||||
| 			return i; |  | ||||||
| 	return -1; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int DiveTripModelTree::findDiveIdx(const dive *d) const | int DiveTripModelTree::findDiveIdx(const dive *d) const | ||||||
| { | { | ||||||
| 	for (int i = 0; i < (int)items.size(); ++i) | 	return index_of_if(items, [d] (const Item &item) | ||||||
| 		if (items[i].isDive(d)) | 				  { return item.isDive(d); }); | ||||||
| 			return i; |  | ||||||
| 	return -1; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int DiveTripModelTree::findDiveInTrip(int tripIdx, const dive *d) const | int DiveTripModelTree::findDiveInTrip(int tripIdx, const dive *d) const | ||||||
| { | { | ||||||
| 	const Item &item = items[tripIdx]; | 	return index_of(items[tripIdx].dives, d); | ||||||
| 	for (int i = 0; i < (int)item.dives.size(); ++i) |  | ||||||
| 		if (item.dives[i] == d) |  | ||||||
| 			return i; |  | ||||||
| 	return -1; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int DiveTripModelTree::findInsertionIndex(const dive_trip *trip) const | int DiveTripModelTree::findInsertionIndex(const dive_trip *trip) const | ||||||
| { | { | ||||||
| 	dive_or_trip d_or_t{ nullptr, (dive_trip *)trip }; | 	dive_or_trip d_or_t{ nullptr, (dive_trip *)trip }; | ||||||
| 	for (int i = 0; i < (int)items.size(); ++i) { | 	for (auto [i, item]: enumerated_range(items)) { | ||||||
| 		if (dive_or_trip_less_than(d_or_t, items[i].d_or_t)) | 		if (dive_or_trip_less_than(d_or_t, item.d_or_t)) | ||||||
| 			return i; | 			return i; | ||||||
| 	} | 	} | ||||||
| 	return items.size(); | 	return items.size(); | ||||||
|  | @ -1121,8 +1114,8 @@ static QVector<dive *> visibleDives(const QVector<dive *> &dives) | ||||||
| #ifdef SUBSURFACE_MOBILE | #ifdef SUBSURFACE_MOBILE | ||||||
| int DiveTripModelTree::tripInDirection(const struct dive *d, int direction) const | int DiveTripModelTree::tripInDirection(const struct dive *d, int direction) const | ||||||
| { | { | ||||||
| 	for (int i = 0; i < (int)items.size(); ++i) { | 	for (auto [i, item]: enumerated_range(items)) { | ||||||
| 		if (items[i].d_or_t.dive == d || (items[i].d_or_t.trip && findDiveInTrip(i, d) != -1)) { | 		if (item.d_or_t.dive == d || (item.d_or_t.trip && findDiveInTrip(i, d) != -1)) { | ||||||
| 			// now walk in the direction given to find a trip
 | 			// now walk in the direction given to find a trip
 | ||||||
| 			int offset = direction; | 			int offset = direction; | ||||||
| 			while (i + offset >= 0 && i + offset < (int)items.size()) { | 			while (i + offset >= 0 && i + offset < (int)items.size()) { | ||||||
|  | @ -1405,8 +1398,8 @@ void DiveTripModelTree::divesSelectedTrip(dive_trip *trip, const QVector<dive *> | ||||||
| 		// Since both lists are sorted, we can do this linearly. Perhaps a binary search
 | 		// Since both lists are sorted, we can do this linearly. Perhaps a binary search
 | ||||||
| 		// would be better?
 | 		// would be better?
 | ||||||
| 		int j = 0; // Index in items array
 | 		int j = 0; // Index in items array
 | ||||||
| 		for (int i = 0; i < dives.size(); ++i) { | 		for (struct dive *dive: dives) { | ||||||
| 			while (j < (int)items.size() && !items[j].isDive(dives[i])) | 			while (j < (int)items.size() && !items[j].isDive(dive)) | ||||||
| 				++j; | 				++j; | ||||||
| 			if (j >= (int)items.size()) | 			if (j >= (int)items.size()) | ||||||
| 				break; | 				break; | ||||||
|  | @ -1426,8 +1419,8 @@ void DiveTripModelTree::divesSelectedTrip(dive_trip *trip, const QVector<dive *> | ||||||
| 		// would be better?
 | 		// would be better?
 | ||||||
| 		int j = 0; // Index in items array
 | 		int j = 0; // Index in items array
 | ||||||
| 		const Item &entry = items[idx]; | 		const Item &entry = items[idx]; | ||||||
| 		for (int i = 0; i < dives.size(); ++i) { | 		for (struct dive *dive: dives) { | ||||||
| 			while (j < (int)entry.dives.size() && entry.dives[j] != dives[i]) | 			while (j < (int)entry.dives.size() && entry.dives[j] != dive) | ||||||
| 				++j; | 				++j; | ||||||
| 			if (j >= (int)entry.dives.size()) | 			if (j >= (int)entry.dives.size()) | ||||||
| 				break; | 				break; | ||||||
|  | @ -1662,8 +1655,8 @@ void DiveTripModelList::divesSelected(const QVector<dive *> &divesIn) | ||||||
| 	// Since both lists are sorted, we can do this linearly. Perhaps a binary search
 | 	// Since both lists are sorted, we can do this linearly. Perhaps a binary search
 | ||||||
| 	// would be better?
 | 	// would be better?
 | ||||||
| 	int j = 0; // Index in items array
 | 	int j = 0; // Index in items array
 | ||||||
| 	for (int i = 0; i < dives.size(); ++i) { | 	for (struct dive *dive: dives) { | ||||||
| 		while (j < (int)items.size() && items[j] != dives[i]) | 		while (j < (int)items.size() && items[j] != dive) | ||||||
| 			++j; | 			++j; | ||||||
| 		if (j >= (int)items.size()) | 		if (j >= (int)items.size()) | ||||||
| 			break; | 			break; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue