1 /*
2 	Display icon window.
3 
4 	Rewritten from the code previously contained in hud.c, primarily so
5 	the icons can be configured from an xml file at run-time.
6 
7 	Author bluap/pjbroad December 2012 / January 2013
8 */
9 #include <iostream>
10 #include <sstream>
11 #include <utility>
12 #include <cassert>
13 #include <algorithm>
14 
15 #include "asc.h"
16 #include "command_queue.hpp"
17 #include "context_menu.h"
18 #include "elwindows.h"
19 #include "elloggingwrapper.h"
20 #include "gamewin.h"
21 #include "gl_init.h"
22 #include "hud.h"
23 #include "interface.h"
24 #include "icon_window.h"
25 #include "textures.h"
26 #include "translate.h"
27 #include "sound.h"
28 
29 /*
30  * TODO		Add icon window position code - allowing the window to be repositioned
31  * TODO		Give indication that command queue is busy?
32  */
33 
34 namespace IconWindow
35 {
36 	//	Common base class for all icon types.
37 	//
38 	class Virtual_Icon
39 	{
40 		public:
41 			virtual const char *get_help_message(void) const = 0;
42 			virtual void set_flash(Uint32 seconds) = 0;
43 			virtual void set_highlight(bool is_highlighted) = 0;
44 			virtual void update_highlight(void) = 0;
45 			virtual std::pair<float, float> get_uv(void) = 0;
46 			virtual void action(void) = 0;
47 			virtual void menu(void) = 0;
48 			virtual int cm_handler(size_t option) = 0;
~Virtual_Icon(void)49 			virtual ~Virtual_Icon(void) {};
50 	};
51 
52 
53 	//	Implements the basic icon function code
54 	//
55 	class Basic_Icon : public Virtual_Icon
56 	{
57 		public:
58 			Basic_Icon(int icon_id, int coloured_icon_id, const char * help_str, const std::vector<CommandQueue::Line> *lines = 0);
~Basic_Icon(void)59 			virtual ~Basic_Icon(void) { if (cq) delete cq; if (cm_valid(cm_menu_id)) cm_destroy(cm_menu_id); }
get_help_message(void) const60 			virtual const char *get_help_message(void) const { return help_message.c_str(); }
set_flash(Uint32 seconds)61 			virtual void set_flash(Uint32 seconds) { flashing = 4*seconds; }
set_highlight(bool is_highlighted)62 			virtual void set_highlight(bool is_highlighted) { has_highlight = is_highlighted; }
update_highlight(void)63 			virtual void update_highlight(void) { has_highlight = false; if (cq) cq->process(); }
action(void)64 			virtual void action(void) { flashing = 0; do_icon_click_sound(); }
menu(void)65 			virtual void menu(void) { if (cm_valid(cm_menu_id)) cm_show_direct(cm_menu_id, -1, -1); else action(); }
66 			virtual std::pair<float, float> get_uv(void);
cm_handler(size_t option)67 			virtual int cm_handler(size_t option) { if (!cm_valid(cm_menu_id) || (option>=menu_lines.size())) return 0; menu_lines[option].action(*get_cq()); return 1; }
68 		protected:
get_cq(void)69 			CommandQueue::Queue *get_cq(void) { if (!cq) cq = new CommandQueue::Queue(); return cq; }
70 		private:
71 			bool has_highlight;				// true if the icon is highlighted
72 			float u[2], v[2];				// icon image positions
73 			std::string help_message;		// icon help message
74 			Uint32 flashing;				// if non-zero, the number times left to flash
75 			Uint32 last_flash_change;		// if flashing, the time the flashing state last changed
76 			std::vector<CommandQueue::Line> menu_lines; 	// context menu #command lines
77 			CommandQueue::Queue *cq;		// Command queue for commands
78 			size_t cm_menu_id;				// if this icon has a context menu, this is the id, otherwise CM_INIT_VALUE
79 	};
80 
81 
82 	//	Implements multi icon, icons using a specifed control state variable.
83 	//
84 	class Multi_Icon : public Virtual_Icon
85 	{
86 		public:
Multi_Icon(const char * control_name,std::vector<Virtual_Icon * > & the_icons)87 			Multi_Icon(const char *control_name, std::vector<Virtual_Icon *> &the_icons)
88 				: control_var(0), icons(the_icons)
89 			{
90 				assert(!the_icons.empty());
91 				if (control_name && (strlen(control_name) > 0))
92 					for (size_t i=0; multi_icon_vars[i].name!=0; i++)
93 						if (strcmp(control_name, multi_icon_vars[i].name) == 0)
94 							control_var = multi_icon_vars[i].var;
95 			}
~Multi_Icon(void)96 			~Multi_Icon(void)
97 			{
98 				for (size_t i=0; i<icons.size(); ++i)
99 					delete icons[i];
100 			}
get_help_message(void) const101 			const char *get_help_message(void) const { return icons[get_index()]->get_help_message(); }
set_flash(Uint32 seconds)102 			void set_flash(Uint32 seconds) { icons[get_index()]->set_flash(seconds); }
set_highlight(bool is_highlighted)103 			void set_highlight(bool is_highlighted) { icons[get_index()]->set_highlight(is_highlighted); }
update_highlight(void)104 			void update_highlight(void) { icons[get_index()]->update_highlight(); }
get_uv(void)105 			std::pair<float, float> get_uv(void) { return icons[get_index()]->get_uv(); }
action(void)106 			void action(void) { icons[get_index()]->action(); }
menu(void)107 			void menu(void) { icons[get_index()]->menu(); }
cm_handler(size_t option)108 			int cm_handler(size_t option) { return icons[get_index()]->cm_handler(option); }
109 		private:
get_index(void) const110 			size_t get_index(void) const
111 			{
112 				if (control_var && (*control_var>=0) && ((size_t)*control_var<icons.size()))
113 					return (size_t)*control_var;
114 				else
115 					return 0;
116 			}
117 			int *control_var;
118 			std::vector<Virtual_Icon *> icons;
119 			typedef struct { const char *name; int *var; } multi_icon_var;
120 			static const multi_icon_var multi_icon_vars[];
121 	};
122 
123 
124 	// Add new multi icon control variables to this list
125 	const Multi_Icon::multi_icon_var Multi_Icon::multi_icon_vars[] = {
126 		{ "you_sit", &you_sit },
127 		{ 0, 0 } /* needed as terminator */ };
128 
129 
130 	//	Implements window open/close icons.
131 	//
132 	class Window_Icon : public Basic_Icon
133 	{
134 		public:
Window_Icon(int icon_id,int coloured_icon_id,const char * help_str,const char * window_name,const std::vector<CommandQueue::Line> * lines=0)135 			Window_Icon(int icon_id, int coloured_icon_id, const char * help_str, const char * window_name, const std::vector<CommandQueue::Line> *lines = 0)
136 				: Basic_Icon(icon_id, coloured_icon_id, help_str, lines), managed_win(MW_MAX)
137 				{ managed_win = get_by_name_MW(window_name); }
update_highlight(void)138 			void update_highlight(void)
139 			{
140 				Basic_Icon::update_highlight();
141 				Basic_Icon::set_highlight(get_window_showable_MW(managed_win));
142 			}
action(void)143 			void action(void)
144 			{
145 				view_window(managed_win);
146 				Basic_Icon::action();
147 			}
~Window_Icon(void)148 			~Window_Icon(void) {}
149 		private:
150 			enum managed_window_enum managed_win;
151 	};
152 
153 
154 	//	Implements keypress icons.
155 	//
156 	class Keypress_Icon : public Basic_Icon
157 	{
158 		public:
Keypress_Icon(int icon_id,int coloured_icon_id,const char * help_str,const char * the_key_name,const std::vector<CommandQueue::Line> * lines=0)159 			Keypress_Icon(int icon_id, int coloured_icon_id, const char * help_str, const char * the_key_name, const std::vector<CommandQueue::Line> *lines = 0)
160 				: Basic_Icon(icon_id, coloured_icon_id, help_str, lines)
161 			{
162 				if (the_key_name && (strlen(the_key_name)>0))
163 					key_name = std::string(the_key_name);
164 			}
~Keypress_Icon(void)165 			~Keypress_Icon(void) {}
action(void)166 			void action(void)
167 			{
168 				if (!key_name.empty())
169 				{
170 					el_key_def value = get_key_value(key_name.c_str());
171 					if (value.key_code != SDLK_UNKNOWN)
172 						do_keypress(value);
173 				}
174 				Basic_Icon::action();
175 			}
176 		private:
177 			std::string key_name;
178 	};
179 
180 
181 	//	Implements cursor action mode icons.
182 	//
183 	class Actionmode_Icon : public Basic_Icon
184 	{
185 		public:
Actionmode_Icon(int icon_id,int coloured_icon_id,const char * help_str,const char * action_name,const std::vector<CommandQueue::Line> * lines=0)186 			Actionmode_Icon(int icon_id, int coloured_icon_id, const char * help_str, const char * action_name, const std::vector<CommandQueue::Line> *lines = 0)
187 				: Basic_Icon(icon_id, coloured_icon_id, help_str, lines), the_action_mode(ACTION_WALK)
188 			{
189 				if (action_name && (strlen(action_name) > 0))
190 					for (size_t i=0; icon_action_modes[i].name!=0; i++)
191 						if (strcmp(action_name, icon_action_modes[i].name) == 0)
192 							the_action_mode = icon_action_modes[i].mode;
193 			}
~Actionmode_Icon(void)194 			~Actionmode_Icon(void) {}
update_highlight(void)195 			void update_highlight(void)
196 			{
197 				Basic_Icon::update_highlight();
198 				if (get_gamewin_action_mode() == the_action_mode)
199 					Basic_Icon::set_highlight(true);
200 			}
action(void)201 			void action(void)
202 			{
203 				switch_action_mode(the_action_mode);
204 				Basic_Icon::action();
205 			}
206 		private:
207 			int the_action_mode;
208 			typedef struct { const char *name; int mode; } icon_action_mode;
209 			static const icon_action_mode icon_action_modes[];
210 	};
211 
212 	// Add new cursor action modes to this list
213 	const Actionmode_Icon::icon_action_mode Actionmode_Icon::icon_action_modes[] = {
214 		{ "walk", ACTION_WALK },
215 		{ "look", ACTION_LOOK },
216 		{ "use", ACTION_USE },
217 		{ "use_witem", ACTION_USE_WITEM },
218 		{ "trade", ACTION_TRADE },
219 		{ "attack", ACTION_ATTACK },
220 		{ 0, 0 } /* needed as terminator */ };
221 
222 
223 	//	Implements #command icons.
224 	//
225 	class Command_Icon : public Basic_Icon
226 	{
227 		public:
Command_Icon(int icon_id,int coloured_icon_id,const char * help_str,const char * command,const std::vector<CommandQueue::Line> * lines=0)228 			Command_Icon(int icon_id, int coloured_icon_id, const char * help_str, const char * command, const std::vector<CommandQueue::Line> *lines = 0)
229 				: Basic_Icon(icon_id, coloured_icon_id, help_str, lines), command_text(command) {}
action(void)230 			void action(void)
231 			{
232 				if (!command_text.empty())
233 				{
234 					std::string temp("CommandIcon||");
235 					temp += command_text;
236 					CommandQueue::Line temp_line(temp);
237 					temp_line.action(*get_cq());
238 				}
239 				Basic_Icon::action();
240 			}
~Command_Icon(void)241 			~Command_Icon(void) {}
242 		private:
243 			std::string command_text;
244 	};
245 
246 
247 	//	A generation and container class for the icons
248 	//
249 	class Container
250 	{
251 		public:
Container(void)252 			Container(void) : mouse_over_icon(-1), display_icon_size(def_icon_size), unscale_display_icon_size(def_icon_size),
253 				icon_spacing(0), unscaled_icon_spacing(0) {}
destroy(void)254 			void destroy(void) { free_icons(); }
get_num_icons(void) const255 			size_t get_num_icons(void) const { return icon_list.size(); }
empty(void) const256 			bool empty(void) const { return icon_list.empty(); }
mouse_over(size_t icon_number)257 			void mouse_over(size_t icon_number) { if (icon_number < icon_list.size()) mouse_over_icon = icon_number; }
258 			void draw_icons(window_info *win);
259 			void default_icons(icon_window_mode icon_mode);
260 			Virtual_Icon * icon_xml_factory(const xmlNodePtr cur);
261 			bool read_xml(icon_window_mode icon_mode);
get_icon_size(void) const262 			int get_icon_size(void) const { return display_icon_size; }
get_icon_spacing(void) const263 			int get_icon_spacing(void) const { return icon_spacing; }
get_icon_slot_width(void) const264 			int get_icon_slot_width(void) const { return  get_icon_spacing() + get_icon_size(); }
get_icons_win_width(void) const265 			int get_icons_win_width(void) const { return  get_icon_slot_width() * get_num_icons() - get_icon_spacing(); }
set_icon_size(int icon_size)266 			void set_icon_size(int icon_size) { unscale_display_icon_size = icon_size; ui_scale_handler(); }
set_icon_spacing(int icon_spacing)267 			void set_icon_spacing(int icon_spacing) { unscaled_icon_spacing = icon_spacing; ui_scale_handler(); }
ui_scale_handler(void)268 			void ui_scale_handler(void)
269 			{
270 				display_icon_size = scale_value(unscale_display_icon_size);
271 				icon_spacing = scale_value(unscaled_icon_spacing);
272 				update_window();
273 			}
cm_generic_handler(window_info * win,int widget_id,int mx,int my,int option)274 			static int cm_generic_handler(window_info *win, int widget_id, int mx, int my, int option)
275 			{
276 				Busy dummy;
277 				IconWindow::Virtual_Icon * the_icon = reinterpret_cast<IconWindow::Virtual_Icon *>(cm_get_data(cm_window_shown()));
278 				if (the_icon)
279 					return the_icon->cm_handler(option);
280 				return 0;
281 			}
action(size_t icon_number)282 			void action(size_t icon_number)
283 			{
284 				Busy dummy;
285 				if (icon_number < icon_list.size())
286 					icon_list[icon_number]->action();
287 			}
menu(size_t icon_number)288 			void menu(size_t icon_number)
289 			{
290 				Busy dummy;
291 				if (icon_number < icon_list.size())
292 					icon_list[icon_number]->menu();
293 			}
free_icons(void)294 			void free_icons(void)
295 			{
296 				if (busy)
297 				{
298 					const char *error_str = " : not freeing as doing action";
299 					std::cerr << __PRETTY_FUNCTION__ << error_str << std::endl;
300 					LOG_ERROR("%s%s", __PRETTY_FUNCTION__, error_str );
301 					return;
302 				}
303 				for (size_t i=0; i<icon_list.size(); ++i)
304 					delete icon_list[i];
305 				icon_list.clear();
306 			}
flash(const char * name,Uint32 seconds)307 			void flash(const char* name, Uint32 seconds)
308 			{
309 				for (size_t i=0; i<icon_list.size(); ++i)
310 					if (strcmp(name, icon_list[i]->get_help_message()) == 0)
311 					{
312 						icon_list[i]->set_flash(seconds);
313 						break;
314 					}
315 			}
over_icon(int mx)316 			size_t over_icon(int mx)
317 			{
318 				int icon_number = mx / get_icon_slot_width();
319 				if (mx - (icon_number * get_icon_slot_width()) <= get_icon_size())
320 					return static_cast<size_t>(icon_number);
321 				else
322 					return icon_list.size();
323 			}
update_window(void)324 			void update_window(void)
325 			{
326 				if(icons_win < 0)
327 					return;
328 				resize_window(icons_win, get_icons_win_width(), get_icon_size());
329 				move_window(icons_win, -1, 0, 0, window_height-get_icon_size());
330 			}
scale_value(int value) const331 			int scale_value(int value) const
332 			{
333 				if (icons_win >= 0 && icons_win < windows_list.num_windows)
334 					value = (int)(0.5 + windows_list.window[icons_win].current_scale * value);
335 				return value;
336 			}
337 			void set_state(const char *help_name, bool the_state);
338 			static const int def_icon_size;
339 		private:
Busy(void)340 			class Busy { public: Busy(void) { busy = true; } ~Busy(void) { busy = false; } };
341 			std::vector <Virtual_Icon *> icon_list;
342 			std::vector <std::string> disabled_icon_list;
343 			int mouse_over_icon;
344 			static bool busy;
345 			int display_icon_size;
346 			int unscale_display_icon_size;
347 			int icon_spacing;
348 			int unscaled_icon_spacing;
349 	};
350 
351 	bool Container::busy = false;
352 	const int Container::def_icon_size = 32;
353 
354 	// Constucture basic icon object
355 	//
Basic_Icon(int icon_id,int coloured_icon_id,const char * help_str,const std::vector<CommandQueue::Line> * lines)356 	Basic_Icon::Basic_Icon(int icon_id, int coloured_icon_id, const char * help_str, const std::vector<CommandQueue::Line> *lines)
357 		: help_message(help_str), cq(0), cm_menu_id(CM_INIT_VALUE)
358 	{
359 		has_highlight = false;
360 		u[0] = 32.0 * (float)(icon_id % 8)/256.0;
361 		u[1] = 32.0 * (float)(coloured_icon_id % 8)/256.0;
362 		v[0] = 32.0 * (float)(icon_id >> 3)/256.0;
363 		v[1] = 32.0 * (float)(coloured_icon_id >> 3)/256.0;
364 		flashing = 0;
365 		last_flash_change = 0;
366 
367 		// construct context menu if we need one
368 		if (lines)
369 		{
370 			std::copy(lines->begin(), lines->end(), back_inserter(menu_lines));
371 			std::string menu_text;
372 			for (size_t i=0; i<menu_lines.size(); i++)
373 				menu_text += menu_lines[i].get_text() + "\n";
374 			cm_menu_id = cm_create(menu_text.c_str(), Container::cm_generic_handler);
375 			cm_set_data(cm_menu_id, dynamic_cast<void *>(this));
376 		}
377 	}
378 
379 
380 	//	Return the uv values of the icon bitmap need for drawing
381 	//	Implements the plain/highlighted icon switch.
382 	//
get_uv(void)383 	std::pair<float, float> Basic_Icon::get_uv(void)
384 	{
385 		size_t index = (has_highlight)? 1: 0;
386 		if (flashing)
387 		{
388 			if ((SDL_GetTicks() - last_flash_change) > 250)
389 			{
390 				last_flash_change = SDL_GetTicks();
391 				flashing--;
392 			}
393 			index = flashing & 1;
394 		}
395 		return std::pair<float, float>(u[index], v[index]);
396 	}
397 
398 
399 	//	Draw the icons into the window
400 	//
draw_icons(window_info * win)401 	void Container::draw_icons(window_info *win)
402 	{
403 		Busy dummy;
404 		for (size_t i=0; i<icon_list.size(); ++i)
405 			icon_list[i]->update_highlight();
406 		if ((mouse_over_icon >= 0) && ((size_t)mouse_over_icon < icon_list.size()))
407 			icon_list[mouse_over_icon]->set_highlight(true);
408 		float uoffset = 31.0/256.0, voffset = 31.0/256.0;
409 		bind_texture(icons_text);
410 		glColor3f(1.0f,1.0f,1.0f);
411 		glBegin(GL_QUADS);
412 		for (size_t i=0; i<icon_list.size(); ++i)
413 		{
414 			std::pair<float, float> uv = icon_list[i]->get_uv();
415 			draw_2d_thing( uv.first, uv.second, uv.first+uoffset, uv.second+voffset,
416 				i*get_icon_slot_width(), 0, i*get_icon_slot_width()+(get_icon_size()-1), get_icon_size() );
417 		}
418 		glEnd();
419 		if (show_help_text && (mouse_over_icon >= 0) && ((size_t)mouse_over_icon < icon_list.size()))
420 			show_help(icon_list[mouse_over_icon]->get_help_message(), get_icon_slot_width()*(mouse_over_icon+1)+2, 0, win->current_scale);
421 		mouse_over_icon = -1;
422 	}
423 
424 
425 	// helper function for reading xml strings, perhaps make generally available?
426 	//
get_xml_field_string(std::string & ret_string,const char * field_name,const xmlNodePtr cur)427 	bool get_xml_field_string(std::string &ret_string, const char *field_name, const xmlNodePtr cur)
428 	{
429 		char *tmp = (char*)xmlGetProp(cur, (xmlChar *)field_name);
430 		if (!tmp)
431 			return false;
432 		char *parsed = 0;
433 		MY_XMLSTRCPY(&parsed, tmp);
434 		xmlFree(tmp);
435 		if (!parsed)
436 			return false;
437 		ret_string = parsed;
438 		free(parsed);
439 		return true;
440 	}
441 
442 
443 	// helper function for reading xml ints, perhaps make generally available?
444 	//
get_xml_field_int(int * ret_int,const char * field_name,const xmlNodePtr cur)445 	bool get_xml_field_int(int *ret_int, const char *field_name, const xmlNodePtr cur)
446 	{
447 		std::string tmpstr;
448 		int tmpint;
449 		get_xml_field_string(tmpstr, field_name, cur);
450 		if (tmpstr.empty())
451 			return false;
452 		std::stringstream ss(tmpstr);
453 		if( (ss >> tmpint).fail() )
454 			return false;
455 		*ret_int = tmpint;
456 		return true;
457 	}
458 
459 	//	Construct a new icon object from the xml information
460 	//
icon_xml_factory(const xmlNodePtr cur)461 	Virtual_Icon * Container::icon_xml_factory(const xmlNodePtr cur)
462 	{
463 		std::string the_type, help_name, help_text, param_name;
464 		const char *help_str;
465 		int image_id = -1, alt_image_id = -1;
466 
467 		get_xml_field_string(the_type, "type", cur);
468 		get_xml_field_int(&image_id, "image_id", cur);
469 		get_xml_field_int(&alt_image_id, "alt_image_id", cur);
470 		get_xml_field_string(help_name, "help_name", cur);
471 		get_xml_field_string(help_text, "help_text", cur);
472 		get_xml_field_string(param_name, "param_name", cur);
473 
474 		// if the icon is the list to disable, don't create it
475 		if (std::find(disabled_icon_list.begin(), disabled_icon_list.end(), help_name) != disabled_icon_list.end())
476 			return 0;
477 
478 		std::vector<CommandQueue::Line> *menu_lines_ptr = 0;
479 		std::vector<CommandQueue::Line> menu_lines;
480 		{
481 			char *text = (char*)(cur->children ? cur->children->content : NULL);
482 			char *parsed = 0;
483 			MY_XMLSTRCPY(&parsed, text);
484 			if (parsed)
485 			{
486     			std::istringstream lines(parsed);
487     			std::string line;
488     			while (std::getline(lines, line))
489 					if (!line.empty())
490 						menu_lines.push_back(CommandQueue::Line(line));
491 				free(parsed);
492 				if (!menu_lines.empty())
493 					menu_lines_ptr = &menu_lines;
494 			}
495 		}
496 
497 		if (the_type.empty() || (image_id<0) || (alt_image_id<0) ||
498 			(help_name.empty() && help_text.empty()) || param_name.empty())
499 		{
500 			LOG_ERROR("icon window factory: xml field error type=[%s] image_id=[%d] alt_image_id=[%d] help_name=[%s] help_text=[%s] param_name=[%s]\n",
501 				the_type.c_str(), image_id, alt_image_id, help_name.c_str(), help_text.c_str(), param_name.c_str() );
502 			return 0;
503 		}
504 
505 		if (!help_text.empty())
506 			help_str = help_text.c_str();
507 		else
508 			help_str = get_named_string("tooltips", help_name.c_str());
509 
510 		if (the_type == "keypress")
511 			return new Keypress_Icon(image_id, alt_image_id, help_str, param_name.c_str(), menu_lines_ptr);
512 		else if (the_type == "window")
513 			return new Window_Icon(image_id, alt_image_id, help_str, param_name.c_str(), menu_lines_ptr);
514 		else if (the_type == "action_mode")
515 			return new Actionmode_Icon(image_id, alt_image_id, help_str, param_name.c_str(), menu_lines_ptr);
516 		else if (the_type == "#command")
517 			return new Command_Icon(image_id, alt_image_id, help_str, param_name.c_str(), menu_lines_ptr);
518 		return 0;
519 	}
520 
521 
522 	//	Read the icon xml file, constructing icon objects as we go.
523 	//
read_xml(icon_window_mode icon_mode)524 	bool Container::read_xml(icon_window_mode icon_mode)
525 	{
526 		char const *error_prefix = __PRETTY_FUNCTION__;
527 		std::string file_name;
528 		if (icon_mode == NEW_CHARACTER_ICONS)
529 			file_name = "new_character_icon_window.xml";
530 		else if (icon_mode == MAIN_WINDOW_ICONS)
531 			file_name = "main_icon_window.xml";
532 		else
533 		{
534 			LOG_ERROR("%s : invalid icon mode\n", error_prefix );
535 			return false;
536 		}
537 
538 		// shame but xmlFileMatch gives an additional error message
539 		if (!el_file_exists_anywhere(file_name.c_str()))
540 			return false;
541 
542 		xmlDocPtr doc;
543 		xmlNodePtr cur;
544 
545 		if ((doc = xmlReadFile(file_name.c_str(), NULL, 0)) == NULL)
546 		{
547 			LOG_ERROR("%s : Can't open file [%s]\n", error_prefix, file_name.c_str() );
548 			return false;
549 		}
550 
551 		if ((cur = xmlDocGetRootElement (doc)) == NULL)
552 		{
553 			LOG_ERROR("%s : Empty xml document\n", error_prefix );
554 			xmlFreeDoc(doc);
555 			return false;
556 		}
557 
558 		if (xmlStrcasecmp (cur->name, (const xmlChar *) "icon_window"))
559 		{
560 			LOG_ERROR("%s : Not icon_window file\n", error_prefix );
561 			xmlFreeDoc(doc);
562 			return false;
563 		}
564 
565 		for (cur = cur->xmlChildrenNode; cur; cur = cur->next)
566 		{
567 			if (!xmlStrcasecmp(cur->name, (const xmlChar *)"icon"))
568 			{
569 				Virtual_Icon *new_icon = icon_xml_factory(cur);
570 				if (new_icon)
571 					icon_list.push_back(new_icon);
572 			}
573 			else if (!xmlStrcasecmp(cur->name, (const xmlChar *)"multi_icon"))
574 			{
575 				std::string control_name;
576 				get_xml_field_string(control_name, "control_name", cur);
577 				if (control_name.empty())
578 					LOG_ERROR("%s : invalid multi icon control_name\n", error_prefix );
579 				else
580 				{
581 					std::vector<Virtual_Icon *> multi_icons;
582 					for (xmlNodePtr multi_cur = cur->xmlChildrenNode; multi_cur; multi_cur = multi_cur->next)
583 					{
584 						if (!xmlStrcasecmp(multi_cur->name, (const xmlChar *)"icon"))
585 						{
586 							Virtual_Icon *new_icon = icon_xml_factory(multi_cur);
587 							if (new_icon)
588 								multi_icons.push_back(new_icon);
589 						}
590 					}
591 					icon_list.push_back(new Multi_Icon(control_name.c_str(), multi_icons));
592 				}
593 			}
594 			else if (!xmlStrcasecmp(cur->name, (const xmlChar *)"image_settings"))
595 			{
596 				int temp_size = def_icon_size;
597 				get_xml_field_int(&temp_size, "display_size", cur);
598 				if (temp_size != def_icon_size)
599 					LOG_ERROR("Setting the icon size from the XML file is no longer supported.");
600 			}
601 		}
602 		xmlFreeDoc(doc);
603 		return true;
604 	}
605 
606 
607 	//	If the xml file is missing or does not pass, fall back to default icons
608 	//
default_icons(icon_window_mode icon_mode)609 	void Container::default_icons(icon_window_mode icon_mode)
610 	{
611 		if (icon_mode == NEW_CHARACTER_ICONS)
612 		{
613 			icon_list.push_back(new Window_Icon(39, 38, get_named_string("tooltips", "help"), "help"));
614 			icon_list.push_back(new Window_Icon(14, 34, get_named_string("tooltips", "opts"), "opts"));
615 			LOG_ERROR("%s : Using default new character icons\n", __PRETTY_FUNCTION__ );
616 		}
617 		else if (icon_mode == MAIN_WINDOW_ICONS)
618 		{
619 			icon_list.push_back(new Actionmode_Icon(0, 18, get_named_string("tooltips", "walk"), "walk"));
620 			std::vector<Virtual_Icon *> sit_stand_icons;
621 			sit_stand_icons.push_back(new Keypress_Icon(7, 25, get_named_string("tooltips", "sit"), "#K_SIT"));
622 			sit_stand_icons.push_back(new Keypress_Icon(8, 26, get_named_string("tooltips", "stand"), "#K_SIT"));
623 			icon_list.push_back(new Multi_Icon("you_sit", sit_stand_icons));
624 			icon_list.push_back(new Actionmode_Icon(2, 20, get_named_string("tooltips", "look"), "look"));
625 			icon_list.push_back(new Actionmode_Icon(15, 35, get_named_string("tooltips", "use"), "use"));
626 			icon_list.push_back(new Actionmode_Icon(47, 46, get_named_string("tooltips", "use_witem"), "use_witem"));
627 			icon_list.push_back(new Actionmode_Icon(4, 22, get_named_string("tooltips", "trade"), "trade"));
628 			icon_list.push_back(new Actionmode_Icon(5, 23, get_named_string("tooltips", "attack"), "attack"));
629 			icon_list.push_back(new Window_Icon(11, 29, get_named_string("tooltips", "invent"), "invent"));
630 			icon_list.push_back(new Window_Icon(9, 27, get_named_string("tooltips", "spell"), "spell"));
631 			icon_list.push_back(new Window_Icon(12, 32, get_named_string("tooltips", "manu"), "manu"));
632 			icon_list.push_back(new Window_Icon(45, 44, get_named_string("tooltips", "emotewin"), "emotewin"));
633 			icon_list.push_back(new Window_Icon(19, 21, get_named_string("tooltips", "quest"), "quest"));
634 			icon_list.push_back(new Window_Icon(36, 37, get_named_string("tooltips", "map"), "map"));
635 			icon_list.push_back(new Window_Icon(3, 6, get_named_string("tooltips", "info"), "info"));
636 			icon_list.push_back(new Window_Icon(10, 24, get_named_string("tooltips", "buddy"), "buddy"));
637 			icon_list.push_back(new Window_Icon(13, 33, get_named_string("tooltips", "stats"), "stats"));
638 			icon_list.push_back(new Window_Icon(1, 28, get_named_string("tooltips", "console"), "console"));
639 			icon_list.push_back(new Window_Icon(39, 38, get_named_string("tooltips", "help"), "help"));
640 			icon_list.push_back(new Window_Icon(14, 34, get_named_string("tooltips", "opts"), "opts"));
641 			LOG_ERROR("%s : Using default main icons\n", __PRETTY_FUNCTION__ );
642 		}
643 		else
644 			LOG_ERROR("%s : Invalid mode for defaul icons\n", __PRETTY_FUNCTION__ );
645 	}
646 
647 
648 	//	Set the state if an icon in the window, reloading the icons if the state has changed.
649 	//	If the state is set to false, the icon will not created when the icons are reloaded.
650 	//
set_state(const char * help_name,bool the_state)651 	void Container::set_state(const char *help_name, bool the_state)
652 	{
653 		auto entry = std::find(disabled_icon_list.begin(), disabled_icon_list.end(), help_name);
654 		if (!the_state && (entry == disabled_icon_list.end()))
655 		{
656 			disabled_icon_list.push_back(help_name);
657 			reload_icon_window(0, 0);
658 		}
659 		else if (the_state && (entry != disabled_icon_list.end()))
660 		{
661 			disabled_icon_list.erase(entry);
662 			reload_icon_window(0, 0);
663 		}
664 	}
665 
666 }
667 
668 
669 //	The icon container object, does nothing much when constructed, frees the icons when looses scope.
670 static IconWindow::Container action_icons;
671 
672 int	icons_win = -1;
673 static int reload_flag = false;
674 static icon_window_mode last_mode = (icon_window_mode)0;
675 
676 //	Window callback for ui_scale
ui_scale_icons_handler(window_info * win)677 static int ui_scale_icons_handler(window_info *win)
678 {
679 	action_icons.ui_scale_handler();
680 	return 1;
681 }
682 
683 //	Window callback for mouse over
mouseover_icons_handler(window_info * win,int mx,int my)684 static int mouseover_icons_handler(window_info *win, int mx, int my)
685 {
686 	action_icons.mouse_over(action_icons.over_icon(mx));
687 	return 0;
688 }
689 
690 //	Window callback for display
display_icons_handler(window_info * win)691 static int display_icons_handler(window_info *win)
692 {
693 	if (reload_flag)
694 	{
695 		action_icons.free_icons();
696 		init_icon_window(last_mode);
697 		reload_flag = false;
698 	}
699 	action_icons.draw_icons(win);
700 	return 1;
701 }
702 
703 //	Window callback mouse click
click_icons_handler(window_info * win,int mx,int my,Uint32 flags)704 static int click_icons_handler(window_info *win, int mx, int my, Uint32 flags)
705 {
706 	if ( (flags & ELW_MOUSE_BUTTON) == 0)
707 		return 0; // only handle mouse button clicks, not scroll wheels moves;
708 	if (flags & ELW_RIGHT_MOUSE)
709 		action_icons.menu(action_icons.over_icon(mx));
710 	else if (flags & ELW_LEFT_MOUSE)
711 		action_icons.action(action_icons.over_icon(mx));
712 	return 1;
713 }
714 
715 //	Reload the icons from file
reload_icon_window(char * text,int len)716 extern "C" int reload_icon_window(char *text, int len)
717 {
718 	reload_flag = true;
719 	return 1;
720 }
721 
722 //	Returns width occupied by the icons
get_icons_win_active_len(void)723 extern "C" int get_icons_win_active_len(void)
724 {
725 	return action_icons.get_icons_win_width() ;
726 }
727 
728 //	Returns height occupied by the icons
get_icons_win_active_height(void)729 extern "C" int get_icons_win_active_height(void)
730 {
731 	return action_icons.get_icon_size();
732 }
733 
734 //	Make the specified icon flash
flash_icon(const char * name,Uint32 seconds)735 extern "C" void flash_icon(const char* name, Uint32 seconds)
736 {
737 	action_icons.flash(name, seconds);
738 }
739 
740 //	Set the icon size
set_icon_size(int icon_size)741 extern "C" void set_icon_size(int icon_size)
742 {
743 	action_icons.set_icon_size(icon_size);
744 }
745 
746 //	Set the spacing between icons
set_icon_spacing(int icon_spacing)747 extern "C" void set_icon_spacing(int icon_spacing)
748 {
749 	action_icons.set_icon_spacing(icon_spacing);
750 }
751 
752 //	Create/display the icon window and create the icons as needed
init_icon_window(icon_window_mode icon_mode)753 extern "C" void init_icon_window(icon_window_mode icon_mode)
754 {
755 	if (!action_icons.empty() && (last_mode != icon_mode))
756 		action_icons.free_icons();
757 	last_mode = icon_mode;
758 
759 	if (action_icons.empty())
760 	{
761 		action_icons.read_xml(icon_mode);
762 		if (action_icons.empty())
763 			action_icons.default_icons(icon_mode);
764 	}
765 
766 	if(icons_win < 0)
767 	{
768 		icons_win= create_window("Icons", -1, 0, 0, 0, 0, 0, ELW_USE_UISCALE|ELW_TITLE_NONE|ELW_SHOW_LAST);
769 		set_window_handler(icons_win, ELW_HANDLER_DISPLAY, (int (*)())&display_icons_handler);
770 		set_window_handler(icons_win, ELW_HANDLER_CLICK, (int (*)())&click_icons_handler);
771 		set_window_handler(icons_win, ELW_HANDLER_MOUSEOVER, (int (*)())&mouseover_icons_handler);
772 		set_window_handler(icons_win, ELW_HANDLER_UI_SCALE, (int (*)())&ui_scale_icons_handler);
773 	}
774 
775 	action_icons.ui_scale_handler();
776 }
777 
destroy_icon_window(void)778 extern "C" void destroy_icon_window(void) { action_icons.destroy(); }
779 
set_icon_state(const char * help_name,int enabled)780 extern "C" void set_icon_state(const char *help_name, int enabled) { action_icons.set_state(help_name, enabled); }
781