openmohaa/code/uilib/uimledit.cpp

815 lines
25 KiB
C++
Raw Normal View History

2016-03-27 11:49:47 +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;
} 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()) {
str text = m_lines.getCurrent();
if (i == 0 && !text.length() && m_lines.getCount() <= 1) {
// file is empty
return;
}
data += text;
2024-01-01 20:34:50 +01:00
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);
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) {
2024-06-10 14:17:35 +02:00
// Print regular line without any selection or cursor present
2024-01-01 20:34:50 +01:00
m_font->setColor(m_foreground_color);
m_font->Print(0, aty, cur, -1, m_bVirtual);
} else {
2024-06-10 14:17:35 +02:00
// Current line contains cursor and/or selected text
2024-01-01 20:34:50 +01:00
float linewidth = m_font->getWidth(cur, -1);
if (i > topsel->line && i < botsel->line) {
2024-06-10 14:17:35 +02:00
// all text in current line is selected, it's part of a larger selection,
// print entire line with the selection highlight box around it
DrawBox(0.0f, aty, linewidth, m_font->getHeight(m_bVirtual), selectionBG, 1.f);
2024-01-01 20:34:50 +01:00
m_font->setColor(selectionColor);
// Fixed in OPM:
// don't spam LOCALIZATION ERROR messages to console
// for clicking lines in the opened text document
//m_font->Print(0, aty, Sys_LV_CL_ConvertString(cur), -1, m_bVirtual);
m_font->Print(0, aty, cur, -1, m_bVirtual);
2024-01-01 20:34:50 +01:00
} else if (i != topsel->line) {
2024-06-10 14:17:35 +02:00
// part of this line is selected, and selection continues/began above
if (i == botsel->line) { // sanity check, should always be true
int toplen = m_font->getWidth(cur, botsel->column); // X coord of highlighting end
2024-01-01 20:34:50 +01:00
if (toplen) {
2024-06-10 14:17:35 +02:00
// selection contains text from the beginning of the line,
// print it with the selection highlight box around it
2024-01-01 20:34:50 +01:00
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);
}
2024-06-10 14:17:35 +02:00
if (toplen < linewidth) { // is there still text on this line after the selection?
2024-01-01 20:34:50 +01:00
m_font->setColor(m_foreground_color);
m_font->Print(toplen, aty, &cur[botsel->column], -1, m_bVirtual);
}
if (botsel == &m_selection.end) {
2024-06-10 14:17:35 +02:00
// Place cursor at the end of the selection if it was started from above
2024-01-01 20:34:50 +01:00
caret = toplen;
}
}
} else if (i != botsel->line) {
2024-06-10 14:17:35 +02:00
// part of this line is selected, and selection continues/began below
int toplen = m_font->getWidth(cur, topsel->column); // X coord of highlighting start
if (topsel->column) { // is there any text on this line before the selection?
2024-01-01 20:34:50 +01:00
m_font->setColor(m_foreground_color);
m_font->Print(0, aty, cur, topsel->column, m_bVirtual);
}
2024-06-10 14:17:35 +02:00
if (toplen < linewidth) { // is there any selected text before the end of the line?
// print the selected text with the selection highlight box around it
2024-01-01 20:34:50 +01:00
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) {
2024-06-10 14:17:35 +02:00
// Place cursor before the highlight box if selection was made from the bottom up
2024-01-01 20:34:50 +01:00
caret = toplen;
}
} else {
2024-06-10 14:17:35 +02:00
// current line contains the cursor
2024-01-01 20:34:50 +01:00
if (topsel->column == botsel->column) {
2024-06-10 14:17:35 +02:00
// no selection or highlighted text
2024-01-01 20:34:50 +01:00
caret = m_font->getWidth(cur, topsel->column);
m_font->setColor(m_foreground_color);
// Fixed in OPM:
// don't spam LOCALIZATION ERROR messages to console
// for clicking lines in the opened text document
//m_font->Print(0, aty, Sys_LV_CL_ConvertString(cur), -1, m_bVirtual);
m_font->Print(0, aty, cur, -1, m_bVirtual);
2024-01-01 20:34:50 +01:00
} else {
2024-06-10 14:17:35 +02:00
// selection starts and ends on this line
int toplen = m_font->getWidth(cur, topsel->column); // X coord of highlighting start
int botlen = toplen + m_font->getWidth(&cur[topsel->column], botsel->column - topsel->column); // X coord of selection end
2024-01-01 20:34:50 +01:00
2024-06-10 14:17:35 +02:00
if (toplen) { // is there any text on this line before the selection?
2024-01-01 20:34:50 +01:00
m_font->setColor(m_foreground_color);
m_font->Print(0, aty, cur, topsel->column, m_bVirtual);
}
2024-06-10 14:17:35 +02:00
if (botlen != toplen) { // is the selection wider than 0 pixels? (sanity check, always true)
// print the selected text with the selection highlight box around it
2024-01-01 20:34:50 +01:00
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);
}
2024-06-10 14:17:35 +02:00
if (cur.length() != botsel->column) { // is there still text on this line after the selection?
// print the leftover text
2024-01-01 20:34:50 +01:00
m_font->setColor(m_foreground_color);
// 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
}
2024-06-10 14:17:35 +02:00
// Place the cursor at the end of the selection...
2024-01-01 20:34:50 +01:00
caret = botlen;
if (topsel == &m_selection.end) {
2024-06-10 14:17:35 +02:00
// ... except if selection was made from the bottom up
2024-01-01 20:34:50 +01:00
caret = toplen;
}
}
m_font->getWidth(cur, botsel->column);
m_font->getWidth(cur, topsel->column);
}
}
2024-06-10 14:17:35 +02:00
if (m_selection.end.line == i && (uid.time % 750) >= 375 && IsActive()) {
// draw cursor caret
2024-01-01 20:34:50 +01:00
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
}
// num is the ZERO-BASED index of the sought line!
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
}
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);
clickedLine = Q_min(clickedLine, m_lines.getCount() - 1);
2024-01-01 20:34:50 +01:00
if (clickedLine < 0) {
sel.line = 0;
sel.column = 0;
return;
}
const char *line = LineFromLineNumber(clickedLine, true).c_str();
for (i = 0; line[i] && totalWidth < p.x; i++) {
2024-01-01 20:34:50 +01:00
lastWidth = m_font->getCharWidth(line[i]);
totalWidth += lastWidth;
}
if (line[i] && i) {
2024-01-01 20:34:50 +01:00
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
{
// since LineFromLineNumber expects a zero-based line index,
// clamp it to one less than the number of lines if the selection point
// is right at the end of the text document
point.line = Q_clamp_int(point.line, 0, m_lines.getCount() - 1);
2024-01-01 20:34:50 +01:00
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;
key_rec = true;
2024-01-01 20:34:50 +01:00
break;
case K_DOWNARROW:
m_selection.end.line++;
caret_moved = true;
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:
// 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();
}
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);
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 < botsel->line;
2024-01-01 20:34:50 +01:00
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()) {}
// delete topmost line of the selection, but only up to topsel->column
2024-01-01 20:34:50 +01:00
str& topline = m_lines.getCurrent();
if (topline.length() > topsel->column) {
topline.CapLength(topsel->column);
}
2024-01-01 20:34:50 +01:00
m_lines.IterateNext();
str line = m_lines.getCurrent();
// merge remainder of topmost line with the remainder after the end of selection
if (line.length() > botsel->column) {
topline += &line[botsel->column];
}
2024-01-01 20:34:50 +01:00
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 = "";
if (line.length() > m_selection.begin.column) {
otherline = &line[m_selection.begin.column];
line.CapLength(m_selection.begin.column);
}
2024-01-01 20:34:50 +01:00
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);
if (line.length() > topsel->column) {
clipText += &line[topsel->column];
}
2024-01-01 20:34:50 +01:00
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);
if (line.length() > botsel->column) {
line.CapLength(botsel->column);
}
2024-01-01 20:34:50 +01:00
clipText += "\n" + line;
}
uii.Sys_SetClipboard(clipText);
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;
2024-09-21 19:32:23 +02:00
// variable added in OPM as str cannot handle NULL assignment
// we can get NULL here if clipboard is empty/couldn't be retrieved
const char *clipboardData = uii.Sys_GetClipboard();
if (clipboardData == NULL)
{
return;
}
sel = clipboardData;
2024-01-01 20:34:50 +01:00
DeleteSelection();
for (i = 0; i < sel.length(); i++) {
if (sel[i] == '\n') {
CharEvent('\r');
2024-09-21 19:32:23 +02:00
} else if (sel[i] == '\r') {
// Changed in OPM:
// NOP, drop CR characters.
// On Linux/Mac they aren't present anyway,
// on Windows we already have LF chars next to them.
// The filtering for CR on the Windows side was originally done
// in Sys_GetWholeClipboard with a "manual" selective strcpy,
// but here we iterate over all characters of the clipboard anyways,
// so this feels like a better place to do the filtering.
2024-01-01 20:34:50 +01:00
} else {
2024-09-21 19:32:23 +02:00
CharEvent(sel[i]); // FIXME: this is VERY slow and jams up the EventQueue!
2024-01-01 20:34:50 +01:00
}
}
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) {}