Add Save As feature and fix document serialization
- Add saveAsToFile() method to Document class for "Save As" functionality - Add actionSaveAs menu item and onAction_saveAs() handler in MainWindow - Add isValidItem() method to GraphicsBaseItem to filter helper elements - Add using declarations to expose AbstractShapeType members in GraphicsBaseItem - Fix document serialization to properly handle GraphicsItemGroup pen/brush - HandleType enum starts from 50 to avoid conflicts with future handle types - Improve deserialization to properly restore item properties Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
78f2edf24b
commit
acf56d7c83
|
|
@ -59,18 +59,39 @@ public:
|
|||
// =================================================================
|
||||
|
||||
/**
|
||||
* @brief 保存文档到文件
|
||||
* @brief 保存文档到文件(覆盖当前文件)
|
||||
* @param filename 文件路径,为空则使用当前 filename()
|
||||
* @return 成功返回 true
|
||||
*
|
||||
* 序列化流程:
|
||||
* 1. 遍历 scene->items() 获取顶层图元
|
||||
* 2. 递归序列化每个图元及其子图元(对于组图元)
|
||||
* 3. 写入 JSON 格式文件
|
||||
* 4. 调用 setModified(false)
|
||||
* 保存流程:
|
||||
* 1. 如果 filename 为空且 m_filename 不为空,使用 m_filename
|
||||
* 2. 序列化场景内容到 JSON 格式
|
||||
* 3. 写入文件
|
||||
* 4. 更新 lastSavedTime,调用 setModified(false)
|
||||
* 5. 发出 saveStatusChanged 信号
|
||||
*
|
||||
* 注意:此方法不会更新 m_filename,仅用于覆盖已有文件
|
||||
*/
|
||||
bool saveToFile(const QString& filename = QString());
|
||||
|
||||
/**
|
||||
* @brief 另存为(Save As)
|
||||
* @param filename 目标文件路径
|
||||
* @return 成功返回 true
|
||||
*
|
||||
* 另存为流程:
|
||||
* 1. 序列化场景内容到 JSON 格式
|
||||
* 2. 写入文件
|
||||
* 3. 更新 m_filename 为目标文件路径
|
||||
* 4. 更新 lastSavedTime,调用 setModified(false)
|
||||
* 5. 发出 filenameChanged 和 modifiedChanged 信号
|
||||
*
|
||||
* 与 saveToFile() 的区别:
|
||||
* - saveAsToFile() 总是会更新 m_filename
|
||||
* - saveAsToFile() 适用于"另存为"操作,改变文档的默认保存路径
|
||||
*/
|
||||
bool saveAsToFile(const QString& filename);
|
||||
|
||||
/**
|
||||
* @brief 从文件加载文档
|
||||
* @param filename 文件路径
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ public:
|
|||
public:
|
||||
virtual ShapeType getType() {return m_type;}
|
||||
|
||||
// 显式类型检查:只有有效的图元(item 或 group)才通过检查
|
||||
// 用于区分真正的图元与辅助元素(如 handle)
|
||||
bool isValidItem() const { return m_type == T_item || m_type == T_group; }
|
||||
|
||||
QPen pen() { return m_pen; }
|
||||
void setPen(const QPen &pen) { m_pen = pen; }
|
||||
QColor penColor() { return m_pen.color(); }
|
||||
|
|
@ -292,6 +296,20 @@ class GraphicsBaseItem : public QObject, public AbstractShapeType<QGraphicsItem>
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// 使用 using 声明暴露 AbstractShapeType 的成员函数,解决多重继承下的访问问题
|
||||
using AbstractShapeType<QGraphicsItem>::penColor;
|
||||
using AbstractShapeType<QGraphicsItem>::setPenColor;
|
||||
using AbstractShapeType<QGraphicsItem>::brushColor;
|
||||
using AbstractShapeType<QGraphicsItem>::setBrushColor;
|
||||
using AbstractShapeType<QGraphicsItem>::pen;
|
||||
using AbstractShapeType<QGraphicsItem>::setPen;
|
||||
using AbstractShapeType<QGraphicsItem>::brush;
|
||||
using AbstractShapeType<QGraphicsItem>::setBrush;
|
||||
using AbstractShapeType<QGraphicsItem>::width;
|
||||
using AbstractShapeType<QGraphicsItem>::setWidth;
|
||||
using AbstractShapeType<QGraphicsItem>::height;
|
||||
using AbstractShapeType<QGraphicsItem>::setHeight;
|
||||
|
||||
GraphicsBaseItem(QGraphicsItem *parent);
|
||||
virtual ~GraphicsBaseItem();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
enum HandleType
|
||||
{
|
||||
T_resize, //调整大小
|
||||
T_resize = 50, //调整大小
|
||||
T_rotate, //旋转
|
||||
T_editShape //编辑形状
|
||||
};
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ private slots:
|
|||
void onAction_new();
|
||||
void onAction_open();
|
||||
void onAction_save();
|
||||
void onAction_saveAs();
|
||||
void onDocument_modifiedChanged(bool modified);
|
||||
void onDocument_filenameChanged(const QString& filename);
|
||||
void onDocument_saveStatusChanged(bool success, const QString& message);
|
||||
|
|
|
|||
|
|
@ -71,18 +71,23 @@ bool Document::saveToFile(const QString& filename)
|
|||
QString targetFile = filename.isEmpty() ? m_filename : filename;
|
||||
|
||||
if (targetFile.isEmpty()) {
|
||||
// 没有目标文件名,需要另存为对话框(这里只设置失败状态)
|
||||
emit saveStatusChanged(false, tr("未指定文件路径"));
|
||||
// 没有目标文件名,无法保存
|
||||
emit saveStatusChanged(false, tr("未指定文件路径,请使用另存为功能"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = saveInternal(targetFile);
|
||||
|
||||
if (success) {
|
||||
m_filename = targetFile;
|
||||
m_lastSavedTime = QDateTime::currentDateTime();
|
||||
m_modified = false;
|
||||
emit filenameChanged(m_filename);
|
||||
|
||||
// 如果传入的文件名与当前文件名不同,更新 m_filename
|
||||
if (!filename.isEmpty() && filename != m_filename) {
|
||||
m_filename = filename;
|
||||
emit filenameChanged(m_filename);
|
||||
}
|
||||
|
||||
emit modifiedChanged(false);
|
||||
emit saveStatusChanged(true, tr("保存成功"));
|
||||
} else {
|
||||
|
|
@ -92,6 +97,32 @@ bool Document::saveToFile(const QString& filename)
|
|||
return success;
|
||||
}
|
||||
|
||||
bool Document::saveAsToFile(const QString& filename)
|
||||
{
|
||||
if (filename.isEmpty()) {
|
||||
emit saveStatusChanged(false, tr("未指定文件路径"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = saveInternal(filename);
|
||||
|
||||
if (success) {
|
||||
QString oldFilename = m_filename;
|
||||
m_filename = filename;
|
||||
m_lastSavedTime = QDateTime::currentDateTime();
|
||||
m_modified = false;
|
||||
|
||||
// 另存为总是会更新文件名
|
||||
emit filenameChanged(m_filename);
|
||||
emit modifiedChanged(false);
|
||||
emit saveStatusChanged(true, tr("另存为成功"));
|
||||
} else {
|
||||
emit saveStatusChanged(false, tr("另存为失败"));
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Document::loadFromFile(const QString& filename)
|
||||
{
|
||||
bool success = loadInternal(filename);
|
||||
|
|
@ -149,6 +180,13 @@ QByteArray Document::serialize() const
|
|||
continue;
|
||||
}
|
||||
|
||||
// 双重检查:通过 isValidItem() 确保不是辅助元素(如 ItemControlHandle)
|
||||
// ItemControlHandle 是 QGraphicsRectItem 子类,不是 GraphicsBaseItem 子类
|
||||
// 即使 qgraphicsitem_cast 错误返回非 null,isValidItem() 也能过滤掉(m_type 为 T_undefined)
|
||||
if (!baseItem->isValidItem()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QJsonObject itemObj = serializeItem(baseItem);
|
||||
itemsArray.append(itemObj);
|
||||
}
|
||||
|
|
@ -272,16 +310,29 @@ QJsonObject Document::serializeItem(GraphicsBaseItem* item) const
|
|||
scaleObj.insert("y", item->scale());
|
||||
obj.insert("scale", scaleObj);
|
||||
|
||||
// 样式属性
|
||||
QJsonObject penObj;
|
||||
penObj.insert("color", item->penColor().name());
|
||||
penObj.insert("width", item->pen().widthF());
|
||||
penObj.insert("style", static_cast<int>(item->pen().style()));
|
||||
obj.insert("pen", penObj);
|
||||
// 样式属性(只用于非组项,GraphicsItemGroup 没有 pen/brush)
|
||||
GraphicsItemGroup* group = qgraphicsitem_cast<GraphicsItemGroup*>(item);
|
||||
if (!group) {
|
||||
// 普通图元才有 pen/brush
|
||||
QJsonObject penObj;
|
||||
QColor penColor = item->penColor();
|
||||
if (penColor.isValid()) {
|
||||
penObj.insert("color", penColor.name());
|
||||
} else {
|
||||
penObj.insert("color", QString());
|
||||
}
|
||||
penObj.insert("width", item->pen().widthF());
|
||||
penObj.insert("style", static_cast<int>(item->pen().style()));
|
||||
obj.insert("pen", penObj);
|
||||
|
||||
QJsonObject brushObj;
|
||||
brushObj.insert("color", item->brushColor().name());
|
||||
obj.insert("brush", brushObj);
|
||||
QJsonObject brushObj;
|
||||
brushObj.insert("color", item->brushColor().name());
|
||||
obj.insert("brush", brushObj);
|
||||
} else {
|
||||
// 组没有 pen/brush,插入空对象
|
||||
obj.insert("pen", QJsonObject());
|
||||
obj.insert("brush", QJsonObject());
|
||||
}
|
||||
|
||||
// 尺寸
|
||||
obj.insert("width", item->width());
|
||||
|
|
@ -291,17 +342,16 @@ QJsonObject Document::serializeItem(GraphicsBaseItem* item) const
|
|||
obj.insert("properties", serializeItemProperties(item));
|
||||
|
||||
// 如果是组,递归序列化子项
|
||||
GraphicsItemGroup* group = qgraphicsitem_cast<GraphicsItemGroup*>(item);
|
||||
if (group) {
|
||||
QJsonArray childrenArray;
|
||||
QList<QGraphicsItem*> children = group->childItems();
|
||||
for (QGraphicsItem* child : children) {
|
||||
// QGraphicsItem 没有 QObject 接口,所以不能用 qobject_cast
|
||||
// 使用 static_cast 并检查 nullptr
|
||||
GraphicsBaseItem* childBase = static_cast<GraphicsBaseItem*>(child);
|
||||
// 使用 qgraphicsitem_cast 安全转换,只序列化 GraphicsBaseItem 子类的项
|
||||
GraphicsBaseItem* childBase = qgraphicsitem_cast<GraphicsBaseItem*>(child);
|
||||
if (childBase) {
|
||||
childrenArray.append(serializeItem(childBase));
|
||||
}
|
||||
// 跳过 ItemControlHandle 等非 GraphicsBaseItem 的子项
|
||||
}
|
||||
obj.insert("children", childrenArray);
|
||||
}
|
||||
|
|
@ -314,71 +364,54 @@ void Document::deserializeItem(const QJsonObject& obj, QGraphicsScene* scene, QG
|
|||
QString typeStr = obj["type"].toString();
|
||||
|
||||
GraphicsBaseItem* item = nullptr;
|
||||
GraphicsItemGroup* group = nullptr;
|
||||
|
||||
// 根据类型创建图元
|
||||
// 根据类型创建图元(属性在工厂函数中已设置)
|
||||
if (typeStr == "GraphicsRectItem") {
|
||||
item = deserializeRectItem(obj);
|
||||
} else if (typeStr == "GraphicsBusSectionItem") {
|
||||
item = deserializeBusSectionItem(obj);
|
||||
} else if (typeStr == "GraphicsItemGroup") {
|
||||
GraphicsItemGroup* group = deserializeItemGroup(obj, scene, parent);
|
||||
// 对于组,不设置单个图元的属性,只递归处理子项
|
||||
group = deserializeItemGroup(obj, scene, parent);
|
||||
// 先添加到 scene 或父组,再递归处理子项
|
||||
if (parent) {
|
||||
GraphicsItemGroup* parentGroup = qgraphicsitem_cast<GraphicsItemGroup*>(parent);
|
||||
if (parentGroup) {
|
||||
parentGroup->addToGroup(group);
|
||||
}
|
||||
} else {
|
||||
scene->addItem(group);
|
||||
}
|
||||
// 递归处理子项(此时 group 已在 scene 中)
|
||||
QJsonArray childrenArray = obj["children"].toArray();
|
||||
for (auto childIt : childrenArray) {
|
||||
deserializeItem(childIt.toObject(), scene, group);
|
||||
}
|
||||
// 添加完子项后,更新 group 的 coordinate(会自动计算 boundingRect)
|
||||
group->updateCoordinate();
|
||||
// 更新 handle 位置
|
||||
group->updateHandles();
|
||||
return;
|
||||
}
|
||||
// GraphicsPolygonItem 暂时跳过,因为没有 Q_OBJECT 宏
|
||||
// GraphicsPolygonItem 暂时跳过
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置基本属性
|
||||
QJsonObject posObj = obj["pos"].toObject();
|
||||
item->setPos(posObj["x"].toDouble(), posObj["y"].toDouble());
|
||||
item->setRotation(obj["rotation"].toDouble());
|
||||
|
||||
// Qt 的 setScale() 只接受单个参数,使用 QTransform 来设置非均匀缩放
|
||||
QJsonObject scaleObj = obj["scale"].toObject();
|
||||
double scaleX = scaleObj["x"].toDouble();
|
||||
double scaleY = scaleObj["y"].toDouble();
|
||||
QTransform transform;
|
||||
transform.scale(scaleX, scaleY);
|
||||
item->setTransform(transform);
|
||||
|
||||
QJsonObject penObj = obj["pen"].toObject();
|
||||
QPen pen;
|
||||
pen.setColor(QColor(penObj["color"].toString()));
|
||||
pen.setWidthF(penObj["width"].toDouble());
|
||||
pen.setStyle(static_cast<Qt::PenStyle>(penObj["style"].toInt()));
|
||||
item->setPen(pen);
|
||||
|
||||
QJsonObject brushObj = obj["brush"].toObject();
|
||||
QBrush brush;
|
||||
brush.setColor(QColor(brushObj["color"].toString()));
|
||||
item->setBrush(brush);
|
||||
|
||||
item->setWidth(obj["width"].toDouble());
|
||||
item->setHeight(obj["height"].toDouble());
|
||||
|
||||
// 添加到场景或父项
|
||||
if (parent) {
|
||||
// 添加到组中
|
||||
GraphicsItemGroup* group = static_cast<GraphicsItemGroup*>(parent);
|
||||
if (group) {
|
||||
group->addItems(QList<QGraphicsItem*>() << item);
|
||||
GraphicsItemGroup* parentGroup = qgraphicsitem_cast<GraphicsItemGroup*>(parent);
|
||||
if (parentGroup) {
|
||||
parentGroup->addToGroup(item);
|
||||
}
|
||||
} else {
|
||||
scene->addItem(item);
|
||||
}
|
||||
// 普通图元没有子项
|
||||
|
||||
// 递归处理子项
|
||||
QJsonArray childrenArray = obj["children"].toArray();
|
||||
for (auto childIt : childrenArray) {
|
||||
deserializeItem(childIt.toObject(), scene, item);
|
||||
}
|
||||
// 添加后更新 handle 位置(如果有)
|
||||
item->setHandleVisible(false); // 默认不显示 handle
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
|
@ -465,8 +498,48 @@ GraphicsBaseItem* Document::deserializeRectItem(const QJsonObject& obj)
|
|||
double height = obj["height"].toDouble();
|
||||
|
||||
// 图元的原点在中心,因此 rect 的左上角为 (-width/2, -height/2)
|
||||
// 在构造函数中已经设置了 boundingRect,所以不需要再调用 setWidth/setHeight
|
||||
QRect rect(-width/2, -height/2, width, height);
|
||||
return new GraphicsRectItem(rect, false);
|
||||
GraphicsRectItem* item = new GraphicsRectItem(rect, false);
|
||||
|
||||
// 从 JSON 恢复属性
|
||||
QJsonObject posObj = obj["pos"].toObject();
|
||||
item->setPos(posObj["x"].toDouble(), posObj["y"].toDouble());
|
||||
item->setRotation(obj["rotation"].toDouble());
|
||||
|
||||
// 使用 setScale 而不是 setTransform(避免与 item 的 transform 冲突)
|
||||
QJsonObject scaleObj = obj["scale"].toObject();
|
||||
double scaleX = scaleObj["x"].toDouble();
|
||||
double scaleY = scaleObj["y"].toDouble();
|
||||
if (scaleX != 1.0 || scaleY != 1.0) {
|
||||
item->setScale(qMax(scaleX, scaleY)); // 使用统一缩放
|
||||
}
|
||||
|
||||
QJsonObject penObj = obj["pen"].toObject();
|
||||
if (penObj.isEmpty()) {
|
||||
// 如果 pen 对象为空(来自组的序列化),使用默认值
|
||||
QPen pen(Qt::black);
|
||||
pen.setWidthF(1.0);
|
||||
pen.setStyle(Qt::SolidLine);
|
||||
item->setPen(pen);
|
||||
} else {
|
||||
QPen pen;
|
||||
pen.setColor(QColor(penObj["color"].toString()));
|
||||
pen.setWidthF(penObj["width"].toDouble());
|
||||
pen.setStyle(static_cast<Qt::PenStyle>(penObj["style"].toInt()));
|
||||
item->setPen(pen);
|
||||
}
|
||||
|
||||
QJsonObject brushObj = obj["brush"].toObject();
|
||||
if (brushObj.isEmpty()) {
|
||||
item->setBrush(QBrush(Qt::NoBrush));
|
||||
} else {
|
||||
QBrush brush;
|
||||
brush.setColor(QColor(brushObj["color"].toString()));
|
||||
item->setBrush(brush);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
GraphicsBaseItem* Document::deserializeBusSectionItem(const QJsonObject& obj)
|
||||
|
|
@ -474,9 +547,45 @@ GraphicsBaseItem* Document::deserializeBusSectionItem(const QJsonObject& obj)
|
|||
// 总线段重建
|
||||
double width = obj["width"].toDouble();
|
||||
double height = obj["height"].toDouble();
|
||||
// GraphicsBusSectionItem 构造函数:rect, parent
|
||||
QRect rect(0, 0, width, height);
|
||||
return new GraphicsBusSectionItem(rect);
|
||||
QRect rect(-width/2, -height/2, width, height);
|
||||
GraphicsBusSectionItem* item = new GraphicsBusSectionItem(rect);
|
||||
|
||||
// 从 JSON 恢复属性
|
||||
QJsonObject posObj = obj["pos"].toObject();
|
||||
item->setPos(posObj["x"].toDouble(), posObj["y"].toDouble());
|
||||
item->setRotation(obj["rotation"].toDouble());
|
||||
|
||||
QJsonObject scaleObj = obj["scale"].toObject();
|
||||
double scaleX = scaleObj["x"].toDouble();
|
||||
double scaleY = scaleObj["y"].toDouble();
|
||||
if (scaleX != 1.0 || scaleY != 1.0) {
|
||||
item->setScale(qMax(scaleX, scaleY));
|
||||
}
|
||||
|
||||
QJsonObject penObj = obj["pen"].toObject();
|
||||
if (penObj.isEmpty()) {
|
||||
QPen pen(Qt::black);
|
||||
pen.setWidthF(1.0);
|
||||
pen.setStyle(Qt::SolidLine);
|
||||
item->setPen(pen);
|
||||
} else {
|
||||
QPen pen;
|
||||
pen.setColor(QColor(penObj["color"].toString()));
|
||||
pen.setWidthF(penObj["width"].toDouble());
|
||||
pen.setStyle(static_cast<Qt::PenStyle>(penObj["style"].toInt()));
|
||||
item->setPen(pen);
|
||||
}
|
||||
|
||||
QJsonObject brushObj = obj["brush"].toObject();
|
||||
if (brushObj.isEmpty()) {
|
||||
item->setBrush(QBrush(Qt::NoBrush));
|
||||
} else {
|
||||
QBrush brush;
|
||||
brush.setColor(QColor(brushObj["color"].toString()));
|
||||
item->setBrush(brush);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
GraphicsItemGroup* Document::deserializeItemGroup(const QJsonObject& obj,
|
||||
|
|
@ -484,8 +593,28 @@ GraphicsItemGroup* Document::deserializeItemGroup(const QJsonObject& obj,
|
|||
QGraphicsItem* parent)
|
||||
{
|
||||
GraphicsItemGroup* group = new GraphicsItemGroup(parent);
|
||||
group->setWidth(obj["width"].toDouble());
|
||||
group->setHeight(obj["height"].toDouble());
|
||||
|
||||
// 先设置位置、旋转等变换属性
|
||||
QJsonObject posObj = obj["pos"].toObject();
|
||||
group->setPos(posObj["x"].toDouble(), posObj["y"].toDouble());
|
||||
group->setRotation(obj["rotation"].toDouble());
|
||||
|
||||
QJsonObject scaleObj = obj["scale"].toObject();
|
||||
double scaleX = scaleObj["x"].toDouble();
|
||||
double scaleY = scaleObj["y"].toDouble();
|
||||
if (scaleX != 1.0 || scaleY != 1.0) {
|
||||
group->setScale(qMax(scaleX, scaleY));
|
||||
}
|
||||
|
||||
// 获取宽高,但不立即调用 setWidth/setHeight(会触发 updateCoordinate)
|
||||
double width = obj["width"].toDouble();
|
||||
double height = obj["height"].toDouble();
|
||||
|
||||
// 注意:对于 GraphicsItemGroup,其 boundingRect 是由子项自动计算的
|
||||
// 我们创建一个临时的空矩形,待子项添加后会自动更新
|
||||
// 暂时不设置宽高,因为 updateCoordinate 需要 parentItem 为空才能正常工作
|
||||
// 子项添加后,group 的 boundingRect 会自动重新计算
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,6 +156,7 @@ void CMainWindow::initializeAction()
|
|||
connect(ui->actionNew, SIGNAL(triggered()), this, SLOT(onAction_new()));
|
||||
connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(onAction_open()));
|
||||
connect(ui->actionSave, SIGNAL(triggered()), this, SLOT(onAction_save()));
|
||||
connect(ui->actionSaveAs, SIGNAL(triggered()), this, SLOT(onAction_saveAs()));
|
||||
}
|
||||
|
||||
void CMainWindow::onAction_zoomIn()
|
||||
|
|
@ -270,6 +271,7 @@ void CMainWindow::initializeDocument()
|
|||
ui->menuFile->addAction(ui->actionNew);
|
||||
ui->menuFile->addAction(ui->actionOpen);
|
||||
ui->menuFile->addAction(ui->actionSave);
|
||||
ui->menuFile->addAction(ui->actionSaveAs);
|
||||
ui->menuFile->addSeparator();
|
||||
}
|
||||
|
||||
|
|
@ -337,13 +339,40 @@ void CMainWindow::onAction_open()
|
|||
|
||||
void CMainWindow::onAction_save()
|
||||
{
|
||||
// 如果文档没有文件名,执行另存为
|
||||
if (m_pDocument->filename().isEmpty()) {
|
||||
onAction_saveAs();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pDocument->saveToFile()) {
|
||||
// 保存成功,标记为未修改
|
||||
m_pDocument->setModified(false);
|
||||
updateWindowTitle();
|
||||
}
|
||||
}
|
||||
|
||||
void CMainWindow::onAction_saveAs()
|
||||
{
|
||||
QString defaultFileName = m_pDocument->filename();
|
||||
if (defaultFileName.isEmpty()) {
|
||||
defaultFileName = QString("未命名.bay");
|
||||
}
|
||||
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("另存为"), defaultFileName,
|
||||
tr("BayTemplate Files (*.bay);;All Files (*)"));
|
||||
if (fileName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保文件扩展名为.bay
|
||||
if (!fileName.toLower().endsWith(".bay")) {
|
||||
fileName += ".bay";
|
||||
}
|
||||
|
||||
if (!m_pDocument->saveAsToFile(fileName)) {
|
||||
QMessageBox::critical(this, tr("错误"), tr("无法保存文件:%1").arg(fileName));
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// Document 信号处理
|
||||
// =================================================================
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
<addaction name="actionNew"/>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionSave"/>
|
||||
<addaction name="actionSaveAs"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionCopy"/>
|
||||
<addaction name="actionCut"/>
|
||||
|
|
@ -105,6 +106,20 @@
|
|||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSaveAs">
|
||||
<property name="icon">
|
||||
<iconset theme="document-save-as"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>另存为</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>另存为 (A)</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCopy">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-copy"/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue