Example #1
0
void ButtonTaskMenu::createGroup()
{
    QDesignerFormWindowInterface *fw = formWindow();
    const ButtonList bl = buttonList(fw->cursor());
    // Do we need to remove the buttons from an existing group?
    QUndoCommand *removeCmd = 0;
    if (bl.front()->group()) {
        removeCmd = createRemoveButtonsCommand(fw, bl);
        if (!removeCmd)
            return;
    }
    // Add cmd
    CreateButtonGroupCommand *addCmd = new CreateButtonGroupCommand(fw);
    if (!addCmd->init(bl)) {
        qWarning("** WARNING Failed to initialize CreateButtonGroupCommand!");
        delete addCmd;
        return;
    }
    // Need a macro [even if we only have the add command] since the command might trigger additional commands
    QUndoStack *history = fw->commandHistory();
    history->beginMacro(addCmd->text());
    if (removeCmd)
        history->push(removeCmd);
    history->push(addCmd);
    history->endMacro();
}
Example #2
0
void ButtonTaskMenu::addToGroup(QAction *a)
{
    QButtonGroup *bg = qvariant_cast<QButtonGroup *>(a->data());
    Q_ASSERT(bg);

    QDesignerFormWindowInterface *fw = formWindow();
    const ButtonList bl = buttonList(fw->cursor());
    // Do we need to remove the buttons from an existing group?
    QUndoCommand *removeCmd = 0;
    if (bl.front()->group()) {
        removeCmd = createRemoveButtonsCommand(fw, bl);
        if (!removeCmd)
            return;
    }
    AddButtonsToGroupCommand *addCmd = new AddButtonsToGroupCommand(fw);
    addCmd->init(bl, bg);

    QUndoStack *history = fw->commandHistory();
    if (removeCmd) {
        history->beginMacro(addCmd->text());
        history->push(removeCmd);
        history->push(addCmd);
        history->endMacro();
    } else {
        history->push(addCmd);
    }
}
Example #3
0
void TilesetDock::embedTileset()
{
    Tileset *tileset = currentTileset();
    if (!tileset)
        return;

    if (!tileset->isExternal())
        return;

    // To embed a tileset we clone it, since further changes to that tileset
    // should not affect the exteral tileset.
    SharedTileset embeddedTileset = tileset->clone();

    QUndoStack *undoStack = mMapDocument->undoStack();
    int mapTilesetIndex = mMapDocument->map()->tilesets().indexOf(tileset->sharedPointer());

    // Tileset may not be part of the map yet
    if (mapTilesetIndex == -1)
        undoStack->push(new AddTileset(mMapDocument, embeddedTileset));
    else
        undoStack->push(new ReplaceTileset(mMapDocument, mapTilesetIndex, embeddedTileset));

    // Make sure the embedded tileset is selected
    int embeddedTilesetIndex = mTilesets.indexOf(embeddedTileset);
    if (embeddedTilesetIndex != -1)
        mTabBar->setCurrentIndex(embeddedTilesetIndex);
}
Example #4
0
void AddShape::performAction(Block* block) const
{
	Node* node = static_cast<Node*>(block);

	GenerateShape dialog(_generator, PantinEngine::MainWindow());

	if(dialog.createShape())
	{
		K_ASSERT( node->instance()->fastInherits<Model>() ); // The instance HAS to be a model in Nigel. Only models can contain geometry.

		Model* model = static_cast<Model*>(node->instance());

		QUndoStack* undoStack = PantinEngine::InstanceUndoStack( model );
		undoStack->setActive(true);

		undoStack->beginMacro(
				tr("Adding new %1 to node %2")
				.arg(_generator->shape()->blockName(), node->blockName())
			);

		AddBlock* addGeometry = new AddBlock(model->geometries(), dialog.geometry());
		undoStack->push(addGeometry);

		AddBlock* addReference = new AddBlock(node, dialog.geometryInstance());
		undoStack->push(addReference);

		undoStack->endMacro();
	}
}
Example #5
0
void PropertyBrowser::applyImageLayerValue(PropertyId id, const QVariant &val)
{
    ImageLayer *imageLayer = static_cast<ImageLayer*>(mObject);
    QUndoStack *undoStack = mMapDocument->undoStack();

    switch (id) {
    case ImageSourceProperty: {
        const QString imageSource = val.toString();
        const QColor &color = imageLayer->transparentColor();
        undoStack->push(new ChangeImageLayerProperties(mMapDocument,
                                                       imageLayer,
                                                       color,
                                                       imageSource));
        break;
    }
    case ColorProperty: {
        QColor color = val.value<QColor>();
        if (color == Qt::gray)
            color = QColor();

        const QString &imageSource = imageLayer->imageSource();
        undoStack->push(new ChangeImageLayerProperties(mMapDocument,
                                                       imageLayer,
                                                       color,
                                                       imageSource));
        break;
    }
    default:
        break;
    }
}
Example #6
0
void MapDocumentActionHandler::delete_()
{
    if (!mMapDocument)
        return;

    Layer *currentLayer = mMapDocument->currentLayer();
    if (!currentLayer)
        return;

    TileLayer *tileLayer = dynamic_cast<TileLayer*>(currentLayer);
    const QRegion &selectedArea = mMapDocument->selectedArea();
    const QList<MapObject*> &selectedObjects = mMapDocument->selectedObjects();

    QUndoStack *undoStack = mMapDocument->undoStack();
    undoStack->beginMacro(tr("Delete"));

    if (tileLayer && !selectedArea.isEmpty()) {
        undoStack->push(new EraseTiles(mMapDocument, tileLayer, selectedArea));
    } else if (!selectedObjects.isEmpty()) {
        foreach (MapObject *mapObject, selectedObjects)
            undoStack->push(new RemoveMapObject(mMapDocument, mapObject));
    }

    selectNone();
    undoStack->endMacro();
}
Example #7
0
void EditPolygonTool::splitSegments()
{
    if (mSelectedHandles.size() < 2)
        return;

    const PointIndexesByObject p = groupIndexesByObject(mSelectedHandles);
    QMapIterator<MapObject*, RangeSet<int> > i(p);

    QUndoStack *undoStack = mapDocument()->undoStack();
    bool macroStarted = false;

    while (i.hasNext()) {
        MapObject *object = i.next().key();
        const RangeSet<int> &indexRanges = i.value();

        const bool closed = object->shape() == MapObject::Polygon;
        QPolygonF oldPolygon = object->polygon();
        QPolygonF newPolygon = splitPolygonSegments(oldPolygon, indexRanges,
                                                    closed);

        if (newPolygon.size() > oldPolygon.size()) {
            if (!macroStarted) {
                undoStack->beginMacro(tr("Split Segments"));
                macroStarted = true;
            }

            undoStack->push(new ChangePolygon(mapDocument(), object,
                                              newPolygon,
                                              oldPolygon));
        }
    }

    if (macroStarted)
        undoStack->endMacro();
}
Example #8
0
void PropertiesDock::pasteProperties()
{
    auto clipboardManager = ClipboardManager::instance();

    Properties pastedProperties = clipboardManager->properties();
    if (pastedProperties.isEmpty())
        return;

    const QList<Object *> objects = mDocument->currentObjects();
    if (objects.isEmpty())
        return;

    QList<QUndoCommand*> commands;

    for (Object *object : objects) {
        Properties properties = object->properties();
        properties.merge(pastedProperties);

        if (object->properties() != properties) {
            commands.append(new ChangeProperties(mDocument, QString(), object,
                                                 properties));
        }
    }

    if (!commands.isEmpty()) {
        QUndoStack *undoStack = mDocument->undoStack();
        undoStack->beginMacro(tr("Paste Property/Properties", nullptr,
                                 pastedProperties.size()));

        for (QUndoCommand *command : commands)
            undoStack->push(command);

        undoStack->endMacro();
    }
}
Example #9
0
void MapPropertiesDialog::accept()
{
    int format = mLayerDataCombo->currentIndex();
    if (format == -1) {
        // this shouldn't happen!
        format = 0;
    }

    Map::LayerDataFormat newLayerDataFormat = static_cast<Map::LayerDataFormat>(format - 1);

    QUndoStack *undoStack = mMapDocument->undoStack();

    QColor newColor = mColorButton->color();
    if (newColor == Qt::darkGray)
        newColor = QColor();

    const Map *map = mMapDocument->map();
    bool localChanges = newColor != map->backgroundColor() ||
            newLayerDataFormat != map->layerDataFormat();

    if (localChanges) {
        undoStack->beginMacro(QCoreApplication::translate(
            "Undo Commands",
            "Change Map Properties"));

        undoStack->push(new ChangeMapProperties(mMapDocument,
                                                newColor,
                                                newLayerDataFormat));
    }

    PropertiesDialog::accept();

    if (localChanges)
        undoStack->endMacro();
}
void ImageLayerPropertiesDialog::accept()
{
    QUndoStack *undoStack = mMapDocument->undoStack();

    const QColor newColor = mColorButton->color() != Qt::gray
        ? mColorButton->color()
        : QColor();

    const QString newPath = mImage->text();
    const bool localChanges = newColor != mImageLayer->transparentColor() ||
            newPath != mImageLayer->imageSource();

    if (localChanges) {
        undoStack->beginMacro(QCoreApplication::translate(
            "Undo Commands",
            "Change Image Layer Properties"));

        undoStack->push(new ChangeImageLayerProperties(
                            mMapDocument,
                            mImageLayer,
                            newColor,
                            newPath));
    }

    PropertiesDialog::accept();

    if (localChanges)
        undoStack->endMacro();
}
Example #11
0
void PropertiesDock::removeProperties()
{
    Object *object = mDocument->currentObject();
    if (!object)
        return;

    const QList<QtBrowserItem*> items = mPropertyBrowser->selectedItems();
    if (items.isEmpty() || !mPropertyBrowser->allCustomPropertyItems(items))
        return;

    QStringList propertyNames;
    for (QtBrowserItem *item : items)
        propertyNames.append(item->property()->propertyName());

    QUndoStack *undoStack = mDocument->undoStack();
    undoStack->beginMacro(tr("Remove Property/Properties", nullptr,
                             propertyNames.size()));

    for (const QString &name : propertyNames) {
        undoStack->push(new RemoveProperty(mDocument,
                                           mDocument->currentObjects(),
                                           name));
    }

    undoStack->endMacro();
}
Example #12
0
void LayerModel::toggleOtherLayers(int layerIndex)
{
    if (mMap->layerCount() <= 1) // No other layers
        return;

    bool visibility = true;
    for (int i = 0; i < mMap->layerCount(); i++) {
        if (i == layerIndex)
            continue;

        Layer *layer = mMap->layerAt(i);
        if (layer->isVisible()) {
            visibility = false;
            break;
        }
    }

    QUndoStack *undoStack = mMapDocument->undoStack();
    if (visibility)
        undoStack->beginMacro(tr("Show Other Layers"));
    else
        undoStack->beginMacro(tr("Hide Other Layers"));

    for (int i = 0; i < mMap->layerCount(); i++) {
        if (i == layerIndex)
            continue;

        if (visibility != mMap->layerAt(i)->isVisible())
            undoStack->push(new SetLayerVisible(mMapDocument, i, visibility));
    }

    undoStack->endMacro();
}
Example #13
0
void eraseRegionObjectGroup(MapDocument *mapDocument,
                            ObjectGroup *layer,
                            const QRegion &where)
{
    QUndoStack *undo = mapDocument->undoStack();

    const auto objects = layer->objects();
    for (MapObject *obj : objects) {
        // TODO: we are checking bounds, which is only correct for rectangles and
        // tile objects. polygons and polylines are not covered correctly by this
        // erase method (we are in fact deleting too many objects)
        // TODO2: toAlignedRect may even break rects.

        // Convert the boundary of the object into tile space
        const QRectF objBounds = obj->boundsUseTile();
        QPointF tl = mapDocument->renderer()->pixelToTileCoords(objBounds.topLeft());
        QPointF tr = mapDocument->renderer()->pixelToTileCoords(objBounds.topRight());
        QPointF br = mapDocument->renderer()->pixelToTileCoords(objBounds.bottomRight());
        QPointF bl = mapDocument->renderer()->pixelToTileCoords(objBounds.bottomLeft());

        QRectF objInTileSpace;
        objInTileSpace.setTopLeft(tl);
        objInTileSpace.setTopRight(tr);
        objInTileSpace.setBottomRight(br);
        objInTileSpace.setBottomLeft(bl);

        const QRect objAlignedRect = objInTileSpace.toAlignedRect();
        if (where.intersects(objAlignedRect))
            undo->push(new RemoveMapObject(mapDocument, obj));
    }
}
Example #14
0
void TilesetDock::swapTiles(Tile *tileA, Tile *tileB)
{
    if (!mMapDocument)
        return;

    QUndoStack *undoStack = mMapDocument->undoStack();
    undoStack->push(new SwapTiles(mMapDocument, tileA, tileB));
}
Example #15
0
  void HydrogenTool::mousePressEvent(QGraphicsSceneMouseEvent *event)
  {
    QPointF downPos = event->buttonDownScenePos(event->button());

    // Check possible targets
    Atom* atom = scene()->atomAt(downPos);
    if (!atom)
      return;

    QUndoStack *stack = scene()->stack();

    if (event->button() == Qt::RightButton) {
      if (atom->numImplicitHydrogens() > 0)
        stack->push(new RemoveImplicitHydrogen(atom, tr("remove implicit hydrogen")));
    } else {
      stack->push(new AddImplicitHydrogen(atom, tr("add implicit hydrogen")));
    }
  }
Example #16
0
  void ReactionArrowTool::mousePressEvent(QGraphicsSceneMouseEvent *event)
  {
    QPointF downPos = event->buttonDownScenePos(event->button());
    QUndoStack *stack = scene()->stack();

    ReactionArrow *arrow = new ReactionArrow;
    stack->push(new AddItem(arrow, scene()));
    arrow->setPos(downPos);
  }
Example #17
0
/**
 * Convenience method that deals with some of the logic related to pasting
 * a group of objects.
 */
void ClipboardManager::pasteObjectGroup(const ObjectGroup *objectGroup,
                                        MapDocument *mapDocument,
                                        const MapView *view,
                                        PasteFlags flags)
{
    Layer *currentLayer = mapDocument->currentLayer();
    if (!currentLayer)
        return;

    ObjectGroup *currentObjectGroup = currentLayer->asObjectGroup();
    if (!currentObjectGroup)
        return;

    QPointF insertPos;

    if (!(flags & PasteInPlace)) {
        // Determine where to insert the objects
        const MapRenderer *renderer = mapDocument->renderer();
        const QPointF center = objectGroup->objectsBoundingRect().center();

        // Take the mouse position if the mouse is on the view, otherwise
        // take the center of the view.
        QPoint viewPos;
        if (view->underMouse())
            viewPos = view->mapFromGlobal(QCursor::pos());
        else
            viewPos = QPoint(view->width() / 2, view->height() / 2);

        const QPointF scenePos = view->mapToScene(viewPos);

        insertPos = renderer->screenToPixelCoords(scenePos) - center;
        SnapHelper(renderer).snap(insertPos);
    }

    QUndoStack *undoStack = mapDocument->undoStack();
    QList<MapObject*> pastedObjects;
    pastedObjects.reserve(objectGroup->objectCount());

    undoStack->beginMacro(tr("Paste Objects"));
    for (const MapObject *mapObject : objectGroup->objects()) {
        if (flags & PasteNoTileObjects && !mapObject->cell().isEmpty())
            continue;

        MapObject *objectClone = mapObject->clone();
        objectClone->resetId();
        objectClone->setPosition(objectClone->position() + insertPos);
        pastedObjects.append(objectClone);
        undoStack->push(new AddMapObject(mapDocument,
                                         currentObjectGroup,
                                         objectClone));
    }
    undoStack->endMacro();

    mapDocument->setSelectedObjects(pastedObjects);
}
Example #18
0
void PropertyBrowser::applyTilesetValue(PropertyBrowser::PropertyId id, const QVariant &val)
{
    Tileset *tileset = static_cast<Tileset*>(mObject);
    QUndoStack *undoStack = mMapDocument->undoStack();

    switch (id) {
    case NameProperty:
        undoStack->push(new RenameTileset(mMapDocument,
                                          tileset,
                                          val.toString()));
        break;
    case TileOffsetProperty:
        undoStack->push(new ChangeTilesetTileOffset(mMapDocument,
                                                    tileset,
                                                    val.toPoint()));
        break;
    default:
        break;
    }
}
void AbstractAspect::exec(QUndoCommand *cmd)
{
	Q_CHECK_PTR(cmd);
	QUndoStack *stack = undoStack();
	if (stack)
		stack->push(cmd);
	else {
		cmd->redo();
		delete cmd;
	}
}
Example #20
0
void EditPolygonTool::deleteNodes()
{
    if (mSelectedHandles.isEmpty())
        return;

    PointIndexesByObject p = groupIndexesByObject(mSelectedHandles);
    QMapIterator<MapObject*, RangeSet<int> > i(p);

    QUndoStack *undoStack = mapDocument()->undoStack();

    QString delText = tr("Delete %n Node(s)", "", mSelectedHandles.size());
    undoStack->beginMacro(delText);

    while (i.hasNext()) {
        MapObject *object = i.next().key();
        const RangeSet<int> &indexRanges = i.value();

        QPolygonF oldPolygon = object->polygon();
        QPolygonF newPolygon = oldPolygon;

        // Remove points, back to front to keep the indexes valid
        RangeSet<int>::Range it = indexRanges.end();
        RangeSet<int>::Range begin = indexRanges.begin();
        // assert: end != begin, since there is at least one entry
        do {
            --it;
            newPolygon.remove(it.first(), it.length());
        } while (it != begin);

        if (newPolygon.size() < 2) {
            // We've removed the entire object
            undoStack->push(new RemoveMapObject(mapDocument(), object));
        } else {
            undoStack->push(new ChangePolygon(mapDocument(), object,
                                              newPolygon,
                                              oldPolygon));
        }
    }

    undoStack->endMacro();
}
Example #21
0
void RaiseLowerHelper::push(const QList<QUndoCommand*> &commands,
                            const QString &text)
{
    if (commands.isEmpty())
        return;

    QUndoStack *undoStack = mMapDocument->undoStack();
    undoStack->beginMacro(text);
    foreach (QUndoCommand *command, commands)
        undoStack->push(command);
    undoStack->endMacro();
}
Example #22
0
void PropertyBrowser::applyTerrainValue(PropertyId id, const QVariant &val)
{
    Terrain *terrain = static_cast<Terrain*>(mObject);

    if (id == NameProperty) {
        QUndoStack *undoStack = mMapDocument->undoStack();
        undoStack->push(new RenameTerrain(mMapDocument,
                                          terrain->tileset(),
                                          terrain->id(),
                                          val.toString()));
    }
}
Example #23
0
void TilesetDock::removeTiles()
{
    TilesetView *view = currentTilesetView();
    if (!view)
        return;
    if (!view->selectionModel()->hasSelection())
        return;

    const QModelIndexList indexes = view->selectionModel()->selectedIndexes();
    const TilesetModel *model = view->tilesetModel();
    QList<Tile*> tiles;

    for (const QModelIndex &index : indexes)
        if (Tile *tile = model->tileAt(index))
            tiles.append(tile);

    auto matchesAnyTile = [&tiles] (const Cell &cell) {
        if (Tile *tile = cell.tile)
            return tiles.contains(tile);
        return false;
    };
    const bool inUse = hasTileReferences(mMapDocument, matchesAnyTile);

    // If the tileset is in use, warn the user and confirm removal
    if (inUse) {
        QMessageBox warning(QMessageBox::Warning,
                            tr("Remove Tiles"),
                            tr("One or more of the tiles to be removed are "
                               "still in use by the map!"),
                            QMessageBox::Yes | QMessageBox::No,
                            this);
        warning.setDefaultButton(QMessageBox::Yes);
        warning.setInformativeText(tr("Remove all references to these tiles?"));

        if (warning.exec() != QMessageBox::Yes)
            return;
    }

    QUndoStack *undoStack = mMapDocument->undoStack();
    undoStack->beginMacro(tr("Remove Tiles"));

    if (inUse)
        removeTileReferences(mMapDocument, matchesAnyTile);

    Tileset *tileset = view->tilesetModel()->tileset();
    undoStack->push(new RemoveTiles(mMapDocument, tileset, tiles));

    undoStack->endMacro();

    // Clear the current tiles, will be referencing the removed tiles
    setCurrentTiles(nullptr);
    setCurrentTile(nullptr);
}
Example #24
0
void ObjectPropertiesDialog::accept()
{
    const QString newName = mUi->name->text();
    const QString newType = mUi->type->text();

    const qreal newPosX = mUi->x->value();
    const qreal newPosY = mUi->y->value();
    const qreal newWidth = mUi->width->value();
    const qreal newHeight = mUi->height->value();

    bool changed = false;
    changed |= mMapObject->name() != newName;
    changed |= mMapObject->type() != newType;
    changed |= mMapObject->x() != newPosX;
    changed |= mMapObject->y() != newPosY;
    changed |= mMapObject->width() != newWidth;
    changed |= mMapObject->height() != newHeight;

    if (changed) {
        QUndoStack *undo = mMapDocument->undoStack();
        undo->beginMacro(tr("Change Object"));
        undo->push(new ChangeMapObject(mMapDocument, mMapObject,
                                       newName, newType));

        const QPointF oldPos = mMapObject->position();
        mMapObject->setX(newPosX);
        mMapObject->setY(newPosY);
        undo->push(new MoveMapObject(mMapDocument, mMapObject, oldPos));

        const QSizeF oldSize = mMapObject->size();
        mMapObject->setWidth(newWidth);
        mMapObject->setHeight(newHeight);
        undo->push(new ResizeMapObject(mMapDocument, mMapObject, oldSize));

        PropertiesDialog::accept(); // Let PropertiesDialog add its command
        undo->endMacro();
    } else {
        PropertiesDialog::accept();
    }
}
Example #25
0
void PropertiesDock::removeProperty()
{
    QtBrowserItem *item = mPropertyBrowser->currentItem();
    Object *object = mMapDocument->currentObject();
    if (!item || !object)
        return;

    const QString name = item->property()->propertyName();
    QUndoStack *undoStack = mMapDocument->undoStack();
    undoStack->push(new RemoveProperty(mMapDocument, mMapDocument->currentObjects(), name));

    // TODO: Would be nice to automatically select the next property
}
Example #26
0
static void removeTileReferences(MapDocument *mapDocument,
                                 std::function<bool(const Cell &)> condition)
{
    QUndoStack *undoStack = mapDocument->undoStack();
    undoStack->beginMacro(QCoreApplication::translate("Undo Commands", "Remove Tiles"));

    for (Layer *layer : mapDocument->map()->layers()) {
        if (TileLayer *tileLayer = layer->asTileLayer()) {
            const QRegion refs = tileLayer->region(condition);
            if (!refs.isEmpty())
                undoStack->push(new EraseTiles(mapDocument, tileLayer, refs));

        } else if (ObjectGroup *objectGroup = layer->asObjectGroup()) {
            for (MapObject *object : *objectGroup) {
                if (condition(object->cell()))
                    undoStack->push(new RemoveMapObject(mapDocument, object));
            }
        }
    }

    undoStack->endMacro();
}
Example #27
0
void TileCollisionDock::applyChanges()
{
    if (mSynchronizing)
        return;

    ObjectGroup *objectGroup = static_cast<ObjectGroup*>(mDummyMapDocument->map()->layerAt(1));
    ObjectGroup *clonedGroup = objectGroup->clone();

    QUndoStack *undoStack = mTilesetDocument->undoStack();
    mApplyingChanges = true;
    undoStack->push(new ChangeTileObjectGroup(mTilesetDocument, mTile, clonedGroup));
    mApplyingChanges = false;
}
Example #28
0
void ObjectContainer::modifyCmd(const QJsonObject& data, Modifications mod)
{
	ObjectItem* item = getItem(data["id"].toInt());

	if (!item)
		return;

	QJsonObject oldData = item->data().toJsonObject();
	if (oldData != data)
	{
		QUndoCommand* cmd = new ModifyObjectCmd(oldData, data, item, mod);
		g_undoStack.push(cmd);
	}
}
Example #29
0
void ObjectContainer::addCmd(const QJsonObject& data)
{
	if (stringToObjectType(data["type"].toString().toStdString()) == ObjectType::VoxelGrid)
	{
		if (m_voxelGridAdded)
		{
			StaticLogger::logit("WARNING: Currently, multiple VoxelGrids are not supported. Creation aborted!");
			return;
		}
		m_voxelGridAdded = true;
	}
	QUndoCommand* cmd = new AddObjectCmd(data, this);
	g_undoStack.push(cmd);
}
Example #30
0
void ButtonGroupMenu::breakGroup()
{
    BreakButtonGroupCommand *cmd = new BreakButtonGroupCommand(m_formWindow);
    if (cmd->init(m_buttonGroup)) {
        // Need a macro since the command might trigger additional commands
        QUndoStack *history = m_formWindow->commandHistory();
        history->beginMacro(cmd->text());
        history->push(cmd);
        history->endMacro();
    } else {
        qWarning("** WARNING Failed to initialize BreakButtonGroupCommand!");
        delete cmd;
    }
}