#include #include #include "InputText.h" #include "constant.h" #include "InputMenu.h" using namespace std; extern FONT *joystix; extern int layerCount; extern BITMAP *layer[maxLayers]; static string clipboardText; extern void bringRectangleInteriorOntoScreen(int *x, int *y, int width, int height); static const int inputTextContextMenuLength = 6; static const char *inputTextContextMenuCommand[inputTextContextMenuLength] = { "Undo Alt+Bksp, Ctrl+Z", "Cut Shift+Del, Ctrl+X", "Copy Ctrl+Ins, Ctrl+C", "Paste Shift+Ins, Ctrl+V", "Clear Del", "Select All Ctrl+A" }; static const int inputTextContextMenuAcceleratorKey[inputTextContextMenuLength] = { 0, 2, 0, 0, 2, 7 }; static const int inputTextContextMenuCommandCharCount = 24; string InputText::getStringValue() { return value; } void InputText::setCursorShown(bool desiredCursorShown) { cursorShown = desiredCursorShown; } void InputText::type(char ascii) { cursorShown = active; if (selectedCount < 0) { typing = true; canUndo = true; undoValue = value; undoCursorPosition = cursorPosition - selectedCount; undoSelectedCount = -selectedCount; value.replace(cursorPosition, -selectedCount, 1, ascii); undoUndoCursorPosition = cursorPosition + 1; undoUndoSelectedCount = 1; typingPosition = cursorPosition; } else if (selectedCount == 0) { if (typing) { undoUndoCursorPosition++; undoUndoSelectedCount++; } else { typing = true; canUndo = true; undoValue = value; undoCursorPosition = cursorPosition; undoSelectedCount = 0; undoUndoCursorPosition = cursorPosition + 1; undoUndoSelectedCount = 1; typingPosition = cursorPosition; } if (insert || cursorPosition == value.length()) value.insert(cursorPosition, 1, ascii); else value[cursorPosition] = ascii; } else if (selectedCount > 0) { typing = false; canUndo = true; undoValue = value; undoCursorPosition = cursorPosition; undoSelectedCount = selectedCount; value.replace(cursorPosition - selectedCount, selectedCount, 1, ascii); cursorPosition -= selectedCount; undoUndoCursorPosition = cursorPosition + 1; undoUndoSelectedCount = 1; typingPosition = cursorPosition; } cursorPosition++; selectedCount = 0; } bool InputText::backSpace() { cursorShown = active; selectedCount = 0; if (cursorPosition) { if (typing) { undoUndoCursorPosition--; if (cursorPosition > typingPosition) undoUndoSelectedCount--; else if (cursorPosition <= typingPosition) undoSelectedCount++; } else { typing = true; typingPosition = cursorPosition; canUndo = true; undoValue = value; undoCursorPosition = cursorPosition; undoSelectedCount = 1; undoUndoCursorPosition = cursorPosition - 1; undoUndoSelectedCount = 0; } value.erase(--cursorPosition, 1); return true; } else return false; } void InputText::toggleInsert() { cursorShown = active; typing = false; insert = !insert; } bool InputText::seekLeft() { cursorShown = active; if (cursorPosition) { typing = false; cursorPosition--; selectedCount = 0; return true; } else return false; } bool InputText::seekRight() { cursorShown = active; if (cursorPosition < value.length()) { typing = false; cursorPosition++; selectedCount = 0; return true; } else return false; } void InputText::seekHome() { cursorShown = active; typing = false; cursorPosition = 0; selectedCount = 0; } void InputText::seekEnd() { cursorShown = active; typing = false; cursorPosition = value.length(); selectedCount = 0; } bool InputText::selectLeft() { cursorShown = active; if (cursorPosition) { typing = false; cursorPosition--; selectedCount--; return true; } else return false; } bool InputText::selectRight() { cursorShown = active; if (cursorPosition < value.length()) { typing = false; cursorPosition++; selectedCount++; return true; } else return false; } void InputText::selectHome() { cursorShown = active; typing = false; selectedCount -= cursorPosition; cursorPosition = 0; } void InputText::selectEnd() { cursorShown = active; typing = false; selectedCount += value.length() - cursorPosition; cursorPosition = value.length(); } bool InputText::undo() { cursorShown = active; typing = false; if (canUndo) { value.swap(undoValue); cursorPosition = undoCursorPosition; selectedCount = undoSelectedCount; undoCursorPosition = undoUndoCursorPosition; undoSelectedCount = undoUndoSelectedCount; undoUndoCursorPosition = cursorPosition; undoUndoSelectedCount = selectedCount; return true; } else return false; } bool InputText::cut() { cursorShown = active; typing = false; if (selectedCount < 0) { canUndo = true; undoValue = value; undoCursorPosition = cursorPosition - selectedCount; undoSelectedCount = -selectedCount; clipboardText = value.substr(cursorPosition, -selectedCount); value.erase(cursorPosition, -selectedCount); undoUndoCursorPosition = cursorPosition; undoUndoSelectedCount = 0; selectedCount = 0; return true; } else if (selectedCount == 0) return false; else if (selectedCount > 0) { canUndo = true; undoValue = value; undoCursorPosition = cursorPosition; undoSelectedCount = selectedCount; clipboardText = value.substr(cursorPosition - selectedCount, selectedCount); value.erase(cursorPosition - selectedCount, selectedCount); cursorPosition -= selectedCount; undoUndoCursorPosition = cursorPosition; undoUndoSelectedCount = 0; selectedCount = 0; return true; } } bool InputText::copy() { cursorShown = active; typing = false; if (selectedCount < 0) { clipboardText = value.substr(cursorPosition, -selectedCount); return true; } else if (selectedCount == 0) return false; else if (selectedCount > 0) { clipboardText = value.substr(cursorPosition - selectedCount, selectedCount); return true; } } bool InputText::paste() { cursorShown = active; typing = false; if (clipboardText == "") return false; else { canUndo = true; undoValue = value; undoCursorPosition = cursorPosition; undoSelectedCount = selectedCount; if (selectedCount < 0) value.replace(cursorPosition, -selectedCount, clipboardText); else if (selectedCount == 0) value.insert(cursorPosition, clipboardText); else if (selectedCount > 0) { value.replace(cursorPosition - selectedCount, selectedCount, clipboardText); cursorPosition -= selectedCount; } cursorPosition += clipboardText.length(); undoUndoCursorPosition = cursorPosition; undoUndoSelectedCount = clipboardText.length(); selectedCount = 0; return true; } } bool InputText::del() { cursorShown = active; if (selectedCount < 0) { typing = false; canUndo = true; undoValue = value; undoCursorPosition = cursorPosition - selectedCount; undoSelectedCount = -selectedCount; value.erase(cursorPosition, -selectedCount); undoUndoCursorPosition = cursorPosition; undoUndoSelectedCount = 0; selectedCount = 0; return true; } else if (selectedCount == 0) if (cursorPosition < value.length()) { if (typing) { undoCursorPosition++; undoSelectedCount++; } else { typing = true; typingPosition = cursorPosition; canUndo = true; undoValue = value; undoCursorPosition = cursorPosition + 1; undoSelectedCount = 1; undoUndoCursorPosition = cursorPosition; undoUndoSelectedCount = 0; } value.erase(cursorPosition, 1); return true; } else return false; else if (selectedCount > 0) { typing = false; canUndo = true; undoValue = value; undoCursorPosition = cursorPosition; undoSelectedCount = selectedCount; value.erase(cursorPosition - selectedCount, selectedCount); cursorPosition -= selectedCount; undoUndoCursorPosition = cursorPosition; undoUndoSelectedCount = 0; selectedCount = 0; return true; } } bool InputText::selectAll() { cursorShown = active; typing = false; selectedCount = value.length(); cursorPosition = value.length(); return true; } bool (InputText::* const action[inputTextContextMenuLength])() = { &InputText::undo, &InputText::cut, &InputText::copy, &InputText::paste, &InputText::del, &InputText::selectAll }; bool InputText::contextMenu(int menuX, int menuY) { bringRectangleInteriorOntoScreen(&menuX, &menuY, inputTextContextMenuCommandCharCount * charWidth, inputTextContextMenuLength * charHeight); rect(layer[layerAllToSelf], menuX - 1, menuY - 1, menuX + inputTextContextMenuCommandCharCount * charWidth, menuY + inputTextContextMenuLength * charHeight, 15); const int actionIndex = InputMenu(layer[layerAllToSelf], menuX, menuY, inputTextContextMenuLength, inputTextContextMenuCommand, inputTextContextMenuAcceleratorKey, true, true).block(true); clear_bitmap(layer[layerAllToSelf]); return actionIndex == -1 ? true : (this->*action[actionIndex])(); } char* InputText::getValue() { const char *resultValue = value.c_str(); char *result = (char*)malloc((strlen(resultValue) + 1) * sizeof(char)); if (!result) { unload(); allegro_message("Outrageous shortage of memory."); exit(0); } return strcpy(result, resultValue); } void InputText::setValue(const string &desiredValue, int desiredCursorPosition, int desiredSelectedCount, int desiredScrollPosition) { value = desiredValue; cursorPosition = desiredCursorPosition; selectedCount = desiredSelectedCount; scrollPosition = desiredScrollPosition; } bool InputText::acknowledgeKeyStroke(int keyStroke) { switch (keyStroke >> 8) { case KEY_A: if (key_shifts & KB_CTRL_FLAG) return selectAll(); break; case KEY_C: if (key_shifts & KB_CTRL_FLAG) return copy(); break; case KEY_V: if (key_shifts & KB_CTRL_FLAG) return paste(); break; case KEY_X: if (key_shifts & KB_CTRL_FLAG) return cut(); break; case KEY_Z: if (key_shifts & KB_CTRL_FLAG) return undo(); break; case KEY_0_PAD: if (!(key_shifts & KB_NUMLOCK_FLAG)) if (key_shifts & KB_SHIFT_FLAG) if (key_shifts & KB_CTRL_FLAG) return false; else return paste(); else if (key_shifts & KB_CTRL_FLAG) return copy(); else { toggleInsert(); return true; } break; case KEY_1_PAD: if (!(key_shifts & KB_NUMLOCK_FLAG)) if (key_shifts & KB_SHIFT_FLAG) { selectEnd(); return true; } else { seekEnd(); return true; } break; case KEY_2_PAD: case KEY_3_PAD: case KEY_5_PAD: case KEY_8_PAD: case KEY_9_PAD: if (!(key_shifts & KB_NUMLOCK_FLAG)) return false; break; case KEY_4_PAD: if (!(key_shifts & KB_NUMLOCK_FLAG)) if (key_shifts & KB_SHIFT_FLAG) return selectLeft(); else return seekLeft(); break; case KEY_6_PAD: if (!(key_shifts & KB_NUMLOCK_FLAG)) if (key_shifts & KB_SHIFT_FLAG) return selectRight(); else return seekRight(); break; case KEY_7_PAD: if (!(key_shifts & KB_NUMLOCK_FLAG)) if (key_shifts & KB_SHIFT_FLAG) { selectHome(); return true; } else { seekHome(); return true; } break; case KEY_F10: if (key_shifts & KB_SHIFT_FLAG) { contextMenu(x + (cursorPosition - scrollPosition) * charWidth, y - inputTextContextMenuLength * charHeight - 1); return true; } break; case KEY_ESC: if (blocking) { commit(NULL); return true; } break; case KEY_BACKSPACE: if (key_shifts & KB_ALT_FLAG) return undo(); else return backSpace(); case KEY_ENTER: case KEY_ENTER_PAD: if (blocking) { commit(getValue()); return true; } break; case KEY_INSERT: if (key_shifts & KB_SHIFT_FLAG) if (key_shifts & KB_CTRL_FLAG) return false; else return paste(); else if (key_shifts & KB_CTRL_FLAG) return copy(); else { toggleInsert(); return true; } case KEY_DEL: if (key_shifts & KB_SHIFT_FLAG) return cut(); else return del(); case KEY_HOME: if (key_shifts & KB_SHIFT_FLAG) { selectHome(); return true; } else { seekHome(); return true; } case KEY_END: if (key_shifts & KB_SHIFT_FLAG) { selectEnd(); return true; } else { seekEnd(); return true; } case KEY_LEFT: if (key_shifts & KB_SHIFT_FLAG) return selectLeft(); else return seekLeft(); case KEY_RIGHT: if (key_shifts & KB_SHIFT_FLAG) return selectRight(); else return seekRight(); case KEY_DEL_PAD: if (!(key_shifts & KB_NUMLOCK_FLAG)) if (key_shifts & KB_SHIFT_FLAG) return cut(); else return del(); } const int ascii = keyStroke & 0xFF; if (0x20 <= ascii && ascii <= 0x7E) { type(ascii); return true; } return false; } bool InputText::acknowledgeKeyArray() { bool result = false; const char menuKey = key[KEY_MENU]; if (menuKey && !oldMenuKey) { contextMenu(x + (cursorPosition - scrollPosition) * charWidth, y - inputTextContextMenuLength * charHeight - 1); result = true; } oldMenuKey = menuKey; return result; } bool InputText::acknowledgeMouse() { bool result = false; const int mousePosition = mouse_pos; const int mouseButtons = mouse_b; int mouseCharacterPosition = scrollPosition + ((mouse_pos >> 16) - x) / charWidth; if (mouseCharacterPosition > value.length()) mouseCharacterPosition = value.length(); else if (mouseCharacterPosition < 0) mouseCharacterPosition = 0; const bool mouseWithinField = within(mousePosition >> 16, mousePosition & 0xffff); bool mouseWithinSelection; if (mouseWithinField) { if (selectedCount < 0) mouseWithinSelection = (mousePosition >> 16) >= x + (cursorPosition - scrollPosition) * charWidth && (mousePosition >> 16) < x + (cursorPosition - selectedCount + 1 - scrollPosition) * charWidth; else if (selectedCount == 0) mouseWithinSelection = false; else if (selectedCount > 0) mouseWithinSelection = (mousePosition >> 16) >= x + (cursorPosition - selectedCount - scrollPosition) * charWidth && (mousePosition >> 16) < x + (cursorPosition - scrollPosition) * charWidth; } else mouseWithinSelection = false; if (mouseButtons & 0x1) { if (!(oldMouseB & 0x1) && mouseWithinField) { dragging = true; cursorShown = true; cursorPosition = mouseCharacterPosition; clickCharacterPosition = mouseCharacterPosition; selectedCount = 0; result = true; } else if (dragging) { cursorShown = true; cursorPosition = mouseCharacterPosition; selectedCount = mouseCharacterPosition - clickCharacterPosition; result = true; } } else if (dragging) { dragging = false; cursorShown = active; result = true; } if ((mouseButtons & 0x2) && mouseWithinField) { if (!mouseWithinSelection) { cursorPosition = mouseCharacterPosition; selectedCount = 0; cursorShown = true; draw(); } contextMenu(mousePosition >> 16, mousePosition & 0xffff); cursorShown = active; result = true; } oldMouseB = mouseButtons; return result; } void InputText::acknowledgeHeartBeat() { if (active) cursorShown = !cursorShown; typing = false; } void InputText::focus() { cursorShown = true; active = true; typing = false; } void InputText::blur() { cursorShown = false; active = false; typing = false; } void InputText::shift(int xOffset, int yOffset) { x += xOffset; y += yOffset; } bool InputText::within(int xTarget, int yTarget) { return xTarget >= x && xTarget < x + displayLength * charWidth && yTarget >= y && yTarget < y + charHeight; } void InputText::draw() { if (cursorPosition - scrollPosition < scrollDiff) scrollPosition -= scrollStep; else if (displayLength - cursorPosition + scrollPosition <= scrollDiff) scrollPosition += scrollStep; if (cursorPosition < scrollPosition || cursorPosition >= scrollPosition + displayLength) scrollPosition = cursorPosition - displayLength / 2; if (scrollPosition < 0) scrollPosition = 0; rectfill(canvas, x, y, x + displayLength * charWidth - 1, y + charHeight - 1, 16); if (scrollPosition < value.length()) textout_ex(canvas, joystix, value.substr(scrollPosition, displayLength).c_str(), x, y, 15, -1); if (selectedCount < 0) if (cursorPosition - scrollPosition - selectedCount > displayLength) textout_ex(canvas, joystix, value.substr(cursorPosition, displayLength - cursorPosition + scrollPosition).c_str(), x + (cursorPosition - scrollPosition) * charWidth, y, 16, interactivityColor); else textout_ex(canvas, joystix, value.substr(cursorPosition, -selectedCount).c_str(), x + (cursorPosition - scrollPosition) * charWidth, y, 16, interactivityColor); else if (selectedCount > 0) if (cursorPosition - scrollPosition - selectedCount < 0) { if (scrollPosition < value.length()) textout_ex(canvas, joystix, value.substr(scrollPosition, cursorPosition - scrollPosition).c_str(), x, y, 16, interactivityColor); } else textout_ex(canvas, joystix, value.substr(cursorPosition - selectedCount, selectedCount).c_str(), x + (cursorPosition - scrollPosition - selectedCount) * charWidth, y, 16, interactivityColor); if (cursorShown) rectfill(canvas, x + (cursorPosition - scrollPosition) * charWidth, y, x + (cursorPosition - scrollPosition + 1) * charWidth - 1, y + charHeight - 1, interactivityColor); } void InputText::erase() { rectfill(canvas, x, y, x + displayLength * charWidth - 1, y + charHeight - 1, 0); }