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