v1.2: .VSC 文件格式 & 跨平台安装程序
【文件格式】 - 工程文件扩展名从 .json 改为 .VSC(内容仍为 JSON) - 文件对话框过滤、保存/打开全部适配 .VSC - 双击 .VSC 文件可直接打开工程(命令行传参) 【安装程序】 - Windows: NSIS 安装脚本 (installer/voiletcstudio.nsi) 支持注册 .VSC 文件关联、开始菜单/桌面快捷方式、卸载 - Linux: install.sh/uninstall.sh 一键安装/卸载 自动注册 MIME 类型、.desktop 文件、命令行软链接 - 内置 --register/--unregister 命令行参数用于文件关联 【代码清理】 - 移除所有拖拽功能(dragEnter/Move/DropEvent) - 新增 MainWindow::openProjectFile() 用于命令行打开 - main.cpp 完整重写,加入命令行解析器 【其他】 - .gitignore 排除 NSIS 输出 (.exe)
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -12,3 +12,8 @@ VoiletCStudio
|
|||||||
.idea/
|
.idea/
|
||||||
*.user
|
*.user
|
||||||
*.pro.user
|
*.pro.user
|
||||||
|
|
||||||
|
# Installer output
|
||||||
|
*.exe
|
||||||
|
VoiletCStudio-Setup-*.exe
|
||||||
|
installer/build/
|
||||||
|
|||||||
92
installer/install.sh
Normal file
92
installer/install.sh
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# =====================================================
|
||||||
|
# VoiletCStudio Linux 安装脚本
|
||||||
|
# 用法: sudo bash installer/install.sh
|
||||||
|
# =====================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
APP_NAME="VoiletCStudio"
|
||||||
|
VERSION="1.2"
|
||||||
|
INSTALL_DIR="/opt/${APP_NAME}"
|
||||||
|
BIN_DIR="/usr/local/bin"
|
||||||
|
DESKTOP_DIR="/usr/share/applications"
|
||||||
|
MIME_DIR="/usr/share/mime/packages"
|
||||||
|
ICON_DIR="/usr/share/icons/hicolor/256x256/apps"
|
||||||
|
|
||||||
|
echo "========================================="
|
||||||
|
echo " VoiletCStudio v${VERSION} - Linux 安装程序"
|
||||||
|
echo "========================================="
|
||||||
|
|
||||||
|
# 检查 root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "请使用 sudo 运行: sudo bash installer/install.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "[1/5] 编译程序..."
|
||||||
|
mkdir -p build && cd build
|
||||||
|
qmake ../VoiletCStudio.pro && make -j$(nproc)
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "[2/5] 安装到 ${INSTALL_DIR}..."
|
||||||
|
mkdir -p "${INSTALL_DIR}"
|
||||||
|
cp build/VoiletCStudio "${INSTALL_DIR}/"
|
||||||
|
chmod +x "${INSTALL_DIR}/VoiletCStudio"
|
||||||
|
|
||||||
|
# 创建命令行软链接
|
||||||
|
ln -sf "${INSTALL_DIR}/VoiletCStudio" "${BIN_DIR}/voiletcstudio"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "[3/5] 安装图标..."
|
||||||
|
mkdir -p "${ICON_DIR}"
|
||||||
|
cp resources/voiletcstudio.png "${ICON_DIR}/voiletcstudio.png"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "[4/5] 注册 MIME 类型和 .desktop..."
|
||||||
|
mkdir -p "${MIME_DIR}"
|
||||||
|
cat > "${MIME_DIR}/voiletcstudio.xml" << 'MIMEXML'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||||
|
<mime-type type="application/x-voiletcstudio">
|
||||||
|
<comment>VoiletCStudio Project</comment>
|
||||||
|
<comment xml:lang="zh_CN">VoiletCStudio 工程文件</comment>
|
||||||
|
<glob pattern="*.VSC"/>
|
||||||
|
<glob pattern="*.vsc"/>
|
||||||
|
</mime-type>
|
||||||
|
</mime-info>
|
||||||
|
MIMEXML
|
||||||
|
|
||||||
|
mkdir -p "${DESKTOP_DIR}"
|
||||||
|
cat > "${DESKTOP_DIR}/voiletcstudio.desktop" << DESKTOPEOF
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=VoiletCStudio
|
||||||
|
Name[zh_CN]=紫罗兰 C 工程配置器
|
||||||
|
Comment=C Project Configurator with CMake
|
||||||
|
Comment[zh_CN]=C 语言工程配置器,自动生成 CMakeLists.txt
|
||||||
|
Exec=${INSTALL_DIR}/VoiletCStudio %f
|
||||||
|
Icon=voiletcstudio
|
||||||
|
MimeType=application/x-voiletcstudio;
|
||||||
|
Categories=Development;IDE;
|
||||||
|
Terminal=false
|
||||||
|
DESKTOPEOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "[5/5] 更新系统数据库..."
|
||||||
|
update-mime-database /usr/share/mime
|
||||||
|
update-desktop-database
|
||||||
|
gtk-update-icon-cache /usr/share/icons/hicolor 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================="
|
||||||
|
echo " ✅ VoiletCStudio 安装完成!"
|
||||||
|
echo "========================================="
|
||||||
|
echo ""
|
||||||
|
echo " 命令行: voiletcstudio"
|
||||||
|
echo " 或双击 .VSC 工程文件自动打开"
|
||||||
|
echo ""
|
||||||
|
echo " 卸载: sudo bash installer/uninstall.sh"
|
||||||
|
echo ""
|
||||||
25
installer/uninstall.sh
Normal file
25
installer/uninstall.sh
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# =====================================================
|
||||||
|
# VoiletCStudio Linux 卸载脚本
|
||||||
|
# =====================================================
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "请使用 sudo 运行"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
APP_NAME="VoiletCStudio"
|
||||||
|
|
||||||
|
echo "正在卸载 ${APP_NAME}..."
|
||||||
|
|
||||||
|
rm -rf "/opt/${APP_NAME}"
|
||||||
|
rm -f "/usr/local/bin/voiletcstudio"
|
||||||
|
rm -f "/usr/share/applications/voiletcstudio.desktop"
|
||||||
|
rm -f "/usr/share/mime/packages/voiletcstudio.xml"
|
||||||
|
rm -f "/usr/share/icons/hicolor/256x256/apps/voiletcstudio.png"
|
||||||
|
|
||||||
|
update-mime-database /usr/share/mime 2>/dev/null || true
|
||||||
|
update-desktop-database 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "✅ ${APP_NAME} 已卸载"
|
||||||
85
installer/voiletcstudio.nsi
Normal file
85
installer/voiletcstudio.nsi
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
; =====================================================
|
||||||
|
; VoiletCStudio Windows 安装脚本 (NSIS)
|
||||||
|
; 用法: makensis installer/voiletcstudio.nsi
|
||||||
|
; =====================================================
|
||||||
|
|
||||||
|
!define PRODUCT_NAME "VoiletCStudio"
|
||||||
|
!define PRODUCT_VERSION "1.2"
|
||||||
|
!define PRODUCT_PUBLISHER "LinuxAcme"
|
||||||
|
!define PRODUCT_EXE "VoiletCStudio.exe"
|
||||||
|
|
||||||
|
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
|
||||||
|
OutFile "VoiletCStudio-Setup-${PRODUCT_VERSION}.exe"
|
||||||
|
InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
|
||||||
|
RequestExecutionLevel admin
|
||||||
|
|
||||||
|
; ----- 安装页面 -----
|
||||||
|
Page directory
|
||||||
|
Page instfiles
|
||||||
|
|
||||||
|
; ----- 默认安装路径 -----
|
||||||
|
Section "Install"
|
||||||
|
SetOutPath "$INSTDIR"
|
||||||
|
|
||||||
|
; 复制主程序
|
||||||
|
File "VoiletCStudio.exe"
|
||||||
|
|
||||||
|
; 复制 Qt 运行时 DLL(需要提前准备好)
|
||||||
|
; File /r "Qt5Core.dll"
|
||||||
|
; File /r "Qt5Gui.dll"
|
||||||
|
; File /r "Qt5Widgets.dll"
|
||||||
|
|
||||||
|
; 复制 MinGW 运行时
|
||||||
|
; File /r "libgcc_s_seh-1.dll"
|
||||||
|
; File /r "libstdc++-6.dll"
|
||||||
|
; File /r "libwinpthread-1.dll"
|
||||||
|
|
||||||
|
; 创建卸载程序
|
||||||
|
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
|
; 注册 .VSC 文件关联
|
||||||
|
WriteRegStr HKCR ".VSC" "" "VoiletCStudio.VSC"
|
||||||
|
WriteRegStr HKCR "VoiletCStudio.VSC" "" "VoiletCStudio 工程文件"
|
||||||
|
WriteRegStr HKCR "VoiletCStudio.VSC\DefaultIcon" "" "$INSTDIR\${PRODUCT_EXE},0"
|
||||||
|
WriteRegStr HKCR "VoiletCStudio.VSC\shell\open\command" "" '"$INSTDIR\${PRODUCT_EXE}" "%1"'
|
||||||
|
|
||||||
|
; 注册表卸载信息
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayName" "${PRODUCT_NAME}"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "UninstallString" "$INSTDIR\uninstall.exe"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayVersion" "${PRODUCT_VERSION}"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "Publisher" "${PRODUCT_PUBLISHER}"
|
||||||
|
|
||||||
|
; 开始菜单快捷方式
|
||||||
|
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
|
||||||
|
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\VoiletCStudio.lnk" "$INSTDIR\${PRODUCT_EXE}"
|
||||||
|
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\卸载.lnk" "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
|
; 桌面快捷方式
|
||||||
|
CreateShortCut "$DESKTOP\VoiletCStudio.lnk" "$INSTDIR\${PRODUCT_EXE}"
|
||||||
|
|
||||||
|
; 通知系统刷新文件关联
|
||||||
|
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
; ----- 卸载 -----
|
||||||
|
Section "Uninstall"
|
||||||
|
; 删除程序
|
||||||
|
Delete "$INSTDIR\${PRODUCT_EXE}"
|
||||||
|
Delete "$INSTDIR\uninstall.exe"
|
||||||
|
RMDir "$INSTDIR"
|
||||||
|
|
||||||
|
; 删除文件关联
|
||||||
|
DeleteRegKey HKCR ".VSC"
|
||||||
|
DeleteRegKey HKCR "VoiletCStudio.VSC"
|
||||||
|
|
||||||
|
; 删除注册表
|
||||||
|
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
|
||||||
|
|
||||||
|
; 删除快捷方式
|
||||||
|
Delete "$SMPROGRAMS\${PRODUCT_NAME}\VoiletCStudio.lnk"
|
||||||
|
Delete "$SMPROGRAMS\${PRODUCT_NAME}\卸载.lnk"
|
||||||
|
RMDir "$SMPROGRAMS\${PRODUCT_NAME}"
|
||||||
|
Delete "$DESKTOP\VoiletCStudio.lnk"
|
||||||
|
|
||||||
|
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
|
||||||
|
SectionEnd
|
||||||
@@ -4,7 +4,8 @@ Name=VoiletCStudio
|
|||||||
Name[zh_CN]=紫罗兰 C 工程配置器
|
Name[zh_CN]=紫罗兰 C 工程配置器
|
||||||
Comment=C Project Configurator with CMake
|
Comment=C Project Configurator with CMake
|
||||||
Comment[zh_CN]=C 语言工程配置器,自动生成 CMakeLists.txt
|
Comment[zh_CN]=C 语言工程配置器,自动生成 CMakeLists.txt
|
||||||
Exec=VoiletCStudio
|
Exec=VoiletCStudio %f
|
||||||
Icon=voiletcstudio
|
Icon=voiletcstudio
|
||||||
|
MimeType=application/x-voiletcstudio;
|
||||||
Categories=Development;IDE;
|
Categories=Development;IDE;
|
||||||
Terminal=false
|
Terminal=false
|
||||||
|
|||||||
156
src/main.cpp
156
src/main.cpp
@@ -1,8 +1,125 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QStyleFactory>
|
#include <QStyleFactory>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QDebug>
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ===== 文件关联注册 =====
|
||||||
|
static void registerFileAssociation()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
QString appPath = QCoreApplication::applicationFilePath();
|
||||||
|
appPath.replace("/", "\\");
|
||||||
|
|
||||||
|
// 注册 .VSC 扩展名
|
||||||
|
QSettings settings("HKEY_CLASSES_ROOT\\.VSC", QSettings::NativeFormat);
|
||||||
|
settings.setValue("Default", "VoiletCStudio.VSC");
|
||||||
|
settings.sync();
|
||||||
|
|
||||||
|
// 注册文件类型描述
|
||||||
|
QSettings typeSettings("HKEY_CLASSES_ROOT\\VoiletCStudio.VSC", QSettings::NativeFormat);
|
||||||
|
typeSettings.setValue("Default", "VoiletCStudio 工程文件");
|
||||||
|
typeSettings.sync();
|
||||||
|
|
||||||
|
// 注册图标
|
||||||
|
QSettings iconSettings("HKEY_CLASSES_ROOT\\VoiletCStudio.VSC\\DefaultIcon", QSettings::NativeFormat);
|
||||||
|
iconSettings.setValue("Default", appPath + ",0");
|
||||||
|
iconSettings.sync();
|
||||||
|
|
||||||
|
// 注册打开命令
|
||||||
|
QSettings cmdSettings("HKEY_CLASSES_ROOT\\VoiletCStudio.VSC\\shell\\open\\command", QSettings::NativeFormat);
|
||||||
|
cmdSettings.setValue("Default", "\"" + appPath + "\" \"%1\"");
|
||||||
|
cmdSettings.sync();
|
||||||
|
|
||||||
|
// 通知系统刷新
|
||||||
|
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
|
||||||
|
|
||||||
|
qDebug() << "[OK] .VSC file association registered on Windows";
|
||||||
|
#else
|
||||||
|
// Linux: 写入 MIME 类型定义 + 更新 desktop 数据库
|
||||||
|
QString mimeDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/mime/packages";
|
||||||
|
QDir().mkpath(mimeDir);
|
||||||
|
|
||||||
|
QString mimeFile = mimeDir + "/voiletcstudio.xml";
|
||||||
|
QFile f(mimeFile);
|
||||||
|
if (f.open(QIODevice::WriteOnly)) {
|
||||||
|
f.write(R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||||
|
<mime-type type="application/x-voiletcstudio">
|
||||||
|
<comment>VoiletCStudio Project</comment>
|
||||||
|
<comment xml:lang="zh_CN">VoiletCStudio 工程文件</comment>
|
||||||
|
<glob pattern="*.VSC"/>
|
||||||
|
<glob pattern="*.vsc"/>
|
||||||
|
</mime-type>
|
||||||
|
</mime-info>
|
||||||
|
)");
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入 .desktop 文件
|
||||||
|
QString appsDir = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
|
||||||
|
QDir().mkpath(appsDir);
|
||||||
|
|
||||||
|
QString desktopFile = appsDir + "/voiletcstudio.desktop";
|
||||||
|
QFile df(desktopFile);
|
||||||
|
if (df.open(QIODevice::WriteOnly)) {
|
||||||
|
df.write(R"([Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=VoiletCStudio
|
||||||
|
Name[zh_CN]=紫罗兰 C 工程配置器
|
||||||
|
Comment=C Project Configurator
|
||||||
|
Comment[zh_CN]=C 语言工程配置器
|
||||||
|
Exec=)" + QCoreApplication::applicationFilePath().toUtf8() + R"( %f
|
||||||
|
Icon=voiletcstudio
|
||||||
|
MimeType=application/x-voiletcstudio;
|
||||||
|
Categories=Development;IDE;
|
||||||
|
Terminal=false
|
||||||
|
)");
|
||||||
|
df.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 MIME 数据库
|
||||||
|
QProcess::execute("update-mime-database", QStringList() <<
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/mime");
|
||||||
|
QProcess::execute("update-desktop-database", QStringList());
|
||||||
|
|
||||||
|
qDebug() << "[OK] .VSC file association registered on Linux";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unregisterFileAssociation()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
QSettings settings("HKEY_CLASSES_ROOT\\.VSC", QSettings::NativeFormat);
|
||||||
|
settings.remove("Default");
|
||||||
|
settings.sync();
|
||||||
|
|
||||||
|
QSettings("HKEY_CLASSES_ROOT\\VoiletCStudio.VSC", QSettings::NativeFormat).remove("");
|
||||||
|
SHDeleteKey(HKEY_CLASSES_ROOT, L"VoiletCStudio.VSC");
|
||||||
|
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
|
||||||
|
|
||||||
|
qDebug() << "[OK] .VSC file association removed";
|
||||||
|
#else
|
||||||
|
QString mimeFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/mime/packages/voiletcstudio.xml";
|
||||||
|
QFile::remove(mimeFile);
|
||||||
|
QProcess::execute("update-mime-database", QStringList() <<
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/mime");
|
||||||
|
qDebug() << "[OK] .VSC file association removed";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
@@ -12,14 +129,49 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
// 设置应用程序信息
|
// 设置应用程序信息
|
||||||
app.setApplicationName("VoiletCStudio");
|
app.setApplicationName("VoiletCStudio");
|
||||||
app.setApplicationVersion("1.1");
|
app.setApplicationVersion("1.2");
|
||||||
app.setOrganizationName("LinuxAcme");
|
app.setOrganizationName("LinuxAcme");
|
||||||
|
|
||||||
// 设置样式(使用系统原生样式)
|
// 命令行参数解析
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription("紫罗兰 C 工程配置器 - 自动生成 CMakeLists.txt");
|
||||||
|
parser.addHelpOption();
|
||||||
|
parser.addVersionOption();
|
||||||
|
|
||||||
|
QCommandLineOption registerOpt("register", "注册 .VSC 文件关联到系统");
|
||||||
|
QCommandLineOption unregisterOpt("unregister", "取消 .VSC 文件关联");
|
||||||
|
parser.addOption(registerOpt);
|
||||||
|
parser.addOption(unregisterOpt);
|
||||||
|
|
||||||
|
// 接受 .VSC 文件作为位置参数(双击打开)
|
||||||
|
parser.addPositionalArgument("file", "要打开的 .VSC 工程文件");
|
||||||
|
|
||||||
|
parser.process(app);
|
||||||
|
|
||||||
|
// 处理注册/取消注册
|
||||||
|
if (parser.isSet(registerOpt)) {
|
||||||
|
registerFileAssociation();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (parser.isSet(unregisterOpt)) {
|
||||||
|
unregisterFileAssociation();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置样式
|
||||||
app.setStyle(QStyleFactory::create("Fusion"));
|
app.setStyle(QStyleFactory::create("Fusion"));
|
||||||
|
|
||||||
MainWindow window;
|
MainWindow window;
|
||||||
window.show();
|
window.show();
|
||||||
|
|
||||||
|
// 通过命令行传入的文件自动打开
|
||||||
|
QStringList posArgs = parser.positionalArguments();
|
||||||
|
if (!posArgs.isEmpty()) {
|
||||||
|
QString filePath = posArgs.first();
|
||||||
|
if (filePath.endsWith(".VSC", Qt::CaseInsensitive) || filePath.endsWith(".json", Qt::CaseInsensitive)) {
|
||||||
|
window.openProjectFile(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QFile>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
@@ -18,9 +19,6 @@
|
|||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QDragEnterEvent>
|
|
||||||
#include <QDragMoveEvent>
|
|
||||||
#include <QDropEvent>
|
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent)
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
@@ -31,9 +29,6 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
config = new ProjectConfig(this);
|
config = new ProjectConfig(this);
|
||||||
cmakeGenerator = new CMakeGenerator(this);
|
cmakeGenerator = new CMakeGenerator(this);
|
||||||
|
|
||||||
// ★ 启用拖拽
|
|
||||||
setAcceptDrops(true);
|
|
||||||
|
|
||||||
setupUI();
|
setupUI();
|
||||||
setupConnections();
|
setupConnections();
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
@@ -109,7 +104,7 @@ void MainWindow::setupUI()
|
|||||||
basicLayout->addWidget(projectNameEdit);
|
basicLayout->addWidget(projectNameEdit);
|
||||||
|
|
||||||
basicLayout->addStretch();
|
basicLayout->addStretch();
|
||||||
basicLayout->addWidget(new QLabel("📂 工程路径:同 JSON 配置文件所在目录"));
|
basicLayout->addWidget(new QLabel("📂 工程路径:同 VSC 工程文件所在目录"));
|
||||||
|
|
||||||
mainLayout->addWidget(basicGroup);
|
mainLayout->addWidget(basicGroup);
|
||||||
|
|
||||||
@@ -280,22 +275,6 @@ void MainWindow::setupUI()
|
|||||||
|
|
||||||
// 状态栏
|
// 状态栏
|
||||||
statusBar()->showMessage("就绪");
|
statusBar()->showMessage("就绪");
|
||||||
|
|
||||||
// ★ 禁用子控件的拖拽,让所有拖拽事件由 MainWindow 统一处理
|
|
||||||
sourceTree->setAcceptDrops(false);
|
|
||||||
sourceTree->viewport()->setAcceptDrops(false);
|
|
||||||
includeDirList->setAcceptDrops(false);
|
|
||||||
includeDirList->viewport()->setAcceptDrops(false);
|
|
||||||
libraryList->setAcceptDrops(false);
|
|
||||||
libraryList->viewport()->setAcceptDrops(false);
|
|
||||||
defineList->setAcceptDrops(false);
|
|
||||||
defineList->viewport()->setAcceptDrops(false);
|
|
||||||
optionList->setAcceptDrops(false);
|
|
||||||
optionList->viewport()->setAcceptDrops(false);
|
|
||||||
projectNameEdit->setAcceptDrops(false);
|
|
||||||
compilerPathEdit->setAcceptDrops(false);
|
|
||||||
assemblerPathEdit->setAcceptDrops(false);
|
|
||||||
linkerPathEdit->setAcceptDrops(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setupConnections()
|
void MainWindow::setupConnections()
|
||||||
@@ -361,99 +340,6 @@ bool MainWindow::maybeSave()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
|
|
||||||
{
|
|
||||||
if (event->mimeData()->hasUrls()) {
|
|
||||||
QList<QUrl> urls = event->mimeData()->urls();
|
|
||||||
if (!urls.isEmpty()) {
|
|
||||||
QString file = urls.first().toLocalFile();
|
|
||||||
if (file.endsWith(".json", Qt::CaseInsensitive)) {
|
|
||||||
event->acceptProposedAction();
|
|
||||||
statusBar()->showMessage("📂 释放鼠标打开工程:" + file, 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
event->ignore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::dragMoveEvent(QDragMoveEvent *event)
|
|
||||||
{
|
|
||||||
// 拖拽移动时也要接受,否则 Qt 不会触发 dropEvent
|
|
||||||
if (event->mimeData()->hasUrls()) {
|
|
||||||
QList<QUrl> urls = event->mimeData()->urls();
|
|
||||||
if (!urls.isEmpty()) {
|
|
||||||
QString file = urls.first().toLocalFile();
|
|
||||||
if (file.endsWith(".json", Qt::CaseInsensitive)) {
|
|
||||||
event->acceptProposedAction();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
event->ignore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::dropEvent(QDropEvent *event)
|
|
||||||
{
|
|
||||||
if (event->mimeData()->hasUrls()) {
|
|
||||||
QList<QUrl> urls = event->mimeData()->urls();
|
|
||||||
if (!urls.isEmpty()) {
|
|
||||||
QString file = urls.first().toLocalFile();
|
|
||||||
if (file.endsWith(".json", Qt::CaseInsensitive)) {
|
|
||||||
if (!maybeSave()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->loadConfig(file)) {
|
|
||||||
currentFilePath = file;
|
|
||||||
modified = false;
|
|
||||||
|
|
||||||
// 更新 UI
|
|
||||||
projectNameEdit->setText(config->getProjectName());
|
|
||||||
compilerPathEdit->setText(config->getCompilerPath());
|
|
||||||
assemblerPathEdit->setText(config->getAssemblerPath());
|
|
||||||
linkerPathEdit->setText(config->getLinkerPath());
|
|
||||||
|
|
||||||
// 更新虚拟目录树
|
|
||||||
sourceTree->clear();
|
|
||||||
QMap<QString, VirtualDir> dirs = config->getVirtualDirs();
|
|
||||||
for (auto it = dirs.begin(); it != dirs.end(); ++it) {
|
|
||||||
QTreeWidgetItem *dirItem = new QTreeWidgetItem(sourceTree);
|
|
||||||
dirItem->setText(0, "📁 " + it.key());
|
|
||||||
for (const QString &f : it->files) {
|
|
||||||
QTreeWidgetItem *fileItem = new QTreeWidgetItem(dirItem);
|
|
||||||
fileItem->setText(0, "📄 " + QFileInfo(f).fileName());
|
|
||||||
fileItem->setToolTip(0, f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sourceTree->expandAll();
|
|
||||||
|
|
||||||
// 更新包含目录
|
|
||||||
includeDirList->clear();
|
|
||||||
includeDirList->addItems(config->getIncludeDirs());
|
|
||||||
|
|
||||||
// 更新库文件
|
|
||||||
libraryList->clear();
|
|
||||||
libraryList->addItems(config->getLibraries());
|
|
||||||
|
|
||||||
// 更新编译宏
|
|
||||||
defineList->clear();
|
|
||||||
defineList->addItems(config->getDefines());
|
|
||||||
|
|
||||||
// 更新编译选项
|
|
||||||
optionList->clear();
|
|
||||||
optionList->addItems(config->getCompilerOptions());
|
|
||||||
|
|
||||||
updateWindowTitle();
|
|
||||||
statusBar()->showMessage("✅ 工程已加载:" + file, 5000);
|
|
||||||
} else {
|
|
||||||
QMessageBox::critical(this, "错误", "无法加载工程文件:" + file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::newProject()
|
void MainWindow::newProject()
|
||||||
{
|
{
|
||||||
if (!maybeSave()) {
|
if (!maybeSave()) {
|
||||||
@@ -485,7 +371,7 @@ void MainWindow::openProject()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString filePath = QFileDialog::getOpenFileName(this, "打开工程", "",
|
QString filePath = QFileDialog::getOpenFileName(this, "打开工程", "",
|
||||||
"JSON 配置文件 (*.json);;所有文件 (*)");
|
"VSC 工程文件 (*.VSC);;所有文件 (*)");
|
||||||
|
|
||||||
if (filePath.isEmpty()) {
|
if (filePath.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@@ -530,6 +416,54 @@ void MainWindow::openProject()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::openProjectFile(const QString &filePath)
|
||||||
|
{
|
||||||
|
// 直接打开指定文件(用于命令行和双击打开),跳过 maybeSave
|
||||||
|
if (!QFile::exists(filePath)) {
|
||||||
|
QMessageBox::critical(this, "错误", "文件不存在:" + filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->loadConfig(filePath)) {
|
||||||
|
currentFilePath = filePath;
|
||||||
|
modified = false;
|
||||||
|
|
||||||
|
projectNameEdit->setText(config->getProjectName());
|
||||||
|
compilerPathEdit->setText(config->getCompilerPath());
|
||||||
|
assemblerPathEdit->setText(config->getAssemblerPath());
|
||||||
|
linkerPathEdit->setText(config->getLinkerPath());
|
||||||
|
|
||||||
|
sourceTree->clear();
|
||||||
|
QMap<QString, VirtualDir> dirs = config->getVirtualDirs();
|
||||||
|
for (auto it = dirs.begin(); it != dirs.end(); ++it) {
|
||||||
|
QTreeWidgetItem *dirItem = new QTreeWidgetItem(sourceTree);
|
||||||
|
dirItem->setText(0, it.key());
|
||||||
|
for (const QString &file : it->files) {
|
||||||
|
QTreeWidgetItem *fileItem = new QTreeWidgetItem(dirItem);
|
||||||
|
fileItem->setText(0, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourceTree->expandAll();
|
||||||
|
|
||||||
|
includeDirList->clear();
|
||||||
|
includeDirList->addItems(config->getIncludeDirs());
|
||||||
|
|
||||||
|
libraryList->clear();
|
||||||
|
libraryList->addItems(config->getLibraries());
|
||||||
|
|
||||||
|
defineList->clear();
|
||||||
|
defineList->addItems(config->getDefines());
|
||||||
|
|
||||||
|
optionList->clear();
|
||||||
|
optionList->addItems(config->getCompilerOptions());
|
||||||
|
|
||||||
|
updateWindowTitle();
|
||||||
|
statusBar()->showMessage("✅ 工程已加载:" + filePath, 5000);
|
||||||
|
} else {
|
||||||
|
QMessageBox::critical(this, "错误", "无法加载工程文件:" + filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool MainWindow::saveProject()
|
bool MainWindow::saveProject()
|
||||||
{
|
{
|
||||||
if (currentFilePath.isEmpty()) {
|
if (currentFilePath.isEmpty()) {
|
||||||
@@ -555,7 +489,7 @@ bool MainWindow::saveProject()
|
|||||||
bool MainWindow::saveProjectAs()
|
bool MainWindow::saveProjectAs()
|
||||||
{
|
{
|
||||||
QString filePath = QFileDialog::getSaveFileName(this, "保存工程", "",
|
QString filePath = QFileDialog::getSaveFileName(this, "保存工程", "",
|
||||||
"JSON 配置文件 (*.json);;所有文件 (*)");
|
"VSC 工程文件 (*.VSC);;所有文件 (*)");
|
||||||
|
|
||||||
if (filePath.isEmpty()) {
|
if (filePath.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -572,7 +506,7 @@ void MainWindow::generateCMake()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取工程目录(JSON 配置文件所在目录)
|
// 获取工程目录(VSC 工程文件所在目录)
|
||||||
QString projectDir = QFileInfo(currentFilePath).absolutePath();
|
QString projectDir = QFileInfo(currentFilePath).absolutePath();
|
||||||
QString buildDir = projectDir + "/build";
|
QString buildDir = projectDir + "/build";
|
||||||
QString cmakePath = projectDir + "/CMakeLists.txt";
|
QString cmakePath = projectDir + "/CMakeLists.txt";
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ class MainWindow : public QMainWindow
|
|||||||
public:
|
public:
|
||||||
MainWindow(QWidget *parent = nullptr);
|
MainWindow(QWidget *parent = nullptr);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
|
// 通过命令行/双击打开工程文件
|
||||||
|
void openProjectFile(const QString &filePath);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// 文件操作
|
// 文件操作
|
||||||
@@ -67,11 +70,6 @@ private slots:
|
|||||||
void browseAssemblerPath();
|
void browseAssemblerPath();
|
||||||
void browseLinkerPath();
|
void browseLinkerPath();
|
||||||
|
|
||||||
protected:
|
|
||||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
|
||||||
void dragMoveEvent(QDragMoveEvent *event) override;
|
|
||||||
void dropEvent(QDropEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUI();
|
void setupUI();
|
||||||
void setupConnections();
|
void setupConnections();
|
||||||
|
|||||||
Reference in New Issue
Block a user