1 #include "scenetooltexturebrush.hpp"
2
3 #include <QFrame>
4 #include <QIcon>
5 #include <QTableWidget>
6 #include <QHBoxLayout>
7
8 #include <QWidget>
9 #include <QSpinBox>
10 #include <QGroupBox>
11 #include <QSlider>
12 #include <QEvent>
13 #include <QDropEvent>
14 #include <QButtonGroup>
15 #include <QVBoxLayout>
16 #include <QDragEnterEvent>
17 #include <QDrag>
18 #include <QTableWidget>
19 #include <QHeaderView>
20 #include <QApplication>
21 #include <QSizePolicy>
22
23 #include "scenetool.hpp"
24
25 #include "../../model/doc/document.hpp"
26 #include "../../model/prefs/state.hpp"
27 #include "../../model/world/commands.hpp"
28 #include "../../model/world/data.hpp"
29 #include "../../model/world/idcollection.hpp"
30 #include "../../model/world/idtable.hpp"
31 #include "../../model/world/landtexture.hpp"
32 #include "../../model/world/universalid.hpp"
33
34
BrushSizeControls(const QString & title,QWidget * parent)35 CSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *parent)
36 : QGroupBox(title, parent),
37 mLayoutSliderSize(new QHBoxLayout),
38 mBrushSizeSlider(new QSlider(Qt::Horizontal)),
39 mBrushSizeSpinBox(new QSpinBox)
40 {
41 mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides);
42 mBrushSizeSlider->setTickInterval(10);
43 mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
44 mBrushSizeSlider->setSingleStep(1);
45
46 mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
47 mBrushSizeSpinBox->setSingleStep(1);
48
49 mLayoutSliderSize->addWidget(mBrushSizeSlider);
50 mLayoutSliderSize->addWidget(mBrushSizeSpinBox);
51
52 connect(mBrushSizeSlider, SIGNAL(valueChanged(int)), mBrushSizeSpinBox, SLOT(setValue(int)));
53 connect(mBrushSizeSpinBox, SIGNAL(valueChanged(int)), mBrushSizeSlider, SLOT(setValue(int)));
54
55 setLayout(mLayoutSliderSize);
56 }
57
TextureBrushWindow(CSMDoc::Document & document,QWidget * parent)58 CSVWidget::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QWidget *parent)
59 : QFrame(parent, Qt::Popup),
60 mDocument(document)
61 {
62 mBrushTextureLabel = "Selected texture: " + mBrushTexture + " ";
63
64 CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
65
66 int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
67 int index = landtexturesCollection.searchId(mBrushTexture);
68
69 if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
70 {
71 mSelectedBrush = new QLabel(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());
72 } else
73 {
74 mBrushTextureLabel = "No selected texture or invalid texture";
75 mSelectedBrush = new QLabel(QString::fromStdString(mBrushTextureLabel));
76 }
77
78 mButtonPoint = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-point")), "", this);
79 mButtonSquare = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-square")), "", this);
80 mButtonCircle = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-circle")), "", this);
81 mButtonCustom = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-custom")), "", this);
82
83 mSizeSliders = new BrushSizeControls("Brush size", this);
84
85 QVBoxLayout *layoutMain = new QVBoxLayout;
86 layoutMain->setSpacing(0);
87 layoutMain->setContentsMargins(4,0,4,4);
88
89 QHBoxLayout *layoutHorizontal = new QHBoxLayout;
90 layoutHorizontal->setSpacing(0);
91 layoutHorizontal->setContentsMargins (QMargins (0, 0, 0, 0));
92
93 configureButtonInitialSettings(mButtonPoint);
94 configureButtonInitialSettings(mButtonSquare);
95 configureButtonInitialSettings(mButtonCircle);
96 configureButtonInitialSettings(mButtonCustom);
97
98 mButtonPoint->setToolTip (toolTipPoint);
99 mButtonSquare->setToolTip (toolTipSquare);
100 mButtonCircle->setToolTip (toolTipCircle);
101 mButtonCustom->setToolTip (toolTipCustom);
102
103 QButtonGroup* brushButtonGroup = new QButtonGroup(this);
104 brushButtonGroup->addButton(mButtonPoint);
105 brushButtonGroup->addButton(mButtonSquare);
106 brushButtonGroup->addButton(mButtonCircle);
107 brushButtonGroup->addButton(mButtonCustom);
108
109 brushButtonGroup->setExclusive(true);
110
111 layoutHorizontal->addWidget(mButtonPoint, 0, Qt::AlignTop);
112 layoutHorizontal->addWidget(mButtonSquare, 0, Qt::AlignTop);
113 layoutHorizontal->addWidget(mButtonCircle, 0, Qt::AlignTop);
114 layoutHorizontal->addWidget(mButtonCustom, 0, Qt::AlignTop);
115
116 mHorizontalGroupBox = new QGroupBox(tr(""));
117 mHorizontalGroupBox->setLayout(layoutHorizontal);
118
119 layoutMain->addWidget(mHorizontalGroupBox);
120 layoutMain->addWidget(mSizeSliders);
121 layoutMain->addWidget(mSelectedBrush);
122
123 setLayout(layoutMain);
124
125 connect(mButtonPoint, SIGNAL(clicked()), this, SLOT(setBrushShape()));
126 connect(mButtonSquare, SIGNAL(clicked()), this, SLOT(setBrushShape()));
127 connect(mButtonCircle, SIGNAL(clicked()), this, SLOT(setBrushShape()));
128 connect(mButtonCustom, SIGNAL(clicked()), this, SLOT(setBrushShape()));
129 }
130
configureButtonInitialSettings(QPushButton * button)131 void CSVWidget::TextureBrushWindow::configureButtonInitialSettings(QPushButton *button)
132 {
133 button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
134 button->setContentsMargins (QMargins (0, 0, 0, 0));
135 button->setIconSize (QSize (48-6, 48-6));
136 button->setFixedSize (48, 48);
137 button->setCheckable(true);
138 }
139
setBrushTexture(std::string brushTexture)140 void CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture)
141 {
142 CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
143 *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
144 QUndoStack& undoStack = mDocument.getUndoStack();
145
146 CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
147 int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
148
149 int index = 0;
150 int pluginInDragged = 0;
151 CSMWorld::LandTexture::parseUniqueRecordId(brushTexture, pluginInDragged, index);
152 std::string newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(0, index);
153 int rowInBase = landtexturesCollection.searchId(brushTexture);
154 int rowInNew = landtexturesCollection.searchId(newBrushTextureId);
155
156 // Check if texture exists in current plugin, and clone if id found in base, otherwise reindex the texture
157 // TO-DO: Handle case when texture is not found in neither base or plugin properly (finding new index is not enough)
158 // TO-DO: Handle conflicting plugins properly
159 if (rowInNew == -1)
160 {
161 if (rowInBase == -1)
162 {
163 int counter=0;
164 bool freeIndexFound = false;
165 const int maxCounter = std::numeric_limits<uint16_t>::max() - 1;
166 do {
167 newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
168 if (landtexturesCollection.searchId(brushTexture) != -1 &&
169 landtexturesCollection.getRecord(brushTexture).isDeleted() == 0 &&
170 landtexturesCollection.searchId(newBrushTextureId) != -1 &&
171 landtexturesCollection.getRecord(newBrushTextureId).isDeleted() == 0)
172 counter = (counter + 1) % maxCounter;
173 else freeIndexFound = true;
174 } while (freeIndexFound == false || counter < maxCounter);
175 }
176
177 undoStack.beginMacro ("Add land texture record");
178 undoStack.push (new CSMWorld::CloneCommand (ltexTable, brushTexture, newBrushTextureId, CSMWorld::UniversalId::Type_LandTexture));
179 undoStack.endMacro();
180 }
181
182 if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
183 {
184 mBrushTextureLabel = "Selected texture: " + newBrushTextureId + " ";
185 mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());
186 } else
187 {
188 newBrushTextureId = "";
189 mBrushTextureLabel = "No selected texture or invalid texture";
190 mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));
191 }
192
193 mBrushTexture = newBrushTextureId;
194
195 emit passTextureId(mBrushTexture);
196 emit passBrushShape(mBrushShape); // updates the icon tooltip
197 }
198
setBrushSize(int brushSize)199 void CSVWidget::TextureBrushWindow::setBrushSize(int brushSize)
200 {
201 mBrushSize = brushSize;
202 emit passBrushSize(mBrushSize);
203 }
204
setBrushShape()205 void CSVWidget::TextureBrushWindow::setBrushShape()
206 {
207 if (mButtonPoint->isChecked())
208 mBrushShape = CSVWidget::BrushShape_Point;
209 if (mButtonSquare->isChecked())
210 mBrushShape = CSVWidget::BrushShape_Square;
211 if (mButtonCircle->isChecked())
212 mBrushShape = CSVWidget::BrushShape_Circle;
213 if (mButtonCustom->isChecked())
214 mBrushShape = CSVWidget::BrushShape_Custom;
215 emit passBrushShape(mBrushShape);
216 }
217
adjustToolTips()218 void CSVWidget::SceneToolTextureBrush::adjustToolTips()
219 {
220 }
221
SceneToolTextureBrush(SceneToolbar * parent,const QString & toolTip,CSMDoc::Document & document)222 CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document)
223 : SceneTool (parent, Type_TopAction),
224 mToolTip (toolTip),
225 mDocument (document),
226 mTextureBrushWindow(new TextureBrushWindow(document, this))
227 {
228 mBrushHistory.resize(1);
229 mBrushHistory[0] = "L0#0";
230
231 setAcceptDrops(true);
232 connect(mTextureBrushWindow, SIGNAL(passBrushShape(CSVWidget::BrushShape)), this, SLOT(setButtonIcon(CSVWidget::BrushShape)));
233 setButtonIcon(mTextureBrushWindow->mBrushShape);
234
235 mPanel = new QFrame (this, Qt::Popup);
236
237 QHBoxLayout *layout = new QHBoxLayout (mPanel);
238
239 layout->setContentsMargins (QMargins (0, 0, 0, 0));
240
241 mTable = new QTableWidget (0, 2, this);
242
243 mTable->setShowGrid (true);
244 mTable->verticalHeader()->hide();
245 mTable->horizontalHeader()->hide();
246 mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch);
247 mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch);
248 mTable->setSelectionMode (QAbstractItemView::NoSelection);
249
250 layout->addWidget (mTable);
251
252 connect (mTable, SIGNAL (clicked (const QModelIndex&)),
253 this, SLOT (clicked (const QModelIndex&)));
254
255 }
256
setButtonIcon(CSVWidget::BrushShape brushShape)257 void CSVWidget::SceneToolTextureBrush::setButtonIcon (CSVWidget::BrushShape brushShape)
258 {
259 QString tooltip = "Change brush settings <p>Currently selected: ";
260
261 switch (brushShape)
262 {
263 case BrushShape_Point:
264
265 setIcon (QIcon (QPixmap (":scenetoolbar/brush-point")));
266 tooltip += mTextureBrushWindow->toolTipPoint;
267 break;
268
269 case BrushShape_Square:
270
271 setIcon (QIcon (QPixmap (":scenetoolbar/brush-square")));
272 tooltip += mTextureBrushWindow->toolTipSquare;
273 break;
274
275 case BrushShape_Circle:
276
277 setIcon (QIcon (QPixmap (":scenetoolbar/brush-circle")));
278 tooltip += mTextureBrushWindow->toolTipCircle;
279 break;
280
281 case BrushShape_Custom:
282
283 setIcon (QIcon (QPixmap (":scenetoolbar/brush-custom")));
284 tooltip += mTextureBrushWindow->toolTipCustom;
285 break;
286 }
287
288 tooltip += "<p>(right click to access of previously used brush settings)";
289
290
291 CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
292
293 int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
294 int index = landtexturesCollection.searchId(mTextureBrushWindow->mBrushTexture);
295
296 if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
297 {
298 tooltip += "<p>Selected texture: " + QString::fromStdString(mTextureBrushWindow->mBrushTexture) + " ";
299
300 tooltip += landtexturesCollection.getData(index, landTextureFilename).value<QString>();
301 } else
302 {
303 tooltip += "<p>No selected texture or invalid texture";
304 }
305
306 tooltip += "<br>(drop texture here to change)";
307 setToolTip (tooltip);
308 }
309
showPanel(const QPoint & position)310 void CSVWidget::SceneToolTextureBrush::showPanel (const QPoint& position)
311 {
312 updatePanel();
313 mPanel->move (position);
314 mPanel->show();
315 }
316
updatePanel()317 void CSVWidget::SceneToolTextureBrush::updatePanel()
318 {
319 mTable->setRowCount (mBrushHistory.size());
320
321 for (int i = mBrushHistory.size()-1; i >= 0; --i)
322 {
323 CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
324 int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
325 int index = landtexturesCollection.searchId(mBrushHistory[i]);
326
327 if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
328 {
329 mTable->setItem (i, 1, new QTableWidgetItem (landtexturesCollection.getData(index, landTextureFilename).value<QString>()));
330 mTable->setItem (i, 0, new QTableWidgetItem (QString::fromStdString(mBrushHistory[i])));
331 } else
332 {
333 mTable->setItem (i, 1, new QTableWidgetItem ("Invalid/deleted texture"));
334 mTable->setItem (i, 0, new QTableWidgetItem (QString::fromStdString(mBrushHistory[i])));
335 }
336 }
337 }
338
updateBrushHistory(const std::string & brushTexture)339 void CSVWidget::SceneToolTextureBrush::updateBrushHistory (const std::string& brushTexture)
340 {
341 mBrushHistory.insert(mBrushHistory.begin(), brushTexture);
342 if(mBrushHistory.size() > 5) mBrushHistory.pop_back();
343 }
344
clicked(const QModelIndex & index)345 void CSVWidget::SceneToolTextureBrush::clicked (const QModelIndex& index)
346 {
347 if (index.column()==0 || index.column()==1)
348 {
349 std::string brushTexture = mBrushHistory[index.row()];
350 std::swap(mBrushHistory[index.row()], mBrushHistory[0]);
351 mTextureBrushWindow->setBrushTexture(brushTexture);
352 emit passTextureId(brushTexture);
353 updatePanel();
354 mPanel->hide();
355 }
356 }
357
activate()358 void CSVWidget::SceneToolTextureBrush::activate ()
359 {
360 QPoint position = QCursor::pos();
361 mTextureBrushWindow->mSizeSliders->mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
362 mTextureBrushWindow->mSizeSliders->mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
363 mTextureBrushWindow->move (position);
364 mTextureBrushWindow->show();
365 }
366
dragEnterEvent(QDragEnterEvent * event)367 void CSVWidget::SceneToolTextureBrush::dragEnterEvent (QDragEnterEvent *event)
368 {
369 emit passEvent(event);
370 event->accept();
371 }
dropEvent(QDropEvent * event)372 void CSVWidget::SceneToolTextureBrush::dropEvent (QDropEvent *event)
373 {
374 emit passEvent(event);
375 event->accept();
376 }
377