Explorar o código

Merge pull request #3046 from Nordsoft91/editor-improvements-1.4

Add map editor zoom and objects lock functionality
Nordsoft91 %!s(int64=2) %!d(string=hai) anos
pai
achega
02c1425939

+ 2 - 3
lib/mapObjects/CGHeroInstance.cpp

@@ -1066,9 +1066,8 @@ std::string CGHeroInstance::getBiographyTextID() const
 		return biographyCustomTextId;
 	if (type)
 		return type->getBiographyTextID();
-
-	assert(0);
-	return "";
+	
+	return ""; //for random hero
 }
 
 CGHeroInstance::ArtPlacementMap CGHeroInstance::putArtifact(ArtifactPosition pos, CArtifactInstance * art)

+ 3 - 2
mapeditor/Animation.cpp

@@ -14,6 +14,7 @@
 #include "Animation.h"
 
 #include "BitmapHandler.h"
+#include "graphics.h"
 
 #include "../lib/vcmi_endian.h"
 #include "../lib/filesystem/Filesystem.h"
@@ -585,8 +586,8 @@ void Animation::init()
 
 	JsonPath resID = JsonPath::builtin("SPRITES/" + name);
 
-	//if(vstd::contains(graphics->imageLists, resID.getName()))
-		//initFromJson(graphics->imageLists[resID.getName()]);
+	if(vstd::contains(graphics->imageLists, resID.getName()))
+		initFromJson(graphics->imageLists[resID.getName()]);
 
 	auto configList = CResourceHandler::get()->getResourcesWithName(resID);
 

+ 3 - 0
mapeditor/CMakeLists.txt

@@ -35,6 +35,7 @@ set(editor_SRCS
 		inspector/questwidget.cpp
 		inspector/heroskillswidget.cpp
 		inspector/PickObjectDelegate.cpp
+		inspector/portraitwidget.cpp
 		resourceExtractor/ResourceConverter.cpp
 )
 
@@ -74,6 +75,7 @@ set(editor_HEADERS
 		inspector/questwidget.h
 		inspector/heroskillswidget.h
 		inspector/PickObjectDelegate.h
+		inspector/portraitwidget.h
 		resourceExtractor/ResourceConverter.h
 )
 
@@ -99,6 +101,7 @@ set(editor_FORMS
 		inspector/rewardswidget.ui
 		inspector/questwidget.ui
 		inspector/heroskillswidget.ui
+		inspector/portraitwidget.ui
 )
 
 set(editor_TS

+ 1 - 0
mapeditor/graphics.h

@@ -11,6 +11,7 @@
 //code is copied from vcmiclient/Graphics.h with minimal changes
 
 #include "../lib/GameConstants.h"
+#include "../lib/filesystem/ResourcePath.h"
 #include <QImage>
 
 VCMI_LIB_NAMESPACE_BEGIN

BIN=BIN
mapeditor/icons/brush-0.png


BIN=BIN
mapeditor/icons/brush-6.png


BIN=BIN
mapeditor/icons/brush-7.png


BIN=BIN
mapeditor/icons/zoom_base.png


BIN=BIN
mapeditor/icons/zoom_minus.png


BIN=BIN
mapeditor/icons/zoom_plus.png


BIN=BIN
mapeditor/icons/zoom_zero.png


+ 2 - 1
mapeditor/inspector/inspector.cpp

@@ -26,6 +26,7 @@
 #include "rewardswidget.h"
 #include "questwidget.h"
 #include "heroskillswidget.h"
+#include "portraitwidget.h"
 #include "PickObjectDelegate.h"
 #include "../mapcontroller.h"
 
@@ -272,7 +273,7 @@ void Inspector::updateProperties(CGHeroInstance * o)
 	}
 	addProperty("Name", o->getNameTranslated(), false);
 	addProperty("Biography", o->getBiographyTranslated(), new MessageDelegate, false);
-	addProperty("Portrait", o->customPortraitSource, false);
+	addProperty("Portrait", PropertyEditorPlaceholder(), new PortraitDelegate(*o), false);
 	
 	auto * delegate = new HeroSkillsDelegate(*o);
 	addProperty("Skills", PropertyEditorPlaceholder(), delegate, false);

+ 135 - 0
mapeditor/inspector/portraitwidget.cpp

@@ -0,0 +1,135 @@
+/*
+ * portraitwidget.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "portraitwidget.h"
+#include "ui_portraitwidget.h"
+#include "../../lib/CHeroHandler.h"
+#include "../Animation.h"
+
+PortraitWidget::PortraitWidget(CGHeroInstance & h, QWidget *parent):
+	QDialog(parent),
+	ui(new Ui::PortraitWidget),
+	hero(h),
+	portraitIndex(0)
+{
+	ui->setupUi(this);
+	ui->portraitView->setScene(&scene);
+	ui->portraitView->fitInView(scene.itemsBoundingRect(), Qt::KeepAspectRatio);
+}
+
+PortraitWidget::~PortraitWidget()
+{
+	delete ui;
+}
+
+void PortraitWidget::obtainData()
+{
+	portraitIndex = VLC->heroh->getById(hero.getPortraitSource())->getIndex();
+	if(hero.customPortraitSource.isValid())
+	{
+		ui->isDefault->setChecked(true);
+	}
+	
+	drawPortrait();
+}
+
+void PortraitWidget::commitChanges()
+{
+	if(portraitIndex == VLC->heroh->getById(HeroTypeID(hero.subID))->getIndex())
+		hero.customPortraitSource = HeroTypeID::NONE;
+	else
+		hero.customPortraitSource = VLC->heroh->getByIndex(portraitIndex)->getId();
+}
+
+void PortraitWidget::drawPortrait()
+{
+	static Animation portraitAnimation(AnimationPath::builtin("PortraitsLarge").getOriginalName());
+	portraitAnimation.preload();
+	auto icon = VLC->heroTypes()->getByIndex(portraitIndex)->getIconIndex();
+	pixmap = QPixmap::fromImage(*portraitAnimation.getImage(icon));
+	scene.addPixmap(pixmap);
+	ui->portraitView->fitInView(scene.itemsBoundingRect(), Qt::KeepAspectRatio);
+}
+
+void PortraitWidget::resizeEvent(QResizeEvent *)
+{
+	ui->portraitView->fitInView(scene.itemsBoundingRect(), Qt::KeepAspectRatio);
+}
+
+void PortraitWidget::on_isDefault_toggled(bool checked)
+{
+	if(checked)
+	{
+		ui->buttonNext->setEnabled(false);
+		ui->buttonPrev->setEnabled(false);
+		portraitIndex = VLC->heroh->getById(HeroTypeID(hero.subID))->getIndex();
+	}
+	else
+	{
+		ui->buttonNext->setEnabled(true);
+		ui->buttonPrev->setEnabled(true);
+	}
+	drawPortrait();
+}
+
+
+void PortraitWidget::on_buttonNext_clicked()
+{
+	if(portraitIndex < VLC->heroh->size() - 1)
+		++portraitIndex;
+	else
+		portraitIndex = 0;
+	
+	drawPortrait();
+}
+
+
+void PortraitWidget::on_buttonPrev_clicked()
+{
+	if(portraitIndex > 0)
+		--portraitIndex;
+	else
+		portraitIndex = VLC->heroh->size() - 1;
+	
+	drawPortrait();
+}
+
+PortraitDelegate::PortraitDelegate(CGHeroInstance & h): hero(h), QStyledItemDelegate()
+{
+}
+
+QWidget * PortraitDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+	return new PortraitWidget(hero, parent);
+}
+
+void PortraitDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
+{
+	if(auto * ed = qobject_cast<PortraitWidget *>(editor))
+	{
+		ed->obtainData();
+	}
+	else
+	{
+		QStyledItemDelegate::setEditorData(editor, index);
+	}
+}
+
+void PortraitDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
+{
+	if(auto * ed = qobject_cast<PortraitWidget *>(editor))
+	{
+		ed->commitChanges();
+	}
+	else
+	{
+		QStyledItemDelegate::setModelData(editor, model, index);
+	}
+}

+ 64 - 0
mapeditor/inspector/portraitwidget.h

@@ -0,0 +1,64 @@
+/*
+ * portraitwidget.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include <QDialog>
+#include "../../lib/mapObjects/CGHeroInstance.h"
+
+namespace Ui {
+class PortraitWidget;
+}
+
+class PortraitWidget : public QDialog
+{
+	Q_OBJECT
+
+public:
+	explicit PortraitWidget(CGHeroInstance &, QWidget *parent = nullptr);
+	~PortraitWidget();
+
+	void obtainData();
+	void commitChanges();
+	
+	void resizeEvent(QResizeEvent *) override;
+
+private slots:
+	void on_isDefault_toggled(bool checked);
+
+	void on_buttonNext_clicked();
+
+	void on_buttonPrev_clicked();
+
+private:
+	void drawPortrait();
+	
+	Ui::PortraitWidget *ui;
+	QGraphicsScene scene;
+	QPixmap pixmap;
+
+	CGHeroInstance & hero;
+	int portraitIndex;
+};
+
+class PortraitDelegate : public QStyledItemDelegate
+{
+	Q_OBJECT
+public:
+	using QStyledItemDelegate::QStyledItemDelegate;
+
+	PortraitDelegate(CGHeroInstance &);
+
+	QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+	void setEditorData(QWidget *editor, const QModelIndex &index) const override;
+	void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
+
+private:
+	CGHeroInstance & hero;
+};

+ 96 - 0
mapeditor/inspector/portraitwidget.ui

@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PortraitWidget</class>
+ <widget class="QDialog" name="PortraitWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>286</width>
+    <height>305</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Portrait</string>
+  </property>
+  <property name="modal">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="spacing">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QGraphicsView" name="portraitView">
+       <property name="minimumSize">
+        <size>
+         <width>116</width>
+         <height>128</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QToolButton" name="buttonNext">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="arrowType">
+          <enum>Qt::UpArrow</enum>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="buttonPrev">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="arrowType">
+          <enum>Qt::DownArrow</enum>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="isDefault">
+     <property name="text">
+      <string>Default</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 157 - 100
mapeditor/mainwindow.cpp

@@ -222,6 +222,8 @@ MainWindow::MainWindow(QWidget* parent) :
 	scenePreview = new QGraphicsScene(this);
 	ui->objectPreview->setScene(scenePreview);
 
+	initialScale = ui->mapView->viewport()->geometry();
+
 	//loading objects
 	loadObjectsTree();
 	
@@ -296,6 +298,7 @@ void MainWindow::initializeMap(bool isNew)
 	ui->mapView->setScene(controller.scene(mapLevel));
 	ui->minimapView->setScene(controller.miniScene(mapLevel));
 	ui->minimapView->dimensions();
+	initialScale = ui->mapView->mapToScene(ui->mapView->viewport()->geometry()).boundingRect();
 	
 	//enable settings
 	ui->actionMapSettings->setEnabled(true);
@@ -536,19 +539,21 @@ void MainWindow::addGroupIntoCatalog(const std::string & groupName, bool useCust
 				painter.scale(scale, scale);
 				painter.drawImage(QPoint(0, 0), *picture);
 			}
-
+			
+			//create object to extract name
+			std::unique_ptr<CGObjectInstance> temporaryObj(factory->create(templ));
+			QString translated = useCustomName ? QString::fromStdString(temporaryObj->getObjectName().c_str()) : subGroupName;
+			itemType->setText(translated);
+			
 			//add parameters
 			QJsonObject data{{"id", QJsonValue(ID)},
 							 {"subid", QJsonValue(secondaryID)},
 							 {"template", QJsonValue(templateId)},
 							 {"animationEditor", QString::fromStdString(templ->editorAnimationFile.getOriginalName())},
 							 {"animation", QString::fromStdString(templ->animationFile.getOriginalName())},
-							 {"preview", jsonFromPixmap(preview)}};
-			
-			//create object to extract name
-			std::unique_ptr<CGObjectInstance> temporaryObj(factory->create(templ));
-			QString translated = useCustomName ? tr(temporaryObj->getObjectName().c_str()) : subGroupName;
-			itemType->setText(translated);
+							 {"preview", jsonFromPixmap(preview)},
+							 {"typeName", QString::fromStdString(factory->getJsonKey())}
+			};
 
 			//do not have extra level
 			if(singleTemplate)
@@ -830,99 +835,12 @@ void MainWindow::changeBrushState(int idx)
 
 }
 
-void MainWindow::on_toolBrush_clicked(bool checked)
-{
-	//ui->toolBrush->setChecked(false);
-	ui->toolBrush2->setChecked(false);
-	ui->toolBrush4->setChecked(false);
-	ui->toolArea->setChecked(false);
-	ui->toolLasso->setChecked(false);
-
-	if(checked)
-		ui->mapView->selectionTool = MapView::SelectionTool::Brush;
-	else
-		ui->mapView->selectionTool = MapView::SelectionTool::None;
-	
-	ui->tabWidget->setCurrentIndex(0);
-}
-
-void MainWindow::on_toolBrush2_clicked(bool checked)
-{
-	ui->toolBrush->setChecked(false);
-	//ui->toolBrush2->setChecked(false);
-	ui->toolBrush4->setChecked(false);
-	ui->toolArea->setChecked(false);
-	ui->toolLasso->setChecked(false);
-
-	if(checked)
-		ui->mapView->selectionTool = MapView::SelectionTool::Brush2;
-	else
-		ui->mapView->selectionTool = MapView::SelectionTool::None;
-	
-	ui->tabWidget->setCurrentIndex(0);
-}
-
-
-void MainWindow::on_toolBrush4_clicked(bool checked)
-{
-	ui->toolBrush->setChecked(false);
-	ui->toolBrush2->setChecked(false);
-	//ui->toolBrush4->setChecked(false);
-	ui->toolArea->setChecked(false);
-	ui->toolLasso->setChecked(false);
-
-	if(checked)
-		ui->mapView->selectionTool = MapView::SelectionTool::Brush4;
-	else
-		ui->mapView->selectionTool = MapView::SelectionTool::None;
-	
-	ui->tabWidget->setCurrentIndex(0);
-}
-
-void MainWindow::on_toolArea_clicked(bool checked)
-{
-	ui->toolBrush->setChecked(false);
-	ui->toolBrush2->setChecked(false);
-	ui->toolBrush4->setChecked(false);
-	//ui->toolArea->setChecked(false);
-	ui->toolLasso->setChecked(false);
-
-	if(checked)
-		ui->mapView->selectionTool = MapView::SelectionTool::Area;
-	else
-		ui->mapView->selectionTool = MapView::SelectionTool::None;
-	
-	ui->tabWidget->setCurrentIndex(0);
-}
-
-void MainWindow::on_toolLasso_clicked(bool checked)
-{
-	ui->toolBrush->setChecked(false);
-	ui->toolBrush2->setChecked(false);
-	ui->toolBrush4->setChecked(false);
-	ui->toolArea->setChecked(false);
-	//ui->toolLasso->setChecked(false);
-	
-	if(checked)
-		ui->mapView->selectionTool = MapView::SelectionTool::Lasso;
-	else
-		ui->mapView->selectionTool = MapView::SelectionTool::None;
-	
-	ui->tabWidget->setCurrentIndex(0);
-}
-
 void MainWindow::on_actionErase_triggered()
-{
-	on_toolErase_clicked();
-}
-
-void MainWindow::on_toolErase_clicked()
 {
 	if(controller.map())
 	{
 		controller.commitObjectErase(mapLevel);
 	}
-	ui->tabWidget->setCurrentIndex(0);
 }
 
 void MainWindow::preparePreview(const QModelIndex &index)
@@ -939,16 +857,14 @@ void MainWindow::preparePreview(const QModelIndex &index)
 			scenePreview->addPixmap(objPreview);
 		}
 	}
+
+	ui->objectPreview->fitInView(scenePreview->itemsBoundingRect(), Qt::KeepAspectRatio);
 }
 
 
 void MainWindow::treeViewSelected(const QModelIndex & index, const QModelIndex & deselected)
 {
-	ui->toolBrush->setChecked(false);
-	ui->toolBrush2->setChecked(false);
-	ui->toolBrush4->setChecked(false);
-	ui->toolArea->setChecked(false);
-	ui->toolLasso->setChecked(false);
+	ui->toolSelect->setChecked(true);
 	ui->mapView->selectionTool = MapView::SelectionTool::None;
 	
 	preparePreview(index);
@@ -1109,7 +1025,6 @@ void MainWindow::onSelectionMade(int level, bool anythingSelected)
 	if (level == mapLevel)
 	{
 		ui->actionErase->setEnabled(anythingSelected);
-		ui->toolErase->setEnabled(anythingSelected);
 	}
 }
 void MainWindow::displayStatus(const QString& message, int timeout /* = 2000 */)
@@ -1279,3 +1194,145 @@ void MainWindow::on_actionh3m_converter_triggered()
 	}
 }
 
+
+void MainWindow::on_actionLock_triggered()
+{
+	if(controller.map())
+	{
+		if(controller.scene(mapLevel)->selectionObjectsView.getSelection().empty())
+		{
+			for(auto obj : controller.map()->objects)
+			{
+				controller.scene(mapLevel)->selectionObjectsView.setLockObject(obj, true);
+				controller.scene(mapLevel)->objectsView.setLockObject(obj, true);
+			}
+		}
+		else
+		{
+			for(auto * obj : controller.scene(mapLevel)->selectionObjectsView.getSelection())
+			{
+				controller.scene(mapLevel)->selectionObjectsView.setLockObject(obj, true);
+				controller.scene(mapLevel)->objectsView.setLockObject(obj, true);
+			}
+			controller.scene(mapLevel)->selectionObjectsView.clear();
+		}
+		controller.scene(mapLevel)->objectsView.update();
+		controller.scene(mapLevel)->selectionObjectsView.update();
+	}
+}
+
+
+void MainWindow::on_actionUnlock_triggered()
+{
+	if(controller.map())
+	{
+		controller.scene(mapLevel)->selectionObjectsView.unlockAll();
+		controller.scene(mapLevel)->objectsView.unlockAll();
+	}
+	controller.scene(mapLevel)->objectsView.update();
+}
+
+
+void MainWindow::on_actionZoom_in_triggered()
+{
+	auto rect = ui->mapView->mapToScene(ui->mapView->viewport()->geometry()).boundingRect();
+	rect -= QMargins{32 + 1, 32 + 1, 32 + 2, 32 + 2}; //compensate bounding box
+	ui->mapView->fitInView(rect, Qt::KeepAspectRatioByExpanding);
+}
+
+
+void MainWindow::on_actionZoom_out_triggered()
+{
+	auto rect = ui->mapView->mapToScene(ui->mapView->viewport()->geometry()).boundingRect();
+	rect += QMargins{32 - 1, 32 - 1, 32 - 2, 32 - 2}; //compensate bounding box
+	ui->mapView->fitInView(rect, Qt::KeepAspectRatioByExpanding);
+}
+
+
+void MainWindow::on_actionZoom_reset_triggered()
+{
+	auto center = ui->mapView->mapToScene(ui->mapView->viewport()->geometry().center());
+	ui->mapView->fitInView(initialScale, Qt::KeepAspectRatioByExpanding);
+	ui->mapView->centerOn(center);
+}
+
+
+void MainWindow::on_toolLine_toggled(bool checked)
+{
+	if(checked)
+	{
+		ui->mapView->selectionTool = MapView::SelectionTool::Line;
+		ui->tabWidget->setCurrentIndex(0);
+	}
+}
+
+
+void MainWindow::on_toolBrush2_toggled(bool checked)
+{
+	if(checked)
+	{
+		ui->mapView->selectionTool = MapView::SelectionTool::Brush2;
+		ui->tabWidget->setCurrentIndex(0);
+	}
+}
+
+
+void MainWindow::on_toolBrush_toggled(bool checked)
+{
+	if(checked)
+	{
+		ui->mapView->selectionTool = MapView::SelectionTool::Brush;
+		ui->tabWidget->setCurrentIndex(0);
+	}
+}
+
+
+void MainWindow::on_toolBrush4_toggled(bool checked)
+{
+	if(checked)
+	{
+		ui->mapView->selectionTool = MapView::SelectionTool::Brush4;
+		ui->tabWidget->setCurrentIndex(0);
+	}
+}
+
+
+void MainWindow::on_toolLasso_toggled(bool checked)
+{
+	if(checked)
+	{
+		ui->mapView->selectionTool = MapView::SelectionTool::Lasso;
+		ui->tabWidget->setCurrentIndex(0);
+	}
+}
+
+
+void MainWindow::on_toolArea_toggled(bool checked)
+{
+	if(checked)
+	{
+		ui->mapView->selectionTool = MapView::SelectionTool::Area;
+		ui->tabWidget->setCurrentIndex(0);
+	}
+}
+
+
+void MainWindow::on_toolFill_toggled(bool checked)
+{
+	if(checked)
+	{
+		ui->mapView->selectionTool = MapView::SelectionTool::Fill;
+		ui->tabWidget->setCurrentIndex(0);
+	}
+}
+
+
+void MainWindow::on_toolSelect_toggled(bool checked)
+{
+	if(checked)
+	{
+		ui->mapView->selectionTool = MapView::SelectionTool::None;
+		ui->tabWidget->setCurrentIndex(0);
+	}
+}
+

+ 27 - 12
mapeditor/mainwindow.h

@@ -76,28 +76,16 @@ private slots:
 
 	void on_actionGrid_triggered(bool checked);
 
-	void on_toolBrush_clicked(bool checked);
-
-	void on_toolArea_clicked(bool checked);
-
 	void terrainButtonClicked(TerrainId terrain);
 	void roadOrRiverButtonClicked(ui8 type, bool isRoad);
 	void currentCoordinatesChanged(int x, int y);
 
-	void on_toolErase_clicked();
-
 	void on_terrainFilterCombo_currentIndexChanged(int index);
 
 	void on_filter_textChanged(const QString &arg1);
 
 	void on_actionFill_triggered();
 
-	void on_toolBrush2_clicked(bool checked);
-
-	void on_toolBrush4_clicked(bool checked);
-	
-	void on_toolLasso_clicked(bool checked);
-
 	void on_inspectorWidget_itemChanged(QTableWidgetItem *item);
 
 	void on_actionMapSettings_triggered();
@@ -124,6 +112,32 @@ private slots:
 	
 	void on_actionh3m_converter_triggered();
 
+	void on_actionLock_triggered();
+
+	void on_actionUnlock_triggered();
+
+	void on_actionZoom_in_triggered();
+
+	void on_actionZoom_out_triggered();
+
+	void on_actionZoom_reset_triggered();
+
+	void on_toolLine_toggled(bool checked);
+
+	void on_toolBrush2_toggled(bool checked);
+
+	void on_toolBrush_toggled(bool checked);
+
+	void on_toolBrush4_toggled(bool checked);
+
+	void on_toolLasso_toggled(bool checked);
+
+	void on_toolArea_toggled(bool checked);
+
+	void on_toolFill_toggled(bool checked);
+
+	void on_toolSelect_toggled(bool checked);
+
 public slots:
 
 	void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected);
@@ -166,6 +180,7 @@ private:
 	QStandardItemModel objectsModel;
 
 	int mapLevel = 0;
+	QRectF initialScale;
 
 	std::set<int> catalog;
 

+ 443 - 307
mapeditor/mainwindow.ui

@@ -86,6 +86,8 @@
     <addaction name="actionCopy"/>
     <addaction name="actionPaste"/>
     <addaction name="actionErase"/>
+    <addaction name="actionLock"/>
+    <addaction name="actionUnlock"/>
    </widget>
    <widget class="QMenu" name="menuView">
     <property name="title">
@@ -94,6 +96,10 @@
     <addaction name="actionLevel"/>
     <addaction name="actionGrid"/>
     <addaction name="actionPass"/>
+    <addaction name="separator"/>
+    <addaction name="actionZoom_in"/>
+    <addaction name="actionZoom_out"/>
+    <addaction name="actionZoom_reset"/>
    </widget>
    <widget class="QMenu" name="menuPlayer">
     <property name="title">
@@ -143,6 +149,13 @@
    <addaction name="separator"/>
    <addaction name="actionFill"/>
    <addaction name="actionTranslations"/>
+   <addaction name="separator"/>
+   <addaction name="actionLock"/>
+   <addaction name="actionUnlock"/>
+   <addaction name="separator"/>
+   <addaction name="actionZoom_in"/>
+   <addaction name="actionZoom_out"/>
+   <addaction name="actionZoom_reset"/>
   </widget>
   <widget class="QDockWidget" name="dockWidget_2">
    <property name="sizePolicy">
@@ -163,6 +176,9 @@
      <height>214</height>
     </size>
    </property>
+   <property name="features">
+    <set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
+   </property>
    <property name="windowTitle">
     <string>Minimap</string>
    </property>
@@ -203,12 +219,6 @@
          <height>192</height>
         </size>
        </property>
-       <property name="maximumSize">
-        <size>
-         <width>192</width>
-         <height>192</height>
-        </size>
-       </property>
        <property name="verticalScrollBarPolicy">
         <enum>Qt::ScrollBarAlwaysOff</enum>
        </property>
@@ -239,6 +249,9 @@
      <height>524287</height>
     </size>
    </property>
+   <property name="features">
+    <set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
+   </property>
    <property name="windowTitle">
     <string>Map Objects View</string>
    </property>
@@ -429,17 +442,8 @@
      <verstretch>0</verstretch>
     </sizepolicy>
    </property>
-   <property name="minimumSize">
-    <size>
-     <width>128</width>
-     <height>192</height>
-    </size>
-   </property>
-   <property name="maximumSize">
-    <size>
-     <width>524287</width>
-     <height>192</height>
-    </size>
+   <property name="features">
+    <set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
    </property>
    <property name="windowTitle">
     <string>Tools</string>
@@ -468,289 +472,361 @@
     </property>
     <layout class="QVBoxLayout" name="verticalLayout_8">
      <property name="leftMargin">
-      <number>0</number>
+      <number>3</number>
      </property>
      <property name="topMargin">
-      <number>0</number>
+      <number>3</number>
      </property>
      <property name="rightMargin">
-      <number>0</number>
+      <number>3</number>
      </property>
      <property name="bottomMargin">
-      <number>0</number>
+      <number>3</number>
      </property>
      <item>
-      <widget class="QGroupBox" name="groupBox">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>16777215</width>
-         <height>16777215</height>
-        </size>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <property name="topMargin">
+        <number>0</number>
        </property>
-       <property name="title">
-        <string>Brush</string>
+       <item>
+        <widget class="QPushButton" name="toolBrush">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset>
+           <normaloff>icons:brush-1.png</normaloff>icons:brush-1.png</iconset>
+         </property>
+         <property name="iconSize">
+          <size>
+           <width>16</width>
+           <height>16</height>
+          </size>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="autoExclusive">
+          <bool>true</bool>
+         </property>
+         <property name="flat">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="toolBrush2">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset>
+           <normaloff>icons:brush-2.png</normaloff>icons:brush-2.png</iconset>
+         </property>
+         <property name="iconSize">
+          <size>
+           <width>16</width>
+           <height>16</height>
+          </size>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="autoExclusive">
+          <bool>true</bool>
+         </property>
+         <property name="flat">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="toolBrush4">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset>
+           <normaloff>icons:brush-4.png</normaloff>icons:brush-4.png</iconset>
+         </property>
+         <property name="iconSize">
+          <size>
+           <width>16</width>
+           <height>16</height>
+          </size>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="autoExclusive">
+          <bool>true</bool>
+         </property>
+         <property name="flat">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="toolLasso">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset>
+           <normaloff>icons:brush-3.png</normaloff>icons:brush-3.png</iconset>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="autoExclusive">
+          <bool>true</bool>
+         </property>
+         <property name="flat">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <property name="topMargin">
+        <number>0</number>
        </property>
-       <layout class="QFormLayout" name="formLayout">
-        <property name="leftMargin">
-         <number>0</number>
-        </property>
-        <property name="topMargin">
-         <number>0</number>
-        </property>
-        <property name="rightMargin">
-         <number>0</number>
-        </property>
-        <property name="bottomMargin">
-         <number>0</number>
-        </property>
-        <item row="0" column="0">
-         <widget class="QPushButton" name="toolBrush">
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="minimumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="maximumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="text">
-           <string/>
-          </property>
-          <property name="icon">
-           <iconset>
-            <normaloff>icons:brush-1.png</normaloff>icons:brush-1.png</iconset>
-          </property>
-          <property name="iconSize">
-           <size>
-            <width>16</width>
-            <height>16</height>
-           </size>
-          </property>
-          <property name="checkable">
-           <bool>true</bool>
-          </property>
-          <property name="flat">
-           <bool>false</bool>
-          </property>
-         </widget>
-        </item>
-        <item row="0" column="1">
-         <widget class="QPushButton" name="toolBrush2">
-          <property name="enabled">
-           <bool>true</bool>
-          </property>
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="minimumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="maximumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="text">
-           <string/>
-          </property>
-          <property name="icon">
-           <iconset>
-            <normaloff>icons:brush-2.png</normaloff>icons:brush-2.png</iconset>
-          </property>
-          <property name="iconSize">
-           <size>
-            <width>16</width>
-            <height>16</height>
-           </size>
-          </property>
-          <property name="checkable">
-           <bool>true</bool>
-          </property>
-          <property name="flat">
-           <bool>false</bool>
-          </property>
-         </widget>
-        </item>
-        <item row="1" column="0">
-         <widget class="QPushButton" name="toolBrush4">
-          <property name="enabled">
-           <bool>true</bool>
-          </property>
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="minimumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="maximumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="text">
-           <string/>
-          </property>
-          <property name="icon">
-           <iconset>
-            <normaloff>icons:brush-4.png</normaloff>icons:brush-4.png</iconset>
-          </property>
-          <property name="iconSize">
-           <size>
-            <width>16</width>
-            <height>16</height>
-           </size>
-          </property>
-          <property name="checkable">
-           <bool>true</bool>
-          </property>
-          <property name="flat">
-           <bool>false</bool>
-          </property>
-         </widget>
-        </item>
-        <item row="1" column="1">
-         <widget class="QPushButton" name="toolArea">
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="minimumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="maximumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="text">
-           <string/>
-          </property>
-          <property name="icon">
-           <iconset>
-            <normaloff>icons:brush-5.png</normaloff>icons:brush-5.png</iconset>
-          </property>
-          <property name="checkable">
-           <bool>true</bool>
-          </property>
-          <property name="flat">
-           <bool>false</bool>
-          </property>
-         </widget>
-        </item>
-        <item row="2" column="0">
-         <widget class="QPushButton" name="toolLasso">
-          <property name="enabled">
-           <bool>true</bool>
-          </property>
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="minimumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="maximumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="text">
-           <string/>
-          </property>
-          <property name="icon">
-           <iconset>
-            <normaloff>icons:brush-3.png</normaloff>icons:brush-3.png</iconset>
-          </property>
-          <property name="checkable">
-           <bool>true</bool>
-          </property>
-          <property name="flat">
-           <bool>false</bool>
-          </property>
-         </widget>
-        </item>
-        <item row="2" column="1">
-         <widget class="QPushButton" name="toolErase">
-          <property name="enabled">
-           <bool>false</bool>
-          </property>
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="minimumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="maximumSize">
-           <size>
-            <width>40</width>
-            <height>40</height>
-           </size>
-          </property>
-          <property name="text">
-           <string/>
-          </property>
-          <property name="icon">
-           <iconset>
-            <normaloff>icons:edit-clear.png</normaloff>icons:edit-clear.png</iconset>
-          </property>
-          <property name="checkable">
-           <bool>false</bool>
-          </property>
-          <property name="flat">
-           <bool>false</bool>
-          </property>
-         </widget>
-        </item>
-       </layout>
-      </widget>
+       <item>
+        <widget class="QPushButton" name="toolLine">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset>
+           <normaloff>icons:brush-7.png</normaloff>icons:brush-7.png</iconset>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="autoExclusive">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="toolArea">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset>
+           <normaloff>icons:brush-5.png</normaloff>icons:brush-5.png</iconset>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="autoExclusive">
+          <bool>true</bool>
+         </property>
+         <property name="flat">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="toolFill">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset>
+           <normaloff>icons:brush-6.png</normaloff>icons:brush-6.png</iconset>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="autoExclusive">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="toolSelect">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>40</width>
+           <height>40</height>
+          </size>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset>
+           <normaloff>icons:brush-0.png</normaloff>icons:brush-0.png</iconset>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="checked">
+          <bool>true</bool>
+         </property>
+         <property name="autoExclusive">
+          <bool>true</bool>
+         </property>
+         <property name="flat">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
      </item>
     </layout>
    </widget>
@@ -762,6 +838,9 @@
      <verstretch>0</verstretch>
     </sizepolicy>
    </property>
+   <property name="features">
+    <set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
+   </property>
    <property name="windowTitle">
     <string>Painting</string>
    </property>
@@ -804,8 +883,8 @@
          <rect>
           <x>0</x>
           <y>0</y>
-          <width>128</width>
-          <height>192</height>
+          <width>256</width>
+          <height>120</height>
          </rect>
         </property>
         <property name="sizePolicy">
@@ -847,8 +926,8 @@
          <rect>
           <x>0</x>
           <y>0</y>
-          <width>128</width>
-          <height>192</height>
+          <width>256</width>
+          <height>120</height>
          </rect>
         </property>
         <property name="sizePolicy">
@@ -883,8 +962,8 @@
          <rect>
           <x>0</x>
           <y>0</y>
-          <width>128</width>
-          <height>192</height>
+          <width>256</width>
+          <height>120</height>
          </rect>
         </property>
         <property name="sizePolicy">
@@ -920,11 +999,8 @@
    </widget>
   </widget>
   <widget class="QDockWidget" name="dockWidget_5">
-   <property name="maximumSize">
-    <size>
-     <width>524287</width>
-     <height>150</height>
-    </size>
+   <property name="features">
+    <set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
    </property>
    <property name="windowTitle">
     <string>Preview</string>
@@ -954,12 +1030,6 @@
          <height>128</height>
         </size>
        </property>
-       <property name="maximumSize">
-        <size>
-         <width>128</width>
-         <height>128</height>
-        </size>
-       </property>
       </widget>
      </item>
     </layout>
@@ -1341,6 +1411,72 @@
     <string>h3m converter</string>
    </property>
   </action>
+  <action name="actionLock">
+   <property name="icon">
+    <iconset>
+     <normaloff>icons:lock-closed.png</normaloff>icons:lock-closed.png</iconset>
+   </property>
+   <property name="text">
+    <string>Lock</string>
+   </property>
+   <property name="toolTip">
+    <string>Lock objects on map to avoid unnecessary changes</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+L</string>
+   </property>
+  </action>
+  <action name="actionUnlock">
+   <property name="icon">
+    <iconset>
+     <normaloff>icons:lock-open.png</normaloff>icons:lock-open.png</iconset>
+   </property>
+   <property name="text">
+    <string>Unlock</string>
+   </property>
+   <property name="toolTip">
+    <string>Unlock all objects on the map</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+Shift+L</string>
+   </property>
+  </action>
+  <action name="actionZoom_in">
+   <property name="icon">
+    <iconset>
+     <normaloff>icons:zoom_plus.png</normaloff>icons:zoom_plus.png</iconset>
+   </property>
+   <property name="text">
+    <string>Zoom in</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+=</string>
+   </property>
+  </action>
+  <action name="actionZoom_out">
+   <property name="icon">
+    <iconset>
+     <normaloff>icons:zoom_minus.png</normaloff>icons:zoom_minus.png</iconset>
+   </property>
+   <property name="text">
+    <string>Zoom out</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+-</string>
+   </property>
+  </action>
+  <action name="actionZoom_reset">
+   <property name="icon">
+    <iconset>
+     <normaloff>icons:zoom_zero.png</normaloff>icons:zoom_zero.png</iconset>
+   </property>
+   <property name="text">
+    <string>Zoom reset</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+Shift+=</string>
+   </property>
+  </action>
  </widget>
  <customwidgets>
   <customwidget>

+ 13 - 3
mapeditor/maphandler.cpp

@@ -322,8 +322,11 @@ std::vector<TileObject> & MapHandler::getObjects(int x, int y, int z)
 	return ttiles[index(x, y, z)];
 }
 
-void MapHandler::drawObjects(QPainter & painter, int x, int y, int z)
+void MapHandler::drawObjects(QPainter & painter, int x, int y, int z, const std::set<const CGObjectInstance *> & locked)
 {
+	painter.setRenderHint(QPainter::Antialiasing, false);
+	painter.setRenderHint(QPainter::SmoothPixmapTransform, false);
+
 	for(auto & object : getObjects(x, y, z))
 	{
 		const CGObjectInstance * obj = object.obj;
@@ -343,8 +346,15 @@ void MapHandler::drawObjects(QPainter & painter, int x, int y, int z)
 		{
 			auto pos = obj->getPosition();
 
-			painter.drawImage(QPoint(x * tileSize, y * tileSize), *objData.objBitmap, object.rect);
-			
+			painter.drawImage(QPoint(x * tileSize, y * tileSize), *objData.objBitmap, object.rect, Qt::AutoColor | Qt::NoOpaqueDetection);
+
+			if(locked.count(obj))
+			{
+				painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
+				painter.fillRect(x * tileSize, y * tileSize, object.rect.width(), object.rect.height(), Qt::Dense4Pattern);
+				painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
+			}
+
 			if(objData.flagBitmap)
 			{
 				if(x == pos.x && y == pos.y)

+ 3 - 1
mapeditor/maphandler.h

@@ -88,6 +88,8 @@ private:
 	void initObjectRects();
 	void initTerrainGraphics();
 	QRgb getTileColor(int x, int y, int z);
+
+	QPolygon lockBitMask;
 		
 public:
 	MapHandler();
@@ -110,7 +112,7 @@ public:
 	std::vector<int3> getTilesUnderObject(CGObjectInstance *) const;
 	
 	/// draws all objects on current tile (higher-level logic, unlike other draw*** methods)
-	void drawObjects(QPainter & painter, int x, int y, int z);
+	void drawObjects(QPainter & painter, int x, int y, int z, const std::set<const CGObjectInstance *> & locked);
 	void drawObject(QPainter & painter, const TileObject & object);
 	void drawObjectAt(QPainter & painter, const CGObjectInstance * object, int x, int y);
 	std::vector<TileObject> & getObjects(int x, int y, int z);

+ 111 - 10
mapeditor/mapview.cpp

@@ -44,15 +44,9 @@ void MinimapView::mouseMoveEvent(QMouseEvent *mouseEvent)
 	if(!sc)
 		return;
 	
-	int w = sc->viewport.viewportWidth();
-	int h = sc->viewport.viewportHeight();
 	auto pos = mapToScene(mouseEvent->pos());
-	pos.setX(pos.x() - w / 2);
-	pos.setY(pos.y() - h / 2);
-	
-	QPointF point = pos * 32;
-			
-	emit cameraPositionChanged(point);
+	pos *= 32;
+	emit cameraPositionChanged(pos);
 }
 
 void MinimapView::mousePressEvent(QMouseEvent* event)
@@ -68,8 +62,7 @@ MapView::MapView(QWidget * parent):
 
 void MapView::cameraChanged(const QPointF & pos)
 {
-	horizontalScrollBar()->setValue(pos.x());
-	verticalScrollBar()->setValue(pos.y());
+	centerOn(pos);
 }
 
 void MapView::setController(MapController * ctrl)
@@ -158,6 +151,60 @@ void MapView::mouseMoveEvent(QMouseEvent *mouseEvent)
 		sc->selectionTerrainView.draw();
 		break;
 			
+	case MapView::SelectionTool::Line:
+		{
+			assert(tile.z == tileStart.z);
+			const auto diff = tile - tileStart;
+			if(diff == int3{})
+				break;
+			
+			const int edge = std::max(abs(diff.x), abs(diff.y));
+			int distMin = std::numeric_limits<int>::max();
+			int3 dir;
+			
+			for(auto & d : int3::getDirs())
+			{
+				if(tile.dist2d(d * edge + tileStart) < distMin)
+				{
+					distMin = tile.dist2d(d * edge + tileStart);
+					dir = d;
+				}
+			}
+			
+			assert(dir != int3{});
+			
+			if(mouseEvent->buttons() == Qt::LeftButton)
+			{
+				for(auto & ts : temporaryTiles)
+					sc->selectionTerrainView.erase(ts);
+				
+				for(auto ts = tileStart; ts.dist2d(tileStart) < edge; ts += dir)
+				{
+					if(!controller->map()->isInTheMap(ts))
+						break;
+					if(!sc->selectionTerrainView.selection().count(ts))
+						temporaryTiles.insert(ts);
+					sc->selectionTerrainView.select(ts);
+				}
+			}
+			if(mouseEvent->buttons() == Qt::RightButton)
+			{
+				for(auto & ts : temporaryTiles)
+					sc->selectionTerrainView.select(ts);
+				
+				for(auto ts = tileStart; ts.dist2d(tileStart) < edge; ts += dir)
+				{
+					if(!controller->map()->isInTheMap(ts))
+						break;
+					if(sc->selectionTerrainView.selection().count(ts))
+						temporaryTiles.insert(ts);
+					sc->selectionTerrainView.erase(ts);
+				}
+			}
+			sc->selectionTerrainView.draw();
+			break;
+		}
+			
 	case MapView::SelectionTool::Lasso:
 		if(mouseEvent->buttons() == Qt::LeftButton)
 		{
@@ -212,6 +259,7 @@ void MapView::mousePressEvent(QMouseEvent *event)
 	switch(selectionTool)
 	{
 	case MapView::SelectionTool::Brush:
+	case MapView::SelectionTool::Line:
 		sc->selectionObjectsView.clear();
 		sc->selectionObjectsView.draw();
 
@@ -269,6 +317,55 @@ void MapView::mousePressEvent(QMouseEvent *event)
 		sc->selectionObjectsView.clear();
 		sc->selectionObjectsView.draw();
 		break;
+			
+	case MapView::SelectionTool::Fill:
+		{
+			if(event->button() != Qt::RightButton && event->button() != Qt::LeftButton)
+				break;
+			
+			std::vector<int3> queue;
+			queue.push_back(tileStart);
+			
+			const std::array<int3, 4> dirs{ int3{1, 0, 0}, int3{-1, 0, 0}, int3{0, 1, 0}, int3{0, -1, 0} };
+			
+			while(!queue.empty())
+			{
+				auto tile = queue.back();
+				queue.pop_back();
+				if(event->button() == Qt::LeftButton)
+					sc->selectionTerrainView.select(tile);
+				else
+					sc->selectionTerrainView.erase(tile);
+				for(auto & d : dirs)
+				{
+					auto tilen = tile + d;
+					if(!controller->map()->isInTheMap(tilen))
+						continue;
+					if(event->button() == Qt::LeftButton)
+					{
+						if(controller->map()->getTile(tile).roadType
+						   && controller->map()->getTile(tile).roadType != controller->map()->getTile(tilen).roadType)
+							continue;
+						else if(controller->map()->getTile(tile).riverType
+						   && controller->map()->getTile(tile).riverType != controller->map()->getTile(tilen).riverType)
+							continue;
+						else if(controller->map()->getTile(tile).terType != controller->map()->getTile(tilen).terType)
+							continue;
+					}
+					if(event->button() == Qt::LeftButton && sc->selectionTerrainView.selection().count(tilen))
+						continue;
+					if(event->button() == Qt::RightButton && !sc->selectionTerrainView.selection().count(tilen))
+						continue;
+					queue.push_back(tilen);
+				}
+			}
+			
+			
+			sc->selectionTerrainView.draw();
+			sc->selectionObjectsView.clear();
+			sc->selectionObjectsView.draw();
+			break;
+		}
 
 	case MapView::SelectionTool::None:
 		sc->selectionTerrainView.clear();
@@ -408,6 +505,10 @@ void MapView::mouseReleaseEvent(QMouseEvent *event)
 		break;
 	}
 			
+	case MapView::SelectionTool::Line:
+		temporaryTiles.clear();
+		break;
+			
 	case MapView::SelectionTool::None:
 		if(event->button() == Qt::RightButton)
 			break;

+ 3 - 1
mapeditor/mapview.h

@@ -89,7 +89,7 @@ class MapView : public QGraphicsView
 public:
 	enum class SelectionTool
 	{
-		None, Brush, Brush2, Brush4, Area, Lasso
+		None, Brush, Brush2, Brush4, Area, Lasso, Line, Fill
 	};
 
 public:
@@ -124,6 +124,8 @@ private:
 	int3 tileStart;
 	int3 tilePrev;
 	bool pressedOnSelected;
+	
+	std::set<int3> temporaryTiles;
 };
 
 class MinimapView : public QGraphicsView

+ 5 - 1
mapeditor/objectbrowser.cpp

@@ -76,8 +76,12 @@ bool ObjectBrowserProxyModel::filterAcceptsRowText(int source_row, const QModelI
 	auto item = dynamic_cast<QStandardItemModel*>(sourceModel())->itemFromIndex(index);
 	if(!item)
 		return false;
+	
+	auto data = item->data().toJsonObject();
 
-	return (filter.isNull() || filter.isEmpty() || item->text().contains(filter, Qt::CaseInsensitive));
+	return (filter.isNull() || filter.isEmpty()
+			|| item->text().contains(filter, Qt::CaseInsensitive)
+			|| data["typeName"].toString().contains(filter, Qt::CaseInsensitive));
 }
 
 Qt::ItemFlags ObjectBrowserProxyModel::flags(const QModelIndex & index) const

+ 34 - 8
mapeditor/scenelayer.cpp

@@ -377,8 +377,7 @@ void ObjectsLayer::draw(bool onlyDirty)
 		return;
 	
 	QPainter painter(pixmap.get());
-	std::set<const CGObjectInstance *> drawen;
-	
+
 	if(onlyDirty)
 	{
 		//objects could be modified
@@ -392,7 +391,7 @@ void ObjectsLayer::draw(bool onlyDirty)
 		painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
 		
 		for(auto & p : dirty)
-			handler->drawObjects(painter, p.x, p.y, p.z);
+			handler->drawObjects(painter, p.x, p.y, p.z, lockedObjects);
 	}
 	else
 	{
@@ -401,7 +400,7 @@ void ObjectsLayer::draw(bool onlyDirty)
 		{
 			for(int i = 0; i < map->width; ++i)
 			{
-				handler->drawObjects(painter, i, j, scene->level);
+				handler->drawObjects(painter, i, j, scene->level, lockedObjects);
 			}
 		}
 	}
@@ -430,6 +429,19 @@ void ObjectsLayer::setDirty(const CGObjectInstance * object)
 	}
 }
 
+void ObjectsLayer::setLockObject(const CGObjectInstance * object, bool lock)
+{
+	if(lock)
+		lockedObjects.insert(object);
+	else
+		lockedObjects.erase(object);
+}
+
+void ObjectsLayer::unlockAll()
+{
+	lockedObjects.clear();
+}
+
 SelectionObjectsLayer::SelectionObjectsLayer(MapSceneBase * s): AbstractLayer(s), newObject(nullptr)
 {
 }
@@ -501,7 +513,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGO
 	//visitable is most important
 	for(auto & object : objects)
 	{
-		if(!object.obj || object.obj == ignore)
+		if(!object.obj || object.obj == ignore || lockedObjects.count(object.obj))
 			continue;
 		
 		if(object.obj->visitableAt(x, y))
@@ -513,7 +525,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGO
 	//if not visitable tile - try to get blocked
 	for(auto & object : objects)
 	{
-		if(!object.obj || object.obj == ignore)
+		if(!object.obj || object.obj == ignore || lockedObjects.count(object.obj))
 			continue;
 		
 		if(object.obj->blockingAt(x, y))
@@ -525,7 +537,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGO
 	//finally, we can take any object
 	for(auto & object : objects)
 	{
-		if(!object.obj || object.obj == ignore)
+		if(!object.obj || object.obj == ignore || lockedObjects.count(object.obj))
 			continue;
 		
 		if(object.obj->coveringAt(x, y))
@@ -555,7 +567,8 @@ void SelectionObjectsLayer::selectObjects(int x1, int y1, int x2, int y2)
 			if(map->isInTheMap(int3(i, j, scene->level)))
 			{
 				for(auto & o : handler->getObjects(i, j, scene->level))
-					selectObject(o.obj, false); //do not inform about each object added
+					if(!lockedObjects.count(o.obj))
+						selectObject(o.obj, false); //do not inform about each object added
 			}
 		}
 	}
@@ -599,6 +612,19 @@ void SelectionObjectsLayer::onSelection()
 	emit selectionMade(!selectedObjects.empty());
 }
 
+void SelectionObjectsLayer::setLockObject(const CGObjectInstance * object, bool lock)
+{
+	if(lock)
+		lockedObjects.insert(object);
+	else
+		lockedObjects.erase(object);
+}
+
+void SelectionObjectsLayer::unlockAll()
+{
+	lockedObjects.clear();
+}
+
 MinimapLayer::MinimapLayer(MapSceneBase * s): AbstractLayer(s)
 {
 	

+ 8 - 0
mapeditor/scenelayer.h

@@ -120,9 +120,13 @@ public:
 	
 	void setDirty(int x, int y);
 	void setDirty(const CGObjectInstance * object);
+
+	void setLockObject(const CGObjectInstance * object, bool lock);
+	void unlockAll();
 	
 private:
 	std::set<const CGObjectInstance *> objDirty;
+	std::set<const CGObjectInstance *> lockedObjects;
 	std::set<int3> dirty;
 };
 
@@ -180,6 +184,9 @@ public:
 	bool isSelected(const CGObjectInstance *) const;
 	std::set<CGObjectInstance*> getSelection() const;
 	void clear();
+
+	void setLockObject(const CGObjectInstance * object, bool lock);
+	void unlockAll();
 		
 	QPoint shift;
 	CGObjectInstance * newObject;
@@ -191,6 +198,7 @@ signals:
 	
 private:
 	std::set<CGObjectInstance *> selectedObjects;
+	std::set<const CGObjectInstance *> lockedObjects;
 
 	void onSelection();
 };