1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ultima/shared/std/string.h"
24 #include "ultima/nuvie/core/nuvie_defs.h"
25 #include "ultima/nuvie/conf/configuration.h"
26 #include "ultima/nuvie/misc/u6_misc.h"
27 #include "ultima/nuvie/fonts/font_manager.h"
28 #include "ultima/nuvie/fonts/font.h"
29 #include "ultima/nuvie/screen/game_palette.h"
30 #include "ultima/nuvie/gui/gui.h"
31 #include "ultima/nuvie/gui/widgets/msg_scroll.h"
32 #include "ultima/nuvie/portraits/portrait.h"
33 #include "ultima/nuvie/core/player.h"
34 #include "ultima/nuvie/gui/widgets/converse_gump.h"
35 #include "ultima/nuvie/actors/actor_manager.h"
36 #include "ultima/nuvie/actors/actor.h"
37 #include "ultima/nuvie/keybinding/keys.h"
38 
39 namespace Ultima {
40 namespace Nuvie {
41 
42 #define CURSOR_COLOR 248
43 
44 // ConverseGump Class
45 
ConverseGump(Configuration * cfg,Font * f,Screen * s)46 ConverseGump::ConverseGump(Configuration *cfg, Font *f, Screen *s) {
47 // uint16 x, y;
48 
49 	init(cfg, f);
50 	Game *game = Game::get_game();
51 	game_type = game->get_game_type();
52 
53 	scroll_width = 30;
54 	scroll_height = 8;
55 
56 // x = 8;
57 // y = 8;
58 	int gump_h;
59 	uint8 min_h, default_c;
60 	Std::string height_str;
61 	min_w = game->get_min_converse_gump_width();
62 	uint16 x_off = game->get_game_x_offset();
63 	uint16 y_off = game->get_game_y_offset();
64 	int game_h = game->get_game_height();
65 
66 	if (game_type == NUVIE_GAME_SE) {
67 		default_c = 216;
68 		min_h = 185;
69 	} else if (game_type == NUVIE_GAME_MD) {
70 		default_c = 136;
71 		min_h = 181;
72 	} else {// U6
73 		default_c = 218;
74 		min_h = 152;
75 	}
76 	cfg->value(config_get_game_key(cfg) + "/converse_height", height_str, "default");
77 
78 	if (game->is_orig_style()) {
79 		gump_h = game_h;
80 	} else {
81 		if (height_str == "default") {
82 			if (game_h > min_h * 1.5) // big enough that we probably don't want to take up the whole screen
83 				gump_h = min_h;
84 			else
85 				gump_h = game_h;
86 		} else {
87 			cfg->value(config_get_game_key(cfg) + "/converse_height", gump_h, game_h);
88 			if (gump_h < min_h)
89 				gump_h = min_h;
90 			else if (gump_h > game_h)
91 				gump_h = game_h;
92 		}
93 	}
94 
95 	GUI_Widget::Init(NULL, x_off, y_off, game->get_converse_gump_width(), (uint16)gump_h);
96 	npc_portrait = NULL;
97 	avatar_portrait = NULL;
98 	keyword_list = NULL;
99 
100 	font = game->get_font_manager()->get_conv_font();
101 
102 	found_break_char = false;
103 	cursor_wait = 0;
104 
105 	if (game->is_forcing_solid_converse_bg()) {
106 		solid_bg = true;
107 		force_solid_bg = true;
108 	} else {
109 		force_solid_bg = false;
110 		cfg->value(config_get_game_key(config) + "/converse_solid_bg", solid_bg, false);
111 	}
112 
113 	int c;
114 	cfg->value(config_get_game_key(config) + "/converse_bg_color", c, default_c);
115 	if (c < 256)
116 		converse_bg_color = (uint8)c;
117 
118 	cursor_position = 0;
119 
120 	portrait_width = frame_w = game->get_portrait()->get_portrait_width();
121 	portrait_height = frame_h = game->get_portrait()->get_portrait_height();
122 	if (game_type == NUVIE_GAME_U6) {
123 		frame_w = portrait_width + 8;
124 		frame_h = portrait_height + 9;
125 	}
126 //DEBUG(0, LEVEL_DEBUGGING, "\nMin w = %d\n", frame_w + 12 + 210);
127 }
128 
~ConverseGump()129 ConverseGump::~ConverseGump() {
130 	if (npc_portrait)
131 		free(npc_portrait);
132 	if (avatar_portrait)
133 		free(avatar_portrait);
134 	conv_keywords.clear();
135 	permitted_input_keywords.clear();
136 }
137 
set_talking(bool state,Actor * actor)138 void ConverseGump::set_talking(bool state, Actor *actor) {
139 	if (state == true) {
140 		Game::get_game()->get_keybinder()->set_enable_joy_repeat(false);
141 		found_break_char = true;
142 		conv_keywords.clear();
143 		permitted_input_keywords.clear();
144 		Show();
145 		set_input_mode(false);
146 		clear_scroll();
147 		set_found_break_char(true);
148 		bool altar = (game_type == NUVIE_GAME_U6 // Singularity is excluded on purpose
149 		              && actor->get_actor_num() >= 192 && actor->get_actor_num() <= 199);
150 		if (!altar) {
151 			add_keyword("name");
152 			add_keyword("job");
153 			add_keyword("bye");
154 		}
155 		bool cant_join = (game_type == NUVIE_GAME_U6 // statues and altars
156 		                  && actor->get_actor_num() >= 189 && actor->get_actor_num() <= 200);
157 		if (actor->is_in_party())
158 			add_keyword("leave");
159 		else if (!cant_join)
160 			add_keyword("join");
161 		if (game_type == NUVIE_GAME_U6 && !altar) {
162 			add_keyword("rune");
163 			add_keyword("mantra");
164 		}
165 		keyword_list = &conv_keywords;
166 
167 		if (avatar_portrait) {
168 			free(avatar_portrait);
169 			avatar_portrait = NULL;
170 		}
171 
172 		cursor_position = 0;
173 	} else {
174 		Game::get_game()->get_keybinder()->set_enable_joy_repeat(true);
175 	}
176 
177 	MsgScroll::set_talking(state);
178 }
179 
set_actor_portrait(Actor * a)180 void ConverseGump::set_actor_portrait(Actor *a) {
181 	if (npc_portrait)
182 		free(npc_portrait);
183 
184 	if (Game::get_game()->get_portrait()->has_portrait(a))
185 		npc_portrait = get_portrait_data(a);
186 	else
187 		npc_portrait = NULL;
188 
189 	if (avatar_portrait == NULL) {
190 		Actor *p = Game::get_game()->get_player()->get_actor();
191 		Actor *p1 = Game::get_game()->get_actor_manager()->get_actor(1);
192 		avatar_portrait = get_portrait_data(p->get_actor_num() != 0 ? p : p1); // don't use portrait 0 when in a vehicle
193 	}
194 }
195 
get_portrait_data(Actor * a)196 unsigned char *ConverseGump::get_portrait_data(Actor *a) {
197 	if (game_type == NUVIE_GAME_U6) {
198 		return create_framed_portrait(a);
199 	}
200 
201 	Portrait *p = Game::get_game()->get_portrait();
202 	return p->get_portrait_data(a);
203 }
204 
create_framed_portrait(Actor * a)205 unsigned char *ConverseGump::create_framed_portrait(Actor *a) { //FIXME U6 specific.
206 	//uint8 FRAME_W = portrait_width + 8;
207 	uint16 i;
208 	Portrait *p = Game::get_game()->get_portrait();
209 	unsigned char *portrait_data = p->get_portrait_data(a);
210 	unsigned char *framed_data = (unsigned char *)malloc(frame_w * frame_h);
211 
212 	memset(framed_data, 255, frame_w * frame_h);
213 
214 	memset(framed_data, 0, frame_w);
215 	memset(framed_data + (frame_h - 1)*frame_w, 0, frame_w);
216 	memset(framed_data + 1 * frame_w + 2, 53, 57);
217 	memset(framed_data + 2 * frame_w + 2, 57, 59);
218 
219 	memset(framed_data + 3 * frame_w + 4, 0, 57);
220 
221 	//top left corner
222 	framed_data[1 * frame_w] = 0;
223 	framed_data[1 * frame_w + 1] = 138;
224 	framed_data[2 * frame_w] = 0;
225 	framed_data[2 * frame_w + 1] = 139;
226 	framed_data[3 * frame_w] = 0;
227 	framed_data[3 * frame_w + 1] = 139;
228 	framed_data[3 * frame_w + 2] = 57;
229 	framed_data[3 * frame_w + 3] = 143;
230 
231 	for (i = 0; i < portrait_height; i++) {
232 		framed_data[(i + 4)*frame_w] = 0;
233 		framed_data[(i + 4)*frame_w + 1] = 139;
234 		framed_data[(i + 4)*frame_w + 2] = 57;
235 		framed_data[(i + 4)*frame_w + 3] = 142;
236 
237 		memcpy(&framed_data[(i + 4)*frame_w + 4], &portrait_data[i * p->get_portrait_width()], portrait_width);
238 
239 		framed_data[(i + 4)*frame_w + 4 + portrait_width] = 0;
240 		framed_data[(i + 4)*frame_w + 4 + portrait_width + 1] = 57;
241 		framed_data[(i + 4)*frame_w + 4 + portrait_width + 2] = 53;
242 		framed_data[(i + 4)*frame_w + 4 + portrait_width + 3] = 0;
243 	}
244 
245 	memset(framed_data + (frame_h - 5)*frame_w + 3, 142, 57);
246 	memset(framed_data + (frame_h - 4)*frame_w + 2, 57, 60);
247 	memset(framed_data + (frame_h - 3)*frame_w + 1, 139, 61);
248 	memset(framed_data + (frame_h - 2)*frame_w + 1, 142, 62);
249 
250 	//bottom left
251 	framed_data[(frame_h - 5)*frame_w] = 0;
252 	framed_data[(frame_h - 5)*frame_w + 1] = 139;
253 	framed_data[(frame_h - 5)*frame_w + 2] = 57;
254 	framed_data[(frame_h - 4)*frame_w] = 0;
255 	framed_data[(frame_h - 4)*frame_w + 1] = 139;
256 	framed_data[(frame_h - 3)*frame_w] = 0;
257 	framed_data[(frame_h - 2)*frame_w] = 0;
258 
259 	//top right
260 	framed_data[1 * frame_w + 59] = 50;
261 	framed_data[1 * frame_w + 59 + 1] = 49;
262 	framed_data[1 * frame_w + 59 + 2] = 49;
263 	framed_data[1 * frame_w + 59 + 3] = 15;
264 	framed_data[1 * frame_w + 59 + 4] = 0;
265 	framed_data[2 * frame_w + 59 + 2] = 15;
266 	framed_data[2 * frame_w + 59 + 3] = 49;
267 	framed_data[2 * frame_w + 59 + 4] = 0;
268 	framed_data[3 * frame_w + 59 + 2] = 57;
269 	framed_data[3 * frame_w + 59 + 3] = 49;
270 	framed_data[3 * frame_w + 59 + 4] = 0;
271 	framed_data[4 * frame_w + 59 + 3] = 50;
272 
273 	//bottom right
274 	framed_data[(frame_h - 5)*frame_w + 60] = 143;
275 	framed_data[(frame_h - 5)*frame_w + 61] = 57;
276 	framed_data[(frame_h - 5)*frame_w + 62] = 53;
277 	framed_data[(frame_h - 5)*frame_w + 63] = 0;
278 	framed_data[(frame_h - 4)*frame_w + 62] = 53;
279 	framed_data[(frame_h - 4)*frame_w + 63] = 0;
280 	framed_data[(frame_h - 3)*frame_w + 62] = 173;
281 	framed_data[(frame_h - 3)*frame_w + 63] = 0;
282 	framed_data[(frame_h - 2)*frame_w + 63] = 0;
283 
284 	free(portrait_data);
285 
286 	return framed_data;
287 }
288 
set_permitted_input(const char * allowed)289 void ConverseGump::set_permitted_input(const char *allowed) {
290 	permitted_input_keywords.clear();
291 	keyword_list = &permitted_input_keywords;
292 	MsgScroll::set_permitted_input(allowed);
293 
294 	if (yes_no_only) {
295 		add_keyword("yes");
296 		add_keyword("no");
297 	} else if (aye_nay_only) {
298 		add_keyword("aye");
299 		add_keyword("nay");
300 	} else if (numbers_only) {
301 		add_keyword("0");
302 		add_keyword("1");
303 		add_keyword("2");
304 		add_keyword("3");
305 		add_keyword("4");
306 		add_keyword("5");
307 		add_keyword("6");
308 		add_keyword("7");
309 		add_keyword("8");
310 		add_keyword("9");
311 	}
312 
313 	cursor_position = 0;
314 }
315 
clear_permitted_input()316 void ConverseGump::clear_permitted_input() {
317 	keyword_list = &conv_keywords;
318 	MsgScroll::clear_permitted_input();
319 }
320 
321 /*
322 void ConverseGump::add_token(MsgText *token)
323 {
324 	DEBUG(0,LEVEL_ALERT, "TOKEN: %s\n", token->s.c_str());
325 
326 	display_text.push_back(*token);
327 }
328 */
329 
display_string(Std::string s,Font * f,bool include_on_map_window)330 void ConverseGump::display_string(Std::string s, Font *f,  bool include_on_map_window) {
331 	if (s.empty())
332 		return;
333 
334 	MsgScroll::display_string(strip_whitespace_after_break(s), f, include_on_map_window);//, MSGSCROLL_NO_MAP_DISPLAY);
335 }
336 
strip_whitespace_after_break(Std::string s)337 Std::string ConverseGump::strip_whitespace_after_break(Std::string s) {
338 	Std::string::iterator iter;
339 	for (iter = s.begin(); iter != s.end();) {
340 		if (found_break_char == true) {
341 			char c = *iter;
342 			if (c == ' ' || c == '\t' || c == '\n' || c == '*') {
343 				iter = s.erase(iter);
344 			} else {
345 				found_break_char = false;
346 				iter++;
347 			}
348 		} else {
349 			char c = *iter;
350 			if (c == '*') {
351 				found_break_char = true;
352 			}
353 			iter++;
354 		}
355 	}
356 
357 	return s;
358 }
359 
parse_token(MsgText * token)360 bool ConverseGump::parse_token(MsgText *token) {
361 	int at_idx = token->s.findFirstOf('@', 0);
362 	int i = 0;
363 	int len = (int)token->s.length();
364 	while (at_idx != -1 && i < len) {
365 		Std::string keyword = "";
366 		for (i = at_idx + 1; i < len; i++) {
367 			char c = token->s[i];
368 			if (Common::isAlpha(c)) {
369 				keyword.push_back(c);
370 			}
371 
372 			if (!Common::isAlpha(c) || i == len - 1) {
373 				token->s.erase(at_idx, 1);
374 				i--;
375 				at_idx = token->s.findFirstOf('@', i);
376 				break;
377 			}
378 		}
379 		DEBUG(0, LEVEL_WARNING, "%s", keyword.c_str());
380 		add_keyword(keyword);
381 	}
382 
383 	parse_fm_towns_token(token);
384 	return MsgScroll::parse_token(token);
385 }
386 
387 // Add FM-Towns keywords which take the form. +actor_numKeyword+ eg. +5runes+
388 // Only add keyword if the player has met the actor given by the actor_num
parse_fm_towns_token(MsgText * token)389 void ConverseGump::parse_fm_towns_token(MsgText *token) {
390 	int at_idx = token->s.findFirstOf('+', 0);
391 	int i = 0;
392 	int len = (int)token->s.length();
393 	bool has_met = false;
394 	while (at_idx != -1 && i < len) {
395 		i = at_idx + 1;
396 		char c = token->s[i];
397 		if (i < len && Common::isDigit(c)) {
398 			const char *c_str = token->s.c_str();
399 			uint16 actor_num = (int)strtol(&c_str[i], NULL, 10);
400 			if (actor_num < 256) {
401 				Actor *actor = Game::get_game()->get_actor_manager()->get_actor(actor_num);
402 				if (actor) {
403 					has_met = actor->is_met();
404 				}
405 			}
406 			for (; Common::isDigit(c_str[i]);)
407 				i++;
408 		}
409 
410 		Std::string keyword = "";
411 		for (; i < len; i++) {
412 			char ch = token->s[i];
413 
414 			if (Common::isAlpha(ch)) {
415 				keyword.push_back(ch);
416 			}
417 
418 			if (!Common::isAlpha(ch) || i == len - 1) {
419 				token->s.erase(at_idx, (i - at_idx) + 1);
420 				i -= i - at_idx;
421 				at_idx = token->s.findFirstOf('+', i);
422 				break;
423 			}
424 		}
425 		DEBUG(0, LEVEL_WARNING, "%s", keyword.c_str());
426 		if (has_met) { //only add keyword if the player has met the actor in question.
427 			add_keyword(keyword);
428 			has_met = false;
429 		}
430 	}
431 
432 	return;
433 }
434 
add_keyword(Std::string keyword)435 void ConverseGump::add_keyword(Std::string keyword) {
436 	keyword = " *" + keyword;
437 
438 	Std::list<MsgText>::iterator iter;
439 	for (iter = keyword_list->begin(); iter != keyword_list->end(); iter++) {
440 		if (string_i_compare((*iter).s, keyword)) {
441 			return;
442 		}
443 	}
444 
445 	MsgText m_text;
446 	m_text.s = keyword;
447 	m_text.font = font;
448 	keyword_list->push_back(m_text);
449 }
450 
get_token_string_at_pos(uint16 x,uint16 y)451 Std::string ConverseGump::get_token_string_at_pos(uint16 x, uint16 y) {
452 	uint16 total_length = 0;
453 	uint16 tmp_y = area.top + portrait_height + 8 + 3 + 4;
454 	Std::list<MsgText>::iterator iter;
455 	for (iter = keyword_list->begin(); iter != keyword_list->end(); iter++) {
456 		MsgText t = *iter;
457 		uint16 token_len = font->getStringWidth(t.s.c_str());
458 
459 //		if(token_len + total_length >= (26 * 8))
460 		if (portrait_width / 2 + portrait_width + token_len + total_length + 8 >= min_w - 4) {
461 			total_length = 0;
462 			tmp_y += 10;
463 		}
464 		//t.font->drawString(screen, t.s.c_str(), area.left + portrait_width / 2 + portrait_width + 8 + total_length * 8, y + portrait_height + 8, 0);
465 		if (x > area.left + portrait_width / 2 + portrait_width + 8 + total_length && x < area.left + portrait_width / 2 + portrait_width + 8 + total_length + token_len) {
466 			if (y > tmp_y && y < tmp_y + 8) {
467 				if (!is_permanent_keyword(t.s))
468 					keyword_list->erase(iter);
469 				return t.s;
470 			}
471 		}
472 		total_length += token_len;
473 	}
474 	return "";
475 }
476 
get_token_at_cursor()477 Std::string ConverseGump::get_token_at_cursor() {
478 	uint16 i = 0;
479 	Std::list<MsgText>::iterator iter;
480 	for (iter = keyword_list->begin(); iter != keyword_list->end(); i++, iter++) {
481 		if (i == cursor_position) {
482 			Std::string keyword = (*iter).s;
483 			if (!is_permanent_keyword(keyword)) {
484 				keyword_list->erase(iter);
485 				if (permit_input)
486 					keyword = keyword.at(2); // only return first char after " *"
487 			}
488 			return keyword;
489 		}
490 	}
491 
492 	return "";
493 }
494 
input_buf_add_char(char c)495 bool ConverseGump::input_buf_add_char(char c) {
496 	input_char = 0;
497 	if (permit_input != NULL)
498 		input_buf_remove_char();
499 	input_buf.push_back(c);
500 	return true;
501 }
502 
input_buf_remove_char()503 bool ConverseGump::input_buf_remove_char() {
504 	if (input_buf.length()) {
505 		input_buf.erase(input_buf.length() - 1, 1);
506 		return true;
507 	}
508 
509 	return false;
510 }
511 
Display(bool full_redraw)512 void ConverseGump::Display(bool full_redraw) {
513 	MsgText *token;
514 	//Std::list<MsgText>::iterator iter;
515 	uint16 total_length = 0;
516 	uint16 y = area.top + portrait_height + 8 + 3;
517 
518 	if (converse_bg_color != 255 || force_solid_bg) {
519 		if (solid_bg)
520 			screen->fill(converse_bg_color, area.left, area.top, area.width(), area.height());
521 		else
522 			screen->stipple_8bit(converse_bg_color, area.left, area.top, area.width(), area.height());
523 	}
524 
525 	bool use_transparency = (game_type == NUVIE_GAME_U6) ? false : true;
526 
527 	if (npc_portrait) {
528 		screen->blit(area.left + 4, area.top + 4, npc_portrait, 8, frame_w, frame_h, frame_w, use_transparency);
529 	}
530 
531 	if (!page_break && input_mode && avatar_portrait && is_talking()) {
532 		screen->blit(area.left + portrait_width / 2 + 4, y, avatar_portrait, 8, frame_w, frame_h, frame_w, use_transparency);
533 		Std::list<MsgText>::iterator iter;
534 		sint16 i = 0;
535 		for (iter = keyword_list->begin(); iter != keyword_list->end(); i++, iter++) {
536 			MsgText t = *iter;
537 			uint16 token_len = font->getStringWidth(t.s.c_str());
538 //			 if(token_len + total_length >= (26 * 8))
539 			if (portrait_width / 2 + portrait_width + token_len + total_length + 8 >= min_w - 4) {
540 				total_length = 0;
541 				y += 10;
542 			}
543 			t.font->drawString(screen, t.s.c_str(), area.left + portrait_width / 2 + portrait_width + 8 + total_length, y + 4, 0, 0);
544 			if (cursor_position == i) {
545 				screen->fill(CURSOR_COLOR, area.left + portrait_width / 2 + portrait_width + 16 + total_length, y + 4 + 8, token_len - 8, 1);
546 			}
547 			total_length += token_len;
548 			//total_length += t.s.length();
549 		}
550 		y += 16;
551 		font->drawString(screen, " *", area.left + portrait_width / 2 + portrait_width + 8, y, 0, 0);
552 		font->drawString(screen, input_buf.c_str(), area.left + portrait_width / 2 + portrait_width + 8 + font->getStringWidth(" *"), y, 0, 0);
553 		drawCursor(area.left + portrait_width / 2 + portrait_width + 8 + font->getStringWidth(" *") + font->getStringWidth(input_buf.c_str()), y);
554 		if (cursor_position == keyword_list->size()) {
555 			screen->fill(CURSOR_COLOR, area.left + portrait_width / 2 + portrait_width + 16, y + 8, font->getStringWidth(input_buf.c_str()) + 8, 1);
556 		}
557 	}
558 
559 	y = area.top + 4;
560 	total_length = 0;
561 	Std::list<MsgLine *>::iterator iter;
562 	for (iter = msg_buf.begin(); iter != msg_buf.end(); iter++) {
563 		MsgLine *msg_line = *iter;
564 		Std::list<MsgText *>::iterator iter1;
565 
566 		for (iter1 = msg_line->text.begin(); iter1 != msg_line->text.end() ; iter1++) {
567 			token = *iter1;
568 
569 			total_length += token->font->drawString(screen, token->s.c_str(), area.left + 4 + frame_w + 4 + total_length, y + 4, 0, 0); //FIX for hardcoded font height
570 
571 			//token->s.length();
572 			//token->font->drawChar(screen, ' ', area.left + portrait_width + 8 + total_length * 8, y, 0);
573 			//total_length += 1;
574 
575 		}
576 		y += 10;
577 		total_length = 0;
578 	}
579 
580 	//font->drawString(screen, conv_str.c_str(), area.left, area.top);
581 	screen->update(area.left, area.top, area.width(), area.height());
582 }
583 
584 
KeyDown(const Common::KeyState & keyState)585 GUI_status ConverseGump::KeyDown(const Common::KeyState &keyState) {
586 	Common::KeyState key = keyState;
587 	char ascii = get_ascii_char_from_keysym(key);
588 
589 	if (page_break || !is_talking()) {
590 		page_break = false;
591 		just_finished_page_break = true;
592 		if (!input_mode)
593 			Game::get_game()->get_gui()->unlock_input();
594 		if (!is_holding_buffer_empty() || !input_mode || !is_talking()) {
595 			clear_scroll();
596 			process_holding_buffer(); // Process any text in the holding buffer.
597 		}
598 		return (GUI_YUM);
599 	}
600 
601 	if (!input_mode || !Common::isPrint(ascii)) {
602 		KeyBinder *keybinder = Game::get_game()->get_keybinder();
603 		ActionType a = keybinder->get_ActionType(key);
604 		switch (keybinder->GetActionKeyType(a)) {
605 		case WEST_KEY:
606 			key.keycode = Common::KEYCODE_LEFT;
607 			break;
608 		case EAST_KEY:
609 			key.keycode = Common::KEYCODE_RIGHT;
610 			break;
611 		case SOUTH_KEY:
612 			key.keycode = Common::KEYCODE_DOWN;
613 			break;
614 		case NORTH_KEY:
615 			key.keycode = Common::KEYCODE_UP;
616 			break;
617 		case CANCEL_ACTION_KEY:
618 			key.keycode = Common::KEYCODE_ESCAPE;
619 			break;
620 		case DO_ACTION_KEY:
621 			key.keycode = Common::KEYCODE_RETURN;
622 			break;
623 		default:
624 			if (keybinder->handle_always_available_keys(a)) return GUI_YUM;
625 			break;
626 		}
627 	}
628 
629 	switch (key.keycode) {
630 	case Common::KEYCODE_LEFT:
631 		if (cursor_at_input_section() && input_char != 0)
632 			input_char = 0;
633 		else {
634 			if (!cursor_at_input_section() || !input_buf_remove_char()) {
635 				if (cursor_position == 0) {
636 					cursor_position = keyword_list->size();
637 				} else {
638 					cursor_position--;
639 				}
640 			}
641 		}
642 		break;
643 	case Common::KEYCODE_RIGHT:
644 		if (cursor_at_input_section() && input_char != 0 && permit_input == NULL)
645 			input_buf_add_char(get_char_from_input_char());
646 		else
647 			cursor_position = (cursor_position + 1) % (keyword_list->size() + 1);
648 		break;
649 	case Common::KEYCODE_DOWN:
650 		cursor_move_to_input();
651 		increase_input_char();
652 		break;
653 	case Common::KEYCODE_UP:
654 		cursor_move_to_input();
655 		decrease_input_char();
656 		break;
657 	case Common::KEYCODE_ESCAPE:
658 		if (permit_inputescape) {
659 			// reset input buffer
660 			permit_input = NULL;
661 			if (input_mode)
662 				set_input_mode(false);
663 		}
664 		return (GUI_YUM);
665 	case Common::KEYCODE_KP_ENTER:
666 	case Common::KEYCODE_RETURN:
667 		if (permit_inputescape || !cursor_at_input_section()
668 		        || input_char != 0) { // input_char should only be permit_input
669 			if (!cursor_at_input_section())
670 				input_add_string(get_token_at_cursor());
671 			else {
672 				if (input_char != 0)
673 					input_buf_add_char(get_char_from_input_char());
674 			}
675 			//if(input_mode)
676 			set_input_mode(false);
677 			clear_scroll();
678 			found_break_char = true; //strip leading whitespace.
679 			cursor_reset();
680 		}
681 
682 		return (GUI_YUM);
683 	case Common::KEYCODE_BACKSPACE :
684 		if (input_mode)
685 			input_buf_remove_char();
686 		break;
687 	default: // alphanumeric characters
688 		if (input_mode && Common::isPrint(ascii)) {
689 			cursor_move_to_input();
690 			if (permit_input == NULL) {
691 				if (!numbers_only || Common::isDigit(ascii))
692 					if (input_char != 0)
693 						input_buf_add_char(get_char_from_input_char());
694 				input_buf_add_char(ascii);
695 			} else if (strchr(permit_input, ascii) || strchr(permit_input, tolower(ascii))) {
696 				input_buf_add_char(toupper(ascii));
697 				set_input_mode(false);
698 				clear_scroll();
699 				found_break_char = true;
700 			}
701 		}
702 		break;
703 	}
704 	return GUI_YUM;
705 }
706 
MouseUp(int x,int y,Shared::MouseButton button)707 GUI_status ConverseGump::MouseUp(int x, int y, Shared::MouseButton button) {
708 	Std::string token_str;
709 
710 	if (page_break || !is_talking()) { // any click == scroll-to-end
711 		page_break = false;
712 		just_finished_page_break = true;
713 		if (!input_mode)
714 			Game::get_game()->get_gui()->unlock_input();
715 
716 		if (!is_holding_buffer_empty() || !input_mode || !is_talking()) {
717 			clear_scroll();
718 			process_holding_buffer(); // Process any text in the holding buffer.
719 		}
720 		return (GUI_YUM);
721 	} else if (button == 1) { // left click == select word
722 		if (input_mode) {
723 			token_str = get_token_string_at_pos(x, y);
724 			if (token_str.length() > 0) {
725 				input_add_string(token_str);
726 				set_input_mode(false);
727 				clear_scroll();
728 				found_break_char = true; //strip leading whitespace.
729 			}
730 		}
731 	}
732 	/*
733 	else if(button == 3) // right click == send input
734 	    if(permit_inputescape && input_mode)
735 	    {
736 	        set_input_mode(false);
737 	        return(GUI_YUM);
738 	    }
739 	 */
740 	return (GUI_YUM);
741 }
742 
input_add_string(Std::string token_str)743 void ConverseGump::input_add_string(Std::string token_str) {
744 	input_buf.clear();
745 	for (uint16 i = 0; i < token_str.length(); i++) {
746 		if (Common::isAlnum(token_str[i]) && (!permit_input || strchr(permit_input, token_str[i])
747 		                              || strchr(permit_input, tolower(token_str[i]))))
748 			input_buf_add_char(token_str[i]);
749 	}
750 }
751 
is_permanent_keyword(Std::string keyword)752 bool ConverseGump::is_permanent_keyword(Std::string keyword) {
753 	return (string_i_compare(keyword, " *buy") || string_i_compare(keyword, " *sell")
754 	        || string_i_compare(keyword, " *bye") || string_i_compare(keyword, " *spells")
755 	        || string_i_compare(keyword, " *reagents"));
756 }
757 
drawCursor(uint16 x,uint16 y)758 void ConverseGump::drawCursor(uint16 x, uint16 y) {
759 	if (input_char != 0) {
760 		font->drawChar(screen, get_char_from_input_char(), x, y);
761 	} else {
762 		MsgScroll::drawCursor(x, y);
763 	}
764 }
765 
766 } // End of namespace Nuvie
767 } // End of namespace Ultima
768