The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
construct_dialog.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2016 by Patrick Parker <[email protected]>
3  wesnoth widget Copyright (C) 2003-5 by David White <[email protected]>
4  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
18 #include "construct_dialog.hpp"
19 
20 #include "config_assign.hpp"
21 #include "formula/string_utils.hpp"
22 #include "game_config.hpp"
23 #include "gettext.hpp"
24 #include "sound.hpp"
25 #include "log.hpp"
26 #include "marked-up_text.hpp"
29 #include "sdl/utils.hpp"
30 #include "sdl/rect.hpp"
31 
32 #include "utils/functional.hpp"
33 
34 static lg::log_domain log_display("display");
35 #define ERR_DP LOG_STREAM(err, log_display)
36 #define LOG_DP LOG_STREAM(info, log_display)
37 #define DBG_DP LOG_STREAM(debug, log_display)
38 #define ERR_G LOG_STREAM(err, lg::general)
39 
40 namespace gui {
41 
42 //static initialization
43 //note: style names are directly related to the panel image file names
46 const dialog::style dialog::hotkeys_style("menu", 0);
49 const size_t dialog::left_padding = font::relative_size(10);
51 const size_t dialog::image_h_pad = font::relative_size(/*image_ == nullptr ? 0 :*/ 10);
52 const size_t dialog::top_padding = font::relative_size(10);
54 
55 const int dialog::max_menu_width = -1;
56 
57 menu * dialog::empty_menu = nullptr;
58 }
59 
60 namespace {
61 
62 std::vector<std::string> empty_string_vector;
63 
64 } //end anonymous namespace
65 
66 namespace gui {
67 
69 {
70  delete label_;
71 }
72 
74  x(-1),
75  y(-1),
76  interior(sdl::empty_rect),
79  menu_width(0),
80  panes(),
81  label_x(-1),
82  label_y(-1),
83  menu_x(-1),
84  menu_y(-1),
85  menu_height(-1),
86  image_x(-1),
87  image_y(-1),
88  caption_x(-1),
89  caption_y(-1),
90  buttons()
91 {
92  //note: this is not defined in the header file to C++ ODR (one-definition rule)
93  //since each inclusion of the header file uses a different version of empty_rect
94  //(unnamed namespace and/or const object defined at declaration time).
95 }
96 
97 dialog::dialog(CVideo& video, const std::string& title, const std::string& message,
98  const DIALOG_TYPE type, const style& dialog_style) :
99  video_(video),
100  image_(nullptr),
101  title_(title),
102  style_(dialog_style),
103  title_widget_(nullptr),
104  message_(nullptr),
105  type_(type),
106  menu_(get_empty_menu(video)),
107  preview_panes_(),
108  button_pool_(),
110  extra_buttons_(),
111  top_buttons_(),
112  frame_buttons_(),
113  topic_(),
114  help_button_(nullptr),
115  text_widget_(nullptr),
116  frame_(nullptr),
117  dim_(),
119 {
120  switch(type)
121  {
122  case MESSAGE:
123  default:
124  break;
125  case OK_ONLY:
127  break;
128  case YES_NO:
131  break;
132  case OK_CANCEL:
134  add_button(new standard_dialog_button(video_,_("Cancel"),1,true), BUTTON_STANDARD);
135  break;
136  case CANCEL_ONLY:
137  add_button(new standard_dialog_button(video_,_("Cancel"),0,true), BUTTON_STANDARD);
138  break;
139  case CLOSE_ONLY:
141  break;
142  }
143  //dialog creator should catch(button::error&) ?
144 
145  try {
148  } catch(utf8::invalid_utf8_exception&) {
149  ERR_DP << "Problem handling utf8 in message '" << message << "'" << std::endl;
150  throw;
151  }
152 
153 }
154 
156 {
157  if(menu_ != empty_menu)
158  {
159  delete menu_;
160  }
161  delete title_widget_;
162  delete message_;
163  delete text_widget_;
164  delete image_;
165  delete frame_;
166 
168  for (b = button_pool_.begin(); b != button_pool_.end(); ++b) {
169  delete b->first;
170  }
171 // pp_iterator p;
172 // for (p = preview_panes_.begin(); p != preview_panes_.end(); ++p) {
173 // delete (*p);
174 // }
175 }
176 
177 bool dialog::option_checked(unsigned int option_index)
178 {
179  unsigned int i = 0;
181  for (b = button_pool_.begin(); b != button_pool_.end(); ++b) {
182  if(b->first->is_option()) {
183  if(option_index == i++) {
184  return b->first->checked();
185  }
186  }
187  }
188  return false;
189 }
190 
192 {
193  std::pair<dialog_button *, BUTTON_LOCATION> new_pair(btn,loc);
194  button_pool_.push_back(new_pair);
195  switch(loc)
196  {
197  case BUTTON_HELP:
198  delete help_button_;
199  help_button_ = btn;
200  break;
201  case BUTTON_EXTRA:
202  case BUTTON_EXTRA_LEFT:
203  case BUTTON_CHECKBOX:
205  extra_buttons_.push_back(btn);
206  break;
207  case BUTTON_STANDARD:
208  standard_buttons_.push_back(btn);
209  break;
210  case BUTTON_TOP:
211  top_buttons_.push_back(btn);
212  break;
213  default:
214  break;
215  }
216  btn->set_parent(this);
217 }
218 
220 {
222  add_button(btn, loc);
223 }
224 
225 void dialog::add_option(const std::string& label, bool checked, BUTTON_LOCATION loc, const std::string& help_string)
226 {
228  btn->set_check(checked);
229  btn->set_help_string(help_string);
230  add_button(btn, loc);
231 }
232 
233 void dialog::set_textbox(const std::string& text_widget_label,
234  const std::string& text_widget_text,
235  const int text_widget_max_chars, const unsigned int text_box_width)
236 {
237  label *label_ptr = new label(video_, text_widget_label, message_font_size, font::NORMAL_COLOR, false);
238  const bool editable_textbox = std::find(text_widget_text.begin(),text_widget_text.end(),'\n') == text_widget_text.end();
239  text_widget_ = new dialog_textbox(label_ptr, video_, text_box_width, text_widget_text, editable_textbox, text_widget_max_chars);
240  text_widget_->set_wrap(!editable_textbox);
241 }
242 
243 void dialog::set_menu(const std::vector<std::string> &menu_items, menu::sorter* sorter)
244 {
245  set_menu(new gui::menu(video_, menu_items, (type_==MESSAGE),
246  -1, dialog::max_menu_width, sorter, &menu::default_style, false));
247 }
248 
249 /**
250  * Changes (or sets, if not previously set) the dialog's menu items to the
251  * provided list. If @a keep_selected is true, the selection will remain at
252  * the same index if possible (otherwise it gets reset to zero).
253  */
254 void dialog::set_menu_items(const std::vector<std::string> &menu_items, bool keep_selection)
255 {
256  if(menu_ == empty_menu) {
257  set_menu(menu_items);
258  } else {
259  menu_->set_items(menu_items, true, keep_selection);
260 
261  for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
262  (**i).set_selection(menu_->selection());
263  }
264  }
265 }
266 
267 /**
268  * Provides create-on-use semantics for empty_menu.
269  * This is called by dialog's constructor, so other code can use empty_menu directly.
270  */
272 {
273  if ( empty_menu == nullptr ) {
274  empty_menu = new gui::menu(video, empty_string_vector, false, -1, -1, nullptr, &menu::simple_style);
275  empty_menu->leave();
276  }
277  return empty_menu;
278 }
279 
280 int dialog::show(int xloc, int yloc)
281 {
282  layout(xloc, yloc);
283  return show();
284 }
285 
287 {
288  if (video_.faked()) {
290  if (pm && pm->any_running()) {
291  pm->notify_event("show_dialog", config(config_of
292  ("title", title_)
293  ("message", message_->get_text())
294  ));
295 
296  plugins_context pc("Dialog");
297  pc.set_callback("set_result", std::bind(&dialog::set_result, this, std::bind(get_int, _1, "result", CLOSE_DIALOG)), false);
298 
299  while (pm->any_running() && result() == CONTINUE_DIALOG) {
300  pc.play_slice();
301  }
302  return result();
303  }
304  return CLOSE_DIALOG;
305  }
306 
307  if(video_.update_locked()) {
308  ERR_DP << "display locked ignoring dialog '" << title_ << "' '" << message_->get_text() << "'" << std::endl;
309  return CLOSE_DIALOG;
310  }
311 
312  LOG_DP << "showing dialog '" << title_ << "' '" << message_->get_text() << "'\n";
313  if(dim_.interior == sdl::empty_rect) { layout(); }
314 
315  //create the event context, remember to instruct any passed-in widgets to join it
316  const events::event_context dialog_events_context;
317  const dialog_manager manager;
318 
319  get_frame().join();
320 
321  //draw
322  draw_frame();
324  draw_contents();
325 
326  //process
328  ("title", title_)
329  ("message", message_->get_text())
330  ));
331 
332  plugins_context pc("Dialog");
333  pc.set_callback("set_result", std::bind(&dialog::set_result, this, std::bind(get_int, _1, "result", CLOSE_DIALOG)), false);
334 
335  dialog_process_info dp_info;
336  do
337  {
338  events::pump();
339  set_result(process(dp_info));
340  if(!done()) {
341  refresh();
342  }
343  action(dp_info);
344  dp_info.cycle();
345 
346  pc.play_slice();
347  } while(!done());
348 
350  return result();
351 }
352 
354 {
355  if(!preview_panes_.empty()) {
356  for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
357  preview_pane *pane = *i;
358  if(!pane->handler_members().empty())
359  {
360  pane->draw();
361  pane->needs_restore_ = false; //prevent panes from drawing over members
362  }
363  }
364  }
365  events::raise_draw_event(); //draw widgets
366 
367  video_.flip();
368 }
369 
371 {
372  if(frame_ == nullptr) {
373  frame_buttons_.clear();
374  for(button_iterator b = standard_buttons_.begin(); b != standard_buttons_.end(); ++b)
375  {
376  frame_buttons_.push_back(*b);
377  }
379  }
380  return *frame_;
381 }
382 
384  delete frame_;
385  frame_ = nullptr;
386 }
387 
389 {
390  get_frame().draw();
391 }
392 
394 {
395  if(!preview_panes_.empty()) {
396  for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
397  preview_pane *pane = *i;
398  pane->leave();
399  pane->join();
400  pane->set_location(dim_.panes.find(pane)->second);
401  }
402  }
403  if(text_widget_) {
404  text_widget_->leave();
405  text_widget_->join();
407  if(text_widget_->get_label()) {
409  }
410  }
411  if(get_menu().height() > 0) {
412  menu_->leave();
413  menu_->join();
416  menu_->set_max_width( dim_.menu_width ); //lock the menu width
417  if(dim_.menu_height >= 0) {
419  }
421  }
422  if(image_) {
423  image_->leave();
424  image_->join();
426  if(image_->caption()) {
428  }
429  }
431  for(b = top_buttons_.begin(); b != top_buttons_.end(); ++b) {
432  dialog_button *btn = *b;
433  btn->leave();
434  btn->join();
435  std::pair<int,int> coords = dim_.buttons.find(btn)->second;
436  btn->set_location(coords.first, coords.second);
437  }
438  for(b = extra_buttons_.begin(); b != extra_buttons_.end(); ++b) {
439  dialog_button *btn = *b;
440  btn->leave();
441  btn->join();
442  std::pair<int,int> coords = dim_.buttons.find(btn)->second;
443  btn->set_location(coords.first, coords.second);
444  }
445  for(b = standard_buttons_.begin(); b != standard_buttons_.end(); ++b) {
446  dialog_button *btn = *b;
447  btn->leave();
448  btn->join();
449  }
450  if(help_button_) {
451  help_button_->leave();
452  help_button_->join();
453  }
455  message_->leave();
456  message_->join();
457 }
458 
460 {
461  video_.flip();
462  CVideo::delay(10);
463 }
464 
466 {
467  CVideo& screen = video_;
468  const surface& scr = screen.getSurface();
469 
471  dim.x = xloc;
472  dim.y = yloc;
473 
474  const bool use_textbox = (text_widget_ != nullptr);
475  int text_widget_width = 0;
476  int text_widget_height = 0;
477  if(use_textbox) {
478  const SDL_Rect& area = font::text_area(text_widget_->text(),message_font_size);
479  dim.textbox.w = std::min<size_t>(screen.getx()/2,std::max<size_t>(area.w,text_widget_->width()));
480  dim.textbox.h = std::min<size_t>(screen.gety()/2,std::max<size_t>(area.h,text_widget_->height()));
481  text_widget_width = dim.textbox.w;
482  text_widget_width += (text_widget_->get_label() == nullptr) ? 0 : text_widget_->get_label()->width();
483  text_widget_height = dim.textbox.h + message_font_size;
484  }
485 
486  const bool use_menu = (get_menu().height() > 0);
487  if(!message_->get_text().empty()) {
488  dim.message.w = message_->width();
489  dim.message.h = message_->height();
490  }
491  unsigned int caption_width = 0;
492  unsigned int caption_height = 0;
493  if (image_ != nullptr && image_->caption() != nullptr) {
494  caption_width = image_->caption()->width();
495  caption_height = image_->caption()->height();
496  }
497 
498  int check_button_height = 0;
499  int left_check_button_height = 0;
500  int top_button_height = 0;
501  const int button_height_padding = 5;
502 
503  for(button_pool_const_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
504  dialog_button const *const btn = b->first;
505  switch(b->second)
506  {
507  case BUTTON_EXTRA:
508  case BUTTON_CHECKBOX:
509  check_button_height += btn->height() + button_height_padding;
510  break;
511  case BUTTON_EXTRA_LEFT:
513  left_check_button_height += btn->height() + button_height_padding;
514  break;
515  case BUTTON_TOP:
516  top_button_height += btn->height() + button_height_padding;
517  break;
518  case BUTTON_STANDARD:
519  default:
520  break;
521  }
522  }
523  check_button_height = std::max<int>(check_button_height, left_check_button_height);
524 
525  size_t above_preview_pane_height = 0, above_left_preview_pane_width = 0, above_right_preview_pane_width = 0;
526  size_t preview_pane_height = 0, left_preview_pane_width = 0, right_preview_pane_width = 0;
527  if(!preview_panes_.empty()) {
528  for(pp_const_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
529  preview_pane const *const pane = *i;
530  const SDL_Rect& rect = pane->location();
531  if(pane->show_above() == false) {
532  preview_pane_height = std::max<size_t>(rect.h,preview_pane_height);
533  if(pane->left_side()) {
534  left_preview_pane_width += rect.w;
535  } else {
536  right_preview_pane_width += rect.w;
537  }
538  } else {
539  above_preview_pane_height = std::max<size_t>(rect.h,above_preview_pane_height);
540  if(pane->left_side()) {
541  above_left_preview_pane_width += rect.w;
542  } else {
543  above_right_preview_pane_width += rect.w;
544  }
545  }
546  }
547  }
548 
549  const int menu_hpadding = font::relative_size((dim.message.h > 0 && use_menu) ? 10 : 0);
550  const size_t image_h_padding = (image_ == nullptr)? 0 : image_h_pad;
551  const size_t padding_width = left_padding + right_padding + image_h_padding;
552  const size_t padding_height = top_padding + bottom_padding + menu_hpadding;
553  const size_t image_width = (image_ == nullptr) ? 0 : image_->width();
554  const size_t image_height = (image_ == nullptr) ? 0 : image_->height();
555  const size_t total_text_height = dim.message.h + caption_height;
556 
557  size_t text_width = dim.message.w;
558  if(caption_width > text_width)
559  text_width = caption_width;
560 
561  // Prevent the menu to be larger than the screen
562  dim.menu_width = menu_->width();
563  if(dim.menu_width + image_width + padding_width + left_preview_pane_width + right_preview_pane_width > static_cast<size_t>(scr->w))
564  dim.menu_width = scr->w - image_width - padding_width - left_preview_pane_width - right_preview_pane_width;
565  if(dim.menu_width > text_width)
566  text_width = dim.menu_width;
567 
568 
569  size_t total_width = image_width + text_width + padding_width;
570 
571  if(text_widget_width+left_padding+right_padding > total_width)
572  total_width = text_widget_width+left_padding+right_padding;
573 
574  //Prevent the menu from being too skinny
575  if(use_menu && preview_panes_.empty() &&
576  total_width > dim.menu_width + image_width + padding_width) {
577  dim.menu_width = total_width - image_width - padding_width;
578  }
579 
580  const size_t text_and_image_height = image_height > total_text_height ? image_height : total_text_height;
581 
582  const int top_widgets_height = std::max<int>(text_widget_height, top_button_height);
583 
584  const int total_height = text_and_image_height + padding_height + menu_->height() +
585  top_widgets_height + check_button_height;
586 
587  dim.interior.w = std::max<int>(total_width,above_left_preview_pane_width + above_right_preview_pane_width);
588  dim.interior.h = std::max<int>(total_height,static_cast<int>(preview_pane_height));
589  dim.interior.x = std::max<int>(0,dim.x >= 0 ? dim.x : scr->w/2 - (dim.interior.w + left_preview_pane_width + right_preview_pane_width)/2);
590  dim.interior.y = std::max<int>(0,dim.y >= 0 ? dim.y : scr->h/2 - (dim.interior.h + above_preview_pane_height)/2);
591 
592  DBG_DP << "above_preview_pane_height: " << above_preview_pane_height << "; "
593  << "dim.interior.y: " << scr->h/2 << " - " << (dim.interior.h + above_preview_pane_height)/2 << " = "
594  << dim.interior.y << "; " << "dim.interior.h: " << dim.interior.h << "\n";
595 
596  if(dim.x <= -1 || dim.y <= -1) {
597  dim.x = dim.interior.x + left_preview_pane_width;
598  dim.y = dim.interior.y + above_preview_pane_height;
599  }
600 
601  if(dim.x + dim.interior.w > scr->w) {
602  dim.x = scr->w - dim.interior.w;
603  if(dim.x < dim.interior.x) {
604  dim.interior.x = dim.x;
605  }
606  }
607 
608  const int frame_top_pad = get_frame().top_padding();
609  const int frame_bottom_pad = get_frame().bottom_padding();
610  if(dim.y + dim.interior.h + frame_bottom_pad > scr->h) {
611  dim.y = std::max<int>(frame_top_pad, scr->h - dim.interior.h - frame_bottom_pad);
612  if(dim.y < dim.interior.y) {
613  dim.interior.y = dim.y;
614  }
615  }
616 
617  dim.interior.w += left_preview_pane_width + right_preview_pane_width;
618  dim.interior.h += above_preview_pane_height;
619 
620  const int max_height = scr->h - dim.interior.y - frame_bottom_pad;
621  if(dim.interior.h > max_height) {
622  //try to rein in the menu height a little bit
623  const int menu_height = menu_->height();
624  if(menu_height > 0) {
625  dim.menu_height = std::max<int>(1, max_height - dim.interior.h + menu_height);
626  dim.interior.h -= menu_height - dim.menu_height;
627  }
628  }
629 
630  //calculate the positions of the preview panes to the sides of the dialog
631  if(!preview_panes_.empty()) {
632 
633  int left_preview_pane = dim.interior.x;
634  int right_preview_pane = dim.interior.x + total_width + left_preview_pane_width;
635  int above_left_preview_pane = dim.interior.x + dim.interior.w/2;
636  int above_right_preview_pane = above_left_preview_pane;
637 
638  for(pp_const_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
639  preview_pane const *const pane = *i;
640  SDL_Rect area = pane->location();
641 
642  if(pane->show_above() == false) {
643  area.y = dim.y;
644  area.h = dim.interior.h;
645  if(pane->left_side()) {
646  area.x = left_preview_pane;
647  left_preview_pane += area.w;
648  } else {
649  area.x = right_preview_pane;
650  right_preview_pane += area.w;
651  }
652  } else {
653  area.y = dim.interior.y;
654  area.h = above_preview_pane_height;
655  if(pane->left_side()) {
656  area.x = above_left_preview_pane - area.w;
657  above_left_preview_pane -= area.w;
658  } else {
659  area.x = above_right_preview_pane;
660  above_right_preview_pane += area.w;
661  }
662  }
663  dim.panes[*i] = area;
664  }
665  }
666 
667  const int text_widget_y = dim.y + top_padding + text_and_image_height - 6 + menu_hpadding;
668 
669  if(use_textbox) {
670  dim.textbox.x = dim.x + left_padding + text_widget_width - dim.textbox.w;
671  dim.textbox.y = text_widget_y + (text_widget_height - dim.textbox.h)/2;
672  dim.label_x = dim.x+left_padding;
673  dim.label_y = dim.textbox.y;
674  }
675 
676  if(top_buttons_.empty() == false) {
677  int top_buttons_y = text_widget_y;
678 
679  for(button_const_iterator b = top_buttons_.begin(); b != top_buttons_.end(); ++b) {
680  const dialog_button& btn = **b;
681 
682  std::pair<int, int> coords(0, 0);
683 
684  coords.first = dim.x + total_width - btn.width() - ButtonHPadding;
685  // As a special case, if there's only one button
686  // and a text box, try to align the button to it
687  if(top_buttons_.size() == 1 && use_textbox) {
688  coords.second = text_widget_y + (text_widget_height - btn.height())/2;
689  } else {
690  coords.second = top_buttons_y;
691  }
692 
693  top_buttons_y += btn.height() + button_height_padding;
694 
695  dim.buttons[*b] = coords;
696  }
697  }
698 
699  dim.menu_x = dim.x+image_width+left_padding+image_h_padding;
700  dim.menu_y = dim.y+top_padding+text_and_image_height+menu_hpadding+ std::max<int>(top_button_height - button_height_padding, (use_textbox ? text_widget_->location().h + top_padding : 0));
701 
702  dim.message.x = dim.x + left_padding;
703  dim.message.y = dim.y + top_padding + caption_height;
704 
705  if(image_ != nullptr) {
706  const int x = dim.x + left_padding;
707  const int y = dim.y + top_padding;
708  dim.message.x += image_width + image_h_padding;
709  dim.image_x = x;
710  dim.image_y = y;
711  dim.caption_x = dim.x + image_width + left_padding + image_h_padding;
712  dim.caption_y = dim.y + top_padding;
713  }
714 
715  //set the position of any tick boxes. by default, they go right below the menu,
716  //slammed against the right side of the dialog
717  if(extra_buttons_.empty() == false) {
718  int options_y = dim.menu_y + menu_->height() + menu_hpadding + button_height_padding;
719  int options_left_y = options_y;
720  for(button_pool_const_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
721  dialog_button const *const btn = b->first;
722  std::pair<int,int> coords;
723  switch(b->second)
724  {
725  case BUTTON_EXTRA:
726  case BUTTON_CHECKBOX:
727  coords.first = dim.x + total_width - btn->width() - ButtonHPadding;
728  coords.second = options_y;
729  dim.buttons[b->first] = coords;
730  options_y += btn->height() + button_height_padding;
731  break;
732  case BUTTON_EXTRA_LEFT:
734  coords.first = dim.x + ButtonHPadding;
735  coords.second = options_left_y;
736  dim.buttons[b->first] = coords;
737  options_left_y += btn->height() + button_height_padding;
738  break;
739  case BUTTON_STANDARD:
740  default:
741  break;
742  }
743  }
744  }
745  set_layout(dim);
746  return dim;
747 }
748 
750  get_frame().layout(new_dim.interior);
751  dim_ = new_dim;
752 }
753 
754 
756 {
757 
758  int mousex, mousey;
759  int mouse_flags = SDL_GetMouseState(&mousex,&mousey);
760 
761  info.new_right_button = (mouse_flags&SDL_BUTTON_RMASK) != 0;
762  info.new_left_button = (mouse_flags&SDL_BUTTON_LMASK) != 0;
763  info.new_key_down = info.key[SDLK_SPACE] || info.key[SDLK_RETURN] ||
764  info.key[SDLK_ESCAPE] || info.key[SDLK_KP_ENTER];
766  const bool use_menu = (menu_ != empty_menu);
767  const bool use_text_input = (text_widget_!=nullptr);
768  const bool has_input = (use_menu||use_text_input);//input of any sort has to be made
769 
770  if((((!info.key_down && (info.key[SDLK_RETURN] || info.key[SDLK_KP_ENTER])) || info.double_clicked) &&
771  (type_ == YES_NO || type_ == OK_CANCEL || type_ == OK_ONLY || type_ == CLOSE_ONLY))) {
772 
773  return (use_menu ? menu_->selection() : 0);
774  }
775 
776  //escape quits from the dialog -- unless it's an "ok" dialog with input
777  if(!info.key_down && info.key[SDLK_ESCAPE] && !(type_ == OK_ONLY && has_input)) {
778  return (CLOSE_DIALOG);
779  }
780 
781  //inform preview panes when there is a new menu selection
782  if((menu_->selection() != info.selection) || info.first_time) {
783  info.selection = menu_->selection();
784  int selection = info.selection;
785  if(selection < 0) {
786  selection = 0;
787  }
788  if(!preview_panes_.empty()) {
789  for(pp_iterator i = preview_panes_.begin(); i != preview_panes_.end(); ++i) {
790  (**i).set_selection(selection);
791  if(info.first_time) {
792  (**i).set_dirty();
793  }
794  }
795  }
796  }
797 
798  info.first_time = false;
799 
800  if(use_menu) {
801  //get any drop-down choice or context-menu click
802  const int selection = menu_->process();
803  if(selection != -1)
804  {
805  return (selection);
806  }
807  }
808 
809  draw_frame();
810  //draw_contents();
811 
814 
815  //left-clicking outside of a drop-down or context-menu should close it
816  if (info.new_left_button && !info.left_button) {
817  if (standard_buttons_.empty() && !sdl::point_in_rect(mousex,mousey, menu_->location())) {
818  if (use_menu)
820  return CLOSE_DIALOG;
821  }
822  }
823 
824  //right-clicking outside of a dialog should close it unless a choice is required
825  //note: this will also close any context-menu or drop-down when it is right-clicked
826  // but that may be changed to allow right-click selection instead.
827  if (info.new_right_button && !info.right_button) {
828  if( standard_buttons_.empty()
829  || (!sdl::point_in_rect(mousex,mousey,get_frame().get_layout().exterior)
830  && type_ != YES_NO && !(type_ == OK_ONLY && has_input))) {
832  return CLOSE_DIALOG;
833  }
834  }
835 
836  //any keypress should close a dialog if it has one standard button (or less)
837  //and no menu options.
838  if (info.new_key_down && !info.key_down) {
839  if (standard_buttons_.size() < 2 && !has_input)
840  return CLOSE_DIALOG;
841  }
842 
843  //now handle any button presses
844  for(button_pool_iterator b = button_pool_.begin(); b != button_pool_.end(); ++b) {
845  if(b->first->pressed()) {
846  return b->first->action(info);
847  }
848  }
849 
850  return CONTINUE_DIALOG;
851 }
852 
854  if(handler_ != nullptr) {
855  menu &menu_ref = parent_->get_menu();
857 
858  if(res == DELETE_ITEM || res == CLOSE_DIALOG) {
859  return res;
860  }
861 
862  //reset button-tracking flags so that if the action displays a dialog, a button-press
863  //at the end of the dialog won't be mistaken for a button-press in this dialog.
864  //(We should eventually use a proper event-handling system instead of tracking
865  //flags to avoid problems like this altogether).
866  info.clear_buttons();
867  return CONTINUE_DIALOG;
868  }
869  return simple_result_;
870 }
871 
873 {
874  //default way of handling a "delete item" request
875  if(result() == DELETE_ITEM) {
876  const int selection = menu_->selection();
877  if(selection >= 0) {
878  menu_->erase_item(selection);
879  }
880  // was used before to auto close empty menu
881  //if(menu_->number_of_items() == 0) {
882  // set_result(CLOSE_DIALOG);
883  //} else {
884 
886  info.first_time = true;
887 
888  }
889 }
890 
892  //if the menu is not used, then return the index of the
893  //button pressed, otherwise return the index of the menu
894  //item selected if the last button is not pressed, and
895  //cancel (-1) otherwise
896  if(dialog()->get_menu().height() <= 0) {
897  return simple_result_;
898  } else if((simple_result_ == 0 && is_last_) || !is_last_) {
899  return (dialog()->get_menu().selection());
900  }
901  return CLOSE_DIALOG;
902 }
903 
905 {
906  label *label_ptr = nullptr;
907  if(!caption.empty()) {
908  label_ptr = new label(video_, caption, caption_font_size, font::NORMAL_COLOR, false);
909  }
910  set_image( new dialog_image(label_ptr, video_, surf ));
911 }
912 
914 {
916 }
917 
918 int filter_textbox::get_index(int selection) const {
919  // don't translate special values
920  if(selection < 0) {
921  return selection;
922  }
923  //we must the header row value to the index to ignore this row and
924  //then subtract it from the result to return the index not including
925  //the possible header row.
926 
927  if (selection + header_row_ >= index_map_.size()) {
928  return -1; // bad index, cancel
929  }
930 
931  return index_map_[selection+header_row_]-header_row_;
932 }
933 
934 void filter_textbox::delete_item(int selection) {
935  // use the real selection
936  size_t adjusted_selection = selection + header_row_;
937 
938  if (adjusted_selection >= index_map_.size())
939  return;
940 
941  filtered_items_.erase(filtered_items_.begin() + adjusted_selection);
942  items_to_filter_.erase(items_to_filter_.begin() + index_map_[adjusted_selection]);
943  items_.erase(items_.begin() + index_map_[adjusted_selection]);
944  index_map_.erase(index_map_.begin() + adjusted_selection);
945 
946  // don't forget to also shift the next index values
947  // this assume that index_map_and items_ have the same order
948  for(size_t i = adjusted_selection; i < index_map_.size(); ++i) {
949  index_map_[i] = index_map_[i]-1;
950  }
951 
952  //for now, assume the dialog menu item is deleted using DELETE_ITEM
953  /* dialog_.set_menu_items(filtered_items_); */
954 }
955 
957  const std::vector<std::string> words = utils::split(unicode_cast<utf8::string>(text),' ');
958  if (words == last_words)
959  return;
960  last_words = words;
961 
962  filtered_items_.clear();
963  index_map_.clear();
964 
965  if(header_row_ == 1) {
966  filtered_items_.push_back(items_[0]);
967  index_map_.push_back(0);
968  }
969 
970  // we keep all items containing each word
971  for(size_t n = header_row_; n < items_to_filter_.size(); ++n) {
972  std::vector<std::string>::const_iterator w = words.begin();
973  for(; w != words.end(); ++w)
974  {
975  if (std::search(items_to_filter_[n].begin(), items_to_filter_[n].end(),
976  w->begin(), w->end(),
978  break; // one word doesn't match, we don't reach words.end()
979  }
980  if (w == words.end()) {
981  // all words have matched, keep the item
982  filtered_items_.push_back(items_[n]);
983  index_map_.push_back(n);
984  }
985  }
986 
988 }
989 
990 }//end namespace gui
#define LOG_DP
static style simple_style
Definition: menu.hpp:117
SDL_Rect text_area(const std::string &text, int size, int style)
Calculate the size of a text (in pixels) if it were to be drawn.
void set_callback(const std::string &name, callback_function)
Definition: context.cpp:53
std::vector< std::string > last_words
void set_check(bool check)
Definition: button.cpp:352
void set_result(const int result)
std::vector< char_t > string
bool done() const
void set_numeric_keypress_selection(bool value)
Definition: menu.cpp:761
void update_widget_positions()
void add_option(const std::string &label, bool checked=false, BUTTON_LOCATION loc=BUTTON_CHECKBOX, const std::string &help_string="")
int get_index(int selection) const
dialog_button_action * handler_
DIALOG_TYPE type_
dialog_button_action * handler
static const int message_font_size
void set_textbox(dialog_textbox *const box)
static const style default_style
Definition: show_dialog.hpp:69
class dialog * parent_
const std::function< int(const config &, const std::string &, int) > get_int
Definition: context.cpp:125
dimension_measurements dim_
std::vector< dialog_button * > standard_buttons_
std::vector< dialog_button * > extra_buttons_
virtual void action(dialog_process_info &dp_info)
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
logger & info()
Definition: log.cpp:91
Definition: video.hpp:58
class dialog * dialog() const
game_display * screen
Definition: resources.cpp:27
int relative_size(int size)
Definition: font.hpp:72
const SDL_Rect empty_rect
Definition: rect.cpp:26
void flip()
Definition: video.cpp:496
static const style & default_style
static const size_t image_h_pad
const style & style_
void add_button(dialog_button *const btn, BUTTON_LOCATION loc)
CVideo & video() const
Definition: widget.hpp:83
General purpose widgets.
#define ERR_DP
std::vector< std::string > empty_string_vector
Definition: help_impl.cpp:75
void set_width(int w)
Definition: widget.cpp:119
std::string word_wrap_text(const std::string &unwrapped_text, int font_size, int max_width, int max_height, int max_lines, bool partial_line)
Wrap text.
virtual bool show_above() const
dialog(CVideo &video, const std::string &title="", const std::string &message="", const DIALOG_TYPE type=MESSAGE, const style &dialog_style=default_style)
std::vector< button * > frame_buttons_
virtual void erase_item(size_t index)
Definition: menu.cpp:406
dialog_textbox * text_widget_
static const size_t bottom_padding
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
void blit_surface(int x, int y, surface surf, SDL_Rect *srcrect=nullptr, SDL_Rect *clip_rect=nullptr)
Definition: video.cpp:290
const std::string text() const
Definition: textbox.cpp:69
const int SIZE_PLUS
Definition: font.hpp:65
unsigned image_width(const std::string &filename)
Definition: help_impl.cpp:1379
const SDL_Color NORMAL_COLOR
Definition: font.cpp:564
surface & getSurface()
Definition: dummy_video.cpp:22
std::vector< std::string > filtered_items_
static menu * get_empty_menu(CVideo &video)
Provides create-on-use semantics for empty_menu.
dialog_frame * frame_
static const size_t right_padding
const int ButtonHPadding
Definition: show_dialog.cpp:43
std::vector< std::pair< dialog_button *, BUTTON_LOCATION > > button_pool_
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
dimension_measurements layout(int x, int y, int w, int h)
int top_padding() const
std::string topic_
std::vector< dialog_button * > top_buttons_
int bottom_padding() const
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
std::vector< std::string > items_to_filter_
static const style & message_style
bool chars_equal_insensitive(char a, char b)
Definition: util.hpp:206
GLuint GLuint end
Definition: glew.h:1221
void set_wrap(bool val)
Definition: textbox.cpp:277
virtual sdl_handler_vector handler_members()
int action(dialog_process_info &info)
int gety() const
Definition: video.cpp:481
std::vector< preview_pane * >::iterator pp_iterator
#define DBG_DP
std::string title_
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
void set_menu(menu *const m)
dialog_button * help_button_
int process()
Definition: menu.cpp:739
virtual bool left_side() const =0
static lg::log_domain log_display("display")
virtual dimension_measurements layout(int xloc=-1, int yloc=-1)
virtual void join()
Definition: events.cpp:173
static const size_t top_padding
std::vector< dialog_button * >::iterator button_iterator
void set_menu_items(const std::vector< std::string > &menu_items, bool keep_selection=false)
Changes (or sets, if not previously set) the dialog's menu items to the provided list.
dimension_measurements get_layout() const
static style & default_style
Definition: menu.hpp:116
void set_max_width(const int new_max_width)
Definition: menu.cpp:485
static const style hotkeys_style
surf
Definition: filter.cpp:143
void raise_draw_event()
Definition: events.cpp:565
void pump()
Definition: events.cpp:336
const std::string & get_text() const
Definition: label.cpp:50
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:47
virtual RESULT button_pressed(int menu_selection)=0
virtual void set_items(const std::vector< std::string > &items, bool strip_spaces=true, bool keep_viewport=false)
Set new items to show and redraw/recalculate everything.
Definition: menu.cpp:446
void set_help_string(const std::string &str)
Definition: widget.cpp:314
void set_image(dialog_image *const img)
GLuint res
Definition: glew.h:9258
dialog_frame & get_frame()
void raise_process_event()
Definition: events.cpp:539
std::vector< std::pair< dialog_button *, BUTTON_LOCATION > >::const_iterator button_pool_const_iterator
Thrown by operations encountering invalid UTF-8 data.
std::vector< preview_pane * >::const_iterator pp_const_iterator
int getx() const
Definition: video.cpp:472
bool update_locked() const
Definition: video.cpp:517
int result() const
static const size_t left_padding
std::map< dialog_button *, std::pair< int, int > > buttons
void set_parent(class dialog *parent)
virtual void handle_text_changed(const ucs4::string &text)
static menu * empty_menu
A pointer to this empty menu is used instead of nullptr (for menu_).
size_t i
Definition: function.cpp:1057
GLenum GLuint coords
Definition: glew.h:5805
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
label * get_label() const
virtual void draw()
Definition: widget.cpp:283
virtual int action(dialog_process_info &info)
std::map< preview_pane *, SDL_Rect > panes
std::vector< preview_pane * > preview_panes_
DIALOG_RESULT
Definition: show_dialog.hpp:33
std::vector< std::string > items_
bool double_clicked()
Definition: menu.cpp:749
std::vector< int > index_map_
GLint GLint GLint GLint GLint GLint GLsizei GLsizei height
Definition: glew.h:1220
const int SIZE_LARGE
Definition: font.hpp:66
const std::string button_press
bool option_checked(unsigned int option_index=0)
bool any_running()
Definition: manager.cpp:209
bool faked() const
Definition: video.hpp:166
static const int caption_font_size
GLclampd n
Definition: glew.h:5903
Contains the SDL_Rect helper code.
std::vector< dialog_button * >::const_iterator button_const_iterator
label * caption() const
bool find(E event, F functor)
Tests whether an event handler is available.
static const int max_menu_width
void play_UI_sound(const std::string &files)
Definition: sound.cpp:842
Standard logging facilities (interface).
int process(dialog_process_info &info)
gui::menu * menu_
static void delay(unsigned int milliseconds)
Definition: video.cpp:490
GLsizei GLenum GLuint GLuint GLsizei char * message
Definition: glew.h:2499
virtual void set_location(SDL_Rect const &rect)
Definition: widget.cpp:85
std::vector< std::pair< dialog_button *, BUTTON_LOCATION > >::iterator button_pool_iterator
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
int width() const
Definition: widget.cpp:134
SDL_Rect const & location() const
Definition: widget.cpp:144
virtual void leave()
Definition: events.cpp:212
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
void delete_item(int selection)
void notify_event(const std::string &name, const config &data)
Definition: manager.cpp:147
GLsizei const GLcharARB ** string
Definition: glew.h:4503
static const style message_style
Definition: show_dialog.hpp:70
void set_max_height(const int new_max_height)
Set a new max height for this menu.
Definition: menu.cpp:477
int selection() const
Definition: menu.cpp:369
void set_layout(dimension_measurements &new_dim)
static plugins_manager * get()
Definition: manager.cpp:61
void play_slice()
Definition: context.cpp:104
dialog_image * image_
int height() const
Definition: widget.cpp:139