├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── assets ├── gadget_example.png ├── gadget_icon_med.png └── gadget_title.png └── src ├── Command.cpp ├── Command.h ├── CommandBar.cpp ├── CommandBar.h ├── Editor.cpp ├── Editor.h ├── FileHelper.cpp ├── FileHelper.h ├── FileTree.cpp ├── FileTree.h ├── Frame.cpp ├── Frame.h ├── Gadget.cpp ├── Gadget.h ├── HelpFile.cpp ├── HelpFile.h ├── Panel.cpp ├── Panel.h ├── StatusBar.cpp ├── StatusBar.h ├── StatusSection.cpp ├── StatusSection.h ├── Window.cpp ├── Window.h └── res └── gadget.xpm /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | end_of_line = lf 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | gadget 2 | **/*.swp 3 | *.sh 4 | *.vim 5 | *.txt 6 | *.o 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | language: cpp 3 | compiler: gcc 4 | 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - gcc-8 11 | - g++-8 12 | 13 | before_install: 14 | - sudo apt-get update 15 | - sudo apt-get -y install libgtk2.0-dev 16 | 17 | install: 18 | - sudo ln -s /usr/bin/gcc-8 /usr/local/bin/gcc 19 | - sudo ln -s /usr/bin/g++-8 /usr/local/bin/g++ 20 | - export CC=/usr/bin/gcc-8 21 | - export CXX=/usr/bin/g++-8 22 | - gcc -v && g++ -v 23 | - cd $HOME 24 | - curl -fsSL -O https://github.com/wxWidgets/wxWidgets/releases/download/v3.0.5/wxWidgets-3.0.5.tar.bz2 25 | - tar -xjf wxWidgets-3.0.5.tar.bz2 26 | - cd wxWidgets-3.0.5 27 | - mkdir gtk-build 28 | - cd gtk-build 29 | - ../configure 30 | - make -j3 31 | - sudo make install 32 | - sudo ldconfig 33 | 34 | script: 35 | - cd $TRAVIS_BUILD_DIR 36 | - make 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Christopher Lin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CXX=g++ 3 | RM=rm -f 4 | CXXFLAGS=`wx-config --cxxflags` -std=c++17 -lstdc++fs 5 | LDLIBS=`wx-config --libs core,aui,richtext,stc` 6 | 7 | SRCS=Gadget.cpp Frame.cpp Window.cpp Editor.cpp FileTree.cpp Panel.cpp Command.cpp CommandBar.cpp StatusBar.cpp StatusSection.cpp HelpFile.cpp FileHelper.cpp 8 | OBJS=$(subst .cpp,.o,$(SRCS)) 9 | 10 | VPATH=./src 11 | 12 | all: gadget 13 | 14 | gadget: $(OBJS) 15 | $(CXX) -o gadget $(OBJS) $(CXXFLAGS) $(LDLIBS) 16 | 17 | Gadget.o: Gadget.cpp Gadget.h ./src/res/gadget.xpm 18 | $(CXX) -c ./src/Gadget.cpp $(CXXFLAGS) 19 | 20 | Frame.o: Frame.cpp Frame.h 21 | $(CXX) -c ./src/Frame.cpp $(CXXFLAGS) 22 | 23 | Window.o: Window.cpp Window.h 24 | $(CXX) -c ./src/Window.cpp $(CXXFLAGS) 25 | 26 | Editor.o: Editor.cpp Editor.h 27 | $(CXX) -c ./src/Editor.cpp $(CXXFLAGS) 28 | 29 | FileTree.o: FileTree.cpp FileTree.h 30 | $(CXX) -c ./src/FileTree.cpp $(CXXFLAGS) 31 | 32 | Panel.o: Panel.cpp Panel.h 33 | $(CXX) -c ./src/Panel.cpp $(CXXFLAGS) 34 | 35 | Command.o: Command.cpp Command.h 36 | $(CXX) -c ./src/Command.cpp $(CXXFLAGS) 37 | 38 | CommandBar.o: CommandBar.cpp CommandBar.h 39 | $(CXX) -c ./src/CommandBar.cpp $(CXXFLAGS) 40 | 41 | StatusBar.o: StatusBar.cpp StatusBar.h 42 | $(CXX) -c ./src/StatusBar.cpp $(CXXFLAGS) 43 | 44 | StatusSection.o: StatusSection.cpp StatusSection.h 45 | $(CXX) -c ./src/StatusSection.cpp $(CXXFLAGS) 46 | 47 | HelpFile.o: HelpFile.cpp HelpFile.h 48 | $(CXX) -c ./src/HelpFile.cpp $(CXXFLAGS) 49 | 50 | FileHelper.o: FileHelper.cpp FileHelper.h 51 | $(CXX) -c ./src/FileHelper.cpp $(CXXFLAGS) 52 | 53 | clean: 54 | $(RM) $(OBJS) 55 | 56 | distclean: clean 57 | $(RM) gadget 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | A no-hassle GVim-inspired GUI text editor. 7 |

8 | 9 |

10 | 11 | 12 | 13 | 14 | 15 | 16 |

17 | 18 |

19 | 20 |

21 | 22 | ## Installation 23 | 24 | ### Linux 25 | 26 | #### Debian-based distros 27 | 28 | Install via the latest corresponding Debian package: 29 | 30 | ``` 31 | curl -L -O https://github.com/lintopher0315/gadget/releases/download/v1.0.0/gadget_1.0.0_amd64.deb 32 | sudo apt install ./gadget_1.0.0_amd64.deb 33 | ``` 34 | 35 | Alternatively, navigate to https://github.com/lintopher0315/gadget/releases/, download the Debian package manually, and then run `sudo apt install ./gadget_1.0.0_amd64.deb`. 36 | 37 | ## Features 38 | * One-to-one functionality of numerous Vim commands 39 | * 4 editor modes: *Normal*, *Edit*, *Visual*, and *Line* 40 | * Similar to Vim, *Visual* mode can be activated by clicking and dragging with the mouse 41 | * Allows CTRL-C and CTRL-V to/from clipboard in *Visual* and *Line* modes 42 | * Minimalistic status bar 43 | * Displays unsaved and read-only files 44 | * Shows caret position and size of file by bytes and lines 45 | * Smart indentation 46 | * Color-coded file tree 47 | * Sorted by directories, dotfiles, then regular files by extension 48 | * Directories colored according to amount of files: pink if empty, red if <10, yellow if <30, green if <50, blue if <70, and purple for any higher quantity 49 | * Tab splitting 50 | * Syntax highlighting (only for C++ for now, work in progress) 51 | * Soon-to-be cross-platform (also work in progress) 52 | 53 | ## Usage 54 | 55 | After installing, run the following command to open Gadget: 56 | 57 | ``` 58 | gadget & 59 | ``` 60 | 61 | Gadget will open up in *Normal* mode which is the main mode for running commands for moving, editing, configuring, etc. Directly typing in *Normal* mode will begin forming a command underneath the status bar. Pressing `ESC` will clear the command bar. `ESC` will also switch from any other mode to *Normal* mode. 62 | 63 | | *Normal* mode commands | Behavior | 64 | | -------------------- | -------- | 65 | | `[num]h` | move caret left `[num]` times | 66 | | `[num]j` | move caret down `[num]` times | 67 | | `[num]k` | move caret up `[num]` times | 68 | | `[num]l` | move caret right `[num]` times | 69 | | `[num]w` | jump to the start of the next word `[num]` times | 70 | | `[num]b` | jump to the start of the previous word `[num]` times | 71 | | `gg` | jump to the beginning of the file | 72 | | `G` | jump to the end of the file | 73 | | `[num]G` | jump to the beginning of line `[num]` | 74 | | `_` | jump to the first non-whitespace character of the current line | 75 | | `0` | jump to the beginning of the current line | 76 | | `$` | jump to the end of the current line | 77 | | `[num]x` | cut the character the caret is currently on `[num]` times | 78 | | `[num]o` | append `[num]` empty lines below the current line | 79 | | `[num]O` | append `[num]` empty lines above the current line | 80 | | `[num]dd` | cut `[num]` lines | 81 | | `D` | cut to the end of the current line | 82 | | `f [char]` | jump to the next instance of `[char]` on the current line | 83 | | `F [char]` | jump to the previous instance of `[char]` on the current line | 84 | | `t [char]` | jump just before the next instance of `[char]` on the current line | 85 | | `T [char]` | jump just after the previous instance of `[char]` on the current line | 86 | | `p` | past the contents of the clipboard after the caret | 87 | | `P` | past the contents of the clipboard before the caret | 88 | | `[num]gt` | switch to the tab indicated by `[num]` | 89 | | `i` | change to *Edit* mode 90 | | `I` | change to *Edit* mode and jump before the first non-whitespace character of the current line | 91 | | `a` | change to *Edit* mode and move the caret right | 92 | | `A` | change to *Edit* mode and jump to the end of the current line | 93 | | `v` | switch to *Visual* mode | 94 | | `V` | switch to *Line* mode | 95 | | `:q` | quit and close the current document | 96 | | `:w` | save the document to the file it points to | 97 | | `:w [file]` | save the document to `[file]` and set it to point to `[file]` | 98 | | `:wq` | save and close the document | 99 | | `:e [file]` | open a new file, `[file]`, in the current editor | 100 | | `:tabedit` | create a new empty document in another tab | 101 | | `:tabedit [file]` | create a new document in another tab pointing to `[file]` 102 | | `:split` | split the current tab horizontally | 103 | | `:vsplit` | split the current tab vertically | 104 | | `:help` | open the help file in a new tab | 105 | 106 | *Edit* mode is for writing and acts as any standard text editor. *Edit* mode has no commands. 107 | 108 | *Visual* mode allows the performing of actions on blocks of text like copying, pasting, case-changing, etc. Once in visual mode, the anchor of the selection will begin from where the caret is positioned. All movement commands seen in *Normal* mode (e.g. `hjkl`, `gg`, `w`, etc.) can be used to extend the selection. 109 | 110 | | *Visual* mode commands | Behavior | 111 | | -------------------- | -------- | 112 | | `x` | cuts the selection | 113 | | `u` | changes selection to lowercase | 114 | | `U` | changes selection to uppercase | 115 | | `y` | copies selection to the clipboard | 116 | 117 | *Line* mode is similar to visual mode, but differs in that it only extends the selection line-by-line. This is useful for selecting blocks of text where only the lines matter. All *Visual* commands will also work in *Line* mode as well as *Normal* mode movement commands. 118 | 119 | | *Line* mode commands | Behavior | 120 | | -------------------- | -------- | 121 | | `[num]<` | shifts the selected lines `[num]` times to the left | 122 | | `[num]>` | shifts the selected lines `[num]` times to the right | -------------------------------------------------------------------------------- /assets/gadget_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lintopher0315/gadget/9bcd399aa087f6e82a9353e883d004c50d92c9d5/assets/gadget_example.png -------------------------------------------------------------------------------- /assets/gadget_icon_med.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lintopher0315/gadget/9bcd399aa087f6e82a9353e883d004c50d92c9d5/assets/gadget_icon_med.png -------------------------------------------------------------------------------- /assets/gadget_title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lintopher0315/gadget/9bcd399aa087f6e82a9353e883d004c50d92c9d5/assets/gadget_title.png -------------------------------------------------------------------------------- /src/Command.cpp: -------------------------------------------------------------------------------- 1 | #include "Command.h" 2 | 3 | Command::Command(void) { 4 | prefix = NORMAL_PREFIX; 5 | } 6 | 7 | void Command::clear(void) { 8 | prefix = NORMAL_PREFIX; 9 | cmd = ""; 10 | } 11 | 12 | bool Command::isClear(void) const { 13 | return prefix == NORMAL_PREFIX && cmd.empty(); 14 | } 15 | 16 | int Command::isValidNormal(void) const { 17 | if (prefix == NORMAL_PREFIX) { 18 | for (int i = 0; i < sizeof(ACT_LIST) / sizeof(ACT_LIST[0]); ++i) { 19 | if (std::regex_match(cmd, std::regex(ACT_LIST[i]))) { 20 | return i; 21 | } 22 | } 23 | } 24 | return -1; 25 | } 26 | 27 | int Command::isValidCommand(void) const { 28 | if (prefix == COMMAND_PREFIX) { 29 | for (int i = 0; i < sizeof(CMD_LIST) / sizeof(CMD_LIST[0]); ++i) { 30 | if (std::regex_match(cmd, std::regex(CMD_LIST[i]))) { 31 | return i; 32 | } 33 | } 34 | } 35 | return -1; 36 | } 37 | 38 | int Command::isValidVisual(void) const { 39 | if (prefix == NORMAL_PREFIX) { 40 | for (int i = 0; i < sizeof(VIS_LIST) / sizeof(VIS_LIST[0]); ++i) { 41 | if (std::regex_match(cmd, std::regex(VIS_LIST[i]))) { 42 | return i; 43 | } 44 | } 45 | } 46 | return -1; 47 | } 48 | 49 | int Command::isValidLine(void) const { 50 | if (prefix == NORMAL_PREFIX) { 51 | for (int i = 0; i < sizeof(LIN_LIST) / sizeof(LIN_LIST[0]); ++i) { 52 | if (std::regex_match(cmd, std::regex(LIN_LIST[i]))) { 53 | return i; 54 | } 55 | } 56 | } 57 | return -1; 58 | } 59 | 60 | std::pair Command::parseNormal() const { 61 | int num = 0; 62 | int i = 0; 63 | while (i < cmd.size() && isdigit(cmd[i])) { 64 | num *= 10; 65 | num += cmd[i] - '0'; 66 | ++i; 67 | } 68 | return {std::max(1, num), cmd.substr(i, cmd.size()-i)}; 69 | } 70 | 71 | std::vector Command::parseCommand() const { 72 | std::vector result; 73 | std::string token; 74 | std::istringstream tokenStream(cmd); 75 | while (std::getline(tokenStream, token, ' ')) { 76 | result.push_back(token); 77 | } 78 | return result; 79 | } 80 | -------------------------------------------------------------------------------- /src/Command.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define NORMAL_PREFIX 0 9 | #define COMMAND_PREFIX 1 10 | 11 | class Command { 12 | public: 13 | Command(void); 14 | 15 | void clear(void); 16 | bool isClear(void) const; 17 | int isValidNormal(void) const; 18 | int isValidCommand(void) const; 19 | int isValidVisual(void) const; 20 | int isValidLine(void) const; 21 | std::pair parseNormal(void) const; 22 | std::vector parseCommand(void) const; 23 | 24 | unsigned short prefix; 25 | std::string cmd; 26 | 27 | private: 28 | const std::string CMD_LIST[6]={"q", "(w|wq)( [^ ]+)?", "e [^ ]+", "tabedit( [^ ]+)*", "v?split", "help"}; 29 | const std::string ACT_LIST[13]={"[iIaA]", "([1-9][0-9]*)?[hjkl]", "([1-9][0-9]*)?[oO]", "[_$0]", "(gg|G|[1-9][0-9]*G)", "[1-9][0-9]*gt", "([1-9][0-9]*)?[wb]", "[fFtT].", "v", "V", "[pP]", "x", "(([1-9][0-9]*)?dd|D)"}; 30 | const std::string VIS_LIST[8]={"([1-9][0-9]*)?[hjkl]", "[xd]", "[uU]", "(gg|G|[1-9][0-9]*G)", "y", "([1-9][0-9]*)?[wb]", "[_$0]", "[fFtT]."}; 31 | const std::string LIN_LIST[6]={"([1-9][0-9]*)?[jk]", "[xd]", "[uU]", "([1-9][0-9]*)?[<>]", "y", "(gg|G|[1-9][0-9]*G)"}; 32 | }; 33 | -------------------------------------------------------------------------------- /src/CommandBar.cpp: -------------------------------------------------------------------------------- 1 | #include "CommandBar.h" 2 | 3 | CommandBar::CommandBar(wxWindow *parent) : wxRichTextCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(wxGetDisplaySize().GetWidth()/2, 30), wxRE_MULTILINE, wxDefaultValidator) { 4 | SetEditable(false); 5 | EnableVerticalScrollbar(false); 6 | wxFont *font = new wxFont(10, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString); 7 | SetFont(*font); 8 | 9 | SetBackgroundColour(wxColour(37, 37, 38)); 10 | wxRichTextAttr attr = GetBasicStyle(); 11 | attr.SetTextColour(wxColour(224, 227, 23)); 12 | SetBasicStyle(attr); 13 | 14 | Bind(wxEVT_LEFT_DOWN, &CommandBar::onClick, this); 15 | Bind(wxEVT_RIGHT_DOWN, &CommandBar::onClick, this); 16 | } 17 | 18 | void CommandBar::onClick(wxMouseEvent& event) { 19 | event.Skip(false); 20 | } 21 | -------------------------------------------------------------------------------- /src/CommandBar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class CommandBar : public wxRichTextCtrl { 7 | public: 8 | CommandBar(wxWindow *parent); 9 | 10 | 11 | private: 12 | void onClick(wxMouseEvent& event); 13 | }; 14 | -------------------------------------------------------------------------------- /src/Editor.cpp: -------------------------------------------------------------------------------- 1 | #include "Window.h" 2 | #include "Editor.h" 3 | #include "FileHelper.h" 4 | 5 | Editor::Editor(wxWindow *parent, int lexer) : wxStyledTextCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, "editor") { 6 | SetCaretStyle(2); 7 | SetCaretPeriod(0); 8 | SetTabWidth(4); 9 | SetUseHorizontalScrollBar(false); 10 | SetWrapMode(1); 11 | SetMarginType(1, wxSTC_MARGIN_NUMBER); 12 | SetMarginWidth(1, 30); 13 | SetUseTabs(false); 14 | 15 | std::string whitespaceChars = std::string(GetWhitespaceChars().mb_str()) + "\n"; 16 | SetWhitespaceChars(whitespaceChars); 17 | 18 | StyleSetBackground(wxSTC_STYLE_DEFAULT, wxColour(30, 30, 30)); 19 | StyleSetForeground(wxSTC_STYLE_DEFAULT, wxColour(255, 255, 255)); 20 | SetCaretForeground(wxColour(219, 131, 0)); 21 | 22 | StyleClearAll(); 23 | 24 | applyLexer(lexer); 25 | 26 | StyleSetBackground(wxSTC_STYLE_LINENUMBER, wxColour(37, 37, 38)); 27 | StyleSetForeground(wxSTC_STYLE_LINENUMBER, wxColour(240, 240, 41)); 28 | 29 | Bind(wxEVT_CHAR, &Editor::onChar, this); 30 | Bind(wxEVT_KEY_DOWN, &Editor::onKey, this); 31 | Bind(wxEVT_LEFT_UP, &Editor::onClickUp, this); 32 | Bind(wxEVT_LEFT_DOWN, &Editor::onClickDown, this); 33 | Bind(wxEVT_STC_CHANGE, &Editor::onChange, this); 34 | 35 | saved = true; 36 | readOnly = false; 37 | } 38 | 39 | Window *Editor::getWindow(void) { 40 | return (Window *)GetGrandParent(); 41 | } 42 | 43 | void Editor::onChar(wxKeyEvent& event) { 44 | wxChar c = event.GetUnicodeKey(); 45 | Window *w = getWindow(); 46 | if (c != WXK_NONE) { 47 | if (c >= 32) { 48 | if (w->mode == NORMAL_MODE) { 49 | w->commandBar->AppendText(c); 50 | if (c == ':' && w->command->cmd.empty()) { 51 | w->command->prefix = COMMAND_PREFIX; 52 | return; 53 | } 54 | w->command->cmd += c; 55 | 56 | int ind = -1; 57 | if ((ind = w->command->isValidNormal()) >= 0) { 58 | w->executeNormal(ind); 59 | } 60 | } 61 | else if (w->mode == EDIT_MODE) { 62 | AddText(c); 63 | w->updateStatus(); 64 | } 65 | else if (w->mode == VISUAL_MODE) { 66 | w->commandBar->AppendText(c); 67 | w->command->cmd += c; 68 | 69 | int ind = -1; 70 | if ((ind = w->command->isValidVisual()) >= 0) { 71 | w->executeVisual(ind); 72 | } 73 | } 74 | else if (w->mode == LINE_MODE) { 75 | w->commandBar->AppendText(c); 76 | w->command->cmd += c; 77 | 78 | int ind = -1; 79 | if ((ind = w->command->isValidLine()) >= 0) { 80 | w->executeLine(ind); 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | void Editor::onKey(wxKeyEvent& event) { 88 | Window *w = getWindow(); 89 | int key = event.GetKeyCode(); 90 | if (key >= 32 && key <= 127) { 91 | if (!event.ControlDown() || key == 67 || key == 86) { 92 | event.Skip(); 93 | } 94 | } 95 | else if (key == WXK_ESCAPE) { 96 | if (w->mode == EDIT_MODE) { 97 | caretLeft(1); 98 | } 99 | else if (w->mode == VISUAL_MODE || w->mode == LINE_MODE) { 100 | removeSelection(); 101 | if (GetCurrentPos() == lineEndPos()) { 102 | caretLeft(1); 103 | } 104 | } 105 | w->mode = NORMAL_MODE; 106 | w->command->clear(); 107 | w->commandBar->Clear(); 108 | } 109 | else if (key == WXK_RETURN) { 110 | if (w->mode == NORMAL_MODE) { 111 | int ind = -1; 112 | if ((ind = w->command->isValidCommand()) >= 0) { 113 | w->executeCommand(ind); 114 | } 115 | } 116 | else if (w->mode == EDIT_MODE) { 117 | std::string indentSpace = getIndentSpace(currLine()); 118 | NewLine(); 119 | AddText(indentSpace); 120 | } 121 | } 122 | else if (key == WXK_BACK) { 123 | if (w->mode == NORMAL_MODE) { 124 | if (!w->command->cmd.empty()) { 125 | w->command->cmd.pop_back(); 126 | } 127 | else { 128 | w->command->prefix = NORMAL_PREFIX; 129 | } 130 | int l = w->commandBar->GetLineLength(0); 131 | w->commandBar->Remove(l-1, l); 132 | } 133 | else if (w->mode == EDIT_MODE) { 134 | std::string upToCaret = std::string(GetTextRange(lineStartPos(), GetCurrentPos())); 135 | if (upToCaret.empty() || upToCaret.find_first_not_of(' ') != std::string::npos) { 136 | DeleteBack(); 137 | } 138 | else { 139 | DeleteRange(std::max(lineStartPos(), GetCurrentPos() - 4), std::min(linePos(), 4)); 140 | } 141 | } 142 | } 143 | else if (key == WXK_TAB) { 144 | if (w->mode == EDIT_MODE) { 145 | Tab(); 146 | } 147 | } 148 | else if (key == WXK_LEFT) { 149 | if (w->command->isClear()) { 150 | caretLeft(1); 151 | } 152 | } 153 | else if (key == WXK_RIGHT) { 154 | if (w->command->isClear()) { 155 | caretRight(1); 156 | } 157 | } 158 | else if (key == WXK_UP) { 159 | if (w->command->isClear()) { 160 | caretUp(1); 161 | } 162 | } 163 | else if (key == WXK_DOWN) { 164 | if (w->command->isClear()) { 165 | caretDown(1); 166 | } 167 | } 168 | else if (key == WXK_END) { 169 | if (w->command->isClear()) { 170 | LineEnd(); 171 | caretLeft(1); 172 | } 173 | } 174 | else if (key == WXK_HOME) { 175 | if (w->command->isClear()) { 176 | VCHome(); 177 | } 178 | } 179 | else if (key == WXK_PAGEUP) { 180 | if (w->command->isClear()) { 181 | PageUp(); 182 | } 183 | } 184 | else if (key == WXK_PAGEDOWN) { 185 | if (w->command->isClear()) { 186 | PageDown(); 187 | } 188 | } 189 | w->updateStatus(); 190 | } 191 | 192 | void Editor::onClickUp(wxMouseEvent& event) { 193 | Window *w = getWindow(); 194 | 195 | if (GetSelectionStart() != GetSelectionEnd()) { 196 | w->mode = VISUAL_MODE; 197 | } 198 | else { 199 | if (w->mode == VISUAL_MODE || w->mode == LINE_MODE) { 200 | w->mode = NORMAL_MODE; 201 | } 202 | } 203 | getWindow()->updateStatus(); 204 | event.Skip(); 205 | } 206 | 207 | void Editor::onClickDown(wxMouseEvent& event) { 208 | Window *w = getWindow(); 209 | 210 | removeSelection(); 211 | w->command->clear(); 212 | w->commandBar->Clear(); 213 | event.Skip(); 214 | } 215 | 216 | void Editor::onChange(wxStyledTextEvent& event) { 217 | saved = false; 218 | event.Skip(); 219 | } 220 | 221 | void Editor::applyLexer(const int& lexer) { 222 | if (lexer == CPP_LEX) { 223 | SetLexer(wxSTC_LEX_CPP); 224 | 225 | StyleSetForeground(wxSTC_C_COMMENTLINE, wxColor(60, 162, 2)); 226 | StyleSetForeground(wxSTC_C_COMMENT, wxColor(60, 162, 2)); 227 | StyleSetForeground(wxSTC_C_PREPROCESSOR, wxColor(250,128,114)); 228 | StyleSetForeground(wxSTC_C_STRING, wxColor(222, 203, 62)); 229 | StyleSetForeground(wxSTC_C_NUMBER, wxColor(83, 195, 230)); 230 | StyleSetForeground(wxSTC_C_CHARACTER, wxColor(123, 224, 112)); 231 | StyleSetForeground(wxSTC_C_WORD, wxColor(245, 113, 208)); 232 | StyleSetForeground(wxSTC_C_WORD2, wxColor(230, 78, 86)); 233 | SetKeyWords(0, wxT( 234 | "alignas alignof and and_eq asm atomic_cancel atomic_commit atomic_noexcept auto" 235 | " bitand bitor bool char char8_t char16_t char32_t compl string" 236 | " concept consteval constexpr constinit const_cast co_await co_return" 237 | " co_yield decltype default delete do double dynamic_cast enum explicit export" 238 | " extern float friend inline int long mutable namespace new noexcept" 239 | " not not_eq nullptr operator or or_eq reflexpr register" 240 | " reinterpret_cast requires short signed sizeof static static_assert static_cast" 241 | " struct synchronized template this thread_local typedef typeid" 242 | " typename union unsigned using virtual void volatile wchar_t xor xor_eq" 243 | )); 244 | SetKeyWords(1, wxT( 245 | "const class private protected public false true if for goto break case catch" 246 | " continue else return switch throw try while" 247 | )); 248 | SetProperty("styling.within.preprocessor", "1"); 249 | } 250 | else { 251 | SetLexer(wxSTC_LEX_NULL); 252 | } 253 | wxFont *font = new wxFont(10, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString); 254 | for (int i = 0; i < 32; ++i) { 255 | StyleSetFont(i, *font); 256 | } 257 | } 258 | 259 | void Editor::loadFormatted(const std::string& file) { 260 | LoadFile(file); 261 | convertTabs(); 262 | } 263 | 264 | void Editor::convertTabs(void) { 265 | std::string oldText = std::string(GetText().mb_str()); 266 | std::string newText; 267 | for (char c : oldText) { 268 | if (c == '\t') { 269 | newText.append(" "); 270 | } 271 | else { 272 | newText.push_back(c); 273 | } 274 | } 275 | wxString docText(newText); 276 | SetText(docText); 277 | } 278 | 279 | int Editor::linePos(void) const { 280 | return GetCurrentPos() - lineStartPos(); 281 | } 282 | 283 | int Editor::currLine(void) const { 284 | return LineFromPosition(GetCurrentPos()); 285 | } 286 | 287 | int Editor::lineStartPos(void) const { 288 | return PositionFromLine(currLine()); 289 | } 290 | 291 | int Editor::lineEndPos(void) const { 292 | return GetLineEndPosition(currLine()); 293 | } 294 | 295 | std::string Editor::getIndentSpace(const int& line) const { 296 | std::string lineIndent = std::string(GetLine(line).mb_str()); 297 | int ind = 0; 298 | while (ind < lineIndent.size() && lineIndent[ind] == ' ') { 299 | ++ind; 300 | } 301 | return lineIndent.substr(0, ind); 302 | } 303 | 304 | void Editor::caretLeft(const int& num) { 305 | SetSelectionEnd(std::max(lineStartPos(), GetCurrentPos() - num)); 306 | } 307 | 308 | void Editor::caretRight(const int& num) { 309 | if (lineStartPos() < lineEndPos()) { 310 | SetSelectionStart(std::min(lineEndPos() - 1, GetCurrentPos() + num)); 311 | } 312 | } 313 | 314 | void Editor::caretUp(const int& num) { 315 | int pos = linePos(); 316 | GotoLine(std::max(0, currLine() - num)); 317 | caretRight(pos); 318 | } 319 | 320 | void Editor::caretDown(const int& num) { 321 | int pos = linePos(); 322 | GotoLine(std::min(GetLineCount() - 1, currLine() + num)); 323 | caretRight(pos); 324 | } 325 | 326 | void Editor::append(void) { 327 | if (lineEndPos() - 1 == GetCurrentPos()) { 328 | LineEnd(); 329 | } 330 | else { 331 | caretRight(1); 332 | } 333 | } 334 | 335 | void Editor::insertLineBelow(const int& num) { 336 | LineEnd(); 337 | std::string indentSpace = getIndentSpace(currLine()); 338 | for (int i = 0; i < num; ++i) { 339 | NewLine(); 340 | AddText(indentSpace); 341 | } 342 | Window *w = getWindow(); 343 | w->mode = EDIT_MODE; 344 | } 345 | 346 | void Editor::insertLineAbove(const int& num) { 347 | std::string indentSpace = getIndentSpace(currLine()); 348 | for (int i = 0; i < num; ++i) { 349 | Home(); 350 | NewLine(); 351 | caretUp(1); 352 | AddText(indentSpace); 353 | } 354 | 355 | Window *w = getWindow(); 356 | w->mode = EDIT_MODE; 357 | } 358 | 359 | void Editor::wordLeft(const int& num) { 360 | for (int i = 0; i < num; ++i) { 361 | WordLeft(); 362 | } 363 | } 364 | 365 | void Editor::wordRight(const int& num) { 366 | for (int i = 0; i < num; ++i) { 367 | WordRight(); 368 | } 369 | } 370 | 371 | void Editor::charSearchAhead(const char& c, const bool& inc) { 372 | for (int i = GetCurrentPos() + 1; i < lineEndPos(); ++i) { 373 | if (GetCharAt(i) == c) { 374 | if (inc) { 375 | GotoPos(i); 376 | } 377 | else { 378 | GotoPos(i-1); 379 | } 380 | return; 381 | } 382 | } 383 | } 384 | 385 | void Editor::charSearchBehind(const char& c, const bool& inc) { 386 | for (int i = GetCurrentPos() - 1; i >= lineStartPos(); --i) { 387 | if (GetCharAt(i) == c) { 388 | if (inc) { 389 | GotoPos(i); 390 | } 391 | else { 392 | GotoPos(i+1); 393 | } 394 | return; 395 | } 396 | } 397 | } 398 | 399 | void Editor::cutToLineEnd(void) { 400 | SetAnchor(GetCurrentPos()); 401 | SetCurrentPos(lineEndPos() - 1); 402 | cutSelection(); 403 | caretLeft(1); 404 | } 405 | 406 | void Editor::cutLines(const int& num) { 407 | GotoPos(lineStartPos()); 408 | DeleteBack(); 409 | int endLine = std::min(GetLineCount() - 1, currLine() + num - 1); 410 | SetAnchor(GetLineEndPosition(endLine)); 411 | cutSelection(); 412 | caretDown(1); 413 | VCHome(); 414 | } 415 | 416 | void Editor::removeSelection(void) { 417 | SetEmptySelection(GetCurrentPos()); 418 | } 419 | 420 | void Editor::cutSelection(void) { 421 | if (GetAnchor() <= GetCurrentPos()) { 422 | CharRightExtend(); 423 | } 424 | Cut(); 425 | } 426 | 427 | void Editor::copySelection(void) { 428 | if (GetAnchor() <= GetCurrentPos()) { 429 | CharRightExtend(); 430 | } 431 | Copy(); 432 | } 433 | 434 | void Editor::caseChangeSelection(const bool& upper) { 435 | if (GetAnchor() <= GetCurrentPos()) { 436 | CharRightExtend(); 437 | } 438 | if (upper) { 439 | UpperCase(); 440 | } 441 | else { 442 | LowerCase(); 443 | } 444 | if (GetAnchor() <= GetCurrentPos()) { 445 | CharLeftExtend(); 446 | } 447 | } 448 | 449 | void Editor::caretLeftVis(const int& num) { 450 | int pos = std::max(lineStartPos(), GetCurrentPos() - num); 451 | if (GetCurrentPos() != pos && GetCurrentPos() == GetAnchor()) { 452 | SetAnchor(GetAnchor() + 1); 453 | } 454 | SetCurrentPos(pos); 455 | } 456 | 457 | void Editor::caretRightVis(const int& num) { 458 | int pos = std::min(lineEndPos() - 1, GetCurrentPos() + num); 459 | if (pos == GetAnchor() - 1) { 460 | SetAnchor(GetAnchor() - 1); 461 | } 462 | SetCurrentPos(pos); 463 | } 464 | 465 | void Editor::caretUpVis(const int& num) { 466 | int pos = linePos(); 467 | int endLine = std::max(0, currLine() - num); 468 | int endPos = std::min(GetLineEndPosition(endLine) - 1, PositionFromLine(endLine) + pos); 469 | if (PositionFromLine(endLine) == GetLineEndPosition(endLine)) { 470 | ++endPos; 471 | } 472 | if (endPos < GetAnchor() && GetAnchor() <= GetCurrentPos()) { 473 | SetAnchor(GetAnchor() + 1); 474 | } 475 | SetCurrentPos(endPos); 476 | } 477 | 478 | void Editor::caretDownVis(const int& num) { 479 | int pos = linePos(); 480 | int endLine = std::min(GetLineCount() - 1, currLine() + num); 481 | int endPos = std::min(GetLineEndPosition(endLine) - 1, PositionFromLine(endLine) + pos); 482 | if (PositionFromLine(endLine) == GetLineEndPosition(endLine)) { 483 | ++endPos; 484 | } 485 | if (endPos >= GetAnchor() - 1 && GetAnchor() > GetCurrentPos()) { 486 | SetAnchor(GetAnchor() - 1); 487 | } 488 | SetCurrentPos(endPos); 489 | } 490 | 491 | void Editor::jumpStartVis(void) { 492 | if (GetCurrentPos() >= GetAnchor()) { 493 | SetAnchor(GetAnchor() + 1); 494 | } 495 | SetCurrentPos(0); 496 | } 497 | 498 | void Editor::jumpEndVis(void) { 499 | if (GetCurrentPos() < GetAnchor()) { 500 | SetAnchor(GetAnchor() - 1); 501 | } 502 | SetCurrentPos(GetTextLength() - 1); 503 | } 504 | 505 | void Editor::jumpLineVis(const int& line) { 506 | int endPos = PositionFromLine(line); 507 | if (endPos >= GetAnchor() - 1 && GetAnchor() > GetCurrentPos()) { 508 | SetAnchor(GetAnchor() - 1); 509 | } 510 | else if (endPos < GetAnchor() && GetAnchor() <= GetCurrentPos()) { 511 | SetAnchor(GetAnchor() + 1); 512 | } 513 | SetCurrentPos(endPos); 514 | } 515 | 516 | void Editor::wordLeftVis(const int& num) { 517 | for (int i = 0; i < num; ++i) { 518 | int startPos = GetCurrentPos(); 519 | WordLeftExtend(); 520 | if (GetCurrentPos() == startPos) { 521 | return; 522 | } 523 | if (GetCurrentPos() < GetAnchor() && GetAnchor() <= startPos) { 524 | SetAnchor(GetAnchor() + 1); 525 | } 526 | } 527 | } 528 | 529 | void Editor::wordRightVis(const int& num) { 530 | for (int i = 0; i < num; ++i) { 531 | int startPos = GetCurrentPos(); 532 | WordRightExtend(); 533 | if (GetCurrentPos() == startPos) { 534 | return; 535 | } 536 | if (GetCurrentPos() >= GetAnchor() - 1 && GetAnchor() - 1 >= startPos) { 537 | SetAnchor(GetAnchor() - 1); 538 | } 539 | } 540 | } 541 | 542 | void Editor::lineEndVis(void) { 543 | int startPos = GetCurrentPos(); 544 | LineEndExtend(); 545 | if (GetCurrentPos() >= GetAnchor() - 1 && GetAnchor() - 1 >= startPos) { 546 | SetAnchor(GetAnchor() - 1); 547 | } 548 | caretLeftVis(1); 549 | } 550 | 551 | void Editor::lineStartVis(void) { 552 | int startPos = GetCurrentPos(); 553 | HomeExtend(); 554 | if (GetCurrentPos() != startPos && GetCurrentPos() < GetAnchor() && GetAnchor() <= startPos) { 555 | SetAnchor(GetAnchor() + 1); 556 | } 557 | } 558 | 559 | void Editor::lineHomeVis(void) { 560 | int startPos = GetCurrentPos(); 561 | VCHomeExtend(); 562 | if (GetCurrentPos() != startPos && GetCurrentPos() < GetAnchor() && GetAnchor() <= startPos) { 563 | SetAnchor(GetAnchor() + 1); 564 | } 565 | } 566 | 567 | void Editor::charSearchAheadVis(const char& c, const bool& inc) { 568 | int endPos = GetCurrentPos() + 1; 569 | while (endPos < lineEndPos() && GetCharAt(endPos) != c) { 570 | ++endPos; 571 | } 572 | if (endPos >= lineEndPos()) { 573 | return; 574 | } 575 | if (!inc) { 576 | --endPos; 577 | } 578 | if (endPos >= GetAnchor() - 1 && GetAnchor() - 1 >= GetCurrentPos()) { 579 | SetAnchor(GetAnchor() - 1); 580 | } 581 | SetCurrentPos(endPos); 582 | } 583 | 584 | void Editor::charSearchBehindVis(const char& c, const bool& inc) { 585 | int endPos = GetCurrentPos() - 1; 586 | while (endPos >= lineStartPos() && GetCharAt(endPos) != c) { 587 | --endPos; 588 | } 589 | if (endPos < lineStartPos()) { 590 | return; 591 | } 592 | if (!inc) { 593 | ++endPos; 594 | } 595 | if (endPos < GetAnchor() && GetAnchor() <= GetCurrentPos()) { 596 | SetAnchor(GetAnchor() + 1); 597 | } 598 | SetCurrentPos(endPos); 599 | } 600 | 601 | void Editor::caretUpLine(const int& num) { 602 | int endLine = std::max(currLine() - num, 0); 603 | int startLine = LineFromPosition(GetAnchor()); 604 | if (endLine <= startLine) { 605 | SetAnchor(GetLineEndPosition(startLine)); 606 | SetCurrentPos(PositionFromLine(endLine)); 607 | } 608 | else { 609 | SetCurrentPos(GetLineEndPosition(endLine)); 610 | } 611 | } 612 | 613 | void Editor::caretDownLine(const int& num) { 614 | int endLine = std::min(currLine() + num, GetLineCount() - 1); 615 | int startLine = LineFromPosition(GetAnchor()); 616 | if (endLine >= startLine) { 617 | SetAnchor(PositionFromLine(startLine)); 618 | SetCurrentPos(GetLineEndPosition(endLine)); 619 | } 620 | else { 621 | SetCurrentPos(PositionFromLine(endLine)); 622 | } 623 | } 624 | 625 | void Editor::shiftLine(const int& num, const bool& dir) { 626 | int startLine = LineFromPosition(GetSelectionStart()); 627 | int endLine = LineFromPosition(GetSelectionEnd()); 628 | if (dir == 1) { 629 | std::string indent(num * 4, ' '); 630 | for (int i = startLine; i <= endLine; ++i) { 631 | InsertText(PositionFromLine(i), indent); 632 | } 633 | } 634 | else { 635 | for (int i = startLine; i <= endLine; ++i) { 636 | int rem = std::min((int)getIndentSpace(i).size(), num * 4); 637 | DeleteRange(PositionFromLine(i), rem); 638 | } 639 | } 640 | } 641 | 642 | void Editor::jumpStartLine(void) { 643 | int startLine = LineFromPosition(GetAnchor()); 644 | SetAnchor(GetLineEndPosition(startLine)); 645 | SetCurrentPos(0); 646 | } 647 | 648 | void Editor::jumpEndLine(void) { 649 | int startLine = LineFromPosition(GetAnchor()); 650 | SetAnchor(PositionFromLine(startLine)); 651 | SetCurrentPos(GetTextLength() - 1); 652 | } 653 | 654 | void Editor::jumpLineLine(const int& line) { 655 | int startLine = LineFromPosition(GetAnchor()); 656 | if (line >= startLine) { 657 | SetAnchor(PositionFromLine(startLine)); 658 | SetCurrentPos(GetLineEndPosition(line)); 659 | } 660 | else { 661 | SetAnchor(GetLineEndPosition(startLine)); 662 | SetCurrentPos(PositionFromLine(line)); 663 | } 664 | } 665 | -------------------------------------------------------------------------------- /src/Editor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Window; 7 | 8 | class Editor : public wxStyledTextCtrl { 9 | public: 10 | Editor(wxWindow *parent, int lexer); 11 | 12 | Window *getWindow(void); 13 | 14 | void applyLexer(const int& lexer); 15 | void loadFormatted(const std::string& file); 16 | void convertTabs(void); 17 | 18 | int linePos(void) const; 19 | int currLine(void) const; 20 | int lineStartPos(void) const; 21 | int lineEndPos(void) const; 22 | std::string getIndentSpace(const int& line) const; 23 | 24 | void caretLeft(const int& num); 25 | void caretRight(const int& num); 26 | void caretUp(const int& num); 27 | void caretDown(const int& num); 28 | void append(void); 29 | void insertLineBelow(const int& num); 30 | void insertLineAbove(const int& num); 31 | void wordLeft(const int& num); 32 | void wordRight(const int& num); 33 | void charSearchAhead(const char& c, const bool& inc); 34 | void charSearchBehind(const char& c, const bool& inc); 35 | void cutToLineEnd(void); 36 | void cutLines(const int& num); 37 | 38 | void removeSelection(void); 39 | void cutSelection(void); 40 | void copySelection(void); 41 | void caseChangeSelection(const bool& upper); 42 | 43 | void caretLeftVis(const int& num); 44 | void caretRightVis(const int& num); 45 | void caretUpVis(const int& num); 46 | void caretDownVis(const int& num); 47 | void jumpStartVis(void); 48 | void jumpEndVis(void); 49 | void jumpLineVis(const int& line); 50 | void wordLeftVis(const int& num); 51 | void wordRightVis(const int& num); 52 | void lineEndVis(void); 53 | void lineStartVis(void); 54 | void lineHomeVis(void); 55 | void charSearchAheadVis(const char& c, const bool& inc); 56 | void charSearchBehindVis(const char& c, const bool& inc); 57 | 58 | void caretUpLine(const int& num); 59 | void caretDownLine(const int& num); 60 | void shiftLine(const int& num, const bool& dir); 61 | void jumpStartLine(void); 62 | void jumpEndLine(void); 63 | void jumpLineLine(const int& line); 64 | 65 | std::string relPath; 66 | bool saved; 67 | bool readOnly; 68 | 69 | private: 70 | void onChar(wxKeyEvent& event); 71 | void onKey(wxKeyEvent& event); 72 | void onClickUp(wxMouseEvent& event); 73 | void onClickDown(wxMouseEvent& event); 74 | void onChange(wxStyledTextEvent& event); 75 | }; 76 | -------------------------------------------------------------------------------- /src/FileHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "FileHelper.h" 2 | 3 | bool FileHelper::isExistingPath(const std::string& cwd, const std::string& relPath) { 4 | if (relPath.empty()) { 5 | return false; 6 | } 7 | std::string absPath = cwd + relPath; 8 | if (std::filesystem::exists(absPath) && std::filesystem::is_regular_file(absPath)) { 9 | return true; 10 | } 11 | return false; 12 | } 13 | 14 | bool FileHelper::isValidPath(const std::string& cwd, const std::string& relPath) { 15 | // currently only allows relative paths from cwd 16 | if (relPath.empty()) { 17 | return false; 18 | } 19 | std::string absDir = cwd + relPath; 20 | // maybe check for backwards slash on Windows 21 | while (absDir.back() != '/') { 22 | absDir.pop_back(); 23 | } 24 | if (std::filesystem::exists(absDir) && std::filesystem::is_directory(absDir)) { 25 | return true; 26 | } 27 | return false; 28 | } 29 | 30 | bool FileHelper::isReadOnlyFile(const std::string& cwd, const std::string& relPath) { 31 | std::string absPath = cwd + relPath; 32 | if (FileHelper::isExistingPath(cwd, relPath)) { 33 | std::filesystem::perms p = std::filesystem::status(absPath).permissions(); 34 | if ((p & std::filesystem::perms::owner_write) == std::filesystem::perms::none) { 35 | return true; 36 | } 37 | } 38 | return false; 39 | } 40 | 41 | bool FileHelper::isDotFile(const std::string& relPath) { 42 | std::string cp = relPath; 43 | int dir = cp.find_last_of('/'); 44 | if (dir != std::string::npos) { 45 | cp = cp.substr(dir + 1, cp.size() - dir - 1); 46 | } 47 | int dot = cp.find_last_of('.'); 48 | if (dot == std::string::npos) { 49 | return false; 50 | } 51 | if (dot == 0) { 52 | return true; 53 | } 54 | return false; 55 | } 56 | 57 | std::string FileHelper::getExtension(const std::string& relPath) { 58 | int start = relPath.find_last_of('.'); 59 | if (start == std::string::npos) { 60 | return ""; 61 | } 62 | if (FileHelper::isDotFile(relPath)) { 63 | return ""; 64 | } 65 | return relPath.substr(start + 1, relPath.size() - start - 1); 66 | } 67 | 68 | int FileHelper::getLexerFromExtension(const std::string& relPath) { 69 | std::string ext = getExtension(relPath); 70 | if (ext.empty()) { 71 | return NULL_LEX; 72 | } 73 | if (ext == "cpp" || ext == "cc" || ext == "cxx" || ext == "h" || ext == "hpp") { 74 | return CPP_LEX; 75 | } 76 | return NULL_LEX; 77 | } 78 | -------------------------------------------------------------------------------- /src/FileHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define NULL_LEX 0 8 | #define CPP_LEX 1 9 | 10 | class FileHelper { 11 | public: 12 | static bool isExistingPath(const std::string& cwd, const std::string& relPath); 13 | static bool isValidPath(const std::string& cwd, const std::string& relPath); 14 | static bool isReadOnlyFile(const std::string& cwd, const std::string& relPath); 15 | static bool isDotFile(const std::string& relPath); 16 | static std::string getExtension(const std::string& relPath); 17 | static int getLexerFromExtension(const std::string& relPath); 18 | }; 19 | -------------------------------------------------------------------------------- /src/FileTree.cpp: -------------------------------------------------------------------------------- 1 | #include "Frame.h" 2 | #include "Window.h" 3 | #include "FileTree.h" 4 | #include "FileHelper.h" 5 | 6 | FileTree::FileTree(wxWindow *parent) : wxTreeCtrl(parent, wxID_ANY, wxDefaultPosition, wxSize(wxGetDisplaySize().GetWidth()/4, wxGetDisplaySize().GetHeight()/2), wxTR_DEFAULT_STYLE | wxTR_NO_LINES, wxDefaultValidator, wxTreeCtrlNameStr) { 7 | wxFont *font = new wxFont(10, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString); 8 | SetFont(*font); 9 | SetMinClientSize(wxSize(100, 100)); 10 | SetBackgroundColour(wxColour(37, 37, 38)); 11 | SetIndent(20); 12 | 13 | cwd = getFrame()->cwd; 14 | root = AddRoot(cwd); 15 | 16 | int numFiles = loadTree(cwd, root); 17 | SortChildren(root); 18 | ClearFocusedItem(); 19 | SetItemBold(root); 20 | SetItemBackgroundColour(root, getDirectoryColor(numFiles)); 21 | Expand(root); 22 | 23 | Bind(wxEVT_TREE_ITEM_ACTIVATED, &FileTree::onActivate, this); 24 | Bind(wxEVT_KILL_FOCUS, &FileTree::onKillFocus, this); 25 | Bind(wxEVT_LEFT_DOWN, &FileTree::onClick, this); 26 | Bind(wxEVT_RIGHT_DOWN, &FileTree::onClick, this); 27 | } 28 | 29 | Frame *FileTree::getFrame(void) const { 30 | return (Frame *)GetParent(); 31 | } 32 | 33 | void FileTree::onActivate(wxTreeEvent& event) { 34 | std::string relPath = getRelPathFromItem(event.GetItem()); 35 | std::string absPath = cwd + relPath; 36 | if (std::filesystem::is_regular_file(absPath)) { 37 | Window *w = getFrame()->window; 38 | int lexer = FileHelper::getLexerFromExtension(relPath); 39 | w->panel->AddPage(new Editor(w->panel, lexer), relPath, true); 40 | w->getCurrentEditor()->relPath = relPath; 41 | w->getCurrentEditor()->loadFormatted(absPath); 42 | w->getCurrentEditor()->saved = true; 43 | } 44 | ClearFocusedItem(); 45 | event.Skip(); 46 | } 47 | 48 | void FileTree::onKillFocus(wxFocusEvent& event) { 49 | ClearFocusedItem(); 50 | event.Skip(); 51 | } 52 | 53 | void FileTree::onClick(wxMouseEvent& event) { 54 | ClearFocusedItem(); 55 | event.Skip(); 56 | } 57 | 58 | int FileTree::loadTree(const std::string& cwd, wxTreeItemId parent) { 59 | int numFiles = 0; 60 | for (const auto & file : std::filesystem::directory_iterator(cwd)) { 61 | wxTreeItemId item = AppendItem(parent, file.path().filename().string()); 62 | std::string ext = file.path().extension(); 63 | if (!ext.empty() && ext[0] == '.') { 64 | ext.erase(0, 1); 65 | } 66 | if (std::filesystem::is_directory(file.path())) { 67 | int num = loadTree(file.path().string(), item); 68 | numFiles += num; 69 | SortChildren(item); 70 | SetItemBackgroundColour(item, wxColour(getDirectoryColor(num))); 71 | SetItemBold(item); 72 | } 73 | else { 74 | SetItemTextColour(item, getColorFromExtension(ext)); 75 | ++numFiles; 76 | } 77 | } 78 | return numFiles; 79 | } 80 | 81 | // every time a file is added or deleted, reload tree 82 | void FileTree::reloadTree(void) { 83 | DeleteChildren(root); 84 | int numFiles = loadTree(cwd, root); 85 | SortChildren(root); 86 | SetItemBackgroundColour(root, getDirectoryColor(numFiles)); 87 | Expand(root); 88 | } 89 | 90 | std::string FileTree::getRelPathFromItem(const wxTreeItemId& item) const { 91 | std::string absPath; 92 | wxTreeItemId id = item; 93 | while (id != root) { 94 | absPath.insert(0, GetItemText(id).ToStdString() + "/"); 95 | id = GetItemParent(id); 96 | } 97 | if (!absPath.empty()) { 98 | absPath.pop_back(); 99 | } 100 | return absPath; 101 | } 102 | 103 | wxColour FileTree::getColorFromExtension(const std::string& ext) const { 104 | std::hash h; 105 | unsigned int value = h(ext); 106 | unsigned int r = 255 - (value >> 16 & 0xff); 107 | unsigned int g = 255 - ((value >> 8) & 0xff); 108 | unsigned int b = 255 - (value & 0xff); 109 | r = (255 - r) / 10 * 3 + r; 110 | g = (255 - g) / 10 * 3 + g; 111 | b = (255 - b) / 10 * 3 + b; 112 | return wxColour(r, g, b); 113 | } 114 | 115 | wxColour FileTree::getDirectoryColor(const int& num) const { 116 | if (num == 0) { 117 | return wxColour(225, 191, 227); 118 | } 119 | if (num <= 10) { 120 | return wxColour(207, 109, 109); 121 | } 122 | if (num <= 30) { 123 | return wxColour(173, 173, 76); 124 | } 125 | if (num <= 50) { 126 | return wxColour(76, 173, 76); 127 | } 128 | if (num <= 70) { 129 | return wxColour(76, 173, 173); 130 | } 131 | return wxColour(102, 102, 232); 132 | } 133 | 134 | int FileTree::OnCompareItems(const wxTreeItemId& item1, const wxTreeItemId& item2) { 135 | std::string text1 = std::string(GetItemText(item1).mb_str()); 136 | std::string text2 = std::string(GetItemText(item2).mb_str()); 137 | std::string ext1 = FileHelper::getExtension(text1); 138 | std::string ext2 = FileHelper::getExtension(text2); 139 | 140 | if (ItemHasChildren(item1) && ItemHasChildren(item2)) { 141 | return text1.compare(text2); 142 | } 143 | else if (ItemHasChildren(item1) && !ItemHasChildren(item2)) { 144 | return -1; 145 | } 146 | else if (!ItemHasChildren(item1) && ItemHasChildren(item2)) { 147 | return 1; 148 | } 149 | 150 | if (FileHelper::isDotFile(text1) && FileHelper::isDotFile(text2)) { 151 | return text1.compare(text2); 152 | } 153 | else if (FileHelper::isDotFile(text1) && !FileHelper::isDotFile(text2)) { 154 | return -1; 155 | } 156 | else if (!FileHelper::isDotFile(text1) && FileHelper::isDotFile(text2)) { 157 | return 1; 158 | } 159 | 160 | if (ext1.empty() && ext2.empty()) { 161 | return text1.compare(text2); 162 | } 163 | else if (ext1.empty() && !ext2.empty()) { 164 | return -1; 165 | } 166 | else if (!ext1.empty() && ext2.empty()) { 167 | return 1; 168 | } 169 | 170 | if (ext1 == ext2) { 171 | return text1.compare(text2); 172 | } 173 | return ext1.compare(ext2); 174 | } 175 | -------------------------------------------------------------------------------- /src/FileTree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class Frame; 9 | class Window; 10 | 11 | class FileTree : public wxTreeCtrl { 12 | public: 13 | FileTree(wxWindow *parent); 14 | 15 | Frame *getFrame(void) const; 16 | void reloadTree(void); 17 | 18 | private: 19 | void onActivate(wxTreeEvent& event); 20 | void onKillFocus(wxFocusEvent& event); 21 | void onClick(wxMouseEvent& event); 22 | 23 | int loadTree(const std::string& cwd, wxTreeItemId parent); 24 | std::string getRelPathFromItem(const wxTreeItemId& item) const; 25 | wxColour getColorFromExtension(const std::string& ext) const; 26 | wxColour getDirectoryColor(const int& num) const; 27 | 28 | int OnCompareItems(const wxTreeItemId& item1, const wxTreeItemId& item2); 29 | 30 | std::string cwd; 31 | wxTreeItemId root; 32 | }; 33 | -------------------------------------------------------------------------------- /src/Frame.cpp: -------------------------------------------------------------------------------- 1 | #include "Frame.h" 2 | 3 | Frame::Frame(const wxSize& size) : wxFrame(NULL, wxID_ANY, "Gadget", wxDefaultPosition, size) { 4 | SetBackgroundColour(wxColour(54, 54, 54)); 5 | sizer = new wxBoxSizer(wxHORIZONTAL); 6 | 7 | cwd = std::filesystem::current_path().string() + "/"; 8 | 9 | window = new Window(this); 10 | tree = new FileTree(this); 11 | 12 | sizer->Add(tree, 1, wxEXPAND | wxALL, 0); 13 | sizer->Add(window, 3, wxEXPAND | wxALL, 0); 14 | 15 | SetSizerAndFit(sizer); 16 | SetSize(size); 17 | } 18 | -------------------------------------------------------------------------------- /src/Frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Window.h" 7 | 8 | class Frame : public wxFrame { 9 | public: 10 | Frame(const wxSize& size); 11 | 12 | Window *window; 13 | FileTree *tree; 14 | 15 | std::string cwd; 16 | 17 | private: 18 | wxBoxSizer *sizer; 19 | }; 20 | -------------------------------------------------------------------------------- /src/Gadget.cpp: -------------------------------------------------------------------------------- 1 | #include "Gadget.h" 2 | #include "Frame.h" 3 | #include "res/gadget.xpm" 4 | 5 | bool Gadget::OnInit() { 6 | wxSize s = wxGetDisplaySize(); 7 | s.Scale(0.5, 0.5); 8 | 9 | Frame *f = new Frame(s); 10 | f->Show(true); 11 | 12 | wxIcon icon(gadget_icon); 13 | f->SetIcon(icon); 14 | 15 | return true; 16 | } 17 | 18 | wxIMPLEMENT_APP(Gadget); 19 | -------------------------------------------------------------------------------- /src/Gadget.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Gadget : public wxApp { 6 | public: 7 | // App startup 8 | virtual bool OnInit(); 9 | }; 10 | -------------------------------------------------------------------------------- /src/HelpFile.cpp: -------------------------------------------------------------------------------- 1 | #include "Window.h" 2 | #include "FileHelper.h" 3 | 4 | HelpFile::HelpFile(wxWindow *parent) : Editor(parent, NULL_LEX) { 5 | SetLexer(wxSTC_LEX_LUA); 6 | 7 | StyleSetForeground(wxSTC_LUA_WORD4, wxColor(209, 73, 94)); 8 | StyleSetForeground(wxSTC_LUA_WORD5, wxColor(219, 131, 0)); 9 | StyleSetForeground(wxSTC_LUA_WORD6, wxColor(120, 161, 109)); 10 | StyleSetForeground(wxSTC_LUA_WORD7, wxColor(147, 196, 82)); 11 | StyleSetForeground(wxSTC_LUA_WORD8, wxColor(222, 149, 222)); 12 | 13 | SetKeyWords(3, wxT("Gadget")); 14 | SetKeyWords(4, wxT("NORMAL")); 15 | SetKeyWords(5, wxT("EDIT")); 16 | SetKeyWords(6, wxT("VISUAL")); 17 | SetKeyWords(7, wxT("LINE")); 18 | 19 | AddText( 20 | "Welcome to the Gadget help file!\n" 21 | "This file is read-only\n\n" 22 | 23 | "Gadget is a GUI text editor inspired by GVim, the interface variant of the popular text editor, Vim.\n\n" 24 | 25 | "Gadget features four modes: NORMAL EDIT VISUAL and LINE\n\n" 26 | 27 | "~~~~~~~~~~~~\nNORMAL MODE:\n~~~~~~~~~~~~\n\n" 28 | 29 | "hjkl\n\t\t\t\th - left, j - down, k - up, l - right; can be prepended with [num]\n" 30 | "w\n\t\t\t\tjump forward to the start of the next word; can be prepended with [num]\n" 31 | "b\n\t\t\t\tjump backward to the start of the previous word; can be prepended with [num]\n" 32 | "gg\n\t\t\t\tjump to the beginning of the file\n" 33 | "G\n\t\t\t\tjump to the end of the file\n" 34 | "[num]G\n\t\t\t\tjump to the beginning of the line indicated by [num]\n" 35 | "_\n\t\t\t\tjump to the first non-whitespace character of the current line\n" 36 | "0\n\t\t\t\tjump to the beginning of the current line\n" 37 | "$\n\t\t\t\tjump to the end of the current line\n" 38 | "x\n\t\t\t\tcuts the character the caret is currently on\n" 39 | "o\n\t\t\t\tappend an empty line below the current line; can be prepended with [num]\n" 40 | "O\n\t\t\t\tappend an empty line above the current line; can be prepended with [num]\n" 41 | "dd\n\t\t\t\tcut a line; can be prepended with [num]\n" 42 | "D\n\t\t\t\tcut to the end of the current line\n" 43 | "f [char]\n\t\t\t\tjump to the next instance of [char] on the current line\n" 44 | "F [char]\n\t\t\t\tjump to the previous instance of [char] on the current line\n" 45 | "t [char]\n\t\t\t\tjump just before the next instance of [char] on the current line\n" 46 | "T [char]\n\t\t\t\tjump just after the previous instance of [char] on the current line\n" 47 | "p\n\t\t\t\tpaste the contents of the clipboard after the caret\n" 48 | "P\n\t\t\t\tpaste the contents of the clipboard before the caret\n" 49 | "[num]gt\n\t\t\t\tswitch to the tab indicated by [num]\n" 50 | "i\n\t\t\t\tswitch to EDIT mode\n" 51 | "I\n\t\t\t\tswitch to EDIT mode and jump to the first non-whitespace character of the current line\n" 52 | "a\n\t\t\t\tswitch to EDIT mode and move caret right once\n" 53 | "A\n\t\t\t\tswitch to EDIT mode and jump to the end of the current line\n" 54 | "v\n\t\t\t\tswitch to VISUAL mode\n" 55 | "V\n\t\t\t\tswitch to LINE mode\n" 56 | ":q\n\t\t\t\tquit and close the buffer\n" 57 | ":w\n\t\t\t\tsave the buffer to the file it points to; can be postpended by [string] to specify a file\n" 58 | ":wq\n\t\t\t\tsave and close the buffer\n" 59 | ":e [string]\n\t\t\t\tedit a new file indicated by [string] in the current buffer\n" 60 | ":tabedit\n\t\t\t\topen a new buffer; can be postpended by [string] to specify a file\n" 61 | ":split\n\t\t\t\tsplit the window horizontally\n" 62 | ":vsplit\n\t\t\t\tsplit the window vertically\n" 63 | ":help\n\t\t\t\topen the help file\n\n" 64 | 65 | "~~~~~~~~~~~~\nVISUAL MODE:\n~~~~~~~~~~~~\n\n" 66 | 67 | "hjkl\n\t\t\t\textend the selection left, down, up, or right; can be prepended with [num]\n" 68 | "w\n\t\t\t\textend the selection to the start of the next word; can be prepended with [num]\n" 69 | "b\n\t\t\t\textend the selection to the start of the previous word; can be prepended with [num]\n" 70 | "gg\n\t\t\t\textend the selection to the beginning of the file\n" 71 | "G\n\t\t\t\textend the selection to the end of the file\n" 72 | "[num]G\n\t\t\t\textend the selection to the beginning of the line indicated by [num]\n" 73 | "_\n\t\t\t\textend the selection to the first non-whitespace character of the current line\n" 74 | "0\n\t\t\t\textend the selection to the beginning of the current line\n" 75 | "$\n\t\t\t\textend the selection to the end of the current line\n" 76 | "x\n\t\t\t\tcuts the selection\n" 77 | "d\n\t\t\t\tsame as x\n" 78 | "f [char]\n\t\t\t\textend the selection to the next instance of [char] on the current line\n" 79 | "F [char]\n\t\t\t\textend the selection to the previous instance of [char] on the current line\n" 80 | "t [char]\n\t\t\t\textend the selection just before the next instance of [char] on the current line\n" 81 | "T [char]\n\t\t\t\textend the selection just after the previous instance of [char] on the current line\n" 82 | "u\n\t\t\t\tswitch selection to lowercase\n" 83 | "U\n\t\t\t\tswitch selection to uppercase\n" 84 | "y\n\t\t\t\tcopy the selection to the clipboard\n\n" 85 | 86 | "~~~~~~~~~~\nLINE MODE:\n~~~~~~~~~~\n\n" 87 | 88 | "jk\n\t\t\t\textend the line selection down or up; can be prepended with [num]\n" 89 | "gg\n\t\t\t\textend the line selection to the beginning of the file\n" 90 | "G\n\t\t\t\textend the line selection to the end of the file\n" 91 | "[num]G\n\t\t\t\textend the line selection to the beginning of the line indicated by [num]\n" 92 | "x\n\t\t\t\tcuts the line selection\n" 93 | "d\n\t\t\t\tsame as x\n" 94 | "u\n\t\t\t\tswitch line selection to lowercase\n" 95 | "U\n\t\t\t\tswitch line selection to uppercase\n" 96 | "y\n\t\t\t\tcopy the line selection to the clipboard\n" 97 | "<\n\t\t\t\tshift the lines selected to the left; can be prepended with [num]\n" 98 | ">\n\t\t\t\tshift the lines selected to the right; can be prepended with [num]\n\n" 99 | ); 100 | 101 | SetEditable(false); 102 | SetReadOnly(true); 103 | } 104 | -------------------------------------------------------------------------------- /src/HelpFile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Window; 6 | 7 | class HelpFile : public Editor { 8 | public: 9 | HelpFile(wxWindow *parent); 10 | }; 11 | -------------------------------------------------------------------------------- /src/Panel.cpp: -------------------------------------------------------------------------------- 1 | #include "Frame.h" 2 | #include "Window.h" 3 | #include "Panel.h" 4 | #include "FileHelper.h" 5 | 6 | Panel::Panel(wxWindow *parent) : wxAuiNotebook(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_NB_DEFAULT_STYLE) { 7 | SetBackgroundColour(wxColour(54, 54, 54)); 8 | tabArt = new wxAuiSimpleTabArt(); 9 | tabArt->SetColour(wxColour(77, 77, 77)); 10 | tabArt->SetActiveColour(wxColour(227, 73, 73)); 11 | 12 | SetArtProvider(tabArt); 13 | 14 | AddPage(new Editor(this, NULL_LEX), "[NO FILE]"); 15 | AddPage(new Editor(this, NULL_LEX), "[NO FILE]"); 16 | AddPage(new Editor(this, NULL_LEX), "[NO FILE]"); 17 | // indicate it's a new file 18 | 19 | Bind(wxEVT_AUINOTEBOOK_PAGE_CHANGED, &Panel::onTabChange, this); 20 | Bind(wxEVT_AUINOTEBOOK_PAGE_CLOSED, &Panel::onTabRemove, this); 21 | } 22 | 23 | Window *Panel::getWindow(void) { 24 | return (Window *)GetParent(); 25 | } 26 | 27 | void Panel::onTabChange(wxAuiNotebookEvent& event) { 28 | Window *w = getWindow(); 29 | w->currEditor = event.GetSelection(); 30 | 31 | w->command->clear(); 32 | w->commandBar->Clear(); 33 | 34 | // call window update status bar 35 | w->updateStatus(); 36 | } 37 | 38 | void Panel::onTabRemove(wxAuiNotebookEvent& event) { 39 | Window *w = getWindow(); 40 | 41 | w->command->clear(); 42 | w->commandBar->Clear(); 43 | 44 | if (GetPageCount() == 0) { 45 | w->getFrame()->Destroy(); 46 | } 47 | } 48 | 49 | void Panel::deleteCurr(void) { 50 | DeletePage(GetSelection()); 51 | if (GetPageCount() == 0) { 52 | getWindow()->getFrame()->Destroy(); 53 | } 54 | } 55 | 56 | void Panel::setTab(const int& ind) { 57 | if (ind <= 0 || ind > GetPageCount()) { 58 | return; 59 | } 60 | SetSelection(ind - 1); 61 | } 62 | -------------------------------------------------------------------------------- /src/Panel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Frame; 7 | class Window; 8 | 9 | #include "Editor.h" 10 | 11 | class Panel : public wxAuiNotebook { 12 | public: 13 | Panel(wxWindow *parent); 14 | 15 | Window *getWindow(void); 16 | 17 | void deleteCurr(void); 18 | void setTab(const int& ind); 19 | 20 | private: 21 | void onTabChange(wxAuiNotebookEvent& event); 22 | void onTabRemove(wxAuiNotebookEvent& event); 23 | 24 | wxAuiSimpleTabArt *tabArt; 25 | }; 26 | -------------------------------------------------------------------------------- /src/StatusBar.cpp: -------------------------------------------------------------------------------- 1 | #include "StatusBar.h" 2 | 3 | StatusBar::StatusBar(wxWindow *parent) : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxSize(wxGetDisplaySize().GetWidth()/2, 30)) { 4 | wxFont *font = new wxFont(10, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString); 5 | SetFont(*font); 6 | 7 | sizer = new wxBoxSizer(wxHORIZONTAL); 8 | 9 | modeDisplay = new StatusSection(this, wxSize(90, 30), wxSize(90, 30), wxColour(219, 131, 0), wxColour(0, 0, 0)); 10 | modeDisplay->setText(" ~NORMAL~"); 11 | 12 | pathDisplay = new StatusSection(this, wxSize(90, 30), parent->GetMaxClientSize(), wxColour(214, 141, 141), wxColour(227, 11, 11)); 13 | pathDisplay->setText("[NO FILE]"); 14 | 15 | positionDisplay = new StatusSection(this, wxSize(90, 30), wxSize(150, 30), wxColour(171, 141, 214), wxColour(0, 12, 4)); 16 | positionDisplay->setText("1,1 | 100%"); 17 | 18 | sizeDisplay = new StatusSection(this, wxSize(150, 30), wxSize(210, 30), wxColour(136, 178, 194), wxColour(13, 77, 122)); 19 | sizeDisplay->setText("0 bytes | 1 lines"); 20 | 21 | sizer->Add(modeDisplay, 1, wxEXPAND | wxALL, 0); 22 | sizer->Add(pathDisplay, 2, wxEXPAND | wxALL, 0); 23 | sizer->Add(positionDisplay, 1, wxEXPAND | wxALL, 0); 24 | sizer->Add(sizeDisplay, 2, wxEXPAND | wxALL, 0); 25 | 26 | SetSizer(sizer); 27 | } 28 | -------------------------------------------------------------------------------- /src/StatusBar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "StatusSection.h" 6 | 7 | class StatusBar : public wxWindow { 8 | public: 9 | StatusBar(wxWindow *parent); 10 | 11 | StatusSection *modeDisplay; 12 | StatusSection *pathDisplay; 13 | StatusSection *positionDisplay; 14 | StatusSection *sizeDisplay; 15 | 16 | private: 17 | wxBoxSizer *sizer; 18 | }; 19 | -------------------------------------------------------------------------------- /src/StatusSection.cpp: -------------------------------------------------------------------------------- 1 | #include "StatusSection.h" 2 | 3 | StatusSection::StatusSection(wxWindow *parent, wxSize minSize, wxSize maxSize, wxColour background, wxColour foreground) : wxRichTextCtrl(parent) { 4 | SetEditable(false); 5 | EnableVerticalScrollbar(false); 6 | 7 | SetMinClientSize(minSize); 8 | SetMaxClientSize(maxSize); 9 | 10 | setForeground(foreground); 11 | setBackground(background); 12 | 13 | Bind(wxEVT_LEFT_DOWN, &StatusSection::onClick, this); 14 | Bind(wxEVT_RIGHT_DOWN, &StatusSection::onClick, this); 15 | } 16 | 17 | void StatusSection::onClick(wxMouseEvent& event) { 18 | event.Skip(false); 19 | } 20 | 21 | void StatusSection::setText(const std::string& text) { 22 | Clear(); 23 | AppendText(text); 24 | } 25 | 26 | void StatusSection::setForeground(const wxColour& colour) { 27 | wxRichTextAttr attr = GetBasicStyle(); 28 | attr.SetTextColour(colour); 29 | SetBasicStyle(attr); 30 | } 31 | 32 | void StatusSection::setBackground(const wxColour& colour) { 33 | SetBackgroundColour(colour); 34 | } 35 | 36 | wxColour StatusSection::getForeground(void) const { 37 | return GetBasicStyle().GetTextColour(); 38 | } 39 | 40 | wxColour StatusSection::getBackground(void) const { 41 | return GetBackgroundColour(); 42 | } 43 | -------------------------------------------------------------------------------- /src/StatusSection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class StatusSection : public wxRichTextCtrl { 7 | public: 8 | StatusSection(wxWindow *parent, wxSize minSize, wxSize maxSize, wxColour background, wxColour foreground); 9 | 10 | void setText(const std::string& text); 11 | void setForeground(const wxColour& colour); 12 | void setBackground(const wxColour& colour); 13 | wxColour getForeground(void) const; 14 | wxColour getBackground(void) const; 15 | 16 | private: 17 | void onClick(wxMouseEvent& event); 18 | }; 19 | -------------------------------------------------------------------------------- /src/Window.cpp: -------------------------------------------------------------------------------- 1 | #include "Window.h" 2 | #include "Frame.h" 3 | #include "FileHelper.h" 4 | 5 | Window::Window(wxWindow *parent) : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize) { 6 | SetMinClientSize(wxSize(500, 100)); 7 | 8 | sizer = new wxBoxSizer(wxVERTICAL); 9 | 10 | panel = new Panel(this); 11 | statusBar = new StatusBar(this); 12 | commandBar = new CommandBar(this); 13 | 14 | sizer->Add(panel, 1, wxEXPAND | wxALL, 0); 15 | sizer->Add(statusBar, 0, wxEXPAND | wxALL, -3); 16 | sizer->Add(commandBar, 0, wxEXPAND | wxALL, 0); 17 | 18 | SetSizer(sizer); 19 | 20 | command = new Command(); 21 | 22 | mode = NORMAL_MODE; 23 | currEditor = 0; 24 | lastCopiedMode = EDIT_MODE; 25 | } 26 | 27 | Frame *Window::getFrame(void) const { 28 | return (Frame *)GetParent(); 29 | } 30 | 31 | Editor *Window::getCurrentEditor(void) const { 32 | return (Editor *)panel->GetPage(currEditor); 33 | } 34 | 35 | void Window::executeNormal(const int& cmdInd) { 36 | switch(cmdInd) { 37 | case 0: 38 | doInsertion(); 39 | break; 40 | case 1: 41 | doBasicMovement(); 42 | break; 43 | case 2: 44 | doNewLine(); 45 | break; 46 | case 3: 47 | doIntraLineJump(); 48 | break; 49 | case 4: 50 | doInterLineJump(); 51 | break; 52 | case 5: 53 | doTabChange(); 54 | break; 55 | case 6: 56 | doWordJump(); 57 | break; 58 | case 7: 59 | doCharSearch(); 60 | break; 61 | case 8: 62 | doVisualMode(); 63 | break; 64 | case 9: 65 | doLineMode(); 66 | break; 67 | case 10: 68 | doPaste(); 69 | break; 70 | case 11: 71 | doVisOrLineOrNormalDelete(); 72 | break; 73 | case 12: 74 | doLineCut(); 75 | break; 76 | } 77 | command->clear(); 78 | commandBar->Clear(); 79 | updateStatus(); 80 | } 81 | 82 | void Window::executeCommand(const int& cmdInd) { 83 | switch(cmdInd) { 84 | case 0: 85 | doQuitFile(); 86 | break; 87 | case 1: 88 | doSaveFile(); 89 | break; 90 | case 2: 91 | doOpenFile(); 92 | break; 93 | case 3: 94 | doNewTab(); 95 | break; 96 | case 4: 97 | doSplitTab(); 98 | break; 99 | case 5: 100 | doOpenHelpFile(); 101 | break; 102 | } 103 | command->clear(); 104 | commandBar->Clear(); 105 | updateStatus(); 106 | } 107 | 108 | void Window::executeVisual(const int& cmdInd) { 109 | switch(cmdInd) { 110 | case 0: 111 | doBasicVisMovement(); 112 | break; 113 | case 1: 114 | doVisOrLineOrNormalDelete(); 115 | break; 116 | case 2: 117 | doVisOrLineCaseChange(); 118 | break; 119 | case 3: 120 | doVisInterLineJump(); 121 | break; 122 | case 4: 123 | doVisOrLineCopy(); 124 | break; 125 | case 5: 126 | doVisWordJump(); 127 | break; 128 | case 6: 129 | doVisIntraLineJump(); 130 | break; 131 | case 7: 132 | doVisCharSearch(); 133 | break; 134 | } 135 | command->clear(); 136 | commandBar->Clear(); 137 | updateStatus(); 138 | } 139 | 140 | void Window::executeLine(const int& cmdInd) { 141 | switch(cmdInd) { 142 | case 0: 143 | doBasicLineMovement(); 144 | break; 145 | case 1: 146 | doVisOrLineOrNormalDelete(); 147 | break; 148 | case 2: 149 | doVisOrLineCaseChange(); 150 | break; 151 | case 3: 152 | doLineShift(); 153 | break; 154 | case 4: 155 | doVisOrLineCopy(); 156 | break; 157 | case 5: 158 | doLineInterLineJump(); 159 | break; 160 | } 161 | command->clear(); 162 | commandBar->Clear(); 163 | updateStatus(); 164 | } 165 | 166 | void Window::doInsertion(void) { 167 | Editor *e = getCurrentEditor(); 168 | 169 | mode = EDIT_MODE; 170 | 171 | if (command->cmd == "a") { 172 | e->append(); 173 | } 174 | else if (command->cmd == "A") { 175 | e->LineEnd(); 176 | } 177 | else if (command->cmd == "I") { 178 | e->VCHome(); 179 | } 180 | } 181 | 182 | void Window::doBasicMovement(void) { 183 | std::pair parsedCmd = command->parseNormal(); 184 | Editor *e = getCurrentEditor(); 185 | 186 | if (parsedCmd.second == "h") { 187 | e->caretLeft(parsedCmd.first); 188 | } 189 | else if (parsedCmd.second == "j") { 190 | e->caretDown(parsedCmd.first); 191 | } 192 | else if (parsedCmd.second == "k") { 193 | e->caretUp(parsedCmd.first); 194 | } 195 | else if (parsedCmd.second == "l") { 196 | e->caretRight(parsedCmd.first); 197 | } 198 | } 199 | 200 | void Window::doNewLine(void) { 201 | std::pair parsedCmd = command->parseNormal(); 202 | Editor *e = getCurrentEditor(); 203 | 204 | if (parsedCmd.second == "o") { 205 | e->insertLineBelow(parsedCmd.first); 206 | } 207 | else if (parsedCmd.second == "O") { 208 | e->insertLineAbove(parsedCmd.first); 209 | } 210 | } 211 | 212 | void Window::doIntraLineJump(void) { 213 | Editor *e = getCurrentEditor(); 214 | 215 | if (command->cmd == "_") { 216 | e->VCHome(); 217 | } 218 | else if (command->cmd == "$") { 219 | e->LineEnd(); 220 | e->caretLeft(1); 221 | } 222 | else if (command->cmd == "0") { 223 | e->Home(); 224 | } 225 | } 226 | 227 | void Window::doInterLineJump(void) { 228 | Editor *e = getCurrentEditor(); 229 | 230 | if (command->cmd == "gg") { 231 | e->GotoLine(0); 232 | } 233 | else if (command->cmd == "G") { 234 | e->GotoLine(e->GetLineCount() - 1); 235 | } 236 | else { 237 | std::pair parsedCmd = command->parseNormal(); 238 | e->GotoLine(parsedCmd.first - 1); 239 | } 240 | } 241 | 242 | void Window::doTabChange(void) { 243 | std::pair parsedCmd = command->parseNormal(); 244 | if (parsedCmd.first != panel->GetSelection() + 1) { 245 | panel->setTab(parsedCmd.first); 246 | } 247 | } 248 | 249 | void Window::doWordJump(void) { 250 | std::pair parsedCmd = command->parseNormal(); 251 | Editor *e = getCurrentEditor(); 252 | 253 | if (parsedCmd.second == "w") { 254 | e->wordRight(parsedCmd.first); 255 | } 256 | else { 257 | e->wordLeft(parsedCmd.first); 258 | } 259 | } 260 | 261 | void Window::doCharSearch(void) { 262 | Editor *e = getCurrentEditor(); 263 | 264 | if (command->cmd[0] == 'f') { 265 | e->charSearchAhead(command->cmd[1], 1); 266 | } 267 | else if (command->cmd[0] == 'F') { 268 | e->charSearchBehind(command->cmd[1], 1); 269 | } 270 | else if (command->cmd[0] == 't') { 271 | e->charSearchAhead(command->cmd[1], 0); 272 | } 273 | else if (command->cmd[0] == 'T') { 274 | e->charSearchBehind(command->cmd[1], 0); 275 | } 276 | } 277 | 278 | void Window::doVisualMode(void) { 279 | mode = VISUAL_MODE; 280 | } 281 | 282 | void Window::doLineMode(void) { 283 | Editor *e = getCurrentEditor(); 284 | 285 | mode = LINE_MODE; 286 | 287 | e->SetAnchor(e->lineStartPos()); 288 | e->SetCurrentPos(e->lineEndPos()); 289 | } 290 | 291 | void Window::doPaste(void) { 292 | Editor *e = getCurrentEditor(); 293 | 294 | if (lastCopiedMode == EDIT_MODE) { 295 | return; 296 | } 297 | if (lastCopiedMode == VISUAL_MODE || lastCopiedMode == NORMAL_MODE) { 298 | if (command->cmd == "p") { 299 | e->CharRight(); 300 | } 301 | e->Paste(); 302 | } 303 | else if (lastCopiedMode == LINE_MODE) { 304 | if (command->cmd == "P") { 305 | e->Home(); 306 | e->NewLine(); 307 | e->caretUp(1); 308 | e->Paste(); 309 | } 310 | else if (command->cmd == "p") { 311 | e->LineEnd(); 312 | e->NewLine(); 313 | e->Paste(); 314 | } 315 | std::string line = std::string(e->GetCurLine().mb_str()); 316 | if (line.empty() || line == "\n") { 317 | e->DeleteBack(); 318 | } 319 | e->VCHome(); 320 | } 321 | } 322 | 323 | void Window::doLineCut(void) { 324 | Editor *e = getCurrentEditor(); 325 | 326 | if (command->cmd == "D") { 327 | e->cutToLineEnd(); 328 | lastCopiedMode = NORMAL_MODE; 329 | } 330 | else { 331 | std::pair parsedCmd = command->parseNormal(); 332 | 333 | e->cutLines(parsedCmd.first); 334 | lastCopiedMode = LINE_MODE; 335 | } 336 | mode = NORMAL_MODE; 337 | } 338 | 339 | void Window::doQuitFile(void) { 340 | // later: check if curr editor saved before exiting 341 | panel->deleteCurr(); 342 | } 343 | 344 | void Window::doSaveFile(void) { 345 | std::vector parsedCmd = command->parseCommand(); 346 | Editor *e = getCurrentEditor(); 347 | 348 | if (parsedCmd.size() == 1) { 349 | if (e->relPath == "") { 350 | // later this should display an error: editor has no file 351 | return; 352 | } 353 | } 354 | else { 355 | if (FileHelper::isValidPath(getFrame()->cwd, parsedCmd[1])) { 356 | e->relPath = parsedCmd[1]; 357 | panel->SetPageText(currEditor, parsedCmd[1]); 358 | } 359 | else { 360 | // also display error 361 | return; 362 | } 363 | } 364 | if (FileHelper::isValidPath(getFrame()->cwd, e->relPath)) { 365 | if (!FileHelper::isExistingPath(getFrame()->cwd, e->relPath)) { 366 | // reload file tree 367 | e->SaveFile(getFrame()->cwd + e->relPath); 368 | getFrame()->tree->reloadTree(); 369 | } 370 | else { 371 | e->SaveFile(getFrame()->cwd + e->relPath); 372 | } 373 | e->saved = true; 374 | } 375 | if (parsedCmd[0] == "wq") { 376 | panel->deleteCurr(); 377 | } 378 | } 379 | 380 | void Window::doOpenFile(void) { 381 | std::vector parsedCmd = command->parseCommand(); 382 | Editor *e = getCurrentEditor(); 383 | 384 | // later: check if curr editor saved before replacing 385 | if (FileHelper::isValidPath(getFrame()->cwd, parsedCmd[1])) { 386 | e->relPath = parsedCmd[1]; 387 | panel->SetPageText(currEditor, parsedCmd[1]); 388 | e->SetEditable(true); 389 | e->SetReadOnly(false); 390 | e->ClearAll(); 391 | int lexer = FileHelper::getLexerFromExtension(e->relPath); 392 | e->applyLexer(lexer); 393 | if (FileHelper::isExistingPath(getFrame()->cwd, parsedCmd[1])) { 394 | e->loadFormatted(getFrame()->cwd + e->relPath); 395 | if (FileHelper::isReadOnlyFile(getFrame()->cwd, e->relPath)) { 396 | e->SetEditable(false); 397 | e->SetReadOnly(true); 398 | e->readOnly = true; 399 | } 400 | else { 401 | e->readOnly = false; 402 | } 403 | e->saved = true; 404 | } 405 | // else indicate that it's a new file 406 | } 407 | } 408 | 409 | void Window::doNewTab(void) { 410 | std::vector parsedCmd = command->parseCommand(); 411 | 412 | if (parsedCmd.size() == 1) { 413 | panel->AddPage(new Editor(panel, NULL_LEX), "[NO FILE]", true); 414 | return; 415 | } 416 | for (int i = 1; i < parsedCmd.size(); ++i) { 417 | if (!FileHelper::isValidPath(getFrame()->cwd, parsedCmd[i])) { 418 | continue; 419 | } 420 | int lexer = FileHelper::getLexerFromExtension(parsedCmd[i]); 421 | panel->AddPage(new Editor(panel, lexer), parsedCmd[i], true); 422 | Editor *e = getCurrentEditor(); 423 | e->relPath = parsedCmd[i]; 424 | if (FileHelper::isExistingPath(getFrame()->cwd, parsedCmd[i])) { 425 | e->loadFormatted(getFrame()->cwd + e->relPath); 426 | if (FileHelper::isReadOnlyFile(getFrame()->cwd, e->relPath)) { 427 | e->SetEditable(false); 428 | e->SetReadOnly(true); 429 | e->readOnly = true; 430 | } 431 | else { 432 | e->readOnly = false; 433 | } 434 | e->saved = true; 435 | } 436 | } 437 | } 438 | 439 | void Window::doSplitTab(void) { 440 | if (command->cmd == "split") { 441 | panel->Split(currEditor, wxBOTTOM); 442 | } 443 | else { 444 | panel->Split(currEditor, wxRIGHT); 445 | } 446 | } 447 | 448 | void Window::doOpenHelpFile(void) { 449 | panel->AddPage(new HelpFile(panel), "Help", true); 450 | } 451 | 452 | void Window::doBasicVisMovement(void) { 453 | std::pair parsedCmd = command->parseNormal(); 454 | Editor *e = getCurrentEditor(); 455 | 456 | if (parsedCmd.second == "h") { 457 | e->caretLeftVis(parsedCmd.first); 458 | } 459 | else if (parsedCmd.second == "j") { 460 | e->caretDownVis(parsedCmd.first); 461 | } 462 | else if (parsedCmd.second == "k") { 463 | e->caretUpVis(parsedCmd.first); 464 | } 465 | else if (parsedCmd.second == "l") { 466 | e->caretRightVis(parsedCmd.first); 467 | } 468 | } 469 | 470 | void Window::doVisOrLineOrNormalDelete(void) { 471 | Editor *e = getCurrentEditor(); 472 | 473 | lastCopiedMode = mode; 474 | e->cutSelection(); 475 | mode = NORMAL_MODE; 476 | } 477 | 478 | void Window::doVisOrLineCaseChange(void) { 479 | Editor *e = getCurrentEditor(); 480 | 481 | if (command->cmd == "u") { 482 | e->caseChangeSelection(false); 483 | } 484 | else if (command->cmd == "U") { 485 | e->caseChangeSelection(true); 486 | } 487 | 488 | e->removeSelection(); 489 | mode = NORMAL_MODE; 490 | } 491 | 492 | void Window::doVisInterLineJump(void) { 493 | Editor *e = getCurrentEditor(); 494 | 495 | if (command->cmd == "gg") { 496 | e->jumpStartVis(); 497 | } 498 | else if (command->cmd == "G") { 499 | e->jumpEndVis(); 500 | } 501 | else { 502 | std::pair parsedCmd = command->parseNormal(); 503 | e->jumpLineVis(parsedCmd.first - 1); 504 | } 505 | } 506 | 507 | void Window::doVisOrLineCopy(void) { 508 | Editor *e = getCurrentEditor(); 509 | 510 | lastCopiedMode = mode; 511 | e->copySelection(); 512 | e->removeSelection(); 513 | mode = NORMAL_MODE; 514 | } 515 | 516 | void Window::doVisWordJump(void) { 517 | std::pair parsedCmd = command->parseNormal(); 518 | Editor *e = getCurrentEditor(); 519 | 520 | if (parsedCmd.second == "w") { 521 | e->wordRightVis(parsedCmd.first); 522 | } 523 | else { 524 | e->wordLeftVis(parsedCmd.first); 525 | } 526 | } 527 | 528 | void Window::doVisIntraLineJump(void) { 529 | Editor *e = getCurrentEditor(); 530 | 531 | if (command->cmd == "_") { 532 | e->lineHomeVis(); 533 | } 534 | else if (command->cmd == "$") { 535 | e->lineEndVis(); 536 | } 537 | else if (command->cmd == "0") { 538 | e->lineStartVis(); 539 | } 540 | } 541 | 542 | void Window::doVisCharSearch(void) { 543 | Editor *e = getCurrentEditor(); 544 | 545 | if (command->cmd[0] == 'f') { 546 | e->charSearchAheadVis(command->cmd[1], 1); 547 | } 548 | else if (command->cmd[0] == 'F') { 549 | e->charSearchBehindVis(command->cmd[1], 1); 550 | } 551 | else if (command->cmd[0] == 't') { 552 | e->charSearchAheadVis(command->cmd[1], 0); 553 | } 554 | else if (command->cmd[0] == 'T') { 555 | e->charSearchBehindVis(command->cmd[1], 0); 556 | } 557 | } 558 | 559 | void Window::doBasicLineMovement(void) { 560 | std::pair parsedCmd = command->parseNormal(); 561 | Editor *e = getCurrentEditor(); 562 | 563 | if (parsedCmd.second == "j") { 564 | e->caretDownLine(parsedCmd.first); 565 | } 566 | else if (parsedCmd.second == "k") { 567 | e->caretUpLine(parsedCmd.first); 568 | } 569 | } 570 | 571 | void Window::doLineShift(void) { 572 | std::pair parsedCmd = command->parseNormal(); 573 | Editor *e = getCurrentEditor(); 574 | 575 | if (parsedCmd.second == "<") { 576 | e->shiftLine(parsedCmd.first, 0); 577 | } 578 | else if (parsedCmd.second == ">") { 579 | e->shiftLine(parsedCmd.first, 1); 580 | } 581 | 582 | e->removeSelection(); 583 | mode = NORMAL_MODE; 584 | } 585 | 586 | void Window::doLineInterLineJump(void) { 587 | Editor *e = getCurrentEditor(); 588 | 589 | if (command->cmd == "gg") { 590 | e->jumpStartLine(); 591 | } 592 | else if (command->cmd == "G") { 593 | e->jumpEndLine(); 594 | } 595 | else { 596 | std::pair parsedCmd = command->parseNormal(); 597 | e->jumpLineLine(parsedCmd.first - 1); 598 | } 599 | } 600 | 601 | void Window::updateStatus(void) { 602 | if (panel->GetPageCount() == 0) { 603 | return; 604 | } 605 | Editor *e = getCurrentEditor(); 606 | 607 | if (mode == NORMAL_MODE) { 608 | statusBar->modeDisplay->setText(" ~NORMAL~"); 609 | statusBar->modeDisplay->setBackground(wxColour(219, 131, 0)); 610 | e->SetCaretForeground(wxColour(219, 131, 0)); 611 | } 612 | else if (mode == EDIT_MODE) { 613 | statusBar->modeDisplay->setText(" ~EDIT~"); 614 | statusBar->modeDisplay->setBackground(wxColour(120, 161, 109)); 615 | e->SetCaretForeground(wxColour(120, 161, 109)); 616 | } 617 | else if (mode == VISUAL_MODE) { 618 | statusBar->modeDisplay->setText(" ~VISUAL~"); 619 | statusBar->modeDisplay->setBackground(wxColour(147, 196, 82)); 620 | e->SetCaretForeground(wxColour(147, 196, 82)); 621 | } 622 | else if (mode == LINE_MODE) { 623 | statusBar->modeDisplay->setText(" ~LINE~"); 624 | statusBar->modeDisplay->setBackground(wxColour(222, 149, 222)); 625 | e->SetCaretForeground(wxColour(222, 149, 222)); 626 | } 627 | 628 | if (e->relPath == "") { 629 | statusBar->pathDisplay->setText("[NO FILE]"); 630 | statusBar->pathDisplay->setForeground(wxColour(227, 11, 11)); 631 | statusBar->pathDisplay->setBackground(wxColour(214, 141, 141)); 632 | } 633 | else { 634 | std::string pathText = getFrame()->cwd + e->relPath; 635 | std::string panelText = std::string(panel->GetPageText(currEditor).mb_str()); 636 | if (!e->saved) { 637 | pathText += "***"; 638 | statusBar->pathDisplay->setForeground(wxColour(82, 7, 7)); 639 | statusBar->pathDisplay->setBackground(wxColour(214, 141, 141)); 640 | statusBar->pathDisplay->BeginBold(); 641 | statusBar->pathDisplay->setText(pathText); 642 | statusBar->pathDisplay->EndBold(); 643 | 644 | if (panelText[0] != '*') { 645 | panel->SetPageText(currEditor, "*" + panelText); 646 | } 647 | } 648 | else { 649 | statusBar->pathDisplay->setForeground(wxColour(82, 7, 7)); 650 | statusBar->pathDisplay->setBackground(wxColour(214, 141, 141)); 651 | statusBar->pathDisplay->setText(pathText); 652 | 653 | if (panelText[0] == '*') { 654 | panel->SetPageText(currEditor, panelText.substr(1, panelText.size()-1)); 655 | } 656 | } 657 | if (e->readOnly) { 658 | statusBar->pathDisplay->AppendText(" [R]"); 659 | } 660 | } 661 | 662 | int curr = e->currLine() + 1; 663 | int len = e->GetLineCount(); 664 | int percent = (int)((double)curr * 100 / len); 665 | statusBar->positionDisplay->setText(std::to_string(curr) + "," + std::to_string(e->linePos() + 1) + " | " + std::to_string(percent) + "%"); 666 | wxColour colour = statusBar->positionDisplay->getForeground(); 667 | colour.Set(255 - (int)((double)percent * 2.55), 12, 174 - (int)((double)percent * 1.7)); 668 | statusBar->positionDisplay->setForeground(colour); 669 | 670 | statusBar->sizeDisplay->setText(std::to_string(e->GetLength()) + " bytes | " + std::to_string(len) + " lines"); 671 | } 672 | -------------------------------------------------------------------------------- /src/Window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Frame; 6 | 7 | #include "Panel.h" 8 | #include "FileTree.h" 9 | #include "CommandBar.h" 10 | #include "Command.h" 11 | #include "StatusBar.h" 12 | #include "HelpFile.h" 13 | 14 | #define NORMAL_MODE 0 15 | #define EDIT_MODE 1 16 | #define VISUAL_MODE 2 17 | #define LINE_MODE 3 18 | 19 | class Window : public wxWindow { 20 | public: 21 | Window(wxWindow *parent); 22 | 23 | Frame *getFrame(void) const; 24 | Editor *getCurrentEditor(void) const; 25 | void executeNormal(const int& cmdInd); 26 | void executeCommand(const int& cmdInd); 27 | void executeVisual(const int& cmdInd); 28 | void executeLine(const int& cmdInd); 29 | 30 | void updateStatus(void); 31 | 32 | Panel *panel; 33 | StatusBar *statusBar; 34 | CommandBar *commandBar; 35 | Command *command; 36 | 37 | unsigned short mode; 38 | unsigned short currEditor; 39 | unsigned short lastCopiedMode; 40 | 41 | private: 42 | void doInsertion(void); 43 | void doBasicMovement(void); 44 | void doNewLine(void); 45 | void doIntraLineJump(void); 46 | void doInterLineJump(void); 47 | void doTabChange(void); 48 | void doWordJump(void); 49 | void doCharSearch(void); 50 | void doVisualMode(void); 51 | void doLineMode(void); 52 | void doPaste(void); 53 | void doLineCut(void); 54 | 55 | void doQuitFile(void); 56 | void doSaveFile(void); 57 | void doOpenFile(void); 58 | void doNewTab(void); 59 | void doSplitTab(void); 60 | void doOpenHelpFile(void); 61 | 62 | void doBasicVisMovement(void); 63 | void doVisOrLineOrNormalDelete(void); 64 | void doVisOrLineCaseChange(void); 65 | void doVisInterLineJump(void); 66 | void doVisOrLineCopy(void); 67 | void doVisWordJump(void); 68 | void doVisIntraLineJump(void); 69 | void doVisCharSearch(void); 70 | 71 | void doBasicLineMovement(void); 72 | void doLineShift(void); 73 | void doLineInterLineJump(void); 74 | 75 | wxBoxSizer *sizer; 76 | }; 77 | -------------------------------------------------------------------------------- /src/res/gadget.xpm: -------------------------------------------------------------------------------- 1 | /* XPM */ 2 | static const char *gadget_icon[] = { 3 | /* columns rows colors chars-per-pixel */ 4 | "39 39 220 2 ", 5 | " c None", 6 | ". c #3C2828", 7 | "X c #3C2829", 8 | "o c #3D2929", 9 | "O c #3E2A29", 10 | "+ c #3E2A2A", 11 | "@ c #3F2B2B", 12 | "# c #432B2A", 13 | "$ c #402C2C", 14 | "% c #412D2D", 15 | "& c #412E2D", 16 | "* c #412E2E", 17 | "= c #422E2E", 18 | "- c #432F2F", 19 | "; c #43302F", 20 | ": c #443030", 21 | "> c #443130", 22 | ", c #443131", 23 | "< c #453131", 24 | "1 c #463232", 25 | "2 c #473333", 26 | "3 c #473334", 27 | "4 c #483334", 28 | "5 c #483434", 29 | "6 c #493535", 30 | "7 c #4A3535", 31 | "8 c #4A3636", 32 | "9 c #4A3637", 33 | "0 c #4B3637", 34 | "q c #4B3737", 35 | "w c #4D3737", 36 | "e c #4C3838", 37 | "r c #4C3839", 38 | "t c #4D3939", 39 | "y c #4E3939", 40 | "u c #4E393A", 41 | "i c #4E3A3A", 42 | "p c #4F3B3B", 43 | "a c #513735", 44 | "s c #5E3A36", 45 | "d c #503B3B", 46 | "f c #513B3B", 47 | "g c #503C3C", 48 | "h c #503D3C", 49 | "j c #513D3D", 50 | "k c #523E3E", 51 | "l c #533F3F", 52 | "z c #543E3D", 53 | "x c #623735", 54 | "c c #673C37", 55 | "v c #5E413E", 56 | "b c #69443E", 57 | "n c #544040", 58 | "m c #554141", 59 | "M c #554242", 60 | "N c #564242", 61 | "B c #574343", 62 | "V c #5C4442", 63 | "C c #5E4443", 64 | "Z c #584444", 65 | "A c #594545", 66 | "S c #5A4646", 67 | "D c #5A4647", 68 | "F c #5B4747", 69 | "G c #5B4748", 70 | "H c #5C4848", 71 | "J c #5D4848", 72 | "K c #5D4948", 73 | "L c #5D4949", 74 | "P c #5D4A4A", 75 | "I c #5E4A4A", 76 | "U c #5F4A4A", 77 | "Y c #5F4B4B", 78 | "T c #634644", 79 | "R c #694642", 80 | "E c #694947", 81 | "W c #604C4C", 82 | "Q c #614D4D", 83 | "! c #624E4E", 84 | "~ c #634F4F", 85 | "^ c #644D4C", 86 | "/ c #6B4C4A", 87 | "( c #6E4F4E", 88 | ") c #714941", 89 | "_ c #784841", 90 | "` c #704C49", 91 | "' c #774F4C", 92 | "] c #784D48", 93 | "[ c #7E4F48", 94 | "{ c #7E524E", 95 | "} c #645050", 96 | "| c #655151", 97 | " . c #665252", 98 | ".. c #675353", 99 | "X. c #685353", 100 | "o. c #685454", 101 | "O. c #695555", 102 | "+. c #6A5656", 103 | "@. c #6B5656", 104 | "#. c #6A5756", 105 | "$. c #6B5757", 106 | "%. c #6B5858", 107 | "&. c #6C5858", 108 | "*. c #6D5959", 109 | "=. c #6E5858", 110 | "-. c #6E5A5A", 111 | ";. c #765251", 112 | ":. c #7C5351", 113 | ">. c #84524A", 114 | ",. c #8E5448", 115 | "<. c #91574C", 116 | "1. c #9E544A", 117 | "2. c #975A4F", 118 | "3. c #9D5B4D", 119 | "4. c #865450", 120 | "5. c #885551", 121 | "6. c #885654", 122 | "7. c #8D5755", 123 | "8. c #8F5856", 124 | "9. c #805858", 125 | "0. c #875A5B", 126 | "q. c #8D5959", 127 | "w. c #9D5C51", 128 | "e. c #935959", 129 | "r. c #935A58", 130 | "t. c #915B5C", 131 | "y. c #AE564D", 132 | "u. c #A7584C", 133 | "i. c #AF5F58", 134 | "p. c #A66150", 135 | "a. c #AC6255", 136 | "s. c #B16356", 137 | "d. c #B46457", 138 | "f. c #B66657", 139 | "g. c #B86554", 140 | "h. c #B66856", 141 | "j. c #BF6B56", 142 | "k. c #BF6C57", 143 | "l. c #B8615B", 144 | "z. c #BD6659", 145 | "x. c #C16B57", 146 | "c. c #C8675D", 147 | "v. c #C4685B", 148 | "b. c #C56D5A", 149 | "n. c #CD6A5D", 150 | "m. c #CB6D5C", 151 | "M. c #D46559", 152 | "N. c #D56C5F", 153 | "B. c #DC705D", 154 | "V. c #D8775E", 155 | "C. c #DA785F", 156 | "Z. c #E4675F", 157 | "A. c #D55F62", 158 | "S. c #DE7C60", 159 | "D. c #E16A62", 160 | "F. c #FC6266", 161 | "G. c #FA6466", 162 | "H. c #FF6567", 163 | "J. c #FC6667", 164 | "K. c #FF6867", 165 | "L. c #FC6E67", 166 | "P. c #FF6668", 167 | "I. c #FF6768", 168 | "U. c #FF6868", 169 | "Y. c #FF6968", 170 | "T. c #FF6A68", 171 | "R. c #FF6B68", 172 | "E. c #FF6C68", 173 | "W. c #FF6D68", 174 | "Q. c #FF6E68", 175 | "!. c #FF6F68", 176 | "~. c #E37561", 177 | "^. c #EA7261", 178 | "/. c #E97563", 179 | "(. c #ED7064", 180 | "). c #E67861", 181 | "_. c #FA7166", 182 | "`. c #FB7567", 183 | "'. c #FD7567", 184 | "]. c #F47E66", 185 | "[. c #F47F66", 186 | "{. c #F77E67", 187 | "}. c #FE7B67", 188 | "|. c #FA7D66", 189 | " X c #FD7D67", 190 | ".X c #FE7E67", 191 | "XX c #FF7068", 192 | "oX c #FF7168", 193 | "OX c #FF7268", 194 | "+X c #FF7368", 195 | "@X c #FF7468", 196 | "#X c #FF7568", 197 | "$X c #FF7768", 198 | "%X c #FF7868", 199 | "&X c #FF7968", 200 | "*X c #FF7A68", 201 | "=X c #FF7B68", 202 | "-X c #FF7C68", 203 | ";X c #FF7D68", 204 | ":X c #FF7E68", 205 | ">X c #FF7F68", 206 | ",X c #EF8364", 207 | ".a.2X.X` Z H W W ! ! ~ o.o. ", 244 | " + + + @ $ ; , 2 2 6 9 y y h.qXw.~.:Xv.F H H H ! ~ | | o.o. ", 245 | " . + $ $ $ ; 2 2 6 r r y g 2.5Xm.:X*X' H H ! ! ! | | X.o.o.*. ", 246 | " + @ $ , : 2 6 6 9 r i p g ] 5X.X.Xn.G H H ! ! } | o.o.o.*. ", 247 | " @ $ : : : 6 9 9 p p g N B :X*X*X4.H ! W ! } | | o.&.*. ", 248 | " : : 2 6 e r p p p n n N /.*XN.H H W ! | | o.o.$.*. ", 249 | " 2 6 6 r i p h n n N Z n.*X5.H ! ! ! | o.o.o.&.*. ", 250 | " e y y p p n n N Z Z c.'.H ! ! } | X.o.o.*.*. ", 251 | " y y p n n M B Z / `.'.( ! ! | | o.&.*.*. ", 252 | " g g v n n M Z Z i.#XXX:.| | | o.&.o.*. ", 253 | " g n n M A H H (.#XXX6.| | o.o.&.*. ", 254 | " N Z Z F H 5.#XE.E.8.| o.o.o.*. ", 255 | " Z Z H H l.XXXXR.e.o.o.*.*. ", 256 | " H H Y D.XXE.R.e.o.o.*.*. ", 257 | " H ! XXE.E.R.q.*.*.*. ", 258 | " ;.E.R.I.I.9.*.*. ", 259 | " 7.R.R.I.G.&.*. ", 260 | " R.I.I.A.*. ", 261 | " H.H.F.t. ", 262 | " 0.*. ", 263 | " ", 264 | " " 265 | }; 266 | --------------------------------------------------------------------------------