1 //
2 // C++ Implementation: FeaturesDock
3 //
4 // Description:
5 //
6 //
7 // Author: cbro <cbro@semperpax.com>, (C) 2009
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12 #include "FeaturesDock.h"
13 
14 #include "MainWindow.h"
15 #include "MapView.h"
16 #include "Document.h"
17 #include "ImageMapLayer.h"
18 #include "PropertiesDock.h"
19 #include "Global.h"
20 
21 #include "Features.h"
22 
23 #include "SelectionDialog.h"
24 #include "TagSelector.h"
25 
26 #include <QAction>
27 #include <QTimer>
28 #include <QMenu>
29 
30 #define MAX_FEATS 100
31 
FeaturesDock(MainWindow * aParent)32 FeaturesDock::FeaturesDock(MainWindow* aParent)
33     : MDockAncestor(aParent),
34     Main(aParent),
35     curFeatType(IFeature::OsmRelation),
36     findMode(false)
37 {
38 //    setMinimumSize(220,100);
39     setObjectName("FeaturesDock");
40 
41     ui.setupUi(getWidget());
42 
43 #ifdef Q_OS_MAC
44     ui.cbWithin->setMinimumWidth(30);
45 #endif
46 
47     ui.cbWithin->setChecked(M_PREFS->getFeaturesWithin());
48     ui.FeaturesList->sortItems();
49 
50     connect(this, SIGNAL(visibilityChanged(bool)), this, SLOT(on_Viewport_changed(bool)));
51 
52     deleteAction = new QAction(NULL, this);
53 //    deleteAction->setShortcut(QKeySequence::Delete);
54     ui.FeaturesList->addAction(deleteAction);
55     connect(deleteAction, SIGNAL(triggered()), SLOT(on_FeaturesList_delete()));
56 
57     connect(ui.FeaturesList, SIGNAL(itemSelectionChanged()), this, SLOT(on_FeaturesList_itemSelectionChanged()));
58     connect(ui.FeaturesList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(on_FeaturesList_itemDoubleClicked(QListWidgetItem*)));
59     connect(ui.FeaturesList, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(on_FeaturesList_customContextMenuRequested(const QPoint &)));
60 
61     connect(ui.cbWithin, SIGNAL(stateChanged(int)), this, SLOT(on_rbWithin_stateChanged(int)));
62     connect(ui.cbSelectionFilter, SIGNAL(stateChanged(int)), this, SLOT(on_rbSelectionFilter_stateChanged(int)));
63     connect(ui.btFind, SIGNAL(clicked(bool)), SLOT(on_btFind_clicked(bool)));
64     connect(ui.btReset, SIGNAL(clicked(bool)), SLOT(on_btReset_clicked(bool)));
65 
66     connect(Main->properties(), SIGNAL(selectionChanged()), this, SLOT(updateList()));
67 
68     centerAction = new QAction(NULL, this);
69     connect(centerAction, SIGNAL(triggered()), this, SLOT(on_centerAction_triggered()));
70     centerZoomAction = new QAction(NULL, this);
71     connect(centerZoomAction, SIGNAL(triggered()), this, SLOT(on_centerZoomAction_triggered()));
72     downloadAction = new QAction(NULL, this);
73     connect(downloadAction, SIGNAL(triggered()), this, SLOT(on_downloadAction_triggered()));
74     addSelectAction = new QAction(NULL, this);
75     connect(addSelectAction, SIGNAL(triggered()), this, SLOT(on_addSelectAction_triggered()));
76 
77     int t;
78     t = ui.tabBar->addTab(NULL);
79     ui.tabBar->setTabData(t, IFeature::OsmRelation);
80     t = ui.tabBar->addTab(NULL);
81     ui.tabBar->setTabData(t, IFeature::LineString);
82     t = ui.tabBar->addTab(NULL);
83     ui.tabBar->setTabData(t, IFeature::Point);
84     t = ui.tabBar->addTab(NULL);
85     ui.tabBar->setTabData(t, IFeature::All);
86     ui.tabBar->setElideMode(Qt::ElideRight);
87     ui.tabBar->setUsesScrollButtons(true);
88     retranslateTabBar();
89 
90     connect(ui.tabBar, SIGNAL(currentChanged (int)), this, SLOT(tabChanged(int)));
91     //connect(ui.tabBar, SIGNAL(customContextMenuRequested (const QPoint&)), this, SLOT(tabContextMenuRequested(const QPoint&)));
92 
93     retranslateUi();
94 }
95 
~FeaturesDock()96 FeaturesDock::~FeaturesDock()
97 {
98 }
99 
on_FeaturesList_itemSelectionChanged()100 void FeaturesDock::on_FeaturesList_itemSelectionChanged()
101 {
102     Highlighted.clear();
103     for (int i=0; i<ui.FeaturesList->selectedItems().count(); ++i) {
104         QListWidgetItem* item = ui.FeaturesList->selectedItems()[i];
105         Feature * F = item->data(Qt::UserRole).value<Feature*>();
106         Highlighted.push_back(F);
107     }
108 
109     Main->view()->update();
110 }
111 
on_FeaturesList_itemDoubleClicked(QListWidgetItem * item)112 void FeaturesDock::on_FeaturesList_itemDoubleClicked(QListWidgetItem* item)
113 {
114     Feature * F = item->data(Qt::UserRole).value<Feature*>();
115     Main->properties()->setSelection(F);
116     Main->view()->update();
117 }
118 
on_FeaturesList_customContextMenuRequested(const QPoint & pos)119 void FeaturesDock::on_FeaturesList_customContextMenuRequested(const QPoint & pos)
120 {
121     QListWidgetItem *it = ui.FeaturesList->itemAt(pos);
122     if (!it)
123         return;
124 
125     QMenu menu(ui.FeaturesList);
126     menu.addAction(addSelectAction);
127     menu.addAction(deleteAction);
128     menu.addSeparator();
129     menu.addAction(centerAction);
130     menu.addAction(centerZoomAction);
131     menu.addSeparator();
132 
133     downloadAction->setEnabled(false);
134     Feature* F;
135     for (int i=0; i < ui.FeaturesList->selectedItems().count(); ++i) {
136         F = ui.FeaturesList->selectedItems()[i]->data(Qt::UserRole).value<Feature*>();
137         if (F->notEverythingDownloaded()) {
138             downloadAction->setEnabled(true);
139             break;
140         }
141     }
142     menu.addAction(downloadAction);
143     menu.exec(ui.FeaturesList->mapToGlobal(pos));
144 }
145 
on_FeaturesList_delete()146 void FeaturesDock::on_FeaturesList_delete()
147 {
148     if (!ui.FeaturesList->selectedItems().count())
149         return;
150 
151     Feature* F;
152     Main->view()->blockSignals(true);
153 
154     Highlighted.clear();
155     Main->properties()->setSelection(0);
156     for (int i=0; i < ui.FeaturesList->selectedItems().count(); ++i) {
157         F = ui.FeaturesList->selectedItems()[i]->data(Qt::UserRole).value<Feature*>();
158         if (F) {
159             Main->properties()->addSelection(F);
160         }
161     }
162 
163     Main->view()->blockSignals(false);
164 #ifndef _MOBILE
165     Main->on_editRemoveAction_triggered();
166 #endif
167 }
168 
on_rbWithin_stateChanged(int state)169 void FeaturesDock::on_rbWithin_stateChanged ( int state )
170 {
171     M_PREFS->setFeaturesWithin((state == Qt::Checked));
172 
173     updateList();
174 }
175 
on_rbSelectionFilter_stateChanged(int state)176 void FeaturesDock::on_rbSelectionFilter_stateChanged ( int state )
177 {
178     M_PREFS->setFeaturesSelectionFilter((state == Qt::Checked));
179 
180     updateList();
181 }
182 
on_centerAction_triggered()183 void FeaturesDock::on_centerAction_triggered()
184 {
185     Feature* F;
186     CoordBox cb;
187 
188     Main->view()->blockSignals(true);
189 
190     for (int i=0; i < ui.FeaturesList->selectedItems().count(); ++i) {
191         F = ui.FeaturesList->selectedItems()[i]->data(Qt::UserRole).value<Feature*>();
192         if (F) {
193             if (cb.isNull())
194                 cb = F->boundingBox();
195             else
196                 cb.merge(F->boundingBox());
197         }
198     }
199     if (!cb.isNull()) {
200         Coord c = cb.center();
201         Main->view()->setCenter(c, Main->view()->rect());
202         Main->invalidateView();
203     }
204     Main->view()->blockSignals(false);
205 
206     QTimer::singleShot(10, this, SLOT(on_Viewport_changed(true)));
207 }
208 
on_centerZoomAction_triggered()209 void FeaturesDock::on_centerZoomAction_triggered()
210 {
211     Feature* F;
212     CoordBox cb;
213 
214     Main->view()->blockSignals(true);
215 
216     for (int i=0; i < ui.FeaturesList->selectedItems().count(); ++i) {
217         F = ui.FeaturesList->selectedItems()[i]->data(Qt::UserRole).value<Feature*>();
218         if (F) {
219             if (cb.isNull())
220                 cb = F->boundingBox();
221             else
222                 cb.merge(F->boundingBox());
223         }
224     }
225     if (!cb.isNull()) {
226         CoordBox mini(cb.center()-COORD_ENLARGE, cb.center()+COORD_ENLARGE);
227         cb.merge(mini);
228         cb = cb.zoomed(1.1);
229         Main->view()->setViewport(cb, Main->view()->rect());
230         Main->invalidateView();
231     }
232     Main->view()->blockSignals(false);
233 
234     QTimer::singleShot(10, this, SLOT(on_Viewport_changed(true)));
235 }
236 
on_downloadAction_triggered()237 void FeaturesDock::on_downloadAction_triggered()
238 {
239 #ifndef _MOBILE
240     Feature* F;
241     QList<Feature*> toResolve;
242     for (int i=0; i < ui.FeaturesList->selectedItems().count(); ++i) {
243         F = ui.FeaturesList->selectedItems()[i]->data(Qt::UserRole).value<Feature*>();
244 
245         if (F->notEverythingDownloaded()) {
246             toResolve.push_back(F);
247         }
248     }
249     Main->downloadFeatures(toResolve);
250 #endif
251 }
252 
on_addSelectAction_triggered()253 void FeaturesDock::on_addSelectAction_triggered()
254 {
255     Feature* F;
256     Main->view()->blockSignals(true);
257 
258     for (int i=0; i < ui.FeaturesList->selectedItems().count(); ++i) {
259         F = ui.FeaturesList->selectedItems()[i]->data(Qt::UserRole).value<Feature*>();
260         if (F) {
261             Main->properties()->addSelection(F);
262         }
263     }
264 
265     Main->view()->blockSignals(false);
266 }
267 
on_btFind_clicked(bool)268 void FeaturesDock::on_btFind_clicked(bool)
269 {
270     SelectionDialog* dlg = new SelectionDialog(Main);
271     if (!dlg->exec())
272         return;
273 
274     TagSelector* tsel = TagSelector::parse(dlg->edTagQuery->text());
275     if (!tsel)
276         return;
277 
278     Found.clear();
279     int added = 0;
280     for (VisibleFeatureIterator i(Main->document()); !i.isEnd() && (!dlg->sbMaxResult->value() || added < dlg->sbMaxResult->value()); ++i) {
281         if (tsel->matches(i.get(), Main->view()->pixelPerM())) {
282             Found << i.get();
283         }
284     }
285 
286     findMode = true;
287     ui.tabBar->blockSignals(true);
288     ui.tabBar->setCurrentIndex(3);
289     ui.tabBar->blockSignals(false);
290     tabChanged(3);
291 }
292 
on_btReset_clicked(bool)293 void FeaturesDock::on_btReset_clicked(bool)
294 {
295     findMode = false;
296     updateList();
297 }
298 
changeEvent(QEvent * event)299 void FeaturesDock::changeEvent(QEvent * event)
300 {
301     if (event->type() == QEvent::LanguageChange)
302         retranslateUi();
303     MDockAncestor::changeEvent(event);
304 }
305 
tabChanged(int idx)306 void FeaturesDock::tabChanged(int idx)
307 {
308     curFeatType = (IFeature::FeatureType)ui.tabBar->tabData(idx).toInt();
309     ui.FeaturesList->clear();
310     Highlighted.clear();
311 
312     if (curFeatType == IFeature::OsmRelation)
313         ui.cbSelectionFilter->setEnabled(true);
314     else
315         ui.cbSelectionFilter->setEnabled(false);
316 
317     updateList();
318 }
319 
on_Viewport_changed(bool visible)320 void FeaturesDock::on_Viewport_changed(bool visible)
321 {
322     // Note: This is a bit of a hack. It appers the signal visibilityChanged()
323     // is called AFTER parent windows are destroyed, and the Main object is no
324     // longer valid. This manifests itself on OSX during automated tests, where
325     // the MainWindow is closed rapidly and may be worth a bit more
326     // investigation. We avoid this problem by only updating the data when the
327     // dock is shown, which is good enough for normal operation.
328     if (visible) {
329         theViewport = Main->view()->viewport();
330 
331         updateList();
332     }
333 }
334 
addItem(MapFeaturePtr F)335 void FeaturesDock::addItem(MapFeaturePtr F)
336 {
337     if (ui.FeaturesList->count() > MAX_FEATS)
338         return;
339 
340     QListWidgetItem* newItem = NULL;
341 
342     if (curFeatType == IFeature::OsmRelation || curFeatType == Feature::All)
343     {
344         if (Relation* R = CAST_RELATION(F)) {
345             /* Include all relations if nothing is selected, otherwisde only the
346              * intersection of selected items and relations */
347             bool includeThis;
348             if ((Main->properties()->selection().size() > 0) && (ui.cbSelectionFilter->isChecked())) {
349                 includeThis = false;
350                 foreach (Feature* sel, Main->properties()->selection()) {
351                     if (R->find(sel) < R->size()) {
352                         includeThis = true;
353                         break;
354                     }
355                 }
356             } else includeThis = true;
357 
358             if (includeThis) {
359                 newItem = new QListWidgetItem(R->description(), ui.FeaturesList);
360             }
361         }
362     }
363     if (curFeatType == IFeature::LineString || curFeatType == IFeature::Polygon || curFeatType == Feature::All)
364     {
365         if (Way* R = CAST_WAY(F))
366             newItem = new QListWidgetItem(R->description(), ui.FeaturesList);
367     }
368     if (curFeatType == IFeature::Point || curFeatType == Feature::All)
369     {
370         if (Node* N = CAST_NODE(F))
371             newItem = new QListWidgetItem(N->description(), ui.FeaturesList);
372     }
373     if (newItem) {
374         newItem->setData(Qt::UserRole, QVariant::fromValue(F));
375         if (Highlighted.contains(F)) newItem->setSelected(true);
376     }
377 }
378 
invalidate()379 void FeaturesDock::invalidate()
380 {
381     ui.FeaturesList->clear();
382     Highlighted.clear();
383     Found.clear();
384 }
385 
clearItems()386 void FeaturesDock::clearItems()
387 {
388     for(int i=ui.FeaturesList->count()-1; i>=0; --i) {
389         QListWidgetItem* item = ui.FeaturesList->item(i);
390         if (item->isSelected()) {
391             Feature * F = item->data(Qt::UserRole).value<Feature*>();
392             if (F->isDeleted()) {
393                 item->setSelected(false);
394                 Highlighted.removeOne(F);
395             }
396         }
397         delete item;
398     }
399 }
400 
updateList()401 void FeaturesDock::updateList()
402 {
403     setUpdatesEnabled(false);
404     ui.FeaturesList->blockSignals(true);
405 
406     clearItems();
407 
408     if (!isVisible() || !Main->document())
409         return;
410 
411     if (findMode) {
412         foreach (MapFeaturePtr F, Found) {
413             if (ui.cbWithin->isChecked()) {
414                 if (Main->view()->viewport().contains(F->boundingBox()))
415                     addItem(F);
416             } else
417                 addItem(F);
418         }
419     } else {
420         for (int j=0; j<Main->document()->layerSize(); ++j) {
421             if (!Main->document()->getLayer(j)->size())
422                 continue;
423             QList < Feature* > ret = g_backend.indexFind(Main->document()->getLayer(j), theViewport);
424             foreach (Feature* F, ret) {
425                 if (F->isHidden())
426                     continue;
427 
428                 if (ui.cbWithin->isChecked()) {
429                     if (Main->view()->viewport().contains(F->boundingBox()))
430                         addItem(F);
431                 } else
432                     addItem(F);
433             }
434         }
435     }
436     ui.FeaturesList->sortItems();
437 
438     ui.FeaturesList->blockSignals(false);
439     setUpdatesEnabled(true);
440 }
441 
highlightedSize() const442 int FeaturesDock::highlightedSize() const
443 {
444     if (!isVisible())
445         return 0;
446     return Highlighted.size();
447 }
448 
highlighted(int idx)449 Feature* FeaturesDock::highlighted(int idx)
450 {
451     if (idx < Highlighted.size())
452         return Highlighted[idx];
453     return 0;
454 }
455 
highlighted()456 QList<Feature*> FeaturesDock::highlighted()
457 {
458     return Highlighted;
459 }
460 
retranslateUi()461 void FeaturesDock::retranslateUi()
462 {
463     ui.retranslateUi(getWidget());
464 
465     setWindowTitle(tr("Features"));
466     centerAction->setText(tr("Center map"));
467     centerZoomAction->setText(tr("Center && Zoom map"));
468     downloadAction->setText(tr("Download missing children"));
469     addSelectAction->setText(tr("Add to selection"));
470     deleteAction->setText(tr("Delete"));
471 
472     retranslateTabBar();
473 }
474 
retranslateTabBar()475 void FeaturesDock::retranslateTabBar()
476 {
477     ui.tabBar->setTabText(0, tr("Relations"));
478     ui.tabBar->setTabText(1, tr("Ways"));
479     ui.tabBar->setTabText(2, tr("Nodes"));
480     ui.tabBar->setTabText(3, tr("All"));
481 }
482 
483 
484