mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
Implement UINotepad
Also fix several original game bugs within, some cosmetical, some memory corrupting
This commit is contained in:
parent
4da5e26cdc
commit
9a35d34fc3
2 changed files with 587 additions and 163 deletions
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2015 the OpenMoHAA team
|
||||
Copyright (C) 2015-2024 the OpenMoHAA team
|
||||
|
||||
This file is part of OpenMoHAA source code.
|
||||
|
||||
|
@ -22,245 +22,667 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
|
||||
#include "ui_local.h"
|
||||
|
||||
CLASS_DECLARATION( UIMultiLineEdit, UINotepadEdit, NULL )
|
||||
{
|
||||
{ NULL, NULL }
|
||||
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 }
|
||||
};
|
||||
|
||||
bool UI_LoadNotepadFile( const char *filename )
|
||||
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 }
|
||||
};
|
||||
|
||||
#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 },
|
||||
{NULL, NULL },
|
||||
};
|
||||
|
||||
bool UI_LoadNotepadFile(const char *filename)
|
||||
{
|
||||
return false;
|
||||
UINotepad *uinp = new UINotepad();
|
||||
if (uinp->Create(NULL, UIRect2D(100.0f, 100.0f, 450.0f, 300.0f), filename)) {
|
||||
uWinMan.ActivateControl(uinp);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (uinp) {
|
||||
delete uinp;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
UINotepadEdit::UINotepadEdit()
|
||||
{
|
||||
// FIXME: stub
|
||||
m_notepad = NULL;
|
||||
}
|
||||
|
||||
void UINotepadEdit::CharEvent
|
||||
(
|
||||
int ch
|
||||
)
|
||||
|
||||
void UINotepadEdit::CharEvent(int ch)
|
||||
{
|
||||
// FIXME: stub
|
||||
if (!m_notepad->ProcessCharEvent(ch)) {
|
||||
UIMultiLineEdit::CharEvent(ch);
|
||||
}
|
||||
}
|
||||
|
||||
void UINotepadEdit::setNotepad
|
||||
(
|
||||
UINotepad *notepad
|
||||
)
|
||||
|
||||
void UINotepadEdit::setNotepad(UINotepad *notepad)
|
||||
{
|
||||
// FIXME: stub
|
||||
m_notepad = notepad;
|
||||
}
|
||||
|
||||
bool UINotepadEdit::GotoLine
|
||||
(
|
||||
int line
|
||||
)
|
||||
|
||||
bool UINotepadEdit::GotoLine(int line)
|
||||
{
|
||||
// FIXME: stub
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
|
||||
bool UINotepadEdit::FindText
|
||||
(
|
||||
const char *text,
|
||||
int offsetFromSel
|
||||
)
|
||||
|
||||
bool UINotepadEdit::FindText(const char *text, int offsetFromSel)
|
||||
{
|
||||
// FIXME: stub
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UINotepadEdit::MousePressed
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepadEdit::MousePressed(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
if (m_notepad->m_state != STATE_TIMED_MESSAGE) {
|
||||
m_notepad->m_state = STATE_NONE;
|
||||
}
|
||||
|
||||
UIMultiLineEdit::MouseDown(ev);
|
||||
}
|
||||
|
||||
CLASS_DECLARATION( UIFloatingWindow, UINotepad, NULL )
|
||||
{
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
UINotepad::UINotepad()
|
||||
{
|
||||
// FIXME: stub
|
||||
m_state = STATE_NONE;
|
||||
m_edit = NULL;
|
||||
m_menu = NULL;
|
||||
m_status = NULL;
|
||||
}
|
||||
|
||||
void UINotepad::TimeMessage
|
||||
(
|
||||
const char *message,
|
||||
int howlong
|
||||
)
|
||||
|
||||
UINotepad::~UINotepad()
|
||||
{
|
||||
// FIXME: stub
|
||||
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);
|
||||
// NOTE: `delete uipd` is intentionally missing here!
|
||||
// Since uipds created for this class have data fields
|
||||
// that contain pointers only to static Event objects,
|
||||
// the fields don't need to be cleaned up as they weren't
|
||||
// dynamically allocated in the first place.
|
||||
// See UINotepad::Create for reference.
|
||||
// However, FIXME: the uipds themselves should still
|
||||
// get cleaned up, but that doesn't happen yet.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UINotepad::OpenFile
|
||||
(
|
||||
const char *filename
|
||||
)
|
||||
|
||||
void UINotepad::TimeMessage(const char *message, int howlong)
|
||||
{
|
||||
// FIXME: stub
|
||||
return false;
|
||||
m_state = STATE_TIMED_MESSAGE;
|
||||
m_timedmessage.die = uid.time + howlong;
|
||||
m_timedmessage.text = message;
|
||||
}
|
||||
|
||||
bool UINotepad::Create
|
||||
(
|
||||
UIWidget *parent,
|
||||
UIRect2D& rect,
|
||||
const char *filename
|
||||
)
|
||||
|
||||
bool UINotepad::OpenFile(const char *filename)
|
||||
{
|
||||
// FIXME: stub
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
|
||||
void UINotepad::ChildSizeChanged
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
bool UINotepad::Create(UIWidget *parent, UIRect2D& rect, const char *filename)
|
||||
{
|
||||
// FIXME: stub
|
||||
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);
|
||||
|
||||
uipd = new uipopup_describe(NULL, UIP_SEPARATOR, NULL, NULL);
|
||||
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);
|
||||
|
||||
uipd = new uipopup_describe(NULL, UIP_SEPARATOR, NULL, NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
void UINotepad::SaveAs
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepad::ChildSizeChanged(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
m_menu->Realign();
|
||||
UISize2D menuSize = m_menu->getSize();
|
||||
UISize2D childSpaceSize = getChildSpace()->getSize();
|
||||
|
||||
UIRect2D frame(0.0f, menuSize.height, childSpaceSize.width, childSpaceSize.height - 20.0f - menuSize.height);
|
||||
|
||||
m_edit->setFrame(frame);
|
||||
}
|
||||
|
||||
void UINotepad::Save
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepad::SaveAs(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
m_state = STATE_SAVE_AS;
|
||||
m_textinput.text = "";
|
||||
}
|
||||
|
||||
void UINotepad::Open
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepad::Save(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
if (!m_filename.length()) {
|
||||
SaveAs(NULL);
|
||||
}
|
||||
|
||||
str filecontents;
|
||||
m_edit->getData(filecontents);
|
||||
m_edit->setChanged(false);
|
||||
|
||||
// FIXME: m_filename could be blank
|
||||
FS_WriteTextFile(m_filename, filecontents, filecontents.length());
|
||||
|
||||
str result = "Saved " + m_filename;
|
||||
TimeMessage(result, 3000);
|
||||
}
|
||||
|
||||
void UINotepad::OpenFile
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepad::Open(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
PickFile(m_filename, this, EV_Notepad_OpenFile);
|
||||
}
|
||||
|
||||
void UINotepad::ClosePressed
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepad::OpenFile(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
OpenFile(ev->GetString(1));
|
||||
}
|
||||
|
||||
void UINotepad::OnFind
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepad::ClosePressed(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void UINotepad::OnGoto
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepad::OnFind(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
m_state = STATE_FIND_TEXT;
|
||||
m_textinput.text = m_lastfind;
|
||||
}
|
||||
|
||||
void UINotepad::OnCopy
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepad::OnGoto(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
m_state = STATE_GOTO_LINE;
|
||||
m_textinput.text = "";
|
||||
}
|
||||
|
||||
void UINotepad::OnPaste
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepad::OnCopy(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
m_edit->CopySelection();
|
||||
}
|
||||
|
||||
void UINotepad::OnCut
|
||||
(
|
||||
Event *ev
|
||||
)
|
||||
|
||||
void UINotepad::OnPaste(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
m_edit->PasteSelection();
|
||||
}
|
||||
|
||||
bool UINotepad::ProcessControlEvents
|
||||
(
|
||||
int ch
|
||||
)
|
||||
|
||||
void UINotepad::OnCut(Event *ev)
|
||||
{
|
||||
// FIXME: stub
|
||||
return false;
|
||||
m_edit->CopySelection();
|
||||
m_edit->DeleteSelection();
|
||||
}
|
||||
|
||||
bool UINotepad::ProcessCharEvent
|
||||
(
|
||||
int ch
|
||||
)
|
||||
|
||||
bool UINotepad::ProcessControlEvents(int ch)
|
||||
{
|
||||
// FIXME: stub
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
|
||||
void UINotepad::Draw
|
||||
(
|
||||
void
|
||||
)
|
||||
|
||||
bool UINotepad::ProcessCharEvent(int ch)
|
||||
{
|
||||
// FIXME: stub
|
||||
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;
|
||||
}
|
||||
|
||||
void UINotepad::setFileName
|
||||
(
|
||||
const char *filename
|
||||
)
|
||||
|
||||
void UINotepad::Draw(void)
|
||||
{
|
||||
// FIXME: stub
|
||||
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();
|
||||
}
|
||||
|
||||
void UINotepad::setFileName(const char *filename)
|
||||
{
|
||||
str text = str(filename) + " - Notepad";
|
||||
setTitle(text);
|
||||
m_filename = filename;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue