|
|
|
@ -323,18 +323,16 @@ struct NetworkChatWindow : public Window {
|
|
|
|
|
* Find the next item of the list of things that can be auto-completed.
|
|
|
|
|
* @param item The current indexed item to return. This function can, and most
|
|
|
|
|
* likely will, alter item, to skip empty items in the arrays.
|
|
|
|
|
* @return Returns the char that matched to the index.
|
|
|
|
|
* @return Returns the view that matched to the index.
|
|
|
|
|
*/
|
|
|
|
|
const char *ChatTabCompletionNextItem(uint *item)
|
|
|
|
|
std::optional<std::string> ChatTabCompletionNextItem(uint *item)
|
|
|
|
|
{
|
|
|
|
|
static char chat_tab_temp_buffer[64];
|
|
|
|
|
|
|
|
|
|
/* First, try clients */
|
|
|
|
|
if (*item < MAX_CLIENT_SLOTS) {
|
|
|
|
|
/* Skip inactive clients */
|
|
|
|
|
for (NetworkClientInfo *ci : NetworkClientInfo::Iterate(*item)) {
|
|
|
|
|
*item = ci->index;
|
|
|
|
|
return ci->client_name.c_str();
|
|
|
|
|
return ci->client_name;
|
|
|
|
|
}
|
|
|
|
|
*item = MAX_CLIENT_SLOTS;
|
|
|
|
|
}
|
|
|
|
@ -346,12 +344,11 @@ struct NetworkChatWindow : public Window {
|
|
|
|
|
for (const Town *t : Town::Iterate(*item - MAX_CLIENT_SLOTS)) {
|
|
|
|
|
/* Get the town-name via the string-system */
|
|
|
|
|
SetDParam(0, t->index);
|
|
|
|
|
GetString(chat_tab_temp_buffer, STR_TOWN_NAME, lastof(chat_tab_temp_buffer));
|
|
|
|
|
return &chat_tab_temp_buffer[0];
|
|
|
|
|
return GetString(STR_TOWN_NAME);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -359,13 +356,14 @@ struct NetworkChatWindow : public Window {
|
|
|
|
|
* the word right from that as to complete. It also writes a \0 at the
|
|
|
|
|
* position of the space (if any). If nothing found, buf is returned.
|
|
|
|
|
*/
|
|
|
|
|
static char *ChatTabCompletionFindText(char *buf)
|
|
|
|
|
static std::string_view ChatTabCompletionFindText(std::string_view &buf)
|
|
|
|
|
{
|
|
|
|
|
char *p = strrchr(buf, ' ');
|
|
|
|
|
if (p == nullptr) return buf;
|
|
|
|
|
auto it = buf.find_last_of(' ');
|
|
|
|
|
if (it == std::string_view::npos) return buf;
|
|
|
|
|
|
|
|
|
|
*p = '\0';
|
|
|
|
|
return p + 1;
|
|
|
|
|
std::string_view res = buf.substr(it + 1);
|
|
|
|
|
buf.remove_suffix(res.size() + 1);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -373,46 +371,44 @@ struct NetworkChatWindow : public Window {
|
|
|
|
|
*/
|
|
|
|
|
void ChatTabCompletion()
|
|
|
|
|
{
|
|
|
|
|
static char _chat_tab_completion_buf[NETWORK_CHAT_LENGTH];
|
|
|
|
|
assert(this->message_editbox.text.max_bytes == lengthof(_chat_tab_completion_buf));
|
|
|
|
|
static std::string _chat_tab_completion_buf;
|
|
|
|
|
|
|
|
|
|
Textbuf *tb = &this->message_editbox.text;
|
|
|
|
|
size_t len, tb_len;
|
|
|
|
|
uint item;
|
|
|
|
|
char *tb_buf, *pre_buf;
|
|
|
|
|
const char *cur_name;
|
|
|
|
|
uint item = 0;
|
|
|
|
|
bool second_scan = false;
|
|
|
|
|
|
|
|
|
|
item = 0;
|
|
|
|
|
|
|
|
|
|
/* Copy the buffer so we can modify it without damaging the real data */
|
|
|
|
|
pre_buf = (_chat_tab_completion_active) ? stredup(_chat_tab_completion_buf) : stredup(tb->buf);
|
|
|
|
|
/* Create views, so we do not need to copy the data for now. */
|
|
|
|
|
std::string_view pre_buf = _chat_tab_completion_active ? std::string_view(_chat_tab_completion_buf) : std::string_view(tb->buf);
|
|
|
|
|
std::string_view tb_buf = ChatTabCompletionFindText(pre_buf);
|
|
|
|
|
|
|
|
|
|
tb_buf = ChatTabCompletionFindText(pre_buf);
|
|
|
|
|
tb_len = strlen(tb_buf);
|
|
|
|
|
/*
|
|
|
|
|
* Comparing pointers of the data, as both "Hi:<tab>" and "Hi: Hi:<tab>" will result in
|
|
|
|
|
* tb_buf and pre_buf being "Hi:", which would be equal in content but not in context.
|
|
|
|
|
*/
|
|
|
|
|
bool begin_of_line = tb_buf.data() == pre_buf.data();
|
|
|
|
|
|
|
|
|
|
while ((cur_name = ChatTabCompletionNextItem(&item)) != nullptr) {
|
|
|
|
|
std::optional<std::string> cur_item;
|
|
|
|
|
while ((cur_item = ChatTabCompletionNextItem(&item)).has_value()) {
|
|
|
|
|
std::string_view cur_name = cur_item.value();
|
|
|
|
|
item++;
|
|
|
|
|
|
|
|
|
|
if (_chat_tab_completion_active) {
|
|
|
|
|
/* We are pressing TAB again on the same name, is there another name
|
|
|
|
|
* that starts with this? */
|
|
|
|
|
if (!second_scan) {
|
|
|
|
|
size_t offset;
|
|
|
|
|
size_t length;
|
|
|
|
|
std::string_view view;
|
|
|
|
|
|
|
|
|
|
/* If we are completing at the begin of the line, skip the ': ' we added */
|
|
|
|
|
if (tb_buf == pre_buf) {
|
|
|
|
|
offset = 0;
|
|
|
|
|
length = (tb->bytes - 1) - 2;
|
|
|
|
|
if (begin_of_line) {
|
|
|
|
|
view = std::string_view(tb->buf, (tb->bytes - 1) - 2);
|
|
|
|
|
} else {
|
|
|
|
|
/* Else, find the place we are completing at */
|
|
|
|
|
offset = strlen(pre_buf) + 1;
|
|
|
|
|
length = (tb->bytes - 1) - offset;
|
|
|
|
|
size_t offset = pre_buf.size() + 1;
|
|
|
|
|
view = std::string_view(tb->buf + offset, (tb->bytes - 1) - offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compare if we have a match */
|
|
|
|
|
if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
|
|
|
|
|
if (cur_name == view) second_scan = true;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
@ -420,21 +416,19 @@ struct NetworkChatWindow : public Window {
|
|
|
|
|
/* Now any match we make on _chat_tab_completion_buf after this, is perfect */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = strlen(cur_name);
|
|
|
|
|
if (tb_len < len && StrStartsWith(cur_name, tb_buf)) {
|
|
|
|
|
if (tb_buf.size() < cur_name.size() && StrStartsWith(cur_name, tb_buf)) {
|
|
|
|
|
/* Save the data it was before completion */
|
|
|
|
|
if (!second_scan) seprintf(_chat_tab_completion_buf, lastof(_chat_tab_completion_buf), "%s", tb->buf);
|
|
|
|
|
if (!second_scan) _chat_tab_completion_buf = tb->buf;
|
|
|
|
|
_chat_tab_completion_active = true;
|
|
|
|
|
|
|
|
|
|
/* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
|
|
|
|
|
if (pre_buf == tb_buf) {
|
|
|
|
|
if (begin_of_line) {
|
|
|
|
|
this->message_editbox.text.Assign(fmt::format("{}: ", cur_name));
|
|
|
|
|
} else {
|
|
|
|
|
this->message_editbox.text.Assign(fmt::format("{} {}", pre_buf, cur_name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->SetDirty();
|
|
|
|
|
free(pre_buf);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -446,7 +440,6 @@ struct NetworkChatWindow : public Window {
|
|
|
|
|
|
|
|
|
|
this->SetDirty();
|
|
|
|
|
}
|
|
|
|
|
free(pre_buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) override
|
|
|
|
|