Add text selection and copying to console
authorShadowNinja <shadowninja@minetest.net>
Sat, 27 Feb 2016 21:04:44 +0000 (16:04 -0500)
committerShadowNinja <shadowninja@minetest.net>
Thu, 3 Mar 2016 04:23:31 +0000 (23:23 -0500)
src/chat.cpp
src/chat.h
src/guiChatConsole.cpp

index d8cf3efd93268ab07338aee2b9aa47ddcc2f9213..809d4e4225c6d803ab3ed7120024bd4e489cf362 100644 (file)
@@ -390,6 +390,7 @@ ChatPrompt::ChatPrompt(std::wstring prompt, u32 history_limit):
        m_cols(0),
        m_view(0),
        m_cursor(0),
+       m_cursor_len(0),
        m_nick_completion_start(0),
        m_nick_completion_end(0)
 {
@@ -426,11 +427,6 @@ void ChatPrompt::addToHistory(std::wstring line)
        m_history_index = m_history.size();
 }
 
-std::wstring ChatPrompt::getLine()
-{
-       return m_line;
-}
-
 void ChatPrompt::clear()
 {
        m_line.clear();
@@ -590,14 +586,12 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
        s32 length = m_line.size();
        s32 increment = (dir == CURSOROP_DIR_RIGHT) ? 1 : -1;
 
-       if (scope == CURSOROP_SCOPE_CHARACTER)
-       {
+       switch (scope) {
+       case CURSOROP_SCOPE_CHARACTER:
                new_cursor += increment;
-       }
-       else if (scope == CURSOROP_SCOPE_WORD)
-       {
-               if (increment > 0)
-               {
+               break;
+       case CURSOROP_SCOPE_WORD:
+               if (dir == CURSOROP_DIR_RIGHT) {
                        // skip one word to the right
                        while (new_cursor < length && isspace(m_line[new_cursor]))
                                new_cursor++;
@@ -605,39 +599,47 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
                                new_cursor++;
                        while (new_cursor < length && isspace(m_line[new_cursor]))
                                new_cursor++;
-               }
-               else
-               {
+               } else {
                        // skip one word to the left
                        while (new_cursor >= 1 && isspace(m_line[new_cursor - 1]))
                                new_cursor--;
                        while (new_cursor >= 1 && !isspace(m_line[new_cursor - 1]))
                                new_cursor--;
                }
-       }
-       else if (scope == CURSOROP_SCOPE_LINE)
-       {
+               break;
+       case CURSOROP_SCOPE_LINE:
                new_cursor += increment * length;
+               break;
+       case CURSOROP_SCOPE_SELECTION:
+               break;
        }
 
        new_cursor = MYMAX(MYMIN(new_cursor, length), 0);
 
-       if (op == CURSOROP_MOVE)
-       {
+       switch (op) {
+       case CURSOROP_MOVE:
                m_cursor = new_cursor;
-       }
-       else if (op == CURSOROP_DELETE)
-       {
-               if (new_cursor < old_cursor)
-               {
-                       m_line.erase(new_cursor, old_cursor - new_cursor);
-                       m_cursor = new_cursor;
+               m_cursor_len = 0;
+               break;
+       case CURSOROP_DELETE:
+               if (m_cursor_len > 0) { // Delete selected text first
+                       m_line.erase(m_cursor, m_cursor_len);
+               } else {
+                       m_cursor = MYMIN(new_cursor, old_cursor);
+                       m_line.erase(m_cursor, abs(new_cursor - old_cursor));
                }
-               else if (new_cursor > old_cursor)
-               {
-                       m_line.erase(old_cursor, new_cursor - old_cursor);
-                       m_cursor = old_cursor;
+               m_cursor_len = 0;
+               break;
+       case CURSOROP_SELECT:
+               if (scope == CURSOROP_SCOPE_LINE) {
+                       m_cursor = 0;
+                       m_cursor_len = length;
+               } else {
+                       m_cursor = MYMIN(new_cursor, old_cursor);
+                       m_cursor_len += abs(new_cursor - old_cursor);
+                       m_cursor_len = MYMIN(m_cursor_len, length - m_cursor);
                }
+               break;
        }
 
        clampView();
index 367baaaf212dc66e69c5a3244763ddf1b2b05c26..db4146d35ba8ffd59dd6d2cf0159959a90a727b2 100644 (file)
@@ -150,7 +150,11 @@ public:
        void addToHistory(std::wstring line);
 
        // Get current line
-       std::wstring getLine();
+       std::wstring getLine() const { return m_line; }
+
+       // Get section of line that is currently selected
+       std::wstring getSelection() const
+               { return m_line.substr(m_cursor, m_cursor_len); }
 
        // Clear the current line
        void clear();
@@ -172,10 +176,13 @@ public:
        std::wstring getVisiblePortion() const;
        // Get cursor position (relative to visible portion). -1 if invalid
        s32 getVisibleCursorPosition() const;
+       // Get length of cursor selection
+       s32 getCursorLength() const { return m_cursor_len; }
 
        // Cursor operations
        enum CursorOp {
                CURSOROP_MOVE,
+               CURSOROP_SELECT,
                CURSOROP_DELETE
        };
 
@@ -189,7 +196,8 @@ public:
        enum CursorOpScope {
                CURSOROP_SCOPE_CHARACTER,
                CURSOROP_SCOPE_WORD,
-               CURSOROP_SCOPE_LINE
+               CURSOROP_SCOPE_LINE,
+               CURSOROP_SCOPE_SELECTION
        };
 
        // Cursor operation
@@ -227,6 +235,8 @@ private:
        s32 m_view;
        // Cursor (index into m_line)
        s32 m_cursor;
+       // Cursor length (length of selected portion of line)
+       s32 m_cursor_len;
 
        // Last nick completion start (index into m_line)
        s32 m_nick_completion_start;
index 4a084a8e5a7ef76da059f87b7b5b6526a6e13301..d1351a0f73ae5003708cfa89b2ea3622a44067fc 100644 (file)
@@ -377,13 +377,15 @@ void GUIChatConsole::drawPrompt()
                s32 cursor_pos = prompt.getVisibleCursorPosition();
                if (cursor_pos >= 0)
                {
+                       s32 cursor_len = prompt.getCursorLength();
                        video::IVideoDriver* driver = Environment->getVideoDriver();
                        s32 x = (1 + cursor_pos) * m_fontsize.X;
                        core::rect<s32> destrect(
                                x,
-                               y + (1.0-m_cursor_height) * m_fontsize.Y,
-                               x + m_fontsize.X,
-                               y + m_fontsize.Y);
+                               y + m_fontsize.Y * (1.0 - m_cursor_height),
+                               x + m_fontsize.X * MYMAX(cursor_len, 1),
+                               y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1)
+                       );
                        video::SColor cursor_color(255,255,255,255);
                        driver->draw2DRectangle(
                                cursor_color,
@@ -454,32 +456,20 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
                        prompt.historyNext();
                        return true;
                }
-               else if(event.KeyInput.Key == KEY_LEFT)
+               else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT)
                {
-                       // Left or Ctrl-Left pressed
-                       // move character / word to the left
-                       ChatPrompt::CursorOpScope scope =
-                               event.KeyInput.Control ?
+                       // Left/right pressed
+                       // Move/select character/word to the left depending on control and shift keys
+                       ChatPrompt::CursorOp op = event.KeyInput.Shift ?
+                               ChatPrompt::CURSOROP_SELECT :
+                               ChatPrompt::CURSOROP_MOVE;
+                       ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ?
+                               ChatPrompt::CURSOROP_DIR_LEFT :
+                               ChatPrompt::CURSOROP_DIR_RIGHT;
+                       ChatPrompt::CursorOpScope scope = event.KeyInput.Control ?
                                ChatPrompt::CURSOROP_SCOPE_WORD :
                                ChatPrompt::CURSOROP_SCOPE_CHARACTER;
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_MOVE,
-                               ChatPrompt::CURSOROP_DIR_LEFT,
-                               scope);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_RIGHT)
-               {
-                       // Right or Ctrl-Right pressed
-                       // move character / word to the right
-                       ChatPrompt::CursorOpScope scope =
-                               event.KeyInput.Control ?
-                               ChatPrompt::CURSOROP_SCOPE_WORD :
-                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_MOVE,
-                               ChatPrompt::CURSOROP_DIR_RIGHT,
-                               scope);
+                       prompt.cursorOperation(op, dir, scope);
                        return true;
                }
                else if(event.KeyInput.Key == KEY_HOME)
@@ -530,16 +520,58 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
                                scope);
                        return true;
                }
+               else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control)
+               {
+                       // Ctrl-A pressed
+                       // Select all text
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_SELECT,
+                               ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control)
+               {
+                       // Ctrl-C pressed
+                       // Copy text to clipboard
+                       if (prompt.getCursorLength() <= 0)
+                               return true;
+                       std::string selected = wide_to_narrow(prompt.getSelection());
+                       Environment->getOSOperator()->copyToClipboard(selected.c_str());
+                       return true;
+               }
                else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control)
                {
                        // Ctrl-V pressed
                        // paste text from clipboard
+                       if (prompt.getCursorLength() > 0) {
+                               // Delete selected section of text
+                               prompt.cursorOperation(
+                                       ChatPrompt::CURSOROP_DELETE,
+                                       ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+                                       ChatPrompt::CURSOROP_SCOPE_SELECTION);
+                       }
                        IOSOperator *os_operator = Environment->getOSOperator();
                        const c8 *text = os_operator->getTextFromClipboard();
                        if (text)
                                prompt.input(narrow_to_wide(text));
                        return true;
                }
+               else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control)
+               {
+                       // Ctrl-X pressed
+                       // Cut text to clipboard
+                       if (prompt.getCursorLength() <= 0)
+                               return true;
+                       std::wstring wselected = prompt.getSelection();
+                       std::string selected(wselected.begin(), wselected.end());
+                       Environment->getOSOperator()->copyToClipboard(selected.c_str());
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+                               ChatPrompt::CURSOROP_SCOPE_SELECTION);
+                       return true;
+               }
                else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control)
                {
                        // Ctrl-U pressed