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