11/*
2- * Copyright 2017-2024 Kai Pastor
2+ * Copyright 2017-2020, 2024, 2025 Kai Pastor
33 *
44 * This file is part of OpenOrienteering.
55 *
2222
2323#include < functional>
2424
25- #include < QAction>
2625#include < QAbstractButton>
26+ #include < QAction>
2727#include < QDialog>
2828#include < QDialogButtonBox>
2929#include < QGridLayout>
3535
3636#include " core/map.h"
3737#include " core/map_part.h"
38+ #include " core/objects/object.h"
3839#include " core/objects/object_query.h"
40+ #include " core/symbols/symbol.h"
3941#include " gui/main_window.h"
4042#include " gui/util_gui.h"
4143#include " gui/map/map_editor.h"
4446
4547namespace OpenOrienteering {
4648
47- class Object ;
49+ namespace {
50+
51+ // Returns true if an object can be added to the selection.
52+ bool isSelectable (const Object* object)
53+ {
54+ const auto * symbol = object ? object->getSymbol () : nullptr ;
55+ return symbol && !symbol->isHidden () && !symbol->isProtected ();
56+ }
57+
58+ } // namespace
59+
4860
4961MapFindFeature::MapFindFeature (MapEditorController& controller)
5062: QObject{nullptr }
@@ -117,7 +129,7 @@ void MapFindFeature::showDialog()
117129
118130 auto button_box = new QDialogButtonBox (QDialogButtonBox::Close | QDialogButtonBox::Help);
119131 connect (button_box, &QDialogButtonBox::rejected, &*find_dialog, &QDialog::hide);
120- connect (button_box-> button (QDialogButtonBox::Help) , &QPushButton::clicked , this , &MapFindFeature::showHelp);
132+ connect (button_box, &QDialogButtonBox::helpRequested , this , &MapFindFeature::showHelp);
121133
122134 editor_stack = new QStackedLayout ();
123135 editor_stack->addWidget (text_edit);
@@ -166,52 +178,54 @@ ObjectQuery MapFindFeature::makeQuery() const
166178 query = tag_selector->makeQuery ();
167179 }
168180 }
181+ if (!query)
182+ {
183+ controller.getMap ()->clearObjectSelection (true );
184+ controller.getWindow ()->showStatusBarMessage (OpenOrienteering::TagSelectWidget::tr (" Invalid query" ), 2000 );
185+ }
169186 return query;
170187}
171188
172189
173190void MapFindFeature::findNext ()
174191{
175- auto map = controller.getMap ();
176- auto first_object = map->getFirstSelectedObject ();
192+ if (auto query = makeQuery ())
193+ findNextMatchingObject (controller, query);
194+ }
195+
196+ // static
197+ void MapFindFeature::findNextMatchingObject (MapEditorController& controller, const ObjectQuery& query)
198+ {
199+ auto * map = controller.getMap ();
200+
201+ Object* first_match = nullptr ; // the first match in all objects
202+ Object* pivot_object = map->getFirstSelectedObject ();
203+ Object* next_match = nullptr ; // the next match after pivot_object
177204 map->clearObjectSelection (false );
178205
179- Object* next_object = nullptr ;
180- auto query = makeQuery ();
181- if (!query)
182- {
183- if (auto window = controller.getWindow ())
184- window->showStatusBarMessage (OpenOrienteering::TagSelectWidget::tr (" Invalid query" ), 2000 );
185- return ;
186- }
206+ auto search = [&](Object* object) {
207+ if (next_match)
208+ return ;
187209
188- auto search = [&first_object, &next_object, &query](Object* object) {
189- if (!next_object)
210+ bool after_pivot = (pivot_object == nullptr );
211+ if (object == pivot_object)
212+ pivot_object = nullptr ;
213+
214+ if (isSelectable (object) && query (object))
190215 {
191- if (first_object)
192- {
193- if (object == first_object)
194- first_object = nullptr ;
195- }
196- else if (query (object))
197- {
198- next_object = object;
199- }
216+ if (after_pivot)
217+ next_match = object;
218+ else if (!first_match)
219+ first_match = object;
200220 }
201221 };
202222
203- // Start from selected object
204223 map->getCurrentPart ()->applyOnAllObjects (search);
205- if (!next_object)
206- {
207- // Start from first object
208- first_object = nullptr ;
209- map->getCurrentPart ()->applyOnAllObjects (search);
210- }
224+ if (!next_match)
225+ next_match = first_match;
226+ if (next_match)
227+ map->addObjectToSelection (next_match, false );
211228
212- map->clearObjectSelection (false );
213- if (next_object)
214- map->addObjectToSelection (next_object, false );
215229 map->emitSelectionChanged ();
216230 map->ensureVisibilityOfSelectedObjects (Map::FullVisibility);
217231
@@ -221,20 +235,22 @@ void MapFindFeature::findNext()
221235
222236
223237void MapFindFeature::findAll ()
238+ {
239+ if (auto query = makeQuery ())
240+ findAllMatchingObjects (controller, query);
241+ }
242+
243+ // static
244+ void MapFindFeature::findAllMatchingObjects (MapEditorController& controller, const ObjectQuery& query)
224245{
225246 auto map = controller.getMap ();
226247 map->clearObjectSelection (false );
227248
228- auto query = makeQuery ();
229- if (!query)
230- {
231- controller.getWindow ()->showStatusBarMessage (OpenOrienteering::TagSelectWidget::tr (" Invalid query" ), 2000 );
232- return ;
233- }
234-
235249 map->getCurrentPart ()->applyOnMatchingObjects ([map](Object* object) {
236- map->addObjectToSelection (object, false );
250+ if (isSelectable (object))
251+ map->addObjectToSelection (object, false );
237252 }, std::cref (query));
253+
238254 map->emitSelectionChanged ();
239255 map->ensureVisibilityOfSelectedObjects (Map::FullVisibility);
240256 controller.getWindow ()->showStatusBarMessage (OpenOrienteering::TagSelectWidget::tr (" %n object(s) selected" , nullptr , map->getNumSelectedObjects ()), 2000 );
@@ -244,15 +260,12 @@ void MapFindFeature::findAll()
244260}
245261
246262
247-
248263void MapFindFeature::showHelp () const
249264{
250265 Util::showHelp (controller.getWindow (), " find_objects.html" );
251266}
252267
253268
254-
255- // slot
256269void MapFindFeature::tagSelectorToggled (bool active)
257270{
258271 editor_stack->setCurrentIndex (active ? 1 : 0 );
0 commit comments