(svn r5968) -Feature: add auto-completion in chat-window. It completes Player-Names and
Town-Names (that order) using <tab>. Based on FS#28 by egladil.
This commit is contained in:
		
							
								
								
									
										159
									
								
								network_gui.c
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								network_gui.c
									
									
									
									
									
								
							@@ -26,6 +26,7 @@
 | 
				
			|||||||
#include "network_udp.h"
 | 
					#include "network_udp.h"
 | 
				
			||||||
#include "settings.h"
 | 
					#include "settings.h"
 | 
				
			||||||
#include "string.h"
 | 
					#include "string.h"
 | 
				
			||||||
 | 
					#include "town.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define BGC 5
 | 
					#define BGC 5
 | 
				
			||||||
#define BTC 15
 | 
					#define BTC 15
 | 
				
			||||||
@@ -55,6 +56,7 @@ typedef struct NetworkGameSorting {
 | 
				
			|||||||
static NetworkGameSorting _ng_sorting;
 | 
					static NetworkGameSorting _ng_sorting;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static char _edit_str_buf[64];
 | 
					static char _edit_str_buf[64];
 | 
				
			||||||
 | 
					static bool _chat_tab_completion_active;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ShowNetworkStartServerWindow(void);
 | 
					static void ShowNetworkStartServerWindow(void);
 | 
				
			||||||
static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
 | 
					static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
 | 
				
			||||||
@@ -1488,6 +1490,151 @@ static void SendChat(const char *buf)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Find the next item of the list of things that can be auto-completed.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static 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 */
 | 
				
			||||||
 | 
						if (*item < (uint)MAX_CLIENT_INFO + GetTownPoolSize()) {
 | 
				
			||||||
 | 
							Town *t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
 | 
				
			||||||
 | 
								int32 temp[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Skip empty towns */
 | 
				
			||||||
 | 
								if (t->xy == 0) {
 | 
				
			||||||
 | 
									(*item)++;
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Get the town-name via the string-system */
 | 
				
			||||||
 | 
								temp[0] = t->townnameparts;
 | 
				
			||||||
 | 
								GetStringWithArgs(chat_tab_temp_buffer, t->townnametype, temp);
 | 
				
			||||||
 | 
								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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Scan from the right to the left */
 | 
				
			||||||
 | 
						p = &buf[strlen(buf)];
 | 
				
			||||||
 | 
						while (p != buf) {
 | 
				
			||||||
 | 
							/* If we find a space, we try to complete the thing right of it */
 | 
				
			||||||
 | 
							if (*p == ' ') {
 | 
				
			||||||
 | 
								*p = '\0';
 | 
				
			||||||
 | 
								return p + 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							p--;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Not found, so complete the whole text */
 | 
				
			||||||
 | 
						return p;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * See if we can auto-complete the current text of the user.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void ChatTabCompletion(Window *w)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static char _chat_tab_completion_buf[lengthof(_edit_str_buf)];
 | 
				
			||||||
 | 
						Textbuf *tb = &WP(w, querystr_d).text;
 | 
				
			||||||
 | 
						uint 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) {
 | 
				
			||||||
 | 
									uint offset;
 | 
				
			||||||
 | 
									uint 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, lengthof(_edit_str_buf), "%s: ", cur_name);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									snprintf(tb->buf, lengthof(_edit_str_buf), "%s %s", pre_buf, cur_name);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Update the textbuffer */
 | 
				
			||||||
 | 
								UpdateTextBufferSize(&WP(w, querystr_d).text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								SetWindowDirty(w);
 | 
				
			||||||
 | 
								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(&WP(w, querystr_d).text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SetWindowDirty(w);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						free(pre_buf);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* uses querystr_d WP macro */
 | 
					/* uses querystr_d WP macro */
 | 
				
			||||||
static void ChatWindowWndProc(Window *w, WindowEvent *e)
 | 
					static void ChatWindowWndProc(Window *w, WindowEvent *e)
 | 
				
			||||||
@@ -1515,9 +1662,14 @@ static void ChatWindowWndProc(Window *w, WindowEvent *e)
 | 
				
			|||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case WE_KEYPRESS:
 | 
						case WE_KEYPRESS:
 | 
				
			||||||
		switch (HandleEditBoxKey(w, &WP(w, querystr_d), 1, e, CS_ALPHANUMERAL)) {
 | 
							if (e->keypress.keycode == WKC_TAB) {
 | 
				
			||||||
			case 1: /* Return */ SendChat(WP(w, querystr_d).text.buf); /* FALLTHROUGH */
 | 
								ChatTabCompletion(w);
 | 
				
			||||||
			case 2: /* Escape */ DeleteWindow(w); break;
 | 
							} else {
 | 
				
			||||||
 | 
								_chat_tab_completion_active = false;
 | 
				
			||||||
 | 
								switch (HandleEditBoxKey(w, &WP(w, querystr_d), 1, e, CS_ALPHANUMERAL)) {
 | 
				
			||||||
 | 
									case 1: /* Return */ SendChat(WP(w, querystr_d).text.buf); /* FALLTHROUGH */
 | 
				
			||||||
 | 
									case 2: /* Escape */ DeleteWindow(w); break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1555,6 +1707,7 @@ void ShowNetworkChatQueryWindow(byte desttype, byte dest)
 | 
				
			|||||||
	DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
 | 
						DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_edit_str_buf[0] = '\0';
 | 
						_edit_str_buf[0] = '\0';
 | 
				
			||||||
 | 
						_chat_tab_completion_active = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	w = AllocateWindowDesc(&_chat_window_desc);
 | 
						w = AllocateWindowDesc(&_chat_window_desc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user