Fix console input not scrolling when you hit the side of the screen

- Also changed the console input buffer into a struct instead of a
  frankenstein buffer.
This commit is contained in:
Marisa Heit 2016-11-11 22:31:49 -06:00
parent fd31c84745
commit 3926ca13b0
4 changed files with 340 additions and 289 deletions

View file

@ -100,7 +100,7 @@ extern bool advancedemo;
extern FBaseCVar *CVars;
extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE];
int ConCols, PhysRows;
unsigned ConCols, PhysRows;
int ConWidth;
bool vidactive = false;
bool cursoron = false;
@ -112,7 +112,6 @@ constate_e ConsoleState = c_up;
static int TopLine, InsertLine;
static void ClearConsole ();
static void C_PasteText(FString clip, BYTE *buffer, int len);
struct GameAtExit
{
@ -139,27 +138,9 @@ static bool ConsoleDrawing;
static char *work = NULL;
static int worklen = 0;
struct History
{
struct History *Older;
struct History *Newer;
char String[1];
};
// CmdLine[0] = # of chars on command line
// CmdLine[1] = cursor position
// CmdLine[2+] = command line (max 255 chars + NULL)
// CmdLine[259]= offset from beginning of cmdline to display
static BYTE CmdLine[260];
#define MAXHISTSIZE 50
static struct History *HistHead = NULL, *HistTail = NULL, *HistPos = NULL;
static int HistSize;
CVAR (Float, con_notifytime, 3.f, CVAR_ARCHIVE)
CVAR (Bool, con_centernotify, false, CVAR_ARCHIVE)
CUSTOM_CVAR (Int, con_scaletext, 1, CVAR_ARCHIVE) // Scale notify text at high resolutions?
CVAR(Float, con_notifytime, 3.f, CVAR_ARCHIVE)
CVAR(Bool, con_centernotify, false, CVAR_ARCHIVE)
CUSTOM_CVAR(Int, con_scaletext, 1, CVAR_ARCHIVE) // Scale notify text at high resolutions?
{
if (self < 0) self = 0;
if (self > 3) self = 3;
@ -172,10 +153,16 @@ CUSTOM_CVAR(Int, con_scale, 0, CVAR_ARCHIVE)
int active_con_scale()
{
if (con_scale == 0)
return uiscale;
else
return con_scale;
int scale = con_scale;
if (scale == 0)
{
scale = uiscale;
if (scale == 0)
{
scale = CleanXfac;
}
}
return scale;
}
int active_con_scaletext()
@ -197,7 +184,195 @@ CUSTOM_CVAR(Float, con_alpha, 0.75f, CVAR_ARCHIVE)
}
// Command to run when Ctrl-D is pressed at start of line
CVAR (String, con_ctrl_d, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(String, con_ctrl_d, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
struct History
{
struct History *Older;
struct History *Newer;
FString String;
};
struct FCommandBuffer
{
FString Text; // The actual command line text
unsigned CursorPos;
unsigned StartPos; // First character to display
FCommandBuffer()
{
CursorPos = StartPos = 0;
}
FCommandBuffer(const FCommandBuffer &o)
{
Text = o.Text;
CursorPos = o.CursorPos;
StartPos = o.StartPos;
}
void Draw(int x, int y, int scale, bool cursor)
{
if (scale == 1)
{
screen->DrawChar(ConFont, CR_ORANGE, x, y, '\x1c', TAG_DONE);
screen->DrawText(ConFont, CR_ORANGE, x + ConFont->GetCharWidth(0x1c), y,
&Text[StartPos], TAG_DONE);
if (cursor)
{
screen->DrawChar(ConFont, CR_YELLOW,
x + ConFont->GetCharWidth(0x1c) + (CursorPos - StartPos) * ConFont->GetCharWidth(0xb),
y, '\xb', TAG_DONE);
}
}
else
{
screen->DrawChar(ConFont, CR_ORANGE, x, y, '\x1c',
DTA_VirtualWidth, screen->GetWidth() / scale,
DTA_VirtualHeight, screen->GetHeight() / scale,
DTA_KeepRatio, true, TAG_DONE);
screen->DrawText(ConFont, CR_ORANGE, x + ConFont->GetCharWidth(0x1c), y,
&Text[StartPos],
DTA_VirtualWidth, screen->GetWidth() / scale,
DTA_VirtualHeight, screen->GetHeight() / scale,
DTA_KeepRatio, true, TAG_DONE);
if (cursor)
{
screen->DrawChar(ConFont, CR_YELLOW,
x + ConFont->GetCharWidth(0x1c) + (CursorPos - StartPos) * ConFont->GetCharWidth(0xb),
y, '\xb',
DTA_VirtualWidth, screen->GetWidth() / scale,
DTA_VirtualHeight, screen->GetHeight() / scale,
DTA_KeepRatio, true, TAG_DONE);
}
}
}
void MakeStartPosGood()
{
int n = StartPos;
unsigned cols = ConCols / active_con_scale();
if (StartPos >= Text.Len())
{ // Start of visible line is beyond end of line
n = CursorPos - cols + 2;
}
if ((CursorPos - StartPos) >= cols - 2)
{ // The cursor is beyond the visible part of the line
n = CursorPos - cols + 2;
}
if (StartPos > CursorPos)
{ // The cursor is in front of the visible part of the line
n = CursorPos;
}
StartPos = MAX(0, n);
}
void CursorStart()
{
CursorPos = 0;
StartPos = 0;
}
void CursorEnd()
{
CursorPos = Text.Len();
StartPos = 0;
MakeStartPosGood();
}
void CursorLeft()
{
if (CursorPos > 0)
{
CursorPos--;
MakeStartPosGood();
}
}
void CursorRight()
{
if (CursorPos < Text.Len())
{
CursorPos++;
MakeStartPosGood();
}
}
void DeleteLeft()
{
if (CursorPos > 0)
{
Text.Remove(CursorPos - 1, 1);
CursorPos--;
MakeStartPosGood();
}
}
void DeleteRight()
{
if (CursorPos < Text.Len())
{
Text.Remove(CursorPos, 1);
MakeStartPosGood();
}
}
void AddChar(int character)
{
///FIXME: Not Unicode-aware
if (CursorPos == Text.Len())
{
Text += char(character);
}
else
{
char foo = char(character);
Text.Insert(CursorPos, &foo, 1);
}
CursorPos++;
MakeStartPosGood();
}
void AddString(FString clip)
{
if (clip.IsNotEmpty())
{
// Only paste the first line.
long brk = clip.IndexOfAny("\r\n\b");
if (brk >= 0)
{
clip.Truncate(brk);
}
if (Text.IsEmpty())
{
Text = clip;
}
else
{
Text.Insert(CursorPos, clip);
}
CursorPos += clip.Len();
MakeStartPosGood();
}
}
void SetString(FString str)
{
Text = str;
CursorPos = Text.Len();
MakeStartPosGood();
}
};
static FCommandBuffer CmdLine;
#define MAXHISTSIZE 50
static struct History *HistHead = NULL, *HistTail = NULL, *HistPos = NULL;
static int HistSize;
#define NUMNOTIFIES 4
#define NOTIFYFADETIME 6
@ -860,8 +1035,6 @@ void C_DrawConsole (bool hw2d)
int lines, left, offset;
int textScale = active_con_scale();
if (textScale == 0)
textScale = CleanXfac;
left = LEFTMARGIN;
lines = (ConBottom/textScale-ConFont->GetHeight()*2)/ConFont->GetHeight();
@ -1037,44 +1210,8 @@ void C_DrawConsole (bool hw2d)
{
// Make a copy of the command line, in case an input event is handled
// while we draw the console and it changes.
CmdLine[2+CmdLine[0]] = 0;
FString command((char *)&CmdLine[2+CmdLine[259]]);
int cursorpos = CmdLine[1] - CmdLine[259];
if (textScale == 1)
{
screen->DrawChar(ConFont, CR_ORANGE, left, bottomline, '\x1c', TAG_DONE);
screen->DrawText(ConFont, CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline,
command, TAG_DONE);
if (cursoron)
{
screen->DrawChar(ConFont, CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb),
bottomline, '\xb', TAG_DONE);
}
}
else
{
screen->DrawChar(ConFont, CR_ORANGE, left, bottomline, '\x1c',
DTA_VirtualWidth, screen->GetWidth() / textScale,
DTA_VirtualHeight, screen->GetHeight() / textScale,
DTA_KeepRatio, true, TAG_DONE);
screen->DrawText(ConFont, CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline,
command,
DTA_VirtualWidth, screen->GetWidth() / textScale,
DTA_VirtualHeight, screen->GetHeight() / textScale,
DTA_KeepRatio, true, TAG_DONE);
if (cursoron)
{
screen->DrawChar(ConFont, CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb),
bottomline, '\xb',
DTA_VirtualWidth, screen->GetWidth() / textScale,
DTA_VirtualHeight, screen->GetHeight() / textScale,
DTA_KeepRatio, true, TAG_DONE);
}
}
FCommandBuffer command(CmdLine);
command.Draw(left, bottomline, textScale, cursoron);
}
if (RowAdjust && ConBottom >= ConFont->GetHeight()*7/2)
{
@ -1146,35 +1283,8 @@ void C_HideConsole ()
}
}
static void makestartposgood ()
static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
{
int n;
int pos = CmdLine[259];
int curs = CmdLine[1];
int len = CmdLine[0];
n = pos;
if (pos >= len)
{ // Start of visible line is beyond end of line
n = curs - ConCols + 2;
}
if ((curs - pos) >= ConCols - 2)
{ // The cursor is beyond the visible part of the line
n = curs - ConCols + 2;
}
if (pos > curs)
{ // The cursor is in front of the visible part of the line
n = curs;
}
if (n < 0)
n = 0;
CmdLine[259] = n;
}
static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
{
int i;
int data1 = ev->data1;
switch (ev->subtype)
@ -1184,29 +1294,8 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
case EV_GUI_Char:
// Add keypress to command line
if (buffer[0] < len)
{
if (buffer[1] == buffer[0])
{
buffer[buffer[0] + 2] = BYTE(ev->data1);
}
else
{
char *c, *e;
e = (char *)&buffer[buffer[0] + 1];
c = (char *)&buffer[buffer[1] + 2];
for (; e >= c; e--)
*(e + 1) = *e;
*c = char(ev->data1);
}
buffer[0]++;
buffer[1]++;
makestartposgood ();
HistPos = NULL;
}
buffer.AddChar(ev->data1);
HistPos = NULL;
TabbedLast = false;
TabbedList = false;
break;
@ -1289,7 +1378,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
}
else
{ // Move cursor to start of line
buffer[1] = buffer[len+4] = 0;
buffer.CursorStart();
}
break;
@ -1300,66 +1389,30 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
}
else
{ // Move cursor to end of line
buffer[1] = buffer[0];
makestartposgood ();
buffer.CursorEnd();
}
break;
case GK_LEFT:
// Move cursor left one character
if (buffer[1])
{
buffer[1]--;
makestartposgood ();
}
buffer.CursorLeft();
break;
case GK_RIGHT:
// Move cursor right one character
if (buffer[1] < buffer[0])
{
buffer[1]++;
makestartposgood ();
}
buffer.CursorRight();
break;
case '\b':
// Erase character to left of cursor
if (buffer[0] && buffer[1])
{
char *c, *e;
e = (char *)&buffer[buffer[0] + 2];
c = (char *)&buffer[buffer[1] + 2];
for (; c < e; c++)
*(c - 1) = *c;
buffer[0]--;
buffer[1]--;
if (buffer[len+4])
buffer[len+4]--;
makestartposgood ();
}
buffer.DeleteLeft();
TabbedLast = false;
TabbedList = false;
break;
case GK_DEL:
// Erase character under cursor
if (buffer[1] < buffer[0])
{
char *c, *e;
e = (char *)&buffer[buffer[0] + 2];
c = (char *)&buffer[buffer[1] + 3];
for (; c < e; c++)
*(c - 1) = *c;
buffer[0]--;
makestartposgood ();
}
buffer.DeleteRight();
TabbedLast = false;
TabbedList = false;
break;
@ -1377,10 +1430,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
if (HistPos)
{
strcpy ((char *)&buffer[2], HistPos->String);
buffer[0] = buffer[1] = (BYTE)strlen ((char *)&buffer[2]);
buffer[len+4] = 0;
makestartposgood();
buffer.SetString(HistPos->String);
}
TabbedLast = false;
@ -1392,17 +1442,13 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
if (HistPos && HistPos->Newer)
{
HistPos = HistPos->Newer;
strcpy ((char *)&buffer[2], HistPos->String);
buffer[0] = buffer[1] = (BYTE)strlen ((char *)&buffer[2]);
buffer.SetString(HistPos->String);
}
else
{
HistPos = NULL;
buffer[0] = buffer[1] = 0;
buffer.SetString("");
}
buffer[len+4] = 0;
makestartposgood();
TabbedLast = false;
TabbedList = false;
break;
@ -1410,24 +1456,19 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
case 'X':
if (ev->data3 & GKM_CTRL)
{
buffer[1] = buffer[0] = 0;
buffer.SetString("");
TabbedLast = TabbedList = false;
}
break;
case 'D':
if (ev->data3 & GKM_CTRL && buffer[0] == 0)
if (ev->data3 & GKM_CTRL && buffer.Text.Len() == 0)
{ // Control-D pressed on an empty line
int replen = (int)strlen (con_ctrl_d);
if (replen == 0)
if (strlen(con_ctrl_d) == 0)
{
break; // Replacement is empty, so do nothing
if (replen > len)
replen = len;
memcpy (&buffer[2], con_ctrl_d, replen);
buffer[0] = buffer[1] = replen;
}
buffer.SetString(*con_ctrl_d);
}
else
{
@ -1438,16 +1479,16 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
case '\r':
// Execute command line (ENTER)
buffer[2 + buffer[0]] = 0;
buffer.Text.StripLeftRight();
Printf(127, TEXTCOLOR_WHITE "]%s\n", buffer.Text.GetChars());
AddCommandString(buffer.Text.LockBuffer());
buffer.Text.UnlockBuffer();
for (i = 0; i < buffer[0] && isspace(buffer[2+i]); ++i)
{
}
if (i == buffer[0])
if (buffer.Text.Len() == 0)
{
// Command line is empty, so do nothing to the history
}
else if (HistHead && stricmp (HistHead->String, (char *)&buffer[2]) == 0)
else if (HistHead && HistHead->String.CompareNoCase(buffer.Text) == 0)
{
// Command line was the same as the previous one,
// so leave the history list alone
@ -1458,9 +1499,8 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
// or there is nothing in the history list,
// so add it to the history list.
History *temp = (History *)M_Malloc (sizeof(struct History) + buffer[0]);
strcpy (temp->String, (char *)&buffer[2]);
History *temp = new History;
temp->String = buffer.Text;
temp->Older = HistHead;
if (HistHead)
{
@ -1477,7 +1517,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
if (HistSize == MAXHISTSIZE)
{
HistTail = HistTail->Newer;
M_Free (HistTail->Older);
delete HistTail->Older;
HistTail->Older = NULL;
}
else
@ -1486,9 +1526,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
}
}
HistPos = NULL;
Printf (127, TEXTCOLOR_WHITE "]%s\n", &buffer[2]);
buffer[0] = buffer[1] = buffer[len+4] = 0;
AddCommandString ((char *)&buffer[2]);
buffer.SetString("");
TabbedLast = false;
TabbedList = false;
break;
@ -1514,7 +1552,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
}
else
{
buffer[0] = buffer[1] = buffer[len+4] = 0;
buffer.SetString("");
HistPos = NULL;
C_ToggleConsole ();
}
@ -1532,15 +1570,15 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
{
if (data1 == 'C')
{ // copy to clipboard
if (buffer[0] > 0)
if (buffer.Text.IsNotEmpty())
{
buffer[2 + buffer[0]] = 0;
I_PutInClipboard ((char *)&buffer[2]);
I_PutInClipboard(buffer.Text);
}
}
else
{ // paste from clipboard
C_PasteText(I_GetFromClipboard(false), buffer, len);
buffer.AddString(I_GetFromClipboard(false));
HistPos = NULL;
}
break;
}
@ -1550,7 +1588,8 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
#ifdef __unix__
case EV_GUI_MButtonDown:
C_PasteText(I_GetFromClipboard(true), buffer, len);
buffer.AddString(I_GetFromClipboard(true));
HistPos = NULL;
break;
#endif
}
@ -1560,36 +1599,6 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
return true;
}
static void C_PasteText(FString clip, BYTE *buffer, int len)
{
if (clip.IsNotEmpty())
{
// Only paste the first line.
long brk = clip.IndexOfAny("\r\n\b");
int cliplen = brk >= 0 ? brk : (int)clip.Len();
// Make sure there's room for the whole thing.
if (buffer[0] + cliplen > len)
{
cliplen = len - buffer[0];
}
if (cliplen > 0)
{
if (buffer[1] < buffer[0])
{
memmove (&buffer[2 + buffer[1] + cliplen],
&buffer[2 + buffer[1]], buffer[0] - buffer[1]);
}
memcpy (&buffer[2 + buffer[1]], clip, cliplen);
buffer[0] += cliplen;
buffer[1] += cliplen;
makestartposgood ();
HistPos = NULL;
}
}
}
bool C_Responder (event_t *ev)
{
if (ev->type != EV_GUI_Event ||
@ -1600,7 +1609,7 @@ bool C_Responder (event_t *ev)
return false;
}
return C_HandleKey (ev, CmdLine, 255);
return C_HandleKey(ev, CmdLine);
}
CCMD (history)
@ -1793,7 +1802,7 @@ static int FindDiffPoint (FName name1, const char *str2)
static void C_TabComplete (bool goForward)
{
int i;
unsigned i;
int diffpoint;
if (!TabbedLast)
@ -1801,25 +1810,20 @@ static void C_TabComplete (bool goForward)
bool cancomplete;
// Skip any spaces at beginning of command line
if (CmdLine[2] == ' ')
for (i = 0; i < CmdLine.Text.Len(); ++i)
{
for (i = 0; i < CmdLine[0]; i++)
if (CmdLine[2+i] != ' ')
break;
TabStart = i + 2;
if (CmdLine.Text[i] != ' ')
break;
}
else
{
TabStart = 2;
if (i == CmdLine.Text.Len())
{ // Line was nothing but spaces
return;
}
TabStart = i;
if (TabStart == CmdLine[0] + 2)
return; // Line was nothing but spaces
TabSize = CmdLine.Text.Len() - TabStart;
TabSize = CmdLine[0] - TabStart + 2;
if (!FindTabCommand ((char *)(CmdLine + TabStart), &TabPos, TabSize))
if (!FindTabCommand(&CmdLine.Text[TabStart], &TabPos, TabSize))
return; // No initial matches
// Show a list of possible completions, if more than one.
@ -1842,7 +1846,7 @@ static void C_TabComplete (bool goForward)
{ // Find the last matching tab, then go one past it.
while (++TabPos < (int)TabCommands.Size())
{
if (FindDiffPoint (TabCommands[TabPos].TabName, (char *)(CmdLine + TabStart)) < TabSize)
if (FindDiffPoint(TabCommands[TabPos].TabName, &CmdLine.Text[TabStart]) < TabSize)
{
break;
}
@ -1859,27 +1863,26 @@ static void C_TabComplete (bool goForward)
(!goForward && --TabPos < 0))
{
TabbedLast = false;
CmdLine[0] = CmdLine[1] = TabSize;
CmdLine.Text.Truncate(TabSize);
}
else
{
diffpoint = FindDiffPoint (TabCommands[TabPos].TabName, (char *)(CmdLine + TabStart));
diffpoint = FindDiffPoint(TabCommands[TabPos].TabName, &CmdLine.Text[TabStart]);
if (diffpoint < TabSize)
{
// No more matches
TabbedLast = false;
CmdLine[0] = CmdLine[1] = TabSize + TabStart - 2;
CmdLine.Text.Truncate(TabSize - TabStart);
}
else
{
strcpy ((char *)(CmdLine + TabStart), TabCommands[TabPos].TabName.GetChars());
CmdLine[0] = CmdLine[1] = (BYTE)strlen ((char *)(CmdLine + 2)) + 1;
CmdLine[CmdLine[0] + 1] = ' ';
{
CmdLine.Text.Truncate(TabStart);
CmdLine.Text << TabCommands[TabPos].TabName << ' ';
}
}
makestartposgood ();
CmdLine.CursorPos = CmdLine.Text.Len();
CmdLine.MakeStartPosGood();
}
static bool C_TabCompleteList ()
@ -1893,7 +1896,7 @@ static bool C_TabCompleteList ()
for (i = TabPos; i < (int)TabCommands.Size(); ++i)
{
if (FindDiffPoint (TabCommands[i].TabName, (char *)(CmdLine + TabStart)) < TabSize)
if (FindDiffPoint (TabCommands[i].TabName, &CmdLine.Text[TabStart]) < TabSize)
{
break;
}
@ -1918,7 +1921,7 @@ static bool C_TabCompleteList ()
{
size_t x = 0;
maxwidth += 3;
Printf (TEXTCOLOR_BLUE "Completions for %s:\n", CmdLine+2);
Printf (TEXTCOLOR_BLUE "Completions for %s:\n", CmdLine.Text.GetChars());
for (i = TabPos; nummatches > 0; ++i, --nummatches)
{
// [Dusk] Print console commands blue, CVars green, aliases red.
@ -1936,7 +1939,7 @@ static bool C_TabCompleteList ()
Printf ("%s%-*s", colorcode, int(maxwidth), TabCommands[i].TabName.GetChars());
x += maxwidth;
if (x > ConCols - maxwidth)
if (x > ConCols / active_con_scale() - maxwidth)
{
x = 0;
Printf ("\n");
@ -1950,9 +1953,9 @@ static bool C_TabCompleteList ()
if (TabSize != commonsize)
{
TabSize = commonsize;
strncpy ((char *)CmdLine + TabStart, TabCommands[TabPos].TabName.GetChars(), commonsize);
CmdLine[0] = TabStart + commonsize - 2;
CmdLine[1] = CmdLine[0];
CmdLine.Text.Truncate(TabStart);
CmdLine.Text.AppendCStrPart(TabCommands[TabPos].TabName.GetChars(), commonsize);
CmdLine.CursorPos = CmdLine.Text.Len();
}
return false;
}

View file

@ -877,8 +877,6 @@ void DFrameBuffer::DrawRateStuff ()
int rate_x;
int textScale = active_con_scale();
if (textScale == 0)
textScale = CleanXfac;
chars = mysnprintf (fpsbuff, countof(fpsbuff), "%2u ms (%3u fps)", howlong, LastCount);
rate_x = Width / textScale - ConFont->StringWidth(&fpsbuff[0]);

View file

@ -343,28 +343,74 @@ FString &FString::operator += (char tail)
FString &FString::AppendCStrPart (const char *tail, size_t tailLen)
{
size_t len1 = Len();
ReallocBuffer (len1 + tailLen);
StrCopy (Chars + len1, tail, tailLen);
if (tailLen > 0)
{
size_t len1 = Len();
ReallocBuffer(len1 + tailLen);
StrCopy(Chars + len1, tail, tailLen);
}
return *this;
}
FString &FString::CopyCStrPart(const char *tail, size_t tailLen)
{
ReallocBuffer(tailLen);
StrCopy(Chars, tail, tailLen);
if (tailLen > 0)
{
ReallocBuffer(tailLen);
StrCopy(Chars, tail, tailLen);
}
else
{
Data()->Release();
NullString.RefCount++;
Chars = &NullString.Nothing[0];
}
return *this;
}
void FString::Truncate(long newlen)
{
if (newlen >= 0 && newlen < (long)Len())
if (newlen <= 0)
{
Data()->Release();
NullString.RefCount++;
Chars = &NullString.Nothing[0];
}
else if (newlen < (long)Len())
{
ReallocBuffer (newlen);
Chars[newlen] = '\0';
}
}
void FString::Remove(size_t index, size_t remlen)
{
if (index < Len())
{
if (index + remlen >= Len())
{
Truncate((long)index);
}
else
{
remlen = Len() - remlen < remlen ? Len() - remlen : remlen;
if (Data()->RefCount == 1)
{ // Can do this in place
memmove(Chars + index, Chars + index + remlen, Len() - index - remlen);
Data()->Len -= remlen;
}
else
{ // Must do it in a copy
FStringData *old = Data();
AllocBuffer(old->Len - remlen);
StrCopy(Chars, old->Chars(), index);
StrCopy(Chars + index, old->Chars() + index + remlen, old->Len - index - remlen);
old->Release();
}
}
}
}
FString FString::Left (size_t numChars) const
{
size_t len = Len();
@ -768,25 +814,28 @@ void FString::Insert (size_t index, const char *instr)
void FString::Insert (size_t index, const char *instr, size_t instrlen)
{
size_t mylen = Len();
if (index > mylen)
if (instrlen > 0)
{
index = mylen;
}
if (Data()->RefCount <= 1)
{
ReallocBuffer (mylen + instrlen);
memmove (Chars + index + instrlen, Chars + index, (mylen - index + 1)*sizeof(char));
memcpy (Chars + index, instr, instrlen*sizeof(char));
}
else
{
FStringData *old = Data();
AllocBuffer (mylen + instrlen);
StrCopy (Chars, old->Chars(), index);
StrCopy (Chars + index, instr, instrlen);
StrCopy (Chars + index + instrlen, old->Chars() + index, mylen - index);
old->Release();
size_t mylen = Len();
if (index >= mylen)
{
AppendCStrPart(instr, instrlen);
}
else if (Data()->RefCount <= 1)
{
ReallocBuffer(mylen + instrlen);
memmove(Chars + index + instrlen, Chars + index, (mylen - index + 1) * sizeof(char));
memcpy(Chars + index, instr, instrlen * sizeof(char));
}
else
{
FStringData *old = Data();
AllocBuffer(mylen + instrlen);
StrCopy(Chars, old->Chars(), index);
StrCopy(Chars + index, instr, instrlen);
StrCopy(Chars + index + instrlen, old->Chars() + index, mylen - index);
old->Release();
}
}
}

View file

@ -268,6 +268,7 @@ public:
bool IsNotEmpty() const { return Len() != 0; }
void Truncate (long newlen);
void Remove(size_t index, size_t remlen);
int Compare (const FString &other) const { return strcmp (Chars, other.Chars); }
int Compare (const char *other) const { return strcmp (Chars, other); }