1 /*
2 * newtilesetdialog.cpp
3 * Copyright 2009-2010, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
4 *
5 * This file is part of Tiled.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "newtilesetdialog.h"
22 #include "ui_newtilesetdialog.h"
23
24 #include "documentmanager.h"
25 #include "imagecolorpickerwidget.h"
26 #include "mapdocument.h"
27 #include "session.h"
28 #include "utils.h"
29
30 #include <QFileDialog>
31 #include <QFileInfo>
32 #include <QImage>
33 #include <QMessageBox>
34 #include <QCheckBox>
35
36 using namespace Tiled;
37
38 namespace session {
39 static SessionOption<int> tilesetType { "tileset.type" };
40 static SessionOption<bool> embedInMap { "tileset.embedInMap" };
41 static SessionOption<bool> useTransparentColor { "tileset.useTransparentColor" };
42 static SessionOption<QColor> transparentColor { "tileset.transparentColor", Qt::magenta };
43 static SessionOption<QSize> tileSize { "tileset.tileSize", QSize(32, 32) };
44 static SessionOption<int> tilesetSpacing { "tileset.spacing" };
45 static SessionOption<int> tilesetMargin { "tileset.margin" };
46 } // namespace session
47
48 enum TilesetType {
49 TilesetImage,
50 ImageCollection
51 };
52
tilesetType(Ui::NewTilesetDialog * ui)53 static TilesetType tilesetType(Ui::NewTilesetDialog *ui)
54 {
55 switch (ui->tilesetType->currentIndex()) {
56 default:
57 case 0:
58 return TilesetImage;
59 case 1:
60 return ImageCollection;
61 }
62 }
63
NewTilesetDialog(QWidget * parent)64 NewTilesetDialog::NewTilesetDialog(QWidget *parent) :
65 QDialog(parent),
66 mUi(new Ui::NewTilesetDialog),
67 mNameWasEdited(false)
68 {
69 mUi->setupUi(this);
70 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
71 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
72 #endif
73
74 const QSize tileSize = session::tileSize;
75
76 mUi->tilesetType->setCurrentIndex(session::tilesetType);
77 mUi->embedded->setChecked(session::embedInMap);
78 mUi->useTransparentColor->setChecked(session::useTransparentColor);
79 mUi->colorButton->setColor(session::transparentColor);
80 mUi->tileWidth->setValue(tileSize.width());
81 mUi->tileHeight->setValue(tileSize.height());
82 mUi->spacing->setValue(session::tilesetSpacing);
83 mUi->margin->setValue(session::tilesetMargin);
84
85 connect(mUi->browseButton, &QAbstractButton::clicked, this, &NewTilesetDialog::browse);
86 connect(mUi->name, &QLineEdit::textEdited, this, &NewTilesetDialog::nameEdited);
87 connect(mUi->name, &QLineEdit::textChanged, this, &NewTilesetDialog::updateOkButton);
88 connect(mUi->embedded, &QCheckBox::toggled, this, &NewTilesetDialog::updateOkButton);
89 connect(mUi->image, &QLineEdit::textChanged, this, &NewTilesetDialog::updateOkButton);
90 connect(mUi->image, &QLineEdit::textChanged, this, &NewTilesetDialog::updateColorPickerButton);
91 connect(mUi->useTransparentColor, &QCheckBox::toggled, this, &NewTilesetDialog::updateColorPickerButton);
92 connect(mUi->tilesetType, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
93 this, &NewTilesetDialog::tilesetTypeChanged);
94 connect(mUi->dropperButton, &QAbstractButton::clicked, this, &NewTilesetDialog::pickColorFromImage);
95
96 connect(mUi->buttonBox, &QDialogButtonBox::accepted, this, &NewTilesetDialog::tryAccept);
97 connect(mUi->buttonBox, &QDialogButtonBox::rejected, this, &NewTilesetDialog::reject);
98
99 mUi->imageGroupBox->setVisible(session::tilesetType == 0);
100 updateOkButton();
101 }
102
~NewTilesetDialog()103 NewTilesetDialog::~NewTilesetDialog()
104 {
105 delete mUi;
106 }
107
108 /**
109 * Sets the path to start in by default.
110 *
111 * Also sets the image and name fields if the given path is a file.
112 */
setImagePath(const QString & path)113 void NewTilesetDialog::setImagePath(const QString &path)
114 {
115 mPath = path;
116
117 const QFileInfo fileInfo(path);
118 if (fileInfo.isFile()) {
119 mUi->tilesetType->setCurrentIndex(TilesetImage);
120 mUi->image->setText(path);
121 mUi->name->setText(fileInfo.completeBaseName());
122 }
123 }
124
setTileSize(QSize size)125 void NewTilesetDialog::setTileSize(QSize size)
126 {
127 mUi->tileWidth->setValue(size.width());
128 mUi->tileHeight->setValue(size.height());
129 }
130
131 /**
132 * Shows the dialog and returns the created tileset. Returns 0 if the
133 * dialog was cancelled.
134 */
createTileset()135 SharedTileset NewTilesetDialog::createTileset()
136 {
137 bool couldEmbed = false;
138 if (auto document = DocumentManager::instance()->currentDocument()) {
139 if (auto mapDocument = qobject_cast<MapDocument*>(document)) {
140 couldEmbed = true;
141 setTileSize(mapDocument->map()->tileSize());
142 }
143 }
144
145 mUi->embedded->setEnabled(couldEmbed);
146
147 setMode(CreateTileset);
148
149 if (exec() != QDialog::Accepted)
150 return SharedTileset();
151
152 return mNewTileset;
153 }
154
isEmbedded() const155 bool NewTilesetDialog::isEmbedded() const
156 {
157 return mUi->embedded->isChecked();
158 }
159
160 /**
161 * Shows the dialog and allows to change the given parameters.
162 *
163 * Returns whether the dialog was accepted.
164 */
editTilesetParameters(TilesetParameters & parameters)165 bool NewTilesetDialog::editTilesetParameters(TilesetParameters ¶meters)
166 {
167 setMode(EditTilesetParameters);
168
169 // todo: support remote files
170 mPath = parameters.imageSource.toLocalFile();
171 mUi->image->setText(parameters.imageSource.toString(QUrl::PreferLocalFile));
172
173 QColor transparentColor = parameters.transparentColor;
174 mUi->useTransparentColor->setChecked(transparentColor.isValid());
175 if (transparentColor.isValid())
176 mUi->colorButton->setColor(transparentColor);
177
178 mUi->tileWidth->setValue(parameters.tileSize.width());
179 mUi->tileHeight->setValue(parameters.tileSize.height());
180 mUi->spacing->setValue(parameters.tileSpacing);
181 mUi->margin->setValue(parameters.margin);
182
183 if (exec() != QDialog::Accepted)
184 return false;
185
186 parameters = TilesetParameters(*mNewTileset);
187 return true;
188 }
189
tryAccept()190 void NewTilesetDialog::tryAccept()
191 {
192 const QString name = mUi->name->text();
193
194 SharedTileset tileset;
195
196 if (tilesetType(mUi) == TilesetImage) {
197 const QString image = mUi->image->text();
198 const bool useTransparentColor = mUi->useTransparentColor->isChecked();
199 const QColor transparentColor = mUi->colorButton->color();
200 const int tileWidth = mUi->tileWidth->value();
201 const int tileHeight = mUi->tileHeight->value();
202 const int spacing = mUi->spacing->value();
203 const int margin = mUi->margin->value();
204
205 tileset = Tileset::create(name,
206 tileWidth, tileHeight,
207 spacing, margin);
208
209 if (useTransparentColor)
210 tileset->setTransparentColor(transparentColor);
211
212 if (!image.isEmpty()) {
213 if (!tileset->loadFromImage(image)) {
214 QMessageBox::critical(this, tr("Error"),
215 tr("Failed to load tileset image '%1'.")
216 .arg(image));
217 return;
218 }
219
220 if (tileset->tileCount() == 0) {
221 QMessageBox::critical(this, tr("Error"),
222 tr("No tiles found in the tileset image "
223 "when using the given tile size, "
224 "margin and spacing!"));
225 return;
226 }
227
228 tileset->syncExpectedColumnsAndRows();
229 }
230
231 if (mMode == CreateTileset) {
232 session::useTransparentColor = useTransparentColor;
233 session::transparentColor = transparentColor;
234 session::tileSize = QSize(tileWidth, tileHeight);
235 session::tilesetSpacing = spacing;
236 session::tilesetMargin = margin;
237 }
238 } else {
239 tileset = Tileset::create(name, 1, 1);
240 }
241
242 if (mMode == CreateTileset) {
243 session::tilesetType = mUi->tilesetType->currentIndex();
244 session::embedInMap = mUi->embedded->isChecked();
245 }
246
247 mNewTileset = tileset;
248 accept();
249 }
250
setMode(Mode mode)251 void NewTilesetDialog::setMode(Mode mode)
252 {
253 mMode = mode;
254
255 if (mode == EditTilesetParameters) {
256 mUi->tilesetType->setCurrentIndex(0);
257 setWindowTitle(QApplication::translate("NewTilesetDialog", "Edit Tileset"));
258 } else {
259 setWindowTitle(QApplication::translate("NewTilesetDialog", "New Tileset"));
260 }
261
262 mUi->tilesetGroupBox->setVisible(mode == CreateTileset);
263 updateOkButton();
264 }
265
browse()266 void NewTilesetDialog::browse()
267 {
268 const QString filter = Utils::readableImageFormatsFilter();
269 QString f = QFileDialog::getOpenFileName(this, tr("Tileset Image"), mPath,
270 filter);
271 if (!f.isEmpty()) {
272 mUi->image->setText(f);
273 mPath = f;
274
275 if (!mNameWasEdited)
276 mUi->name->setText(QFileInfo(f).completeBaseName());
277 }
278 }
279
nameEdited(const QString & name)280 void NewTilesetDialog::nameEdited(const QString &name)
281 {
282 mNameWasEdited = !name.isEmpty();
283 }
284
tilesetTypeChanged(int index)285 void NewTilesetDialog::tilesetTypeChanged(int index)
286 {
287 mUi->imageGroupBox->setVisible(index == 0);
288 updateOkButton();
289 }
290
updateOkButton()291 void NewTilesetDialog::updateOkButton()
292 {
293 QPushButton *okButton = mUi->buttonBox->button(QDialogButtonBox::Ok);
294
295 bool enabled = true;
296 QString text;
297
298 if (mMode == CreateTileset) {
299 enabled &= !mUi->name->text().isEmpty();
300 text = isEmbedded() ? tr("&OK") : tr("&Save As...");
301 } else {
302 text = tr("&OK");
303 }
304
305 if (tilesetType(mUi) == TilesetImage)
306 enabled &= !mUi->image->text().isEmpty();
307
308 okButton->setEnabled(enabled);
309 okButton->setText(text);
310 }
311
updateColorPickerButton()312 void NewTilesetDialog::updateColorPickerButton()
313 {
314 mUi->dropperButton->setEnabled(mUi->useTransparentColor->isChecked() &&
315 !mUi->image->text().isEmpty());
316 }
317
318 /**
319 * Shows the popup window used to select the color from the chosen image.
320 */
pickColorFromImage()321 void NewTilesetDialog::pickColorFromImage()
322 {
323 auto *popup = new ImageColorPickerWidget(mUi->dropperButton);
324 popup->setAttribute(Qt::WA_DeleteOnClose);
325
326 connect(popup, &ImageColorPickerWidget::colorSelected,
327 this, &NewTilesetDialog::colorSelected);
328
329 if (!popup->selectColor(mUi->image->text()))
330 delete popup;
331 }
332
colorSelected(QColor color)333 void NewTilesetDialog::colorSelected(QColor color)
334 {
335 mUi->colorButton->setColor(color);
336 }
337
338 #include "moc_newtilesetdialog.cpp"
339