1 #include <stdlib.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <ctype.h>
5 #include <errno.h>
6 #include <libxml/parser.h>
7 #include "chat.h"
8 #include "asc.h"
9 #include "colors.h"
10 #include "console.h"
11 #include "consolewin.h"
12 #include "elconfig.h"
13 #include "errors.h"
14 #include "gamewin.h"
15 #include "hud.h"
16 #include "icon_window.h"
17 #include "init.h"
18 #include "interface.h"
19 #ifdef JSON_FILES
20 #include "json_io.h"
21 #endif
22 #include "loginwin.h"
23 #include "main.h"
24 #include "mapwin.h"
25 #include "multiplayer.h"
26 #include "queue.h"
27 #include "text.h"
28 #include "translate.h"
29 #ifdef OPENGL_TRACE
30 #include "gl_init.h"
31 #endif
32 #include "io/elfilewrapper.h"
33 #include "io/elpathwrapper.h"
34 #include "sound.h"
35 
36 static queue_t *chan_name_queue;
37 static widget_list *input_widget = NULL;
38 int console_input_at_top = 0;
39 
40 /*!
41  * \name Tabbed and old behaviour chat
42  */
43 /*! @{ */
44 max_chat_lines_def max_chat_lines = { .value = 10, .lower = 5, .upper = 100 }; /*!< the upper, lower and current max lines to display */
45 static int lines_to_show = 0; /*!< the number of lines currently shown */
get_lines_to_show(void)46 int get_lines_to_show(void) { return lines_to_show; }
dec_lines_to_show(void)47 void dec_lines_to_show(void) { if (lines_to_show > 0) lines_to_show--; }
clear_lines_to_show(void)48 void clear_lines_to_show(void) { lines_to_show = 0; }
49 /*! @} */
50 
51 static void remove_chat_tab (Uint8 channel);
52 static int add_chat_tab (int nlines, Uint8 channel);
53 static void update_chat_tab_idx (Uint8 old_ix, Uint8 new_idx);
54 static void remove_tab_button (Uint8 channel);
55 static int add_tab_button (Uint8 channel);
56 static void update_tab_button_idx (Uint8 old_idx, Uint8 new_idx);
57 static int resize_chat_handler(window_info *win, int width, int height);
58 static void change_to_current_tab(const char *input);
59 static int display_channel_color_win(Uint32 channel_number);
60 
61 #define MAX_CHANNEL_COLORS 64
62 #define MAX_CHAT_TABS 12			/*!< Size of the \see channels array */
63 #define MAX_ACTIVE_CHANNELS	10		/*!< Maximum number of channels in use */
64 #define SPEC_CHANS 12				/*!< 11 are currently in use. read channels.xml for the list */
65 
66 typedef struct
67 {
68 	int tab_id;
69 	int out_id;
70 	Uint8 chan_nr;
71 	int nr_lines;
72 	char open, newchan, highlighted;
73 } chat_channel;
74 
75 typedef struct
76 {
77 	Uint8 channel;
78 	int button;
79 	char highlighted;
80 	char * description;
81 } chat_tab;
82 
83 typedef struct
84 {
85 	Uint32 channel;
86 	char * name;
87 	char * description;
88 } chan_name;
89 
90 static chat_channel channels[MAX_CHAT_TABS]; /*!< Infos about a chat window tabs  */
91 static channelcolor channel_colors[MAX_CHANNEL_COLORS];
92 static Uint32 active_channels[MAX_ACTIVE_CHANNELS];
93 static Uint8 current_channel = 0;
94 static chan_name * pseudo_chans[SPEC_CHANS];
95 
96 static const int tab_control_border_sep = 2;
97 static const int tab_control_half_len = 5;
98 static const int tab_control_underline_sep = 4;
99 
100 int enable_chat_show_hide = 0;
101 static int chat_shown = 1;
102 
enable_chat_shown(void)103 void enable_chat_shown(void)
104 {
105 	chat_shown = 1;
106 	switch (use_windowed_chat)
107 	{
108 		case 0: // old chat
109 			lines_to_show = max_chat_lines.value;
110 			break;
111 		case 1: // chat bar
112 			show_window(tab_bar_win);
113 			lines_to_show = max_chat_lines.value;
114 			break;
115 		case 2: // chat window
116 			show_window(get_id_MW(MW_CHAT));
117 			break;
118 	}
119 	set_icon_state("chat", (enable_chat_show_hide != 0));
120 	update_console_input_size_and_position();
121 }
122 
is_chat_shown(void)123 int is_chat_shown(void)
124 {
125 	return chat_shown;
126 }
127 
open_chat(void)128 void open_chat(void)
129 {
130 	if (enable_chat_show_hide)
131 		toggle_chat();
132 }
133 
toggle_chat(void)134 void toggle_chat(void)
135 {
136 	if (get_window_showable(get_id_MW(MW_CONSOLE)))
137 		return;
138 	if (!enable_chat_show_hide)
139 		return;
140 	switch (use_windowed_chat)
141 	{
142 		case 0: // old chat
143 			if ((chat_shown = ((chat_shown) ?0 :1)))
144 				lines_to_show = max_chat_lines.value;
145 			break;
146 		case 1: // chat bar
147 			toggle_window(tab_bar_win);
148 			if ((chat_shown = get_window_showable(tab_bar_win)))
149 				lines_to_show = max_chat_lines.value;
150 			break;
151 		case 2: // chat window
152 			toggle_window(get_id_MW(MW_CHAT));
153 			chat_shown = get_window_showable(get_id_MW(MW_CHAT));
154 			break;
155 	}
156 	update_console_input_size_and_position();
157 }
158 
get_tabbed_chat_end_x(void)159 int get_tabbed_chat_end_x(void)
160 {
161 	if ((tab_bar_win < 0) || (use_windowed_chat != 1))
162 		return 0;
163 	return windows_list.window[tab_bar_win].cur_x + windows_list.window[tab_bar_win].len_x;
164 }
165 
input_widget_move_to_win(int window_id)166 void input_widget_move_to_win(int window_id)
167 {
168 	window_info *win = NULL;
169 
170 	if (input_widget == NULL)
171 		return;
172 
173 	if (window_id < 0)
174 		window_id = input_widget->window_id;
175 
176 	if ((window_id >= 0) && (window_id < windows_list.num_windows))
177 		win = &windows_list.window[window_id];
178 	if (win == NULL)
179 		return;
180 
181 	if (input_widget->window_id != window_id)
182 		widget_move_win(input_widget->window_id, input_widget->id, window_id);
183 
184 	if(window_id == get_id_MW(MW_CHAT)) {
185 		widget_set_flags(input_widget->window_id, input_widget->id, TEXT_FIELD_BORDER|TEXT_FIELD_EDITABLE|TEXT_FIELD_NO_KEYPRESS);
186 		input_widget->OnResize = NULL;
187 		resize_chat_handler(win, win->len_x, win->len_y);
188 	} else {
189 		text_field *tf = input_widget->widget_info;
190 		int text_height = get_text_height(tf->nr_lines, CHAT_FONT, input_widget->size);
191 		Uint32 flags;
192 
193 		input_widget->OnResize = input_field_resize;
194 		if(window_id == get_id_MW(MW_CONSOLE)) {
195 			flags = (TEXT_FIELD_BORDER|INPUT_DEFAULT_FLAGS)^WIDGET_CLICK_TRANSPARENT;
196 		} else if(window_id == game_root_win && input_text_line.len == 0) {
197 			flags = INPUT_DEFAULT_FLAGS|WIDGET_DISABLED;
198 		} else if(window_id == get_id_MW(MW_TABMAP)) {
199 			flags = INPUT_DEFAULT_FLAGS|WIDGET_INVISIBLE;
200 		} else {
201 			flags = INPUT_DEFAULT_FLAGS;
202 		}
203 		if (console_input_at_top)
204 			flags |= TEXT_FIELD_BORDER;
205 		widget_set_flags(input_widget->window_id, input_widget->id, flags);
206 		tf->x_space = tf->y_space = INPUT_MARGIN * font_scales[CHAT_FONT];
207 		widget_resize(input_widget->window_id, input_widget->id,
208 			win->len_x - HUD_MARGIN_X, 2 * tf->y_space + text_height);
209 
210 		if (console_input_at_top)
211 			widget_move(input_widget->window_id, input_widget->id, 0, get_tab_bar_y());
212 		else
213 			widget_move(input_widget->window_id, input_widget->id, 0, win->len_y-input_widget->len_y-HUD_MARGIN_Y);
214 	}
215 }
216 
add_tab(Uint8 channel)217 static void add_tab (Uint8 channel)
218 {
219 	if (tab_bar_win != -1) add_tab_button (channel);
220 	if (get_id_MW(MW_CHAT) != -1) add_chat_tab (0, channel);
221 }
222 
remove_tab(Uint8 channel)223 static void remove_tab (Uint8 channel)
224 {
225 	recolour_messages(display_text_buffer);
226 	if (tab_bar_win != -1) remove_tab_button (channel);
227 	if (get_id_MW(MW_CHAT) != -1) remove_chat_tab (channel);
228 }
229 
update_tab_idx(Uint8 old_idx,Uint8 new_idx)230 static void update_tab_idx (Uint8 old_idx, Uint8 new_idx)
231 {
232 	// XXX: CAUTION
233 	// Since this function simply replaces old_idx y new_idx, it could
234 	// potentially cause trouble when new_idx is already in use by
235 	// another tab. However, as the code is now, successive calls to
236 	// update_tab_idx are in increasing order of old_idx, and new_idx
237 	// is lower than old_idx, so we should be safe.
238 
239 	if (tab_bar_win != -1) update_tab_button_idx (old_idx, new_idx);
240 	if (get_id_MW(MW_CHAT) != -1) update_chat_tab_idx (old_idx, new_idx);
241 }
242 
set_channel_tabs(const Uint32 * chans)243 static void set_channel_tabs (const Uint32 *chans)
244 {
245 	int nmax = CHAT_CHANNEL3-CHAT_CHANNEL1+1;
246 	Uint32 chan;
247 	Uint8 chan_nr, chan_nrp;
248 
249 	for (chan_nr = 0; chan_nr < nmax; chan_nr++)
250 	{
251 		chan = chans[chan_nr];
252 		if (chan == 0) continue;
253 
254 		for (chan_nrp = 0; chan_nrp < nmax; chan_nrp++)
255 		{
256 			if (active_channels[chan_nrp] == chan) break;
257 		}
258 
259 		if (chan_nrp >= nmax)
260 		{
261 			// we left this channel
262 			remove_tab (chan_nr+CHAT_CHANNEL1);
263 		}
264 		else
265 		{
266 			update_tab_idx (chan_nr+CHAT_CHANNEL1, chan_nrp+CHAT_CHANNEL1);
267 		}
268 	}
269 
270 	for (chan_nrp = 0; chan_nrp < nmax; chan_nrp++)
271 	{
272 		chan = active_channels[chan_nrp];
273 
274 		if (chan == 0) continue;
275 
276 		for (chan_nr = 0; chan_nr < nmax; chan_nr++)
277 		{
278 			if (chans[chan_nr] == chan) break;
279 		}
280 
281 		if (chan_nr >= nmax)
282 		{
283 			// we have a new channel
284 			add_tab (chan_nrp+CHAT_CHANNEL1);
285 		}
286 	}
287 }
288 
set_active_channels(Uint8 active,const Uint32 * channels,int nchan)289 void set_active_channels (Uint8 active, const Uint32 *channels, int nchan)
290 {
291 	Uint32 tmp[MAX_ACTIVE_CHANNELS];
292 	int i;
293 
294 	for (i = 0; i < MAX_ACTIVE_CHANNELS; i++)
295 		tmp[i] = active_channels[i];
296 
297 	for (i = 0; i < nchan; i++)
298 		active_channels[i] = SDL_SwapLE32(channels[i]);
299 	for ( ; i < MAX_ACTIVE_CHANNELS; i++)
300 		active_channels[i] = 0;
301 
302 	set_channel_tabs (tmp);
303 
304 	current_channel = active;
305 }
306 
send_active_channel(Uint8 chan)307 static void send_active_channel (Uint8 chan)
308 {
309 	Uint8 msg[2];
310 
311 	if (chan >= CHAT_CHANNEL1 && chan <= CHAT_CHANNEL3)
312 	{
313 		msg[0] = SET_ACTIVE_CHANNEL;
314 		msg[1] = chan;
315 		my_tcp_send (my_socket, msg, 2);
316 
317 		current_channel = chan - CHAT_CHANNEL1;
318 	}
319 }
320 
get_active_channel(Uint8 idx)321 Uint32 get_active_channel (Uint8 idx)
322 {
323 	if (idx >= CHAT_CHANNEL1 && idx <= CHAT_CHANNEL3)
324 		return active_channels[idx-CHAT_CHANNEL1];
325 	return 0;
326 }
327 
328 #define CHAT_WIN_SPACE		4
329 #define CHAT_WIN_TAG_SPACE	3
330 #define CHAT_WIN_TEXT_WIDTH  	500
331 
332 static int CHAT_WIN_TAG_HEIGHT = 20;
333 static int CHAT_WIN_SCROLL_WIDTH = ELW_BOX_SIZE;
334 static int CLOSE_SIZE = ELW_BOX_SIZE;
335 
336 int local_chat_separate = 0;
337 int personal_chat_separate = 0;
338 int guild_chat_separate = 1;
339 int server_chat_separate = 0;
340 int mod_chat_separate = 0;
341 
342 /*
343  * use_windowed_chat == 0: old behaviour, all text is printed
344  * use_windowed_chat == 1: channel selection bar
345  * use_windowed_chat == 2: chat window
346  */
347 int use_windowed_chat = 1;
348 //int highlight_tab_on_nick = 1;
349 
350 ////////////////////////////////////////////////////////////////////////
351 // Chat window variables
352 
353 static int chat_scroll_id = 15;
354 static int chat_tabcollection_id = 20;
355 static int chat_out_start_id = 21;
356 static int chat_win_text_width = CHAT_WIN_TEXT_WIDTH;
357 static int chat_out_text_height = 0;
358 static int current_line = 0;
359 static int text_changed = 1;
360 static int nr_displayed_lines;
361 static int active_tab = -1;
362 static chan_name *tab_label (Uint8 chan);//Forward declaration
363 
clear_chat_wins(void)364 void clear_chat_wins (void)
365 {
366 	int chat_win = get_id_MW(MW_CHAT);
367 	int i = 0;
368 	if(use_windowed_chat != 2){return;}
369 
370 	for (;i < MAX_CHAT_TABS; ++i){
371 		channels[i].nr_lines = 0;
372 	}
373 
374 	vscrollbar_set_bar_len (chat_win, chat_scroll_id, 0);
375 	vscrollbar_set_pos (chat_win, chat_scroll_id, 0);
376 	current_line = 0;
377 	text_changed = 1;
378 }
379 
380 
init_chat_channels(void)381 void init_chat_channels(void)
382 {
383 	int itab;
384 
385 	for (itab = 0; itab < MAX_CHAT_TABS; itab++)
386 	{
387 		channels[itab].tab_id = -1;
388 		channels[itab].out_id = chat_out_start_id + itab;
389 		channels[itab].chan_nr = CHAT_ALL;
390 		channels[itab].nr_lines = 0;
391 		channels[itab].open = 0;
392 		channels[itab].newchan = 0;
393 	}
394 }
395 
clear_input_line(void)396 void clear_input_line(void)
397 {
398 	input_text_line.data[0] = '\0';
399 	input_text_line.len = 0;
400 	if (input_widget != NULL)
401 	{
402 		text_field *field = input_widget->widget_info;
403 		field->cursor = 0;
404 		field->cursor_line = 0;
405 		field->nr_lines = 1;
406 		input_widget_move_to_win(-1);
407 	}
408 	history_reset();
409 }
410 
close_channel(window_info * win)411 static int close_channel (window_info *win)
412 {
413 	int id = win->window_id;
414 	int ichan;
415 
416 	for (ichan = 0; ichan < MAX_CHAT_TABS; ichan++)
417 	{
418 		if (channels[ichan].tab_id == id)
419 		{
420 			int idx = channels[ichan].chan_nr - CHAT_CHANNEL1;
421 
422 			if (idx >= 0 && idx < MAX_ACTIVE_CHANNELS)
423 			{
424 				char str[256];
425 				safe_snprintf(str, sizeof(str), "%c#lc %d", RAW_TEXT, active_channels[idx]);
426 				my_tcp_send(my_socket, (Uint8*)str, strlen(str+1)+1);
427 			}
428 
429 			// Safe to remove?
430 			if (tab_bar_win != -1) remove_tab_button(channels[ichan].chan_nr);
431 
432 			return 1;
433 		}
434 	}
435 
436 	// we shouldn't get here
437 	LOG_ERROR ("Trying to close non-existant channel\n");
438 	return 0;
439 }
440 
remove_chat_tab(Uint8 channel)441 static void remove_chat_tab (Uint8 channel)
442 {
443 	int chat_win = get_id_MW(MW_CHAT);
444 	int ichan;
445 
446 	for (ichan = 0; ichan < MAX_CHAT_TABS; ichan++)
447 	{
448 		if (channels[ichan].chan_nr == channel && channels[ichan].open)
449 		{
450 			int nr = tab_collection_get_tab_nr (chat_win, chat_tabcollection_id, channels[ichan].tab_id);
451 			tab_collection_close_tab (chat_win, chat_tabcollection_id, nr);
452 
453 			channels[ichan].tab_id = -1;
454 			channels[ichan].chan_nr = CHAT_ALL;
455 			channels[ichan].nr_lines = 0;
456 			channels[ichan].open = 0;
457 			channels[ichan].newchan = 0;
458 			channels[ichan].highlighted = 0;
459 
460 			return;
461 		}
462 	}
463 }
464 
add_chat_tab(int nlines,Uint8 channel)465 static int add_chat_tab(int nlines, Uint8 channel)
466 {
467 	int chat_win = get_id_MW(MW_CHAT);
468 	int ichan;
469 
470 	for (ichan = 0; ichan < MAX_CHAT_TABS; ichan++)
471 	{
472 		if (!channels[ichan].open)
473 		{
474 			// yay, found an empty slot
475 			char title[64];
476 			int inout_width = chat_win_text_width + 2 * CHAT_WIN_SPACE;
477 			int output_height = chat_out_text_height + 2 * CHAT_WIN_SPACE;
478 
479 			channels[ichan].chan_nr = channel;
480 			channels[ichan].nr_lines = nlines;
481 			channels[ichan].open = 1;
482 			channels[ichan].newchan = 1;
483 			channels[ichan].highlighted = 0;
484 
485 			safe_strncpy(title,(tab_label(channel))->name, sizeof(title));
486 
487 			channels[ichan].tab_id = tab_add (chat_win, chat_tabcollection_id, title, 0, 1, 0);
488 			set_window_flag (channels[ichan].tab_id, ELW_CLICK_TRANSPARENT);
489 
490 			set_window_min_size (channels[ichan].tab_id, 0, 0);
491 			channels[ichan].out_id = text_field_add_extended(channels[ichan].tab_id,
492 				channels[ichan].out_id, NULL, 0, 0, inout_width, output_height, 0,
493 				CHAT_FONT, 1.0, display_text_buffer,
494 				DISPLAY_TEXT_BUFFER_SIZE, channel, CHAT_WIN_SPACE, CHAT_WIN_SPACE);
495 
496 			set_window_handler (channels[ichan].tab_id, ELW_HANDLER_DESTROY, close_channel);
497 
498 			if(!channels[ichan].highlighted && channels[active_tab].chan_nr != CHAT_ALL)
499 			{
500 				tab_set_label_color_by_id (chat_win, chat_tabcollection_id, channels[ichan].tab_id, 1.0, 1.0, 0.0);
501 			}
502 
503 			return ichan;
504 		}
505 	}
506 	//no empty slot found
507 	return -1;
508 }
509 
update_chat_tab_idx(Uint8 old_idx,Uint8 new_idx)510 static void update_chat_tab_idx (Uint8 old_idx, Uint8 new_idx)
511 {
512 	int itab;
513 
514 	if (old_idx == new_idx) return;
515 
516 	for (itab = 0; itab < MAX_CHAT_TABS; itab++)
517 	{
518 		if (channels[itab].chan_nr == old_idx && channels[itab].open)
519 		{
520 			channels[itab].chan_nr = new_idx;
521 			return;
522 		}
523 	}
524 }
525 
get_tab_channel(Uint8 channel)526 static Uint8 get_tab_channel (Uint8 channel)
527 {
528 	switch (channel)
529 	{
530 		case CHAT_LOCAL:
531 			if (!local_chat_separate) return CHAT_ALL;
532 			break;
533 		case CHAT_PERSONAL:
534 			if (!personal_chat_separate) return CHAT_ALL;
535 			break;
536 		case CHAT_GM:
537 			if (!guild_chat_separate) return CHAT_ALL;
538 			break;
539 		case CHAT_SERVER:
540 			if (!server_chat_separate) return CHAT_ALL;
541 			break;
542 		case CHAT_MOD:
543 			if (!mod_chat_separate) return CHAT_ALL;
544 			break;
545 		case CHAT_MODPM:
546 			// always display moderator PMs in all tabs
547 			return CHAT_ALL;
548 	}
549 
550 	return channel;
551 }
552 
update_chat_window(text_message * msg,char highlight)553 static void update_chat_window (text_message *msg, char highlight)
554 {
555 	int chat_win = get_id_MW(MW_CHAT);
556 	int ichan, len, nlines, width, channel;
557 	char found;
558 
559 	// don't bother if there's no chat window
560 	if (chat_win < 0) return;
561 
562 	// rewrap message to get correct # of lines
563 	width = windows_list.window[chat_win].len_x;
564 	nlines = rewrap_message(msg, CHAT_FONT, 1.0, width, NULL);
565 
566 	// first check if we need to display in all open channels
567 	channel = get_tab_channel (msg->chan_idx);
568 
569 	if (channel == CHAT_ALL || channel == CHAT_MODPM)
570 	{
571 		for (ichan = 0; ichan < MAX_CHAT_TABS; ichan++)
572 		{
573 			if (channels[ichan].open) {
574 				if (msg->deleted) {
575 					channels[ichan].nr_lines -= nlines;
576 				} else {
577 					channels[ichan].nr_lines += nlines;
578 				}
579 			}
580 		}
581 
582 		len = channels[active_tab].nr_lines - nr_displayed_lines;
583 		if (len < 0) len = 0;
584 		vscrollbar_set_bar_len (chat_win, chat_scroll_id, len);
585 		vscrollbar_set_pos (chat_win, chat_scroll_id, len);
586 
587 		current_line = channels[active_tab].nr_lines;
588 		text_changed = 1;
589 		return;
590 	}
591 
592 	// message not for all channels, see if this channel is already open
593 	found = 0;
594 	for (ichan = 0; ichan < MAX_CHAT_TABS; ichan++)
595 	{
596 		if (channels[ichan].open && (channels[ichan].chan_nr == channel || channels[ichan].chan_nr == CHAT_ALL))
597 		{
598 			if (msg->deleted) {
599 				channels[ichan].nr_lines -= nlines;
600 			} else {
601 				channels[ichan].nr_lines += nlines;
602 			}
603 			channels[ichan].newchan = 1;
604 
605 			if (ichan == active_tab)
606 			{
607 				len = channels[ichan].nr_lines - nr_displayed_lines;
608 				if (len < 0) len = 0;
609 
610 				vscrollbar_set_bar_len (chat_win, chat_scroll_id, len);
611 				vscrollbar_set_pos (chat_win, chat_scroll_id, len);
612 				current_line = channels[ichan].nr_lines;
613 				text_changed = 1;
614 			}
615 			//Make sure we don't change the color of a highlighted tab
616 			else if (highlight && !channels[ichan].highlighted && channels[active_tab].chan_nr != CHAT_ALL &&
617 					channels[ichan].chan_nr != CHAT_ALL && !get_show_window_MW(MW_CONSOLE))
618 			{
619 				tab_set_label_color_by_id (chat_win, chat_tabcollection_id, channels[ichan].tab_id, 1.0, 1.0, 0.0);
620 			}
621 			if (found) return; // we found the respective tab and the "all" tab now
622 			found++;
623 		}
624 	}
625 
626 	// nothing to delete from
627 	if (msg->deleted) return;
628 
629 	// channel not found, try to create a new one
630 	if(add_chat_tab(nlines, channel) == -1)
631 	{
632 		// uh oh, no empty slot found. this shouldn't really be happening...
633 		// log in general channel
634 		channels[0].nr_lines += nlines;
635 		channels[0].newchan = 1;
636 		if (0 == active_tab)
637 		{
638 			len = channels[0].nr_lines - nr_displayed_lines;
639 			if (len < 0) len = 0;
640 
641 			vscrollbar_set_bar_len (chat_win, chat_scroll_id, len);
642 			vscrollbar_set_pos (chat_win, chat_scroll_id, len);
643 			current_line = channels[active_tab].nr_lines;
644 			text_changed = 1;
645 		}
646 		else if (highlight && !channels[0].highlighted) //Make sure we don't change the color of a highlighted tab
647 		{
648 			tab_set_label_color_by_id (chat_win, chat_tabcollection_id, channels[0].tab_id, 1.0, 1.0, 0.0);
649 		}
650 	}
651 }
652 
display_chat_handler(window_info * win)653 static int display_chat_handler (window_info *win)
654 {
655 	static int msg_start = 0, offset_start = 0;
656 	if (text_changed)
657 	{
658 		int line = vscrollbar_get_pos (get_id_MW(MW_CHAT), chat_scroll_id);
659 
660 		find_line_nr(channels[active_tab].nr_lines, line, channels[active_tab].chan_nr,
661 			&msg_start, &offset_start, CHAT_FONT, 1.0, chat_win_text_width);
662 		text_field_set_buf_pos (channels[active_tab].tab_id, channels[active_tab].out_id, msg_start, offset_start);
663 		text_changed = 0;
664 	}
665 
666 	check_and_get_console_input(win->window_id);
667 
668 	return 1;
669 }
670 
switch_to_chat_tab(int id,char click)671 static void switch_to_chat_tab(int id, char click)
672 {
673 	int chat_win = get_id_MW(MW_CHAT);
674 	if(!click)
675 	{
676 		int itab;
677 		//Do what a mouse click would do
678 		widget_list *widget = widget_find(chat_win, chat_tabcollection_id);
679 		tab_collection *collection = widget->widget_info;
680 
681 		for(itab = 0; itab < collection->nr_tabs; itab++)
682 		{
683 			if(collection->tabs[itab].content_id == id)
684 			{
685 				break;
686 			}
687 		}
688 		tab_collection_select_tab(chat_win, chat_tabcollection_id, itab);
689 	}
690 	tab_set_label_color_by_id (chat_win, chat_tabcollection_id, id, -1.0, -1.0, -1.0);
691 
692 	//set active_tab
693 	for (active_tab = 0; active_tab < MAX_CHAT_TABS; active_tab++)
694 	{
695 		if (channels[active_tab].tab_id == id && channels[active_tab].open)
696 		{
697 			break;
698 		}
699 	}
700 	if (active_tab >= MAX_CHAT_TABS)
701 	{
702 		// This shouldn't be happening
703 		LOG_ERROR ("Trying to switch to non-existant channel");
704 		active_tab = 0;
705 	}
706 	current_line = channels[active_tab].nr_lines - nr_displayed_lines;
707 	if (current_line < 0)
708 	{
709 		current_line = 0;
710 	}
711 	vscrollbar_set_bar_len(chat_win, chat_scroll_id, current_line);
712 	vscrollbar_set_pos(chat_win, chat_scroll_id, current_line);
713 	text_changed = 1;
714 	channels[active_tab].highlighted = 0;
715 
716 	if (channels[active_tab].chan_nr >= CHAT_CHANNEL1 && channels[active_tab].chan_nr <= CHAT_CHANNEL3) {
717 		send_active_channel (channels[active_tab].chan_nr);
718 		recolour_messages(display_text_buffer);
719 	}
720 }
721 
change_to_current_chat_tab(const char * input)722 static void change_to_current_chat_tab(const char *input)
723 {
724 	Uint8 channel;
725 	int ichan;
726 	int itab;
727 	int input_len = strlen(input);
728 
729 	if(input[0] == '@' || input[0] == char_at_str[0])
730 	{
731 		channel = CHAT_CHANNEL1 + current_channel;
732 	}
733 	else if(!strncasecmp(input, "#gm ", 4) || (!strncasecmp(input, gm_cmd_str, strlen(gm_cmd_str)) && input_len > strlen(gm_cmd_str)+1 && input[strlen(gm_cmd_str)] == ' '))
734 	{
735 		channel = CHAT_GM;
736 	}
737 	else if(!strncasecmp(input, "#mod ", 5) || (!strncasecmp(input, mod_cmd_str, strlen(mod_cmd_str)) && input_len > strlen(mod_cmd_str)+1 && input[strlen(mod_cmd_str)] == ' '))
738 	{
739 		channel = CHAT_MOD;
740 	}
741 	else if(!strncasecmp(input, "#bc ", 4) || (!strncasecmp(input, bc_cmd_str, strlen(bc_cmd_str)) && input_len > strlen(bc_cmd_str)+1 && input[strlen(bc_cmd_str)] == ' '))
742 	{
743 		channel = CHAT_SERVER;
744 	}
745 	else if(input[0] == '/' || input[0] == char_slash_str[0])
746 	{
747 		channel = CHAT_PERSONAL;
748 	}
749 	else if(input[0] == '#' || input[0] == char_cmd_str[0]) {
750 		//We don't want to switch tab on commands.
751 		channel = CHAT_ALL;
752 	}
753 	else
754 	{
755 		channel = CHAT_LOCAL;
756 	}
757 	channel = get_tab_channel (channel);
758 
759 	if(channel != CHAT_ALL)
760 	{
761 		for(ichan = 0; ichan < MAX_CHAT_TABS; ichan++)
762 		{
763 			if(channels[ichan].chan_nr == channel && channels[ichan].open)
764 			{
765 				if(ichan != active_tab) //We don't want to switch to the tab we're already in
766 				{
767 					switch_to_chat_tab(channels[ichan].tab_id, 0);
768 				}
769 				return;
770 			}
771 		}
772 		//We didn't find any tab to switch to, create new
773 		itab = add_chat_tab(0, channel);
774 		if(itab == -1)
775 		{
776 			//Eek, it failed, switch to general
777 			switch_to_chat_tab(channels[0].tab_id, 0);
778 		}
779 		else
780 		{
781 			switch_to_chat_tab(channels[itab].tab_id, 0);
782 		}
783 	}
784 }
785 
chat_tabs_click(widget_list * widget,int mx,int my,Uint32 flags)786 static int chat_tabs_click (widget_list *widget, int mx, int my, Uint32 flags)
787 {
788 	int id;
789 
790 	id = tab_collection_get_tab_id (get_id_MW(MW_CHAT), widget->id);
791 
792 	if (flags&ELW_RIGHT_MOUSE)
793 	{
794 		int i;
795 		for(i=0; i < MAX_CHAT_TABS; i++)
796 		{
797 			if(channels[i].tab_id == id)
798 			{
799 				display_channel_color_win(get_active_channel(channels[i].chan_nr));
800 				return 1;
801 			}
802 		}
803 	}
804 	else
805 	{
806 		if (id != channels[active_tab].tab_id)
807 		{
808 			//We're not looking at the tab we clicked
809 			switch_to_chat_tab(id, 1);
810 			return 1;
811 		}
812 	}
813 	return 0;
814 }
815 
chat_scroll_drag(widget_list * widget,int mx,int my,Uint32 flags,int dx,int dy)816 static int chat_scroll_drag (widget_list *widget, int mx, int my, Uint32 flags, int dx, int dy)
817 {
818 	int line = vscrollbar_get_pos (get_id_MW(MW_CHAT), widget->id);
819 	if (line != current_line)
820 	{
821 		text_changed = 1;
822 		current_line = line;
823 	}
824         return 0;
825 }
826 
chat_scroll_click(widget_list * widget,int mx,int my,Uint32 flags)827 static int chat_scroll_click (widget_list *widget, int mx, int my, Uint32 flags)
828 {
829 	int line = vscrollbar_get_pos (get_id_MW(MW_CHAT), widget->id);
830 	if (line != current_line)
831 	{
832 		text_changed = 1;
833 		current_line = line;
834 	}
835         return 0;
836 }
837 
chat_input_key(widget_list * widget,int mx,int my,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)838 static int chat_input_key (widget_list *widget, int mx, int my, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
839 {
840 	text_field *tf;
841 	text_message *msg;
842 
843 	if (widget == NULL) {
844 		return 0;
845 	}
846 	tf = (text_field *) widget->widget_info;
847 	msg = tf->buffer;
848 
849 	if ( (!(key_mod & KMOD_CTRL) && ( (key_code == SDLK_UP) || (key_code == SDLK_DOWN) ) ) ||
850 		(key_code == SDLK_LEFT) || (key_code == SDLK_RIGHT) || (key_code == SDLK_HOME) ||
851 		(key_code == SDLK_END) || (key_code == SDLK_DELETE && tf->cursor < msg->len) ) {
852 		//pass it along. the defaults are good enough
853 		widget->Flags &= ~TEXT_FIELD_NO_KEYPRESS;
854 		text_field_keypress (widget, mx, my, key_code, key_unicode, key_mod);
855 		widget->Flags |= TEXT_FIELD_NO_KEYPRESS;
856 	}
857 	else
858 	{
859 		return 0;
860 	}
861 	/* Key was handled, stop blinking on input */
862 	tf->next_blink = cur_time + TF_BLINK_DELAY;
863 	return 1;
864 }
865 
resize_chat_handler(window_info * win,int width,int height)866 static int resize_chat_handler(window_info *win, int width, int height)
867 {
868 	int chat_win = get_id_MW(MW_CHAT);
869 	int itab;
870 	int line_height = get_line_height(CHAT_FONT, 1.0);
871 	int line_skip = get_line_skip(CHAT_FONT, 1.0);
872 	int scroll_x = width - CHAT_WIN_SCROLL_WIDTH;
873 	int scroll_height = height - 2*CLOSE_SIZE;
874 	int inout_width = width - CHAT_WIN_SCROLL_WIDTH - 2 * CHAT_WIN_SPACE;
875 	int input_height = line_height + 2 * line_skip + 2 * CHAT_WIN_SPACE;
876 	int input_y = height - input_height - CHAT_WIN_SPACE;
877 	int tabcol_height = input_y - 2 * CHAT_WIN_SPACE;
878 	int output_height = tabcol_height - CHAT_WIN_TAG_HEIGHT;
879 
880 	if (output_height < line_height + 4*line_skip + 2 * CHAT_WIN_SPACE
881 		&& input_height > line_height + 2*line_skip + 2 * CHAT_WIN_SPACE)
882 	{
883 		input_height -= 2*line_skip;
884 		input_y += 2*line_skip;
885 		output_height += 2*line_skip;
886 		tabcol_height += 2*line_skip;
887 	}
888 	else if (output_height < line_height + 7*line_skip + 2 * CHAT_WIN_SPACE
889 		&& input_height > line_height + line_skip + 2 * CHAT_WIN_SPACE)
890 	{
891 		input_height -= line_skip;
892 		input_y += line_skip;
893 		output_height += line_skip;
894 		tabcol_height += line_skip;
895 	}
896 
897 	chat_win_text_width = inout_width - 2 * CHAT_WIN_SPACE;
898 	chat_out_text_height = output_height - 2 * CHAT_WIN_SPACE;
899 
900 	widget_resize (chat_win, chat_scroll_id, CHAT_WIN_SCROLL_WIDTH, scroll_height);
901 	widget_move (chat_win, chat_scroll_id, scroll_x, CLOSE_SIZE);
902 
903 	widget_resize (chat_win, chat_tabcollection_id, inout_width, tabcol_height);
904 
905 	for (itab = 0; itab < MAX_CHAT_TABS; itab++)
906 		if (channels[itab].tab_id >= 0)
907 			widget_resize (channels[itab].tab_id, channels[itab].out_id, inout_width, output_height);
908 
909 	widget_resize (chat_win, input_widget->id, inout_width, input_height);
910 	widget_move (chat_win, input_widget->id, CHAT_WIN_SPACE, input_y);
911 
912 	update_chat_win_buffers();
913 
914 	return 0;
915 }
916 
917 
update_chat_win_buffers(void)918 void update_chat_win_buffers(void)
919 {
920 	int chat_win = get_id_MW(MW_CHAT);
921 	int itab, imsg;
922 	// recompute line breaks
923 	for (itab = 0; itab < MAX_CHAT_TABS; itab++)
924 		channels[itab].nr_lines = 0;
925 
926 	imsg = 0;
927 	while (1)
928 	{
929 		update_chat_window (&display_text_buffer[imsg], 0);
930 		if (imsg == last_message || last_message < 0)
931 			break;
932 		if (++imsg >= DISPLAY_TEXT_BUFFER_SIZE)
933 			imsg = 0;
934 	}
935 
936 	// adjust the text position and scroll bar
937 	nr_displayed_lines = get_max_nr_lines(chat_out_text_height, CHAT_FONT, 1.0);
938 	current_line = channels[active_tab].nr_lines - nr_displayed_lines;
939 	if (current_line < 0)
940 		current_line = 0;
941 	vscrollbar_set_bar_len (chat_win, chat_scroll_id, current_line);
942 	vscrollbar_set_pos (chat_win, chat_scroll_id, current_line);
943 	text_changed = 1;
944 }
945 
parse_input(char * data,int len)946 void parse_input(char *data, int len)
947 {
948 	if (len > MAX_TEXT_MESSAGE_LENGTH)
949 	{
950 		LOG_TO_CONSOLE(c_red2, command_too_long_str);
951 		return;
952 	}
953 
954 	if (data[0] == '%' && len > 1)
955 	{
956 		if ( (check_var ((char*)&(data[1]), IN_GAME_VAR) ) < 0)
957 		{
958 			send_input_text_line ((char*)data, len);
959 		}
960 	}
961 	else if ( data[0] == '#' || data[0] == char_cmd_str[0] )
962 	{
963 		test_for_console_command ((char*)data, len);
964 	}
965 	else if (data[0] == '/' && len > 1)
966 	{
967 		// Forum #58898: Please the server when sending player messages;
968 		// remove all but one space between name and message start.
969 		// do not assume data is null terminated
970 		size_t dx = 0;
971 		char *rebuf = (char *)malloc(len+1);
972 		for (dx=0; (dx < len) && (data[dx] != ' '); dx++)
973 			rebuf[dx] = data[dx];
974 		rebuf[dx] = '\0';
975 		while ((dx < len) && (data[dx] == ' '))
976 			dx++;
977 		if (dx < len)
978 		{
979 			size_t rebuf_len = 0;
980 			safe_strcat(rebuf, " ", len+1);
981 			rebuf_len = strlen(rebuf);
982 			safe_strncpy2(&rebuf[rebuf_len], &data[dx], len+1-rebuf_len, len-dx);
983 		}
984 		send_input_text_line (rebuf, strlen(rebuf));
985 		free(rebuf);
986 	}
987 	else
988 	{
989 		if(data[0] == char_at_str[0])
990 			data[0] = '@';
991 		send_input_text_line ((char*)data, len);
992 	}
993 }
994 
995 
root_key_to_input_field(SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)996 int root_key_to_input_field (SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
997 {
998 	Uint8 ch = key_to_char (key_unicode);
999 	text_field *tf;
1000 	text_message *msg;
1001 	int alt_on = key_mod & KMOD_ALT, ctrl_on = key_mod & KMOD_CTRL;
1002 	int do_reset_tab_completer = 1;
1003 
1004 	if(input_widget == NULL || (input_widget->Flags & TEXT_FIELD_EDITABLE) == 0) {
1005 		return 0;
1006 	}
1007 
1008 	tf = input_widget->widget_info;
1009 	msg = &(tf->buffer[tf->msg]);
1010 
1011 	if (key_code == SDLK_ESCAPE)
1012 	{
1013 		clear_input_line();
1014 	}
1015 	else if ((key_code == SDLK_RETURN || key_code == SDLK_KP_ENTER) && msg->len > 0)
1016 	{
1017 		parse_input(msg->data, msg->len);
1018 		add_line_to_history((char*)msg->data, msg->len);
1019 		clear_input_line();
1020 	}
1021 	else if (tf->cursor == 1 && (ch == '/' || ch == char_slash_str[0])
1022 	             && (msg->data[0] == '/' || msg->data[0]==char_slash_str[0])
1023 	             && last_pm_from[0])
1024 	{
1025 		// watch for the '//' shortcut
1026 		tf->cursor += put_string_in_buffer (msg, (unsigned char*)last_pm_from, 1);
1027 		tf->cursor += put_char_in_buffer (msg, ' ', tf->cursor);
1028 
1029 		// set invalid width to force rewrap
1030 		msg->wrap_width = 0;
1031 		tf->nr_lines = rewrap_message(msg, input_widget->fcat, input_widget->size,
1032 			input_widget->len_x - 2 * tf->x_space, &tf->cursor);
1033 	}
1034 	else if (key_code == SDLK_BACKSPACE || key_code == SDLK_DELETE
1035 #ifdef OSX
1036 		|| ch == 127
1037 #endif
1038 		|| (!alt_on && !ctrl_on && is_printable (ch) && ch != '`'))
1039 	{
1040 		if (is_printable (ch) && !get_show_window_MW(MW_TABMAP)) {
1041 			//Make sure the widget is visible.
1042 			widget_unset_flags (input_widget->window_id, input_widget->id, WIDGET_DISABLED);
1043 			widget_unset_flags (input_widget->window_id, input_widget->id, WIDGET_INVISIBLE);
1044 		}
1045 		// XXX FIXME: we've set the input widget with the
1046 		// TEXT_FIELD_NO_KEYPRESS flag so that the default key
1047 		// handler for a text field isn't called, but now
1048 		// we do want to call it directly. So we clear the flag,
1049 		// and reset it afterwards.
1050 		input_widget->Flags &= ~TEXT_FIELD_NO_KEYPRESS;
1051 		text_field_keypress (input_widget, 0, 0, key_code, key_unicode, key_mod);
1052 		input_widget->Flags |= TEXT_FIELD_NO_KEYPRESS;
1053 		if (input_text_line.len == 0)
1054 			clear_input_line();
1055 	}
1056 	else if (KEY_DEF_CMP(K_TABCOMPLETE, key_code, key_mod) && input_text_line.len > 0)
1057 	{
1058 		do_tab_complete(&input_text_line);
1059 		do_reset_tab_completer = 0;
1060 	}
1061 	else if (get_show_window_MW(MW_CONSOLE))
1062 	{
1063 		if (!chat_input_key (input_widget, 0, 0, key_code, key_unicode, key_mod))
1064 			return 0;
1065 	}
1066 	else
1067 	{
1068 		return 0;
1069 	}
1070 
1071 	if (do_reset_tab_completer)
1072 		reset_tab_completer();
1073 
1074 	tf->next_blink = cur_time + TF_BLINK_DELAY;
1075 	if (input_widget->window_id != get_id_MW(MW_CHAT)
1076 		&& tf->nr_lines != get_max_nr_lines(input_widget->len_y-2*tf->y_space, CHAT_FONT, 1.0))
1077 	{
1078 		/* Resize the input widget if needed */
1079 		widget_resize(input_widget->window_id, input_widget->id,
1080 			input_widget->len_x, tf->y_space*2 + get_text_height(tf->nr_lines, CHAT_FONT, 1.0));
1081 	}
1082 	while(tf->buffer->data[tf->cursor] == '\r' && tf->cursor < tf->buffer->len)
1083 	{
1084 		tf->cursor++;
1085 	}
1086 	return 1;
1087 }
1088 
paste_in_input_field(const Uint8 * text)1089 void paste_in_input_field (const Uint8 *text)
1090 {
1091 	text_field *tf;
1092 	text_message *msg;
1093 
1094 	if (input_widget == NULL) {
1095 		return;
1096 	} else if (input_widget->window_id == game_root_win) {
1097 		widget_unset_flags (game_root_win, input_widget->id, WIDGET_DISABLED);
1098 	}
1099 
1100 	tf = input_widget->widget_info;
1101 	msg = &(tf->buffer[tf->msg]);
1102 
1103 	tf->cursor += put_string_in_buffer(msg, text, tf->cursor);
1104 
1105 	// set invalid width to force rewrap
1106 	msg->wrap_width = 0;
1107 	tf->nr_lines = rewrap_message(msg, input_widget->fcat, input_widget->size,
1108 		input_widget->len_x - 2 * tf->x_space, &tf->cursor);
1109 	if (use_windowed_chat != 2)
1110 	{
1111 		widget_resize(input_widget->window_id, input_widget->id,
1112 			input_widget->len_x, tf->y_space*2 + get_text_height(tf->nr_lines, CHAT_FONT, 1.0));
1113 	}
1114 }
1115 
put_string_in_input_field(const Uint8 * text)1116 void put_string_in_input_field(const Uint8 *text)
1117 {
1118 	text_field *tf = input_widget->widget_info;
1119 	text_message *msg = &(tf->buffer[tf->msg]);
1120 
1121 	if(text != NULL) {
1122 		tf->cursor = msg->len = safe_snprintf((char*)msg->data, msg->size, "%s", text);
1123 		// set invalid width to force rewrap
1124 		msg->wrap_width = 0;
1125 		tf->nr_lines = rewrap_message(msg, input_widget->fcat, input_widget->size,
1126 			input_widget->len_x - 2 * tf->x_space, &tf->cursor);
1127 		if (use_windowed_chat != 2)
1128 		{
1129 			widget_resize(input_widget->window_id, input_widget->id,
1130 				input_widget->len_x, tf->y_space*2 + get_text_height(tf->nr_lines, CHAT_FONT, 1.0));
1131 		}
1132 		if(input_widget->window_id == game_root_win) {
1133 			widget_unset_flags (input_widget->window_id, input_widget->id, WIDGET_DISABLED);
1134 		}
1135 	}
1136 }
1137 
close_chat_handler(window_info * win)1138 static int close_chat_handler (window_info *win)
1139 {
1140 	// if show and hide enabled, just moved to the hidden state
1141 	if (enable_chat_show_hide)
1142 	{
1143 		chat_shown = 0;
1144 		return 1;
1145 	}
1146 
1147 	// revert to using the tab bar
1148 	// call the config function to make sure it's done properly
1149 	change_windowed_chat(&use_windowed_chat, 1);
1150 	set_var_unsaved("windowed_chat", INI_FILE_VAR);
1151 
1152 	return 1;
1153 }
1154 
ui_scale_chat_handler(window_info * win)1155 static int ui_scale_chat_handler(window_info *win)
1156 {
1157 	int tab_tag_height = tab_collection_calc_tab_height(win->font_category,
1158 		win->current_scale_small);
1159 	widget_list *w = widget_find (win->window_id, chat_tabcollection_id);
1160 
1161 	CHAT_WIN_TAG_HEIGHT = tab_tag_height;
1162 	CHAT_WIN_SCROLL_WIDTH = (int)(0.5 + win->current_scale * ELW_BOX_SIZE);
1163 	CLOSE_SIZE = win->box_size;
1164 
1165 	widget_set_size(win->window_id, chat_tabcollection_id, win->current_scale_small);
1166 
1167 	tab_collection_resize(w, win->len_x, win->len_y);
1168 	tab_collection_move(w, win->pos_x + CHAT_WIN_SPACE, win->pos_y + tab_tag_height + CHAT_WIN_SPACE);
1169 
1170 	resize_window(win->window_id, win->len_x, win->len_y);
1171 
1172 	return 1;
1173 }
1174 
change_chat_font_handler(window_info * win,font_cat cat)1175 static int change_chat_font_handler(window_info* win, font_cat cat)
1176 {
1177 	if (cat != CHAT_FONT)
1178 		return 0;
1179 	text_changed = 1;
1180 	return 1;
1181 }
1182 
create_chat_window(void)1183 static void create_chat_window(void)
1184 {
1185 	int chat_win_width = (int)(CHAT_WIN_TEXT_WIDTH * font_scales[CHAT_FONT]) + 4 * CHAT_WIN_SPACE + CHAT_WIN_SCROLL_WIDTH;
1186 	int input_height = get_text_height(3, CHAT_FONT, 1.0) + 2 * CHAT_WIN_SPACE;
1187 	int output_height = get_text_height(8, CHAT_FONT, 1.0) + 2 * CHAT_WIN_SPACE;
1188 	int chat_win_height = output_height + input_height + 3 * CHAT_WIN_SPACE + CHAT_WIN_TAG_HEIGHT;
1189 	int inout_width = CHAT_WIN_TEXT_WIDTH + 2 * CHAT_WIN_SPACE;
1190 	int tabcol_height = output_height + CHAT_WIN_TAG_HEIGHT;
1191 	int input_y = tabcol_height + 2 * CHAT_WIN_SPACE;
1192 	int chat_win = -1;
1193 
1194 	int min_width = chat_win_width * 0.5;
1195 	int min_height = 7 * CHAT_WIN_SPACE + CHAT_WIN_TAG_HEIGHT + get_text_height(2, CHAT_FONT, 1.0)
1196 		+ get_text_height(5, CHAT_FONT, 1.0);
1197 
1198 	nr_displayed_lines = get_max_nr_lines(output_height - 2*CHAT_WIN_SPACE, CHAT_FONT, 1.0);
1199 	chat_out_text_height = output_height - 2 * CHAT_WIN_SPACE;
1200 
1201 	chat_win = create_window ("Chat", game_root_win, 0, get_pos_x_MW(MW_CHAT), get_pos_y_MW(MW_CHAT), chat_win_width, chat_win_height, ELW_USE_UISCALE|ELW_WIN_DEFAULT|ELW_RESIZEABLE|ELW_CLICK_TRANSPARENT);
1202 	if (chat_win < 0 || chat_win >= windows_list.num_windows)
1203 		return;
1204 	set_id_MW(MW_CHAT, chat_win);
1205 	set_window_custom_scale(chat_win, MW_CHAT);
1206 
1207 	set_window_handler (chat_win, ELW_HANDLER_DISPLAY, &display_chat_handler);
1208 	set_window_handler (chat_win, ELW_HANDLER_RESIZE, &resize_chat_handler);
1209 	set_window_handler (chat_win, ELW_HANDLER_UI_SCALE, &ui_scale_chat_handler);
1210 	set_window_handler (chat_win, ELW_HANDLER_CLOSE, &close_chat_handler);
1211 	set_window_handler(chat_win, ELW_HANDLER_FONT_CHANGE, &change_chat_font_handler);
1212 
1213 	chat_scroll_id = vscrollbar_add_extended (chat_win, chat_scroll_id, NULL, chat_win_width - CHAT_WIN_SCROLL_WIDTH, CLOSE_SIZE, CHAT_WIN_SCROLL_WIDTH, chat_win_height - 2*CLOSE_SIZE, 0, 1.0f, 0, 1, 0);
1214 	widget_set_OnDrag (chat_win, chat_scroll_id, chat_scroll_drag);
1215 	widget_set_OnClick (chat_win, chat_scroll_id, chat_scroll_click);
1216 
1217 	chat_tabcollection_id = tab_collection_add_extended (chat_win, chat_tabcollection_id, NULL, CHAT_WIN_SPACE, CHAT_WIN_SPACE, inout_width, tabcol_height, 0, DEFAULT_SMALL_RATIO, MAX_CHAT_TABS, 0);
1218 	widget_set_OnClick (chat_win, chat_tabcollection_id, chat_tabs_click);
1219 
1220 	channels[0].tab_id = tab_add (chat_win, chat_tabcollection_id, (tab_label(CHAT_ALL))->name, 0, 0, 0);
1221 	set_window_flag (channels[0].tab_id, ELW_CLICK_TRANSPARENT);
1222 	set_window_min_size (channels[0].tab_id, 0, 0);
1223 	channels[0].out_id = text_field_add_extended(channels[0].tab_id, channels[0].out_id,
1224 		NULL, 0, 0, inout_width, output_height, 0, CHAT_FONT, 1.0,
1225 		display_text_buffer, DISPLAY_TEXT_BUFFER_SIZE, FILTER_ALL, CHAT_WIN_SPACE, CHAT_WIN_SPACE);
1226 	channels[0].chan_nr = CHAT_ALL;
1227 	channels[0].nr_lines = 0;
1228 	channels[0].open = 1;
1229 	channels[0].newchan = 0;
1230 	active_tab = 0;
1231 
1232 	if(input_widget == NULL) {
1233 		Uint32 id;
1234 		set_text_message_color (&input_text_line, 1.0f, 1.0f, 1.0f);
1235 		id = text_field_add_extended (chat_win, 19, NULL, CHAT_WIN_SPACE, input_y,
1236 			inout_width, input_height, TEXT_FIELD_BORDER|TEXT_FIELD_EDITABLE|TEXT_FIELD_NO_KEYPRESS,
1237 			CHAT_FONT, 1.0, &input_text_line, 1, FILTER_ALL,
1238 			CHAT_WIN_SPACE, CHAT_WIN_SPACE);
1239 		widget_set_OnKey (chat_win, id, (int (*)())chat_input_key);
1240 		input_widget = widget_find(chat_win, id);
1241 	}
1242 	set_window_min_size (chat_win, min_width, min_height);
1243 	ui_scale_chat_handler(&windows_list.window[chat_win]);
1244 
1245 	check_proportional_move(MW_CHAT);
1246 }
1247 
display_chat(void)1248 void display_chat(void)
1249 {
1250 	int chat_win = get_id_MW(MW_CHAT);
1251 	if (chat_win < 0)
1252 	{
1253 		create_chat_window ();
1254 	}
1255 	else
1256 	{
1257 		if(input_widget != NULL) {
1258 			input_widget->OnResize = NULL;
1259 		}
1260 		show_window (chat_win);
1261 		select_window (chat_win);
1262 	}
1263 	update_chat_win_buffers();
1264 	update_console_input_size_and_position();
1265 }
1266 
1267 ////////////////////////////////////////////////////////////////////////
1268 
1269 #define CS_MAX_DISPLAY_CHANS 10
1270 
1271 int tab_bar_win = -1;
1272 static int chan_sel_win = -1;
1273 static int chan_sel_scroll_id = -1;
1274 static int chan_sel_border = 5;
1275 static int chan_sel_sep_offset = 2;
1276 static chat_tab tabs[MAX_CHAT_TABS];
1277 static int cur_button_id = 0;
1278 static int tabs_in_use = 0;
1279 static int current_tab = 0;
1280 static int tab_bar_width = 0;
1281 static int tab_bar_height = 0;
1282 static float tab_bar_zoom = 0.75;
1283 
create_chan_name(int no,const char * name,const char * desc)1284 static chan_name *create_chan_name(int no, const char* name, const char* desc)
1285 {
1286 	chan_name *entry = malloc(sizeof(*entry));
1287 	if (!entry)
1288 		return NULL;
1289 	if ( !(entry->description = strdup(desc)) )
1290 	{
1291 		free(entry);
1292 		return NULL;
1293 	}
1294 	if ( !(entry->name = strdup(name)) )
1295 	{
1296 		free(entry->description);
1297 		free(entry);
1298 		return NULL;
1299 	}
1300 	entry->channel = no;
1301 	return entry;
1302 }
1303 
add_chan_name(int no,const char * name,const char * desc)1304 static chan_name *add_chan_name(int no, const char * name, const char * desc)
1305 {
1306 	chan_name *entry = create_chan_name(no, name, desc);
1307 	int len;
1308 
1309 	if (!entry)
1310 	{
1311 		LOG_ERROR("Memory allocation error reading channel list");
1312 		return NULL;
1313 	}
1314 	queue_push(chan_name_queue, entry);
1315 	len = chan_name_queue->nodes-CS_MAX_DISPLAY_CHANS;
1316 	if(len > 0 && chan_sel_scroll_id == -1 && chan_sel_win != -1) {
1317 		chan_sel_scroll_id = vscrollbar_add_extended (chan_sel_win, 0, NULL, 165, 20, 20, 163, 0, 1.0, 0, 1, len);
1318 	}
1319 
1320 	return entry;
1321 }
1322 
add_spec_chan_name(int no,const char * name,const char * desc)1323 static void add_spec_chan_name(int no, const char* name, const char* desc)
1324 {
1325 	chan_name *entry = create_chan_name(no, name, desc);
1326 	if (entry)
1327 		pseudo_chans[no] = entry;
1328 	else
1329 		LOG_ERROR("Memory allocation error reading channel list");
1330 }
1331 
generic_chans(void)1332 static void generic_chans(void)
1333 {	//the channel list file is missing. We'll use hard-coded values
1334 	//remake the queue, just in case we got half way through the file
1335 	queue_destroy(chan_name_queue);
1336 	queue_initialise(&chan_name_queue);
1337 	add_spec_chan_name(0, "Channel %d", "Channel %d");
1338 	add_spec_chan_name(1, "Guild", "Your guild's chat channel");
1339 	add_spec_chan_name(2, "All", "Display chat in all channels");
1340 	add_spec_chan_name(3, "None", "Messages not on any channel");
1341 	add_spec_chan_name(4, "Options", "Select which channels to join");
1342 	add_spec_chan_name(5, "History", "View all previous chat in all channels you have been on");
1343 	add_spec_chan_name(6, "Local", "Chat in your local area");
1344 	add_spec_chan_name(7, "PMs", "Private messages");
1345 	add_spec_chan_name(8, "GMs", "Guild Messages");
1346 	add_spec_chan_name(9, "Server", "Messages from the server");
1347 	add_spec_chan_name(10, "Mod", "Mod chat");
1348 	add_chan_name(1, "Newbie", "Newbie Q&A about the game");
1349 	add_chan_name(3, "Market", "Trading, hiring, and price checks");
1350 	add_chan_name(4, "EL Gen Chat", "Chat about EL topics");
1351 	add_chan_name(5, "Roleplay", "Discussion about, and Roleplaying");
1352 	add_chan_name(6, "Contests", "Contest information and sometimes chat");
1353 }
1354 
init_channel_names(void)1355 void init_channel_names(void)
1356 {
1357 	char file[256];
1358 	xmlDocPtr doc;
1359 	xmlNodePtr cur;
1360 
1361 	// Per-channel info
1362 	char *channelname;
1363 	char *channeldesc;
1364 	int channelno;
1365 
1366 	// Temp info
1367 	xmlChar *attrib;
1368 
1369 	queue_initialise(&chan_name_queue);
1370 
1371 	// Load the file, depending on WINDOWS = def|undef
1372 	// Then parse it. If that fails, fallback onto the english one. If that fails, use builtins.
1373 	safe_snprintf (file, sizeof (file), "languages/%s/strings/channels.xml", lang);
1374 
1375 	doc = xmlParseFile (file);
1376 	if (doc == NULL ) {
1377 		doc = xmlParseFile("languages/en/strings/channels.xml");
1378 		if (doc == NULL) { //darn, don't have that either?
1379 			LOG_ERROR (using_builtin_chanlist);
1380 			generic_chans();
1381 			return;
1382 		}
1383 		//well the localised version didn't load, but the 'en' version did
1384 		LOG_ERROR (using_eng_chanlist, lang);
1385 	}
1386 
1387 	// Get the root element, if it exists.
1388 	cur = xmlDocGetRootElement (doc);
1389 	if (cur == NULL) {
1390 		// Use generics. Defaulting to english, then using the fallbacks makes obfuscated, messy code.
1391 		LOG_ERROR (using_builtin_chanlist);
1392 		generic_chans();
1393 		xmlFreeDoc(doc);
1394 		return;
1395 	}
1396 
1397 	// Check the root element.
1398 	if (xmlStrcasecmp (cur->name, (const xmlChar *) "CHANNELS")) {
1399 		LOG_ERROR (xml_bad_root_node, file);
1400 		xmlFreeDoc(doc);
1401 		generic_chans();
1402 		return;
1403 	}
1404 
1405 	// Load first child node
1406 	cur = cur->xmlChildrenNode;
1407 
1408 	// Loop while we have a node, copying ATTRIBS, etc
1409 	while (cur != NULL)	{
1410 		if(cur->type != XML_ELEMENT_NODE) {
1411 			/* NO-OP. better performance to check now than later */
1412 		} else if ((!xmlStrcmp (cur->name, (const xmlChar *)"label"))) {
1413 			// Get the name.
1414 			attrib = xmlGetProp (cur, (xmlChar*)"name");
1415 			if (attrib == NULL) {
1416 				LOG_ERROR (xml_bad_node);
1417 				xmlFree (attrib);
1418 				continue;
1419 			}
1420 			if (xmlStrlen(attrib) < 1) {
1421 				LOG_ERROR (xml_bad_node);
1422 				xmlFree (attrib);
1423 				continue;
1424 			}
1425 			channelname = (char*)xmlStrdup(attrib);
1426 			xmlFree (attrib);
1427 
1428 			// Get the index number
1429 			attrib = xmlGetProp (cur, (xmlChar*)"index");
1430 			if (attrib == NULL) {
1431 				LOG_ERROR (xml_bad_node);
1432 				xmlFree (attrib);
1433 				continue;
1434 			}
1435 			if (xmlStrlen(attrib) < 1) {
1436 				LOG_ERROR (xml_bad_node);
1437 				xmlFree (attrib);
1438 				continue;
1439 			}
1440 			channelno = atoi ((char*)attrib);
1441 			xmlFree (attrib);
1442 
1443 			// Get the description.
1444 			if ((cur->children == NULL) || (strlen ((char*)cur->children->content) < 1)) {
1445 				free (channelname);
1446 				LOG_ERROR (xml_bad_node);
1447 				continue;
1448 			}
1449 			attrib = cur->children->content;
1450 			channeldesc = (char*)xmlStrdup(attrib);
1451 
1452 			// Add it.
1453 			add_spec_chan_name(channelno, channelname, channeldesc);
1454 			free(channelname);
1455 			free(channeldesc);
1456 		} else if ((!xmlStrcmp (cur->name, (const xmlChar *)"channel"))) {
1457 			// Get the channel.
1458 			attrib = xmlGetProp (cur, (xmlChar*)"number");
1459 			if (attrib == NULL){
1460 				LOG_ERROR (xml_bad_node);
1461 				xmlFree (attrib);
1462 				continue;
1463 			}
1464 			if (xmlStrlen(attrib) < 1){
1465 				LOG_ERROR (xml_bad_node);
1466 				xmlFree (attrib);
1467 				continue;
1468 			}
1469 			channelno = atoi ((char*)attrib);
1470 			xmlFree (attrib);
1471 
1472 			// Get the name.
1473 			attrib = xmlGetProp (cur, (xmlChar*)"name");
1474 			if (attrib == NULL){
1475 				LOG_ERROR (xml_bad_node);
1476 				xmlFree (attrib);
1477 				continue;
1478 			}
1479 			if (xmlStrlen(attrib) < 1){
1480 				LOG_ERROR (xml_bad_node);
1481 				xmlFree (attrib);
1482 				continue;
1483 			}
1484 			channelname = (char*)xmlStrdup(attrib);
1485 			xmlFree (attrib);
1486 
1487 			// Get the description.
1488 			if (cur->children == NULL) {
1489 				free (channelname);
1490 				LOG_ERROR (xml_bad_node);
1491 				continue;
1492 			} else if (strlen ((char*)cur->children->content) < 1) {
1493 				free (channelname);
1494 				LOG_ERROR (xml_bad_node);
1495 				continue;
1496 			}
1497 			attrib = cur->children->content;
1498 			channeldesc = (char*)xmlStrdup(attrib);
1499 
1500 			// Add it.
1501 			add_chan_name(channelno, channelname, channeldesc);
1502 			free(channelname);
1503 			free(channeldesc);
1504 		} else {
1505 			LOG_ERROR (xml_undefined_node, file, (cur->name != NULL && strlen((char*)cur->name) < 100) ? cur->name	: (const xmlChar *)"not a string");
1506 		}
1507 		cur = cur->next;         // Advance to the next node.
1508 	}
1509 	if(queue_isempty(chan_name_queue)) {
1510 		//how did we not get any channels from it?
1511 		LOG_ERROR(using_builtin_chanlist);
1512 		generic_chans();
1513 	}
1514 	xmlFreeDoc(doc);
1515 }
1516 
cleanup_chan_names(void)1517 void cleanup_chan_names(void)
1518 {
1519 	//don't call this command unless the client is closing
1520 	int i=0;
1521 	node_t *temp_node, *step = queue_front_node(chan_name_queue);
1522 	chan_name *temp_cn;
1523 	for (;i<SPEC_CHANS;++i) {
1524 		if(pseudo_chans[i] == NULL) {
1525 			continue;
1526 		}
1527 		if(pseudo_chans[i]->name != NULL) {
1528 			free(pseudo_chans[i]->name);
1529 		}
1530 		if(pseudo_chans[i]->description != NULL) {
1531 			free(pseudo_chans[i]->description);
1532 		}
1533 		free(pseudo_chans[i]);
1534 	}
1535 	while(step != NULL) {
1536 		temp_node = step;
1537 		step = step->next;
1538 		temp_cn = queue_delete_node(chan_name_queue, temp_node);
1539 		if(temp_cn == NULL || temp_cn->name == NULL || strlen(temp_cn->name) < 1) {
1540 			continue;
1541 		}
1542 		if(temp_cn->name != NULL) {
1543 			free(temp_cn->name);
1544 		}
1545 		if(temp_cn->description != NULL) {
1546 			free(temp_cn->description);
1547 		}
1548 		free(temp_cn);
1549 	}
1550 	queue_destroy(chan_name_queue);
1551 }
1552 
1553 // Not called from anywhere, I wonder what happened?  My may to restore one day.....
1554 //
1555 //int highlight_tab(const Uint8 channel)
1556 //{
1557 //	int i;
1558 //
1559 //	if(!highlight_tab_on_nick || channel == CHAT_ALL)
1560 //	{
1561 //		//We don't want to highlight
1562 //		return 0;
1563 //	}
1564 //	switch(use_windowed_chat)
1565 //	{
1566 //		case 1:
1567 //			if (tab_bar_win < 0)
1568 //			{
1569 //				//it doesn't exist
1570 //				return 0;
1571 //			}
1572 //			if(tabs[current_tab].channel != CHAT_ALL) {
1573 //				/* If we're in the All tab, we have already seen this message */
1574 //				for (i = 0; i < tabs_in_use; i++)
1575 //				{
1576 //					if (tabs[i].channel == channel)
1577 //					{
1578 //						if (current_tab != i && !tabs[i].highlighted)
1579 //						{
1580 //							widget_set_color (tab_bar_win, tabs[i].button, 1.0f, 0.0f, 0.0f);
1581 //							tabs[i].highlighted = 1;
1582 //						}
1583 //						break;
1584 //					}
1585 //				}
1586 //			}
1587 //		break;
1588 //		case 2:
1589 //			if (chat_win < 0)
1590 //			{
1591 //				//it doesn't exist
1592 //				return 0;
1593 //			}
1594 //			if(channels[active_tab].chan_nr != CHAT_ALL) {
1595 //				/* If we're in the All tab, we have already seen this message */
1596 //				for (i = 0; i < MAX_CHAT_TABS; i++)
1597 //				{
1598 //					if (channels[i].open && channels[i].chan_nr == channel)
1599 //					{
1600 //						if (i != active_tab && !channels[i].highlighted)
1601 //						{
1602 //							tab_set_label_color_by_id (chat_win, chat_tabcollection_id, channels[i].tab_id, 1.0, 0.0, 0.0);
1603 //							channels[i].highlighted = 1;
1604 //						}
1605 //						break;
1606 //					}
1607 //				}
1608 //			}
1609 //		break;
1610 //		default:
1611 //			return 0;
1612 //		break;
1613 //	}
1614 //	return 1;
1615 //}
1616 
switch_to_tab(int id)1617 static void switch_to_tab(int id)
1618 {
1619 	int i=2;
1620 	widget_set_color (tab_bar_win, tabs[current_tab].button, gui_color[0], gui_color[1], gui_color[2]);
1621 	widget_set_color (tab_bar_win, tabs[0].button,  0.5f, 0.75f, 1.0f);
1622 	widget_set_color (tab_bar_win, tabs[1].button,  0.5f, 0.75f, 1.0f);
1623 	for(;i < MAX_CHAT_TABS; ++i) {
1624 		if(tabs[i].button <= 0) {
1625 			continue;
1626 		} else if(tabs[i].highlighted) {
1627 			continue;
1628 		}
1629 		widget_set_color (tab_bar_win, tabs[i].button, gui_color[0], gui_color[1], gui_color[2]);
1630 	}
1631 	current_tab = id;
1632 	widget_set_color (tab_bar_win, tabs[current_tab].button, 0.57f, 1.0f, 0.59f);
1633 	current_filter = tabs[current_tab].channel;
1634 	tabs[current_tab].highlighted = 0;
1635 	if(tabs[current_tab].channel >= CHAT_CHANNEL1 && tabs[current_tab].channel <= CHAT_CHANNEL3) {
1636 		send_active_channel (tabs[current_tab].channel);
1637 		recolour_messages(display_text_buffer);
1638 	}
1639 }
1640 
tab_bar_button_click(widget_list * w,int mx,int my,Uint32 flags)1641 static int tab_bar_button_click (widget_list *w, int mx, int my, Uint32 flags)
1642 {
1643 	int itab;
1644 
1645 	for (itab = 0; itab < tabs_in_use; itab++)
1646 	{
1647 		if (w->id == tabs[itab].button)
1648 			break;
1649 	}
1650 
1651 	if (itab >= tabs_in_use)
1652 		// shouldn't happen
1653 		return 0;
1654 
1655 	if (flags&ELW_RIGHT_MOUSE)
1656 	{
1657 		display_channel_color_win(get_active_channel(tabs[itab].channel));
1658 		return 1;
1659 	}
1660 	else
1661 	{
1662 		// NOTE: This is an optimization, instead of redefining a "Tab/Button" type.
1663 		//		 Further use of this would be best served be a new definition.
1664 		// Detect clicking on 'x'
1665 		if(tabs[itab].channel == CHAT_CHANNEL1 || tabs[itab].channel == CHAT_CHANNEL2 ||
1666 		   tabs[itab].channel == CHAT_CHANNEL3)
1667 		{
1668 			int half_len = (int)(0.5 + w->size * tab_control_half_len);
1669 			int x = w->len_x - (half_len + (int)(0.5 + w->size * tab_control_border_sep));
1670 			int y = (int)(0.5 + w->size * tab_control_border_sep) + half_len;
1671 			char str[256];
1672 
1673 			// 'x' was clicked?
1674 			if(mx > x - half_len && mx < x + half_len && my > y - half_len && my < y + half_len)
1675 			{
1676 				// Drop this channel via #lc
1677 				safe_snprintf(str, sizeof(str), "%c#lc %d", RAW_TEXT, active_channels[tabs[itab].channel-CHAT_CHANNEL1]);
1678 				my_tcp_send(my_socket, (Uint8*)str, strlen(str+1)+1);
1679 				// Can I remove this?
1680 				remove_tab(tabs[itab].channel);
1681 				if(current_tab == itab) {
1682 					int i;
1683 					//We're closing the current tab, switch to the all-tab
1684 					for(i = 0; i < tabs_in_use; i++)
1685 					{
1686 						if(tabs[i].channel == CHAT_ALL)
1687 						{
1688 							switch_to_tab(i);
1689 							break;
1690 						}
1691 					}
1692 				}
1693 				do_click_sound();
1694 				return 1; //The click was handled, no need to continue
1695 			}
1696 		}
1697 
1698 
1699 		if (current_tab != itab)
1700 		{
1701 			switch_to_tab(itab);
1702 			do_click_sound();
1703 		}
1704 		lines_to_show = max_chat_lines.value;
1705 	}
1706 	return 1;
1707 }
1708 
tab_label(Uint8 chan)1709 static chan_name *tab_label (Uint8 chan)
1710 {
1711 	//return pointer after stepping through chan_name_queue
1712 	int cnr, steps=0;
1713 	node_t *step = queue_front_node(chan_name_queue);
1714 	char name[255];
1715 	char desc[255];
1716 	chan_name *res;
1717 
1718 	switch (chan)
1719 	{
1720 		case CHAT_ALL:	return pseudo_chans[2];
1721 		case CHAT_NONE:	return pseudo_chans[3];
1722 		case CHAT_LIST:	return pseudo_chans[4];
1723 		case CHAT_HIST:	return pseudo_chans[5];
1724 		case CHAT_LOCAL:	return pseudo_chans[6];
1725 		case CHAT_PERSONAL:	return pseudo_chans[7];
1726 		case CHAT_GM:	return pseudo_chans[8];
1727 		case CHAT_SERVER:	return pseudo_chans[9];
1728 		case CHAT_MOD:	return pseudo_chans[10];
1729 	}
1730 	if(chan < CHAT_CHANNEL1 || chan > CHAT_CHANNEL3 ){
1731 		// shouldn't get here...
1732 		return NULL;
1733 	}
1734 	cnr = active_channels[chan-CHAT_CHANNEL1];
1735 	if(cnr >= 1000000000){//ooh, guild channel!
1736 		return pseudo_chans[1];
1737 	}
1738 	if(step == NULL){
1739 		//say what? we don't know _any_ channels? something is very wrong...
1740 		return NULL;
1741 	}
1742 	for (; step != NULL && step->data != NULL; step = step->next, steps++){
1743 		if(((chan_name*)(step->data))->channel == cnr){
1744 			return step->data;
1745 		}
1746 		if(step->next == NULL){
1747 			break;
1748 		}
1749 	}
1750 	//we didn't find it, so we use the generic version
1751 	safe_snprintf(name, sizeof(name), pseudo_chans[0]->name, cnr);
1752 	safe_snprintf(desc, sizeof(desc), pseudo_chans[0]->description, cnr);
1753 	res = add_chan_name(cnr, name, desc);
1754 
1755 	if (chan_sel_scroll_id >= 0 && steps > 8) {
1756 		vscrollbar_set_bar_len(chan_sel_win, chan_sel_scroll_id, steps-8);
1757 		//we're adding another name to the queue, so the window scrollbar needs to be adusted
1758 	}
1759 
1760 	return res;
1761 }
1762 
chan_int_from_name(char * name,int * return_length)1763 static unsigned int chan_int_from_name(char * name, int * return_length)
1764 {
1765 	node_t *step = queue_front_node(chan_name_queue);
1766 	char * cname = name;
1767 
1768 	while(*cname && isspace(*cname)) {	//should there be a space at the front,
1769 		cname++;						//we can handle that.
1770 	}
1771 	while(step->next != NULL) {
1772 		step = step->next;
1773 		if(!strncasecmp(((chan_name*)(step->data))->name, cname, strlen(((chan_name*)(step->data))->name))) {
1774 			if(return_length != NULL) {
1775 				*return_length = strlen(((chan_name*)(step->data))->name);
1776 			}
1777 			return ((chan_name*)(step->data))->channel;
1778 		}
1779 	}
1780 	return 0;
1781 }
1782 
update_tab_button_idx(Uint8 old_idx,Uint8 new_idx)1783 static void update_tab_button_idx (Uint8 old_idx, Uint8 new_idx)
1784 {
1785 	int itab;
1786 
1787 	if (old_idx == new_idx) return;
1788 
1789 	for (itab = 0; itab < tabs_in_use; itab++)
1790 	{
1791 		if (tabs[itab].channel == old_idx)
1792 		{
1793 			tabs[itab].channel = new_idx;
1794 			return;
1795 		}
1796 	}
1797 }
1798 
chan_tab_mouseover_handler(widget_list * widget)1799 static int chan_tab_mouseover_handler(widget_list *widget)
1800 {
1801 	int itab = 0;
1802 	if(!show_help_text){return 0;}
1803 	for (itab = 0; itab < tabs_in_use; itab++){
1804 		if ((tabs[itab].button) == (widget->id)){
1805 			window_info *win = &windows_list.window[widget->window_id];
1806 			show_help(tabs[itab].description, widget->pos_x,widget->pos_y+widget->len_y+2, win->current_scale);
1807 			return 1;
1808 		}
1809 	}
1810 	return 0;
1811 }
1812 
display_chan_sel_handler(window_info * win)1813 static int display_chan_sel_handler(window_info *win)
1814 {
1815 	int i = 0, y = chan_sel_border, x = chan_sel_border, t = 0, num_lines = 0;
1816 	int line_height;
1817 	float local_zoom = win->current_scale_small;
1818 
1819 	node_t *step = queue_front_node(chan_name_queue);
1820 	if(mouse_x >= win->pos_x+win->len_x || mouse_y >= win->pos_y+win->len_y) {
1821 		win->displayed = 0;
1822 		return 0;//auto close when you mouseout
1823 	}
1824 	t = vscrollbar_get_pos(chan_sel_win, chan_sel_scroll_id);
1825 	if(t > 0) {
1826 		for (; i<t ; ++i) {
1827 			if(step->next == NULL) {
1828 				break;
1829 			}
1830 			step = step->next;
1831 		}
1832 	}
1833 	for (i = 0; i < CS_MAX_DISPLAY_CHANS; ++i)
1834 	{
1835 		const unsigned char* name = (const unsigned char*)((chan_name*)(step->data))->name;
1836 		int width = get_string_width_zoom(name, win->font_category, local_zoom);
1837 
1838 		glColor3f(0.5f, 0.75f, 1.0f);
1839 		draw_string_zoomed(x, y, name, 1, local_zoom);
1840 		if (mouse_y > win->pos_y + y && mouse_y < win->pos_y + y + win->small_font_len_y
1841 			&& mouse_x >= win->pos_x + chan_sel_border
1842 			&& mouse_x <= win->pos_x + chan_sel_border + width)
1843 		{
1844 			show_help(((chan_name*)(step->data))->description, mouse_x-win->pos_x,mouse_y-win->pos_y-win->small_font_len_y, win->current_scale);
1845 		}
1846 		y += win->default_font_len_y;
1847 		step = step->next;
1848 		if(step == NULL) {
1849 			y += (win->default_font_len_y*(CS_MAX_DISPLAY_CHANS-i-1));
1850 			break;
1851 		}
1852 	}
1853 	glDisable(GL_TEXTURE_2D);
1854 	glColor3fv(gui_color);
1855 	glBegin(GL_LINES);
1856 		glVertex2i(0, y - chan_sel_sep_offset);
1857 		glVertex2i(win->len_x, y - chan_sel_sep_offset);
1858 	glEnd();
1859 	glEnable(GL_TEXTURE_2D);
1860 
1861 	line_height = get_line_height(UI_FONT, local_zoom);
1862 	num_lines = reset_soft_breaks((unsigned char*)channel_help_str, strlen(channel_help_str),
1863 		sizeof(channel_help_str), UI_FONT, local_zoom, win->len_x - chan_sel_border * 2,
1864 		NULL, NULL);
1865 	draw_string_zoomed(x, y+=chan_sel_border, (unsigned char*)channel_help_str,
1866 		num_lines, local_zoom);
1867 	win->len_y = win->default_font_len_y * CS_MAX_DISPLAY_CHANS
1868 		+ chan_sel_border
1869 		+ num_lines * line_height
1870 		+ chan_sel_border - chan_sel_sep_offset;
1871 #ifdef OPENGL_TRACE
1872 CHECK_GL_ERRORS();
1873 #endif //OPENGL_TRACE
1874 	return 0;
1875 }
1876 
click_chan_sel_handler(window_info * win,int mx,int my,Uint32 flags)1877 static int click_chan_sel_handler(window_info *win, int mx, int my, Uint32 flags)
1878 {
1879 	int i = 0, y = my - chan_sel_border;
1880 	node_t *step = queue_front_node(chan_name_queue);
1881 	y /= win->default_font_len_y;
1882 	i = vscrollbar_get_pos(chan_sel_win, chan_sel_scroll_id);
1883 	if(i>0){
1884 		y+=i;
1885 	}
1886 
1887 	if(flags&ELW_WHEEL_UP) {
1888 		vscrollbar_scroll_up(chan_sel_win, chan_sel_scroll_id);
1889 	} else if(flags&ELW_WHEEL_DOWN) {
1890 		vscrollbar_scroll_down(chan_sel_win, chan_sel_scroll_id);
1891 	} else {
1892 		int width;
1893 		for (i = 0; i < y ; ++i) {
1894 			if(step->next == NULL) {
1895 				return 0;
1896 			}
1897 			step = step->next;
1898 		}
1899 		width = get_string_width_zoom((const unsigned char*)((chan_name*)(step->data))->name,
1900 			win->font_category, win->current_scale_small);
1901 		if (mouse_x >= win->pos_x + chan_sel_border
1902 			&& mouse_x <= win->pos_x + chan_sel_border + width)
1903 		{
1904 			char tmp[20];
1905 			safe_snprintf(tmp, sizeof(tmp), "#jc %d", ((chan_name*)(step->data))->channel);
1906 			send_input_text_line(tmp, strlen(tmp));
1907 			do_click_sound();
1908 		}
1909 	}
1910 	return 1;
1911 }
1912 
ui_scale_chan_sel_handler(window_info * win)1913 static int ui_scale_chan_sel_handler(window_info *win)
1914 {
1915 	int len_x = (int)(0.5 + win->small_font_max_len_x * 20 + 2 * chan_sel_border + win->box_size);
1916 	int len_y = (int)(0.5 + win->default_font_len_y * CS_MAX_DISPLAY_CHANS + chan_sel_border);
1917 
1918 	resize_window(win->window_id, len_x, len_y);
1919 	widget_resize(win->window_id, chan_sel_scroll_id, win->box_size, len_y - win->box_size - chan_sel_sep_offset);
1920 	widget_move(win->window_id, chan_sel_scroll_id, len_x - win->box_size, win->box_size);
1921 
1922 	return 1;
1923 }
1924 
change_chan_sel_font_handler(window_info * win,font_cat cat)1925 static int change_chan_sel_font_handler(window_info *win, font_cat cat)
1926 {
1927 	if (cat != UI_FONT)
1928 		return 0;
1929 	ui_scale_chan_sel_handler(win);
1930 	return 1;
1931 }
1932 
tab_special_click(widget_list * w,int mx,int my,Uint32 flags)1933 static int tab_special_click(widget_list *w, int mx, int my, Uint32 flags)
1934 {
1935 	int itab = 0;
1936 	for (itab = 0; itab < tabs_in_use; itab++) {
1937 		if (tabs[itab].button == w->id){
1938 			switch(tabs[itab].channel) {
1939 				case CHAT_HIST:
1940 					toggle_window(game_root_win);
1941 					toggle_window_MW(MW_CONSOLE);
1942 					do_click_sound();
1943 					break;
1944 				case CHAT_LIST:
1945 					if(chan_sel_win >= 0) {
1946 						toggle_window(chan_sel_win);
1947 					} else {
1948 						chan_sel_win = create_window ("Channel Selection", tab_bar_win, 0, w->pos_x,w->pos_y+w->len_y+1, 100, 0, (ELW_USE_UISCALE|ELW_USE_BACKGROUND|ELW_USE_BORDER|ELW_SHOW|ELW_ALPHA_BORDER|ELW_CLOSE_BOX));
1949 						windows_list.window[chan_sel_win].back_color[3]= 0.25f;
1950 						set_window_handler (chan_sel_win, ELW_HANDLER_DISPLAY, &display_chan_sel_handler);
1951 						set_window_handler (chan_sel_win, ELW_HANDLER_CLICK, &click_chan_sel_handler);
1952 						set_window_handler (chan_sel_win, ELW_HANDLER_UI_SCALE, &ui_scale_chan_sel_handler);
1953 						set_window_handler(chan_sel_win, ELW_HANDLER_FONT_CHANGE, &change_chan_sel_font_handler);
1954 						if(chan_name_queue->nodes >= CS_MAX_DISPLAY_CHANS && chan_sel_scroll_id == -1) {
1955 							int len = chan_name_queue->nodes-CS_MAX_DISPLAY_CHANS;
1956 							chan_sel_scroll_id = vscrollbar_add_extended (chan_sel_win, 0, NULL, 0, 0, 0, 0, 0, 1.0, 0, 1, len);
1957 						}
1958 						if (chan_sel_win >= 0 && chan_sel_win < windows_list.num_windows)
1959 							ui_scale_chan_sel_handler(&windows_list.window[chan_sel_win]);
1960 					}
1961 					do_click_sound();
1962 					break;
1963 				default:
1964 					return tab_bar_button_click(w, mx, my, flags);	//grumble. this shouldn't happen
1965 			}
1966 			return 1;
1967 		}
1968 	}
1969 	return 0;
1970 }
1971 
1972 
1973 // Draw details on the channel buttons.
1974 
draw_tab_details(widget_list * W)1975 static int draw_tab_details (widget_list *W)
1976 {
1977 	int underline_sep = (int)(0.5 + W->size * tab_control_underline_sep);
1978 	int half_len = (int)(0.5 + W->size * tab_control_half_len);
1979 	int itab;
1980 
1981 	glColor3fv(gui_color);
1982 
1983 	glDisable(GL_TEXTURE_2D);
1984 
1985 	/* check for an active "#jc" channel */
1986 	for (itab = 0; itab < tabs_in_use; itab++)
1987 		if ((tabs[itab].button == W->id) && (tabs[itab].channel == CHAT_CHANNEL1 + current_channel))
1988 		{
1989 			int plus_x = W->pos_x + (int)(0.5 + W->size * tab_control_border_sep) + half_len;
1990 			int plus_y = W->pos_y + (int)(0.5 + W->size * tab_control_border_sep) + half_len;
1991 			int i, color;
1992 			/* draw the "+" for the active channel */
1993 			for(i=0; i<MAX_CHANNEL_COLORS; i++)
1994 			{
1995 				if(channel_colors[i].nr == get_active_channel(tabs[itab].channel) && channel_colors[i].color > -1)
1996 					break;
1997 			}
1998 			if(i < MAX_CHANNEL_COLORS)
1999 			{
2000 				color = channel_colors[i].color;
2001 				glColor3ub(colors_list[color].r1, colors_list[color].g1, colors_list[color].b1);
2002 			}
2003 			glBegin(GL_QUADS);
2004 				glVertex2i(plus_x - half_len, plus_y - 1);
2005 				glVertex2i(plus_x + half_len, plus_y - 1);
2006 				glVertex2i(plus_x + half_len, plus_y + 1);
2007 				glVertex2i(plus_x - half_len, plus_y + 1);
2008 				glVertex2i(plus_x - 1, plus_y - half_len);
2009 				glVertex2i(plus_x + 1, plus_y - half_len);
2010 				glVertex2i(plus_x + 1, plus_y + half_len);
2011 				glVertex2i(plus_x - 1, plus_y + half_len);
2012 			glEnd();
2013 			glColor3fv(gui_color);
2014 			/* draw a dotted underline if input would go to this channel */
2015 			if ((input_text_line.len > 0) && (input_text_line.data[0] == '@') && !((input_text_line.len > 1) && (input_text_line.data[1] == '@')))
2016 			{
2017 				glPushAttrib(GL_ENABLE_BIT|GL_LINE_BIT);
2018 				glEnable(GL_LINE_STIPPLE);
2019 				glLineStipple(1, 0xCCCC);
2020 				glLineWidth(3.0);
2021 				glBegin(GL_LINES);
2022 					glVertex2i(W->pos_x, W->pos_y + W->len_y + underline_sep);
2023 					glVertex2i(W->pos_x + W->len_x, W->pos_y + W->len_y + underline_sep);
2024 				glEnd();
2025 				glPopAttrib();
2026 			}
2027 			break;
2028 		}
2029 
2030 	/* draw the closing x */
2031 	draw_cross(W->pos_x + W->len_x - (half_len + (int)(0.5 + W->size * tab_control_border_sep)),
2032 		W->pos_y + half_len + (int)(0.5 + W->size * tab_control_border_sep), half_len, 1);
2033 	glEnable(GL_TEXTURE_2D);
2034 #ifdef OPENGL_TRACE
2035 CHECK_GL_ERRORS();
2036 #endif //OPENGL_TRACE
2037 	return 1;
2038 }
2039 
2040 
add_tab_button(Uint8 channel)2041 static int add_tab_button (Uint8 channel)
2042 {
2043 	int itab;
2044 	const char *label;
2045 	chan_name *chan;
2046 
2047 	for (itab = 0; itab < tabs_in_use; itab++)
2048 	{
2049 		if (tabs[itab].channel == channel)
2050 			// already there
2051 			return itab;
2052 	}
2053 
2054 	if (tabs_in_use >= MAX_CHAT_TABS)
2055 		// no more room. Shouldn't happen anyway.
2056 		return -1;
2057 
2058 	tabs[tabs_in_use].channel = channel;
2059 	tabs[tabs_in_use].highlighted = 0;
2060 	chan = tab_label (channel);
2061 	if(chan == NULL){
2062 		return -1;
2063 	}
2064 	label = chan->name;
2065 	tabs[tabs_in_use].description = chan->description;
2066 
2067 	tabs[tabs_in_use].button = button_add_extended(tab_bar_win, cur_button_id++, NULL, tab_bar_width, 0, 0, tab_bar_height, BUTTON_SQUARE, tab_bar_zoom, label);
2068 	if(channel == CHAT_HIST || channel == CHAT_LIST) {
2069 		//a couple of special cases
2070 		widget_set_OnClick (tab_bar_win, tabs[tabs_in_use].button, tab_special_click);
2071 		widget_set_color (tab_bar_win, tabs[tabs_in_use].button, 0.5f, 0.75f, 1.0f);
2072 	} else {
2073 		//general case
2074 		widget_set_OnClick (tab_bar_win, tabs[tabs_in_use].button, tab_bar_button_click);
2075 	}
2076 	widget_set_OnMouseover (tab_bar_win, tabs[tabs_in_use].button, chan_tab_mouseover_handler);
2077  	// Handlers for the 'x'
2078  	// Make sure it's a CHANNEL first
2079 	if(tabs[tabs_in_use].channel == CHAT_CHANNEL1 || tabs[tabs_in_use].channel == CHAT_CHANNEL2 ||
2080 		tabs[tabs_in_use].channel == CHAT_CHANNEL3)
2081  	{
2082  		widget_set_OnDraw (tab_bar_win, tabs[tabs_in_use].button, draw_tab_details);
2083  	}
2084 	tab_bar_width += widget_get_width (tab_bar_win, tabs[tabs_in_use].button)+1;
2085 	resize_window (tab_bar_win, tab_bar_width, tab_bar_height);
2086 
2087 	tabs_in_use++;
2088 	return tabs_in_use - 1;
2089 }
2090 
remove_tab_button(Uint8 channel)2091 static void remove_tab_button (Uint8 channel)
2092 {
2093 	int itab, w;
2094 
2095 	for (itab = 0; itab < tabs_in_use; itab++)
2096 	{
2097 		if (tabs[itab].channel == channel)
2098 			break;
2099 	}
2100 	if (itab >= tabs_in_use) return;
2101 
2102 	w = widget_get_width (tab_bar_win, tabs[itab].button)+1;
2103 	widget_destroy (tab_bar_win, tabs[itab].button);
2104 	for (++itab; itab < tabs_in_use; itab++)
2105 	{
2106 		widget_move_rel (tab_bar_win, tabs[itab].button, -w, 0);
2107 		tabs[itab-1] = tabs[itab];
2108 	}
2109 	tabs_in_use--;
2110 
2111 	tab_bar_width -= w;
2112 	resize_window (tab_bar_win, tab_bar_width, tab_bar_height);
2113 }
2114 
update_tab_bar(text_message * msg)2115 static void update_tab_bar (text_message * msg)
2116 {
2117 	int itab, new_button;
2118 	Uint8 channel;
2119 
2120 	// dont need to care for deleted messages
2121 	if (msg->deleted) return;
2122 
2123 	// don't bother if there's no tab bar
2124 	if (tab_bar_win < 0) return;
2125 
2126 	// Only update specific channels
2127 	channel = get_tab_channel (msg->chan_idx);
2128 	if (channel == CHAT_ALL || channel == CHAT_MODPM) {
2129 		lines_to_show += rewrap_message(msg, CHAT_FONT, 1.0,
2130 			get_console_text_width(), NULL);
2131 		if (lines_to_show >= max_chat_lines.value) lines_to_show = max_chat_lines.value;
2132 		return;
2133 	}
2134 
2135 	if (tabs[current_tab].channel == CHAT_ALL) {
2136 		lines_to_show += rewrap_message(msg, CHAT_FONT, 1.0,
2137 			get_console_text_width(), NULL);
2138 		if (lines_to_show >= max_chat_lines.value) lines_to_show = max_chat_lines.value;
2139 	}
2140 
2141 	for (itab = 2; itab < tabs_in_use; itab++)
2142 	{
2143 		if (tabs[itab].channel == channel)
2144 		{
2145 			if (current_tab != itab && !tabs[itab].highlighted && tabs[current_tab].channel != CHAT_ALL && !get_show_window_MW(MW_CONSOLE))
2146 				widget_set_color (tab_bar_win, tabs[itab].button, 1.0f, 1.0f, 0.0f);
2147 			if (current_tab == itab) {
2148 				lines_to_show += rewrap_message(msg, CHAT_FONT, 1.0,
2149 					get_console_text_width(), NULL);
2150 				if (lines_to_show >= max_chat_lines.value) lines_to_show = max_chat_lines.value;
2151 			}
2152 			return;
2153 		}
2154 	}
2155 
2156 	// we need a new button
2157 	new_button = add_tab_button (channel);
2158 	if(tabs[current_tab].channel != CHAT_ALL) {
2159 		widget_set_color (tab_bar_win, tabs[new_button].button, 1.0f, 1.0f, 0.0f);
2160 	}
2161 }
2162 
ui_scale_tab_bar_handler(window_info * win)2163 static int ui_scale_tab_bar_handler(window_info *win)
2164 {
2165 	size_t i;
2166 
2167 	tab_bar_height = win->default_font_len_y;
2168 	tab_bar_zoom = win->current_scale * 0.75;
2169 	tab_bar_width = 0;
2170 
2171 	for (i=0; i<tabs_in_use; i++)
2172 	{
2173 		button_resize(win->window_id, tabs[i].button, 0, tab_bar_height, tab_bar_zoom);
2174 		widget_move(win->window_id, tabs[i].button, tab_bar_width, 0);
2175 		tab_bar_width += 1 + widget_get_width(win->window_id, tabs[i].button);
2176 	}
2177 	resize_window(win->window_id, tab_bar_width, tab_bar_height);
2178 
2179 	move_window(chan_sel_win, win->pos_id, win->pos_loc, win->pos_x, win->pos_y + win->len_y + 1);
2180 
2181 	return 1;
2182 }
2183 
change_tab_bar_font_handler(window_info * win,font_cat cat)2184 static int change_tab_bar_font_handler(window_info *win, font_cat cat)
2185 {
2186 	if (cat != UI_FONT)
2187 		return 0;
2188 	ui_scale_tab_bar_handler(win);
2189 	return 1;
2190 }
2191 
create_tab_bar(void)2192 static void create_tab_bar(void)
2193 {
2194 	int tab_bar_x = 10;
2195 	int tab_bar_y = 3;
2196 
2197 	tab_bar_win = create_window ("Tab bar", -1, 0, tab_bar_x, tab_bar_y, 100, 0, ELW_USE_UISCALE|ELW_USE_BACKGROUND|ELW_SHOW);
2198 	set_window_handler(tab_bar_win, ELW_HANDLER_UI_SCALE, &ui_scale_tab_bar_handler );
2199 	set_window_handler(tab_bar_win, ELW_HANDLER_FONT_CHANGE, &change_tab_bar_font_handler);
2200 	if (tab_bar_win >= 0 && tab_bar_win < windows_list.num_windows)
2201 		ui_scale_tab_bar_handler(&windows_list.window[tab_bar_win]);
2202 
2203 	add_tab_button (CHAT_LIST);
2204 	add_tab_button (CHAT_HIST);
2205 	add_tab_button (CHAT_ALL);
2206 	//add_tab_button (CHAT_NONE);
2207 	current_tab = 2;
2208 	widget_set_color (tab_bar_win, tabs[current_tab].button, 0.57f, 1.0f, 0.59f);
2209 	current_filter = tabs[current_tab].channel;
2210 }
2211 
display_tab_bar(void)2212 void display_tab_bar(void)
2213 {
2214 	if (tab_bar_win < 0)
2215 	{
2216 		create_tab_bar ();
2217 	}
2218 	else
2219 	{
2220 		show_window (tab_bar_win);
2221 		select_window (tab_bar_win);
2222 	}
2223 	update_console_input_size_and_position();
2224 }
2225 
change_to_current_tab(const char * input)2226 static void change_to_current_tab(const char *input)
2227 {
2228 	Uint8 channel;
2229 	int itab;
2230 	int input_len = strlen(input);
2231 
2232 	if(input[0] == '@' || input[0] == char_at_str[0])
2233 	{
2234 		channel = CHAT_CHANNEL1 + current_channel;
2235 	}
2236 	else if(!strncasecmp(input, "#gm ", 4) || (!strncasecmp(input, gm_cmd_str,strlen(gm_cmd_str)) && input_len > strlen(gm_cmd_str)+1 && input[strlen(gm_cmd_str)] == ' '))
2237 	{
2238 		channel = CHAT_GM;
2239 	}
2240 	else if(!strncasecmp(input, "#mod ", 5) || (!strncasecmp(input, mod_cmd_str, strlen(mod_cmd_str)) && input_len > strlen(mod_cmd_str)+1 && input[strlen(mod_cmd_str)] == ' '))
2241 	{
2242 		channel = CHAT_MOD;
2243 	}
2244 	else if(!strncasecmp(input, "#bc ", 4) || (!strncasecmp(input, bc_cmd_str, strlen(bc_cmd_str)) && input_len > strlen(bc_cmd_str)+1 && input[strlen(bc_cmd_str)] == ' '))
2245 	{
2246 		channel = CHAT_SERVER;
2247 	}
2248 	else if(input[0] == '/' || input[0]==char_slash_str[0])
2249 	{
2250 		channel = CHAT_PERSONAL;
2251 	}
2252 	else if(input[0] == '#' || input[0] == char_cmd_str[0])
2253 	{
2254 		//We don't want to switch tab on commands.
2255 		channel = CHAT_ALL;
2256 	}
2257 	else
2258 	{
2259 		channel = CHAT_LOCAL;
2260 	}
2261 	switch (channel)
2262 	{
2263 		case CHAT_LOCAL:
2264 			if (!local_chat_separate)
2265 			{
2266 				channel = CHAT_ALL;
2267 			}
2268 		break;
2269 		case CHAT_PERSONAL:
2270 			if (!personal_chat_separate)
2271 			{
2272 				channel = CHAT_ALL;
2273 			}
2274 		break;
2275 		case CHAT_GM:
2276 			if (!guild_chat_separate)
2277 			{
2278 				channel = CHAT_ALL;
2279 			}
2280 		break;
2281 		case CHAT_SERVER:
2282 			if (!server_chat_separate)
2283 			{
2284 				channel = CHAT_ALL;
2285 			}
2286 		break;
2287 		case CHAT_MOD:
2288 			if (!mod_chat_separate)
2289 			{
2290 				channel = CHAT_ALL;
2291 			}
2292 		break;
2293 	}
2294 	if(channel != CHAT_ALL)
2295 	{
2296 		for(itab = 0; itab < tabs_in_use; itab++)
2297 		{
2298 			if(tabs[itab].channel == channel)
2299 			{
2300 				if(itab != current_tab) //We don't want to switch to the tab we're already in
2301 				{
2302 					switch_to_tab(itab);
2303 				}
2304 				return;
2305 			}
2306 		}
2307 		//We didn't find any tab to switch to, create new
2308 		itab = add_tab_button (channel);
2309 		if (itab >= 0)
2310 			switch_to_tab (itab);
2311 	}
2312 }
2313 
convert_tabs(int new_wc)2314 void convert_tabs (int new_wc)
2315 {
2316 	int iwc, ibc;
2317 	Uint8 chan;
2318 
2319 	if (new_wc == 1)
2320 	{
2321 		// first close possible remaining tab buttons that are no
2322 		// longer active
2323 		for (ibc = 2; ibc < tabs_in_use; ibc++)
2324 		{
2325 			chan = tabs[ibc].channel;
2326 			for (iwc = 0; iwc < MAX_CHAT_TABS; iwc++)
2327 			{
2328 				if (channels[iwc].chan_nr == chan && channels[iwc].open)
2329 					break;
2330 			}
2331 
2332 			if (iwc >= MAX_CHAT_TABS)
2333 				remove_tab_button (chan);
2334 		}
2335 
2336 		// now add buttons for every tab that doesn't have a button yet
2337 		for (iwc = 2; iwc < MAX_CHAT_TABS; iwc++)
2338 		{
2339 			if (channels[iwc].open)
2340 			{
2341 				chan = channels[iwc].chan_nr;
2342 				for (ibc = 0; ibc < tabs_in_use; ibc++)
2343 				{
2344 					if (tabs[ibc].channel == chan)
2345 						break;
2346 				}
2347 
2348 				if (ibc >= tabs_in_use)
2349 				{
2350 					add_tab_button (chan);
2351 				}
2352 			}
2353 		}
2354 	}
2355 	else if (new_wc == 2)
2356 	{
2357 		// first close possible remaining tabs that are no
2358 		// longer active
2359 		for (iwc = 2; iwc < MAX_CHAT_TABS; iwc++)
2360 		{
2361 			if (channels[iwc].open)
2362 			{
2363 				chan = channels[iwc].chan_nr;
2364 				for (ibc = 0; ibc < tabs_in_use; ibc++)
2365 				{
2366 					if (tabs[ibc].channel == chan)
2367 						break;
2368 				}
2369 
2370 				if (ibc >= tabs_in_use)
2371 				{
2372 					remove_chat_tab (chan);
2373 				}
2374 			}
2375 		}
2376 
2377 		// now add tabs for every button that doesn't have a tab yet
2378 		for (ibc = 2; ibc < tabs_in_use; ibc++)
2379 		{
2380 			chan = tabs[ibc].channel;
2381 			for (iwc = 0; iwc < MAX_CHAT_TABS; iwc++)
2382 			{
2383 				if (channels[iwc].chan_nr == chan && channels[iwc].open)
2384 					break;
2385 			}
2386 
2387 			if (iwc >= MAX_CHAT_TABS)
2388 				// unfortunately we have no clue about the
2389 				// number of lines written in this channel, so
2390 				// we won't see anything until new messages
2391 				// arrive. Oh well.
2392 				add_chat_tab (0, chan);
2393 		}
2394 	}
2395 }
2396 
command_jlc(char * text,int len)2397 int command_jlc(char * text, int len)
2398 {
2399 	unsigned int num;
2400 	char number[12];
2401 
2402 	num = chan_int_from_name(text, NULL);
2403 	if(num <= 0) {
2404 		return 0;//Don't know this name
2405 	}
2406 	safe_snprintf(number, sizeof(number), " %d", num);
2407 	if(strlen(number) <= strlen(text)) { //it is entirely possible that the number
2408 		safe_strncpy(text, number, strlen(text));	//could be longer than the name, and hence we may
2409 	}							//not have enough storage space to replace the name
2410 	return 0; //note: this change could also put us over the 160-char limit if not checked
2411 }
2412 
2413 ////////////////////////////////////////////////////////////////////////
2414 //  channel color stuff
2415 
2416 #define COLROWS 7
2417 #define COLCOLS 4
2418 
2419 static Uint32 channel_to_change = 0;
2420 static int selected_color = -1;
2421 static int channel_colors_set = 0;
2422 static int channel_color_win = -1;
2423 static int color_set_button_id = COLROWS * COLCOLS;
2424 static int color_delete_button_id = COLROWS * COLCOLS + 1;
2425 static int color_label_id = COLROWS * COLCOLS + 2;
2426 
display_channel_color_handler(window_info * win)2427 static int display_channel_color_handler(window_info *win)
2428 {
2429 	char string[50] = {0};
2430 
2431 	safe_snprintf(string, sizeof(string), "%s %i", channel_color_str, channel_to_change);
2432 	label_set_text(channel_color_win, color_label_id, string);
2433 	return 1;
2434 }
2435 
set_button_highlight(widget_list * w,int set_on)2436 static void set_button_highlight(widget_list *w, int set_on)
2437 {
2438 	if (w == NULL)
2439 		return;
2440 	if (set_on)
2441 		widget_set_flags(w->window_id, w->id, BUTTON_ACTIVE);
2442 	else
2443 		widget_unset_flags(w->window_id, w->id, BUTTON_ACTIVE);
2444 }
2445 
click_channel_color_handler(widget_list * w,int mx,int my,Uint32 flags)2446 static int click_channel_color_handler(widget_list *w, int mx, int my, Uint32 flags)
2447 {
2448 	if(w->id < COLROWS * COLCOLS)
2449 	{
2450 		set_button_highlight(widget_find(w->window_id, selected_color), 0);
2451 		set_button_highlight(w, 1);
2452 		selected_color = w->id;
2453 		do_click_sound();
2454 		return 1;
2455 	}
2456 	if(w->id == color_set_button_id)
2457 	{
2458 		if(channel_to_change > 0 && selected_color >= 0)
2459 		{
2460 			int i;
2461 			for(i=0; i<MAX_CHANNEL_COLORS; i++)
2462 			{
2463 				if(channel_colors[i].nr == channel_to_change)
2464 					break;
2465 			}
2466 			if(i<MAX_CHANNEL_COLORS)
2467 			{
2468 				channel_colors[i].color = selected_color;
2469 				do_click_sound();
2470 				hide_window(channel_color_win);
2471 				channel_to_change = 0;
2472 				selected_color = -1;
2473 				channel_colors_set = 1;
2474 				return 1;
2475 			}
2476 			for(i=0; i<MAX_CHANNEL_COLORS; i++)
2477 			{
2478 				if(channel_colors[i].nr == 0)
2479 					break;
2480 			}
2481 
2482 			if(i<MAX_CHANNEL_COLORS)
2483 			{
2484 				channel_colors[i].nr = channel_to_change;
2485 				channel_colors[i].color = selected_color;
2486 				do_click_sound();
2487 				hide_window(channel_color_win);
2488 				channel_to_change = 0;
2489 				selected_color = -1;
2490 				channel_colors_set = 1;
2491 				return 1;
2492 			} else {
2493 				LOG_TO_CONSOLE(c_red3, "You reached the maximum numbers of channel colors.");
2494 				return 1;
2495 			}
2496 		}
2497 		return 0;
2498 	}
2499 	if(w->id == color_delete_button_id)
2500 	{
2501 		int i;
2502 		for(i=0; i<MAX_CHANNEL_COLORS; i++)
2503 		{
2504 			if(channel_colors[i].nr == channel_to_change)
2505 				break;
2506 		}
2507 		if(i<MAX_CHANNEL_COLORS)
2508 		{
2509 			channel_colors[i].color = -1;
2510 			channel_colors[i].nr = 0;
2511 			channel_colors_set = 1;
2512 		}
2513 		do_click_sound();
2514 		hide_window(channel_color_win);
2515 		channel_to_change = 0;
2516 		selected_color = -1;
2517 		return 1;
2518 	}
2519 	return 0;
2520 }
2521 
hide_channel_color_handler(window_info * win)2522 static int hide_channel_color_handler(window_info *win)
2523 {
2524 	set_button_highlight(widget_find(win->window_id, selected_color), 0);
2525 	return 1;
2526 }
2527 
ui_scale_color_handler(window_info * win)2528 static int ui_scale_color_handler(window_info *win)
2529 {
2530 	int row, col;
2531 	int button_width = (int)(0.5 + 110 * win->current_scale);
2532 	int button_height = (int)(0.5 + 30 * win->current_scale);
2533 	int spacing = (int)(0.5 + win->current_scale * 10);
2534 	float label_size = 0.9 * win->current_scale;
2535 	int buttons_offset = 2 * spacing + label_size * win->default_font_len_y;
2536 	int len_x = 0;
2537 	int len_y = 0;
2538 
2539 	widget_set_size(win->window_id, color_label_id, 0.9 * win->current_scale);
2540 	widget_resize(win->window_id, color_label_id, widget_get_width(win->window_id, color_label_id) * win->current_scale, widget_get_height(win->window_id, color_label_id) * win->current_scale);
2541 	widget_move(win->window_id, color_label_id, spacing, spacing);
2542 
2543 	for (row = 0; row < COLROWS; row++)
2544 		for (col = 0; col < COLCOLS; col++)
2545 		{
2546 			button_resize(win->window_id, row + COLROWS * col, button_width, button_height, win->current_scale);
2547 			widget_move(win->window_id, row + COLROWS * col, spacing + col * (button_width + spacing), buttons_offset + row * (button_height + spacing));
2548 		}
2549 
2550 	button_resize(win->window_id, color_set_button_id, 0, 0, win->current_scale);
2551 	button_resize(win->window_id, color_delete_button_id, 0, 0, win->current_scale);
2552 
2553 	len_x = spacing * (COLCOLS + 1) + COLCOLS * button_width;
2554 	len_y = buttons_offset + COLROWS * (button_height + spacing) + widget_get_height(win->window_id, color_set_button_id) + spacing;
2555 
2556 	widget_move(win->window_id, color_set_button_id, (len_x/2 - widget_get_width(win->window_id, color_set_button_id)) / 2, len_y - spacing - widget_get_height(win->window_id, color_set_button_id));
2557 	widget_move(win->window_id, color_delete_button_id, len_x/2 + (len_x/2 - widget_get_width(win->window_id, color_delete_button_id)) / 2, len_y - spacing - widget_get_height(win->window_id, color_delete_button_id));
2558 
2559 	resize_window(win->window_id, len_x, len_y);
2560 	if (win->pos_y - win->title_height < get_tab_bar_y() * 1.25)
2561 		move_window(win->window_id, win->pos_id, win->pos_loc, win->pos_x, win->title_height + get_tab_bar_y() * 1.25);
2562 
2563 	return 1;
2564 }
2565 
display_channel_color_win(Uint32 channel_number)2566 static int display_channel_color_win(Uint32 channel_number)
2567 {
2568 	if(channel_number == 0){
2569 		return -1;
2570 	}
2571 
2572 	channel_to_change = channel_number;
2573 
2574 	if(channel_color_win < 0)
2575 	{
2576 		/* Create the window */
2577 		channel_color_win = create_window(channel_color_title_str, -1, 0, 300, 0, 0, 0, ELW_USE_UISCALE|ELW_WIN_DEFAULT);
2578 		set_window_handler(channel_color_win, ELW_HANDLER_DISPLAY, &display_channel_color_handler);
2579 		set_window_handler(channel_color_win, ELW_HANDLER_HIDE, &hide_channel_color_handler);
2580 		set_window_handler(channel_color_win, ELW_HANDLER_UI_SCALE, &ui_scale_color_handler);
2581 
2582 		/* Add labels */
2583 		color_label_id = label_add_extended(channel_color_win, color_label_id, NULL, 0, 0, 0, 0.9f, channel_color_str);
2584 
2585 		/* Add color buttons */
2586 		{
2587 			int row, col;
2588 			char *name[COLROWS][COLROWS] = {{"red1", "red2", "red3", "red4"},
2589 							    {"orange1", "orange2", "orange3", "orange4" },
2590 							    {"yellow1", "yellow2", "yellow3", "yellow4"},
2591 							    {"green1", "green2", "green3", "green4"},
2592 							    {"blue1", "blue2", "blue3", "blue4"},
2593 							    {"purple1", "purple2", "purple3", "purple4"},
2594 							    {"grey1", "grey2", "grey3", "grey4"}};
2595 			for (row = 0; row < COLROWS; row++)
2596 				for (col = 0; col < COLCOLS; col++)
2597 				{
2598 					size_t the_colour = row + COLROWS * col;
2599 					button_add_extended(channel_color_win, the_colour, NULL, 0, 0, 0, 0, 0, 1.0,
2600 						name[row][col]);
2601 					widget_set_color(channel_color_win, the_colour,
2602 						colors_list[the_colour].r1/256.0, colors_list[the_colour].g1/256.0,
2603 						colors_list[the_colour].b1/256.0);
2604 					widget_set_OnClick(channel_color_win, the_colour, click_channel_color_handler);
2605 				}
2606 		}
2607 
2608 		/* Add set/delete buttons */
2609 		color_set_button_id = button_add_extended(channel_color_win, color_set_button_id, NULL, 0, 0, 0, 0, 0, 1.0, channel_color_add_str);
2610 		widget_set_OnClick(channel_color_win, color_set_button_id, click_channel_color_handler);
2611 		color_delete_button_id = button_add_extended(channel_color_win, color_delete_button_id, NULL, 0, 0, 0, 0, 0, 1.0, channel_color_delete_str);
2612 		widget_set_OnClick(channel_color_win, color_delete_button_id, click_channel_color_handler);
2613 
2614 		/* scale the window */
2615 		if (channel_color_win >= 0 && channel_color_win < windows_list.num_windows)
2616 			ui_scale_color_handler(&windows_list.window[channel_color_win]);
2617 	}
2618 	else
2619 	{
2620 		toggle_window(channel_color_win);
2621 	}
2622 
2623 	/* highlight the button if there is an active colour */
2624 	if (get_show_window(channel_color_win))
2625 	{
2626 		int i;
2627 		for(i=0; i<MAX_CHANNEL_COLORS; i++)
2628 		{
2629 			if(channel_colors[i].nr == channel_to_change)
2630 			{
2631 				if (channel_colors[i].color >= 0)
2632 				{
2633 					selected_color = channel_colors[i].color;
2634 					set_button_highlight(widget_find(channel_color_win, selected_color), 1);
2635 				}
2636 				break;
2637 			}
2638 		}
2639 	}
2640 
2641 	return channel_color_win;
2642 }
2643 
load_channel_colors()2644 void load_channel_colors ()
2645 {
2646 	char fname[128];
2647 	FILE *fp;
2648 	int i;
2649 	off_t file_size;
2650 
2651 	if (channel_colors_set) {
2652 		/*
2653 		 * save existing channel colors instead of loading them if we are already logged in
2654 		 * this will take place when relogging after disconnection
2655 		 */
2656 		save_channel_colors();
2657 		return;
2658 	}
2659 
2660 	for(i=0; i<MAX_CHANNEL_COLORS; i++)
2661 	{
2662 		channel_colors[i].nr = 0;
2663 		channel_colors[i].color = -1;
2664 	}
2665 
2666 #ifdef JSON_FILES
2667 	if (get_use_json_user_files())
2668 	{
2669 		USE_JSON_DEBUG("Loading json file");
2670 		// try to load the json file
2671 		safe_snprintf(fname, sizeof(fname), "%schannel_colors_%s.json", get_path_config(), get_lowercase_username());
2672 		if (json_load_channel_colours(fname, channel_colors, MAX_CHANNEL_COLORS) >= 0)
2673 		{
2674 			channel_colors_set = 1;
2675 			return;
2676 		}
2677 	}
2678 
2679 	// if there is no json file, or json use disabled, try to load the old binary format
2680 	USE_JSON_DEBUG("Loading binary file");
2681 #endif
2682 
2683 	/* silently ignore non existing file */
2684 	safe_snprintf(fname, sizeof(fname), "channel_colors_%s.dat",get_lowercase_username());
2685 	if (file_exists_config(fname)!=1)
2686 		return;
2687 
2688 	file_size = get_file_size_config(fname);
2689 
2690 	/* if the file exists but is not a valid size, don't use it */
2691 	if ((file_size == 0) || (file_size != sizeof(channel_colors)))
2692 	{
2693 		LOG_ERROR("%s: Invalid format (size mismatch) \"%s\"\n", reg_error_str, fname);
2694 		return;
2695 	}
2696 
2697 	fp = open_file_config(fname,"rb");
2698 	if(fp == NULL){
2699 		LOG_ERROR("%s: %s \"%s\": %s\n", reg_error_str, cant_open_file, fname, strerror(errno));
2700 		return;
2701 	}
2702 
2703 	if (fread (channel_colors,sizeof(channel_colors),1, fp) != 1)
2704 	{
2705 		LOG_ERROR("%s() fail during read of file [%s] : %s\n", __FUNCTION__, fname, strerror(errno));
2706 		fclose (fp);
2707 		return;
2708 	}
2709 
2710 	fclose (fp);
2711 	channel_colors_set = 1;
2712 }
2713 
save_channel_colors()2714 void save_channel_colors()
2715 {
2716 	char fname[128];
2717 	FILE *fp;
2718 
2719 	if (!channel_colors_set)
2720 		return;
2721 
2722 #ifdef JSON_FILES
2723 	if (get_use_json_user_files())
2724 	{
2725 		USE_JSON_DEBUG("Saving json file");
2726 		// save the json file
2727 		safe_snprintf(fname, sizeof(fname), "%schannel_colors_%s.json", get_path_config(), get_lowercase_username());
2728 		if (json_save_channel_colours(fname, channel_colors, MAX_CHANNEL_COLORS) < 0)
2729 			LOG_ERROR("%s: %s \"%s\"\n", reg_error_str, cant_open_file, fname);
2730 		return;
2731 	}
2732 	USE_JSON_DEBUG("Saving binary file");
2733 #endif
2734 
2735 	safe_snprintf(fname, sizeof(fname), "channel_colors_%s.dat",get_lowercase_username());
2736 	fp=open_file_config(fname,"wb");
2737 	if(fp == NULL){
2738 		LOG_ERROR("%s: %s \"%s\": %s\n", reg_error_str, cant_open_file, fname, strerror(errno));
2739 		return;
2740 	}
2741 
2742 	if (fwrite (channel_colors,sizeof(channel_colors),1, fp) != 1)
2743 	{
2744 		LOG_ERROR("%s() fail during write of file [%s] : %s\n", __FUNCTION__, fname, strerror(errno));
2745 	}
2746 
2747 	fclose(fp);
2748 }
2749 
command_channel_colors(char * text,int len)2750 int command_channel_colors(char * text, int len)
2751 {
2752 	int i;
2753 	char string[20];
2754 
2755 	LOG_TO_CONSOLE(c_grey1, "Your currently set channel colors:");
2756 	for(i=0; i<MAX_CHANNEL_COLORS; i++)
2757 	{
2758 		if(channel_colors[i].nr >0 && channel_colors[i].color > -1)
2759 		{
2760 			safe_snprintf(string, sizeof(string), "Channel %u", channel_colors[i].nr);
2761 			LOG_TO_CONSOLE(channel_colors[i].color, string);
2762 		}
2763 	}
2764 	return 1;
2765 }
2766 
get_tab_bar_x(void)2767 int get_tab_bar_x(void)
2768 {
2769 	if (use_windowed_chat == 1 && tab_bar_win >= 0 && tab_bar_win < windows_list.num_windows)
2770 		return windows_list.window[tab_bar_win].pos_x;
2771 	return 10;
2772 }
2773 
get_tab_bar_y(void)2774 int get_tab_bar_y(void)
2775 {
2776 	if (use_windowed_chat == 1 && tab_bar_win >= 0 && tab_bar_win < windows_list.num_windows && chat_shown)
2777 		return 4 + windows_list.window[tab_bar_win].pos_y + windows_list.window[tab_bar_win].len_y;
2778 	return 1;
2779 }
2780 
next_channel_tab(void)2781 void next_channel_tab(void)
2782 {
2783 	int next_tab;
2784 	widget_list *widget;
2785 	tab_collection *collection;
2786 	switch(use_windowed_chat)
2787 	{
2788 		case 1: //Tabs
2789 			if(current_tab == tabs_in_use-1)
2790 			{
2791 				next_tab = 2;
2792 			}
2793 			else
2794 			{
2795 				next_tab = current_tab + 1;
2796 			}
2797 			switch_to_tab(next_tab);
2798 		break;
2799 		case 2: //Window
2800 			widget = widget_find(get_id_MW(MW_CHAT), chat_tabcollection_id);
2801 			collection = widget->widget_info;
2802 			if(active_tab == collection->nr_tabs - 1)
2803 			{
2804 				next_tab = 2;
2805 			}
2806 			else
2807 			{
2808 				next_tab = active_tab + 1;
2809 			}
2810 			switch_to_chat_tab(channels[next_tab].tab_id, 0);
2811 		break;
2812 		default:
2813 			break;
2814 	}
2815 }
2816 
prev_channel_tab(void)2817 void prev_channel_tab(void)
2818 {
2819 	int next_tab;
2820 	widget_list *widget;
2821 	tab_collection *collection;
2822 	switch(use_windowed_chat)
2823 	{
2824 		case 1: //Tab
2825 			if(current_tab == 2)
2826 			{
2827 				next_tab = tabs_in_use-1;
2828 			}
2829 			else
2830 			{
2831 				next_tab = current_tab-1;
2832 			}
2833 			switch_to_tab(next_tab);
2834 			break;
2835 		case 2: //Window
2836 			widget = widget_find(get_id_MW(MW_CHAT), chat_tabcollection_id);
2837 			collection = widget->widget_info;
2838 			if(active_tab == 2)
2839 			{
2840 				next_tab = collection->nr_tabs - 1;
2841 			}
2842 			else
2843 			{
2844 				next_tab = active_tab - 1;
2845 			}
2846 			switch_to_chat_tab(channels[next_tab].tab_id, 0);
2847 		break;
2848 		default:
2849 			break;
2850 	}
2851 }
2852 
update_text_windows(text_message * pmsg)2853 void update_text_windows (text_message * pmsg)
2854 {
2855 	if (get_id_MW(MW_CONSOLE) >= 0) update_console_win (pmsg);
2856 	switch (use_windowed_chat) {
2857 		case 0:
2858 			lines_to_show += pmsg->wrap_lines;
2859 			if (lines_to_show > max_chat_lines.value) lines_to_show = max_chat_lines.value;
2860 			break;
2861 		case 1:
2862 			update_tab_bar (pmsg);
2863 			break;
2864 		case 2:
2865 			update_chat_window (pmsg, 1);
2866 			break;
2867 	}
2868 }
2869 
recolour_message(text_message * msg)2870 void recolour_message(text_message *msg){
2871 	if (msg->chan_idx >= CHAT_CHANNEL1 && msg->chan_idx <= CHAT_CHANNEL3 && msg->len > 0 && msg->data[0] && !msg->deleted)
2872 	{
2873 		int i;
2874 		for(i=0; i< MAX_CHANNEL_COLORS; i++)
2875 		{
2876 			if(channel_colors[i].nr == msg->channel)
2877 				break;
2878 		}
2879 		if(i< MAX_CHANNEL_COLORS && channel_colors[i].color != -1) {
2880 			msg->data[0] = to_color_char (channel_colors[i].color);
2881 		} else if (active_channels[current_channel] != msg->channel){
2882 			msg->data[0] = to_color_char (c_grey2);
2883 		} else {
2884 			msg->data[0] = to_color_char (c_grey1);
2885 		}
2886 	}
2887 }
2888 
recolour_messages(text_message * msgs)2889 void recolour_messages(text_message *msgs){
2890 	int i;
2891 	for(i=0;i<DISPLAY_TEXT_BUFFER_SIZE && msgs[i].data;++i){
2892 		recolour_message(&msgs[i]);
2893 	}
2894 }
2895 
reset_tab_channel_colours(void)2896 void reset_tab_channel_colours(void)
2897 {
2898 	int chat_win = get_id_MW(MW_CHAT);
2899 	int i;
2900 	for (i=0; i < MAX_CHAT_TABS; i++) {
2901 		if (channels[i].open) {
2902 			tab_set_label_color_by_id (chat_win, chat_tabcollection_id, channels[i].tab_id, -1.0f, -1.0f, -1.0f);
2903 		}
2904 	}
2905 }
2906 
2907 
skip_message(const text_message * msg,Uint8 filter)2908 int skip_message (const text_message *msg, Uint8 filter)
2909 {
2910 	int skip = 0;
2911 	int channel = msg->chan_idx;
2912 	if (filter == FILTER_ALL) return 0;
2913 	if (channel != filter)
2914 	{
2915 		switch (channel)
2916 		{
2917 			case CHAT_LOCAL:    skip = local_chat_separate;    break;
2918 			case CHAT_PERSONAL: skip = personal_chat_separate; break;
2919 			case CHAT_GM:       skip = guild_chat_separate;    break;
2920 			case CHAT_SERVER:   skip = server_chat_separate;   break;
2921 			case CHAT_MOD:      skip = mod_chat_separate;      break;
2922 			case CHAT_MODPM:    skip = 0;                      break;
2923 			default:            skip = 1;
2924 		}
2925 		return skip;
2926 	}
2927 	switch (channel) {
2928 		case CHAT_CHANNEL1:
2929 		case CHAT_CHANNEL2:
2930 		case CHAT_CHANNEL3:
2931 			skip = (msg->channel != active_channels[filter - CHAT_CHANNEL1]);
2932 	}
2933 	return skip;
2934 }
2935 
2936 static node_t *p_channel_queue = NULL;
2937 
set_first_tab_channel(void)2938 void set_first_tab_channel(void)
2939 {
2940 	p_channel_queue = queue_front_node(chan_name_queue);
2941 }
2942 
get_tab_channel_name(void)2943 const char * get_tab_channel_name(void)
2944 {
2945 	if (p_channel_queue != NULL)
2946 		return ((chan_name*)(p_channel_queue->data))->name;
2947 	else
2948 		return NULL;
2949 }
2950 
set_next_tab_channel(void)2951 void set_next_tab_channel(void)
2952 {
2953 	if (p_channel_queue != NULL)
2954 		p_channel_queue = p_channel_queue->next;
2955 }
2956 
change_to_channel_tab(const char * line)2957 void change_to_channel_tab(const char *line)
2958 {
2959 	switch(use_windowed_chat)
2960 	{
2961 		case 1:
2962 			if(tabs[current_tab].channel != CHAT_ALL) {
2963 				change_to_current_tab(line);
2964 			}
2965 		break;
2966 		case 2:
2967 			if(channels[active_tab].chan_nr != CHAT_ALL) {
2968 				change_to_current_chat_tab(line);
2969 			}
2970 		break;
2971 	}
2972 }
2973 
2974 /*
2975  * Consolidate all the input widget usage into functions here.
2976 */
2977 
get_input_default_height(void)2978 int get_input_default_height(void)
2979 {
2980 	return get_line_height(CHAT_FONT, 1.0) + 2 * INPUT_MARGIN;
2981 }
2982 
get_input_at_top_height(void)2983 int get_input_at_top_height(void)
2984 {
2985 	if (console_input_at_top && (input_widget != NULL))
2986 		return input_widget->len_y;
2987 	else
2988 		return 0;
2989 }
2990 
get_input_at_bottom_height(void)2991 int get_input_at_bottom_height(void)
2992 {
2993 	if (!console_input_at_top && (input_widget != NULL))
2994 		return input_widget->len_y;
2995 	else
2996 		return 0;
2997 }
2998 
show_console_input(void)2999 void show_console_input(void)
3000 {
3001 	if (input_widget != NULL)
3002 		widget_unset_flags (input_widget->window_id, input_widget->id, WIDGET_INVISIBLE);
3003 }
3004 
check_owned_and_show_console_input(int window_id)3005 void check_owned_and_show_console_input(int window_id)
3006 {
3007 	// moved from gamewin text_input_handler() during tidy up
3008 	if(input_widget != NULL)
3009 	{
3010 		text_field *tf = input_widget->widget_info;
3011 		tf->cursor = tf->buffer->len;
3012 		if(input_widget->window_id == window_id)
3013 			widget_unset_flags (input_widget->window_id, input_widget->id, WIDGET_DISABLED);
3014 	}
3015 }
3016 
get_console_input_cursor(void)3017 int get_console_input_cursor(void)
3018 {
3019 	if ((input_widget != NULL) && (input_widget->widget_info != NULL))
3020 	{
3021 		text_field *tf = input_widget->widget_info;
3022 		return tf->cursor;
3023 	}
3024 	else
3025 		return 0;
3026 }
3027 
set_console_input_cursor(int new_value)3028 void set_console_input_cursor(int new_value)
3029 {
3030 	if ((input_widget != NULL) && (input_widget->widget_info != NULL))
3031 	{
3032 		text_field *tf = input_widget->widget_info;
3033 		tf->cursor = new_value;
3034 	}
3035 }
3036 
update_console_input_zoom(void)3037 void update_console_input_zoom(void)
3038 {
3039 	// Original comment FIXME?
3040 	// Called from change_chat_zoom() moved here while tidying up
3041 	if (input_widget != NULL)
3042 	{
3043 		text_field *tf= input_widget->widget_info;
3044 		if (use_windowed_chat != 2)
3045 		{
3046 			int text_height = get_text_height(tf->nr_lines, CHAT_FONT, input_widget->size);
3047 			tf->x_space = tf->y_space = INPUT_MARGIN * font_scales[CHAT_FONT];
3048 			widget_resize(input_widget->window_id, input_widget->id,
3049 				input_widget->len_x, tf->y_space * 2 + text_height);
3050 		}
3051 	}
3052 }
3053 
update_console_input_size_and_position(void)3054 void update_console_input_size_and_position(void)
3055 {
3056 	if (input_widget != NULL)
3057 		input_widget_move_to_win(input_widget->window_id);
3058 }
3059 
check_and_get_console_input(int window_id)3060 void check_and_get_console_input(int window_id)
3061 {
3062 	if ((input_widget != NULL) && (input_widget->window_id != window_id))
3063 		input_widget_move_to_win(window_id);
3064 }
3065 
move_console_input_on_input_resize(void)3066 void move_console_input_on_input_resize(void)
3067 {
3068 	if ((input_widget != NULL) && ((use_windowed_chat != 2) || !get_show_window(get_id_MW(MW_CHAT))))
3069 	{
3070 		if ((input_widget->window_id >= 0) && (input_widget->window_id < windows_list.num_windows))
3071 		{
3072 			if (console_input_at_top)
3073 				widget_move(input_widget->window_id, input_widget->id, 0, get_tab_bar_y());
3074 			else
3075 				widget_move(input_widget->window_id, input_widget->id,
3076 					0, windows_list.window[input_widget->window_id].len_y - input_widget->len_y - HUD_MARGIN_Y);
3077 		}
3078 	}
3079 }
3080 
have_console_input(void)3081 int have_console_input(void)
3082 {
3083 	return (input_widget != NULL);
3084 }
3085 
create_console_input(int window_id,int widget_id,int pos_x,int pos_y,int len_x,int len_y,Uint32 flags)3086 void create_console_input(int window_id, int widget_id, int pos_x, int pos_y, int len_x, int len_y, Uint32 flags)
3087 {
3088 	Uint32 id;
3089 
3090 	if (console_input_at_top)
3091 	{
3092 		pos_y = get_tab_bar_y();
3093 		flags |= TEXT_FIELD_BORDER;
3094 	}
3095 
3096 	id = text_field_add_extended(window_id, widget_id, NULL,
3097 		pos_x, pos_y, len_x, len_y, flags,
3098 		CHAT_FONT, 1.0, &input_text_line, 1, FILTER_ALL, INPUT_MARGIN * font_scales[CHAT_FONT], INPUT_MARGIN * font_scales[CHAT_FONT]);
3099 
3100 	input_widget = widget_find(window_id, id);
3101 	input_widget->OnResize = input_field_resize;
3102 }
3103 
set_console_input_onkey(void)3104 void set_console_input_onkey(void)
3105 {
3106 	if (input_widget == NULL)
3107 		return;
3108 	widget_set_OnKey(input_widget->window_id, input_widget->id, (int (*)())chat_input_key);
3109 	if (input_text_line.len > 0)
3110 		widget_unset_flags(input_widget->window_id, input_widget->id, WIDGET_DISABLED);
3111 }
3112 
console_input_active_at_top(void)3113 int console_input_active_at_top(void)
3114 {
3115 	return (console_input_at_top && (input_widget != NULL) && (input_text_line.len > 0) && (use_windowed_chat != 2));
3116 }
3117