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