Пример #1
0
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);
}
Пример #2
0
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();
    }
}
Пример #3
0
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);
    }
}
Пример #4
0
/**
 * 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();
    }
}
Пример #5
0
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());
}
Пример #6
0
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);
    }
}
Пример #7
0
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);
    }
}
Пример #8
0
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);
}
Пример #9
0
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;
}
Пример #10
0
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;
}
Пример #11
0
/**
 * 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;
}
Пример #12
0
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);
}
Пример #13
0
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;
}
Пример #14
0
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;
    }
}
Пример #15
0
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());
    }
}
Пример #16
0
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;
    }
}
Пример #17
0
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();
        }
    }
}