diff --git a/images/template-set-add.png b/images/template-set-add.png
new file mode 100644
index 000000000..432c08f67
Binary files /dev/null and b/images/template-set-add.png differ
diff --git a/images/template-set-delete.png b/images/template-set-delete.png
new file mode 100644
index 000000000..4c186dde8
Binary files /dev/null and b/images/template-set-delete.png differ
diff --git a/resources.qrc b/resources.qrc
index 83ef2a9fd..e0cf87008 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -119,17 +119,19 @@
images/mapper-icon/Mapper-128.png
images/map-information.png
doc/tip-of-the-day/tips_en.txt
+ images/template-set-add.png
+ images/template-set-delete.png
-
+
doc/tip-of-the-day/tips_de.txt
-
+
doc/tip-of-the-day/tips_fr.txt
-
+
doc/tip-of-the-day/tips_ru.txt
-
+
doc/tip-of-the-day/tips_uk.txt
diff --git a/src/core/map_view.cpp b/src/core/map_view.cpp
index 138526bbf..1dd4bbb60 100644
--- a/src/core/map_view.cpp
+++ b/src/core/map_view.cpp
@@ -1,6 +1,7 @@
/*
* Copyright 2012, 2013 Thomas Schöps
* Copyright 2014-2020, 2025 Kai Pastor
+ * Copyright 2025 Matthias Kühlewein
*
* This file is part of OpenOrienteering.
*
@@ -54,6 +55,9 @@ namespace literal
static const QLatin1String hidden("hidden");
static const QLatin1String ref("ref");
static const QLatin1String template_string("template");
+ static const QLatin1String template_sets("template_sets");
+ static const QLatin1String template_set("template_set");
+ static const QLatin1String active_template_set("active_template_set");
}
@@ -73,14 +77,11 @@ bool TemplateVisibility::hasAlpha() const
const double MapView::zoom_in_limit = 512;
const double MapView::zoom_out_limit = 1 / 16.0;
-
MapView::MapView(QObject* parent, Map* map)
: QObject { parent }
, map { map }
, zoom { 1.0 }
, rotation { 0.0 }
-, map_visibility{ 1.0f, true }
-, all_templates_hidden{ false }
, grid_visible{ false }
, overprinting_simulation_enabled{ false }
{
@@ -114,21 +115,40 @@ void MapView::save(QXmlStreamWriter& xml, const QLatin1String& element_name, boo
mapview_element.writeAttribute(literal::grid, grid_visible);
mapview_element.writeAttribute(literal::overprinting_simulation_enabled, overprinting_simulation_enabled);
+ // maintain legacy format:
+ saveTemplateSet(xml, template_details, template_visibility_sets.getVisibility(0));
+
+ if (getNumberOfVisibilitySets() < 2)
+ return; // don't duplicate first and only set
+
+ XmlElementWriter template_sets_element(xml, literal::template_sets);
+ template_sets_element.writeAttribute(XmlStreamLiteral::count, getNumberOfVisibilitySets());
+ template_sets_element.writeAttribute(literal::active_template_set, getActiveVisibilityIndex());
+
+ for (int i = 1; i < getNumberOfVisibilitySets(); ++i)
+ {
+ XmlElementWriter template_set_element(xml, literal::template_set);
+ saveTemplateSet(xml, template_details, template_visibility_sets.getVisibility(i));
+ }
+}
+
+void MapView::saveTemplateSet(QXmlStreamWriter& xml, bool template_details, const TemplateVisibilitySet& template_set) const
+{
{
XmlElementWriter map_element(xml, literal::map);
- map_element.writeAttribute(literal::opacity, map_visibility.opacity);
- map_element.writeAttribute(literal::visible, map_visibility.visible);
+ map_element.writeAttribute(literal::opacity, template_set.map_visibility.opacity);
+ map_element.writeAttribute(literal::visible, template_set.map_visibility.visible);
}
{
XmlElementWriter templates_element(xml, literal::templates);
- templates_element.writeAttribute(literal::hidden, all_templates_hidden);
+ templates_element.writeAttribute(literal::hidden, template_set.all_templates_hidden);
if (template_details)
{
- templates_element.writeAttribute(XmlStreamLiteral::count, template_visibilities.size());
- for (auto entry : template_visibilities)
+ templates_element.writeAttribute(XmlStreamLiteral::count, template_set.template_visibilities.size());
+ for (const auto& entry : template_set.template_visibilities)
{
- auto const index = map->findTemplateIndex(entry.temp);
+ auto const index = map->findTemplateIndex(entry.templ);
if (index < 0)
{
qWarning("Template visibility found for unknown template");
@@ -167,52 +187,95 @@ void MapView::load(QXmlStreamReader& xml, int version)
grid_visible = mapview_element.attribute(literal::grid);
overprinting_simulation_enabled = mapview_element.attribute(literal::overprinting_simulation_enabled);
+ int current_template_set = 0;
+ int active_visibility_index = 0;
+ auto& template_set = template_visibility_sets.accessVisibility(0);
+
while (xml.readNextStartElement())
{
- if (xml.name() == literal::map)
- {
- XmlElementReader map_element(xml);
- map_visibility.opacity = map_element.attribute(literal::opacity);
- // Some old maps before version 6 lack visible="true".
- if (version >= 6)
- map_visibility.visible = map_element.attribute(literal::visible);
- else
- map_visibility.visible = true;
- }
- else if (xml.name() == literal::templates)
+ if (xml.name() == literal::template_sets)
{
- XmlElementReader templates_element(xml);
- auto num_template_visibilities = templates_element.attribute(XmlStreamLiteral::count);
- template_visibilities.reserve(qBound(20u, num_template_visibilities, 1000u));
- all_templates_hidden = templates_element.attribute(literal::hidden);
-
+ XmlElementReader template_sets_element(xml);
+ auto visibility_sets_num = template_sets_element.attribute(XmlStreamLiteral::count);
+ template_visibility_sets.template_visibility_sets.reserve(visibility_sets_num);
+ active_visibility_index = template_sets_element.attribute(literal::active_template_set); // stored for later usage
while (xml.readNextStartElement())
{
- if (xml.name() == literal::ref)
+ if (xml.name() == literal::template_set)
{
- XmlElementReader ref_element(xml);
- int pos = ref_element.attribute(literal::template_string);
- if (pos >= 0 && pos < map->getNumTemplates())
+ XmlElementReader template_set_element(xml);
+ template_visibility_sets.template_visibility_sets.push_back(TemplateVisibilitySet());
+ auto& template_set = template_visibility_sets.accessVisibility(++current_template_set);
+ template_visibility_sets.setActiveVisibilityIndex(current_template_set);
+ while (xml.readNextStartElement())
{
- TemplateVisibility vis {
- qBound(0.0f, ref_element.attribute(literal::opacity), 1.0f),
- ref_element.attribute(literal::visible)
- };
- setTemplateVisibilityHelper(map->getTemplate(pos), vis);
+ loadMapAndTemplates(xml, template_set, version);
}
}
else
+ {
xml.skipCurrentElement();
+ }
}
}
else
- xml.skipCurrentElement(); // unsupported
+ {
+ loadMapAndTemplates(xml, template_set, version);
+ }
}
+ if (active_visibility_index >= getNumberOfVisibilitySets())
+ active_visibility_index = 0;
+ template_visibility_sets.setActiveVisibilityIndex(active_visibility_index);
emit viewChanged(CenterChange | ZoomChange | RotationChange);
emit visibilityChanged(MultipleFeatures, true);
}
+void MapView::loadMapAndTemplates(QXmlStreamReader& xml, TemplateVisibilitySet& template_set, int version)
+{
+ if (xml.name() == literal::map)
+ {
+ XmlElementReader map_element(xml);
+ TemplateVisibility map_visibility;
+ map_visibility.opacity = map_element.attribute(literal::opacity);
+ // Some old maps before version 6 lack visible="true".
+ if (version >= 6)
+ map_visibility.visible = map_element.attribute(literal::visible);
+ else
+ map_visibility.visible = true;
+ template_set.map_visibility = map_visibility;
+ }
+ else if (xml.name() == literal::templates)
+ {
+ XmlElementReader templates_element(xml);
+ auto num_template_visibilities = templates_element.attribute(XmlStreamLiteral::count);
+ template_set.template_visibilities.reserve(qBound(20u, num_template_visibilities, 1000u));
+ template_set.all_templates_hidden = templates_element.attribute(literal::hidden);
+
+ while (xml.readNextStartElement())
+ {
+ if (xml.name() == literal::ref)
+ {
+ XmlElementReader ref_element(xml);
+ int pos = ref_element.attribute(literal::template_string);
+ if (pos >= 0 && pos < map->getNumTemplates())
+ {
+ TemplateVisibility vis {
+ qBound(0.0f, ref_element.attribute(literal::opacity), 1.0f),
+ ref_element.attribute(literal::visible)
+ };
+ setTemplateVisibilityHelper(map->getTemplate(pos), vis);
+ }
+ }
+ else
+ xml.skipCurrentElement();
+ }
+ }
+ else
+ xml.skipCurrentElement(); // unsupported
+}
+
+
void MapView::updateAllMapWidgets(VisibilityFeature change)
{
emit visibilityChanged(change, true);
@@ -360,104 +423,97 @@ void MapView::updateTransform()
TemplateVisibility MapView::effectiveMapVisibility() const
{
- if (all_templates_hidden)
+ if (template_visibility_sets.getCurrentVisibility().all_templates_hidden)
return { 1.0f, true };
- else if (map_visibility.opacity < 0.005)
+ else if (template_visibility_sets.getCurrentVisibility().map_visibility.opacity < 0.005)
return { 0.0f, false };
else
- return map_visibility;
+ return template_visibility_sets.getCurrentVisibility().map_visibility;
}
void MapView::setMapVisibility(TemplateVisibility vis)
{
- if (map_visibility != vis)
+ if (template_visibility_sets.getCurrentVisibility().map_visibility != vis)
{
- map_visibility = vis;
+ template_visibility_sets.accessCurrentVisibility().map_visibility = vis;
emit visibilityChanged(VisibilityFeature::MapVisible, vis.visible && vis.opacity > 0, nullptr);
}
}
-MapView::TemplateVisibilityVector::const_iterator MapView::findVisibility(const Template* temp) const
-{
- return std::find_if(begin(template_visibilities), end(template_visibilities), [temp](const TemplateVisibilityEntry& entry)
- {
- return entry.temp == temp;
- } );
-}
-
-MapView::TemplateVisibilityVector::iterator MapView::findVisibility(const Template* temp)
+bool MapView::isTemplateVisible(const Template* templ) const
{
- return std::find_if(begin(template_visibilities), end(template_visibilities), [temp](const TemplateVisibilityEntry& entry)
+ if (template_visibility_sets.existsCurrentTemplateVisibility(templ))
{
- return entry.temp == temp;
- } );
-}
-
-bool MapView::isTemplateVisible(const Template* temp) const
-{
- auto entry = findVisibility(temp);
- return entry != end(template_visibilities)
- && entry->visible
- && entry->opacity > 0;
+ auto current_visibility = template_visibility_sets.getCurrentTemplateVisibility(templ);
+ return current_visibility.visible && current_visibility.opacity > 0;
+ }
+ return false;
}
-TemplateVisibility MapView::getTemplateVisibility(const Template* temp) const
+TemplateVisibility MapView::getTemplateVisibility(const Template* templ) const
{
- auto entry = findVisibility(temp);
- if (entry != end(template_visibilities))
- return *entry;
+ if (template_visibility_sets.existsCurrentTemplateVisibility(templ))
+ return template_visibility_sets.getCurrentTemplateVisibility(templ);
else
return { 1.0f, false };
}
-void MapView::setTemplateVisibility(Template* temp, TemplateVisibility vis)
+void MapView::setTemplateVisibility(Template* templ, TemplateVisibility vis)
{
- if (setTemplateVisibilityHelper(temp, vis))
+ if (setTemplateVisibilityHelper(templ, vis))
{
auto const visible = vis.visible && vis.opacity > 0;
- emit visibilityChanged(VisibilityFeature::TemplateVisible, visible, temp);
+ emit visibilityChanged(VisibilityFeature::TemplateVisible, visible, templ);
}
}
-bool MapView::setTemplateVisibilityHelper(const Template *temp, TemplateVisibility vis)
+bool MapView::setTemplateVisibilityHelper(const Template *templ, TemplateVisibility vis)
{
- auto stored = findVisibility(temp);
- if (stored == end(template_visibilities))
+ if (!template_visibility_sets.existsCurrentTemplateVisibility(templ))
{
- template_visibilities.emplace_back(temp, vis);
+ template_visibility_sets.addTemplate(templ, vis);
return true;
}
- if (*stored != vis)
+ if (template_visibility_sets.getCurrentTemplateVisibility(templ) != vis) // can this condition ever be false?
{
- stored->opacity = vis.opacity;
- stored->visible = vis.visible;
+ template_visibility_sets.setCurrentTemplateVisibility(templ, vis);
return true;
}
return false;
}
-void MapView::onAboutToAddTemplate(int, Template* temp)
+TemplateVisibility MapView::getMapVisibility() const
+{
+ return template_visibility_sets.getCurrentVisibility().map_visibility;
+}
+
+bool MapView::areAllTemplatesHidden() const
{
- setTemplateVisibilityHelper(temp, { 1.0f, false });
+ return template_visibility_sets.getCurrentVisibility().all_templates_hidden;
}
-void MapView::onTemplateAdded(int, Template* temp)
+void MapView::onAboutToAddTemplate(int, Template* templ)
{
- setTemplateVisibility(temp, { 1.0f, true });
+ setTemplateVisibilityHelper(templ, { 1.0f, false });
}
-void MapView::onTemplateDeleted(int, const Template* temp)
+void MapView::onTemplateAdded(int, Template* templ)
{
- template_visibilities.erase(findVisibility(temp));
+ setTemplateVisibility(templ, { 1.0f, true });
+}
+
+void MapView::onTemplateDeleted(int, const Template* templ)
+{
+ template_visibility_sets.deleteTemplate(templ);
}
void MapView::setAllTemplatesHidden(bool value)
{
- if (all_templates_hidden != value)
+ if (template_visibility_sets.getCurrentVisibility().all_templates_hidden != value)
{
- all_templates_hidden = value;
- emit visibilityChanged(VisibilityFeature::AllTemplatesHidden, value);
+ template_visibility_sets.accessCurrentVisibility().all_templates_hidden = value;
+ emit visibilityChanged(VisibilityFeature::AllTemplatesHidden, value); // paramter 'value' is not used for VisibilityFeature::AllTemplatesHidden
}
}
@@ -466,7 +522,7 @@ void MapView::setGridVisible(bool visible)
if (grid_visible != visible)
{
grid_visible = visible;
- emit visibilityChanged(VisibilityFeature::GridVisible, visible);
+ emit visibilityChanged(VisibilityFeature::GridVisible, visible); // paramter 'visible' is not used for VisibilityFeature::GridVisible
}
}
@@ -475,7 +531,7 @@ void MapView::setOverprintingSimulationEnabled(bool enabled)
if (overprinting_simulation_enabled != enabled)
{
overprinting_simulation_enabled = enabled;
- emit visibilityChanged(VisibilityFeature::OverprintingEnabled, enabled);
+ emit visibilityChanged(VisibilityFeature::OverprintingEnabled, enabled); // paramter 'enabled' is not used for VisibilityFeature::OverprintingEnabled
}
}
@@ -492,14 +548,162 @@ bool MapView::hasAlpha() const
for (int i = 0; i < map->getNumTemplates(); ++i)
{
- auto temp = map->getTemplate(i);
- auto visibility = getTemplateVisibility(temp);
- if (visibility.hasAlpha() || temp->hasAlpha())
+ auto templ = map->getTemplate(i);
+ auto visibility = getTemplateVisibility(templ);
+ if (visibility.hasAlpha() || templ->hasAlpha())
return true;
}
return false;
}
+bool MapView::applyVisibilitySet(int new_visibility_set)
+{
+ Q_ASSERT(new_visibility_set < getNumberOfVisibilitySets());
+ Q_ASSERT(template_visibility_sets.getVisibility(new_visibility_set).template_visibilities.size() == template_visibility_sets.getCurrentVisibility().template_visibilities.size());
+
+ if (new_visibility_set != getActiveVisibilityIndex())
+ {
+ auto number_of_templates = (int)template_visibility_sets.getVisibility(new_visibility_set).template_visibilities.size();
+ for (int i = 0; i < number_of_templates; ++i)
+ {
+ auto new_template_visibility = template_visibility_sets.getTemplateVisibility(new_visibility_set, i);
+ if (new_template_visibility != template_visibility_sets.getTemplateVisibility(getActiveVisibilityIndex(), i))
+ {
+ auto const visible = new_template_visibility.visible && new_template_visibility.opacity > 0;
+ emit visibilityChanged(VisibilityFeature::TemplateVisible, visible, const_cast(new_template_visibility.templ));
+ }
+ }
+
+ auto new_map_visibility = template_visibility_sets.getVisibility(new_visibility_set).map_visibility;
+ if (template_visibility_sets.getCurrentVisibility().map_visibility != new_map_visibility)
+ {
+ emit visibilityChanged(VisibilityFeature::MapVisible, new_map_visibility.visible && new_map_visibility.opacity > 0, nullptr); // actual visibility is not used for VisibilityFeature::MapVisible
+ }
+
+ if (template_visibility_sets.getCurrentVisibility().all_templates_hidden != template_visibility_sets.getVisibility(new_visibility_set).all_templates_hidden)
+ return true;
+ }
+
+ return false;
+}
+
+void MapView::setVisibilitySet(int new_visibility_set)
+{
+ auto emit_change = applyVisibilitySet(new_visibility_set);
+ template_visibility_sets.setActiveVisibilityIndex(new_visibility_set);
+ if (emit_change)
+ emit visibilityChanged(VisibilityFeature::AllTemplatesHidden, true); // 'true' is not used for VisibilityFeature::AllTemplatesHidden
+}
+
+void MapView::addVisibilitySet()
+{
+ template_visibility_sets.duplicateVisibility();
+}
+
+void MapView::deleteVisibilitySet()
+{
+ auto new_visibility_set = getActiveVisibilityIndex();
+ if (new_visibility_set < getNumberOfVisibilitySets() - 1)
+ ++new_visibility_set; // deleting not the last set => the following set will be applied
+ else
+ --new_visibility_set; // deleting the last set => the previous set will be applied
+ auto emit_change = applyVisibilitySet(new_visibility_set);
+ template_visibility_sets.deleteVisibility();
+ if (emit_change)
+ emit visibilityChanged(VisibilityFeature::AllTemplatesHidden, true); // 'true' is not used for VisibilityFeature::AllTemplatesHidden
+}
+
+// ### TemplateVisibilitySet ###
+
+TemplateVisibilitySet::TemplateVisibilitySet()
+{
+ map_visibility = {1.0f, true};
+}
+
+TemplateVisibilitySets::TemplateVisibilitySets()
+{
+ template_visibility_sets.push_back(TemplateVisibilitySet());
+ active_visibility_index = 0;
+}
+
+void TemplateVisibilitySets::duplicateVisibility()
+{
+ template_visibility_sets.insert(template_visibility_sets.begin() + active_visibility_index + 1, getCurrentVisibility());
+ ++active_visibility_index;
+}
+
+void TemplateVisibilitySets::deleteVisibility()
+{
+ Q_ASSERT(getNumberOfVisibilitySets() > 1);
+ Q_ASSERT(active_visibility_index < getNumberOfVisibilitySets());
+
+ template_visibility_sets.erase(template_visibility_sets.begin() + active_visibility_index);
+ if (active_visibility_index >= getNumberOfVisibilitySets())
+ --active_visibility_index;
+}
+
+void TemplateVisibilitySets::deleteTemplate(const Template* templ)
+{
+ for (auto& template_set : template_visibility_sets)
+ {
+ template_set.template_visibilities.erase(template_set.findVisibility(templ));
+ }
+}
+
+void TemplateVisibilitySets::addTemplate(const Template *templ, TemplateVisibility vis)
+{
+ for (auto& template_set : template_visibility_sets)
+ {
+ // another check is needed to differentiate between adding a template during loading vs. adding a template by the user
+ if (!template_set.existsVisibility(templ))
+ template_set.template_visibilities.emplace_back(templ, vis);
+ }
+}
+
+TemplateVisibilityEntry TemplateVisibilitySets::getCurrentTemplateVisibility(const Template* templ) const
+{
+ return *(template_visibility_sets.at(active_visibility_index).findVisibility(templ));
+}
+
+TemplateVisibilityEntry TemplateVisibilitySets::getTemplateVisibility(int template_set, int index) const
+{
+ return template_visibility_sets.at(template_set).template_visibilities.at(index);
+}
+
+void TemplateVisibilitySets::setCurrentTemplateVisibility(const Template* templ, TemplateVisibility vis)
+{
+ template_visibility_sets.at(active_visibility_index).findVisibility(templ)->opacity = vis.opacity;
+ template_visibility_sets.at(active_visibility_index).findVisibility(templ)->visible = vis.visible;
+}
+
+bool TemplateVisibilitySets::existsCurrentTemplateVisibility(const Template* templ) const
+{
+ return getCurrentVisibility().existsVisibility(templ);
+}
+
+TemplateVisibilitySet::TemplateVisibilityVector::const_iterator TemplateVisibilitySet::findVisibility(const Template* templ) const
+{
+ return std::find_if(begin(template_visibilities), end(template_visibilities), [templ](const TemplateVisibilityEntry& entry)
+ {
+ return entry.templ == templ;
+ } );
+}
+
+TemplateVisibilitySet::TemplateVisibilityVector::iterator TemplateVisibilitySet::findVisibility(const Template* templ)
+{
+ return std::find_if(begin(template_visibilities), end(template_visibilities), [templ](const TemplateVisibilityEntry& entry)
+ {
+ return entry.templ == templ;
+ } );
+}
+
+bool TemplateVisibilitySet::existsVisibility(const Template* templ) const
+{
+ return end(template_visibilities) != std::find_if(begin(template_visibilities), end(template_visibilities), [templ](const TemplateVisibilityEntry& entry)
+ {
+ return entry.templ == templ;
+ } );
+}
} // namespace OpenOrienteering
diff --git a/src/core/map_view.h b/src/core/map_view.h
index 0f3c342eb..daa374cca 100644
--- a/src/core/map_view.h
+++ b/src/core/map_view.h
@@ -1,6 +1,7 @@
/*
* Copyright 2012, 2013 Thomas Schöps
- * Copyright 2014-2020 Kai Pastor
+ * Copyright 2014-2020, 2025 Kai Pastor
+ * Copyright 2025 Matthias Kühlewein
*
* This file is part of OpenOrienteering.
*
@@ -35,7 +36,6 @@
#include "map_coord.h"
class QLatin1String;
-class QRectF;
class QXmlStreamReader;
class QXmlStreamWriter;
@@ -65,6 +65,68 @@ bool operator==(TemplateVisibility lhs, TemplateVisibility rhs);
bool operator!=(TemplateVisibility lhs, TemplateVisibility rhs);
+class TemplateVisibilityEntry : public TemplateVisibility
+{
+public:
+ TemplateVisibilityEntry() = default;
+ TemplateVisibilityEntry(const TemplateVisibilityEntry&) = default;
+ TemplateVisibilityEntry(TemplateVisibilityEntry&&) = default;
+ TemplateVisibilityEntry& operator=(const TemplateVisibilityEntry&) = default;
+ TemplateVisibilityEntry& operator=(TemplateVisibilityEntry&&) = default;
+ TemplateVisibilityEntry(const Template* templ, TemplateVisibility vis)
+ : TemplateVisibility(vis)
+ , templ(templ)
+ {}
+
+public:
+ const Template* templ;
+};
+
+class TemplateVisibilitySet
+{
+public:
+ TemplateVisibilitySet();
+
+ typedef std::vector TemplateVisibilityVector;
+
+ TemplateVisibilityVector::const_iterator findVisibility(const Template* templ) const;
+ TemplateVisibilityVector::iterator findVisibility(const Template* templ);
+ bool existsVisibility(const Template* templ) const;
+
+ TemplateVisibility map_visibility;
+ TemplateVisibilityVector template_visibilities;
+ bool all_templates_hidden = false;
+};
+
+class TemplateVisibilitySets
+{
+
+public:
+ TemplateVisibilitySets();
+
+ void setVisibility(int current_visibility, TemplateVisibility visibility);
+ void duplicateVisibility();
+ void deleteVisibility();
+ const TemplateVisibilitySet& getCurrentVisibility() const { return template_visibility_sets.at(active_visibility_index); };
+ const TemplateVisibilitySet& getVisibility(int index) const { return template_visibility_sets.at(index); };
+ TemplateVisibilitySet& accessCurrentVisibility() { return template_visibility_sets.at(active_visibility_index); };
+ TemplateVisibilitySet& accessVisibility(int index) { return template_visibility_sets.at(index); };
+ TemplateVisibilityEntry getCurrentTemplateVisibility(const Template* templ) const;
+ TemplateVisibilityEntry getTemplateVisibility(int template_set, int index) const;
+ void setCurrentTemplateVisibility(const Template* templ, TemplateVisibility vis);
+ void deleteTemplate(const Template* templ);
+ void addTemplate(const Template *templ, TemplateVisibility vis);
+ bool existsCurrentTemplateVisibility(const Template* templ) const;
+ void setActiveVisibilityIndex(int active_visibility) { active_visibility_index = active_visibility; };
+ int getActiveVisibilityIndex() const { return active_visibility_index; };
+ int getNumberOfVisibilitySets() const { return template_visibility_sets.size(); };
+
+public:
+ std::vector template_visibility_sets;
+
+private:
+ int active_visibility_index;
+};
/**
* Stores view position, zoom, rotation and grid / template visibilities
@@ -78,7 +140,6 @@ class MapView : public QObject
{
Q_OBJECT
Q_FLAGS(ChangeFlags VisibilityFeature)
-
public:
enum ChangeFlag
{
@@ -278,19 +339,19 @@ class MapView : public QObject
* Checks if the template is visible without creating
* a template visibility object if none exists
*/
- bool isTemplateVisible(const Template* temp) const;
+ bool isTemplateVisible(const Template* templ) const;
/**
* Returns the template visibility.
*
* If the template is unknown, returns default settings.
*/
- TemplateVisibility getTemplateVisibility(const Template* temp) const;
+ TemplateVisibility getTemplateVisibility(const Template* templ) const;
/**
* Sets the template visibility, and emits a change signal.
*/
- void setTemplateVisibility(Template* temp, TemplateVisibility vis);
+ void setTemplateVisibility(Template* templ, TemplateVisibility vis);
/** Enables or disables hiding all templates in this view */
@@ -322,6 +383,12 @@ class MapView : public QObject
*/
bool hasAlpha() const;
+ bool applyVisibilitySet(int new_visibility_set); // private
+ void setVisibilitySet(int new_visibility_set);
+ void addVisibilitySet();
+ void deleteVisibilitySet();
+ int getNumberOfVisibilitySets() const { return template_visibility_sets.getNumberOfVisibilitySets(); };
+ int getActiveVisibilityIndex() const { return template_visibility_sets.getActiveVisibilityIndex(); };
signals:
/**
@@ -341,7 +408,7 @@ class MapView : public QObject
*
* @param feature The map view feature that has changed.
* @param active The features current state of activation.
- * @param temp If a the feature is a template, a pointer to this template.
+ * @param temp If the feature is a template, a pointer to this template.
*/
void visibilityChanged(OpenOrienteering::MapView::VisibilityFeature feature, bool active, OpenOrienteering::Template* temp = nullptr);
@@ -358,48 +425,31 @@ class MapView : public QObject
/**
* Sets the template visibility without emitting signals.
*/
- bool setTemplateVisibilityHelper(const Template *temp, TemplateVisibility vis);
+ bool setTemplateVisibilityHelper(const Template *templ, TemplateVisibility vis);
/**
* Creates a default visibility entry (100% opaque, hidden) before a template is added to the map.
*/
- void onAboutToAddTemplate(int pos, Template* temp);
+ void onAboutToAddTemplate(int pos, Template* templ);
/**
* Changes the visibility entry to 100% visible after a template is added to the map.
*/
- void onTemplateAdded(int pos, Template* temp);
+ void onTemplateAdded(int pos, Template* templ);
/**
* Removes the visibility data when a template is deleted.
*/
- void onTemplateDeleted(int pos, const Template* temp);
+ void onTemplateDeleted(int pos, const Template* templ);
private:
Q_DISABLE_COPY(MapView)
- struct TemplateVisibilityEntry : public TemplateVisibility
- {
- const Template* temp;
- TemplateVisibilityEntry() = default;
- TemplateVisibilityEntry(const TemplateVisibilityEntry&) = default;
- TemplateVisibilityEntry(TemplateVisibilityEntry&&) = default;
- TemplateVisibilityEntry& operator=(const TemplateVisibilityEntry&) = default;
- TemplateVisibilityEntry& operator=(TemplateVisibilityEntry&&) = default;
- TemplateVisibilityEntry(const Template* temp, TemplateVisibility vis)
- : TemplateVisibility(vis)
- , temp(temp)
- {}
- };
- typedef std::vector TemplateVisibilityVector;
-
+ void saveTemplateSet(QXmlStreamWriter& xml, bool template_details, const TemplateVisibilitySet& template_set) const;
+ void loadMapAndTemplates(QXmlStreamReader& xml, TemplateVisibilitySet& template_set, int version);
void updateTransform(); // recalculates the x_to_y matrices
- TemplateVisibilityVector::const_iterator findVisibility(const Template* temp) const;
-
- TemplateVisibilityVector::iterator findVisibility(const Template* temp);
-
Map* map;
@@ -411,10 +461,8 @@ class MapView : public QObject
QTransform view_to_map;
QTransform map_to_view;
- TemplateVisibility map_visibility;
- TemplateVisibilityVector template_visibilities;
+ TemplateVisibilitySets template_visibility_sets;
- bool all_templates_hidden;
bool grid_visible;
bool overprinting_simulation_enabled;
};
@@ -500,18 +548,6 @@ QPoint MapView::panOffset() const
return pan_offset;
}
-inline
-TemplateVisibility MapView::getMapVisibility() const
-{
- return map_visibility;
-}
-
-inline
-bool MapView::areAllTemplatesHidden() const
-{
- return all_templates_hidden;
-}
-
inline
bool MapView::isGridVisible() const
{
@@ -531,4 +567,4 @@ bool MapView::isOverprintingSimulationEnabled() const
Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::MapView::ChangeFlags)
-#endif
+#endif // OPENORIENTEERING_MAP_VIEW_H
diff --git a/src/gui/map/map_editor.cpp b/src/gui/map/map_editor.cpp
index dd85f4146..6476aa49b 100644
--- a/src/gui/map/map_editor.cpp
+++ b/src/gui/map/map_editor.cpp
@@ -1,6 +1,6 @@
/*
* Copyright 2012, 2013 Thomas Schöps
- * Copyright 2012-2021, 2024 Kai Pastor
+ * Copyright 2012-2021, 2024, 2025 Kai Pastor
*
* This file is part of OpenOrienteering.
*
@@ -77,6 +77,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -917,7 +918,6 @@ void MapEditorController::assignKeyboardShortcuts()
findAction("hatchareasview")->setShortcut(QKeySequence(Qt::Key_F2));
findAction("baselineview")->setShortcut(QKeySequence(Qt::Key_F3));
findAction("hidealltemplates")->setShortcut(QKeySequence(Qt::Key_F10));
- findAction("overprintsimulation")->setShortcut(QKeySequence(Qt::Key_F4));
findAction("fullscreen")->setShortcut(QKeySequence(Qt::Key_F11));
tags_window_act->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_6));
color_window_act->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_7));
@@ -2288,6 +2288,9 @@ void MapEditorController::createTemplateWindow()
dock_widget->setVisible(false);
template_dock_widget = dock_widget;
+
+ auto* switch_template_shortcut = new QShortcut(QKeySequence(Qt::Key_F4), window);
+ connect(switch_template_shortcut, &QShortcut::activated, template_list_widget, &TemplateListWidget::switchTemplateSet);
}
}
diff --git a/src/gui/widgets/template_list_widget.cpp b/src/gui/widgets/template_list_widget.cpp
index 5b562c46b..9935fe48e 100644
--- a/src/gui/widgets/template_list_widget.cpp
+++ b/src/gui/widgets/template_list_widget.cpp
@@ -1,6 +1,7 @@
/*
* Copyright 2012, 2013 Thomas Schöps
- * Copyright 2012-2020 Kai Pastor
+ * Copyright 2012-2020, 2025 Kai Pastor
+ * Copyright 2025 Matthias Kühlewein
*
* This file is part of OpenOrienteering.
*
@@ -22,7 +23,6 @@
#include "template_list_widget.h"
#include
-#include
#include
#include
@@ -34,12 +34,15 @@
#include
#include
#include
+#include
#include
+#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -55,7 +58,9 @@
#include
#include
#include
+#include
#include
+#include
#include
#include
#include
@@ -145,6 +150,7 @@ TemplateListWidget::TemplateListWidget(Map& map, MapView& main_view, MapEditorCo
, main_view(main_view)
, controller(controller)
, mobile_mode(controller.isInMobileMode())
+, max_template_sets(9)
{
setWhatsThis(Util::makeWhatThis("templates.html#setup"));
@@ -314,7 +320,8 @@ TemplateListWidget::TemplateListWidget(Map& map, MapView& main_view, MapEditorCo
style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2,
0, // Covered by the main layout's spacing.
style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2,
- style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2
+ //style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2
+ 0
);
all_buttons_layout->addWidget(list_buttons_group);
all_buttons_layout->addWidget(new QLabel(QString::fromLatin1(" ")), 1);
@@ -327,7 +334,49 @@ TemplateListWidget::TemplateListWidget(Map& map, MapView& main_view, MapEditorCo
connect(help_button, &QAbstractButton::clicked, this, &TemplateListWidget::showHelp);
}
- all_templates_layout->addLayout(all_buttons_layout);
+ // Template sets layout
+ auto* set_change_layout = new SegmentedButtonLayout();
+ new_template_set_button = createToolButton(QIcon(QString::fromLatin1(":/images/template-set-add.png")), tr("Add"));
+ set_change_layout->addWidget(new_template_set_button);
+ delete_template_set_button = createToolButton(QIcon(QString::fromLatin1(":/images/template-set-delete.png")), tr("Remove"));
+ set_change_layout->addWidget(delete_template_set_button);
+
+ auto* set_selection_layout = new SegmentedButtonLayout();
+ auto* group = new QButtonGroup(this);
+ template_set_buttons.reserve(max_template_sets);
+ for (int i = 0; i < max_template_sets; ++i)
+ {
+ auto* template_set_button = new QPushButton(tr("%1").arg(i+1));
+ const auto textSize = template_set_button->fontMetrics().size(Qt::TextShowMnemonic, template_set_button->text());
+ QStyleOptionButton opt;
+ opt.initFrom(template_set_button);
+ opt.rect.setSize(textSize);
+ template_set_button->setMinimumSize(template_set_button->style()->sizeFromContents(QStyle::CT_PushButton, &opt, textSize, template_set_button));
+ template_set_button->setAutoFillBackground(true);
+ template_set_buttons.push_back(template_set_button);
+ group->addButton(template_set_button, i);
+ set_selection_layout->addWidget(template_set_button);
+ }
+ updateTemplateSetButtons();
+
+ // The template set buttons row layout
+ auto* template_set_buttons_layout = new QHBoxLayout();
+ //template_set_buttons_layout->setContentsMargins(0,0,0,0);
+ template_set_buttons_layout->setContentsMargins(
+ style()->pixelMetric(QStyle::PM_LayoutLeftMargin, &style_option) / 2,
+ 0, // Covered by the main layout's spacing.
+ style()->pixelMetric(QStyle::PM_LayoutRightMargin, &style_option) / 2,
+ style()->pixelMetric(QStyle::PM_LayoutBottomMargin, &style_option) / 2
+ );
+ template_set_buttons_layout->addLayout(set_change_layout);
+ template_set_buttons_layout->addLayout(set_selection_layout);
+ template_set_buttons_layout->addStretch();
+
+ auto* two_rows_layout = new QVBoxLayout();
+ two_rows_layout->addLayout(all_buttons_layout);
+ two_rows_layout->addLayout(template_set_buttons_layout);
+
+ all_templates_layout->addLayout(two_rows_layout);
setLayout(all_templates_layout);
@@ -361,6 +410,14 @@ TemplateListWidget::TemplateListWidget(Map& map, MapView& main_view, MapEditorCo
connect(adjust_button, &QAbstractButton::clicked, this, &TemplateListWidget::adjustClicked);
connect(position_action, &QAction::triggered, this, &TemplateListWidget::positionClicked);
+ connect(new_template_set_button, &QAbstractButton::clicked, this, &TemplateListWidget::addTemplateSet);
+ connect(delete_template_set_button, &QAbstractButton::clicked, this, &TemplateListWidget::deleteTemplateSet);
+#if QT_VERSION < 0x051500
+ connect(group, QOverload::of(&QButtonGroup::buttonClicked), this, &TemplateListWidget::onGroupButtonClicked);
+#else
+ connect(group, &QButtonGroup::idClicked, this, &TemplateListWidget::onGroupButtonClicked);
+#endif
+
//connect(group_button, SIGNAL(clicked(bool)), this, &TemplateListWidget::groupClicked);
//connect(more_button_menu, SIGNAL(triggered(QAction*)), this, SLOT(moreActionClicked(QAction*)));
@@ -687,6 +744,57 @@ void TemplateListWidget::newTemplate(QAction* action)
}
#endif
+void TemplateListWidget::updateTemplateSetButtons()
+{
+ const auto template_sets = main_view.getNumberOfVisibilitySets();
+ const auto current_template_set = main_view.getActiveVisibilityIndex();
+ delete_template_set_button->setEnabled(template_sets > 1);
+ new_template_set_button->setEnabled(template_sets < max_template_sets);
+ for (int i = 0; i < max_template_sets; ++i)
+ {
+ template_set_buttons.at(i)->setVisible(template_sets > i);
+ if (i < template_sets)
+ {
+ auto pal = template_set_buttons.at(i)->palette();
+ pal.setColor(QPalette::Button, i == current_template_set ? QColor(Qt::green) : QColor(Qt::lightGray));
+ template_set_buttons.at(i)->setPalette(pal);
+ }
+ }
+}
+
+// slots:
+void TemplateListWidget::addTemplateSet()
+{
+ main_view.addVisibilitySet();
+ updateTemplateSetButtons();
+}
+
+void TemplateListWidget::deleteTemplateSet()
+{
+ main_view.deleteVisibilitySet();
+ updateTemplateSetButtons();
+ template_table->viewport()->update();
+}
+
+void TemplateListWidget::switchTemplateSet()
+{
+ auto template_set = main_view.getActiveVisibilityIndex() + 1;
+ if (template_set >= main_view.getNumberOfVisibilitySets())
+ template_set = 0;
+
+ onGroupButtonClicked(template_set);
+}
+
+void TemplateListWidget::onGroupButtonClicked(int val)
+{
+ if (main_view.getActiveVisibilityIndex() != val)
+ {
+ main_view.setVisibilitySet(val);
+ updateTemplateSetButtons();
+ template_table->viewport()->update();
+ }
+}
+
void TemplateListWidget::openTemplate()
{
auto new_template = showOpenTemplateDialog(window(), controller);
@@ -718,7 +826,6 @@ void TemplateListWidget::duplicateTemplate()
Q_ASSERT(row >= 0);
int pos = posFromRow(row);
Q_ASSERT(pos >= 0);
-
const auto* prototype = map.getTemplate(pos);
const auto visibility = main_view.getTemplateVisibility(prototype);
diff --git a/src/gui/widgets/template_list_widget.h b/src/gui/widgets/template_list_widget.h
index 85505d44d..458dec22d 100644
--- a/src/gui/widgets/template_list_widget.h
+++ b/src/gui/widgets/template_list_widget.h
@@ -1,6 +1,7 @@
/*
* Copyright 2012, 2013 Thomas Schöps
- * Copyright 2020 Kai Pastor
+ * Copyright 2020, 2025 Kai Pastor
+ * Copyright 2025 Matthias Kühlewein
*
* This file is part of OpenOrienteering.
*
@@ -23,11 +24,13 @@
#define OPENORIENTEERING_TEMPLATE_LIST_WIDGET_H
#include
+#include
#include
-#include
#include
#include
+#include
+#include
#include "core/map_view.h"
@@ -36,9 +39,9 @@ class QBoxLayout;
class QCheckBox;
class QEvent;
class QModelIndex;
+class QPushButton;
class QTableView;
class QToolButton;
-class QVariant;
namespace OpenOrienteering {
@@ -75,6 +78,9 @@ class TemplateListWidget : public QWidget
void closePositionDockWidget();
void closeClicked();
+public slots:
+ void switchTemplateSet();
+
protected:
TemplateTableModel* model();
const TemplateTableModel* model() const { return static_cast(const_cast(this)->model()); }
@@ -114,6 +120,8 @@ class TemplateListWidget : public QWidget
*/
bool eventFilter(QObject* watched, QEvent* event) override;
+private:
+ void updateTemplateSetButtons();
// slots:
#if 0
@@ -142,6 +150,10 @@ class TemplateListWidget : public QWidget
void showOpacitySlider(int row);
+ void addTemplateSet();
+ void deleteTemplateSet();
+ void onGroupButtonClicked(int val);
+
private:
Map& map;
MapView& main_view;
@@ -151,6 +163,7 @@ class TemplateListWidget : public QWidget
Template* last_template = nullptr;
int last_row = -1;
+ const int max_template_sets;
QCheckBox* all_hidden_check;
QTableView* template_table;
@@ -172,6 +185,10 @@ class TemplateListWidget : public QWidget
QToolButton* adjust_button;
QToolButton* edit_button;
+ QToolButton* new_template_set_button;
+ QToolButton* delete_template_set_button;
+ std::vector template_set_buttons;
+
//QToolButton* group_button;
//QToolButton* more_button;
};
@@ -179,4 +196,4 @@ class TemplateListWidget : public QWidget
} // namespace OpenOrienteering
-#endif
+#endif // OPENORIENTEERING_TEMPLATE_LIST_WIDGET_H