diff --git a/src/common/EventHandlerSDL2.cxx b/src/common/EventHandlerSDL2.cxx index fa19303bc..45d31ce9e 100644 --- a/src/common/EventHandlerSDL2.cxx +++ b/src/common/EventHandlerSDL2.cxx @@ -64,13 +64,6 @@ void EventHandlerSDL2::copyText(const string& text) const SDL_SetClipboardText(text.c_str()); }; -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandlerSDL2::cutText(string& text) const -{ - copyText(text); - text = ""; -}; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - string EventHandlerSDL2::pasteText(string& text) const { diff --git a/src/common/EventHandlerSDL2.hxx b/src/common/EventHandlerSDL2.hxx index c7f9c4a2f..2654cd132 100644 --- a/src/common/EventHandlerSDL2.hxx +++ b/src/common/EventHandlerSDL2.hxx @@ -48,7 +48,6 @@ private: Clipboard methods. */ void copyText(const string& text) const override; - void cutText(string& text) const override; string pasteText(string& text) const override; /** diff --git a/src/debugger/gui/PromptWidget.cxx b/src/debugger/gui/PromptWidget.cxx index 75fede786..4f5a0abae 100644 --- a/src/debugger/gui/PromptWidget.cxx +++ b/src/debugger/gui/PromptWidget.cxx @@ -718,7 +718,7 @@ void PromptWidget::textCut() #if defined(PSEUDO_CUT_COPY_PASTE) string text = getLine(); - instance().eventHandler().cutText(text); + instance().eventHandler().copyText(text); // Remove the current line _currentPos = _promptStartPos; diff --git a/src/emucore/EventHandler.hxx b/src/emucore/EventHandler.hxx index 4e591b869..8649bd9e0 100644 --- a/src/emucore/EventHandler.hxx +++ b/src/emucore/EventHandler.hxx @@ -342,7 +342,6 @@ class EventHandler Clipboard methods. */ virtual void copyText(const string& text) const = 0; - virtual void cutText(string& text) const = 0; virtual string pasteText(string& text) const = 0; #endif diff --git a/src/gui/EditTextWidget.cxx b/src/gui/EditTextWidget.cxx index ff53ced5a..35e6eaf04 100644 --- a/src/gui/EditTextWidget.cxx +++ b/src/gui/EditTextWidget.cxx @@ -68,6 +68,7 @@ void EditTextWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount if(!isEditable()) return; + resetSelection(); x += _editScrollOffset; int width = 0; @@ -105,8 +106,10 @@ void EditTextWidget::drawWidget(bool hilite) _changed && onTop && isEnabled() ? kDbgChangedTextColor : onTop && isEnabled() ? _textcolor : kColor, - TextAlign::Left, isEditable() ? -_editScrollOffset : 0, !isEditable()); + TextAlign::Left, scrollOffset(), !isEditable()); + // Draw selected text + drawSelection(); // Draw the caret drawCaret(); } @@ -120,6 +123,7 @@ Common::Rect EditTextWidget::getEditRect() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EditTextWidget::lostFocusWidget() { + EditableWidget::lostFocusWidget(); // If we loose focus, 'commit' the user changes _backupString = editString(); } diff --git a/src/gui/EditableWidget.cxx b/src/gui/EditableWidget.cxx index edfa99f44..b6120c8ac 100644 --- a/src/gui/EditableWidget.cxx +++ b/src/gui/EditableWidget.cxx @@ -75,6 +75,12 @@ void EditableWidget::setEditable(bool editable, bool hiliteBG) } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EditableWidget::lostFocusWidget() +{ + _selectSize = 0; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool EditableWidget::tryInsertChar(char c, int pos) { @@ -112,15 +118,142 @@ bool EditableWidget::handleKeyDown(StellaKey key, StellaMod mod) if(StellaModTest::isAlt(mod)) return true; + if(StellaModTest::isControl(mod) && handleControlKeys(key, mod)) + return true; + if(StellaModTest::isShift(mod) && handleShiftKeys(key)) return true; - if(StellaModTest::isControl(mod) && handleControlKeys(key)) - return true; - return handleNormalKeys(key); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool EditableWidget::handleControlKeys(StellaKey key, StellaMod mod) +{ + bool shift = StellaModTest::isShift(mod); + bool handled = true; + bool dirty = true; + + switch(key) + { + case KBDK_A: + setCaretPos(0); + _selectSize = -int(_editString.size()); + break; + + case KBDK_C: + case KBDK_INSERT: + copySelectedText(); + break; + + case KBDK_E: + if(shift) + _selectSize += _caretPos - int(_editString.size()); + else + _selectSize = 0; + setCaretPos(int(_editString.size())); + break; + + case KBDK_D: + handled = killChar(+1); + if(handled) sendCommand(EditableWidget::kChangedCmd, key, _id); + break; + + case KBDK_K: // TODO + handled = killLine(+1); + if(handled) sendCommand(EditableWidget::kChangedCmd, key, _id); + break; + + case KBDK_U: // TODO + handled = killLine(-1); + if(handled) sendCommand(EditableWidget::kChangedCmd, key, _id); + break; + + case KBDK_V: + pasteSelectedText(); + sendCommand(EditableWidget::kChangedCmd, key, _id); + break; + + case KBDK_W: // TODO + handled = killLastWord(); + if(handled) sendCommand(EditableWidget::kChangedCmd, key, _id); + break; + + case KBDK_X: + cutSelectedText(); + sendCommand(EditableWidget::kChangedCmd, key, _id); + break; + + case KBDK_LEFT: + handled = moveWord(-1, shift); + if(!shift) + _selectSize = 0; + break; + + case KBDK_RIGHT: + handled = moveWord(+1, shift); + if(!shift) + _selectSize = 0; + break; + + default: + handled = false; + dirty = false; + } + + if(dirty) + setDirty(); + + return handled; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool EditableWidget::handleShiftKeys(StellaKey key) +{ + bool handled = true; + + switch(key) + { + case KBDK_DELETE: + case KBDK_KP_PERIOD: + cutSelectedText(); + sendCommand(EditableWidget::kChangedCmd, key, _id); + break; + + case KBDK_INSERT: + pasteSelectedText(); + sendCommand(EditableWidget::kChangedCmd, key, _id); + break; + + case KBDK_LEFT: + if(_caretPos > 0) + handled = moveCaretPos(-1); + break; + + case KBDK_RIGHT: + if(_caretPos < int(_editString.size())) + handled = moveCaretPos(+1); + break; + + case KBDK_HOME: + handled = moveCaretPos(-_caretPos); + break; + + case KBDK_END: + handled = moveCaretPos(int(_editString.size()) - _caretPos); + break; + + + default: + handled = false; + } + + if(handled) + setDirty(); + + return handled; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool EditableWidget::handleNormalKeys(StellaKey key) { @@ -128,6 +261,12 @@ bool EditableWidget::handleNormalKeys(StellaKey key) switch(key) { + case KBDK_LSHIFT: + case KBDK_RSHIFT: + // stay in select mode + handled = false; + break; + case KBDK_RETURN: case KBDK_KP_ENTER: // confirm edit and exit editmode @@ -174,120 +313,24 @@ bool EditableWidget::handleNormalKeys(StellaKey key) } if(handled) - setDirty(); - - return handled; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool EditableWidget::handleShiftKeys(StellaKey key) -{ - bool handled = true; - - switch(key) { - case KBDK_DELETE: - case KBDK_KP_PERIOD: - cutSelectedText(); - sendCommand(EditableWidget::kChangedCmd, key, _id); - break; - - case KBDK_INSERT: - pasteSelectedText(); - sendCommand(EditableWidget::kChangedCmd, key, _id); - break; - - default: - handled = false; - } - - if(handled) setDirty(); - - return handled; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool EditableWidget::handleControlKeys(StellaKey key) -{ - bool handled = true; - - switch(key) - { - case KBDK_A: - setCaretPos(0); - break; - - case KBDK_C: - copySelectedText(); - break; - - case KBDK_E: - setCaretPos(int(_editString.size())); - break; - - case KBDK_D: - handled = killChar(+1); - if(handled) sendCommand(EditableWidget::kChangedCmd, key, _id); - break; - - case KBDK_K: - handled = killLine(+1); - if(handled) sendCommand(EditableWidget::kChangedCmd, key, _id); - break; - - case KBDK_U: - handled = killLine(-1); - if(handled) sendCommand(EditableWidget::kChangedCmd, key, _id); - break; - - case KBDK_V: - pasteSelectedText(); - sendCommand(EditableWidget::kChangedCmd, key, _id); - break; - - case KBDK_W: - handled = killLastWord(); - if(handled) sendCommand(EditableWidget::kChangedCmd, key, _id); - break; - - case KBDK_X: - cutSelectedText(); - sendCommand(EditableWidget::kChangedCmd, key, _id); - break; - - case KBDK_LEFT: - handled = moveWord(-1); - break; - - case KBDK_RIGHT: - handled = moveWord(+1); - break; - - case KBDK_INSERT: - copySelectedText(); - break; - - default: - handled = false; + _selectSize = 0; } - if(handled) - setDirty(); - return handled; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - int EditableWidget::getCaretOffset() const { - int caretpos = 0; + int caretOfs = 0; for (int i = 0; i < _caretPos; i++) - caretpos += _font.getCharWidth(_editString[i]); + caretOfs += _font.getCharWidth(_editString[i]); - caretpos -= _editScrollOffset; + caretOfs -= _editScrollOffset; - return caretpos; + return caretOfs; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -307,7 +350,46 @@ void EditableWidget::drawCaret() y += _y; FBSurface& s = _boss->dialog().surface(); - s.vLine(x, y+2, y + editRect.h() - 2, kTextColorHi); + s.vLine(x, y + 2, y + editRect.h() - 2, kTextColorHi); + s.vLine(x-1, y + 2, y + editRect.h() - 2, kTextColorHi); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EditableWidget::drawSelection() +{ + // Only draw if item is visible + if(!_editable || !isVisible() || !_boss->isVisible() || !_hasFocus + || !_selectSize) + return; + + FBSurface& s = _boss->dialog().surface(); + string text = selectString(); + const Common::Rect& editRect = getEditRect(); + int x = editRect.x(); + int y = editRect.y(); + int w = editRect.w(); + int h = editRect.h(); + int wt = int(text.length()) * _font.getMaxCharWidth() + 1; + int dx = selectPos() * _font.getMaxCharWidth() - _editScrollOffset; + + if(dx < 0) + { + // selected text starts left of displayed rect + text = text.substr(-(dx - 1) / _font.getMaxCharWidth()); + wt += dx; + dx = 0; + } + else + x += dx; + // limit selection to the right of displayed rect + w = std::min(w - dx + 1, wt); + + x += _x; + y += _y; + + s.fillRect(x - 1, y + 1, w + 1, h - 3, kTextColorHi); + s.drawString(_font, text, x, y + 1, w, h, + kTextColorInv, TextAlign::Left, 0, false); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -319,6 +401,17 @@ bool EditableWidget::setCaretPos(int newPos) return adjustOffset(); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool EditableWidget::moveCaretPos(int direction) +{ + if(setCaretPos(_caretPos + direction)) + { + _selectSize -= direction; + return true; + } + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool EditableWidget::adjustOffset() { @@ -328,18 +421,18 @@ bool EditableWidget::adjustOffset() // For some reason (differences in ScummVM event handling??), // this method should always return true. - int caretpos = getCaretOffset(); + int caretOfs = getCaretOffset(); const int editWidth = getEditRect().w(); - if (caretpos < 0) + if (caretOfs < 0) { // scroll left - _editScrollOffset += caretpos; + _editScrollOffset += caretOfs; } - else if (caretpos >= editWidth) + else if (caretOfs >= editWidth) { // scroll right - _editScrollOffset -= (editWidth - caretpos); + _editScrollOffset -= (editWidth - caretOfs); } else if (_editScrollOffset > 0) { @@ -356,25 +449,34 @@ bool EditableWidget::adjustOffset() return true; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int EditableWidget::scrollOffset() +{ + return _editable ? -_editScrollOffset : 0; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool EditableWidget::killChar(int direction) { - bool handled = false; + bool handled = killSelectedText(); - if(direction == -1) // Delete previous character (backspace) + if(!handled) { - if(_caretPos > 0) + if(direction == -1) // Delete previous character (backspace) + { + if(_caretPos > 0) + { + _caretPos--; + _editString.erase(_caretPos, 1); + handled = true; + } + } + else if(direction == 1) // Delete next character (delete) { - _caretPos--; _editString.erase(_caretPos, 1); handled = true; } } - else if(direction == 1) // Delete next character (delete) - { - _editString.erase(_caretPos, 1); - handled = true; - } return handled; } @@ -442,7 +544,7 @@ bool EditableWidget::killLastWord() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool EditableWidget::moveWord(int direction) +bool EditableWidget::moveWord(int direction, bool select) { bool handled = false; bool space = true; @@ -461,6 +563,8 @@ bool EditableWidget::moveWord(int direction) space = false; currentPos--; + if(select) + _selectSize++; } _caretPos = currentPos; handled = true; @@ -478,6 +582,8 @@ bool EditableWidget::moveWord(int direction) space = false; currentPos++; + if(select) + _selectSize--; } _caretPos = currentPos; handled = true; @@ -486,12 +592,56 @@ bool EditableWidget::moveWord(int direction) return handled; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const string EditableWidget::selectString() const +{ + if(_selectSize) + { + int caretPos = _caretPos; + int selectSize = _selectSize; + + if(selectSize < 0) + { + caretPos += selectSize; + selectSize = -selectSize; + } + return _editString.substr(caretPos, selectSize); + } + return EmptyString; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int EditableWidget::selectPos() +{ + if(_selectSize < 0) + return _caretPos + _selectSize; + else + return _caretPos; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool EditableWidget::killSelectedText() +{ + if(_selectSize) + { + if(_selectSize < 0) + { + _caretPos += _selectSize; + _selectSize = -_selectSize; + } + _editString.erase(_caretPos, _selectSize); + _selectSize = 0; + return true; + } + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EditableWidget::cutSelectedText() { #if defined(PSEUDO_CUT_COPY_PASTE) - instance().eventHandler().cutText(_editString); - _caretPos = 0; + instance().eventHandler().copyText(selectString()); + killSelectedText(); #endif } @@ -499,7 +649,7 @@ void EditableWidget::cutSelectedText() void EditableWidget::copySelectedText() { #if defined(PSEUDO_CUT_COPY_PASTE) - instance().eventHandler().copyText(_editString); + instance().eventHandler().copyText(selectString()); #endif } @@ -507,7 +657,11 @@ void EditableWidget::copySelectedText() void EditableWidget::pasteSelectedText() { #if defined(PSEUDO_CUT_COPY_PASTE) - instance().eventHandler().pasteText(_editString); - _caretPos = int(_editString.length()); + string text; + + instance().eventHandler().pasteText(text); + killSelectedText(); + _editString.insert(_caretPos, text); + _caretPos += int(text.length()); #endif } diff --git a/src/gui/EditableWidget.hxx b/src/gui/EditableWidget.hxx index f2e4595fb..9dd047327 100644 --- a/src/gui/EditableWidget.hxx +++ b/src/gui/EditableWidget.hxx @@ -64,6 +64,8 @@ class EditableWidget : public Widget, public CommandSender void setTextFilter(const TextFilter& filter) { _filter = filter; } protected: + void lostFocusWidget() override; + virtual void startEditMode() { setFlags(Widget::FLAG_WANTS_RAWDATA); } virtual void endEditMode() { clearFlags(Widget::FLAG_WANTS_RAWDATA); } virtual void abortEditMode() { clearFlags(Widget::FLAG_WANTS_RAWDATA); } @@ -72,22 +74,29 @@ class EditableWidget : public Widget, public CommandSender virtual int getCaretOffset() const; void drawCaret(); bool setCaretPos(int newPos); + bool moveCaretPos(int direction); bool adjustOffset(); // This method is used internally by child classes wanting to // access/edit the internal buffer string& editString() { return _editString; } + const string selectString() const; + void resetSelection() { _selectSize = 0; } + void drawSelection(); + int scrollOffset(); private: // Line editing - bool handleControlKeys(StellaKey key); + bool handleControlKeys(StellaKey key, StellaMod mod); bool handleShiftKeys(StellaKey key); bool handleNormalKeys(StellaKey key); bool killChar(int direction); bool killLine(int direction); bool killLastWord(); - bool moveWord(int direction); + bool moveWord(int direction, bool select); + bool killSelectedText(); + int selectPos(); // Clipboard void cutSelectedText(); void copySelectedText(); @@ -101,6 +110,7 @@ class EditableWidget : public Widget, public CommandSender bool _editable{true}; string _editString; int _caretPos{0}; + int _selectSize{0}; protected: int _editScrollOffset{0};