commit 7ef53ce6cf544cb9f3fa2477e2326480b0265da9 Author: taynpg Date: Fri Feb 27 12:31:38 2026 +0800 基本版本 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..36e4997 --- /dev/null +++ b/.clang-format @@ -0,0 +1,17 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +PointerAlignment: Left +AccessModifierOffset: -4 +BreakBeforeBraces: Custom +BraceWrapping: + AfterFunction: true + AfterClass: true +Cpp11BracedListStyle: true +ReflowComments: true +SpacesBeforeTrailingComments: 3 +TabWidth: 4 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ColumnLimit: 130 +AllowShortBlocksOnASingleLine: Never +AllowShortFunctionsOnASingleLine: None +AllowShortEnumsOnASingleLine: false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03b6eee --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# Prerequisites +*.d +.idea +cmake-build-* + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +!lebo_motor.lib +!lebo_motor.dll +!liblebo_motor.so + +# Executables +*.exe +*.out +*.app +build +*.user +compile_commands.json +.vs +out +.cache +CMakeLists.txt.* \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..015d958 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,173 @@ +{ + "files.autoSave": "onFocusChange", + "editor.fontSize": 14, + "cmake.configureOnOpen": true, + "editor.fontFamily": "'Maple Mono NL NF CN Light', 'Maple Mono NL NF CN Light', 'Maple Mono NL NF CN Light'", + "cmake.debugConfig": { + "type": "lldb", + "console": "externalTerminal", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "args": [] + }, + "cmake.configureSettings": { + "CMAKE_PREFIX_PATH": "C:/local", + }, + "cmake.configureArgs": [ + "-Wno-dev" + ], + "cmake.options.statusBarVisibility": "visible", + "cmake.generator": "Ninja", + "C_Cpp.default.compileCommands": "${workspaceRoot}/build/compile_commands.json", + "C_Cpp.default.cppStandard": "c++17", + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", + "editor.inlayHints.enabled": "off", + "editor.unicodeHighlight.allowedLocales": { + "ja": true, + "zh-hant": true, + "zh-hans": true + }, + "files.associations": { + "string": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "source_location": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "format": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "queue": "cpp", + "ranges": "cpp", + "semaphore": "cpp", + "shared_mutex": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "text_encoding": "cpp", + "thread": "cpp", + "cfenv": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "variant": "cpp", + "ios": "cpp", + "locale": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xutility": "cpp", + "coroutine": "cpp", + "csignal": "cpp", + "future": "cpp", + "stdfloat": "cpp", + "regex": "cpp", + "stack": "cpp", + "valarray": "cpp", + "filesystem": "cpp", + "qmainwindow": "cpp", + "qlabel": "cpp", + "*.in": "cpp", + "fstream": "cpp", + "xmemory0": "cpp", + "xstddef": "cpp", + "xtree": "cpp", + "qxmlstreamwriter": "cpp", + "qgraphicsdropshadoweffect": "cpp", + "qpushbutton": "cpp", + "qpainter": "cpp", + "qdialog": "cpp", + "quuid": "cpp", + "complex": "cpp", + "expected": "cpp", + "qmessagebox": "cpp" + }, + "editor.tokenColorCustomizations": { + "textMateRules": [ + { + "scope": "googletest.failed", + "settings": { + "foreground": "#f00" + } + }, + { + "scope": "googletest.passed", + "settings": { + "foreground": "#0f0" + } + }, + { + "scope": "googletest.run", + "settings": { + "foreground": "#0f0" + } + } + ] + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..00e3c85 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.16) + +project(cmakeCreate LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if (MSVC) + add_compile_options(/utf-8) +endif() + +set(CMAKE_DEBUG_POSTFIX "d") +set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE}/) + +find_package(wxWidgets CONFIG REQUIRED) + +set(SOURCES +src/mainframe.h +src/mainframe.cpp +res/ico.rc +res/manifest.rc +) + +include_directories(src) +add_executable(cmakeCreate main.cpp ${SOURCES}) +target_link_libraries(cmakeCreate PRIVATE wx::core wx::base wx::stc) +set_target_properties(cmakeCreate PROPERTIES WIN32_EXECUTABLE TRUE) \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fcd25ef --- /dev/null +++ b/main.cpp @@ -0,0 +1,26 @@ +#include "mainframe.h" + +class CWxNewProject : public wxApp +{ +public: + virtual bool OnInit() + { + wxString title = wxString::Format(wxT("创建cmake工程 v1.0.0")); + auto* f = new MainFrame(title); + f->Show(true); + f->SetIcon(wxICON(IDI_ICON1)); + +#ifdef __WXMSW__ + SetProcessDPIAware(); +#endif + + return true; + } + virtual int OnExit() + { + return wxApp::OnExit(); + } +}; + +IMPLEMENT_APP(CWxNewProject); +DECLARE_APP(CWxNewProject); \ No newline at end of file diff --git a/res/ico.ico b/res/ico.ico new file mode 100644 index 0000000..65de4f2 Binary files /dev/null and b/res/ico.ico differ diff --git a/res/ico.rc b/res/ico.rc new file mode 100644 index 0000000..7e9ebf4 --- /dev/null +++ b/res/ico.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "ico.ico" diff --git a/res/manifest.rc b/res/manifest.rc new file mode 100644 index 0000000..895225f --- /dev/null +++ b/res/manifest.rc @@ -0,0 +1 @@ +1 24 "manifest.xml" \ No newline at end of file diff --git a/res/manifest.xml b/res/manifest.xml new file mode 100644 index 0000000..ec53212 --- /dev/null +++ b/res/manifest.xml @@ -0,0 +1,42 @@ + + + + + Your Application Description + + + + + + + + + + + + true + PerMonitorV2 + + + + + + \ No newline at end of file diff --git a/src/mainframe.cpp b/src/mainframe.cpp new file mode 100644 index 0000000..2a239d0 --- /dev/null +++ b/src/mainframe.cpp @@ -0,0 +1,573 @@ +#include "mainframe.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MainFrame::MainFrame(const wxString& title) : wxFrame(nullptr, wxID_ANY, title) +{ + SetSize(wxSize(1100, 600)); + InitControl(); + InitCMakeSyntaxHighlighting(); + configDir_ = GetConfigDir(); + ShowConfig(); + ShowExist(); + LoadConfig(); +} + +void MainFrame::InitControl() +{ + addID_ = wxNewId(); + delID_ = wxNewId(); + + wxPanel* mainPanel = new wxPanel(this, wxID_ANY); + listBox_ = new wxListBox(mainPanel, wxID_ANY, wxDefaultPosition, wxSize(200, -1)); + listBox_->Bind(wxEVT_COMMAND_LISTBOX_SELECTED, &MainFrame::OnListBoxSelected, this); + listBox_->Bind(wxEVT_CONTEXT_MENU, &MainFrame::OnRightClick, this); + + textCtrl_ = new wxStyledTextCtrl(mainPanel, wxID_ANY); + btnNewer_ = new wxButton(mainPanel, wxID_ANY, wxT("新建工程")); + btnExit_ = new wxButton(mainPanel, wxID_ANY, wxT("退出")); + btnRefresh_ = new wxButton(mainPanel, wxID_ANY, wxT("刷新列表")); + btnRefresh_->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MainFrame::ShowConfig, this); + btnSelectPath_ = new wxButton(mainPanel, wxID_ANY, wxT("选择路径")); + + // + btnSave_ = new wxButton(mainPanel, wxID_ANY, wxT("保存"), wxDefaultPosition, wxSize(150, -1)); + + textPath_ = new wxTextCtrl(mainPanel, wxID_ANY); + auto* labelProjectName = new wxStaticText(mainPanel, wxID_ANY, wxT("项目名称:")); + projectName_ = new wxTextCtrl(mainPanel, wxID_ANY); + + // + btnExit_->SetMinSize(wxSize(150, -1)); + btnNewer_->SetMinSize(wxSize(150, -1)); + + // + btnOpenPrjDir_ = new wxButton(mainPanel, wxID_ANY, wxT("打开项目目录")); + auto* staticSelectPath = new wxStaticText(mainPanel, wxID_ANY, wxT("工程目录:")); + wxBoxSizer* selectSizer = new wxBoxSizer(wxHORIZONTAL); + selectSizer->Add(staticSelectPath, 0, wxALL | wxCENTER, 2); + selectSizer->Add(textPath_, 1, wxALL | wxEXPAND, 2); + selectSizer->Add(btnSelectPath_, 0, wxALL | wxCENTER, 2); + selectSizer->Add(btnOpenPrjDir_, 0, wxALL | wxCENTER, 2); + + // + curFileTitle_ = new wxStaticText(mainPanel, wxID_ANY, wxT("无当前文件")); + auto* centerSizer = new wxBoxSizer(wxVERTICAL); + centerSizer->Add(curFileTitle_, 0, wxALL | wxLEFT, 2); + centerSizer->Add(textCtrl_, 1, wxALL | wxEXPAND, 2); + centerSizer->Add(selectSizer, 0, wxALL | wxEXPAND, 2); + + // + btnOpenResDir_ = new wxButton(mainPanel, wxID_ANY, wxT("打开模板目录")); + auto* controlSizer = new wxBoxSizer(wxVERTICAL); + controlSizer->Add(btnRefresh_, 0, wxALL | wxEXPAND, 2); + controlSizer->Add(btnSave_, 0, wxALL | wxEXPAND, 2); + controlSizer->Add(btnOpenResDir_, 0, wxALL | wxEXPAND, 2); + controlSizer->AddStretchSpacer(1); + controlSizer->Add(labelProjectName, 0, wxALL | wxLEFT, 2); + controlSizer->Add(projectName_, 0, wxALL | wxEXPAND, 2); + controlSizer->Add(btnNewer_, 0, wxALL | wxCENTER, 2); + controlSizer->Add(btnExit_, 0, wxALL | wxCENTER, 2); + + // + auto* topSizer = new wxBoxSizer(wxHORIZONTAL); + topSizer->Add(listBox_, 0, wxALL | wxEXPAND, 2); + topSizer->Add(centerSizer, 3, wxALL | wxEXPAND, 2); + topSizer->Add(controlSizer, 0, wxALL | wxEXPAND, 2); + + SetMinSize(wxSize(800, 500)); + mainPanel->SetSizer(topSizer); + + btnExit_->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MainFrame::OnExitClick, this); + btnNewer_->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MainFrame::OnNewerClick, this); + btnSelectPath_->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MainFrame::OnSelectPathClick, this); + btnSave_->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MainFrame::SaveCurrent, this); + btnOpenResDir_->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MainFrame::OpenResDir, this); + btnOpenPrjDir_->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MainFrame::OpenPrjDir, this); + Bind(wxEVT_MENU, &MainFrame::DelMenu, this, delID_); + Bind(wxEVT_MENU, &MainFrame::AddMenu, this, addID_); +} + +void MainFrame::OnNewerClick(wxCommandEvent& event) +{ + wxString targetPath = textPath_->GetValue().Trim(); + if (targetPath.IsEmpty()) { + wxMessageBox(wxT("目标路径不能为空"), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + if (!wxDirExists(targetPath)) { + wxMessageBox(wxString::Format(wxT("目标路径不存在:\n%s"), targetPath), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + wxString projectName = projectName_->GetValue().Trim(); + if (projectName.IsEmpty()) { + wxMessageBox(wxT("项目名称不能为空"), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + wxString destDir = wxFileName::DirName(targetPath).GetFullPath(); + destDir += wxFileName::GetPathSeparator() + projectName; + destDir = Normalize(destDir); + + if (wxDirExists(destDir)) { + wxMessageBox(wxString::Format(wxT("目标目录已存在:\n%s"), destDir), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + wxString sourceDir = configDir_ + wxFileName::GetPathSeparator() + curFileTitle_->GetLabelText(); + sourceDir = Normalize(sourceDir); + if (!wxDirExists(sourceDir)) { + wxMessageBox(wxString::Format(wxT("源目录不存在:\n%s"), sourceDir), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + if (!wxFileName::Mkdir(destDir, 0755, wxPATH_MKDIR_FULL)) { + wxMessageBox(wxString::Format(wxT("无法创建目录:\n%s"), destDir), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + wxDir dir(sourceDir); + wxString filename; + bool success = true; + wxString errorFile; + + if (dir.GetFirst(&filename, wxEmptyString, wxDIR_FILES | wxDIR_DIRS)) { + do { + wxString sourceItem = sourceDir + wxFileName::GetPathSeparator() + filename; + wxString destItem = destDir + wxFileName::GetPathSeparator() + filename; + sourceItem = Normalize(sourceItem); + destItem = Normalize(destItem); + + if (wxDirExists(sourceItem)) { + if (!wxFileName::Mkdir(destItem, 0755, wxPATH_MKDIR_FULL)) { + success = false; + errorFile = filename; + break; + } + + if (!CopyDirectory(sourceItem, destItem, projectName)) { + success = false; + errorFile = filename; + break; + } + } else { + if (!wxCopyFile(sourceItem, destItem)) { + success = false; + errorFile = filename; + break; + } + if (filename == wxT("CMakeLists.txt")) { + ProcessCMakeListsFile(destItem, projectName); + } + } + } while (success && dir.GetNext(&filename)); + } + if (success) { + wxMessageBox(wxString::Format(wxT("成功创建项目:\n%s\n并复制所有文件"), destDir), wxT("完成"), wxOK | wxICON_INFORMATION, + this); + } else { + wxMessageBox(wxString::Format(wxT("复制文件失败:\n%s"), errorFile), wxT("错误"), wxOK | wxICON_ERROR, this); + wxFileName::Rmdir(destDir, wxPATH_RMDIR_RECURSIVE); + } + SaveConfig(); +} + +void MainFrame::OnExitClick(wxCommandEvent& event) +{ + Close(true); +} + +void MainFrame::OnSelectPathClick(wxCommandEvent& event) +{ + wxDirDialog dirDialog(this, wxT("选择工程创建所在目录"), textPath_->GetValue(), wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); + if (dirDialog.ShowModal() == wxID_OK) { + wxString selectedPath = dirDialog.GetPath(); + textPath_->SetValue(selectedPath); + } +} + +wxString MainFrame::GetConfigDir() +{ + wxString exePath = wxStandardPaths::Get().GetExecutablePath(); + wxFileName exeFile(exePath); + wxString parentDir = exeFile.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR); + wxString configDir = parentDir + wxFileName::GetPathSeparator() + wxT("res"); + wxFileName configDirFile(configDir); + configDirFile.Normalize(wxPATH_NORM_ALL); + if (!configDirFile.DirExists()) { + if (!wxFileName::Mkdir(configDirFile.GetFullPath(), 0755, wxPATH_MKDIR_FULL)) { + wxLogError(wxT("无法创建配置目录: %s"), configDirFile.GetFullPath()); + return wxEmptyString; + } + wxLogMessage(wxT("已创建配置目录: %s"), configDirFile.GetFullPath()); + } + return configDirFile.GetFullPath(); +} + +void MainFrame::ShowConfig() +{ + listBox_->Clear(); + if (configDir_.empty()) { + wxMessageBox(wxT("配置目录路径为空")); + return; + } + if (!wxDirExists(configDir_)) { + if (!wxFileName::Mkdir(configDir_, 0755, wxPATH_MKDIR_FULL)) { + wxMessageBox(wxString::Format(wxT("无法创建配置目录: %s"), configDir_)); + return; + } + wxMessageBox(wxString::Format(wxT("已创建配置目录: %s"), configDir_)); + } + + wxDir dir(configDir_); + if (!dir.IsOpened()) { + wxMessageBox(wxString::Format(wxT("无法打开配置目录: %s"), configDir_)); + return; + } + + wxString dirName; + bool cont = dir.GetFirst(&dirName, wxEmptyString, wxDIR_DIRS); + while (cont) { + wxString fullPath = configDir_ + wxFileName::GetPathSeparator() + dirName; + if (wxDirExists(fullPath)) { + listBox_->Append(dirName); + int lastIndex = listBox_->GetCount() - 1; + listBox_->SetClientData(lastIndex, new wxString(fullPath)); + } + cont = dir.GetNext(&dirName); + } +} + +void MainFrame::ShowConfig(wxCommandEvent& event) +{ + ShowConfig(); +} + +void MainFrame::OnListBoxSelected(wxCommandEvent& event) +{ + wxString cmakeContent; + int selection = listBox_->GetSelection(); + if (selection == wxNOT_FOUND) { + return; + } + + wxString selectedText = listBox_->GetString(selection); + wxString fullPath = configDir_ + wxFileName::GetPathSeparator() + selectedText; + + if (!wxDirExists(fullPath)) { + wxMessageBox(wxString::Format(wxT("目录不存在: %s"), fullPath), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + curFileTitle_->SetLabel(selectedText); + currentFilePath_ = fullPath + wxFileName::GetPathSeparator() + wxT("CMakeLists.txt"); + if (!wxFileExists(currentFilePath_)) { + textCtrl_->SetValue(cmakeContent); + return; + } + + wxFile cmakeFile(currentFilePath_); + if (!cmakeFile.IsOpened()) { + wxMessageBox(wxT("无法打开CMakeLists.txt文件"), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + cmakeFile.ReadAll(&cmakeContent); + cmakeFile.Close(); + textCtrl_->SetValue(cmakeContent); +} + +void MainFrame::InitCMakeSyntaxHighlighting() +{ + textCtrl_->SetLexer(wxSTC_LEX_CMAKE); + + // 基础样式配置 + textCtrl_->StyleSetForeground(wxSTC_CMAKE_DEFAULT, *wxBLACK); + textCtrl_->StyleSetForeground(wxSTC_CMAKE_COMMENT, wxColour(0, 128, 0)); // 绿色注释 + + // 字符串类型 + textCtrl_->StyleSetForeground(wxSTC_CMAKE_STRINGDQ, wxColour(163, 21, 21)); // 红色双引号字符串 + textCtrl_->StyleSetForeground(wxSTC_CMAKE_STRINGLQ, wxColour(163, 21, 21)); // 红色左单引号 + textCtrl_->StyleSetForeground(wxSTC_CMAKE_STRINGRQ, wxColour(163, 21, 21)); // 红色右单引号 + + // 命令和参数 + textCtrl_->StyleSetForeground(wxSTC_CMAKE_COMMANDS, wxColour(0, 0, 255)); // 蓝色命令 + textCtrl_->StyleSetBold(wxSTC_CMAKE_COMMANDS, true); + textCtrl_->StyleSetForeground(wxSTC_CMAKE_PARAMETERS, wxColour(255, 105, 0)); // 橙色参数 + + // 变量和数字 + textCtrl_->StyleSetForeground(wxSTC_CMAKE_VARIABLE, wxColour(128, 0, 128)); // 紫色变量 + textCtrl_->StyleSetForeground(wxSTC_CMAKE_STRINGVAR, wxColour(128, 0, 128)); // 紫色字符串变量 + textCtrl_->StyleSetForeground(wxSTC_CMAKE_NUMBER, wxColour(0, 100, 0)); // 深绿色数字 + + // 控制结构 + textCtrl_->StyleSetForeground(wxSTC_CMAKE_WHILEDEF, wxColour(0, 0, 139)); // 深蓝色while + textCtrl_->StyleSetForeground(wxSTC_CMAKE_FOREACHDEF, wxColour(0, 0, 139)); // 深蓝色foreach + textCtrl_->StyleSetForeground(wxSTC_CMAKE_IFDEFINEDEF, wxColour(0, 0, 139)); // 深蓝色if + textCtrl_->StyleSetForeground(wxSTC_CMAKE_MACRODEF, wxColour(139, 0, 139)); // 深紫色宏 + + const char* cmakeCommands = "add_executable add_library target_link_libraries " + "find_package if else endif foreach endfunction project cmake_minimum_required" + "while endwhile macro endmacro function set add_compile_options"; + + textCtrl_->SetKeyWords(0, cmakeCommands); + textCtrl_->StyleSetBackground(wxSTC_CMAKE_DEFAULT, wxColour(255, 255, 255)); +} + +void MainFrame::SaveCurrent(wxCommandEvent& event) +{ + if (currentFilePath_.empty()) { + wxMessageBox(wxT("当前无配置选择"), wxT("提示"), wxOK | wxICON_ERROR, this); + return; + } + wxFile cmakeFile(currentFilePath_, wxFile::write); + if (!cmakeFile.IsOpened()) { + wxMessageBox(wxT("无法打开CMakeLists.txt文件"), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + wxString content = textCtrl_->GetValue(); + if (!cmakeFile.Write(content)) { + wxMessageBox(wxString::Format(wxT("写入文件失败: %s"), currentFilePath_), wxT("错误"), wxOK | wxICON_ERROR, this); + cmakeFile.Close(); + return; + } + cmakeFile.Close(); + wxMessageBox(wxT("已保存")); +} + +void MainFrame::OnRightClick(wxCommandEvent& event) +{ + wxMenu menu; + bool hasSelection = (listBox_->GetSelection() != wxNOT_FOUND); + menu.Append(addID_, wxT("添加")); + menu.AppendSeparator(); + menu.Append(delID_, wxT("删除"))->Enable(hasSelection); + listBox_->PopupMenu(&menu); +} + +void MainFrame::AddMenu(wxCommandEvent& event) +{ + wxTextEntryDialog dialog(this, wxT("请输入新条目名称:"), wxT("添加新条目"), wxEmptyString, wxOK | wxCANCEL | wxCENTRE); + if (dialog.ShowModal() != wxID_OK) { + return; + } + wxString newItem = dialog.GetValue().Trim(); + if (newItem.IsEmpty()) { + wxMessageBox(wxT("条目名称不能为空"), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + wxString newDir = configDir_ + wxFileName::GetPathSeparator() + newItem; + + if (!wxFileName::Mkdir(newDir, 0755, wxPATH_MKDIR_FULL)) { + wxMessageBox(wxT("无法创建目录: ") + newDir, wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + listBox_->Append(newItem); + listBox_->SetSelection(listBox_->GetCount() - 1); + curFileTitle_->SetLabel(newItem); + currentFilePath_ = newDir + wxFileName::GetPathSeparator() + wxT("CMakeLists.txt"); + textCtrl_->SetValue(wxEmptyString); +} + +void MainFrame::DelMenu(wxCommandEvent& event) +{ + int selection = listBox_->GetSelection(); + if (selection == wxNOT_FOUND) { + return; + } + wxString selectedItem = listBox_->GetString(selection); + wxString targetDir = Normalize(configDir_ + wxFileName::GetPathSeparator() + selectedItem); + wxString msg = wxString::Format(wxT("确定要删除目录:\n%s\n及其所有内容吗?"), targetDir); + + wxMessageDialog dlg(this, msg, wxT("确认删除"), wxYES_NO | wxNO_DEFAULT | wxICON_WARNING); + + if (dlg.ShowModal() != wxID_YES) { + return; + } + + if (wxDirExists(targetDir)) { + wxFileName::Rmdir(targetDir, wxPATH_RMDIR_RECURSIVE); + listBox_->Delete(selection); + ShowExist(); + } else { + wxMessageBox(wxString::Format(wxT("目录 %s 不存在"), targetDir), wxT("错误"), wxOK | wxICON_ERROR, this); + } +} + +void MainFrame::ShowExist() +{ + if (!listBox_ || listBox_->IsEmpty()) { + return; + } + wxString firstItem = listBox_->GetString(0); + listBox_->SetSelection(0); + + wxString selectedText = firstItem; + wxString fullPath = Normalize(configDir_ + wxFileName::GetPathSeparator() + selectedText); + + if (!wxDirExists(fullPath)) { + wxMessageBox(wxString::Format(wxT("目录不存在: %s"), fullPath), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + wxString cmakeContent; + curFileTitle_->SetLabel(selectedText); + currentFilePath_ = Normalize(fullPath + wxFileName::GetPathSeparator() + wxT("CMakeLists.txt")); + + if (!wxFileExists(currentFilePath_)) { + textCtrl_->SetValue(cmakeContent); + return; + } + + wxFile cmakeFile(currentFilePath_); + if (!cmakeFile.IsOpened()) { + wxMessageBox(wxT("无法打开CMakeLists.txt文件"), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + cmakeFile.ReadAll(&cmakeContent); + cmakeFile.Close(); + textCtrl_->SetValue(cmakeContent); +} + +void MainFrame::OpenDir(const wxString& path) +{ + if (!wxDirExists(path)) { + wxMessageBox(wxString::Format(wxT("目录不存在:\n%s"), path), wxT("错误"), wxOK | wxICON_ERROR, this); + return; + } + + bool success = wxLaunchDefaultApplication(path); + + if (!success) { +#ifdef __WXMSW__ + wxString cmd = wxT("explorer \"") + path + wxT("\""); +#elif defined(__WXMAC__) + wxString cmd = wxT("open \"") + path + wxT("\""); +#else // Linux/Unix + wxString cmd = wxT("xdg-open \"") + path + wxT("\""); +#endif + success = wxExecute(cmd) != 0; + } + if (!success) { + wxMessageBox(wxString::Format(wxT("无法打开目录:\n%s"), path), wxT("错误"), wxOK | wxICON_ERROR, this); + } +} + +void MainFrame::OpenResDir(wxCommandEvent& event) +{ + OpenDir(configDir_); +} + +void MainFrame::OpenPrjDir(wxCommandEvent& event) +{ + OpenDir(textPath_->GetValue()); +} + +wxString MainFrame::Normalize(const wxString& path) +{ + wxFileName configDirFile(path); + configDirFile.Normalize(wxPATH_NORM_ALL); + return configDirFile.GetFullPath(); +} + +void MainFrame::SaveConfig() +{ + if (!wxFileName::DirExists(configDir_)) { + if (!wxFileName::Mkdir(configDir_, 0755, wxPATH_MKDIR_FULL)) { + wxLogError(wxT("无法创建配置目录: %s"), configDir_); + return; + } + } + wxString configFile = configDir_ + wxFileName::GetPathSeparator() + wxT("newproject.ini"); + wxFileConfig config(wxEmptyString, wxEmptyString, configFile, wxEmptyString, wxCONFIG_USE_LOCAL_FILE); + config.Write(wxT("/Paths/TextPath"), textPath_->GetValue()); + config.Write(wxT("/Paths/ProjectName"), projectName_->GetValue()); + config.Flush(); +} + +void MainFrame::LoadConfig() +{ + wxString configFile = configDir_ + wxFileName::GetPathSeparator() + wxT("newproject.ini"); + if (!wxFileName::FileExists(configFile)) { + return; + } + wxFileConfig config(wxEmptyString, wxEmptyString, configFile, wxEmptyString, wxCONFIG_USE_LOCAL_FILE); + wxString textPath, projectName; + if (config.Read(wxT("/Paths/TextPath"), &textPath)) { + textPath_->SetValue(textPath); + } + if (config.Read(wxT("/Paths/ProjectName"), &projectName)) { + projectName_->SetValue(projectName); + } +} + +bool MainFrame::CopyDirectory(const wxString& source, const wxString& dest, const wxString& projectName) +{ + wxDir dir(source); + wxString filename; + bool success = true; + + if (dir.GetFirst(&filename, wxEmptyString, wxDIR_FILES | wxDIR_DIRS)) { + do { + wxString sourceItem = source + wxFileName::GetPathSeparator() + filename; + wxString destItem = dest + wxFileName::GetPathSeparator() + filename; + sourceItem = Normalize(sourceItem); + destItem = Normalize(destItem); + + if (wxDirExists(sourceItem)) { + if (!wxFileName::Mkdir(destItem, 0755, wxPATH_MKDIR_FULL)) { + success = false; + break; + } + + if (!CopyDirectory(sourceItem, destItem, projectName)) { + success = false; + break; + } + } else { + if (!wxCopyFile(sourceItem, destItem)) { + success = false; + break; + } + + if (filename == wxT("CMakeLists.txt")) { + ProcessCMakeListsFile(destItem, projectName); + } + } + } while (success && dir.GetNext(&filename)); + } + + return success; +} + +void MainFrame::ProcessCMakeListsFile(const wxString& filePath, const wxString& projectName) +{ + wxFile file(filePath, wxFile::read); + if (file.IsOpened()) { + wxString content; + file.ReadAll(&content); + file.Close(); + + content.Replace(wxT("PJNAME"), projectName); + + wxFile dest(filePath, wxFile::write); + if (dest.IsOpened()) { + dest.Write(content); + dest.Close(); + } else { + wxMessageBox(wxString::Format(wxT("无法打开文件进行修改: %s"), filePath)); + } + } else { + wxMessageBox(wxString::Format(wxT("无法读取源文件: %s"), filePath)); + } +} \ No newline at end of file diff --git a/src/mainframe.h b/src/mainframe.h new file mode 100644 index 0000000..5599113 --- /dev/null +++ b/src/mainframe.h @@ -0,0 +1,62 @@ +#ifndef MAINFRAME_H +#define MAINFRAME_H + +#include +#include +#include +#include +#include + +class MainFrame : public wxFrame +{ +public: + explicit MainFrame(const wxString& title); + +private: + void InitControl(); + +private: + void OnNewerClick(wxCommandEvent& event); + void OnExitClick(wxCommandEvent& event); + void OnSelectPathClick(wxCommandEvent& event); + wxString GetConfigDir(); + void ShowConfig(wxCommandEvent& event); + void ShowConfig(); + void OnListBoxSelected(wxCommandEvent& event); + void InitCMakeSyntaxHighlighting(); + void SaveCurrent(wxCommandEvent& event); + void OnRightClick(wxCommandEvent& event); + void AddMenu(wxCommandEvent& event); + void DelMenu(wxCommandEvent& event); + void ShowExist(); + void OpenResDir(wxCommandEvent& event); + void OpenPrjDir(wxCommandEvent& event); + wxString Normalize(const wxString& path); + void SaveConfig(); + void LoadConfig(); + bool CopyDirectory(const wxString& source, const wxString& dest, const wxString& projectName); + void ProcessCMakeListsFile(const wxString& filePath, const wxString& projectName); + void OpenDir(const wxString& path); + +private: + wxListBox* listBox_; + wxStyledTextCtrl* textCtrl_; + wxButton* btnNewer_; + wxButton* btnExit_; + wxButton* btnSelectPath_; + wxTextCtrl* textPath_; + wxString configDir_; + wxTextCtrl* projectName_; + wxButton* btnRefresh_; + wxButton* btnSave_; + wxString currentFilePath_; + wxStaticText* curFileTitle_; + wxButton* btnOpenResDir_; + wxButton* btnOpenPrjDir_; + +private: + wxWindowID addID_; + wxWindowID delID_; +}; + +#endif // MAINFRAME_H \ No newline at end of file