2016-03-27 11:49:47 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
2024-06-10 14:13:21 +02:00
|
|
|
Copyright (C) 2015-2024 the OpenMoHAA team
|
2016-03-27 11:49:47 +02:00
|
|
|
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
|
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ui_local.h"
|
2024-01-01 20:34:50 +01:00
|
|
|
#include "../qcommon/localization.h"
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
Event EV_Time_Dragged
|
|
|
|
(
|
|
|
|
"_umledit_drag_timer",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Used to time mouse drag events"
|
|
|
|
);
|
|
|
|
Event EV_UIMultiLineEdit_UpdateCvarEvent
|
|
|
|
(
|
|
|
|
"updatecvar",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Causes text box to update itself using the assigned cvar"
|
|
|
|
);
|
|
|
|
Event EV_UIMultiLineEdit_SetEdit
|
|
|
|
(
|
|
|
|
"edit",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"b",
|
|
|
|
"bool",
|
|
|
|
"Set whether or not the text is editable"
|
|
|
|
);
|
|
|
|
|
|
|
|
CLASS_DECLARATION(UIWidget, UIMultiLineEdit, NULL) {
|
|
|
|
{&W_LeftMouseDown, &UIMultiLineEdit::MouseDown },
|
|
|
|
{&W_LeftMouseUp, &UIMultiLineEdit::MouseUp },
|
|
|
|
{&W_LeftMouseDragged, &UIMultiLineEdit::MouseDragged },
|
|
|
|
{&EV_Time_Dragged, &UIMultiLineEdit::DragTimer },
|
|
|
|
{&W_SizeChanged, &UIMultiLineEdit::SizeChanged },
|
|
|
|
{&EV_UIMultiLineEdit_UpdateCvarEvent, &UIMultiLineEdit::UpdateCvarEvent},
|
|
|
|
{&EV_UIMultiLineEdit_SetEdit, &UIMultiLineEdit::SetEdit },
|
|
|
|
{NULL, NULL }
|
2016-03-27 11:49:47 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
UIMultiLineEdit::UIMultiLineEdit()
|
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
m_vertscroll = NULL;
|
|
|
|
m_mouseState = M_NONE;
|
|
|
|
m_shiftForcedDown = 0;
|
|
|
|
m_edit = true;
|
|
|
|
Empty();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::Empty(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
m_lines.RemoveAllItems();
|
|
|
|
m_lines.AddTail(str());
|
|
|
|
|
|
|
|
m_selection.begin = {};
|
|
|
|
m_selection.end = {};
|
|
|
|
|
|
|
|
if (m_vertscroll) {
|
|
|
|
m_vertscroll->setNumItems(1);
|
|
|
|
m_vertscroll->setTopItem(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_changed = false;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::SetEdit(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
m_edit = ev->GetBoolean(1);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::setData(const char *data)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
str toadd;
|
|
|
|
char s[2] {};
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
m_lines.RemoveAllItems();
|
|
|
|
|
|
|
|
for (p = data; *p; p++) {
|
|
|
|
if (*p == '\n') {
|
|
|
|
m_lines.AddTail(toadd);
|
|
|
|
toadd = "";
|
|
|
|
} else if (*p == '\r') {
|
|
|
|
continue;
|
2024-06-10 14:13:21 +02:00
|
|
|
} else {
|
|
|
|
s[0] = *p;
|
|
|
|
toadd += s;
|
2024-01-01 20:34:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_lines.AddTail(toadd);
|
|
|
|
m_selection.begin = {};
|
|
|
|
m_selection.end = {};
|
|
|
|
|
|
|
|
if (m_vertscroll) {
|
|
|
|
m_vertscroll->setNumItems(m_lines.getCount());
|
|
|
|
m_vertscroll->setTopItem(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_changed = false;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::getData(str& data)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
data = "";
|
|
|
|
|
|
|
|
for (i = 0, m_lines.IterateFromHead(); m_lines.IsCurrentValid(); i++, m_lines.IterateNext()) {
|
|
|
|
data += m_lines.getCurrent();
|
|
|
|
|
|
|
|
if (i != m_lines.getCount() - 1) {
|
|
|
|
data += "\n";
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::FrameInitialized(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
delete m_vertscroll;
|
|
|
|
m_vertscroll = new UIVertScroll();
|
|
|
|
m_vertscroll->setPageHeight(m_frame.size.height / m_font->getHeight(false));
|
|
|
|
m_vertscroll->setTopItem(0);
|
|
|
|
m_vertscroll->setNumItems(m_lines.getCount());
|
|
|
|
m_vertscroll->InitFrameAlignRight(this, 0, 0);
|
|
|
|
|
|
|
|
Connect(this, W_SizeChanged, W_SizeChanged);
|
2024-06-10 14:13:21 +02:00
|
|
|
AllowActivate(true);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::SizeChanged(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
m_vertscroll->setPageHeight(m_frame.size.height / m_font->getHeight(m_bVirtual));
|
|
|
|
m_vertscroll->InitFrameAlignRight(this, 0, 0);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::SortSelection(selectionpoint_t **topsel, selectionpoint_t **botsel)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
if (m_selection.begin.line < m_selection.end.line) {
|
|
|
|
*topsel = &m_selection.begin;
|
|
|
|
*botsel = &m_selection.end;
|
|
|
|
} else if (m_selection.begin.line <= m_selection.end.line && m_selection.begin.column <= m_selection.end.column) {
|
|
|
|
*topsel = &m_selection.begin;
|
|
|
|
*botsel = &m_selection.end;
|
|
|
|
} else {
|
|
|
|
*topsel = &m_selection.end;
|
|
|
|
*botsel = &m_selection.begin;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::UpdateCvarEvent(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
if (m_cvarname.length()) {
|
|
|
|
const char *s = uii.Cvar_GetString(m_cvarname, "");
|
|
|
|
if (s) {
|
|
|
|
setData(s);
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::Draw(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
float aty;
|
|
|
|
int i;
|
|
|
|
UColor selectionColor(UWhite);
|
|
|
|
UColor selectionBG(0, 0, 0.5f, 1);
|
|
|
|
selectionpoint_t *topsel = NULL;
|
|
|
|
selectionpoint_t *botsel = NULL;
|
|
|
|
|
|
|
|
SortSelection(&topsel, &botsel);
|
|
|
|
|
|
|
|
m_lines.IterateFromHead();
|
|
|
|
for (i = 0; i < m_vertscroll->getTopItem(); i++) {
|
|
|
|
m_lines.IterateNext();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = m_vertscroll->getTopItem(), aty = 0; m_lines.IsCurrentValid() && aty < m_frame.size.height;
|
|
|
|
m_lines.IterateNext(), i++) {
|
|
|
|
str& cur = m_lines.getCurrent();
|
|
|
|
int caret = 0;
|
|
|
|
|
|
|
|
if (i < topsel->line || i > botsel->line) {
|
|
|
|
m_font->setColor(m_foreground_color);
|
|
|
|
m_font->Print(0, aty, cur, -1, m_bVirtual);
|
|
|
|
} else {
|
|
|
|
float linewidth = m_font->getWidth(cur, -1);
|
|
|
|
|
|
|
|
if (i > topsel->line && i < botsel->line) {
|
|
|
|
DrawBox(0, aty, linewidth, m_font->getHeight(m_bVirtual), selectionBG, 1.f);
|
|
|
|
m_font->setColor(selectionColor);
|
|
|
|
m_font->Print(0, aty, Sys_LV_CL_ConvertString(cur), -1, m_bVirtual);
|
|
|
|
} else if (i != topsel->line) {
|
|
|
|
if (i == botsel->line) {
|
|
|
|
int toplen = m_font->getWidth(cur, botsel->column);
|
|
|
|
if (toplen) {
|
|
|
|
m_font->setColor(selectionColor);
|
|
|
|
DrawBox(0, aty, toplen, m_font->getHeight(m_bVirtual), selectionBG, 1.f);
|
|
|
|
m_font->Print(0, aty, cur, botsel->column, m_bVirtual);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (toplen < linewidth) {
|
|
|
|
m_font->setColor(m_foreground_color);
|
|
|
|
m_font->Print(toplen, aty, &cur[botsel->column], -1, m_bVirtual);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (botsel == &m_selection.end) {
|
|
|
|
caret = toplen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (i != botsel->line) {
|
|
|
|
int toplen = m_font->getWidth(cur, topsel->column);
|
|
|
|
if (topsel->column) {
|
|
|
|
m_font->setColor(m_foreground_color);
|
|
|
|
m_font->Print(0, aty, cur, topsel->column, m_bVirtual);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (toplen < linewidth) {
|
|
|
|
m_font->setColor(selectionColor);
|
|
|
|
DrawBox(toplen, aty, linewidth - toplen, m_font->getHeight(m_bVirtual), selectionBG, 1.f);
|
|
|
|
m_font->Print(toplen, aty, &cur[topsel->column], -1, m_bVirtual);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (topsel == &m_selection.end) {
|
|
|
|
caret = toplen;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (topsel->column == botsel->column) {
|
|
|
|
caret = m_font->getWidth(cur, topsel->column);
|
|
|
|
m_font->setColor(m_foreground_color);
|
|
|
|
m_font->Print(0, aty, Sys_LV_CL_ConvertString(cur), -1, m_bVirtual);
|
|
|
|
} else {
|
|
|
|
int toplen = m_font->getWidth(cur, topsel->column);
|
|
|
|
int botlen = toplen + m_font->getWidth(&cur[topsel->column], botsel->column - topsel->column);
|
|
|
|
|
|
|
|
if (toplen) {
|
|
|
|
m_font->setColor(m_foreground_color);
|
|
|
|
m_font->Print(0, aty, cur, topsel->column, m_bVirtual);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (botlen != toplen) {
|
|
|
|
DrawBox(toplen, aty, botlen - toplen, m_font->getHeight(m_bVirtual), selectionBG, 1.f);
|
|
|
|
m_font->setColor(selectionColor);
|
|
|
|
m_font->Print(toplen, aty, &cur[topsel->column], botsel->column - topsel->column, m_bVirtual);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cur.length() != botsel->column) {
|
|
|
|
m_font->setColor(m_foreground_color);
|
2024-06-10 14:13:21 +02:00
|
|
|
|
|
|
|
// Fixed in OPM:
|
|
|
|
// highlighting text made the rest of the text after the selection on the line disappear.
|
|
|
|
// Cause: the last two arguments were incorrectly passed in originally,
|
|
|
|
// always specifying maxlen as m_bVirtual (which is usually zero).
|
|
|
|
// m_font->Print(botlen, aty, &cur[botsel->column], m_bVirtual, false);
|
|
|
|
m_font->Print(botlen, aty, &cur[botsel->column], -1, m_bVirtual);
|
2024-01-01 20:34:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
caret = botlen;
|
|
|
|
if (topsel == &m_selection.end) {
|
|
|
|
caret = toplen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_font->getWidth(cur, botsel->column);
|
|
|
|
m_font->getWidth(cur, topsel->column);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_selection.end.line == i && (uid.time % 750) > 374 && IsActive()) {
|
|
|
|
DrawBox(caret, aty, 2.f, m_font->getHeight(m_bVirtual), UBlack, 1.f);
|
|
|
|
}
|
|
|
|
|
|
|
|
aty += m_font->getHeight(m_bVirtual);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
str& UIMultiLineEdit::LineFromLineNumber(int num, bool resetpos)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
static str emptyLine;
|
|
|
|
void *pos;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
pos = m_lines.getPosition();
|
|
|
|
|
|
|
|
for (i = 0, m_lines.IterateFromHead(); i < num && m_lines.IsCurrentValid(); i++) {
|
|
|
|
m_lines.IterateNext();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_lines.IsCurrentValid()) {
|
|
|
|
str& ret = m_lines.getCurrent();
|
|
|
|
if (resetpos) {
|
|
|
|
m_lines.setPosition(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resetpos) {
|
|
|
|
m_lines.setPosition(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return emptyLine;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 21:57:49 +01:00
|
|
|
void UIMultiLineEdit::PointToSelectionPoint(const UIPoint2D& p, selectionpoint_t& sel)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
int clickedLine;
|
|
|
|
int i;
|
|
|
|
float totalWidth = 0;
|
|
|
|
float lastWidth = 0;
|
|
|
|
|
|
|
|
clickedLine = m_vertscroll->getTopItem() + p.y / m_font->getHeight(m_bVirtual);
|
|
|
|
if (clickedLine >= m_lines.getCount()) {
|
|
|
|
clickedLine = m_lines.getCount() - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (clickedLine < 0) {
|
|
|
|
sel.line = 0;
|
|
|
|
sel.column = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
str& line = LineFromLineNumber(clickedLine, true);
|
|
|
|
for (i = 0; line[i] && totalWidth < p.x; i++) {
|
|
|
|
lastWidth = m_font->getCharWidth(line[i]);
|
|
|
|
totalWidth += lastWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (line[i] && i) {
|
|
|
|
lastWidth *= 0.5f;
|
|
|
|
if (totalWidth - lastWidth >= p.x) {
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sel.line = clickedLine;
|
|
|
|
sel.column = i;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::MouseDown(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
PointToSelectionPoint(MouseEventToClientPoint(ev), m_selection.begin);
|
|
|
|
m_selection.end = m_selection.begin;
|
|
|
|
EnsureSelectionPointVisible(m_selection.end);
|
|
|
|
m_mouseState = M_DRAGGING;
|
|
|
|
uWinMan.setFirstResponder(this);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::MouseUp(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
CancelEventsOfType(EV_Time_Dragged);
|
|
|
|
|
|
|
|
if (uWinMan.getFirstResponder() == this) {
|
|
|
|
uWinMan.setFirstResponder(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_mouseState == M_DRAGGING) {
|
|
|
|
EnsureSelectionPointVisible(m_selection.end);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_mouseState = M_NONE;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::MouseDragged(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
m_dragState.lastPos = MouseEventToClientPoint(ev);
|
|
|
|
PointToSelectionPoint(m_dragState.lastPos, m_selection.end);
|
|
|
|
|
|
|
|
if (!EventPending(EV_Time_Dragged)) {
|
|
|
|
PostEvent(new Event(EV_Time_Dragged), 0);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::DragTimer(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
int oldtop;
|
|
|
|
|
|
|
|
PointToSelectionPoint(m_dragState.lastPos, m_selection.end);
|
|
|
|
|
|
|
|
oldtop = m_vertscroll->getTopItem();
|
|
|
|
EnsureSelectionPointVisible(m_selection.end);
|
|
|
|
|
|
|
|
if (m_vertscroll->getTopItem() != oldtop) {
|
|
|
|
PostEvent(new Event(EV_Time_Dragged), 0.25f);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::EnsureSelectionPointVisible(selectionpoint_t& point)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
int newtop;
|
|
|
|
|
|
|
|
if (point.line > m_vertscroll->getTopItem()
|
|
|
|
&& point.line < m_vertscroll->getPageHeight() + m_vertscroll->getTopItem()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_vertscroll->getTopItem() > point.line) {
|
|
|
|
newtop = point.line;
|
|
|
|
} else {
|
|
|
|
newtop = point.line - m_vertscroll->getPageHeight() + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newtop < 0) {
|
|
|
|
newtop = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_vertscroll->setTopItem(newtop);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::BoundSelectionPoint(selectionpoint_t& point)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
point.line = Q_clamp_int(point.line, 0, m_lines.getCount());
|
|
|
|
|
|
|
|
str& line = LineFromLineNumber(point.line, true);
|
|
|
|
point.column = Q_clamp_int(point.column, 0, line.length());
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
qboolean UIMultiLineEdit::KeyEvent(int key, unsigned int time)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
bool caret_moved = false;
|
|
|
|
qboolean key_rec = false;
|
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
case K_UPARROW:
|
|
|
|
m_selection.end.line--;
|
|
|
|
caret_moved = true;
|
2024-06-10 14:13:21 +02:00
|
|
|
key_rec = true;
|
2024-01-01 20:34:50 +01:00
|
|
|
break;
|
|
|
|
case K_DOWNARROW:
|
|
|
|
m_selection.end.line++;
|
|
|
|
caret_moved = true;
|
2024-06-10 14:13:21 +02:00
|
|
|
key_rec = true;
|
2024-01-01 20:34:50 +01:00
|
|
|
break;
|
|
|
|
case K_LEFTARROW:
|
|
|
|
m_selection.end.column--;
|
|
|
|
if (m_selection.end.column < 0) {
|
|
|
|
m_selection.end.column = 999999;
|
|
|
|
m_selection.end.line--;
|
|
|
|
}
|
|
|
|
if (m_selection.end.line < 0) {
|
|
|
|
m_selection.end.column = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
caret_moved = true;
|
|
|
|
key_rec = true;
|
|
|
|
break;
|
|
|
|
case K_RIGHTARROW:
|
|
|
|
{
|
|
|
|
int oldcol = m_selection.end.column;
|
|
|
|
|
|
|
|
m_selection.end.column++;
|
|
|
|
BoundSelectionPoint(m_selection.end);
|
|
|
|
if (m_selection.end.column == oldcol) {
|
|
|
|
m_selection.end.column = 0;
|
|
|
|
m_selection.end.line++;
|
|
|
|
if (m_selection.end.line >= m_lines.getCount()) {
|
|
|
|
m_selection.end.line--;
|
|
|
|
m_selection.end.column = 999999;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
caret_moved = true;
|
|
|
|
key_rec = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case K_INS:
|
|
|
|
if (!IsSelectionEmpty()) {
|
|
|
|
if (uii.Sys_IsKeyDown(K_CTRL)) {
|
|
|
|
CopySelection();
|
|
|
|
} else if (uii.Sys_IsKeyDown(K_SHIFT)) {
|
|
|
|
PasteSelection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case K_DEL:
|
2024-06-10 14:13:21 +02:00
|
|
|
// Fixed in OPM:
|
|
|
|
// DEL key needed to be pressed twice to delete a single character,
|
|
|
|
// as the first hit would only create an "internal" selection,
|
|
|
|
// but wouldn't actually delete the character after the cursor.
|
|
|
|
// The DeleteSelection() method should ALWAYS be called,
|
|
|
|
// not only when there's an active selection.
|
2024-01-01 20:34:50 +01:00
|
|
|
if (IsSelectionEmpty()) {
|
|
|
|
m_shiftForcedDown = true;
|
|
|
|
KeyEvent(K_RIGHTARROW, 0);
|
|
|
|
m_shiftForcedDown = false;
|
|
|
|
} else if (uii.Sys_IsKeyDown(K_SHIFT)) {
|
|
|
|
CopySelection();
|
|
|
|
}
|
2024-06-10 14:13:21 +02:00
|
|
|
|
|
|
|
DeleteSelection();
|
2024-01-01 20:34:50 +01:00
|
|
|
break;
|
|
|
|
case K_PGDN:
|
|
|
|
if (m_selection.end.line == (m_vertscroll->getTopItem() + m_vertscroll->getPageHeight() - 1)) {
|
|
|
|
m_selection.end.line = m_vertscroll->getPageHeight() + m_selection.end.line;
|
|
|
|
} else {
|
|
|
|
m_selection.end.line = m_vertscroll->getTopItem() + m_vertscroll->getPageHeight() - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
caret_moved = true;
|
|
|
|
key_rec = true;
|
|
|
|
break;
|
|
|
|
case K_PGUP:
|
|
|
|
if (m_selection.end.line == m_vertscroll->getTopItem()) {
|
|
|
|
m_selection.end.line -= m_vertscroll->getPageHeight();
|
|
|
|
} else {
|
|
|
|
m_selection.end.line = m_vertscroll->getTopItem();
|
|
|
|
}
|
|
|
|
|
|
|
|
caret_moved = true;
|
|
|
|
key_rec = true;
|
|
|
|
break;
|
|
|
|
case K_HOME:
|
|
|
|
if (uii.Sys_IsKeyDown(K_CTRL)) {
|
|
|
|
m_selection.end.column = 0;
|
|
|
|
m_selection.end.line = 0;
|
|
|
|
} else {
|
|
|
|
m_selection.end.column = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
caret_moved = true;
|
|
|
|
key_rec = true;
|
|
|
|
break;
|
|
|
|
case K_END:
|
|
|
|
if (uii.Sys_IsKeyDown(K_CTRL)) {
|
|
|
|
m_selection.end.column = 999999;
|
|
|
|
m_selection.end.line = m_lines.getCount();
|
|
|
|
} else {
|
|
|
|
m_selection.end.column = 999999;
|
|
|
|
}
|
|
|
|
|
|
|
|
caret_moved = true;
|
|
|
|
key_rec = true;
|
|
|
|
break;
|
|
|
|
case K_MWHEELDOWN:
|
|
|
|
m_vertscroll->AttemptScrollTo(m_vertscroll->getTopItem() + 2);
|
|
|
|
key_rec = true;
|
|
|
|
break;
|
|
|
|
case K_MWHEELUP:
|
|
|
|
m_vertscroll->AttemptScrollTo(m_vertscroll->getTopItem() - 2);
|
|
|
|
key_rec = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (caret_moved) {
|
|
|
|
BoundSelectionPoint(m_selection.end);
|
|
|
|
if (!uii.Sys_IsKeyDown(K_SHIFT) && !m_shiftForcedDown) {
|
|
|
|
m_selection.begin.line = m_selection.end.line;
|
|
|
|
m_selection.begin.column = m_selection.end.column;
|
|
|
|
}
|
|
|
|
EnsureSelectionPointVisible(m_selection.end);
|
|
|
|
}
|
|
|
|
|
|
|
|
return key_rec;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::DeleteSelection(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
selectionpoint_t *topsel;
|
|
|
|
selectionpoint_t *botsel;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (IsSelectionEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_changed = true;
|
|
|
|
SortSelection(&topsel, &botsel);
|
|
|
|
|
|
|
|
if (topsel->line == botsel->line) {
|
|
|
|
str& line = LineFromLineNumber(topsel->line, true);
|
|
|
|
intptr_t newlen = line.length() - (botsel->column - topsel->column);
|
|
|
|
|
|
|
|
for (i = topsel->column; i < newlen; i++) {
|
|
|
|
line[i] = line[i + botsel->column - topsel->column];
|
|
|
|
}
|
|
|
|
|
|
|
|
line.CapLength(newlen);
|
|
|
|
*botsel = *topsel;
|
|
|
|
EnsureSelectionPointVisible(*topsel);
|
2024-06-10 14:13:21 +02:00
|
|
|
return;
|
2024-01-01 20:34:50 +01:00
|
|
|
} else if (botsel->line - topsel->line > 1) {
|
|
|
|
for (i = 0, m_lines.IterateFromHead(); m_lines.IsCurrentValid() && i < topsel->line;
|
|
|
|
i++, m_lines.IterateNext()) {
|
|
|
|
if (i > topsel->line) {
|
|
|
|
m_lines.RemoveCurrentSetPrev();
|
|
|
|
i--;
|
|
|
|
botsel->line--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0, m_lines.IterateFromHead(); m_lines.IsCurrentValid() && i < topsel->line; i++, m_lines.IterateNext()) {}
|
|
|
|
|
|
|
|
str& topline = m_lines.getCurrent();
|
|
|
|
topline.CapLength(topsel->column);
|
|
|
|
m_lines.IterateNext();
|
|
|
|
str& line = m_lines.getCurrent();
|
|
|
|
|
|
|
|
topline += &line[botsel->column];
|
|
|
|
m_lines.RemoveCurrentSetPrev();
|
|
|
|
*botsel = *topsel;
|
|
|
|
m_vertscroll->setNumItems(m_lines.getCount());
|
|
|
|
EnsureSelectionPointVisible(*topsel);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::CharEvent(int ch)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
intptr_t i;
|
|
|
|
|
|
|
|
if (!m_edit) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ch >= ' ' || ch == '\t') {
|
|
|
|
DeleteSelection();
|
|
|
|
m_changed = true;
|
|
|
|
|
|
|
|
str& line = LineFromLineNumber(m_selection.begin.line, true);
|
|
|
|
line += " ";
|
|
|
|
|
|
|
|
for (i = line.length() - 1; i > m_selection.begin.column; i--) {
|
|
|
|
line[i] = line[i - 1];
|
|
|
|
}
|
|
|
|
line[m_selection.begin.column] = ch;
|
|
|
|
m_selection.begin.column++;
|
|
|
|
m_selection.end.column++;
|
|
|
|
} else if (ch == '\b') {
|
|
|
|
if (IsSelectionEmpty()) {
|
|
|
|
m_shiftForcedDown = true;
|
|
|
|
KeyEvent(K_LEFTARROW, 0);
|
|
|
|
m_shiftForcedDown = false;
|
|
|
|
}
|
|
|
|
DeleteSelection();
|
|
|
|
} else if (ch == '\r') {
|
|
|
|
DeleteSelection();
|
|
|
|
m_changed = true;
|
|
|
|
|
|
|
|
str& line = LineFromLineNumber(m_selection.begin.line, false);
|
|
|
|
str otherline = &line[m_selection.begin.column];
|
|
|
|
otherline.CapLength(m_selection.begin.column);
|
|
|
|
|
|
|
|
if (m_lines.IsCurrentValid()) {
|
|
|
|
m_lines.InsertAfterCurrent(otherline);
|
|
|
|
} else {
|
|
|
|
m_lines.AddTail(otherline);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_selection.begin.column = 0;
|
|
|
|
m_selection.begin.line++;
|
|
|
|
m_selection.end.column = m_selection.begin.column;
|
|
|
|
m_selection.end.line = m_selection.begin.line;
|
|
|
|
m_vertscroll->setNumItems(m_lines.getCount());
|
|
|
|
|
|
|
|
EnsureSelectionPointVisible(m_selection.end);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
bool UIMultiLineEdit::IsSelectionEmpty(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
return m_selection.begin.column == m_selection.end.column && m_selection.begin.line == m_selection.end.line;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::CopySelection(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
selectionpoint_t *topsel;
|
|
|
|
selectionpoint_t *botsel;
|
|
|
|
str line;
|
|
|
|
str clipText;
|
|
|
|
|
|
|
|
if (IsSelectionEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SortSelection(&topsel, &botsel);
|
|
|
|
|
|
|
|
line = LineFromLineNumber(topsel->line, true);
|
|
|
|
|
|
|
|
clipText += &line[topsel->column];
|
|
|
|
|
|
|
|
if (topsel->line == botsel->line) {
|
|
|
|
clipText.CapLength(botsel->column - topsel->column);
|
|
|
|
} else {
|
|
|
|
for (int i = topsel->line + 1; i < botsel->line; ++i) {
|
|
|
|
clipText += LineFromLineNumber(i, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
line = LineFromLineNumber(botsel->line, true);
|
|
|
|
line.CapLength(botsel->column);
|
|
|
|
clipText += "\n" + line;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::PasteSelection(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
str sel;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
sel = uii.Sys_GetClipboard();
|
|
|
|
|
|
|
|
DeleteSelection();
|
|
|
|
|
|
|
|
for (i = 0; i < sel.length(); i++) {
|
|
|
|
if (sel[i] == '\n') {
|
|
|
|
CharEvent('\r');
|
|
|
|
} else {
|
|
|
|
CharEvent(sel[i]);
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
UIPoint2D UIMultiLineEdit::getEndSelPoint(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
return UIPoint2D(m_selection.end.column, m_selection.end.line);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
void UIMultiLineEdit::setChanged(bool b)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
m_changed = b;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 15:57:44 +01:00
|
|
|
bool UIMultiLineEdit::IsChanged(void)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-01-01 20:34:50 +01:00
|
|
|
return m_changed;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-01-01 20:34:50 +01:00
|
|
|
void UIMultiLineEdit::Scroll(Event *ev) {}
|