diff options
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/network.cpp | 2 | ||||
-rw-r--r-- | src/network/network.h | 2 | ||||
-rw-r--r-- | src/network/network_chat_gui.cpp | 487 | ||||
-rw-r--r-- | src/network/network_func.h | 5 | ||||
-rw-r--r-- | src/network/network_gui.cpp | 248 |
5 files changed, 495 insertions, 249 deletions
diff --git a/src/network/network.cpp b/src/network/network.cpp index 437023636..54f6dd1b4 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -230,7 +230,7 @@ void CDECL NetworkTextMessage(NetworkAction action, ConsoleColour color, bool se DebugDumpCommands("ddc:cmsg:%d;%d;%s\n", _date, _date_fract, message); IConsolePrintF(color, "%s", message); - AddChatMessage(color, duration, "%s", message); + NetworkAddChatMessage(color, duration, "%s", message); } // Calculate the frame-lag of a client diff --git a/src/network/network.h b/src/network/network.h index 8de34f112..9c913ceb6 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -11,6 +11,7 @@ void NetworkStartUp(); void NetworkShutDown(); +void NetworkDrawChatMessage(); extern bool _networking; ///< are we in networking mode? extern bool _network_server; ///< network-server is active @@ -23,6 +24,7 @@ extern bool _is_network_server; ///< Does this client wants to be a network-ser static inline void NetworkStartUp() {} static inline void NetworkShutDown() {} +static inline void NetworkDrawChatMessage() {} #define _networking 0 #define _network_server 0 diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp new file mode 100644 index 000000000..2d4289ffb --- /dev/null +++ b/src/network/network_chat_gui.cpp @@ -0,0 +1,487 @@ +/* $Id$ */ + +/** @file network_chat_gui.cpp GUI for handling chat messages. */ + +#include <stdarg.h> /* va_list */ + +#ifdef ENABLE_NETWORK + +#include "../stdafx.h" +#include "network.h" +#include "network_type.h" +#include "../date_func.h" +#include "../gfx_func.h" +#include "../strings_func.h" +#include "../blitter/factory.hpp" +#include "../console_func.h" +#include "../video/video_driver.hpp" +#include "../table/sprites.h" +#include "../window_gui.h" +#include "../textbuf_gui.h" +#include "../querystring_gui.h" +#include "../town.h" +#include "../window_func.h" +#include "network_internal.h" +#include "network_client.h" + +#include "table/strings.h" + +enum { + MAX_CHAT_MESSAGES = 10, +}; + +struct ChatMessage { + char message[NETWORK_CHAT_LENGTH]; + uint16 color; + Date end_date; +}; + +/* used for chat window */ +static ChatMessage _chatmsg_list[MAX_CHAT_MESSAGES]; +static bool _chatmessage_dirty = false; +static bool _chatmessage_visible = false; +static bool _chat_tab_completion_active; + +/* The chatbox grows from the bottom so the coordinates are pixels from + * the left and pixels from the bottom. The height is the maximum height */ +static const PointDimension _chatmsg_box = {10, 30, 500, 150}; +static uint8 _chatmessage_backup[150 * 500 * 6]; // (height * width) + +static inline uint GetChatMessageCount() +{ + uint i = 0; + for (; i < MAX_CHAT_MESSAGES; i++) { + if (_chatmsg_list[i].message[0] == '\0') break; + } + + return i; +} + +/** + * Add a text message to the 'chat window' to be shown + * @param color The colour this message is to be shown in + * @param duration The duration of the chat message in game-days + * @param message message itself in printf() style + */ +void CDECL NetworkAddChatMessage(uint16 color, uint8 duration, const char *message, ...) +{ + char buf[NETWORK_CHAT_LENGTH]; + const char *bufp; + va_list va; + uint msg_count; + uint16 lines; + + va_start(va, message); + vsnprintf(buf, lengthof(buf), message, va); + va_end(va); + + + Utf8TrimString(buf, NETWORK_CHAT_LENGTH); + + /* Force linebreaks for strings that are too long */ + lines = GB(FormatStringLinebreaks(buf, _chatmsg_box.width - 8), 0, 16) + 1; + if (lines >= MAX_CHAT_MESSAGES) return; + + msg_count = GetChatMessageCount(); + /* We want to add more chat messages than there is free space for, remove 'old' */ + if (lines > MAX_CHAT_MESSAGES - msg_count) { + int i = lines - (MAX_CHAT_MESSAGES - msg_count); + memmove(&_chatmsg_list[0], &_chatmsg_list[i], sizeof(_chatmsg_list[0]) * (msg_count - i)); + msg_count = MAX_CHAT_MESSAGES - lines; + } + + for (bufp = buf; lines != 0; lines--) { + ChatMessage *cmsg = &_chatmsg_list[msg_count++]; + ttd_strlcpy(cmsg->message, bufp, sizeof(cmsg->message)); + + /* The default colour for a message is player colour. Replace this with + * white for any additional lines */ + cmsg->color = (bufp == buf && color & IS_PALETTE_COLOR) ? color : (0x1D - 15) | IS_PALETTE_COLOR; + cmsg->end_date = _date + duration; + + bufp += strlen(bufp) + 1; // jump to 'next line' in the formatted string + } + + _chatmessage_dirty = true; +} + +void NetworkInitChatMessage() +{ + for (uint i = 0; i < MAX_CHAT_MESSAGES; i++) { + _chatmsg_list[i].message[0] = '\0'; + } +} + +/** Hide the chatbox */ +void NetworkUndrawChatMessage() +{ + if (_chatmessage_visible) { + Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter(); + /* Sometimes we also need to hide the cursor + * This is because both textmessage and the cursor take a shot of the + * screen before drawing. + * Now the textmessage takes his shot and paints his data before the cursor + * does, so in the shot of the cursor is the screen-data of the textmessage + * included when the cursor hangs somewhere over the textmessage. To + * avoid wrong repaints, we undraw the cursor in that case, and everything + * looks nicely ;) + * (and now hope this story above makes sense to you ;)) + */ + + if (_cursor.visible) { + if (_cursor.draw_pos.x + _cursor.draw_size.x >= _chatmsg_box.x && + _cursor.draw_pos.x <= _chatmsg_box.x + _chatmsg_box.width && + _cursor.draw_pos.y + _cursor.draw_size.y >= _screen.height - _chatmsg_box.y - _chatmsg_box.height && + _cursor.draw_pos.y <= _screen.height - _chatmsg_box.y) { + UndrawMouseCursor(); + } + } + + int x = _chatmsg_box.x; + int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height; + int width = _chatmsg_box.width; + int height = _chatmsg_box.height; + if (y < 0) { + height = max(height + y, min(_chatmsg_box.height, _screen.height)); + y = 0; + } + if (x + width >= _screen.width) { + width = _screen.width - x; + } + if (width <= 0 || height <= 0) return; + + _chatmessage_visible = false; + /* Put our 'shot' back to the screen */ + blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height); + /* And make sure it is updated next time */ + _video_driver->MakeDirty(x, y, width, height); + + _chatmessage_dirty = true; + } +} + +/** Check if a message is expired every day */ +void NetworkChatMessageDailyLoop() +{ + for (uint i = 0; i < MAX_CHAT_MESSAGES; i++) { + ChatMessage *cmsg = &_chatmsg_list[i]; + if (cmsg->message[0] == '\0') continue; + + /* Message has expired, remove from the list */ + if (cmsg->end_date < _date) { + /* Move the remaining messages over the current message */ + if (i != MAX_CHAT_MESSAGES - 1) memmove(cmsg, cmsg + 1, sizeof(*cmsg) * (MAX_CHAT_MESSAGES - i - 1)); + + /* Mark the last item as empty */ + _chatmsg_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0'; + _chatmessage_dirty = true; + + /* Go one item back, because we moved the array 1 to the left */ + i--; + } + } +} + +/** Draw the chat message-box */ +void NetworkDrawChatMessage() +{ + Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter(); + if (!_chatmessage_dirty) return; + + /* First undraw if needed */ + NetworkUndrawChatMessage(); + + if (_iconsole_mode == ICONSOLE_FULL) return; + + /* Check if we have anything to draw at all */ + uint count = GetChatMessageCount(); + if (count == 0) return; + + int x = _chatmsg_box.x; + int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height; + int width = _chatmsg_box.width; + int height = _chatmsg_box.height; + if (y < 0) { + height = max(height + y, min(_chatmsg_box.height, _screen.height)); + y = 0; + } + if (x + width >= _screen.width) { + width = _screen.width - x; + } + if (width <= 0 || height <= 0) return; + + assert(blitter->BufferSize(width, height) < (int)sizeof(_chatmessage_backup)); + + /* Make a copy of the screen as it is before painting (for undraw) */ + blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, x, y), _chatmessage_backup, width, height); + + _cur_dpi = &_screen; // switch to _screen painting + + /* Paint a half-transparent box behind the chat messages */ + GfxFillRect( + _chatmsg_box.x, + _screen.height - _chatmsg_box.y - count * 13 - 2, + _chatmsg_box.x + _chatmsg_box.width - 1, + _screen.height - _chatmsg_box.y - 2, + PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOR // black, but with some alpha for background + ); + + /* Paint the chat messages starting with the lowest at the bottom */ + for (uint y = 13; count-- != 0; y += 13) { + DoDrawString(_chatmsg_list[count].message, _chatmsg_box.x + 3, _screen.height - _chatmsg_box.y - y + 1, _chatmsg_list[count].color); + } + + /* Make sure the data is updated next flush */ + _video_driver->MakeDirty(x, y, width, height); + + _chatmessage_visible = true; + _chatmessage_dirty = false; +} + + +static void SendChat(const char *buf, DestType type, int dest) +{ + if (StrEmpty(buf)) return; + if (!_network_server) { + SEND_COMMAND(PACKET_CLIENT_CHAT)((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf); + } else { + NetworkServerSendChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, NETWORK_SERVER_INDEX); + } +} + + +struct NetworkChatWindow : public QueryStringBaseWindow { + DestType dtype; + int dest; + + NetworkChatWindow (const WindowDesc *desc, DestType type, int dest) : QueryStringBaseWindow(NETWORK_CHAT_LENGTH, desc) + { + this->LowerWidget(2); + this->dtype = type; + this->dest = dest; + this->afilter = CS_ALPHANUMERAL; + InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 0); + + InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height); + SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys + + _chat_tab_completion_active = false; + + this->FindWindowPlacementAndResize(desc); + } + + ~NetworkChatWindow () + { + InvalidateWindowData(WC_NEWS_WINDOW, 0, 0); + ClrBit(_no_scroll, SCROLL_CHAT); + } + + /** + * 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. + */ + const char *ChatTabCompletionNextItem(uint *item) + { + static char chat_tab_temp_buffer[64]; + + /* First, try clients */ + if (*item < MAX_CLIENT_INFO) { + /* Skip inactive clients */ + while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++; + if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name; + } + + /* Then, try townnames */ + /* Not that the following assumes all town indices are adjacent, ie no + * towns have been deleted. */ + if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) { + const Town *t; + + FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) { + /* Get the town-name via the string-system */ + SetDParam(0, t->index); + GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer)); + return &chat_tab_temp_buffer[0]; + } + } + + return NULL; + } + + /** + * Find what text to complete. It scans for a space from the left and marks + * 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) + { + char *p = strrchr(buf, ' '); + if (p == NULL) return buf; + + *p = '\0'; + return p + 1; + } + + /** + * See if we can auto-complete the current text of the user. + */ + void ChatTabCompletion() + { + static char _chat_tab_completion_buf[NETWORK_CHAT_LENGTH]; + assert(this->edit_str_size == lengthof(_chat_tab_completion_buf)); + + Textbuf *tb = &this->text; + size_t len, tb_len; + uint item; + char *tb_buf, *pre_buf; + const char *cur_name; + 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) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf); + + tb_buf = ChatTabCompletionFindText(pre_buf); + tb_len = strlen(tb_buf); + + while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) { + item++; + + if (_chat_tab_completion_active) { + /* We are pressing TAB again on the same name, is there an other name + * that starts with this? */ + if (!second_scan) { + size_t offset; + size_t length; + + /* If we are completing at the begin of the line, skip the ': ' we added */ + if (tb_buf == pre_buf) { + offset = 0; + length = tb->length - 2; + } else { + /* Else, find the place we are completing at */ + offset = strlen(pre_buf) + 1; + length = tb->length - offset; + } + + /* Compare if we have a match */ + if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true; + + continue; + } + + /* Now any match we make on _chat_tab_completion_buf after this, is perfect */ + } + + len = strlen(cur_name); + if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) { + /* Save the data it was before completion */ + if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", 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) { + snprintf(tb->buf, this->edit_str_size, "%s: ", cur_name); + } else { + snprintf(tb->buf, this->edit_str_size, "%s %s", pre_buf, cur_name); + } + + /* Update the textbuffer */ + UpdateTextBufferSize(&this->text); + + this->SetDirty(); + free(pre_buf); + return; + } + } + + if (second_scan) { + /* We walked all posibilities, and the user presses tab again.. revert to original text */ + strcpy(tb->buf, _chat_tab_completion_buf); + _chat_tab_completion_active = false; + + /* Update the textbuffer */ + UpdateTextBufferSize(&this->text); + + this->SetDirty(); + } + free(pre_buf); + } + + virtual void OnPaint() + { + static const StringID chat_captions[] = { + STR_NETWORK_CHAT_ALL_CAPTION, + STR_NETWORK_CHAT_COMPANY_CAPTION, + STR_NETWORK_CHAT_CLIENT_CAPTION + }; + + this->DrawWidgets(); + + assert((uint)this->dtype < lengthof(chat_captions)); + DrawStringRightAligned(this->widget[2].left - 2, this->widget[2].top + 1, chat_captions[this->dtype], TC_BLACK); + this->DrawEditBox(2); + } + + virtual void OnClick(Point pt, int widget) + { + switch (widget) { + case 2: + ShowOnScreenKeyboard(this, 2, 0, 3); + break; + + case 3: /* Send */ + SendChat(this->text.buf, this->dtype, this->dest); + /* FALLTHROUGH */ + case 0: /* Cancel */ delete this; break; + } + } + + virtual void OnMouseLoop() + { + this->HandleEditBox(2); + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + EventState state = ES_NOT_HANDLED; + if (keycode == WKC_TAB) { + ChatTabCompletion(); + } else { + _chat_tab_completion_active = false; + switch (this->HandleEditBoxKey(2, key, keycode, state)) { + case 1: /* Return */ + SendChat(this->text.buf, this->dtype, this->dest); + /* FALLTHROUGH */ + case 2: /* Escape */ delete this; break; + } + } + return state; + } +}; + +static const Widget _chat_window_widgets[] = { +{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_PANEL, RESIZE_RIGHT, COLOUR_GREY, 11, 319, 0, 13, 0x0, STR_NULL}, // background +{ WWT_EDITBOX, RESIZE_RIGHT, COLOUR_GREY, 75, 257, 1, 12, STR_NETWORK_CHAT_OSKTITLE, STR_NULL}, // text box +{ WWT_PUSHTXTBTN, RESIZE_LR, COLOUR_GREY, 258, 319, 1, 12, STR_NETWORK_SEND, STR_NULL}, // send button +{ WIDGETS_END}, +}; + +static const WindowDesc _chat_window_desc = { + WDP_CENTER, -26, 320, 14, 640, 14, // x, y, width, height + WC_SEND_NETWORK_MSG, WC_NONE, + WDF_STD_TOOLTIPS | WDF_DEF_WIDGET, + _chat_window_widgets, +}; + +void ShowNetworkChatQueryWindow(DestType type, int dest) +{ + DeleteWindowById(WC_SEND_NETWORK_MSG, 0); + new NetworkChatWindow (&_chat_window_desc, type, dest); +} + +#endif /* ENABLE_NETWORK */ diff --git a/src/network/network_func.h b/src/network/network_func.h index 3f7612751..cd1a5aa5d 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -57,6 +57,11 @@ void NetworkServerSendRcon(uint16 client_index, ConsoleColour colour_code, const void NetworkServerSendError(uint16 client_index, NetworkErrorCode error); void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const char *msg, uint16 from_index); +void NetworkInitChatMessage(); +void CDECL NetworkAddChatMessage(uint16 color, uint8 duration, const char *message, ...); +void NetworkUndrawChatMessage(); +void NetworkChatMessageDailyLoop(); + #define FOR_ALL_ACTIVE_CLIENT_INFOS(ci) for (ci = _network_client_info; ci != endof(_network_client_info); ci++) if (ci->client_index != NETWORK_EMPTY_INDEX) #endif /* ENABLE_NETWORK */ diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 70ddccd92..d8ebfffea 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -15,11 +15,9 @@ #include "network_gamelist.h" #include "../gui.h" #include "../window_gui.h" -#include "../textbuf_gui.h" #include "../variables.h" #include "network_server.h" #include "network_udp.h" -#include "../town.h" #include "../newgrf.h" #include "../functions.h" #include "../window_func.h" @@ -37,8 +35,6 @@ #include "../table/sprites.h" -static bool _chat_tab_completion_active; - static void ShowNetworkStartServerWindow(); static void ShowNetworkLobbyWindow(NetworkGameList *ngl); extern void SwitchMode(int new_mode); @@ -1732,250 +1728,6 @@ void ShowJoinStatusWindow() new NetworkJoinStatusWindow(&_network_join_status_window_desc); } -static void SendChat(const char *buf, DestType type, int dest) -{ - if (StrEmpty(buf)) return; - if (!_network_server) { - SEND_COMMAND(PACKET_CLIENT_CHAT)((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf); - } else { - NetworkServerSendChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, NETWORK_SERVER_INDEX); - } -} - - -struct NetworkChatWindow : public QueryStringBaseWindow { - DestType dtype; - int dest; - - NetworkChatWindow (const WindowDesc *desc, DestType type, int dest) : QueryStringBaseWindow(NETWORK_CHAT_LENGTH, desc) - { - this->LowerWidget(2); - this->dtype = type; - this->dest = dest; - this->afilter = CS_ALPHANUMERAL; - InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, 0); - - InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height); - SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys - - _chat_tab_completion_active = false; - - this->FindWindowPlacementAndResize(desc); - } - - ~NetworkChatWindow () - { - InvalidateWindowData(WC_NEWS_WINDOW, 0, 0); - ClrBit(_no_scroll, SCROLL_CHAT); - } - - /** - * 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. - */ - const char *ChatTabCompletionNextItem(uint *item) - { - static char chat_tab_temp_buffer[64]; - - /* First, try clients */ - if (*item < MAX_CLIENT_INFO) { - /* Skip inactive clients */ - while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++; - if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name; - } - - /* Then, try townnames */ - /* Not that the following assumes all town indices are adjacent, ie no - * towns have been deleted. */ - if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) { - const Town *t; - - FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) { - /* Get the town-name via the string-system */ - SetDParam(0, t->index); - GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer)); - return &chat_tab_temp_buffer[0]; - } - } - - return NULL; - } - - /** - * Find what text to complete. It scans for a space from the left and marks - * 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) - { - char *p = strrchr(buf, ' '); - if (p == NULL) return buf; - - *p = '\0'; - return p + 1; - } - - /** - * See if we can auto-complete the current text of the user. - */ - void ChatTabCompletion() - { - static char _chat_tab_completion_buf[NETWORK_CHAT_LENGTH]; - assert(this->edit_str_size == lengthof(_chat_tab_completion_buf)); - - Textbuf *tb = &this->text; - size_t len, tb_len; - uint item; - char *tb_buf, *pre_buf; - const char *cur_name; - 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) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf); - - tb_buf = ChatTabCompletionFindText(pre_buf); - tb_len = strlen(tb_buf); - - while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) { - item++; - - if (_chat_tab_completion_active) { - /* We are pressing TAB again on the same name, is there an other name - * that starts with this? */ - if (!second_scan) { - size_t offset; - size_t length; - - /* If we are completing at the begin of the line, skip the ': ' we added */ - if (tb_buf == pre_buf) { - offset = 0; - length = tb->length - 2; - } else { - /* Else, find the place we are completing at */ - offset = strlen(pre_buf) + 1; - length = tb->length - offset; - } - - /* Compare if we have a match */ - if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true; - - continue; - } - - /* Now any match we make on _chat_tab_completion_buf after this, is perfect */ - } - - len = strlen(cur_name); - if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) { - /* Save the data it was before completion */ - if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", 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) { - snprintf(tb->buf, this->edit_str_size, "%s: ", cur_name); - } else { - snprintf(tb->buf, this->edit_str_size, "%s %s", pre_buf, cur_name); - } - - /* Update the textbuffer */ - UpdateTextBufferSize(&this->text); - - this->SetDirty(); - free(pre_buf); - return; - } - } - - if (second_scan) { - /* We walked all posibilities, and the user presses tab again.. revert to original text */ - strcpy(tb->buf, _chat_tab_completion_buf); - _chat_tab_completion_active = false; - - /* Update the textbuffer */ - UpdateTextBufferSize(&this->text); - - this->SetDirty(); - } - free(pre_buf); - } - - virtual void OnPaint() - { - static const StringID chat_captions[] = { - STR_NETWORK_CHAT_ALL_CAPTION, - STR_NETWORK_CHAT_COMPANY_CAPTION, - STR_NETWORK_CHAT_CLIENT_CAPTION - }; - - this->DrawWidgets(); - - assert((uint)this->dtype < lengthof(chat_captions)); - DrawStringRightAligned(this->widget[2].left - 2, this->widget[2].top + 1, chat_captions[this->dtype], TC_BLACK); - this->DrawEditBox(2); - } - - virtual void OnClick(Point pt, int widget) - { - switch (widget) { - case 2: - ShowOnScreenKeyboard(this, 2, 0, 3); - break; - - case 3: /* Send */ - SendChat(this->text.buf, this->dtype, this->dest); - /* FALLTHROUGH */ - case 0: /* Cancel */ delete this; break; - } - } - - virtual void OnMouseLoop() - { - this->HandleEditBox(2); - } - - virtual EventState OnKeyPress(uint16 key, uint16 keycode) - { - EventState state = ES_NOT_HANDLED; - if (keycode == WKC_TAB) { - ChatTabCompletion(); - } else { - _chat_tab_completion_active = false; - switch (this->HandleEditBoxKey(2, key, keycode, state)) { - case 1: /* Return */ - SendChat(this->text.buf, this->dtype, this->dest); - /* FALLTHROUGH */ - case 2: /* Escape */ delete this; break; - } - } - return state; - } -}; - -static const Widget _chat_window_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_PANEL, RESIZE_RIGHT, COLOUR_GREY, 11, 319, 0, 13, 0x0, STR_NULL}, // background -{ WWT_EDITBOX, RESIZE_RIGHT, COLOUR_GREY, 75, 257, 1, 12, STR_NETWORK_CHAT_OSKTITLE, STR_NULL}, // text box -{ WWT_PUSHTXTBTN, RESIZE_LR, COLOUR_GREY, 258, 319, 1, 12, STR_NETWORK_SEND, STR_NULL}, // send button -{ WIDGETS_END}, -}; - -static const WindowDesc _chat_window_desc = { - WDP_CENTER, -26, 320, 14, 640, 14, // x, y, width, height - WC_SEND_NETWORK_MSG, WC_NONE, - WDF_STD_TOOLTIPS | WDF_DEF_WIDGET, - _chat_window_widgets, -}; - -void ShowNetworkChatQueryWindow(DestType type, int dest) -{ - DeleteWindowById(WC_SEND_NETWORK_MSG, 0); - new NetworkChatWindow (&_chat_window_desc, type, dest); -} /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */ enum NetworkCompanyPasswordWindowWidgets { |