1 /* 2 Minetest 3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Lesser General Public License as published by 7 the Free Software Foundation; either version 2.1 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public License along 16 with this program; if not, write to the Free Software Foundation, Inc., 17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 #pragma once 21 22 #include <string> 23 #include <vector> 24 #include <list> 25 26 #include "irrlichttypes.h" 27 #include "util/enriched_string.h" 28 #include "settings.h" 29 30 // Chat console related classes 31 32 struct ChatLine 33 { 34 // age in seconds 35 f32 age = 0.0f; 36 // name of sending player, or empty if sent by server 37 EnrichedString name; 38 // message text 39 EnrichedString text; 40 ChatLineChatLine41 ChatLine(const std::wstring &a_name, const std::wstring &a_text): 42 name(a_name), 43 text(a_text) 44 { 45 } 46 ChatLineChatLine47 ChatLine(const EnrichedString &a_name, const EnrichedString &a_text): 48 name(a_name), 49 text(a_text) 50 { 51 } 52 }; 53 54 struct ChatFormattedFragment 55 { 56 // text string 57 EnrichedString text; 58 // starting column 59 u32 column; 60 // formatting 61 //u8 bold:1; 62 }; 63 64 struct ChatFormattedLine 65 { 66 // Array of text fragments 67 std::vector<ChatFormattedFragment> fragments; 68 // true if first line of one formatted ChatLine 69 bool first; 70 }; 71 72 class ChatBuffer 73 { 74 public: 75 ChatBuffer(u32 scrollback); 76 ~ChatBuffer() = default; 77 78 // Append chat line 79 // Removes oldest chat line if scrollback size is reached 80 void addLine(const std::wstring &name, const std::wstring &text); 81 82 // Remove all chat lines 83 void clear(); 84 85 // Get number of lines currently in buffer. 86 u32 getLineCount() const; 87 // Get reference to i-th chat line. 88 const ChatLine& getLine(u32 index) const; 89 90 // Increase each chat line's age by dtime. 91 void step(f32 dtime); 92 // Delete oldest N chat lines. 93 void deleteOldest(u32 count); 94 // Delete lines older than maxAge. 95 void deleteByAge(f32 maxAge); 96 97 // Get number of rows, 0 if reformat has not been called yet. 98 u32 getRows() const; 99 // Update console size and reformat all formatted lines. 100 void reformat(u32 cols, u32 rows); 101 // Get formatted line for a given row (0 is top of screen). 102 // Only valid after reformat has been called at least once 103 const ChatFormattedLine& getFormattedLine(u32 row) const; 104 // Scrolling in formatted buffer (relative) 105 // positive rows == scroll up, negative rows == scroll down 106 void scroll(s32 rows); 107 // Scrolling in formatted buffer (absolute) 108 void scrollAbsolute(s32 scroll); 109 // Scroll to bottom of buffer (newest) 110 void scrollBottom(); 111 // Scroll to top of buffer (oldest) 112 void scrollTop(); 113 114 // Format a chat line for the given number of columns. 115 // Appends the formatted lines to the destination array and 116 // returns the number of formatted lines. 117 u32 formatChatLine(const ChatLine& line, u32 cols, 118 std::vector<ChatFormattedLine>& destination) const; 119 120 void resize(u32 scrollback); 121 protected: 122 s32 getTopScrollPos() const; 123 s32 getBottomScrollPos() const; 124 125 private: 126 // Scrollback size 127 u32 m_scrollback; 128 // Array of unformatted chat lines 129 std::vector<ChatLine> m_unformatted; 130 131 // Number of character columns in console 132 u32 m_cols = 0; 133 // Number of character rows in console 134 u32 m_rows = 0; 135 // Scroll position (console's top line index into m_formatted) 136 s32 m_scroll = 0; 137 // Array of formatted lines 138 std::vector<ChatFormattedLine> m_formatted; 139 // Empty formatted line, for error returns 140 ChatFormattedLine m_empty_formatted_line; 141 }; 142 143 class ChatPrompt 144 { 145 public: 146 ChatPrompt(const std::wstring &prompt, u32 history_limit); 147 ~ChatPrompt() = default; 148 149 // Input character or string 150 void input(wchar_t ch); 151 void input(const std::wstring &str); 152 153 // Add a string to the history 154 void addToHistory(const std::wstring &line); 155 156 // Get current line getLine()157 std::wstring getLine() const { return m_line; } 158 159 // Get section of line that is currently selected getSelection()160 std::wstring getSelection() const { return m_line.substr(m_cursor, m_cursor_len); } 161 162 // Clear the current line 163 void clear(); 164 165 // Replace the current line with the given text 166 std::wstring replace(const std::wstring &line); 167 168 // Select previous command from history 169 void historyPrev(); 170 // Select next command from history 171 void historyNext(); 172 173 // Nick completion 174 void nickCompletion(const std::list<std::string>& names, bool backwards); 175 176 // Update console size and reformat the visible portion of the prompt 177 void reformat(u32 cols); 178 // Get visible portion of the prompt. 179 std::wstring getVisiblePortion() const; 180 // Get cursor position (relative to visible portion). -1 if invalid 181 s32 getVisibleCursorPosition() const; 182 // Get length of cursor selection getCursorLength()183 s32 getCursorLength() const { return m_cursor_len; } 184 185 // Cursor operations 186 enum CursorOp { 187 CURSOROP_MOVE, 188 CURSOROP_SELECT, 189 CURSOROP_DELETE 190 }; 191 192 // Cursor operation direction 193 enum CursorOpDir { 194 CURSOROP_DIR_LEFT, 195 CURSOROP_DIR_RIGHT 196 }; 197 198 // Cursor operation scope 199 enum CursorOpScope { 200 CURSOROP_SCOPE_CHARACTER, 201 CURSOROP_SCOPE_WORD, 202 CURSOROP_SCOPE_LINE, 203 CURSOROP_SCOPE_SELECTION 204 }; 205 206 // Cursor operation 207 // op specifies whether it's a move or delete operation 208 // dir specifies whether the operation goes left or right 209 // scope specifies how far the operation will reach (char/word/line) 210 // Examples: 211 // cursorOperation(CURSOROP_MOVE, CURSOROP_DIR_RIGHT, CURSOROP_SCOPE_LINE) 212 // moves the cursor to the end of the line. 213 // cursorOperation(CURSOROP_DELETE, CURSOROP_DIR_LEFT, CURSOROP_SCOPE_WORD) 214 // deletes the word to the left of the cursor. 215 void cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope scope); 216 217 protected: 218 // set m_view to ensure that 0 <= m_view <= m_cursor < m_view + m_cols 219 // if line can be fully shown, set m_view to zero 220 // else, also ensure m_view <= m_line.size() + 1 - m_cols 221 void clampView(); 222 223 private: 224 // Prompt prefix 225 std::wstring m_prompt = L""; 226 // Currently edited line 227 std::wstring m_line = L""; 228 // History buffer 229 std::vector<std::wstring> m_history; 230 // History index (0 <= m_history_index <= m_history.size()) 231 u32 m_history_index = 0; 232 // Maximum number of history entries 233 u32 m_history_limit; 234 235 // Number of columns excluding columns reserved for the prompt 236 s32 m_cols = 0; 237 // Start of visible portion (index into m_line) 238 s32 m_view = 0; 239 // Cursor (index into m_line) 240 s32 m_cursor = 0; 241 // Cursor length (length of selected portion of line) 242 s32 m_cursor_len = 0; 243 244 // Last nick completion start (index into m_line) 245 s32 m_nick_completion_start = 0; 246 // Last nick completion start (index into m_line) 247 s32 m_nick_completion_end = 0; 248 }; 249 250 class ChatBackend 251 { 252 public: 253 ChatBackend(); 254 ~ChatBackend() = default; 255 256 // Add chat message 257 void addMessage(const std::wstring &name, std::wstring text); 258 // Parse and add unparsed chat message 259 void addUnparsedMessage(std::wstring line); 260 261 // Get the console buffer 262 ChatBuffer& getConsoleBuffer(); 263 // Get the recent messages buffer 264 ChatBuffer& getRecentBuffer(); 265 // Concatenate all recent messages 266 EnrichedString getRecentChat() const; 267 // Get the console prompt 268 ChatPrompt& getPrompt(); 269 270 // Reformat all buffers 271 void reformat(u32 cols, u32 rows); 272 273 // Clear all recent messages 274 void clearRecentChat(); 275 276 // Age recent messages 277 void step(float dtime); 278 279 // Scrolling 280 void scroll(s32 rows); 281 void scrollPageDown(); 282 void scrollPageUp(); 283 284 // Resize recent buffer based on settings 285 void applySettings(); 286 287 private: 288 ChatBuffer m_console_buffer; 289 ChatBuffer m_recent_buffer; 290 ChatPrompt m_prompt; 291 }; 292