mapview.cpp 16 KB

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