-
-
Save ra2003/55f78294aa4c45051f9731c73751af0d to your computer and use it in GitHub Desktop.
Text editor wxWidgets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Main.cpp | |
| #include <wx/wx.h> | |
| #include "App.h" | |
| IMPLEMENT_APP(App) | |
| // App.h | |
| #include <wx/wx.h> | |
| #include "Frame.h" | |
| class App : public wxApp | |
| { | |
| public: | |
| bool OnInit(); | |
| }; | |
| bool App::OnInit() | |
| { | |
| Frame<wxFileDialog> *frame = new Frame<wxFileDialog>(); | |
| frame->Show(true); | |
| return true; | |
| } | |
| // Frame.h | |
| #pragma once | |
| #include <wx/wx.h> | |
| #include "CodeTextCtrl.h" | |
| template <class FileDialog> | |
| class Frame : public wxFrame | |
| { | |
| public: | |
| Frame(); | |
| CodeTextCtrl *text; | |
| protected: | |
| void OnNew(wxCommandEvent& event); | |
| void OnOpen(wxCommandEvent& event); | |
| void OnSaveAs(wxCommandEvent& event); | |
| void OnSave(wxCommandEvent& event); | |
| void OnExit(wxCommandEvent& event); | |
| void OnModified(wxStyledTextEvent& event); | |
| wxString fileName; | |
| }; | |
| template <class FileDialog> | |
| Frame<FileDialog>::Frame() : wxFrame(NULL, wxID_ANY, "Текстовый редактор C++", wxDefaultPosition, wxSize(800, 600)) | |
| { | |
| text = new CodeTextCtrl(this); | |
| wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); | |
| sizer->Add(text, 1, wxEXPAND); | |
| SetSizer(sizer); | |
| wxMenu *menuFile = new wxMenu; | |
| menuFile->Append(wxID_NEW, "&Создать...\tCtrl-N", "Создать новый файл"); | |
| menuFile->AppendSeparator(); | |
| menuFile->Append(wxID_OPEN, "&Открыть...\tCtrl-O", "Открыть файл"); | |
| menuFile->AppendSeparator(); | |
| menuFile->Append(wxID_SAVE, "&Сохранить\tCtrl-S", "Сохранить изменения"); | |
| menuFile->Append(wxID_SAVEAS, "&Сохранить как...", "Сохранить файл как..."); | |
| menuFile->AppendSeparator(); | |
| menuFile->Append(wxID_EXIT, "&Выход", "Закрыть программу"); | |
| wxMenuBar *menuBar = new wxMenuBar; | |
| menuBar->Append(menuFile, "&Файл"); | |
| SetMenuBar(menuBar); | |
| CreateStatusBar(); | |
| SetStatusText("Текстовый редактор кода C++ с подсветкой синтаксиса"); | |
| Bind(wxEVT_MENU, &Frame::OnExit, this, wxID_EXIT); | |
| Bind(wxEVT_MENU, &Frame::OnNew, this, wxID_NEW); | |
| Bind(wxEVT_MENU, &Frame::OnOpen, this, wxID_OPEN); | |
| Bind(wxEVT_MENU, &Frame::OnSaveAs, this, wxID_SAVEAS); | |
| Bind(wxEVT_MENU, &Frame::OnSave, this, wxID_SAVE); | |
| Bind(wxEVT_STC_CHANGE, &Frame::OnModified, this); | |
| } | |
| template <class FileDialog> | |
| void Frame<FileDialog>::OnNew(wxCommandEvent & event) | |
| { | |
| fileName = ""; | |
| text->ClearAll(); | |
| SetStatusText("Новый файл"); | |
| } | |
| template <class FileDialog> | |
| void Frame<FileDialog>::OnOpen(wxCommandEvent & event) | |
| { | |
| FileDialog openFileDialog(this, "Открыть файл", "", "", | |
| "Файлы исходного кода (*.cpp)|*.cpp|Заголовочные файлы (*.h)|*.h", wxFD_OPEN | wxFD_FILE_MUST_EXIST); | |
| if (openFileDialog.ShowModal() == wxID_CANCEL) | |
| return; | |
| fileName = openFileDialog.GetPath(); | |
| if (text->LoadFile(fileName)) | |
| SetStatusText("Открыт файл: " + fileName); | |
| else | |
| SetStatusText("Не возможно открыть файл: " + fileName); | |
| } | |
| template <class FileDialog> | |
| void Frame<FileDialog>::OnSaveAs(wxCommandEvent & event) | |
| { | |
| FileDialog saveFileDialog(this, "Сохранить файл", "", "", | |
| "Файлы исходного кода (*.cpp)|*.cpp|Заголовочные файлы (*.h)|*.h", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); | |
| if (saveFileDialog.ShowModal() == wxID_CANCEL) | |
| return; | |
| fileName = saveFileDialog.GetPath(); | |
| if (text->SaveFile(fileName)) | |
| SetStatusText("Файл сохранен как: " + fileName); | |
| else | |
| SetStatusText("Не возможно сохранить файл: " + fileName); | |
| } | |
| template<class FileDialog> | |
| inline void Frame<FileDialog>::OnSave(wxCommandEvent & event) | |
| { | |
| if (fileName.length() > 0) { | |
| if (text->GetModify()) { | |
| if (text->SaveFile(fileName)) | |
| SetStatusText("Изменения сохранены"); | |
| else | |
| SetStatusText("Не возможно сохранить изменения"); | |
| } | |
| } | |
| else { | |
| OnSaveAs(event); | |
| } | |
| } | |
| template <class FileDialog> | |
| void Frame<FileDialog>::OnExit(wxCommandEvent& event) | |
| { | |
| Close(true); | |
| } | |
| template<class FileDialog> | |
| void Frame<FileDialog>::OnModified(wxStyledTextEvent & event) | |
| { | |
| if (text->GetModify()) { | |
| SetStatusText("Файл изменен"); | |
| } | |
| else { | |
| SetStatusText("Нет изменений для сохранения"); | |
| } | |
| } | |
| // CodeTextCtrl.h | |
| #pragma once | |
| #include <wx/wx.h> | |
| #include <wx/stc/stc.h> | |
| class CodeTextCtrl : public wxStyledTextCtrl | |
| { | |
| private: | |
| const int MARGIN_LINE_NUMBERS = 0; | |
| const wxString CPP_WORD_1 = | |
| "asm else new this" | |
| "auto enum operator throw " | |
| "bool explicit private true " | |
| "break export protected try " | |
| "case extern public typedef " | |
| "catch false register typeid " | |
| "char float reinterpret_cast typename " | |
| "class for return union " | |
| "const friend short unsigned " | |
| "const_cast goto signed using " | |
| "continue if sizeof virtual " | |
| "default inline static void " | |
| "delete int static__cast volatile " | |
| "do long struct wchar_t " | |
| "double mutable switch while " | |
| "dynamic_cast namespace template"; | |
| const int MARKER_TAB = 1; | |
| const int MARKER_EOF = 2; | |
| int markerHandlerEOF = 0; | |
| public: | |
| CodeTextCtrl(wxWindow *); | |
| const int MASK_TAB = (1 << MARKER_TAB); | |
| const int MASK_EOF = (1 << MARKER_EOF); | |
| void OnModified(wxStyledTextEvent& event); | |
| }; | |
| // CodeTextCtrl.cpp | |
| #include <wx/wx.h> | |
| #include <wx/stc/stc.h> | |
| #include "CodeTextCtrl.h" | |
| CodeTextCtrl::CodeTextCtrl(wxWindow *parent) : wxStyledTextCtrl(parent) | |
| { | |
| StyleClearAll(); | |
| SetLexer(wxSTC_LEX_CPP); | |
| SetMarginWidth(MARGIN_LINE_NUMBERS, 50); | |
| StyleSetForeground(wxSTC_STYLE_LINENUMBER, wxColour(75, 75, 75)); | |
| StyleSetBackground(wxSTC_STYLE_LINENUMBER, wxColour(220, 220, 220)); | |
| SetMarginType(MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER); | |
| SetWrapMode(wxSTC_WRAP_NONE); // other choice is wxSTC_WRAP_WORD | |
| SetUseHorizontalScrollBar(false); | |
| wxFont font(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); | |
| StyleSetFont(wxSTC_STYLE_LINENUMBER, font); | |
| StyleSetFont(wxSTC_STYLE_DEFAULT, font); | |
| StyleSetFont(wxSTC_C_STRING, font); | |
| StyleSetFont(wxSTC_C_STRING, font); | |
| StyleSetFont(wxSTC_C_PREPROCESSOR, font); | |
| StyleSetFont(wxSTC_C_IDENTIFIER, font); | |
| StyleSetFont(wxSTC_C_NUMBER, font); | |
| StyleSetFont(wxSTC_C_CHARACTER, font); | |
| StyleSetFont(wxSTC_C_WORD, font); | |
| StyleSetFont(wxSTC_C_WORD2, font); | |
| StyleSetFont(wxSTC_C_COMMENT, font); | |
| StyleSetFont(wxSTC_C_COMMENTLINE, font); | |
| StyleSetFont(wxSTC_C_COMMENTDOC, font); | |
| StyleSetFont(wxSTC_C_COMMENTDOCKEYWORD, font); | |
| StyleSetFont(wxSTC_C_COMMENTDOCKEYWORDERROR, font); | |
| StyleSetFont(wxSTC_C_OPERATOR, font); | |
| StyleSetFont(wxSTC_C_DEFAULT, font); | |
| StyleSetForeground(wxSTC_C_STRING, wxColour(150, 0, 0)); | |
| StyleSetForeground(wxSTC_C_PREPROCESSOR, wxColour(165, 105, 0)); | |
| StyleSetForeground(wxSTC_C_IDENTIFIER, wxColour(40, 0, 60)); | |
| StyleSetForeground(wxSTC_C_NUMBER, wxColour(0, 150, 0)); | |
| StyleSetForeground(wxSTC_C_CHARACTER, wxColour(150, 0, 0)); | |
| StyleSetForeground(wxSTC_C_WORD, wxColour(0, 0, 150)); | |
| StyleSetForeground(wxSTC_C_WORD2, wxColour(0, 150, 0)); | |
| StyleSetForeground(wxSTC_C_COMMENT, wxColour(150, 150, 150)); | |
| StyleSetForeground(wxSTC_C_COMMENTLINE, wxColour(150, 150, 150)); | |
| StyleSetForeground(wxSTC_C_COMMENTDOC, wxColour(150, 150, 150)); | |
| StyleSetForeground(wxSTC_C_COMMENTDOCKEYWORD, wxColour(0, 0, 200)); | |
| StyleSetForeground(wxSTC_C_COMMENTDOCKEYWORDERROR, wxColour(0, 0, 200)); | |
| SetKeyWords(0, CPP_WORD_1); | |
| SetEdgeMode(wxSTC_EDGE_BACKGROUND); | |
| SetEdgeColour(wxColour("red")); | |
| SetEdgeColumn(120); | |
| //SetViewWhiteSpace(wxSTC_WS_VISIBLEALWAYS); | |
| wxColor yellow("yellow"); | |
| MarkerDefine(MARKER_TAB, wxSTC_MARK_ARROW); | |
| MarkerSetForeground(MARKER_TAB, yellow); | |
| MarkerSetBackground(MARKER_TAB, yellow); | |
| wxColor orange("orange"); | |
| MarkerDefine(MARKER_EOF, wxSTC_MARK_ARROW); | |
| MarkerSetForeground(MARKER_EOF, orange); | |
| MarkerSetBackground(MARKER_EOF, orange); | |
| SetUseTabs(false); | |
| SetTabWidth(4); | |
| SetIndent(4); | |
| Bind(wxEVT_STC_MODIFIED, &CodeTextCtrl::OnModified, this); | |
| } | |
| void CodeTextCtrl::OnModified(wxStyledTextEvent & event) | |
| { | |
| int type = event.GetModificationType(); | |
| if ((type & (wxSTC_MOD_INSERTTEXT | wxSTC_MOD_DELETETEXT)) == 0) | |
| return; | |
| int linesAdded = event.GetLinesAdded(); | |
| int position = event.GetPosition(); | |
| int curLine = LineFromPosition(position); | |
| int lineCount = GetLineCount(); | |
| for (int j = curLine; (j <= j + linesAdded) && (j < lineCount); ++j) { | |
| int marker = MarkerGet(j); | |
| bool isTAB = (MASK_TAB & marker) > 0; | |
| wxString line = GetLineText(j); | |
| int i = line.find("\t"); | |
| if (i > -1) { // в строке найден символ "\t" | |
| if (!isTAB) { // в строке нет маркера TAB | |
| MarkerAdd(j, MARKER_TAB); | |
| } | |
| } | |
| else { // в строке не найден символ "\t" | |
| if (isTAB) { // установлен маркер TAB | |
| MarkerDelete(j, MARKER_TAB); | |
| } | |
| } | |
| } | |
| if (LineLength(lineCount - 1) > 0) { // длина последней строки больше 0 | |
| int markerLine = MarkerLineFromHandle(markerHandlerEOF); | |
| if (markerLine != lineCount - 1) | |
| markerHandlerEOF = MarkerAdd(lineCount - 1, MARKER_EOF); | |
| } | |
| else { | |
| MarkerDeleteHandle(markerHandlerEOF); | |
| markerHandlerEOF = 0; | |
| } | |
| } | |
| // Tests.cpp | |
| #include <gtest/gtest.h> | |
| #include <wx/wx.h> | |
| #include <Frame.h> | |
| #include <CodeTextCtrl.h> | |
| #include <string> | |
| #include <fstream> | |
| const std::string SOME_TEXT_IN("some text in"); | |
| const std::string SOME_TEXT_OUT("some text out"); | |
| const std::string FILE_NAME("test"); | |
| class MockFileDialog | |
| { | |
| public: | |
| MockFileDialog(wxWindow *parent, | |
| const wxString& message = wxFileSelectorPromptStr, | |
| const wxString& defaultDir = wxEmptyString, | |
| const wxString& defaultFile = wxEmptyString, | |
| const wxString& wildCard = wxFileSelectorDefaultWildcardStr, | |
| long style = wxFD_DEFAULT_STYLE, | |
| const wxPoint& pos = wxDefaultPosition, | |
| const wxSize& sz = wxDefaultSize, | |
| const wxString& name = wxFileDialogNameStr) {}; | |
| wxString GetPath() { | |
| return wxString(FILE_NAME); | |
| }; | |
| int ShowModal() { | |
| return wxID_OPEN; | |
| }; | |
| }; | |
| TEST(CodeText, TabSpace) | |
| { | |
| wxFrame frame(NULL, wxID_ANY, ""); | |
| CodeTextCtrl text(&frame); | |
| const char *textRaw = "1 line\n2\tline\n3 line\n4\tline\n"; | |
| text.InsertTextRaw(0, textRaw); | |
| int maskMarkersLine1 = text.MarkerGet(0); | |
| int maskMarkersLine2 = text.MarkerGet(1); | |
| int maskMarkersLine3 = text.MarkerGet(2); | |
| int maskMarkersLine4 = text.MarkerGet(3); | |
| // В первой строке маркер не установлен | |
| ASSERT_EQ(maskMarkersLine1 & text.MASK_TAB, 0); | |
| // Во второй строке маркер установлен | |
| ASSERT_GT(maskMarkersLine2 & text.MASK_TAB, 0); | |
| // В третьей строке маркер не установлен | |
| ASSERT_EQ(maskMarkersLine3 & text.MASK_TAB, 0); | |
| // В четвертой строке маркер установлен | |
| ASSERT_GT(maskMarkersLine4 & text.MASK_TAB, 0); | |
| } | |
| TEST(CodeText, EndOfFile) | |
| { | |
| wxFrame frame(NULL, wxID_ANY, ""); | |
| CodeTextCtrl text(&frame); | |
| const char *textRaw = "first line\nsecond line"; | |
| int pos = text.GetLastPosition(); | |
| text.InsertTextRaw(pos, textRaw); | |
| int lastLineNumber = text.GetLineCount() - 1; | |
| int maskMarkers = text.MarkerGet(lastLineNumber); | |
| // В последней строке установлен маркер | |
| ASSERT_GT(maskMarkers & text.MASK_EOF, 0); | |
| } | |
| TEST(MenuCommand, New) | |
| { | |
| Frame<MockFileDialog> frame; | |
| // Заполняем редактор произвольныи текстом | |
| frame.text->SetText(wxString(SOME_TEXT_IN)); | |
| ASSERT_LT(0, frame.text->GetTextLength()); | |
| // Команда меню "Создать" должна очищать текст в редакторе | |
| wxCommandEvent evt(wxEVT_MENU, wxID_NEW); | |
| frame.GetEventHandler()->ProcessEvent(evt); | |
| ASSERT_EQ(0, frame.text->GetTextLength()); | |
| } | |
| TEST(MenuCommand, Open) | |
| { | |
| Frame<MockFileDialog> frame; | |
| std::ofstream file(FILE_NAME); | |
| file << SOME_TEXT_IN; | |
| file.close(); | |
| // Команда меню "Открыть..." загружает текст в редактор из файла | |
| wxCommandEvent evt(wxEVT_MENU, wxID_OPEN); | |
| frame.GetEventHandler()->ProcessEvent(evt); | |
| const std::string result = frame.text->GetText().ToStdString(); | |
| ASSERT_EQ(std::string(SOME_TEXT_IN), result); | |
| } | |
| TEST(MenuCommand, SaveAs) | |
| { | |
| Frame<MockFileDialog> frame; | |
| frame.text->SetText(wxString(SOME_TEXT_OUT)); | |
| // Команда меню "Сохранить как..." сохраняет текст из редактора в файл | |
| wxCommandEvent evt(wxEVT_MENU, wxID_SAVEAS); | |
| frame.GetEventHandler()->ProcessEvent(evt); | |
| std::string result; | |
| std::ifstream file(FILE_NAME); | |
| std::getline(file, result); | |
| ASSERT_EQ(SOME_TEXT_OUT, result); | |
| } | |
| TEST(MenuCommand, Save) | |
| { | |
| Frame<MockFileDialog> frame; | |
| std::ofstream file(FILE_NAME); | |
| file << SOME_TEXT_IN; | |
| file.close(); | |
| // Команда меню "Открыть..." загружает текст в редактор из файла | |
| wxCommandEvent evt(wxEVT_MENU, wxID_OPEN); | |
| frame.GetEventHandler()->ProcessEvent(evt); | |
| int lastPos = frame.text->GetLastPosition(); | |
| frame.text->InsertTextRaw(lastPos, SOME_TEXT_OUT.c_str()); | |
| // Команда меню "Сохранить" сохраняет изменения в файл | |
| wxCommandEvent evt2(wxEVT_MENU, wxID_SAVE); | |
| frame.GetEventHandler()->ProcessEvent(evt2); | |
| std::string result; | |
| std::ifstream file2(FILE_NAME); | |
| std::getline(file2, result); | |
| ASSERT_EQ(SOME_TEXT_IN + SOME_TEXT_OUT, result); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment