Index: binaries/data/mods/mod/gui/gui.rnc =================================================================== --- binaries/data/mods/mod/gui/gui.rnc +++ binaries/data/mods/mod/gui/gui.rnc @@ -68,6 +68,7 @@ attribute maxwidth { xsd:decimal }? & attribute multiline { bool }?& attribute offset { pos }?& + attribute readonly { bool }?& attribute scrollbar { bool }?& attribute scrollbar_style { text }?& attribute scroll_bottom { bool }?& Index: binaries/data/mods/mod/gui/gui.rng =================================================================== --- binaries/data/mods/mod/gui/gui.rng +++ binaries/data/mods/mod/gui/gui.rng @@ -277,6 +277,11 @@ + + + + + Index: binaries/data/mods/public/gui/replaymenu/replay_menu.js =================================================================== --- binaries/data/mods/public/gui/replaymenu/replay_menu.js +++ binaries/data/mods/public/gui/replaymenu/replay_menu.js @@ -271,7 +271,7 @@ Engine.GetGUIObjectByName("sgVictory").caption = translateVictoryCondition(replay.attribs.settings.GameType); Engine.GetGUIObjectByName("sgNbPlayers").caption = sprintf(translate("Players: %(numberOfPlayers)s"), { "numberOfPlayers": replay.attribs.settings.PlayerData.length }); - Engine.GetGUIObjectByName("replayFilename").caption = escapeText(Engine.GetReplayDirectoryName(replay.directory)); + Engine.GetGUIObjectByName("replayFilename").caption = Engine.GetReplayDirectoryName(replay.directory); let metadata = Engine.GetReplayMetadata(replay.directory); Engine.GetGUIObjectByName("sgPlayersNames").caption = Index: binaries/data/mods/public/gui/replaymenu/replay_menu.xml =================================================================== --- binaries/data/mods/public/gui/replaymenu/replay_menu.xml +++ binaries/data/mods/public/gui/replaymenu/replay_menu.xml @@ -106,7 +106,7 @@ - + Index: source/gui/CInput.h =================================================================== --- source/gui/CInput.h +++ source/gui/CInput.h @@ -174,6 +174,9 @@ /// If the cursor should be drawn or not. bool m_CursorVisState; + + /// If true it allows to user only select the text by mouse and copy by hotkey. + bool m_Readonly; }; #endif // INCLUDED_CINPUT Index: source/gui/CInput.cpp =================================================================== --- source/gui/CInput.cpp +++ source/gui/CInput.cpp @@ -44,7 +44,7 @@ CInput::CInput() : m_iBufferPos(-1), m_iBufferPos_Tail(-1), m_SelectingText(false), m_HorizontalScroll(0.f), m_PrevTime(0.0), m_CursorVisState(true), m_CursorBlinkRate(0.5), m_ComposingText(false), - m_iComposedLength(0), m_iComposedPos(0), m_iInsertPos(0) + m_iComposedLength(0), m_iComposedPos(0), m_iInsertPos(0), m_Readonly(false) { AddSetting(GUIST_int, "buffer_position"); AddSetting(GUIST_float, "buffer_zone"); @@ -55,6 +55,7 @@ AddSetting(GUIST_bool, "mask"); AddSetting(GUIST_int, "max_length"); AddSetting(GUIST_bool, "multiline"); + AddSetting(GUIST_bool, "readonly"); AddSetting(GUIST_bool, "scrollbar"); AddSetting(GUIST_CStr, "scrollbar_style"); AddSetting(GUIST_CGUISpriteInstance, "sprite"); @@ -96,6 +97,10 @@ { ENSURE(m_iBufferPos != -1); + // Readonly mode allows to use mouse and keyboard to select and copy the text only + if (m_Readonly && ev->ev.type != SDL_HOTKEYDOWN && ev->ev.type != SDL_KEYDOWN) + return IN_PASS; + if (ev->ev.type == SDL_HOTKEYDOWN) { if (m_ComposingText) @@ -197,60 +202,9 @@ szChar = ev->ev.key.keysym.sym; wchar_t cooked = 0; + // Readonly commands switch (szChar) { - case SDLK_TAB: // '\t' - /* Auto Complete */ - // We just send the tab event to JS and let it figure out autocomplete. - SendEvent(GUIM_TAB, "tab"); - break; - - case SDLK_BACKSPACE: // '\b' - m_WantedX = 0.0f; - - if (SelectingText()) - DeleteCurSelection(); - else - { - m_iBufferPos_Tail = -1; - - if (pCaption->empty() || m_iBufferPos == 0) - break; - - if (m_iBufferPos == (int)pCaption->length()) - *pCaption = pCaption->Left((long)pCaption->length()-1); - else - *pCaption = pCaption->Left(m_iBufferPos-1) + - pCaption->Right((long)pCaption->length()-m_iBufferPos); - - --m_iBufferPos; - - UpdateText(m_iBufferPos, m_iBufferPos+1, m_iBufferPos); - - } - - UpdateAutoScroll(); - break; - - case SDLK_DELETE: - m_WantedX = 0.0f; - // If selection: - if (SelectingText()) - DeleteCurSelection(); - else - { - if (pCaption->empty() || m_iBufferPos == (int)pCaption->length()) - break; - - *pCaption = pCaption->Left(m_iBufferPos) + - pCaption->Right((long)pCaption->length()-(m_iBufferPos+1)); - - UpdateText(m_iBufferPos, m_iBufferPos+1, m_iBufferPos); - } - - UpdateAutoScroll(); - break; - case SDLK_HOME: // If there's not a selection, we should create one now if (!shiftKeyPressed) @@ -388,7 +342,7 @@ while (current != m_CharacterPositions.end()) { if (m_iBufferPos >= current->m_ListStart && - m_iBufferPos <= current->m_ListStart+(int)current->m_ListOfX.size()) + m_iBufferPos <= current->m_ListStart + (int)current->m_ListOfX.size()) break; ++current; @@ -396,10 +350,10 @@ float pos_x; - if (m_iBufferPos-current->m_ListStart == 0) + if (m_iBufferPos - current->m_ListStart == 0) pos_x = 0.f; else - pos_x = current->m_ListOfX[m_iBufferPos-current->m_ListStart-1]; + pos_x = current->m_ListOfX[m_iBufferPos - current->m_ListStart - 1]; if (m_WantedX > pos_x) pos_x = m_WantedX; @@ -429,7 +383,7 @@ while (current != m_CharacterPositions.end()) { if (m_iBufferPos >= current->m_ListStart && - m_iBufferPos <= current->m_ListStart+(int)current->m_ListOfX.size()) + m_iBufferPos <= current->m_ListStart + (int)current->m_ListOfX.size()) break; ++current; @@ -437,10 +391,10 @@ float pos_x; - if (m_iBufferPos-current->m_ListStart == 0) + if (m_iBufferPos - current->m_ListStart == 0) pos_x = 0.f; else - pos_x = current->m_ListOfX[m_iBufferPos-current->m_ListStart-1]; + pos_x = current->m_ListOfX[m_iBufferPos - current->m_ListStart - 1]; if (m_WantedX > pos_x) pos_x = m_WantedX; @@ -470,6 +424,70 @@ break; /* END: Message History Lookup */ + default: + break; + } + + if (m_Readonly) + { + UpdateBufferPositionSetting(); + return IN_HANDLED; + } + + switch (szChar) + { + case SDLK_TAB: // '\t' + /* Auto Complete */ + // We just send the tab event to JS and let it figure out autocomplete. + SendEvent(GUIM_TAB, "tab"); + break; + + case SDLK_BACKSPACE: // '\b' + m_WantedX = 0.0f; + + if (SelectingText()) + DeleteCurSelection(); + else + { + m_iBufferPos_Tail = -1; + + if (pCaption->empty() || m_iBufferPos == 0) + break; + + if (m_iBufferPos == (int)pCaption->length()) + *pCaption = pCaption->Left((long)pCaption->length()-1); + else + *pCaption = pCaption->Left(m_iBufferPos-1) + + pCaption->Right((long)pCaption->length()-m_iBufferPos); + + --m_iBufferPos; + + UpdateText(m_iBufferPos, m_iBufferPos+1, m_iBufferPos); + + } + + UpdateAutoScroll(); + break; + + case SDLK_DELETE: + m_WantedX = 0.0f; + // If selection: + if (SelectingText()) + DeleteCurSelection(); + else + { + if (pCaption->empty() || m_iBufferPos == (int)pCaption->length()) + break; + + *pCaption = pCaption->Left(m_iBufferPos) + + pCaption->Right((long)pCaption->length()-(m_iBufferPos+1)); + + UpdateText(m_iBufferPos, m_iBufferPos+1, m_iBufferPos); + } + + UpdateAutoScroll(); + break; + case SDLK_KP_ENTER: case SDLK_RETURN: // 'Return' should do a Press event for single liners (e.g. submitting forms) @@ -534,6 +552,9 @@ bool shiftKeyPressed = g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT]; std::string hotkey = static_cast(ev->ev.user.data1); + if (m_Readonly && hotkey != "copy" && hotkey != "text.move.left" && hotkey != "text.move.right") + return IN_PASS; + if (hotkey == "paste") { m_WantedX = 0.0f; @@ -847,6 +868,9 @@ UpdateText(); } UpdateAutoScroll(); + + if (Message.value == CStr("readonly")) + GUI::GetSetting(this, "readonly", m_Readonly); break; } @@ -1031,6 +1055,8 @@ UpdateText(); UpdateAutoScroll(); + + GUI::GetSetting(this, "readonly", m_Readonly); break; }