2016-03-27 11:49:47 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
2024-06-10 14:02:10 +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-06-10 14:02:10 +02:00
|
|
|
Event EV_Notepad_SaveAs
|
|
|
|
(
|
|
|
|
"_notepad_saveas",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Event sent when user selected Save As..."
|
|
|
|
);
|
|
|
|
|
|
|
|
Event EV_Notepad_Save
|
|
|
|
(
|
|
|
|
"_notepad_save",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Event sent when user selected Save"
|
|
|
|
);
|
|
|
|
|
|
|
|
Event EV_Notepad_Open
|
|
|
|
(
|
|
|
|
"_notepad_open",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Event sent when user selected Open"
|
|
|
|
);
|
|
|
|
|
|
|
|
Event EV_Notepad_OpenFile
|
|
|
|
(
|
|
|
|
"openfile",
|
|
|
|
EV_DEFAULT,
|
|
|
|
"s",
|
|
|
|
"nameOfFile",
|
|
|
|
"called to open a file in the notepad"
|
|
|
|
);
|
|
|
|
|
|
|
|
Event EV_Notepad_Find
|
|
|
|
(
|
|
|
|
"_notepad_find",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Event sent when user selected Find"
|
|
|
|
);
|
|
|
|
|
|
|
|
Event EV_Notepad_Goto
|
|
|
|
(
|
|
|
|
"_notepad_goto",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Event sent when user selected Go to line"
|
|
|
|
);
|
|
|
|
|
|
|
|
Event EV_Notepad_Copy
|
|
|
|
(
|
|
|
|
"_notepad_copy",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Event sent when user selected Copy"
|
|
|
|
);
|
|
|
|
|
|
|
|
Event EV_Notepad_Cut
|
|
|
|
(
|
|
|
|
"_notepad_cut",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Event sent when user selected Cut"
|
|
|
|
);
|
|
|
|
|
|
|
|
Event EV_Notepad_Paste
|
|
|
|
(
|
|
|
|
"_notepad_paste",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Event sent when user selected Paste"
|
|
|
|
);
|
|
|
|
|
|
|
|
Event W_Notepad_ChildSizeChanged
|
|
|
|
(
|
|
|
|
"_notepad_childsizechanged",
|
|
|
|
EV_DEFAULT,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
"Signal that the child area of the floating window has changed"
|
|
|
|
);
|
|
|
|
|
|
|
|
CLASS_DECLARATION(UIMultiLineEdit, UINotepadEdit, NULL) {
|
|
|
|
{&W_LeftMouseDown, &UINotepadEdit::MousePressed},
|
|
|
|
{NULL, NULL }
|
2016-03-27 11:49:47 +02:00
|
|
|
};
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
CLASS_DECLARATION(UIFloatingWindow, UINotepad, NULL) {
|
|
|
|
{&W_Notepad_ChildSizeChanged, &UINotepad::ChildSizeChanged},
|
|
|
|
{&EV_Notepad_SaveAs, &UINotepad::SaveAs },
|
|
|
|
{&EV_Notepad_Save, &UINotepad::Save },
|
|
|
|
{&EV_Notepad_Open, &UINotepad::Open },
|
|
|
|
{&EV_Notepad_OpenFile, &UINotepad::OpenFile },
|
|
|
|
{&UIFloatingWindow::W_ClosePressed, &UINotepad::ClosePressed },
|
|
|
|
{&EV_Notepad_Find, &UINotepad::OnFind },
|
|
|
|
{&EV_Notepad_Goto, &UINotepad::OnGoto },
|
|
|
|
{&EV_Notepad_Copy, &UINotepad::OnCopy },
|
|
|
|
{&EV_Notepad_Paste, &UINotepad::OnPaste },
|
|
|
|
{&EV_Notepad_Cut, &UINotepad::OnCut },
|
|
|
|
{NULL, NULL }
|
|
|
|
};
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
#define CTRL_EVENT_COUNT 10
|
|
|
|
static ctrlevent_s controlEvents[CTRL_EVENT_COUNT] = {
|
|
|
|
{'o', &EV_Notepad_Open },
|
|
|
|
{'s', &EV_Notepad_Save },
|
|
|
|
{'a', &EV_Notepad_SaveAs },
|
|
|
|
{'w', &UIFloatingWindow::W_ClosePressed},
|
|
|
|
{'f', &EV_Notepad_Find },
|
|
|
|
{'g', &EV_Notepad_Goto },
|
|
|
|
{'c', &EV_Notepad_Copy },
|
|
|
|
{'x', &EV_Notepad_Cut },
|
|
|
|
{'v', &EV_Notepad_Paste },
|
2024-08-24 15:08:42 +02:00
|
|
|
{0, NULL },
|
2024-06-10 14:02:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
bool UI_LoadNotepadFile(const char *filename)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
UINotepad *uinp = new UINotepad();
|
2024-06-10 14:43:40 +02:00
|
|
|
UIRect2D rect = UIRect2D(100.0f, 100.0f, 450.0f, 300.0f);
|
|
|
|
if (uinp->Create(NULL, rect, filename)) {
|
2024-06-10 14:02:10 +02:00
|
|
|
uWinMan.ActivateControl(uinp);
|
|
|
|
return true;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
if (uinp) {
|
|
|
|
delete uinp;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
return false;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
UINotepadEdit::UINotepadEdit()
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
m_notepad = NULL;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepadEdit::CharEvent(int ch)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
if (!m_notepad->ProcessCharEvent(ch)) {
|
|
|
|
UIMultiLineEdit::CharEvent(ch);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepadEdit::setNotepad(UINotepad *notepad)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
m_notepad = notepad;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
bool UINotepadEdit::GotoLine(int line)
|
|
|
|
{
|
|
|
|
if (line < 1 || line - 1 >= m_lines.getCount()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_selection.begin.line = m_selection.end.line = line - 1;
|
|
|
|
m_selection.begin.column = 0;
|
|
|
|
m_selection.end.column = 999999;
|
|
|
|
BoundSelectionPoint(m_selection.end);
|
|
|
|
EnsureSelectionPointVisible(m_selection.end);
|
|
|
|
return true;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
bool UINotepadEdit::FindText(const char *text, int offsetFromSel)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
if (!text) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
str lowersearch = text;
|
|
|
|
lowersearch.tolower();
|
|
|
|
|
|
|
|
selectionpoint_s *topsel, *botsel;
|
|
|
|
SortSelection(&topsel, &botsel);
|
|
|
|
|
|
|
|
int foundat = -1;
|
|
|
|
bool searchfromtop = false;
|
|
|
|
|
|
|
|
// This loop runs at most twice:
|
|
|
|
// - once for searchfromtop == false,
|
|
|
|
// - optionally for searchfromtop == true,
|
|
|
|
// but only if text couldn't be found between the cursor and the end of the file
|
|
|
|
while (true) {
|
|
|
|
int i = 0;
|
|
|
|
m_lines.IterateFromHead();
|
|
|
|
for (; m_lines.IsCurrentValid(); i++, m_lines.IterateNext()) {
|
|
|
|
if (i < topsel->line && !searchfromtop) {
|
|
|
|
// skip lines before the cursor if not searching from the top
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (i > topsel->line && searchfromtop) {
|
|
|
|
// no need to continue if we've looped back to the cursor
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
str findin = m_lines.getCurrent();
|
|
|
|
findin.tolower();
|
|
|
|
const char *where = findin.c_str();
|
|
|
|
|
|
|
|
// Added in OPM to avoid code repetition
|
|
|
|
bool searchLineFromCursor = i == topsel->line && !searchfromtop;
|
|
|
|
if (searchLineFromCursor) {
|
|
|
|
// Start searching on the current line, BUT ONLY from the cursor position.
|
|
|
|
// Offset it with the selection offset:
|
|
|
|
// the purpose of this is to prevent matching
|
|
|
|
// on a string that was just found and is still selected,
|
|
|
|
// by offsetting it
|
|
|
|
if (findin.length() <= topsel->column + offsetFromSel) {
|
|
|
|
// cursor is at the end of the line, nothing left to search in this one
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
where = &where[topsel->column + offsetFromSel];
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *found = strstr(where, lowersearch);
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
// use the beginning of the line to calculate where the matched text is
|
|
|
|
foundat = found - where;
|
|
|
|
if (searchLineFromCursor) {
|
|
|
|
// take cursor pos. into account if a match is found on the same line as the cursor
|
|
|
|
foundat += topsel->column + offsetFromSel;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (foundat == -1) {
|
|
|
|
// couldn't find text in current search iteration
|
|
|
|
if (!searchfromtop) {
|
|
|
|
// try searching from the beginning of the document
|
|
|
|
searchfromtop = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// text not found
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
// jump to found text and select it
|
|
|
|
m_selection.begin.column = foundat;
|
|
|
|
m_selection.end.column = foundat + strlen(text);
|
|
|
|
m_selection.begin.line = m_selection.end.line = i;
|
|
|
|
EnsureSelectionPointVisible(m_selection.end);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepadEdit::MousePressed(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
if (m_notepad->m_state != STATE_TIMED_MESSAGE) {
|
|
|
|
m_notepad->m_state = STATE_NONE;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
UIMultiLineEdit::MouseDown(ev);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
|
|
|
UINotepad::UINotepad()
|
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
m_state = STATE_NONE;
|
|
|
|
m_edit = NULL;
|
|
|
|
m_menu = NULL;
|
|
|
|
m_status = NULL;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
UINotepad::~UINotepad()
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
for (int i = m_popups.NumObjects(); i > 0; i--) {
|
|
|
|
Container<uipopup_describe *> *inner = m_popups.ObjectAt(i);
|
|
|
|
for (int j = inner->NumObjects(); j > 0; j--) {
|
|
|
|
uipopup_describe *uipd = inner->ObjectAt(j);
|
|
|
|
inner->RemoveObjectAt(j);
|
2024-09-21 19:46:32 +02:00
|
|
|
delete uipd;
|
2024-06-10 14:02:10 +02:00
|
|
|
}
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::TimeMessage(const char *message, int howlong)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
m_state = STATE_TIMED_MESSAGE;
|
|
|
|
m_timedmessage.die = uid.time + howlong;
|
|
|
|
m_timedmessage.text = message;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
bool UINotepad::OpenFile(const char *filename)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
char *data = NULL;
|
|
|
|
if (!filename || FS_ReadFile(filename, (void **)&data) == -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
setFileName(filename);
|
|
|
|
if (m_edit) {
|
|
|
|
m_edit->Empty();
|
|
|
|
m_edit->setData(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
FS_FreeFile(data);
|
|
|
|
return true;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
bool UINotepad::Create(UIWidget *parent, UIRect2D& rect, const char *filename)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
UIFloatingWindow::Create(parent, rect, "", UColor(0.15f, 0.196f, 0.278f), UHudColor);
|
|
|
|
|
|
|
|
m_edit = new UINotepadEdit();
|
|
|
|
m_edit->setNotepad(this);
|
|
|
|
|
|
|
|
m_edit->InitFrame(getChildSpace(), 0.0f, 0.0f, 20.0f, 20.0f, 0, "verdana-12");
|
|
|
|
m_edit->setBackgroundColor(UWhite, true);
|
|
|
|
m_edit->setForegroundColor(UBlack);
|
|
|
|
m_edit->setFont("verdana-14");
|
|
|
|
|
|
|
|
m_status = new UIStatusBar(WND_ALIGN_BOTTOM, 20.0f);
|
|
|
|
m_status->InitFrame(getChildSpace(), 0.0f, 0.0f, 20.0f, 20.0f, 0, "verdana-12");
|
|
|
|
m_status->EnableSizeBox(this);
|
|
|
|
|
|
|
|
m_menu = new UIPulldownMenu();
|
|
|
|
m_menu->CreateAligned(getChildSpace(), this);
|
|
|
|
// Fixed in OPM: menu text was hard to read when notepad is active
|
|
|
|
//m_menu->setForegroundColor(UBlack);
|
|
|
|
m_menu->setForegroundColor(UHudColor);
|
|
|
|
|
|
|
|
Container<uipopup_describe *> *pops = new Container<uipopup_describe *>();
|
|
|
|
|
|
|
|
uipopup_describe *uipd = new uipopup_describe("Open (Ctrl-O)", UIP_EVENT, &EV_Notepad_Open, NULL);
|
|
|
|
pops->AddObject(uipd);
|
|
|
|
uipd = new uipopup_describe("Save (Ctrl-S)", UIP_EVENT, &EV_Notepad_Save, NULL);
|
|
|
|
pops->AddObject(uipd);
|
|
|
|
uipd = new uipopup_describe("Save As...(Ctrl-A)", UIP_EVENT, &EV_Notepad_SaveAs, NULL);
|
|
|
|
pops->AddObject(uipd);
|
|
|
|
|
2024-09-08 23:54:47 +02:00
|
|
|
uipd = new uipopup_describe(str(), UIP_SEPARATOR, NULL, NULL);
|
2024-06-10 14:02:10 +02:00
|
|
|
pops->AddObject(uipd);
|
|
|
|
|
|
|
|
uipd = new uipopup_describe("Close (Ctrl-W)", UIP_EVENT, &UIFloatingWindow::W_ClosePressed, NULL);
|
|
|
|
pops->AddObject(uipd);
|
|
|
|
|
|
|
|
m_popups.AddObject(pops);
|
|
|
|
for (int i = 1; i <= pops->NumObjects(); i++) {
|
|
|
|
m_menu->AddUIPopupDescribe("File", pops->ObjectAt(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
pops = new Container<uipopup_describe *>();
|
|
|
|
uipd = new uipopup_describe("Copy (Ctrl-C)", UIP_EVENT, &EV_Notepad_Copy, NULL);
|
|
|
|
pops->AddObject(uipd);
|
|
|
|
uipd = new uipopup_describe("Cut (Ctrl-X)", UIP_EVENT, &EV_Notepad_Cut, NULL);
|
|
|
|
pops->AddObject(uipd);
|
|
|
|
uipd = new uipopup_describe("Paste (Ctrl-V)", UIP_EVENT, &EV_Notepad_Paste, NULL);
|
|
|
|
pops->AddObject(uipd);
|
|
|
|
|
2024-09-08 23:54:47 +02:00
|
|
|
uipd = new uipopup_describe(str(), UIP_SEPARATOR, NULL, NULL);
|
2024-06-10 14:02:10 +02:00
|
|
|
pops->AddObject(uipd);
|
|
|
|
|
|
|
|
uipd = new uipopup_describe("Find (Ctrl-F)", UIP_EVENT, &EV_Notepad_Find, NULL);
|
|
|
|
pops->AddObject(uipd);
|
|
|
|
uipd = new uipopup_describe("Go to line... (Ctrl-G)", UIP_EVENT, &EV_Notepad_Goto, NULL);
|
|
|
|
pops->AddObject(uipd);
|
|
|
|
|
|
|
|
m_popups.AddObject(pops);
|
|
|
|
for (int i = 1; i <= pops->NumObjects(); i++) {
|
|
|
|
m_menu->AddUIPopupDescribe("Edit", pops->ObjectAt(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
setFileName("");
|
|
|
|
OpenFile(filename);
|
|
|
|
getChildSpace()->Connect(this, W_SizeChanged, W_Notepad_ChildSizeChanged);
|
|
|
|
getChildSpace()->SendSignal(W_SizeChanged);
|
|
|
|
getChildSpace()->AllowActivate(false);
|
|
|
|
return true;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::ChildSizeChanged(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
m_menu->Realign();
|
|
|
|
UISize2D menuSize = m_menu->getSize();
|
|
|
|
UISize2D childSpaceSize = getChildSpace()->getSize();
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
UIRect2D frame(0.0f, menuSize.height, childSpaceSize.width, childSpaceSize.height - 20.0f - menuSize.height);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
m_edit->setFrame(frame);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::SaveAs(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
m_state = STATE_SAVE_AS;
|
|
|
|
m_textinput.text = "";
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::Save(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
if (!m_filename.length()) {
|
|
|
|
SaveAs(NULL);
|
2024-09-21 19:52:20 +02:00
|
|
|
// Fixed in OPM:
|
|
|
|
// devs probably forgot to return here - saving was attempted,
|
|
|
|
// even though there was no filename to save the file with
|
|
|
|
return;
|
2024-06-10 14:02:10 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
str filecontents;
|
|
|
|
m_edit->getData(filecontents);
|
|
|
|
m_edit->setChanged(false);
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
FS_WriteTextFile(m_filename, filecontents, filecontents.length());
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
str result = "Saved " + m_filename;
|
|
|
|
TimeMessage(result, 3000);
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::Open(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
PickFile(m_filename, this, EV_Notepad_OpenFile);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::OpenFile(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
OpenFile(ev->GetString(1));
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::ClosePressed(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
uWinMan.ActivateControl(this);
|
|
|
|
if (m_edit->IsChanged()) {
|
|
|
|
m_state = STATE_CONFIRMCLOSE;
|
|
|
|
m_textinput.text = "y";
|
|
|
|
} else {
|
|
|
|
PostEvent(EV_Remove, 0.0f);
|
|
|
|
|
|
|
|
// Fixed in OPM:
|
|
|
|
// in the original game, the control is not deactivated after removal,
|
|
|
|
// so the already heap-freed UINotepad instance still "reacts" to
|
|
|
|
// KeyEvents and CharEvents, creating a use-after-free scenario
|
|
|
|
// that causes crashes when debug heap is enabled
|
|
|
|
uWinMan.DeactivateCurrentControl();
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::OnFind(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
m_state = STATE_FIND_TEXT;
|
|
|
|
m_textinput.text = m_lastfind;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::OnGoto(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
m_state = STATE_GOTO_LINE;
|
|
|
|
m_textinput.text = "";
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::OnCopy(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
m_edit->CopySelection();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::OnPaste(Event *ev)
|
|
|
|
{
|
|
|
|
m_edit->PasteSelection();
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::OnCut(Event *ev)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
m_edit->CopySelection();
|
|
|
|
m_edit->DeleteSelection();
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
bool UINotepad::ProcessControlEvents(int ch)
|
|
|
|
{
|
|
|
|
if (ch < CTRL('a') || ch > CTRL('z')) {
|
|
|
|
// not a control character
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fixed in OPM:
|
|
|
|
// Original code didn't bounds-check controlEvents
|
|
|
|
for (int i = 0; i < CTRL_EVENT_COUNT; i++) {
|
|
|
|
ctrlevent_s *event = &controlEvents[i];
|
|
|
|
if (!event->ev) {
|
|
|
|
// no event assigned
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ch == CTRL(event->ch)) {
|
|
|
|
// found it
|
|
|
|
PostEvent(
|
|
|
|
new Event(*event->ev), 0.0f
|
|
|
|
); // create a new Event as it will be deleted by ProcessEvent in L_ProcessPendingEvents
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
bool UINotepad::ProcessCharEvent(int ch)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
if (ProcessControlEvents(ch)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_state == STATE_NONE || m_state == STATE_TIMED_MESSAGE) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
str text;
|
|
|
|
switch (ch) {
|
|
|
|
case '\b': // Backspace
|
|
|
|
m_textinput.text.CapLength(m_textinput.text.length() - 1);
|
|
|
|
break;
|
|
|
|
case K_ENTER:
|
|
|
|
switch (m_state) {
|
|
|
|
case STATE_GOTO_LINE:
|
|
|
|
{
|
|
|
|
if (!m_textinput.text.length()) {
|
|
|
|
m_state = STATE_NONE;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int line = atoi(m_textinput.text);
|
|
|
|
if (m_edit->GotoLine(line)) {
|
|
|
|
m_state = STATE_NONE;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
text = "Line '" + m_textinput.text + "' doesn't exist.";
|
|
|
|
TimeMessage(text, 3000);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case STATE_FIND_TEXT:
|
|
|
|
if (!m_textinput.text.length()) {
|
|
|
|
m_state = STATE_NONE;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_edit->FindText(m_textinput.text, 1)) {
|
|
|
|
text = "Text: '" + m_textinput.text + "' could not be found.";
|
|
|
|
TimeMessage(text, 3000);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_lastfind = m_textinput.text;
|
|
|
|
return true;
|
|
|
|
case STATE_SAVE_AS:
|
|
|
|
m_state = STATE_NONE;
|
|
|
|
|
|
|
|
if (!m_textinput.text.length()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
setFileName(m_textinput.text);
|
|
|
|
Save(NULL);
|
|
|
|
return true;
|
|
|
|
case STATE_CONFIRMCLOSE:
|
|
|
|
{
|
|
|
|
m_state = STATE_NONE;
|
|
|
|
|
|
|
|
if (!m_textinput.text.length()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
char choice = tolower(m_textinput.text[0]);
|
|
|
|
if (choice != 'n' && choice != 'y') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (choice == 'y') {
|
|
|
|
Save(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// close window
|
|
|
|
PostEvent(new Event(EV_Remove), 0.0f);
|
|
|
|
|
|
|
|
// Fixed in OPM:
|
|
|
|
// in the original game, the control is not deactivated after removal,
|
|
|
|
// so the already heap-freed UINotepad instance still "reacts" to
|
|
|
|
// KeyEvents and CharEvents, creating a use-after-free scenario,
|
|
|
|
// causing crashes when debug heap is enabled
|
|
|
|
uWinMan.DeactivateCurrentControl();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
m_state = STATE_NONE;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case K_ESCAPE:
|
|
|
|
case K_TAB:
|
|
|
|
m_state = STATE_NONE;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isprint(ch)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_state == STATE_CONFIRMCLOSE) {
|
|
|
|
m_textinput.text = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
m_textinput.text += (char)ch;
|
|
|
|
return true;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::Draw(void)
|
|
|
|
{
|
|
|
|
UColor out = URed;
|
|
|
|
out.ScaleColor(0.5f);
|
|
|
|
out.ScaleAlpha(0.5f);
|
|
|
|
|
|
|
|
m_status->setBackgroundColor(out, true);
|
|
|
|
m_status->setForegroundColor(UHudColor);
|
|
|
|
|
|
|
|
str text;
|
|
|
|
switch (m_state) {
|
|
|
|
case STATE_GOTO_LINE:
|
|
|
|
text = "Goto line (tab cancels): " + m_textinput.text;
|
|
|
|
break;
|
|
|
|
case STATE_FIND_TEXT:
|
|
|
|
text = "Find text (tab cancels): " + m_textinput.text;
|
|
|
|
break;
|
|
|
|
case STATE_SAVE_AS:
|
|
|
|
text = "Save As (tab cancels): " + m_textinput.text;
|
|
|
|
break;
|
|
|
|
case STATE_CONFIRMCLOSE:
|
|
|
|
text = "Save " + m_filename + "? (yes/no/cancel) " + m_textinput.text;
|
|
|
|
break;
|
|
|
|
case STATE_TIMED_MESSAGE:
|
|
|
|
if (uid.time > m_timedmessage.die) {
|
|
|
|
m_state = STATE_NONE;
|
|
|
|
default:
|
|
|
|
m_status->setBackgroundColor(getBackgroundColor(), true);
|
|
|
|
m_status->setForegroundColor(getForegroundColor()); // Fixed in OPM
|
|
|
|
m_menu->setForegroundColor(getForegroundColor()); // Fixed in OPM
|
|
|
|
UIPoint2D selpoint = m_edit->getEndSelPoint();
|
|
|
|
text = va("Line %d, column %d", (int)selpoint.y + 1, (int)selpoint.x + 1);
|
|
|
|
} else {
|
|
|
|
m_status->setBackgroundColor(getBackgroundColor(), true);
|
|
|
|
m_status->setForegroundColor(URed);
|
|
|
|
text = m_timedmessage.text;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_status->setTitle(text);
|
|
|
|
UIFloatingWindow::Draw();
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-06-10 14:02:10 +02:00
|
|
|
void UINotepad::setFileName(const char *filename)
|
2016-03-27 11:49:47 +02:00
|
|
|
{
|
2024-06-10 14:02:10 +02:00
|
|
|
str text = str(filename) + " - Notepad";
|
|
|
|
setTitle(text);
|
|
|
|
m_filename = filename;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|