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