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