void AbstractObjectTool::detachSelectedObjects() { MapDocument *currentMapDocument = mapDocument(); QList<MapObject *> templateInstances; /** * Stores the unique tilesets used by the templates * to avoid creating multiple undo commands for the same tileset */ QSet<SharedTileset> sharedTilesets; for (MapObject *object : mapDocument()->selectedObjects()) { if (object->templateObject()) { templateInstances.append(object); if (Tile *tile = object->cell().tile()) sharedTilesets.insert(tile->tileset()->sharedPointer()); } } auto changeMapObjectCommand = new DetachObjects(currentMapDocument, templateInstances); // Add any missing tileset used by the templates to the map map before detaching for (SharedTileset sharedTileset : sharedTilesets) { if (!currentMapDocument->map()->tilesets().contains(sharedTileset)) new AddTileset(currentMapDocument, sharedTileset, changeMapObjectCommand); } currentMapDocument->undoStack()->push(changeMapObjectCommand); }
void TileSelectionTool::mouseReleased(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::LeftButton) { mSelecting = false; MapDocument *document = mapDocument(); QRegion selection = document->selectedArea(); const QRect area = selectedArea(); switch (mSelectionMode) { case Replace: selection = area; break; case Add: selection += area; break; case Subtract: selection -= area; break; case Intersect: selection &= area; break; } if (selection != document->selectedArea()) { QUndoCommand *cmd = new ChangeSelectedArea(document, selection); document->undoStack()->push(cmd); } brushItem()->setTileRegion(QRegion()); updateStatusInfo(); } }
void SelectSameTileTool::mousePressed(QGraphicsSceneMouseEvent *event) { if (event->button() != Qt::LeftButton) return; const Qt::KeyboardModifiers modifiers = event->modifiers(); MapDocument *document = mapDocument(); QRegion selection = document->selectedArea(); if (modifiers == Qt::ShiftModifier) selection += mSelectedRegion; else if (modifiers == Qt::ControlModifier) selection -= mSelectedRegion; else if (modifiers == (Qt::ControlModifier | Qt::ShiftModifier)) selection &= mSelectedRegion; else selection = mSelectedRegion; if (selection != document->selectedArea()) { QUndoCommand *cmd = new ChangeSelectedArea(document, selection); document->undoStack()->push(cmd); } }
/** * Shows the context menu for map objects. The menu allows you to duplicate and * remove the map object, or do edit its properties. */ void MapObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { if (!mIsEditable) return; QMenu menu; QIcon dupIcon(QLatin1String(":images/16x16/stock-duplicate-16.png")); QIcon delIcon(QLatin1String(":images/16x16/edit-delete.png")); QIcon propIcon(QLatin1String(":images/16x16/document-properties.png")); QAction *dupAction = menu.addAction(dupIcon, tr("&Duplicate")); QAction *removeAction = menu.addAction(delIcon, tr("&Remove")); menu.addSeparator(); QAction *propertiesAction = menu.addAction(propIcon, tr("&Properties...")); Utils::setThemeIcon(removeAction, "edit-delete"); Utils::setThemeIcon(propertiesAction, "document-properties"); QAction *selectedAction = menu.exec(event->screenPos()); if (selectedAction == dupAction) { MapDocument *doc = mMapDocument; doc->undoStack()->push(new AddMapObject(doc, mObject->objectGroup(), mObject->clone())); } else if (selectedAction == removeAction) { MapDocument *doc = mMapDocument; doc->undoStack()->push(new RemoveMapObject(doc, mObject)); } else if (selectedAction == propertiesAction) { ObjectPropertiesDialog propertiesDialog(mMapDocument, mObject, event->widget()); propertiesDialog.exec(); } }
void DocumentManager::updateDocumentTab() { MapDocument *mapDocument = static_cast<MapDocument*>(sender()); const int index = mDocuments.indexOf(mapDocument); QString tabText = mapDocument->displayName(); if (mapDocument->isModified()) tabText.prepend(QLatin1Char('*')); mTabWidget->setTabText(index, tabText); mTabWidget->setTabToolTip(index, mapDocument->fileName()); }
void ResizeHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { Handle::mouseReleaseEvent(event); // If we resized the object, create an undo command MapObject *obj = mMapObjectItem->mapObject(); if (event->button() == Qt::LeftButton && mOldSize != obj->size()) { MapDocument *document = mMapObjectItem->mapDocument(); QUndoCommand *cmd = new ResizeMapObject(document, obj, mOldSize); document->undoStack()->push(cmd); } }
void MapObjectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { QGraphicsItem::mouseReleaseEvent(event); // If we got moved, create an undo command if (event->button() == Qt::LeftButton && mOldObjectPos != mObject->position()) { MapDocument *document = mMapDocument; QUndoCommand *cmd = new MoveMapObject(document, mObject, mOldObjectPos); document->undoStack()->push(cmd); } }
void Command::execute(bool inTerminal) const { // Save if save option is unset or true QSettings settings; QVariant variant = settings.value(QLatin1String("saveBeforeExecute"), true); if (variant.toBool()) { MapDocument *document = DocumentManager::instance()->currentDocument(); if (document) document->save(); } // Start the process new CommandProcess(*this, inTerminal); }
QString Command::finalCommand() const { QString finalCommand = command; // Perform map filename replacement MapDocument *mapDocument = DocumentManager::instance()->currentDocument(); if (mapDocument) { const QString fileName = mapDocument->fileName(); finalCommand.replace(QLatin1String("%mapfile"), QString(QLatin1String("\"%1\"")).arg(fileName)); } return finalCommand; }
QString TilesetDocument::displayName() const { QString displayName; if (isEmbedded()) { MapDocument *mapDocument = mMapDocuments.first(); displayName = mapDocument->displayName(); displayName += QLatin1String("#"); displayName += mTileset->name(); } else { displayName = QFileInfo(mFileName).fileName(); if (displayName.isEmpty()) displayName = tr("untitled.tsx"); } return displayName; }
/** * Returns the last location of a file chooser for the given file type. As long * as it was set using setLastPath(). * * When no last path for this file type exists yet, the path of the currently * selected map is returned. * * When no map is open, the user's 'Documents' folder is returned. */ QString Preferences::lastPath(FileType fileType) const { QString path = mSettings->value(lastPathKey(fileType)).toString(); if (path.isEmpty()) { DocumentManager *documentManager = DocumentManager::instance(); MapDocument *mapDocument = documentManager->currentDocument(); if (mapDocument) path = QFileInfo(mapDocument->fileName()).path(); } if (path.isEmpty()) { path = QStandardPaths::writableLocation( QStandardPaths::DocumentsLocation); } return path; }
void AbstractObjectTool::changeTile() { QList<MapObject*> tileObjects; MapDocument *currentMapDocument = mapDocument(); for (auto object : currentMapDocument->selectedObjects()) if (object->isTileObject()) tileObjects.append(object); auto changeMapObjectCommand = new ChangeMapObjectsTile(currentMapDocument, tileObjects, tile()); // Make sure the tileset is part of the document SharedTileset sharedTileset = tile()->tileset()->sharedPointer(); if (!currentMapDocument->map()->tilesets().contains(sharedTileset)) new AddTileset(currentMapDocument, sharedTileset, changeMapObjectCommand); currentMapDocument->undoStack()->push(changeMapObjectCommand); }
MapDocument *MapDocument::load(const QString &fileName, MapFormat *format, QString *error) { Map *map = format->read(fileName); if (!map) { if (error) *error = format->errorString(); return nullptr; } MapDocument *document = new MapDocument(map, fileName); document->setReaderFormat(format); if (format->hasCapabilities(MapFormat::Write)) document->setWriterFormat(format); return document; }
void DocumentManager::currentIndexChanged() { if (mSceneWithTool) { mSceneWithTool->disableSelectedTool(); mSceneWithTool = 0; } MapDocument *mapDocument = currentDocument(); if (mapDocument) mUndoGroup->setActiveStack(mapDocument->undoStack()); emit currentDocumentChanged(mapDocument); if (MapScene *mapScene = currentMapScene()) { mapScene->setSelectedTool(mSelectedTool); mapScene->enableSelectedTool(); mSceneWithTool = mapScene; } }
void BrokenLinksWidget::tryFixLink(const BrokenLink &link) { Document *document = mBrokenLinksModel->document(); Preferences *prefs = Preferences::instance(); if (link.type == TilesetImageSource || link.type == TilesetTileImageSource) { auto tilesetDocument = qobject_cast<TilesetDocument*>(document); if (!tilesetDocument) { // We need to open the tileset document in order to be able to make changes to it... const SharedTileset tileset = link.tileset()->sharedPointer(); DocumentManager::instance()->openTileset(tileset); return; } QString startLocation = QFileInfo(prefs->lastPath(Preferences::ImageFile)).absolutePath(); startLocation += QLatin1Char('/'); startLocation += QFileInfo(link.filePath()).fileName(); QString newFileName = QFileDialog::getOpenFileName(window(), tr("Locate File"), startLocation, Utils::readableImageFormatsFilter()); if (newFileName.isEmpty()) return; QImageReader reader(newFileName); QImage image = reader.read(); if (image.isNull()) { QMessageBox::critical(this, tr("Error Loading Image"), reader.errorString()); return; } if (link.type == TilesetImageSource) { TilesetParameters parameters(*link._tileset); parameters.imageSource = newFileName; auto command = new ChangeTilesetParameters(tilesetDocument, parameters); tilesetDocument->undoStack()->push(command); } else { auto command = new ChangeTileImageSource(tilesetDocument, link._tile, newFileName); tilesetDocument->undoStack()->push(command); } prefs->setLastPath(Preferences::ImageFile, newFileName); } else if (link.type == MapTilesetReference) { const QString allFilesFilter = tr("All Files (*)"); QString selectedFilter = allFilesFilter; QString filter = allFilesFilter; FormatHelper<TilesetFormat> helper(FileFormat::Read, filter); QString start = prefs->lastPath(Preferences::ExternalTileset); const QString fileName = QFileDialog::getOpenFileName(this, tr("Locate External Tileset"), start, helper.filter(), &selectedFilter); if (fileName.isEmpty()) return; QString error; // It could be, that we have already loaded this tileset. SharedTileset newTileset = TilesetManager::instance()->findTileset(fileName); if (!newTileset || !newTileset->loaded()) { newTileset = Tiled::readTileset(fileName, &error); if (!newTileset) { QMessageBox::critical(window(), tr("Error Reading Tileset"), error); return; } } MapDocument *mapDocument = static_cast<MapDocument*>(document); int index = mapDocument->map()->tilesets().indexOf(link._tileset->sharedPointer()); if (index != -1) document->undoStack()->push(new ReplaceTileset(mapDocument, index, newTileset)); prefs->setLastPath(Preferences::ExternalTileset, QFileInfo(fileName).path()); } }
void TileCollisionDock::setTile(Tile *tile) { if (mTile == tile) return; mTile = tile; mMapScene->disableSelectedTool(); MapDocument *previousDocument = mDummyMapDocument; mMapView->setEnabled(tile); if (tile) { Map::Orientation orientation = Map::Orthogonal; QSize tileSize = tile->size(); if (tile->tileset()->orientation() == Tileset::Isometric) { orientation = Map::Isometric; tileSize = tile->tileset()->gridSize(); } Map *map = new Map(orientation, 1, 1, tileSize.width(), tileSize.height()); map->addTileset(tile->sharedTileset()); TileLayer *tileLayer = new TileLayer(QString(), 0, 0, 1, 1); tileLayer->setCell(0, 0, Cell(tile)); map->addLayer(tileLayer); ObjectGroup *objectGroup; if (tile->objectGroup()) objectGroup = tile->objectGroup()->clone(); else objectGroup = new ObjectGroup; objectGroup->setDrawOrder(ObjectGroup::IndexOrder); map->setNextObjectId(objectGroup->highestObjectId() + 1); map->addLayer(objectGroup); mDummyMapDocument = new MapDocument(map); mDummyMapDocument->setCurrentLayer(objectGroup); mMapScene->setMapDocument(mDummyMapDocument); mToolManager->setMapDocument(mDummyMapDocument); mMapScene->enableSelectedTool(); connect(mDummyMapDocument->undoStack(), &QUndoStack::indexChanged, this, &TileCollisionDock::applyChanges); connect(mDummyMapDocument, &MapDocument::selectedObjectsChanged, this, &TileCollisionDock::selectedObjectsChanged); } else { mDummyMapDocument = nullptr; mMapScene->setMapDocument(nullptr); mToolManager->setMapDocument(nullptr); } emit dummyMapDocumentChanged(mDummyMapDocument); setHasSelectedObjects(false); if (previousDocument) { // Explicitly disconnect early from this signal, since it can get fired // from the QUndoStack destructor. disconnect(previousDocument->undoStack(), &QUndoStack::indexChanged, this, &TileCollisionDock::applyChanges); delete previousDocument; } }
void SceneObject::draw(ShaderStack *stack) { bool shaderOverridden = false; if ( !geometry()->isEmpty() ) { // If we have a shader override, push the override. QString shaderOverrideName = geometry()->shaderOverride(); if ( !shaderOverrideName.isNull() ) { ShaderProgram* program = resourceManager()->shader(shaderOverrideName); if ( program ) { shaderOverridden = true; stack->shaderPush(program); } } } // Multiply the modelWorld matrix by our current one. // This updates the shader uniform too. // We do this even if the geometry is empty, so that the // transformation will apply recursively. // It is the caller's responsibility to manage pushing // and popping of the m2w matrix. stack->modelToWorldPostMultiply(localToParent()); if ( !geometry()->isEmpty() ) { // Upload and bind the geometry. geometry()->upload(); geometry()->bindVertices(true); geometry()->bindIndices(true); // Apply the data format. geometry()->applyDataFormat(stack->shaderTop()); // If we're selected, set the global colour. bool pushedColor = false; MapDocument* doc = m_pScene->document(); if ( doc->selectedSet().contains(this) ) { pushedColor = true; stack->globalColorPush(); stack->globalColorSetTop(doc->selectedColor()); } // Apply the texture. QOpenGLTexture* tex = resourceManager()->texture(geometry()->texture(0)); tex->bind(0); // Draw. geometry()->draw(); if ( pushedColor ) { stack->globalColorPop(); } // Pop the shader if we pushed one earlier. if ( shaderOverridden ) { stack->shaderPop(); } } }