1 #include <stdlib.h>
2 #include <string.h>
3 #include <math.h>
4 #include <SDL.h>
5 #include "consolewin.h"
6 #include "asc.h"
7 #include "books.h"
8 #include "chat.h"
9 #include "console.h"
10 #include "cursors.h"
11 #include "draw_scene.h"
12 #include "elwindows.h"
13 #include "events.h"
14 #include "gamewin.h"
15 #include "gl_init.h"
16 #include "hud.h"
17 #include "init.h"
18 #include "interface.h"
19 #include "mapwin.h"
20 #include "missiles.h"
21 #include "new_character.h"
22 #if !defined OSX && !defined WINDOWS
23 #ifdef MIDDLE_MOUSE_PASTE
24 #include "paste.h"
25 #endif
26 #endif
27 #include "spells.h"
28 #include "text.h"
29 #include "special_effects.h"
30 #include "eye_candy_wrapper.h"
31 
32 /* To Do
33  * Existing bugs before I even started on the scroll bar:
34  * - Font size of zero - number of lines go crazy
35  * - Changing font size does not update total_nr_lines
36  * - Resize of chat window going very wide after font size change
37  * - Mouse paste does not resize input box
38  * - Mouse paste can resize input box (have a patch)
39  * Code Tidy:
40  * 	- Sort out len_y of console output text field - sep/margin etc
41  */
42 
43 int console_scrollbar_enabled = 1;
44 int locked_to_console = 0;
45 
46 static int console_out_id = 40;
47 static int console_in_id = 41;
48 static int console_scrollbar_id = 42;
49 
50 static int CONSOLE_Y_OFFSET = 0;
51 static const int CONSOLE_TEXT_X_BORDER = 10;
52 
53 static int nr_console_lines = 0;
54 static int total_nr_lines = 0;
55 static int scroll_up_lines = 0;
56 static int console_text_changed = 0;
57 static int console_text_width = -1;
58 static int hud_init_on_resize = 1;
59 
get_console_sep_height(void)60 static inline int get_console_sep_height(void)
61 {
62 	return get_line_height(CHAT_FONT, 1.0);
63 }
64 
update_console_scrollbar(void)65 static void update_console_scrollbar(void)
66 {
67 	int barlen = 0, barpos = 0;
68 	if (!console_scrollbar_enabled)
69 		return;
70 	if (total_nr_lines - nr_console_lines > 0)
71 	{
72 		barlen = total_nr_lines - nr_console_lines;
73 		barpos = barlen - scroll_up_lines;
74 	}
75 	vscrollbar_set_bar_len(get_id_MW(MW_CONSOLE), console_scrollbar_id, barlen);
76 	vscrollbar_set_pos(get_id_MW(MW_CONSOLE), console_scrollbar_id, barpos);
77 	//printf("pos=%d len=%d scroll_up_lines=%d total_nr_lines=%d nr_console_lines=%d\n",
78 	//	barpos, barlen, scroll_up_lines, total_nr_lines, nr_console_lines );
79 }
80 
display_console_handler(window_info * win)81 static int display_console_handler (window_info *win)
82 {
83 	static int msg = 0, offset = 0;
84 
85 	if ((get_tab_bar_y() + get_input_at_top_height()) != CONSOLE_Y_OFFSET)
86 	{
87 		CONSOLE_Y_OFFSET = get_tab_bar_y() + get_input_at_top_height();
88 		// The resize handler calls the HUD initialisation which when done from
89 		// the console display handler, causes the window to flash. As the HUD interface
90 		// is not actually changed when we change CONSOLE_Y_OFFSET, just the console
91 		// window size, we do not need to call the HUD init function in this case.
92 		// Ok, its a hack.
93 		hud_init_on_resize = 0;
94 		resize_window(win->window_id, win->len_x, win->len_y);
95 		hud_init_on_resize = 1;
96 	}
97 
98 	if (console_text_changed)
99 	{
100 		find_line_nr(total_nr_lines, total_nr_lines - nr_console_lines - scroll_up_lines,
101 			FILTER_ALL, &msg, &offset, CHAT_FONT, 1.0, console_text_width);
102 		text_field_set_buf_pos(win->window_id, console_out_id, msg, offset);
103 		update_console_scrollbar();
104 		console_text_changed = 0;
105 	}
106 
107 	draw_console_pic (cons_text);
108 	if (scroll_up_lines != 0)
109 	{
110 		glColor3f (1.0, 1.0, 1.0);
111 		draw_console_separator(CONSOLE_TEXT_X_BORDER,
112 			win->len_y - HUD_MARGIN_Y - get_input_at_bottom_height() - get_console_sep_height(),
113 			console_text_width, 1.0);
114 	}
115 	//ttlanhil: disabled, until the scrolling in console is adusted to work with filtering properly
116 	//if the users prefer that console not be filtered, the following line can be removed.
117 	//if they want it filtered, then more work can be done until it works properly
118 	//((text_field*)((widget_find(win->window_id, console_out_id))->widget_info))->chan_nr = current_filter;
119 
120 	draw_hud_interface (win);
121 
122 	display_handling_common(win);
123 
124 	return 1;
125 }
126 
keypress_console_handler(window_info * win,int mx,int my,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)127 static int keypress_console_handler (window_info *win, int mx, int my, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
128 {
129 	// first try the keypress handler for all root windows
130 	if ( keypress_root_common (key_code, key_unicode, key_mod) )
131 	{
132 		return 1;
133 	}
134 	else if(key_code == SDLK_UP)
135  	{
136 		if (total_nr_lines > nr_console_lines + scroll_up_lines)
137 		{
138 			scroll_up_lines++;
139 			console_text_changed = 1;
140 		}
141  	}
142 	else if (key_code == SDLK_DOWN)
143  	{
144 		if(scroll_up_lines > 0)
145 		{
146 			scroll_up_lines--;
147 			console_text_changed = 1;
148 		}
149 	}
150 	else if (key_mod & KMOD_ALT && key_code == SDLK_PAGEUP && total_nr_lines > nr_console_lines + scroll_up_lines)
151 	{
152 		scroll_up_lines = total_nr_lines - nr_console_lines;
153 		console_text_changed = 1;
154 	}
155 	else if (key_mod & KMOD_ALT && key_code == SDLK_PAGEDOWN && scroll_up_lines > 0)
156 	{
157 		scroll_up_lines = 0;
158 		console_text_changed = 1;
159 	}
160 	else if (key_code == SDLK_PAGEUP && total_nr_lines > nr_console_lines + scroll_up_lines)
161 	{
162 		scroll_up_lines += nr_console_lines - 1;
163 		if (nr_console_lines + scroll_up_lines > total_nr_lines)
164 			scroll_up_lines = total_nr_lines - nr_console_lines;
165 		console_text_changed = 1;
166 	}
167 	else if (key_code == SDLK_PAGEDOWN && scroll_up_lines > 0)
168 	{
169 		scroll_up_lines -= nr_console_lines - 1;
170 		if (scroll_up_lines < 0)
171 			scroll_up_lines = 0;
172 		console_text_changed = 1;
173 	}
174 	else if (KEY_DEF_CMP(K_MAP, key_code, key_mod) || KEY_DEF_CMP(K_MARKFILTER, key_code, key_mod))
175 	{
176 		if (!locked_to_console && switch_to_game_map())
177 		{
178 			// if K_MARKFILTER pressed, open the map window with the filter active
179 			if (KEY_DEF_CMP(K_MARKFILTER, key_code, key_mod))
180 				mark_filter_active = 1;
181 			hide_window (win->window_id);
182 			show_window_MW(MW_TABMAP);
183 		}
184 	}
185 	else
186 	{
187 		Uint8 ch = key_to_char (key_unicode);
188 
189 		if ((ch == '`' || KEY_DEF_CMP(K_CONSOLE, key_code, key_mod)) && !locked_to_console)
190 		{
191 			return_to_gamewin_common();
192 		}
193 		else if ( !text_input_handler (key_code, key_unicode, key_mod) )
194 		{
195 			// nothing we can handle
196 			return 0;
197 		}
198 	}
199 
200 	// we handled it, return 1 to let the window manager know
201 	return 1;
202 }
203 
recalc_message_lines(void)204 static void recalc_message_lines(void)
205 {
206 	size_t i;
207 	total_nr_lines = 0;
208 	for (i = 0; i < DISPLAY_TEXT_BUFFER_SIZE; i++)
209 	{
210 		if (display_text_buffer[i].len && !display_text_buffer[i].deleted)
211 		{
212 			total_nr_lines += rewrap_message(&display_text_buffer[i], CHAT_FONT,
213 				1.0, console_text_width, NULL);
214 		}
215 	}
216 }
217 
resize_console_handler(window_info * win,int width,int height)218 static int resize_console_handler (window_info *win, int width, int height)
219 {
220 	int scrollbar_x_adjust = (console_scrollbar_enabled) ?win->box_size :0;
221 	int console_active_width = width - HUD_MARGIN_X;
222 	int console_active_height = height - HUD_MARGIN_Y;
223 	int text_display_height = console_active_height - get_input_at_bottom_height() - get_console_sep_height() - CONSOLE_Y_OFFSET;
224 
225 	console_text_width = (int) (console_active_width - 2*CONSOLE_TEXT_X_BORDER - scrollbar_x_adjust);
226 
227 	widget_resize (win->window_id, console_out_id, console_text_width, text_display_height);
228 	widget_move (win->window_id, console_out_id, CONSOLE_TEXT_X_BORDER, CONSOLE_Y_OFFSET);
229 	input_widget_move_to_win(win->window_id);
230 
231 	nr_console_lines = get_max_nr_lines(text_display_height, CHAT_FONT, 1.0);
232 	recalc_message_lines();
233 
234 	if (console_scrollbar_enabled)
235 	{
236 		widget_resize(win->window_id, console_scrollbar_id, win->box_size, text_display_height);
237 		widget_move(win->window_id, console_scrollbar_id, console_active_width - win->box_size, CONSOLE_Y_OFFSET);
238 		update_console_scrollbar();
239 	}
240 
241 	/* making the font smaller can leave the scroll position invalid */
242 	if (scroll_up_lines && (total_nr_lines <= nr_console_lines))
243 		scroll_up_lines = 0;
244 
245 	if (get_show_window(win->window_id) && hud_init_on_resize)
246 		init_hud_interface (HUD_INTERFACE_GAME);
247 
248 	return 1;
249 }
250 
console_scroll_click(widget_list * widget,int mx,int my,Uint32 flags)251 static int console_scroll_click(widget_list *widget, int mx, int my, Uint32 flags)
252 {
253 	scroll_up_lines = (total_nr_lines - nr_console_lines) - vscrollbar_get_pos(widget->window_id, console_scrollbar_id);
254 	console_text_changed = 1;
255 	return 1;
256 }
257 
console_scroll_drag(widget_list * widget,int mx,int my,Uint32 flags,int dx,int dy)258 static int console_scroll_drag(widget_list *widget, int mx, int my, Uint32 flags, int dx, int dy)
259 {
260 	return console_scroll_click(widget, mx, my, flags);
261 }
262 
create_console_scrollbar(window_info * win)263 static void create_console_scrollbar(window_info *win)
264 {
265 	int console_active_width = window_width - HUD_MARGIN_X;
266 	int console_active_height = window_height - HUD_MARGIN_Y;
267 	if (!have_console_input())
268 		return;
269 	console_scrollbar_id = vscrollbar_add_extended(win->window_id, console_scrollbar_id, NULL,
270 		console_active_width - win->box_size, CONSOLE_Y_OFFSET,
271 		win->box_size, console_active_height - get_console_sep_height() - CONSOLE_Y_OFFSET - get_input_at_bottom_height(),
272 		0, 1.0, 0, 1, total_nr_lines-nr_console_lines);
273 	widget_set_OnDrag(win->window_id, console_scrollbar_id, console_scroll_drag);
274 	widget_set_OnClick(win->window_id, console_scrollbar_id, console_scroll_click);
275 }
276 
mouseover_console_handler(window_info * win,int mx,int my)277 static int mouseover_console_handler(window_info *win, int mx, int my)
278 {
279 	if (hud_mouse_over(win, mx, my))
280 		return 1;
281 	elwin_mouse = CURSOR_ARROW;
282 	return 1;
283 }
284 
click_console_handler(window_info * win,int mx,int my,Uint32 flags)285 static int click_console_handler(window_info *win, int mx, int my, Uint32 flags)
286 {
287 	if (hud_click(win, mx, my, flags))
288 		return 1;
289 
290 #if !defined OSX && !defined WINDOWS
291 #ifdef MIDDLE_MOUSE_PASTE
292 	if ( (flags & ELW_MID_MOUSE) )
293 	{
294 		start_paste_from_primary(NULL);
295 	}
296 	else
297 #endif
298 #endif
299 	if ( (flags & ELW_WHEEL_UP) && total_nr_lines > nr_console_lines + scroll_up_lines )
300 	{
301 		scroll_up_lines++;
302 		console_text_changed = 1;
303 	}
304 	else if ( (flags & ELW_WHEEL_DOWN) && scroll_up_lines > 0 )
305 	{
306 		scroll_up_lines--;
307 		console_text_changed = 1;
308 	}
309 	else
310 	{
311 		return 0; // we didn't handle it
312 	}
313 
314 	return 1;
315 }
316 
show_console_handler(window_info * win)317 static int show_console_handler (window_info *win)
318 {
319 	reset_tab_channel_colours();
320 	close_book_window();
321 	if (use_windowed_chat == 1) {
322 		display_tab_bar ();
323 	}
324 	return 1;
325 }
326 
get_console_text_width(void)327 int get_console_text_width(void)
328 {
329 	return console_text_width;
330 }
331 
get_total_nr_lines(void)332 int get_total_nr_lines(void)
333 {
334 	return total_nr_lines;
335 }
336 
clear_console(void)337 void clear_console(void)
338 {
339 	console_text_changed = 1;
340 	clear_lines_to_show();
341 	scroll_up_lines = 0;
342 	total_nr_lines = 0;
343 }
344 
update_console_win(text_message * msg)345 void update_console_win (text_message * msg)
346 {
347 	if (msg->deleted) {
348 		if (scroll_up_lines > msg->wrap_lines) {
349 			scroll_up_lines -= msg->wrap_lines;
350 		} else {
351 			scroll_up_lines = 0;
352 			console_text_changed = 1;
353 		}
354 		total_nr_lines -= msg->wrap_lines;
355 	} else {
356 		int nlines = rewrap_message(msg, CHAT_FONT, 1.0, console_text_width, NULL);
357 		if (scroll_up_lines == 0) {
358 			console_text_changed = 1;
359 		} else {
360 			scroll_up_lines += nlines;
361 			if(scroll_up_lines > DISPLAY_TEXT_BUFFER_SIZE){
362 				scroll_up_lines = DISPLAY_TEXT_BUFFER_SIZE;
363 			}
364 		}
365 		total_nr_lines += nlines;
366 	}
367 }
368 
toggle_console_scrollbar(int * enable)369 void toggle_console_scrollbar(int *enable)
370 {
371 	int console_root_win = get_id_MW(MW_CONSOLE);
372 	*enable = !*enable;
373 	if ((console_root_win >= 0) && (console_root_win < windows_list.num_windows))
374 	{
375 		window_info *win = &windows_list.window[console_root_win];
376 		if (!*enable)
377 			widget_destroy(console_root_win, console_scrollbar_id);
378 		else
379 			create_console_scrollbar(win);
380 		resize_console_handler (win, window_width, window_height);
381 	}
382 }
383 
ui_scale_console_handler(window_info * win)384 static int ui_scale_console_handler(window_info *win)
385 {
386 	resize_window(win->window_id, win->len_x, win->len_y);
387 	return 1;
388 }
389 
change_console_font_handler(window_info * win,font_cat cat)390 static int change_console_font_handler(window_info *win, font_cat cat)
391 {
392 	if (cat != CHAT_FONT)
393 		return 0;
394 
395 	nr_console_lines = get_max_nr_lines(window_height - get_input_at_bottom_height() - get_console_sep_height() - hud_y - CONSOLE_Y_OFFSET,
396 		CHAT_FONT, 1.0);
397 	resize_console_handler(win, window_width, window_height);
398 	return 1;
399 }
400 
create_console_root_window(int width,int height)401 void create_console_root_window (int width, int height)
402 {
403 	int console_root_win = get_id_MW(MW_CONSOLE);
404 	if (console_root_win < 0)
405 	{
406 		window_info *win = NULL;
407 		int scrollbar_x_adjust = 0;
408 		int console_active_width = width - HUD_MARGIN_X;
409 		int console_active_height = height - HUD_MARGIN_Y;
410 
411 		console_root_win = create_window ("Console", -1, -1, 0, 0, width, height, ELW_USE_UISCALE|ELW_TITLE_NONE|ELW_SHOW_LAST);
412 		set_id_MW(MW_CONSOLE, console_root_win);
413 		if (console_root_win < 0 || console_root_win >= windows_list.num_windows)
414 			return;
415 
416 		win = &windows_list.window[console_root_win];
417 		scrollbar_x_adjust = (console_scrollbar_enabled) ?win->box_size :0;
418 
419 		console_text_width = (int) (console_active_width - 2*CONSOLE_TEXT_X_BORDER - scrollbar_x_adjust);
420 
421 		set_window_handler (console_root_win, ELW_HANDLER_DISPLAY, &display_console_handler);
422 		set_window_handler (console_root_win, ELW_HANDLER_KEYPRESS, (int (*)())&keypress_console_handler);
423 		set_window_handler (console_root_win, ELW_HANDLER_RESIZE, &resize_console_handler);
424 		set_window_handler (console_root_win, ELW_HANDLER_CLICK, &click_console_handler);
425 		set_window_handler (console_root_win, ELW_HANDLER_MOUSEOVER, &mouseover_console_handler);
426 		set_window_handler (console_root_win, ELW_HANDLER_SHOW, &show_console_handler);
427 		set_window_handler (console_root_win, ELW_HANDLER_UI_SCALE, &ui_scale_console_handler);
428 		set_window_handler(console_root_win, ELW_HANDLER_FONT_CHANGE, &change_console_font_handler);
429 
430 		console_out_id = text_field_add_extended(console_root_win, console_out_id, NULL,
431 			CONSOLE_TEXT_X_BORDER, CONSOLE_Y_OFFSET, console_text_width,
432 			console_active_height - get_input_default_height() - get_console_sep_height() - CONSOLE_Y_OFFSET,
433 			0, CHAT_FONT, 1.0, display_text_buffer, DISPLAY_TEXT_BUFFER_SIZE, CHAT_ALL, 0, 0);
434 		widget_unset_color(console_root_win, console_out_id);
435 
436 		recalc_message_lines();
437 
438 		if (!have_console_input())
439 			create_console_input(console_root_win, console_in_id,
440 				0, console_active_height - get_input_default_height(), console_active_width, get_input_default_height(),
441 				(INPUT_DEFAULT_FLAGS|TEXT_FIELD_BORDER)^WIDGET_CLICK_TRANSPARENT);
442 		set_console_input_onkey();
443 
444 		nr_console_lines = get_max_nr_lines(console_active_height - get_input_at_bottom_height() - get_console_sep_height() - CONSOLE_Y_OFFSET,
445 			CHAT_FONT, 1.0);
446 
447 		if (console_scrollbar_enabled && (console_root_win >= 0) && (console_root_win < windows_list.num_windows))
448 		{
449 			create_console_scrollbar(&windows_list.window[console_root_win]);
450 			update_console_scrollbar();
451 		}
452 	}
453 }
454 
input_field_resize(widget_list * w,Uint32 x,Uint32 y)455 int input_field_resize(widget_list *w, Uint32 x, Uint32 y)
456 {
457 	int console_root_win = get_id_MW(MW_CONSOLE);
458 	window_info *console_win = &windows_list.window[console_root_win];
459 	widget_list *console_out_w = widget_find(console_root_win, console_out_id);
460 	text_field *tf = w->widget_info;
461 	text_message *msg = &(tf->buffer[tf->msg]);
462 	int console_active_height;
463 
464 	// set invalid width to force rewrap
465 	msg->wrap_width = 0;
466 	tf->nr_lines = rewrap_message(msg, w->fcat, w->size,
467 		w->len_x - 2 * tf->x_space, &tf->cursor);
468 
469 	move_console_input_on_input_resize();
470 
471 	console_active_height = console_win->len_y - HUD_MARGIN_Y - get_input_at_bottom_height() - get_console_sep_height() - CONSOLE_Y_OFFSET;
472 	widget_resize(console_root_win, console_out_id, console_out_w->len_x, console_active_height);
473 	if (console_scrollbar_enabled)
474 		widget_resize(console_root_win, console_scrollbar_id, console_win->box_size, console_active_height);
475 	nr_console_lines = get_max_nr_lines(console_out_w->len_y, CHAT_FONT, 1.0);
476 	console_text_changed = 1;
477 	return 1;
478 }
479 
history_grep(const char * text,int len)480 int history_grep (const char* text, int len)
481 {
482 	unsigned int i = 0, wraps = 1;
483 	int idx = last_message;
484 	int skip;
485 
486 	for (skip = 0; skip < len; skip++)
487 		if (text[skip] != ' ') break;
488 	if (skip >= len) return 1;
489 
490 	text += skip;
491 	len -= skip;
492 
493 	for (i = 0; i <= total_nr_lines; ++i)
494 	{
495 		if (++wraps >= display_text_buffer[idx].wrap_lines)
496 		{
497 			wraps = 1;
498 			if (--idx < 0)
499 				break;
500 		}
501 
502 		if (i <= scroll_up_lines || display_text_buffer[idx].len < len)
503 			// line is already visible, or the message is too
504 			// short to contain the search term
505 			continue;
506 
507 		if (safe_strcasestr (display_text_buffer[idx].data, display_text_buffer[idx].len, text, len))
508 		{
509 			if(i > total_nr_lines - nr_console_lines)
510 				scroll_up_lines = total_nr_lines - nr_console_lines;
511 			else
512 				scroll_up_lines = i+1;
513 			console_text_changed = 1;
514 			break;
515 		}
516 	}
517 
518 	return 1;
519 }
520