The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
menu.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 #include "global.hpp"
18 
19 #include "widgets/menu.hpp"
20 
21 #include "game_config.hpp"
22 #include "font.hpp"
23 #include "language.hpp"
24 #include "image.hpp"
25 #include "marked-up_text.hpp"
26 #include "sdl/rect.hpp"
27 #include "sound.hpp"
28 #include "video.hpp"
29 #include "wml_separators.hpp"
30 
31 #include <numeric>
32 
33 namespace gui {
34 
36  : alpha_sort_()
37  , numeric_sort_()
38  , id_sort_()
39  , xp_sort_()
40  , level_sort_()
41  , redirect_sort_()
42  , pos_sort_()
43  , xp_col_(-1)
44 {
45  set_id_sort(-1);
46 }
47 
49 {
50  alpha_sort_.insert(column);
51  return *this;
52 }
53 
55 {
56  numeric_sort_.insert(column);
57  return *this;
58 }
59 
61 {
62  xp_sort_.insert(column);
63  return *this;
64 }
65 
66 menu::basic_sorter& menu::basic_sorter::set_level_sort(int level_column, int xp_column)
67 {
68  level_sort_.insert(level_column);
69  xp_col_ = xp_column;
70  return *this;
71 }
72 
74 {
75  id_sort_.insert(column);
76  return *this;
77 }
78 
80 {
81  if(column != to) {
82  redirect_sort_.insert(std::pair<int,int>(column,to));
83  }
84 
85  return *this;
86 }
87 
89 {
90  pos_sort_[column] = pos;
91  return *this;
92 }
93 
95 {
96  const std::map<int,int>::const_iterator redirect = redirect_sort_.find(column);
97  if(redirect != redirect_sort_.end()) {
98  return column_sortable(redirect->second);
99  }
100 
101  return alpha_sort_.count(column) == 1 || numeric_sort_.count(column) == 1 ||
102  pos_sort_.count(column) == 1 || id_sort_.count(column) == 1 ||
103  xp_sort_.count(column) == 1 || level_sort_.count(column) == 1;
104 }
105 
106 static std::pair<int, int> parse_fraction(const std::string& s)
107 {
108  std::vector<std::string> parts = utils::split(s, '/', 0);
109  parts.resize(2);
110  int num = lexical_cast_default<int>(parts[0], 0);
111  int denom = lexical_cast_default<int>(parts[1], 0);
112  return std::make_pair(num, denom);
113 }
114 
115 static int xp_to_advance(const std::string& s) {
116  std::pair<int,int> xp_frac = parse_fraction(s);
117 
118  //consider units without AMLA or advancement as having xp_max=1000000
119  if(xp_frac.second == 0)
120  xp_frac.second = 1000000;
121 
122  return xp_frac.second - xp_frac.first;
123 }
124 
125 bool menu::basic_sorter::less(int column, const item& row1, const item& row2) const
126 {
127  const std::map<int,int>::const_iterator redirect = redirect_sort_.find(column);
128  if(redirect != redirect_sort_.end()) {
129  return less(redirect->second,row1,row2);
130  }
131 
132  if(id_sort_.count(column) == 1) {
133  return row1.id < row2.id;
134  }
135 
136  if(column < 0 || column >= int(row2.fields.size())) {
137  return false;
138  }
139 
140  if(column >= int(row1.fields.size())) {
141  return true;
142  }
143 
144  const std::string& item1 = font::del_tags(row1.fields[column]);
145  const std::string& item2 = font::del_tags(row2.fields[column]);
146 
147  if(alpha_sort_.count(column) == 1) {
148  std::string::const_iterator begin1 = item1.begin(), end1 = item1.end(),
149  begin2 = item2.begin(), end2 = item2.end();
150  while(begin1 != end1 && is_wml_separator(*begin1)) {
151  ++begin1;
152  }
153 
154  while(begin2 != end2 && is_wml_separator(*begin2)) {
155  ++begin2;
156  }
157 
158  return std::lexicographical_compare(begin1,end1,begin2,end2,chars_less_insensitive);
159  } else if(numeric_sort_.count(column) == 1) {
160  int val_1 = lexical_cast_default<int>(item1, 0);
161  int val_2 = lexical_cast_default<int>(item2, 0);
162 
163  return val_1 > val_2;
164  } else if(xp_sort_.count(column) == 1) {
165  return xp_to_advance(item1) < xp_to_advance(item2);
166  } else if(level_sort_.count(column) == 1) {
167  int level_1 = lexical_cast_default<int>(item1, 0);
168  int level_2 = lexical_cast_default<int>(item2, 0);
169  if (level_1 == level_2) {
170  //break tie using xp
171  const std::string& xp_item1 = font::del_tags(row1.fields[xp_col_]);
172  const std::string& xp_item2 = font::del_tags(row2.fields[xp_col_]);
173  return xp_to_advance(xp_item1) < xp_to_advance(xp_item2);
174  }
175  return level_1 > level_2;
176  }
177 
178  const std::map<int,std::vector<int> >::const_iterator itor = pos_sort_.find(column);
179  if(itor != pos_sort_.end()) {
180  const std::vector<int>& pos = itor->second;
181  if(row1.id >= pos.size()) {
182  return false;
183  }
184 
185  if(row2.id >= pos.size()) {
186  return true;
187  }
188 
189  return pos[row1.id] < pos[row2.id];
190  }
191 
192  return false;
193 }
194 
195 menu::menu(CVideo& video, const std::vector<std::string>& items,
196  bool click_selects, int max_height, int max_width,
197  const sorter* sorter_obj, style *menu_style, const bool auto_join)
198 : scrollarea(video, auto_join), silent_(false),
199  max_height_(max_height), max_width_(max_width),
200  max_items_(-1), item_height_(-1),
201  heading_height_(-1),
202  cur_help_(-1,-1), help_string_(-1),
203  selected_(0), click_selects_(click_selects), out_(false),
204  previous_button_(true), show_result_(false),
205  double_clicked_(false),
206  num_selects_(true),
208  last_was_doubleclick_(false), use_ellipsis_(false),
209  sorter_(sorter_obj), sortby_(-1), sortreversed_(false), highlight_heading_(-1)
210 {
211  style_ = (menu_style) ? menu_style : &default_style;
212  style_->init();
213  fill_items(items, true);
214 }
215 
217 {
218 }
219 
220 void menu::fill_items(const std::vector<std::string>& items, bool strip_spaces)
221 {
222  for(std::vector<std::string>::const_iterator itor = items.begin();
223  itor != items.end(); ++itor) {
224 
225  if(itor->empty() == false && (*itor)[0] == HEADING_PREFIX) {
226  heading_ = utils::quoted_split(itor->substr(1),COLUMN_SEPARATOR, !strip_spaces);
227  continue;
228  }
229 
230  const size_t id = items_.size();
231  item_pos_.push_back(id);
232  const item new_item(utils::quoted_split(*itor, COLUMN_SEPARATOR, !strip_spaces),id);
233  items_.push_back(new_item);
234 
235  //make sure there is always at least one item
236  if(items_.back().fields.empty()) {
237  items_.back().fields.push_back(" ");
238  }
239 
240  //if the first character in an item is an asterisk,
241  //it means this item should be selected by default
242  std::string& first_item = items_.back().fields.front();
243  if(first_item.empty() == false && first_item[0] == DEFAULT_ITEM) {
244  selected_ = id;
245  first_item.erase(first_item.begin());
246  }
247  }
248 
250 
251  if(sortby_ >= 0) {
252  do_sort();
253  }
254  update_size();
255 }
256 
257 namespace {
258 
259 class sort_func
260 {
261 public:
262  sort_func(const menu::sorter& pred, int column) : pred_(&pred), column_(column)
263  {}
264 
265  bool operator()(const menu::item& a, const menu::item& b) const
266  {
267  return pred_->less(column_,a,b);
268  }
269 
270 private:
271  const menu::sorter* pred_;
272  int column_;
273 };
274 
275 }
276 
278 {
279  if(sorter_ == nullptr || sorter_->column_sortable(sortby_) == false) {
280  return;
281  }
282 
283  const int selectid = selection();
284 
285  std::stable_sort(items_.begin(), items_.end(), sort_func(*sorter_, sortby_));
286  if (sortreversed_)
287  std::reverse(items_.begin(), items_.end());
288 
289  recalculate_pos();
290 
291  if(selectid >= 0 && selectid < int(item_pos_.size())) {
292  move_selection_to(selectid, true, NO_MOVE_VIEWPORT);
293  }
294 
295  set_dirty();
296 }
297 
299 {
300  size_t sz = items_.size();
301  item_pos_.resize(sz);
302  for(size_t i = 0; i != sz; ++i)
303  item_pos_[items_[i].id] = i;
304  assert_pos();
305 }
306 
308 {
309  size_t sz = items_.size();
310  assert(item_pos_.size() == sz);
311  for(size_t n = 0; n != sz; ++n) {
312  assert(item_pos_[n] < sz && n == items_[item_pos_[n]].id);
313  }
314 }
315 
317 {
318  for(std::vector<item>::iterator i = items_.begin(); i != items_.end(); ++i) {
319  i->help.clear();
320  for(std::vector<std::string>::iterator j = i->fields.begin(); j != i->fields.end(); ++j) {
321  if(std::find(j->begin(),j->end(),static_cast<char>(HELP_STRING_SEPARATOR)) == j->end()) {
322  i->help.push_back("");
323  } else {
324  const std::vector<std::string>& items = utils::split(*j, HELP_STRING_SEPARATOR, 0);
325  if(items.size() >= 2) {
326  *j = items.front();
327  i->help.push_back(items.back());
328  } else {
329  i->help.push_back("");
330  }
331  }
332  }
333  }
334 }
335 
337 {
338  set_full_size(items_.size());
340 }
341 
343 {
344  int h = heading_height();
345  for(size_t i = get_position(),
346  i_end = std::min(items_.size(), i + max_items_onscreen());
347  i < i_end; ++i)
348  h += get_item_rect(i).h;
349  h = std::max(h, height());
350  if (max_height_ > 0 && h > (max_height_)) {
351  h = max_height_;
352  }
353 
354  use_ellipsis_ = false;
355  std::vector<int> const &widths = column_widths();
356  int w = std::accumulate(widths.begin(), widths.end(), 0);
357  if (items_.size() > max_items_onscreen())
358  w += scrollbar_width();
359  w = std::max(w, width());
360  if (max_width_ > 0 && w > (max_width_)) {
361  use_ellipsis_ = true;
362  w = max_width_;
363  }
364 
366  set_measurements(w, h);
367 }
368 
369 int menu::selection() const
370 {
371  if (selected_ >= items_.size()) {
372  return -1;
373  }
374 
375  return items_[selected_].id;
376 }
377 
378 void menu::set_inner_location(SDL_Rect const &rect)
379 {
380  itemRects_.clear();
382  bg_register(rect);
383 }
384 
385 const menu::item& menu::get_item(int index) const
386 {
387  return items_[index];
388 }
389 
391 {
392  return items_[selection()];
393 }
394 
395 void menu::change_item(int pos1, int pos2,const std::string& str)
396 {
397  if(pos1 < 0 || pos1 >= int(item_pos_.size()) ||
398  pos2 < 0 || pos2 >= int(items_[item_pos_[pos1]].fields.size())) {
399  return;
400  }
401 
402  items_[item_pos_[pos1]].fields[pos2] = str;
403  set_dirty();
404 }
405 
407 {
408  size_t nb_items = items_.size();
409  if (index >= nb_items)
410  return;
411  --nb_items;
412 
413  clear_item(nb_items);
414 
415  // fix ordered positions of items
416  size_t pos = item_pos_[index];
417  item_pos_.erase(item_pos_.begin() + index);
418  items_.erase(items_.begin() + pos);
419  for(size_t i = 0; i != nb_items; ++i) {
420  size_t &n1 = item_pos_[i], &n2 = items_[i].id;
421  if (n1 > pos) --n1;
422  if (n2 > index) --n2;
423  }
424  assert_pos();
425 
426  if (selected_ >= nb_items)
427  selected_ = nb_items - 1;
428 
431  itemRects_.clear();
432  set_dirty();
433 }
434 
435 void menu::set_heading(const std::vector<std::string>& heading)
436 {
437  itemRects_.clear();
438  column_widths_.clear();
439 
440  heading_ = heading;
441  max_items_ = -1;
442 
443  set_dirty();
444 }
445 
446 void menu::set_items(const std::vector<std::string>& items, bool strip_spaces, bool keep_viewport)
447 {
448 
449  const bool scrolled_to_max = (has_scrollbar() && get_position() == get_max_position());
450  items_.clear();
451  item_pos_.clear();
452  itemRects_.clear();
453  column_widths_.clear();
454  //undrawn_items_.clear();
455  max_items_ = -1; // Force recalculation of the max items.
456  item_height_ = -1; // Force recalculation of the item height.
457 
458  if (!keep_viewport || selected_ >= items.size()) {
459  selected_ = 0;
460  }
461 
462  fill_items(items, strip_spaces);
463  if(!keep_viewport) {
464  set_position(0);
465  } else if(scrolled_to_max) {
467  }
468 
470 
471  if(!keep_viewport) {
473  }
474  set_dirty();
475 }
476 
477 void menu::set_max_height(const int new_max_height)
478 {
479  max_height_ = new_max_height;
480  itemRects_.clear();
481  max_items_ = -1;
482  update_size();
483 }
484 
485 void menu::set_max_width(const int new_max_width)
486 {
487  max_width_ = new_max_width;
488  itemRects_.clear();
489  column_widths_.clear();
490  update_size();
491 }
492 
494 {
495  if(max_items_ != -1) {
496  return size_t(max_items_);
497  }
498 
499  const size_t max_height = (max_height_ == -1 ? (video().gety()*66)/100 : max_height_) - heading_height();
500 
501  std::vector<int> heights;
502  size_t n;
503  for(n = 0; n != items_.size(); ++n) {
504  heights.push_back(get_item_height(n));
505  }
506 
507  std::sort(heights.begin(),heights.end(),std::greater<int>());
508  size_t sum = 0;
509  for(n = 0; n != items_.size() && sum < max_height; ++n) {
510  sum += heights[n];
511  }
512 
513  if(sum > max_height && n > 1)
514  --n;
515 
516  return max_items_ = n;
517 }
518 
520 {
521  if(click_selects_)
522  return;
524 }
525 
526 void menu::set_selection_pos(size_t new_selected, bool silent, SELECTION_MOVE_VIEWPORT move_viewport)
527 {
528  if (new_selected >= items_.size())
529  return;
530 
531  bool changed = false;
532  if (new_selected != selected_) {
534  invalidate_row_pos(new_selected);
535  selected_ = new_selected;
536  changed = true;
537  }
538 
539  if(move_viewport == MOVE_VIEWPORT) {
541  if(!silent_ && !silent && changed) {
543  }
544  }
545 }
546 
547 void menu::move_selection_up(size_t dep)
548 {
549  set_selection_pos(selected_ > dep ? selected_ - dep : 0);
550 }
551 
553 {
554  size_t nb_items = items_.size();
555  set_selection_pos(selected_ + dep >= nb_items ? nb_items - 1 : selected_ + dep);
556 }
557 
558 // private function with control over sound and viewport
559 void menu::move_selection_to(size_t id, bool silent, SELECTION_MOVE_VIEWPORT move_viewport)
560 {
561  if(id < item_pos_.size()) {
562  set_selection_pos(item_pos_[id], silent, move_viewport);
563  }
564 }
565 
566 // public function
567 void menu::move_selection(size_t id)
568 {
569  if(id < item_pos_.size()) {
571  }
572 }
573 
574 // public function
576 {
577  if(id < item_pos_.size()) {
579  }
580 }
581 
583 {
584  set_selection_pos(0, true);
585 }
586 
588 {
589  if (!click_selects_) {
590  switch(key) {
591  case SDLK_UP:
593  break;
594  case SDLK_DOWN:
596  break;
597  case SDLK_PAGEUP:
599  break;
600  case SDLK_PAGEDOWN:
602  break;
603  case SDLK_HOME:
605  break;
606  case SDLK_END:
607  set_selection_pos(items_.size() - 1);
608  break;
609  //case SDLK_RETURN:
610  // double_clicked_ = true;
611  // break;
612  default:
613  break;
614  }
615  }
616 
617  if (num_selects_ && key >= SDLK_1 && key <= SDLK_9)
618  set_selection_pos(key - SDLK_1);
619 }
620 
621 bool menu::requires_event_focus(const SDL_Event* event) const
622 {
623  if(!focus_ || height() == 0 || hidden()) {
624  return false;
625  }
626  if(event == nullptr) {
627  //when event is not specified, signal that focus may be desired later
628  return true;
629  }
630 
631  if(event->type == SDL_KEYDOWN) {
632  SDLKey key = event->key.keysym.sym;
633  if (!click_selects_) {
634  switch(key) {
635  case SDLK_UP:
636  case SDLK_DOWN:
637  case SDLK_PAGEUP:
638  case SDLK_PAGEDOWN:
639  case SDLK_HOME:
640  case SDLK_END:
641  return true;
642  default:
643  break;
644  }
645  }
646  if (num_selects_ && key >= SDLK_1 && key <= SDLK_9) {
647  return true;
648  }
649  }
650  //mouse events are processed regardless of focus
651  return false;
652 }
653 
654 void menu::handle_event(const SDL_Event& event)
655 {
657  if (height()==0 || hidden())
658  return;
659 
660  if(event.type == SDL_KEYDOWN) {
661  // Only pass key events if we have the focus
662  if (focus(&event))
663  key_press(event.key.keysym.sym);
664  } else if(!mouse_locked() && ((event.type == SDL_MOUSEBUTTONDOWN &&
665  (event.button.button == SDL_BUTTON_LEFT || event.button.button == SDL_BUTTON_RIGHT)) ||
666  event.type == DOUBLE_CLICK_EVENT)) {
667 
668  int x = 0;
669  int y = 0;
670  if(event.type == SDL_MOUSEBUTTONDOWN) {
671  x = event.button.x;
672  y = event.button.y;
673  } else {
674  x = reinterpret_cast<size_t>(event.user.data1);
675  y = reinterpret_cast<size_t>(event.user.data2);
676  }
677 
678  const int item = hit(x,y);
679  if(item != -1) {
680  set_focus(true);
681  move_selection_to(item);
682 
683  if(click_selects_) {
684  show_result_ = true;
685  }
686 
687  if(event.type == DOUBLE_CLICK_EVENT) {
689  ignore_next_doubleclick_ = false;
690  } else {
691  double_clicked_ = true;
692  last_was_doubleclick_ = true;
693  if(!silent_) {
695  }
696  }
697  } else if (last_was_doubleclick_) {
698  // If we have a double click as the next event, it means
699  // this double click was generated from a click that
700  // already has helped in generating a double click.
701  SDL_Event ev;
702  SDL_PeepEvents(&ev, 1, SDL_PEEKEVENT,
704  if (ev.type == DOUBLE_CLICK_EVENT) {
706  }
707  last_was_doubleclick_ = false;
708  }
709  }
710 
711 
712  if(sorter_ != nullptr) {
713  const int heading = hit_heading(x,y);
714  if(heading >= 0 && sorter_->column_sortable(heading)) {
715  sort_by(heading);
716  }
717  }
718  } else if(!mouse_locked() && event.type == SDL_MOUSEMOTION) {
719  if(click_selects_) {
720  const int item = hit(event.motion.x,event.motion.y);
721  const bool out = (item == -1);
722  if (out_ != out) {
723  out_ = out;
725  }
726  if (item != -1) {
727  move_selection_to(item);
728  }
729  }
730 
731  const int heading_item = hit_heading(event.motion.x,event.motion.y);
732  if(heading_item != highlight_heading_) {
733  highlight_heading_ = heading_item;
735  }
736  }
737 }
738 
740 {
741  if(show_result_) {
742  show_result_ = false;
743  return selected_;
744  } else {
745  return -1;
746  }
747 }
748 
750 {
751  bool old = double_clicked_;
752  double_clicked_ = false;
753  return old;
754 }
755 
757 {
759 }
760 
762 {
764 }
765 
766 void menu::scroll(unsigned int)
767 {
768  itemRects_.clear();
769  set_dirty();
770 }
771 
773 {
774  if(sortby_ >= 0) {
775  //clear an existing sort
776  sort_by(-1);
777  }
778  sorter_ = s;
779  sortreversed_ = false;
780  sortby_ = -1;
781 }
782 
784 {
785  const bool already_sorted = (column == sortby_);
786 
787  if(already_sorted) {
788  if(sortreversed_ == false) {
789  sortreversed_ = true;
790  } else {
791  sortreversed_ = false;
792  sortby_ = -1;
793  }
794  } else {
795  sortby_ = column;
796  sortreversed_ = false;
797  }
798 
799  do_sort();
800  itemRects_.clear();
801  set_dirty();
802 }
803 
804 SDL_Rect menu::style::item_size(const std::string& item) const {
805  SDL_Rect res = {0,0,0,0};
806  std::vector<std::string> img_text_items = utils::split(item, IMG_TEXT_SEPARATOR, utils::REMOVE_EMPTY);
807  for (std::vector<std::string>::const_iterator it = img_text_items.begin();
808  it != img_text_items.end(); ++it) {
809  if (res.w > 0 || res.h > 0) {
810  // Not the first item, add the spacing.
811  res.w += 5;
812  }
813  const std::string str = *it;
814 #ifdef SDL_GPU
815  if (!str.empty() && str[0] == IMAGE_PREFIX) {
816  const std::string image_name(str.begin()+1,str.end());
817  sdl::timage img = get_item_image(image_name);
818  if(!img.null()) {
819  res.w += img.width();
820  res.h = std::max<int>(img.height(), res.h);
821  }
822  }
823 #else
824  if (!str.empty() && str[0] == IMAGE_PREFIX) {
825  const std::string image_name(str.begin()+1,str.end());
826  surface const img = get_item_image(image_name);
827  if(img != nullptr) {
828  res.w += img->w;
829  res.h = std::max<int>(img->h, res.h);
830  }
831  }
832 #endif
833  else {
834  const SDL_Rect area = {0,0,10000,10000};
835  const SDL_Rect font_size =
836  font::draw_text(nullptr,area,get_font_size(),font::NORMAL_COLOR,str,0,0);
837  res.w += font_size.w;
838  res.h = std::max<int>(font_size.h, res.h);
839  }
840  }
841  return res;
842 }
843 
844 void menu::style::draw_row_bg(menu& menu_ref, const size_t /*row_index*/, const SDL_Rect& rect, ROW_TYPE type)
845 {
846  menu_ref.bg_restore(rect);
847 
848  int rgb = 0;
849  double alpha = 0.0;
850 
851  switch(type) {
852  case NORMAL_ROW:
853  rgb = normal_rgb_;
854  alpha = normal_alpha_;
855  break;
856  case SELECTED_ROW:
857  rgb = selected_rgb_;
858  alpha = selected_alpha_;
859  break;
860  case HEADING_ROW:
861  rgb = heading_rgb_;
862  alpha = heading_alpha_;
863  break;
864  }
865 
866 #ifdef SDL_GPU
867  sdl::draw_solid_tinted_rectangle(rect.x, rect.y, rect.w, rect.h,
868  (rgb&0xff0000) >> 16,(rgb&0xff00) >> 8,rgb&0xff,alpha,
869  menu_ref.video().getSurface());
870  sdl::fill_rect(menu_ref.video(), rect, (rgb&0xff0000) >> 16,
871  (rgb&0xff00) >> 8, rgb&0xff, alpha);
872 #else
873  sdl::draw_solid_tinted_rectangle(rect.x, rect.y, rect.w, rect.h,
874  (rgb&0xff0000) >> 16,(rgb&0xff00) >> 8,rgb&0xff,alpha,
875  menu_ref.video().getSurface());
876 #endif
877 }
878 
879 void menu::style::draw_row(menu& menu_ref, const size_t row_index, const SDL_Rect& rect, ROW_TYPE type)
880 {
881  if(rect.w == 0 || rect.h == 0) {
882  return;
883  }
884  draw_row_bg(menu_ref, row_index, rect, type);
885 
886  SDL_Rect minirect = rect;
887  if(type != HEADING_ROW) {
888  minirect.x += thickness_;
889  minirect.y += thickness_;
890  minirect.w -= 2*thickness_;
891  minirect.h -= 2*thickness_;
892  }
893  menu_ref.draw_row(row_index, minirect, type);
894 }
895 
896 
897 
898 void menu::column_widths_item(const std::vector<std::string>& row, std::vector<int>& widths) const
899 {
900  for(size_t col = 0; col != row.size(); ++col) {
901  const SDL_Rect res = style_->item_size(row[col]);
902  size_t text_trailing_space = (item_ends_with_image(row[col])) ? 0 : style_->get_cell_padding();
903 
904  if(col == widths.size()) {
905  widths.push_back(res.w + text_trailing_space);
906  } else if(static_cast<size_t>(res.w) > widths[col] - text_trailing_space) {
907  widths[col] = res.w + text_trailing_space;
908  }
909  }
910 }
911 
913 {
914  std::string::size_type pos = item.find_last_of(IMG_TEXT_SEPARATOR);
915  pos = (pos == std::string::npos) ? 0 : pos+1;
916  return(item.size() > pos && item.at(pos) == IMAGE_PREFIX);
917 }
918 
919 const std::vector<int>& menu::column_widths() const
920 {
921  if(column_widths_.empty()) {
923  for(size_t row = 0; row != items_.size(); ++row) {
925  }
926  }
927 
928  return column_widths_;
929 }
930 
932 {
933  SDL_Rect rect = get_item_rect(item);
934  if (rect.w == 0)
935  return;
936  bg_restore(rect);
937 }
938 
939 void menu::draw_row(const size_t row_index, const SDL_Rect& rect, ROW_TYPE type)
940 {
941  //called from style, draws one row's contents in a generic and adaptable way
942  const std::vector<std::string>& row = (type == HEADING_ROW) ? heading_ : items_[row_index].fields;
943  SDL_Rect const &area = screen_area();
944  SDL_Rect const &loc = inner_location();
945  const std::vector<int>& widths = column_widths();
946  bool lang_rtl = current_language_rtl();
947  int dir = (lang_rtl) ? -1 : 1;
948  SDL_Rect column = loc;
949 
950  int xpos = rect.x;
951  if(lang_rtl)
952  xpos += rect.w;
953  for(size_t i = 0; i != row.size(); ++i) {
954 
955  if(lang_rtl)
956  xpos -= widths[i];
957  if(type == HEADING_ROW) {
958 #ifdef SDL_GPU
959  const SDL_Rect pos = sdl::create_rect(xpos, rect.y, widths[i], rect.h);
960  if(highlight_heading_ == int(i)) {
961  sdl::fill_rect(video(), pos, 255, 255, 255, 75);
962  } else if(sortby_ == int(i)) {
963  sdl::fill_rect(video(), pos, 255, 255, 255, 25);
964  }
965  }
966 
967  const int last_x = xpos;
968  column.w = widths[i];
969  std::string str = row[i];
970  std::vector<std::string> img_text_items = utils::split(str, IMG_TEXT_SEPARATOR, utils::REMOVE_EMPTY);
971  for (std::vector<std::string>::const_iterator it = img_text_items.begin();
972  it != img_text_items.end(); ++it) {
973  str = *it;
974  if (!str.empty() && str[0] == IMAGE_PREFIX) {
975  const std::string image_name(str.begin()+1,str.end());
976  sdl::timage img = style_->get_item_image(image_name);
977  const int remaining_width = max_width_ < 0 ? area.w :
978  std::min<int>(max_width_, ((lang_rtl)? xpos - rect.x : rect.x + rect.w - xpos));
979  if(!img.null() && img.width() <= remaining_width
980  && rect.y + img.height() < area.h) {
981  const size_t y = rect.y + (rect.h - img.height())/2;
982  const size_t w = img.width() + 5;
983  const size_t x = xpos + ((lang_rtl) ? widths[i] - w : 0);
984  video().draw_texture(img, x, y);
985  if(!lang_rtl)
986  xpos += w;
987  column.w -= w;
988  }
989  } else {
990  column.x = xpos;
991  const bool has_wrap = (str.find_first_of("\r\n") != std::string::npos);
992  //prevent ellipsis calculation if there is any line wrapping
993  std::string to_show = str;
994  if (use_ellipsis_ && !has_wrap)
995  {
996  int fs = style_->get_font_size();
997  int style = TTF_STYLE_NORMAL;
998  int w = loc.w - (xpos - rect.x) - 2 * style_->get_thickness();
999  std::string::const_iterator i_beg = to_show.begin(), i_end = to_show.end(),
1000  i = font::parse_markup(i_beg, i_end, &fs, nullptr, &style);
1001  if (i != i_end) {
1002  std::string tmp(i, i_end);
1003  to_show.erase(i - i_beg, i_end - i_beg);
1004  to_show += font::make_text_ellipsis(tmp, fs, w, style);
1005  }
1006  }
1007  const SDL_Rect& text_size = font::text_area(str,style_->get_font_size());
1008  const size_t y = rect.y + (rect.h - text_size.h)/2;
1009  const size_t padding = 2;
1010  sdl::timage text_img = font::draw_text_to_texture(column,style_->get_font_size(),font::NORMAL_COLOR,to_show);
1011  video().draw_texture(text_img, (type == HEADING_ROW ? xpos+padding : xpos), y);
1012  if(type == HEADING_ROW && sortby_ == int(i)) {
1013  sdl::timage sort_img = image::get_texture("buttons/sliders/slider_arrow_blue.png");
1014  sort_img.set_rotation(sortreversed_ ? 0 : 180);
1015  if(!sort_img.null() && sort_img.width() <= widths[i] && sort_img.height() <= rect.h) {
1016  const size_t sort_x = xpos + widths[i] - sort_img.width() - padding;
1017  const size_t sort_y = rect.y + rect.h/2 - sort_img.height()/2;
1018  video().draw_texture(sort_img, sort_x, sort_y);
1019  }
1020  }
1021  xpos += dir * (text_size.w + 5);
1022  }
1023  }
1024  if(lang_rtl)
1025  xpos = last_x;
1026  else
1027  xpos = last_x + widths[i];
1028  }
1029 #else
1030  if(highlight_heading_ == int(i)) {
1031  sdl::draw_solid_tinted_rectangle(xpos,rect.y,widths[i],rect.h,255,255,255,0.3,video().getSurface());
1032  } else if(sortby_ == int(i)) {
1033  sdl::draw_solid_tinted_rectangle(xpos,rect.y,widths[i],rect.h,255,255,255,0.1,video().getSurface());
1034  }
1035  }
1036 
1037  const int last_x = xpos;
1038  column.w = widths[i];
1039  std::string str = row[i];
1040  std::vector<std::string> img_text_items = utils::split(str, IMG_TEXT_SEPARATOR, utils::REMOVE_EMPTY);
1041  for (std::vector<std::string>::const_iterator it = img_text_items.begin();
1042  it != img_text_items.end(); ++it) {
1043  str = *it;
1044  if (!str.empty() && str[0] == IMAGE_PREFIX) {
1045  const std::string image_name(str.begin()+1,str.end());
1046  const surface img = style_->get_item_image(image_name);
1047  const int remaining_width = max_width_ < 0 ? area.w :
1048  std::min<int>(max_width_, ((lang_rtl)? xpos - rect.x : rect.x + rect.w - xpos));
1049  if(img != nullptr && img->w <= remaining_width
1050  && rect.y + img->h < area.h) {
1051  const size_t y = rect.y + (rect.h - img->h)/2;
1052  const size_t w = img->w + 5;
1053  const size_t x = xpos + ((lang_rtl) ? widths[i] - w : 0);
1054  video().blit_surface(x,y,img);
1055  if(!lang_rtl)
1056  xpos += w;
1057  column.w -= w;
1058  }
1059  } else {
1060  column.x = xpos;
1061  const bool has_wrap = (str.find_first_of("\r\n") != std::string::npos);
1062  //prevent ellipsis calculation if there is any line wrapping
1063  std::string to_show = str;
1064  if (use_ellipsis_ && !has_wrap)
1065  {
1066  int fs = style_->get_font_size();
1067  int style = TTF_STYLE_NORMAL;
1068  int w = rect.w - (xpos - rect.x) - 2 * style_->get_thickness();
1069  std::string::const_iterator i_beg = to_show.begin(), i_end = to_show.end(),
1070  i = font::parse_markup(i_beg, i_end, &fs, nullptr, &style);
1071  if (i != i_end) {
1072  std::string tmp(i, i_end);
1073  to_show.erase(i - i_beg, i_end - i_beg);
1074  to_show += font::make_text_ellipsis(tmp, fs, w, style);
1075  }
1076  }
1077  const SDL_Rect& text_size = font::text_area(str,style_->get_font_size());
1078  const size_t y = rect.y + (rect.h - text_size.h)/2;
1079  const size_t padding = 2;
1081  (type == HEADING_ROW ? xpos+padding : xpos), y);
1082 
1083  if(type == HEADING_ROW && sortby_ == int(i)) {
1084  const surface sort_img = image::get_image(sortreversed_ ? "buttons/sliders/slider_arrow_blue.png" :
1085  "buttons/sliders/slider_arrow_blue.png~ROTATE(180)");
1086  if(sort_img != nullptr && sort_img->w <= widths[i] && sort_img->h <= rect.h) {
1087  const size_t sort_x = xpos + widths[i] - sort_img->w - padding;
1088  const size_t sort_y = rect.y + rect.h/2 - sort_img->h/2;
1089  video().blit_surface(sort_x,sort_y,sort_img);
1090  }
1091  }
1092 
1093  xpos += dir * (text_size.w + 5);
1094  }
1095  }
1096  if(lang_rtl)
1097  xpos = last_x;
1098  else
1099  xpos = last_x + widths[i];
1100  }
1101 #endif
1102 }
1103 
1105 {
1106  SDL_Rect heading_rect = inner_location();
1107  heading_rect.h = heading_height();
1108  style_->draw_row(*this,0,heading_rect,HEADING_ROW);
1109 
1110  for(size_t i = 0; i != item_pos_.size(); ++i) {
1112  (!out_ && item_pos_[i] == selected_) ? SELECTED_ROW : NORMAL_ROW);
1113  }
1114 }
1115 
1117 {
1118  if(hidden()) {
1119  return;
1120  }
1121 
1122  if(!dirty()) {
1123 
1124  for(std::set<int>::const_iterator i = invalid_.begin(); i != invalid_.end(); ++i) {
1125  if(*i == -1) {
1126  SDL_Rect heading_rect = inner_location();
1127  heading_rect.h = heading_height();
1128  bg_restore(heading_rect);
1129  style_->draw_row(*this,0,heading_rect,HEADING_ROW);
1130  update_rect(heading_rect);
1131  } else if(*i >= 0 && *i < int(item_pos_.size())) {
1132  const unsigned int pos = item_pos_[*i];
1133  const SDL_Rect& rect = get_item_rect(*i);
1134  bg_restore(rect);
1135  style_->draw_row(*this,pos,rect,
1136  (!out_ && pos == selected_) ? SELECTED_ROW : NORMAL_ROW);
1137  update_rect(rect);
1138  }
1139  }
1140 
1141  invalid_.clear();
1142  return;
1143  }
1144 
1145  invalid_.clear();
1146 
1147  bg_restore();
1148 
1149  clip_rect_setter clipping_rect =
1150  clip_rect_setter(video().getSurface(), clip_rect(), clip_rect() != nullptr);
1151 
1152  draw_contents();
1153 
1154  update_rect(location());
1155  set_dirty(false);
1156 }
1157 
1158 int menu::hit(int x, int y) const
1159 {
1160  SDL_Rect const &loc = inner_location();
1161  if (x >= loc.x && x < loc.x + loc.w && y >= loc.y && y < loc.y + loc.h) {
1162  for(size_t i = 0; i != items_.size(); ++i) {
1163  const SDL_Rect& rect = get_item_rect(i);
1164  if (y >= rect.y && y < rect.y + rect.h)
1165  return i;
1166  }
1167  }
1168 
1169  return -1;
1170 }
1171 
1172 int menu::hit_column(int x) const
1173 {
1174  std::vector<int> const &widths = column_widths();
1175  int j = -1, j_end = widths.size();
1176  for(x -= location().x; x >= 0; x -= widths[j]) {
1177  if(++j == j_end) {
1178  return -1;
1179  }
1180  }
1181  return j;
1182 }
1183 
1184 std::pair<int,int> menu::hit_cell(int x, int y) const
1185 {
1186  const int row = hit(x, y);
1187  if(row < 0) {
1188  return std::pair<int,int>(-1, -1);
1189  }
1190 
1191  const int col = hit_column(x);
1192  if(col < 0) {
1193  return std::pair<int,int>(-1, -1);
1194  }
1195 
1196  return std::pair<int,int>(x,y);
1197 }
1198 
1199 int menu::hit_heading(int x, int y) const
1200 {
1201  const size_t height = heading_height();
1202  const SDL_Rect& loc = inner_location();
1203  if(y >= loc.y && static_cast<size_t>(y) < loc.y + height) {
1204  return hit_column(x);
1205  } else {
1206  return -1;
1207  }
1208 }
1209 
1210 SDL_Rect menu::get_item_rect(int item) const
1211 {
1212  return get_item_rect_internal(item_pos_[item]);
1213 }
1214 
1215 SDL_Rect menu::get_item_rect_internal(size_t item) const
1216 {
1217  unsigned int first_item_on_screen = get_position();
1218  if (item < first_item_on_screen ||
1219  item >= first_item_on_screen + max_items_onscreen()) {
1220  return sdl::empty_rect;
1221  }
1222 
1223  const std::map<int,SDL_Rect>::const_iterator i = itemRects_.find(item);
1224  if(i != itemRects_.end())
1225  return i->second;
1226 
1227  SDL_Rect const &loc = inner_location();
1228 
1229  int y = loc.y + heading_height();
1230  if (item != first_item_on_screen) {
1231  const SDL_Rect& prev = get_item_rect_internal(item-1);
1232  y = prev.y + prev.h;
1233  }
1234 
1235  SDL_Rect res = sdl::create_rect(loc.x, y, loc.w, get_item_height(item));
1236 
1237  SDL_Rect const &screen_area = ::screen_area();
1238 
1239  if(res.x > screen_area.w) {
1240  return sdl::empty_rect;
1241  } else if(res.x + res.w > screen_area.w) {
1242  res.w = screen_area.w - res.x;
1243  }
1244 
1245  if(res.y > screen_area.h) {
1246  return sdl::empty_rect;
1247  } else if(res.y + res.h > screen_area.h) {
1248  res.h = screen_area.h - res.y;
1249  }
1250 
1251  //only insert into the cache if the menu's co-ordinates have
1252  //been initialized
1253  if (loc.x > 0 && loc.y > 0)
1254  itemRects_.insert(std::pair<int,SDL_Rect>(item,res));
1255 
1256  return res;
1257 }
1258 
1259 size_t menu::get_item_height_internal(const std::vector<std::string>& item) const
1260 {
1261  size_t res = 0;
1262  for(std::vector<std::string>::const_iterator i = item.begin(); i != item.end(); ++i) {
1263  SDL_Rect rect = style_->item_size(*i);
1264  res = std::max<int>(rect.h,res);
1265  }
1266 
1267  return res;
1268 }
1269 
1270 size_t menu::heading_height() const
1271 {
1272  if(heading_height_ == -1) {
1274  }
1275 
1276  return std::min<unsigned int>(heading_height_,max_height_);
1277 }
1278 
1279 size_t menu::get_item_height(int) const
1280 {
1281  if(item_height_ != -1)
1282  return size_t(item_height_);
1283 
1284  size_t max_height = 0;
1285  for(size_t n = 0; n != items_.size(); ++n) {
1286  max_height = std::max<int>(max_height,get_item_height_internal(items_[n].fields));
1287  }
1288 
1289  return item_height_ = max_height;
1290 }
1291 
1292 void menu::process_help_string(int mousex, int mousey)
1293 {
1294  if (hidden()) return;
1295 
1296  const std::pair<int,int> loc(hit(mousex,mousey), hit_column(mousex));
1297  if(loc == cur_help_) {
1298  return;
1299  } else if(loc.first == -1) {
1301  help_string_ = -1;
1302  } else {
1303  if(help_string_ != -1) {
1305  help_string_ = -1;
1306  }
1307  if(size_t(loc.first) < items_.size()) {
1308  const std::vector<std::string>& row = items_[item_pos_[loc.first]].help;
1309  if(size_t(loc.second) < row.size()) {
1310  const std::string& help = row[loc.second];
1311  if(help.empty() == false) {
1312  //std::cerr << "setting help string from menu to '" << help << "'\n";
1314  }
1315  }
1316  }
1317  }
1318 
1319  cur_help_ = loc;
1320 }
1321 
1322 void menu::invalidate_row(size_t id)
1323 {
1324  if(id >= items_.size()) {
1325  return;
1326  }
1327 
1328  invalid_.insert(int(id));
1329 }
1330 
1332 {
1333  if(pos >= items_.size()) {
1334  return;
1335  }
1336 
1337  invalidate_row(items_[pos].id);
1338 }
1339 
1341 {
1342  invalid_.insert(-1);
1343 }
1344 
1345 }
basic_sorter & set_alpha_sort(int column)
Definition: menu.cpp:48
size_t max_items_onscreen() const
Definition: menu.cpp:493
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.
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:878
int hit_heading(int x, int y) const
Definition: menu.cpp:1199
void sort_by(int column)
Definition: menu.cpp:783
size_t get_font_size() const
Definition: menu_style.cpp:60
bool last_was_doubleclick_
Definition: menu.hpp:307
char const IMG_TEXT_SEPARATOR
int hit(int x, int y) const
Definition: menu.cpp:1158
virtual void draw_row_bg(menu &menu_ref, const size_t row_index, const SDL_Rect &rect, ROW_TYPE type)
Definition: menu.cpp:844
void set_shown_size(unsigned h)
Definition: scrollarea.cpp:110
const std::vector< int > & column_widths() const
Definition: menu.cpp:919
static std::pair< int, int > parse_fraction(const std::string &s)
Definition: menu.cpp:106
void set_numeric_keypress_selection(bool value)
Definition: menu.cpp:761
void fill_rect(surface &dst, SDL_Rect *dst_rect, const Uint32 color)
Fill a rectangle on a given surface.
Definition: rect.hpp:143
void adjust_position(unsigned pos)
Definition: scrollarea.cpp:100
void invalidate_row(size_t id)
Definition: menu.cpp:1322
virtual void init()
Definition: menu.hpp:39
size_t selected_
Definition: menu.hpp:271
int max_height_
Definition: menu.hpp:251
int pos
Definition: formula.cpp:800
bool num_selects_
variable which determines whether a numeric keypress should select an item on the dialog ...
Definition: menu.hpp:302
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
Definition: video.hpp:58
const SDL_Rect empty_rect
Definition: rect.cpp:26
int sortby_
Definition: menu.hpp:313
void bg_register(SDL_Rect const &rect)
Definition: widget.cpp:109
ROW_TYPE
Definition: menu.hpp:32
basic_sorter & set_position_sort(int column, const std::vector< int > &pos)
Definition: menu.cpp:88
std::vector< std::string > heading_
Definition: menu.hpp:260
CVideo & video() const
Definition: widget.hpp:83
General purpose widgets.
REMOVE_EMPTY : remove empty elements.
bool has_scrollbar() const
Definition: scrollarea.cpp:35
void do_sort()
Definition: menu.cpp:277
unsigned scrollbar_width() const
Definition: scrollarea.cpp:146
basic_sorter & set_redirect_sort(int column, int to)
Definition: menu.cpp:79
void move_selection_up(size_t dep)
Definition: menu.cpp:547
void set_focus(bool focus)
Definition: widget.cpp:149
SELECTION_MOVE_VIEWPORT
Definition: menu.hpp:326
const std::string menu_select
bool show_result_
Definition: menu.hpp:277
size_t get_item_height(int item) const
Definition: menu.cpp:1279
virtual void erase_item(size_t index)
Definition: menu.cpp:406
bool item_ends_with_image(const std::string &item) const
Definition: menu.cpp:912
virtual bool column_sortable(int column) const =0
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
const std::vector< std::string > items
std::vector< int > column_widths_
Definition: menu.hpp:269
void blit_surface(int x, int y, surface surf, SDL_Rect *srcrect=nullptr, SDL_Rect *clip_rect=nullptr)
Definition: video.cpp:290
virtual void handle_event(const SDL_Event &event)
Definition: menu.cpp:654
surface get_item_image(const image::locator &i_locator) const
Definition: menu_style.cpp:92
void adjust_viewport_to_selection()
Definition: menu.cpp:519
const SDL_Color NORMAL_COLOR
Definition: font.cpp:564
-file util.hpp
static int xp_to_advance(const std::string &s)
Definition: menu.cpp:115
void set_sorter(sorter *s)
Definition: menu.cpp:772
#define SDLKey
Definition: compat.hpp:29
unsigned thickness_
The thickness of the line.
Definition: canvas.cpp:293
int item_height_
Definition: menu.hpp:252
surface & getSurface()
Definition: dummy_video.cpp:22
char const IMAGE_PREFIX
size_t get_item_height_internal(const std::vector< std::string > &item) const
Definition: menu.cpp:1259
void update_size()
Definition: menu.cpp:342
virtual void draw_row(const size_t row_index, const SDL_Rect &rect, ROW_TYPE type)
Definition: menu.cpp:939
void set_click_selects(bool value)
Definition: menu.cpp:756
SDL_Rect screen_area()
Definition: video.cpp:135
void set_measurements(int w, int h)
Definition: widget.cpp:129
bool ignore_next_doubleclick_
Definition: menu.hpp:306
virtual bool column_sortable(int column) const
Definition: menu.cpp:94
void reset_selection()
Definition: menu.cpp:582
bool sortreversed_
Definition: menu.hpp:314
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
bool hidden() const
Definition: widget.cpp:198
void set_full_size(unsigned h)
Definition: scrollarea.cpp:117
void set_dirty(bool dirty=true)
Definition: widget.cpp:217
bool chars_less_insensitive(char a, char b)
Definition: util.hpp:207
char const DEFAULT_ITEM
friend class style
Definition: menu.hpp:114
void create_help_strings()
Definition: menu.cpp:316
void move_selection_keeping_viewport(size_t id)
Definition: menu.cpp:575
void draw_contents()
Definition: menu.cpp:1104
void set_position(unsigned pos)
Definition: scrollarea.cpp:95
bool requires_event_focus(const SDL_Event *event=nullptr) const
Definition: menu.cpp:621
char const HELP_STRING_SEPARATOR
SDL_Rect inner_location() const
Definition: scrollarea.cpp:138
const SDL_Rect * clip_rect() const
Definition: widget.cpp:104
void draw_solid_tinted_rectangle(int x, int y, int w, int h, int r, int g, int b, double alpha, surface target)
Fills a specified rectangle area of a surface with a given color and opacity.
Definition: rect.cpp:117
bool current_language_rtl()
Definition: language.cpp:55
void fill_items(const std::vector< std::string > &items, bool strip_spaces)
Set new items to show.
Definition: menu.cpp:220
bool focus(const SDL_Event *event)
Definition: widget.cpp:157
int gety() const
Definition: video.cpp:481
bool out_
Definition: menu.hpp:273
void update_scrollbar_grip_height()
Definition: menu.cpp:336
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
GLsizei const GLfloat * value
Definition: glew.h:1817
std::pair< int, int > cur_help_
Definition: menu.hpp:266
basic_sorter & set_id_sort(int column)
Definition: menu.cpp:73
virtual bool less(int column, const item &row1, const item &row2) const
Definition: menu.cpp:125
bool dirty() const
Definition: widget.cpp:227
std::vector< std::string > fields
Definition: menu.hpp:129
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
int process()
Definition: menu.cpp:739
size_t get_thickness() const
Definition: menu_style.cpp:62
int max_width_
Definition: menu.hpp:251
GLuint num
Definition: glew.h:2552
GLclampf GLclampf GLclampf alpha
Definition: glew.h:1488
void set_heading(const std::vector< std::string > &heading)
Definition: menu.cpp:435
static style & default_style
Definition: menu.hpp:116
void set_max_width(const int new_max_width)
Definition: menu.cpp:485
int highlight_heading_
Definition: menu.hpp:315
void set_inner_location(const SDL_Rect &rect)
Definition: menu.cpp:378
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
void key_press(SDLKey key)
Definition: menu.cpp:587
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
std::string::const_iterator parse_markup(std::string::const_iterator i1, std::string::const_iterator i2, int *font_size, SDL_Color *color, int *style)
Parses the markup-tags at the front of a string.
void move_selection(size_t id)
Definition: menu.cpp:567
unsigned get_position() const
Definition: scrollarea.cpp:85
SDL_Rect get_item_rect_internal(size_t pos) const
Definition: menu.cpp:1215
GLenum GLenum GLvoid GLvoid * column
Definition: glew.h:3805
void change_item(int pos1, int pos2, const std::string &str)
Definition: menu.cpp:395
SDL_Rect draw_text(surface &dst, const SDL_Rect &area, int size, const SDL_Color &color, const std::string &txt, int x, int y, bool use_tooltips, int style)
Function to draw text on a surface.
std::vector< std::string > quoted_split(std::string const &val, char c, int flags, char quote)
This function is identical to split(), except it does not split when it otherwise would if the previo...
GLuint res
Definition: glew.h:9258
void move_selection_down(size_t dep)
Definition: menu.cpp:552
#define DOUBLE_CLICK_EVENT
Definition: events.hpp:23
bool is_wml_separator(char c)
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
const item & get_item(int index) const
Definition: menu.cpp:385
int help_string_
Definition: menu.hpp:267
bool previous_button_
Definition: menu.hpp:274
void assert_pos()
Definition: menu.cpp:307
size_t heading_height() const
Definition: menu.cpp:1270
std::vector< size_t > item_pos_
Definition: menu.hpp:258
void invalidate_heading()
Definition: menu.cpp:1340
GLuint index
Definition: glew.h:1782
virtual void handle_event(const SDL_Event &event)
Definition: scrollarea.cpp:151
bool focus_
Definition: widget.hpp:94
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
size_t i
Definition: function.cpp:1057
void scroll(unsigned int pos)
Definition: menu.cpp:766
bool use_ellipsis_
Definition: menu.hpp:310
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
unsigned get_max_position() const
Definition: scrollarea.cpp:90
static int sort(lua_State *L)
Definition: ltablib.cpp:246
void set_selection_pos(size_t pos, bool silent=false, SELECTION_MOVE_VIEWPORT move_viewport=MOVE_VIEWPORT)
Definition: menu.cpp:526
bool double_clicked()
Definition: menu.cpp:749
std::set< int > invalid_
Definition: menu.hpp:332
basic_sorter & set_numeric_sort(int column)
Definition: menu.cpp:54
void column_widths_item(const std::vector< std::string > &row, std::vector< int > &widths) const
Definition: menu.cpp:898
void invalidate_row_pos(size_t pos)
Definition: menu.cpp:1331
size_t id
Definition: menu.hpp:131
std::pair< int, int > hit_cell(int x, int y) const
Definition: menu.cpp:1184
GLint GLint GLint GLint GLint GLint GLsizei GLsizei height
Definition: glew.h:1220
int set_help_string(const std::string &str)
Definition: video.cpp:625
std::string make_text_ellipsis(const std::string &text, int font_size, int max_width, int style)
If the text exceeds the specified max width, end it with an ellipsis (...)
Definition: font.cpp:996
void draw()
Definition: menu.cpp:1116
SDL_Rect get_item_rect(int item) const
Definition: menu.cpp:1210
const std::string button_press
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an empty SDL_Rect.
Definition: rect.cpp:28
#define SDL_EVENTMASK(EVENT)
Definition: compat.hpp:39
GLenum GLenum GLvoid * row
Definition: glew.h:3805
char const HEADING_PREFIX
void move_selection_to(size_t id, bool silent=false, SELECTION_MOVE_VIEWPORT move_viewport=MOVE_VIEWPORT)
Definition: menu.cpp:559
GLclampd n
Definition: glew.h:5903
Contains the SDL_Rect helper code.
void bg_restore() const
Definition: widget.cpp:251
char const COLUMN_SEPARATOR
basic_sorter & set_level_sort(int level_column, int xp_column)
Definition: menu.cpp:66
std::string del_tags(const std::string &text)
Copy string, but without tags at the beginning.
bool click_selects_
Definition: menu.hpp:272
~menu()
Default implementation, but defined out-of-line for efficiency reasons.
Definition: menu.cpp:216
bool find(E event, F functor)
Tests whether an event handler is available.
cl_event event
Definition: glew.h:3070
map_location prev
Definition: astarsearch.cpp:67
std::vector< item > items_
Definition: menu.hpp:257
basic_sorter & set_xp_sort(int column)
Definition: menu.cpp:60
GLint GLvoid * img
Definition: glew.h:1353
void play_UI_sound(const std::string &files)
Definition: sound.cpp:842
const sorter * sorter_
Definition: menu.hpp:312
bool mouse_locked() const
Definition: widget.cpp:72
int hit_column(int x) const
Definition: menu.cpp:1172
bool double_clicked_
Definition: menu.hpp:279
void process_help_string(int mousex, int mousey)
Definition: menu.cpp:1292
void clear_item(int item)
Definition: menu.cpp:931
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.
bool silent_
Definition: menu.hpp:233
Definition: help.cpp:57
int width() const
Definition: widget.cpp:134
SDL_Rect const & location() const
Definition: widget.cpp:144
int heading_height_
Definition: menu.hpp:261
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
menu(CVideo &video, const std::vector< std::string > &items, bool click_selects=false, int max_height=-1, int max_width=-1, const sorter *sorter_obj=nullptr, style *menu_style=nullptr, const bool auto_join=true)
Definition: menu.cpp:195
GLdouble s
Definition: glew.h:1358
void recalculate_pos()
Definition: menu.cpp:298
const int font_size
void update_rect(const SDL_Rect &)
Definition: dummy_video.cpp:27
virtual void draw_row(menu &menu_ref, const size_t row_index, const SDL_Rect &rect, ROW_TYPE type)
Definition: menu.cpp:879
int max_items_
Definition: menu.hpp:252
GLsizei const GLcharARB ** string
Definition: glew.h:4503
void set_max_height(const int new_max_height)
Set a new max height for this menu.
Definition: menu.cpp:477
std::map< int, SDL_Rect > itemRects_
Definition: menu.hpp:287
size_t get_cell_padding() const
Definition: menu_style.cpp:61
const std::string & id() const
Definition: widget.cpp:232
int selection() const
Definition: menu.cpp:369
const item & get_selected_item() const
Definition: menu.cpp:390
style * style_
Definition: menu.hpp:232
void clear_help_string(int handle)
Definition: video.cpp:656
int height() const
Definition: widget.cpp:139
virtual SDL_Rect item_size(const std::string &item) const
Definition: menu.cpp:804