Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions src/core/map_part.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright 2012, 2013 Thomas Schöps
* Copyright 2012-2017 Kai Pastor
* Copyright 2012-2020, 2025 Kai Pastor
*
* This file is part of OpenOrienteering.
*
Expand Down Expand Up @@ -47,6 +47,7 @@ namespace literal
const QLatin1String objects("objects");
const QLatin1String object("object");
const QLatin1String count("count");
const QLatin1String hidden("hidden");
}


Expand Down Expand Up @@ -75,12 +76,26 @@ void MapPart::setName(const QString& new_name)
emit map->mapPartChanged(map->findPartIndex(this), this);
}


void MapPart::setVisible(bool visible)
{
if (this->visible == visible)
return;

this->visible = visible;
if (map)
{
emit map->mapPartChanged(map->findPartIndex(this), this);
applyOnAllObjects([visible](Object* o) {
o->setVisible(visible);
});
}
}

void MapPart::save(QXmlStreamWriter& xml) const
{
XmlElementWriter part_element(xml, literal::part);
part_element.writeAttribute(literal::name, name);
part_element.writeAttribute(literal::hidden, !visible);
{
XmlElementWriter objects_element(xml, literal::objects);
objects_element.writeAttribute(literal::count, objects.size());
Expand All @@ -99,6 +114,7 @@ MapPart* MapPart::load(QXmlStreamReader& xml, Map& map, SymbolDictionary& symbol

XmlElementReader part_element(xml);
auto part = new MapPart(part_element.attribute<QString>(literal::name), &map);
part->visible = !part_element.attribute<bool>(literal::hidden);

while (xml.readNextStartElement())
{
Expand All @@ -113,9 +129,14 @@ MapPart* MapPart::load(QXmlStreamReader& xml, Map& map, SymbolDictionary& symbol
while (xml.readNextStartElement())
{
if (xml.name() == literal::object)
{
part->objects.push_back(Object::load(xml, &map, symbol_dict));
part->objects.back()->setVisible(part->visible);
}
else
{
xml.skipCurrentElement(); // unknown
}
}
}
else
Expand Down Expand Up @@ -150,6 +171,7 @@ void MapPart::setObject(Object* object, int pos, bool delete_old)
delete objects[pos];

objects[pos] = object;
object->setVisible(visible);
object->setMap(map);
object->update();
map->setObjectsDirty(); // TODO: remove from here, dirty state handling should be separate
Expand All @@ -163,6 +185,7 @@ void MapPart::addObject(Object* object)
void MapPart::addObject(Object* object, int pos)
{
objects.insert(objects.begin() + pos, object);
object->setVisible(visible);
object->setMap(map);
object->update();

Expand Down Expand Up @@ -227,6 +250,7 @@ std::unique_ptr<UndoStep> MapPart::importPart(const MapPart* other, const QHash<
new_object->transform(transform);

objects.push_back(new_object);
new_object->setVisible(visible);
new_object->setMap(map);
new_object->update();

Expand Down
22 changes: 15 additions & 7 deletions src/core/map_part.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright 2012, 2013 Thomas Schöps
* Copyright 2012-2017 Kai Pastor
* Copyright 2012-2020, 2025 Kai Pastor
*
* This file is part of OpenOrienteering.
*
Expand All @@ -25,8 +25,8 @@
#include <cstddef>
#include <functional>
#include <memory>
#include <vector>
#include <utility>
#include <vector>

#include <QHash>
#include <QRectF>
Expand Down Expand Up @@ -62,8 +62,6 @@ using SelectionInfoVector = std::vector<std::pair<int, Object*>> ;
* a map part for event-specific map objects and parts for course-specific
* map objects. Then a course can be printed by merging the event-specific part
* with the part for the course.
*
* Currently, only one map part can be used per map.
*/
class MapPart
{
Expand Down Expand Up @@ -106,6 +104,15 @@ class MapPart
*/
void setName(const QString& new_name);

/**
* Returns the part's visibility.
*/
bool isVisible() const { return visible; };

/**
* Sets the part's visibility.
*/
void setVisible(bool visible);

/**
* Returns the number of objects in the part.
Expand Down Expand Up @@ -155,12 +162,12 @@ class MapPart
void addObject(Object* object, int pos);

/**
* Deleted the object from the given index.
* Deletes the object from the given index.
*/
void deleteObject(int pos);

/**
* Deleted the object from the given index.
* Deletes the object from the given index.
*
* Returns if the object was found in this part.
*/
Expand Down Expand Up @@ -269,6 +276,7 @@ class MapPart
QString name;
ObjectList objects; ///< @todo This could be a spatial representation optimized for quick access
Map* const map;
bool visible = true; ///< Visibility of the part's objects.
};


Expand Down Expand Up @@ -302,4 +310,4 @@ const Object* MapPart::getObject(int i) const

} // namespace OpenOrienteering

#endif
#endif // OPENORIENTEERING_MAP_PART_H
34 changes: 32 additions & 2 deletions src/core/objects/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,36 @@ void Object::setRotation(qreal new_rotation)
}


void Object::setVisible(bool visible)
{
if (!map)
{
this->visible = visible;
return;
}

if (extent.isValid() && isVisible())
{
map->setObjectAreaDirty(extent);
}

this->visible = visible;
if (!visible)
{
map->removeRenderablesOfObject(this, true);
}
else if (output_dirty || !extent.isValid())
{
forceUpdate();
}
else
{
map->insertRenderablesOfObject(this);
map->setObjectAreaDirty(extent);
}
}


void Object::forceUpdate() const
{
output_dirty = true;
Expand All @@ -486,7 +516,7 @@ bool Object::update() const
if (map)
{
options = QFlag(map->renderableOptions());
if (extent.isValid())
if (extent.isValid() && isVisible())
map->setObjectAreaDirty(extent);
}

Expand All @@ -501,7 +531,7 @@ bool Object::update() const
Q_ASSERT(extent.right() < 60000000); // assert if bogus values are returned
output_dirty = false;

if (map)
if (map && isVisible())
{
map->insertRenderablesOfObject(this);
if (extent.isValid())
Expand Down
13 changes: 13 additions & 0 deletions src/core/objects/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,18 @@ friend class XMLImportExport;
void setRotation(qreal new_rotation);


/**
* Changes the visibility of an object.
*
* Even when linked to a map, an object may be invisible when it is in a
* hidden map part.
*/
void setVisible(bool visible);

/** Returns the object's visibility. */
bool isVisible() const noexcept { return visible; }


/**
* If the output_dirty flag is set, regenerates output and extent, and updates the object's map (if set).
*
Expand Down Expand Up @@ -331,6 +343,7 @@ friend class XMLImportExport;
private:
qreal rotation = 0; ///< The object's rotation (in radians).
mutable bool output_dirty = true; // does the output have to be re-generated because of changes?
bool visible = true; ///< The object's renderables are in a visible map part.
mutable QRectF extent; // only valid after calling update()
mutable ObjectRenderables output; // only valid after calling update()
};
Expand Down
26 changes: 21 additions & 5 deletions src/gui/map/map_editor.cpp
Original file line number Diff line number Diff line change
@@ -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.
*
Expand Down Expand Up @@ -258,6 +258,7 @@ MapEditorController::MapEditorController(OperatingMode mode, Map* map, MapView*
, template_list_widget(nullptr)
, mappart_remove_act(nullptr)
, mappart_merge_act(nullptr)
, mappart_visibility_act(nullptr)
, mappart_merge_menu(nullptr)
, mappart_move_menu(nullptr)
, mappart_selector_box(nullptr)
Expand Down Expand Up @@ -470,6 +471,7 @@ void MapEditorController::setEditingInProgress(bool value)
mappart_add_act->setEnabled(!editing_in_progress);
mappart_rename_act->setEnabled(!editing_in_progress && num_parts > 0);
mappart_remove_act->setEnabled(!editing_in_progress && num_parts > 1);
mappart_visibility_act->setEnabled(!editing_in_progress && num_parts > 0);
mappart_move_menu->setEnabled(!editing_in_progress && num_parts > 1);
mappart_merge_act->setEnabled(!editing_in_progress && num_parts > 1);
mappart_merge_menu->setEnabled(!editing_in_progress && num_parts > 1);
Expand Down Expand Up @@ -1103,8 +1105,9 @@ void MapEditorController::createActions()
mappart_add_act = newAction("addmappart", tr("Add new part..."), this, SLOT(addMapPart()));
mappart_rename_act = newAction("renamemappart", tr("Rename current part..."), this, SLOT(renameMapPart()));
mappart_remove_act = newAction("removemappart", tr("Remove current part"), this, SLOT(removeMapPart()));
mappart_visibility_act = newAction("visibilitymappart", {/* set in updateMapPartsUI() */} , this, SLOT(toggleMapPartVisible()));
mappart_merge_act = newAction("mergemapparts", tr("Merge all parts"), this, SLOT(mergeAllMapParts()));

import_act = newAction("import", tr("Import..."), this, SLOT(importClicked()), nullptr, QString{}, "file_menu.html");

map_coordinates_act = new QAction(tr("Map coordinates"), this);
Expand Down Expand Up @@ -1264,6 +1267,7 @@ void MapEditorController::createMenuAndToolbars()
map_menu->addAction(mappart_add_act);
map_menu->addAction(mappart_rename_act);
map_menu->addAction(mappart_remove_act);
map_menu->addAction(mappart_visibility_act);
map_menu->addMenu(mappart_move_menu);
map_menu->addMenu(mappart_merge_menu);
map_menu->addAction(mappart_merge_act);
Expand Down Expand Up @@ -2612,7 +2616,7 @@ void MapEditorController::updateObjectDependentActions()
scale_act->setStatusTip(tr("Scale the selected objects.") + (scale_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one object to activate this tool."))));
mappart_move_menu->setEnabled(have_selection && have_multiple_parts);

// have_rotatable_pattern || have_rotatable_point
// have_rotatable_pattern || have_rotatable_object
rotate_pattern_act->setEnabled(have_rotatable_pattern || have_rotatable_object);
rotate_pattern_act->setStatusTip(tr("Set the direction of area fill patterns or point objects.") + (rotate_pattern_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select an area object with rotatable fill pattern or a rotatable point object to activate this tool."))));

Expand All @@ -2622,7 +2626,7 @@ void MapEditorController::updateObjectDependentActions()
connect_paths_act->setEnabled(have_line);
connect_paths_act->setStatusTip(tr("Connect endpoints of paths which are close together.") + (connect_paths_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one line object to activate this tool."))));

// have_are || have_line
// have_area || have_line
cut_tool_act->setEnabled(have_area || have_line);
cut_tool_act->setStatusTip(tr("Cut the selected objects into smaller parts.") + (cut_tool_act->isEnabled() ? QString{} : QString(QLatin1Char(' ') + tr("Select at least one line or area object to activate this tool."))));
convert_to_curves_act->setEnabled(have_area || have_line);
Expand Down Expand Up @@ -3807,6 +3811,11 @@ void MapEditorController::updateMapPartsUI()
{
toolbar_mapparts->setVisible(have_multiple_parts);
}
if (mappart_visibility_act && count)
{
MapPart* const part = map->getCurrentPart();
mappart_visibility_act->setText(part->isVisible() ? tr("Hide current part") : tr("Show current part"));
}

if (count > 0)
{
Expand Down Expand Up @@ -3973,6 +3982,7 @@ void MapEditorController::mergeCurrentMapPartTo(int target)
void MapEditorController::mergeAllMapParts()
{
QString const name = map->getCurrentPart()->getName();
const auto visibility = map->getCurrentPart()->isVisible();
const QMessageBox::StandardButton button =
QMessageBox::question(
window,
Expand All @@ -3985,7 +3995,7 @@ void MapEditorController::mergeAllMapParts()
auto* undo = new CombinedUndoStep(map);

// For simplicity, we merge to the first part,
// but keep the properties (i.e. name) of the current part.
// but keep the properties (i.e. name, visibility) of the current part.
map->setCurrentPartIndex(0);
MapPart* target_part = map->getPart(0);

Expand All @@ -4002,11 +4012,17 @@ void MapEditorController::mergeAllMapParts()

undo->push(new MapPartUndoStep(map, MapPartUndoStep::ModifyMapPart, 0));
target_part->setName(name);
target_part->setVisible(visibility);

map->push(undo);
}
}

void MapEditorController::toggleMapPartVisible()
{
MapPart* const part = map->getCurrentPart();
part->setVisible(!part->isVisible());
}

void MapEditorController::templateAdded(int /*pos*/, const Template* /*temp*/)
{
Expand Down
Loading