mapview.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. /*
  2. * mapview.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "mapview.h"
  12. #include "mainwindow.h"
  13. #include <QGraphicsSceneMouseEvent>
  14. #include "mapcontroller.h"
  15. #include "../lib/mapObjectConstructors/AObjectTypeHandler.h"
  16. #include "../lib/mapObjectConstructors/CObjectClassesHandler.h"
  17. #include "../lib/mapping/CMap.h"
  18. MinimapView::MinimapView(QWidget * parent):
  19. QGraphicsView(parent)
  20. {
  21. // Disable scrollbars
  22. setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  23. setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  24. }
  25. void MinimapView::dimensions()
  26. {
  27. fitInView(0, 0, controller->map()->width, controller->map()->height, Qt::KeepAspectRatio);
  28. }
  29. void MinimapView::setController(MapController * ctrl)
  30. {
  31. controller = ctrl;
  32. }
  33. void MinimapView::mouseMoveEvent(QMouseEvent *mouseEvent)
  34. {
  35. this->update();
  36. auto * sc = static_cast<MinimapScene*>(scene());
  37. if(!sc)
  38. return;
  39. int w = sc->viewport.viewportWidth();
  40. int h = sc->viewport.viewportHeight();
  41. auto pos = mapToScene(mouseEvent->pos());
  42. pos.setX(pos.x() - w / 2);
  43. pos.setY(pos.y() - h / 2);
  44. QPointF point = pos * 32;
  45. emit cameraPositionChanged(point);
  46. }
  47. void MinimapView::mousePressEvent(QMouseEvent* event)
  48. {
  49. mouseMoveEvent(event);
  50. }
  51. MapView::MapView(QWidget * parent):
  52. QGraphicsView(parent),
  53. selectionTool(MapView::SelectionTool::None)
  54. {
  55. }
  56. void MapView::cameraChanged(const QPointF & pos)
  57. {
  58. horizontalScrollBar()->setValue(pos.x());
  59. verticalScrollBar()->setValue(pos.y());
  60. }
  61. void MapView::setController(MapController * ctrl)
  62. {
  63. controller = ctrl;
  64. }
  65. void MapView::mouseMoveEvent(QMouseEvent *mouseEvent)
  66. {
  67. this->update();
  68. auto * sc = static_cast<MapScene*>(scene());
  69. if(!sc || !controller->map())
  70. return;
  71. auto pos = mapToScene(mouseEvent->pos()); //TODO: do we need to check size?
  72. int3 tile(pos.x() / 32, pos.y() / 32, sc->level);
  73. //if scene will be scrolled without mouse movement, selection, object moving and rubber band will not be updated
  74. //to change this behavior, all this logic should be placed in viewportEvent
  75. if(rubberBand)
  76. rubberBand->setGeometry(QRect(mapFromScene(mouseStart), mouseEvent->pos()).normalized());
  77. if(tile == tilePrev) //do not redraw
  78. return;
  79. tilePrev = tile;
  80. //TODO: cast parent->parent to MainWindow in order to show coordinates or another way to do it?
  81. //main->setStatusMessage(QString("x: %1 y: %2").arg(tile.x, tile.y));
  82. switch(selectionTool)
  83. {
  84. case MapView::SelectionTool::Brush:
  85. if(mouseEvent->buttons() & Qt::RightButton)
  86. sc->selectionTerrainView.erase(tile);
  87. else if(mouseEvent->buttons() == Qt::LeftButton)
  88. sc->selectionTerrainView.select(tile);
  89. sc->selectionTerrainView.draw();
  90. break;
  91. case MapView::SelectionTool::Brush2:
  92. {
  93. std::array<int3, 4> extra{ int3{0, 0, 0}, int3{1, 0, 0}, int3{0, 1, 0}, int3{1, 1, 0} };
  94. for(auto & e : extra)
  95. {
  96. if(mouseEvent->buttons() & Qt::RightButton)
  97. sc->selectionTerrainView.erase(tile + e);
  98. else if(mouseEvent->buttons() == Qt::LeftButton)
  99. sc->selectionTerrainView.select(tile + e);
  100. }
  101. }
  102. sc->selectionTerrainView.draw();
  103. break;
  104. case MapView::SelectionTool::Brush4:
  105. {
  106. std::array<int3, 16> extra{
  107. int3{-1, -1, 0}, int3{0, -1, 0}, int3{1, -1, 0}, int3{2, -1, 0},
  108. int3{-1, 0, 0}, int3{0, 0, 0}, int3{1, 0, 0}, int3{2, 0, 0},
  109. int3{-1, 1, 0}, int3{0, 1, 0}, int3{1, 1, 0}, int3{2, 1, 0},
  110. int3{-1, 2, 0}, int3{0, 2, 0}, int3{1, 2, 0}, int3{2, 2, 0}
  111. };
  112. for(auto & e : extra)
  113. {
  114. if(mouseEvent->buttons() & Qt::RightButton)
  115. sc->selectionTerrainView.erase(tile + e);
  116. else if(mouseEvent->buttons() == Qt::LeftButton)
  117. sc->selectionTerrainView.select(tile + e);
  118. }
  119. }
  120. sc->selectionTerrainView.draw();
  121. break;
  122. case MapView::SelectionTool::Area:
  123. if(mouseEvent->buttons() & Qt::RightButton || !(mouseEvent->buttons() & Qt::LeftButton))
  124. break;
  125. sc->selectionTerrainView.clear();
  126. for(int j = std::min(tile.y, tileStart.y); j < std::max(tile.y, tileStart.y); ++j)
  127. {
  128. for(int i = std::min(tile.x, tileStart.x); i < std::max(tile.x, tileStart.x); ++i)
  129. {
  130. sc->selectionTerrainView.select(int3(i, j, sc->level));
  131. }
  132. }
  133. sc->selectionTerrainView.draw();
  134. break;
  135. case MapView::SelectionTool::Lasso:
  136. if(mouseEvent->buttons() == Qt::LeftButton)
  137. {
  138. sc->selectionTerrainView.select(tile);
  139. sc->selectionTerrainView.draw();
  140. }
  141. break;
  142. case MapView::SelectionTool::None:
  143. if(mouseEvent->buttons() & Qt::RightButton)
  144. break;
  145. auto sh = tile - tileStart;
  146. sc->selectionObjectsView.shift = QPoint(sh.x, sh.y);
  147. if(sh.x || sh.y)
  148. {
  149. if(!sc->selectionObjectsView.newObject && (mouseEvent->buttons() & Qt::LeftButton))
  150. {
  151. if(sc->selectionObjectsView.selectionMode == SelectionObjectsLayer::SELECTION)
  152. {
  153. sc->selectionObjectsView.clear();
  154. sc->selectionObjectsView.selectObjects(tileStart.x, tileStart.y, tile.x, tile.y);
  155. }
  156. }
  157. }
  158. sc->selectionObjectsView.draw();
  159. break;
  160. }
  161. }
  162. void MapView::mousePressEvent(QMouseEvent *event)
  163. {
  164. this->update();
  165. auto * sc = static_cast<MapScene*>(scene());
  166. if(!sc || !controller->map())
  167. return;
  168. if(sc->objectPickerView.isVisible())
  169. return;
  170. mouseStart = mapToScene(event->pos());
  171. tileStart = tilePrev = int3(mouseStart.x() / 32, mouseStart.y() / 32, sc->level);
  172. if(sc->selectionTerrainView.selection().count(tileStart))
  173. pressedOnSelected = true;
  174. else
  175. pressedOnSelected = false;
  176. switch(selectionTool)
  177. {
  178. case MapView::SelectionTool::Brush:
  179. sc->selectionObjectsView.clear();
  180. sc->selectionObjectsView.draw();
  181. if(event->button() == Qt::RightButton)
  182. sc->selectionTerrainView.erase(tileStart);
  183. else if(event->button() == Qt::LeftButton)
  184. sc->selectionTerrainView.select(tileStart);
  185. sc->selectionTerrainView.draw();
  186. break;
  187. case MapView::SelectionTool::Brush2:
  188. sc->selectionObjectsView.clear();
  189. sc->selectionObjectsView.draw();
  190. {
  191. std::array<int3, 4> extra{ int3{0, 0, 0}, int3{1, 0, 0}, int3{0, 1, 0}, int3{1, 1, 0} };
  192. for(auto & e : extra)
  193. {
  194. if(event->button() == Qt::RightButton)
  195. sc->selectionTerrainView.erase(tileStart + e);
  196. else if(event->button() == Qt::LeftButton)
  197. sc->selectionTerrainView.select(tileStart + e);
  198. }
  199. }
  200. sc->selectionTerrainView.draw();
  201. break;
  202. case MapView::SelectionTool::Brush4:
  203. sc->selectionObjectsView.clear();
  204. sc->selectionObjectsView.draw();
  205. {
  206. std::array<int3, 16> extra{
  207. int3{-1, -1, 0}, int3{0, -1, 0}, int3{1, -1, 0}, int3{2, -1, 0},
  208. int3{-1, 0, 0}, int3{0, 0, 0}, int3{1, 0, 0}, int3{2, 0, 0},
  209. int3{-1, 1, 0}, int3{0, 1, 0}, int3{1, 1, 0}, int3{2, 1, 0},
  210. int3{-1, 2, 0}, int3{0, 2, 0}, int3{1, 2, 0}, int3{2, 2, 0}
  211. };
  212. for(auto & e : extra)
  213. {
  214. if(event->button() == Qt::RightButton)
  215. sc->selectionTerrainView.erase(tileStart + e);
  216. else if(event->button() == Qt::LeftButton)
  217. sc->selectionTerrainView.select(tileStart + e);
  218. }
  219. }
  220. sc->selectionTerrainView.draw();
  221. break;
  222. case MapView::SelectionTool::Area:
  223. case MapView::SelectionTool::Lasso:
  224. if(event->button() == Qt::RightButton)
  225. break;
  226. sc->selectionTerrainView.clear();
  227. sc->selectionTerrainView.draw();
  228. sc->selectionObjectsView.clear();
  229. sc->selectionObjectsView.draw();
  230. break;
  231. case MapView::SelectionTool::None:
  232. sc->selectionTerrainView.clear();
  233. sc->selectionTerrainView.draw();
  234. if(sc->selectionObjectsView.newObject && sc->selectionObjectsView.isSelected(sc->selectionObjectsView.newObject))
  235. {
  236. if(event->button() == Qt::RightButton)
  237. controller->discardObject(sc->level);
  238. }
  239. else
  240. {
  241. if(event->button() == Qt::LeftButton)
  242. {
  243. //when paste, new object could be beyond initial object so we need to test two objects in order to select new one
  244. //if object is pasted at place where is multiple objects then proper selection is not guaranteed
  245. auto * firstSelectedObject = sc->selectionObjectsView.selectObjectAt(tileStart.x, tileStart.y);
  246. auto * secondSelectedObject = sc->selectionObjectsView.selectObjectAt(tileStart.x, tileStart.y, firstSelectedObject);
  247. if(firstSelectedObject)
  248. {
  249. if(sc->selectionObjectsView.isSelected(firstSelectedObject))
  250. {
  251. if(qApp->keyboardModifiers() & Qt::ControlModifier)
  252. {
  253. sc->selectionObjectsView.deselectObject(firstSelectedObject);
  254. sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::SELECTION;
  255. }
  256. else
  257. sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::MOVEMENT;
  258. }
  259. else
  260. {
  261. if(!secondSelectedObject || !sc->selectionObjectsView.isSelected(secondSelectedObject))
  262. {
  263. if(!(qApp->keyboardModifiers() & Qt::ControlModifier))
  264. sc->selectionObjectsView.clear();
  265. sc->selectionObjectsView.selectObject(firstSelectedObject);
  266. }
  267. sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::MOVEMENT;
  268. }
  269. }
  270. else
  271. {
  272. sc->selectionObjectsView.clear();
  273. sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::SELECTION;
  274. if(!rubberBand)
  275. rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
  276. rubberBand->setGeometry(QRect(mapFromScene(mouseStart), QSize()));
  277. rubberBand->show();
  278. }
  279. }
  280. sc->selectionObjectsView.shift = QPoint(0, 0);
  281. sc->selectionObjectsView.draw();
  282. }
  283. break;
  284. }
  285. //main->setStatusMessage(QString("x: %1 y: %2").arg(QString::number(event->pos().x()), QString::number(event->pos().y())));
  286. }
  287. void MapView::mouseReleaseEvent(QMouseEvent *event)
  288. {
  289. this->update();
  290. auto * sc = static_cast<MapScene*>(scene());
  291. if(!sc || !controller->map())
  292. return;
  293. if(rubberBand)
  294. rubberBand->hide();
  295. if(sc->objectPickerView.isVisible())
  296. {
  297. if(event->button() == Qt::RightButton)
  298. sc->objectPickerView.discard();
  299. if(event->button() == Qt::LeftButton)
  300. {
  301. mouseStart = mapToScene(event->pos());
  302. tileStart = tilePrev = int3(mouseStart.x() / 32, mouseStart.y() / 32, sc->level);
  303. if(auto * pickedObject = sc->selectionObjectsView.selectObjectAt(tileStart.x, tileStart.y))
  304. sc->objectPickerView.select(pickedObject);
  305. }
  306. return;
  307. }
  308. switch(selectionTool)
  309. {
  310. case MapView::SelectionTool::Lasso: {
  311. if(event->button() == Qt::RightButton)
  312. break;
  313. //key: y position of tile
  314. //value.first: x position of left tile
  315. //value.second: x postiion of right tile
  316. std::map<int, std::pair<int, int>> selectionRangeMapX, selectionRangeMapY;
  317. for(auto & t : sc->selectionTerrainView.selection())
  318. {
  319. auto pairIter = selectionRangeMapX.find(t.y);
  320. if(pairIter == selectionRangeMapX.end())
  321. selectionRangeMapX[t.y] = std::make_pair(t.x, t.x);
  322. else
  323. {
  324. pairIter->second.first = std::min(pairIter->second.first, t.x);
  325. pairIter->second.second = std::max(pairIter->second.second, t.x);
  326. }
  327. pairIter = selectionRangeMapY.find(t.x);
  328. if(pairIter == selectionRangeMapY.end())
  329. selectionRangeMapY[t.x] = std::make_pair(t.y, t.y);
  330. else
  331. {
  332. pairIter->second.first = std::min(pairIter->second.first, t.y);
  333. pairIter->second.second = std::max(pairIter->second.second, t.y);
  334. }
  335. }
  336. std::set<int3> selectionByX, selectionByY;
  337. std::vector<int3> finalSelection;
  338. for(auto & selectionRange : selectionRangeMapX)
  339. {
  340. for(int i = selectionRange.second.first; i < selectionRange.second.second; ++i)
  341. selectionByX.insert(int3(i, selectionRange.first, sc->level));
  342. }
  343. for(auto & selectionRange : selectionRangeMapY)
  344. {
  345. for(int i = selectionRange.second.first; i < selectionRange.second.second; ++i)
  346. selectionByY.insert(int3(selectionRange.first, i, sc->level));
  347. }
  348. std::set_intersection(selectionByX.begin(), selectionByX.end(), selectionByY.begin(), selectionByY.end(), std::back_inserter(finalSelection));
  349. for(auto & lassoTile : finalSelection)
  350. sc->selectionTerrainView.select(lassoTile);
  351. sc->selectionTerrainView.draw();
  352. break;
  353. }
  354. case MapView::SelectionTool::None:
  355. if(event->button() == Qt::RightButton)
  356. break;
  357. //switch position
  358. bool tab = false;
  359. if(sc->selectionObjectsView.selectionMode == SelectionObjectsLayer::MOVEMENT)
  360. {
  361. tab = sc->selectionObjectsView.shift.isNull();
  362. controller->commitObjectShift(sc->level);
  363. }
  364. else
  365. {
  366. sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::NOTHING;
  367. sc->selectionObjectsView.shift = QPoint(0, 0);
  368. sc->selectionObjectsView.draw();
  369. tab = true;
  370. }
  371. auto selection = sc->selectionObjectsView.getSelection();
  372. if(selection.size() == 1)
  373. {
  374. emit openObjectProperties(*selection.begin(), tab);
  375. }
  376. break;
  377. }
  378. }
  379. void MapView::dragEnterEvent(QDragEnterEvent * event)
  380. {
  381. if(!controller || !controller->map())
  382. return;
  383. auto * sc = static_cast<MapScene*>(scene());
  384. if(!sc)
  385. return;
  386. if(event->mimeData()->hasImage())
  387. {
  388. logGlobal->info("Drag'n'drop: dispatching object");
  389. QVariant vdata = event->mimeData()->imageData();
  390. auto data = vdata.toJsonObject();
  391. if(!data.empty())
  392. {
  393. auto preview = data["preview"];
  394. if(preview != QJsonValue::Undefined)
  395. {
  396. auto objId = data["id"].toInt();
  397. auto objSubId = data["subid"].toInt();
  398. auto templateId = data["template"].toInt();
  399. auto factory = VLC->objtypeh->getHandlerFor(objId, objSubId);
  400. auto templ = factory->getTemplates()[templateId];
  401. controller->discardObject(sc->level);
  402. controller->createObject(sc->level, factory->create(templ));
  403. }
  404. }
  405. event->acceptProposedAction();
  406. }
  407. }
  408. void MapView::dropEvent(QDropEvent * event)
  409. {
  410. if(!controller || !controller->map())
  411. return;
  412. auto * sc = static_cast<MapScene*>(scene());
  413. if(!sc)
  414. return;
  415. if(sc->selectionObjectsView.newObject)
  416. {
  417. QString errorMsg;
  418. if(controller->canPlaceObject(sc->level, sc->selectionObjectsView.newObject, errorMsg))
  419. {
  420. auto * obj = sc->selectionObjectsView.newObject;
  421. controller->commitObjectCreate(sc->level);
  422. emit openObjectProperties(obj, false);
  423. }
  424. else
  425. {
  426. controller->discardObject(sc->level);
  427. QMessageBox::information(this, tr("Can't place object"), errorMsg);
  428. }
  429. }
  430. event->acceptProposedAction();
  431. }
  432. void MapView::dragMoveEvent(QDragMoveEvent * event)
  433. {
  434. auto * sc = static_cast<MapScene*>(scene());
  435. if(!sc)
  436. return;
  437. auto rect = event->answerRect();
  438. auto pos = mapToScene(rect.bottomRight()); //TODO: do we need to check size?
  439. int3 tile(pos.x() / 32 + 1, pos.y() / 32 + 1, sc->level);
  440. if(sc->selectionObjectsView.newObject)
  441. {
  442. sc->selectionObjectsView.shift = QPoint(tile.x, tile.y);
  443. sc->selectionObjectsView.selectObject(sc->selectionObjectsView.newObject);
  444. sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::MOVEMENT;
  445. sc->selectionObjectsView.draw();
  446. }
  447. event->acceptProposedAction();
  448. }
  449. void MapView::dragLeaveEvent(QDragLeaveEvent * event)
  450. {
  451. if(!controller || !controller->map())
  452. return;
  453. auto * sc = static_cast<MapScene*>(scene());
  454. if(!sc)
  455. return;
  456. controller->discardObject(sc->level);
  457. }
  458. bool MapView::viewportEvent(QEvent *event)
  459. {
  460. if(auto * sc = static_cast<MapScene*>(scene()))
  461. {
  462. auto rect = mapToScene(viewport()->geometry()).boundingRect();
  463. controller->miniScene(sc->level)->viewport.setViewport(rect.x() / 32, rect.y() / 32, rect.width() / 32, rect.height() / 32);
  464. }
  465. return QGraphicsView::viewportEvent(event);
  466. }
  467. MapSceneBase::MapSceneBase(int lvl):
  468. QGraphicsScene(nullptr),
  469. level(lvl)
  470. {
  471. }
  472. void MapSceneBase::initialize(MapController & controller)
  473. {
  474. for(auto * layer : getAbstractLayers())
  475. layer->initialize(controller);
  476. }
  477. void MapSceneBase::updateViews()
  478. {
  479. for(auto * layer : getAbstractLayers())
  480. layer->update();
  481. }
  482. MapScene::MapScene(int lvl):
  483. MapSceneBase(lvl),
  484. gridView(this),
  485. passabilityView(this),
  486. selectionTerrainView(this),
  487. terrainView(this),
  488. objectsView(this),
  489. selectionObjectsView(this),
  490. objectPickerView(this),
  491. isTerrainSelected(false),
  492. isObjectSelected(false)
  493. {
  494. connect(&selectionTerrainView, &SelectionTerrainLayer::selectionMade, this, &MapScene::terrainSelected);
  495. connect(&selectionObjectsView, &SelectionObjectsLayer::selectionMade, this, &MapScene::objectSelected);
  496. }
  497. std::list<AbstractLayer *> MapScene::getAbstractLayers()
  498. {
  499. //sequence is important because it defines rendering order
  500. return {
  501. &terrainView,
  502. &objectsView,
  503. &gridView,
  504. &passabilityView,
  505. &objectPickerView,
  506. &selectionTerrainView,
  507. &selectionObjectsView
  508. };
  509. }
  510. void MapScene::updateViews()
  511. {
  512. MapSceneBase::updateViews();
  513. terrainView.show(true);
  514. objectsView.show(true);
  515. selectionTerrainView.show(true);
  516. selectionObjectsView.show(true);
  517. objectPickerView.show(true);
  518. }
  519. void MapScene::terrainSelected(bool anythingSelected)
  520. {
  521. isTerrainSelected = anythingSelected;
  522. emit selected(isTerrainSelected || isObjectSelected);
  523. }
  524. void MapScene::objectSelected(bool anythingSelected)
  525. {
  526. isObjectSelected = anythingSelected;
  527. emit selected(isTerrainSelected || isObjectSelected);
  528. }
  529. MinimapScene::MinimapScene(int lvl):
  530. MapSceneBase(lvl),
  531. minimapView(this),
  532. viewport(this)
  533. {
  534. }
  535. std::list<AbstractLayer *> MinimapScene::getAbstractLayers()
  536. {
  537. //sequence is important because it defines rendering order
  538. return {
  539. &minimapView,
  540. &viewport
  541. };
  542. }
  543. void MinimapScene::updateViews()
  544. {
  545. MapSceneBase::updateViews();
  546. minimapView.show(true);
  547. viewport.show(true);
  548. }