The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
list.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2016 by Mark de Wever <[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 #ifdef GUI2_EXPERIMENTAL_LISTBOX
16 
17 #define GETTEXT_DOMAIN "wesnoth-lib"
18 
19 #include "gui/widgets/list.hpp"
20 #include "gui/widgets/listbox.hpp"
21 
23 #include "gui/core/log.hpp"
24 #include "gui/widgets/detail/register.hpp"
26 #include "gui/widgets/settings.hpp"
27 #include "gui/widgets/window.hpp"
28 
29 #include "utils/functional.hpp"
30 
31 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
32 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
33 
34 namespace gui2
35 {
36 
37 #ifdef GUI2_EXPERIMENTAL_LISTBOX
38 REGISTER_WIDGET(listbox)
39 #endif
40 
41 tlist::tlist(const bool has_minimum,
42  const bool has_maximum,
43  const tgenerator_::tplacement placement,
44  const bool select,
45  const tbuilder_grid_const_ptr list_builder)
46  : tcontainer_(2) // FIXME magic number
47  , state_(ENABLED)
48  , generator_(nullptr)
49  , list_builder_(list_builder)
50  , need_layout_(false)
51 {
52  assert(list_builder);
53 
54  generator_
55  = tgenerator_::build(has_minimum, has_maximum, placement, select);
56  assert(generator_);
57 
58  connect_signal<event::LEFT_BUTTON_DOWN>(
59  std::bind(&tlist::signal_handler_left_button_down, this, _2),
61 
62  connect_signal<event::SDL_KEY_DOWN>(std::bind(
63  &tlist::signal_handler_sdl_key_down, this, _2, _3, _5, _6));
64 
65  connect_signal<event::SDL_KEY_DOWN>(
66  std::bind(
67  &tlist::signal_handler_sdl_key_down, this, _2, _3, _5, _6),
69 }
70 
71 void tlist::add_row(const string_map& item, const int index)
72 {
73  std::map<std::string, string_map> data;
74 
75  data.insert(std::make_pair("", item));
76  add_row(data, index);
77 }
78 
79 void
80 tlist::add_row(const std::map<std::string /* widget id */, string_map>& data,
81  const int index)
82 {
83  assert(generator_);
84  tgrid& grid = generator_->create_item(index, list_builder_, data, nullptr);
85 
86  tselectable_* selectable
87  = find_widget<tselectable_>(&grid, "_toggle", false, false);
88 
89  if(selectable) {
90  dynamic_cast<twidget&>(*selectable)
91  .connect_signal<event::LEFT_BUTTON_CLICK>(
92  std::bind(
93  &tlist::signal_handler_pre_child_left_button_click,
94  this,
95  &grid,
96  _2,
97  _3,
98  _4),
100 
101  // Post widget for panel.
102  dynamic_cast<twidget&>(*selectable)
103  .connect_signal<event::LEFT_BUTTON_CLICK>(
104  std::bind(&tlist::signal_handler_left_button_click,
105  this,
106  &grid,
107  _2),
109 
110  // Post widget for button and widgets on the panel.
111  dynamic_cast<twidget&>(*selectable)
112  .connect_signal<event::LEFT_BUTTON_CLICK>(
113  std::bind(&tlist::signal_handler_left_button_click,
114  this,
115  &grid,
116  _2),
118  }
119 }
120 
121 void tlist::append_rows(const std::vector<string_map>& items)
122 {
123  for(const string_map & item : items)
124  {
125  add_row(item);
126  }
127 }
128 
129 void tlist::remove_row(const unsigned row, unsigned count)
130 {
131  assert(generator_);
132 
133  if(row >= get_item_count()) {
134  return;
135  }
136 
137  if(!count || count > get_item_count()) {
138  count = get_item_count();
139  }
140 
141  unsigned height_reduced = 0;
142  for(; count; --count) {
143  if(generator_->item(row).get_visible() != tvisible::invisible) {
144  height_reduced += generator_->item(row).get_height();
145  }
146  generator_->delete_item(row);
147  }
148 
149  if(height_reduced != 0) {
150  // resize_content(0, -height_reduced);
151  }
152 }
153 
154 void tlist::clear()
155 {
156  // Due to the removing from the linked group, don't use
157  // generator_->clear() directly.
158  remove_row(0, 0);
159 }
160 
161 unsigned tlist::get_item_count() const
162 {
163  assert(generator_);
164  return generator_->get_item_count();
165 }
166 
167 void tlist::set_row_active(const unsigned row, const bool active)
168 {
169  assert(generator_);
170  generator_->item(row).set_active(active);
171 }
172 
173 void tlist::set_row_shown(const unsigned row, const bool shown)
174 {
175  assert(generator_);
176 
177  twindow* window = get_window();
178  assert(window);
179 
180  const int selected_row = get_selected_row();
181 
182  bool resize_needed = false;
183  {
184  twindow::tinvalidate_layout_blocker invalidate_layout_blocker(*window);
185 
186  generator_->set_item_shown(row, shown);
187  generator_->place(generator_->get_origin(),
188  generator_->calculate_best_size());
189  // resize_needed = !content_resize_request();
190  }
191 
192  if(resize_needed) {
193  window->invalidate_layout();
194  } else {
195  // grid().set_visible_rectangle(content_visible_rectangle());
196  set_is_dirty(true);
197  }
198 
199  if(selected_row != get_selected_row()) {
200  fire(event::NOTIFY_MODIFIED, *this, nullptr);
201  }
202 }
203 
204 void tlist::set_row_shown(const std::vector<bool>& shown)
205 {
206  assert(generator_);
207  assert(shown.size() == get_item_count());
208 
209  twindow* window = get_window();
210  assert(window);
211 
212  const int selected_row = get_selected_row();
213 
214  bool resize_needed = false;
215  {
216  twindow::tinvalidate_layout_blocker invalidate_layout_blocker(*window);
217 
218  for(size_t i = 0; i < shown.size(); ++i) {
219  generator_->set_item_shown(i, shown[i]);
220  }
221  generator_->place(generator_->get_origin(),
222  generator_->calculate_best_size());
223  // resize_needed = !content_resize_request();
224  }
225 
226  if(resize_needed) {
227  window->invalidate_layout();
228  } else {
229  // content_grid_->set_visible_rectangle(content_visible_rectangle());
230  set_is_dirty(true);
231  }
232 
233  if(selected_row != get_selected_row()) {
234  fire(event::NOTIFY_MODIFIED, *this, nullptr);
235  }
236 }
237 
238 const tgrid* tlist::get_row_grid(const unsigned row) const
239 {
240  assert(generator_);
241  // rename this function and can we return a reference??
242  return &generator_->item(row);
243 }
244 
245 tgrid* tlist::get_row_grid(const unsigned row)
246 {
247  assert(generator_);
248  return &generator_->item(row);
249 }
250 
251 bool tlist::select_row(const unsigned row, const bool select)
252 {
253  assert(generator_);
254 
255  generator_->select_item(row, select);
256 
257  return true; // FIXME test what result should have been!!!
258 }
259 
260 int tlist::get_selected_row() const
261 {
262  assert(generator_);
263 
264  return generator_->get_selected_item();
265 }
266 
267 void tlist::place(const tpoint& origin, const tpoint& size)
268 {
269  // Inherited.
270  tcontainer_::place(origin, size);
271 
272  /**
273  * @todo Work-around to set the selected item visible again.
274  *
275  * At the moment the lists and dialogs in general are resized a lot as
276  * work-around for sizing. So this function makes the selected item in view
277  * again. It doesn't work great in all cases but the proper fix is to avoid
278  * resizing dialogs a lot. Need more work later on.
279  */
280  const int selected_item = generator_->get_selected_item();
281  if(selected_item != -1) {
282  /*
283  const SDL_Rect& visible = content_visible_area();
284  SDL_Rect rect = generator_->item(selected_item).get_rectangle();
285 
286  rect.x = visible.x;
287  rect.w = visible.w;
288 
289  show_content_rect(rect);
290  */
291  }
292 }
293 #if 0
294 void tlist::resize_content(
295  const int width_modification
296  , const int height_modification)
297 {
298  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size()
299  << " width_modification " << width_modification
300  << " height_modification " << height_modification
301  << ".\n";
302 
303  if(content_resize_request(width_modification, height_modification)) {
304 
305  // Calculate new size.
306  tpoint size = content_grid()->get_size();
307  size.x += width_modification;
308  size.y += height_modification;
309 
310  // Set new size.
311  content_grid()->set_size(size);
312 
313  // Set status.
314  need_layout_ = true;
315  // If the content grows assume it "overwrites" the old content.
316  if(width_modification < 0 || height_modification < 0) {
317  set_is_dirty(true);
318  }
319  DBG_GUI_L << LOG_HEADER << " succeeded.\n";
320  } else {
321  DBG_GUI_L << LOG_HEADER << " failed.\n";
322  }
323 }
324 #endif
325 
326 void tlist::init()
327 {
328  init_grid(cast<tlistbox_definition::tresolution>(config()).grid);
329 
330  set_single_child(find_widget<tgrid>(&grid(), "_list_grid", false),
331  generator_);
332 
333  /*
334  * These items should be managed by the new listbox class.
335  * So make them invisible for now.
336  */
337  tgrid* g = find_widget<tgrid>(&grid(), "_header_grid", false, false);
338  if(g)
339  g->set_visible(twidget::tvisible::invisible);
340 
341  g = find_widget<tgrid>(&grid(), "_footer_grid", false, false);
342  if(g)
343  g->set_visible(twidget::tvisible::invisible);
344 
345  g = find_widget<tgrid>(&grid(), "_vertical_scrollbar_grid", false, false);
346  if(g)
347  g->set_visible(twidget::tvisible::invisible);
348 
349  g = find_widget<tgrid>(&grid(), "_horizontal_scrollbar_grid", false, false);
350  if(g)
351  g->set_visible(twidget::tvisible::invisible);
352 }
353 
354 bool tlist::get_active() const
355 {
356  return state_ != DISABLED;
357 }
358 
359 unsigned tlist::get_state() const
360 {
361  return state_;
362 }
363 
364 void tlist::layout_children(const bool force)
365 {
366  if(need_layout_ || force) {
367  grid().place(grid().get_origin(), grid().get_size());
368 
369  /*
370  grid().set_visible_rectangle(content_visible_area_);
371  */
372  need_layout_ = false;
373  set_is_dirty(true);
374  }
375 }
376 
377 void tlist::set_self_active(const bool active)
378 {
379  /* DO NOTHING */
380 }
381 
382 const std::string& tlist::get_control_type() const
383 {
384  static const std::string type = "list";
385  return type;
386 }
387 
388 void tlist::signal_handler_left_button_down(const event::tevent event)
389 {
390  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
391 
392  assert(get_window());
393  get_window()->keyboard_capture(this);
394 }
395 
396 void tlist::signal_handler_pre_child_left_button_click(
397  tgrid* grid, const event::tevent event, bool& handled, bool& halt)
398 {
399  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
400 
401  assert(grid);
402  assert(generator_);
403 
404  for(size_t i = 0; i < generator_->get_item_count(); ++i) {
405  if(&generator_->item(i) == grid) {
406 
407  /**
408  * @todo Here we should check whether the panel can be toggled.
409  *
410  * NO set halt + handled
411  * YES do nothing
412  *
413  * Then a post to the widget, which if done sets the proper state
414  * in the list.
415  *
416  * For now we simply assume an item can only be selected and not
417  * deselected (which is true at the moment).
418  */
419  if(generator_->is_selected(i)) {
420  halt = true;
421  handled = true;
422  }
423  return;
424  }
425  }
426  assert(false);
427 }
428 
429 void tlist::signal_handler_left_button_click(tgrid* grid,
430  const event::tevent event)
431 {
432  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
433  assert(grid);
434  assert(generator_);
435 
436  /** @todo Test the proper state to set. */
437  for(size_t i = 0; i < generator_->get_item_count(); ++i) {
438  if(&generator_->item(i) == grid) {
439  generator_->select_item(i);
440  fire(event::NOTIFY_MODIFIED, *this, nullptr);
441  }
442  }
443 }
444 
445 void tlist::signal_handler_sdl_key_down(const event::tevent event,
446  bool& handled,
447  const SDLKey key,
448  SDLMod modifier)
449 {
450  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
451 
452  if(handled) {
453  return;
454  }
455 
456  switch(key) {
457  case SDLK_UP:
458  generator_->handle_key_up_arrow(modifier, handled);
459  break;
460  case SDLK_DOWN:
461  generator_->handle_key_down_arrow(modifier, handled);
462  break;
463  case SDLK_LEFT:
464  generator_->handle_key_left_arrow(modifier, handled);
465  break;
466  case SDLK_RIGHT:
467  generator_->handle_key_right_arrow(modifier, handled);
468  break;
469  default:
470  ;
471  /* Do nothing. */
472  }
473  if(handled) {
474  fire(event::NOTIFY_MODIFIED, *this, nullptr);
475  }
476 }
477 
478 } // namespace gui2
479 
480 #endif
Define the common log macros for the gui toolkit.
void set_single_child(tgrid &grid, twidget *widget)
Sets the single child in a grid.
Definition: grid.cpp:1021
#define SDLMod
Definition: compat.hpp:30
#define DBG_GUI_L
Definition: log.hpp:58
boost::intrusive_ptr< const tbuilder_grid > tbuilder_grid_const_ptr
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
This file contains the window object, this object is a top level container which has the event manage...
void clear(const std::string &key)
tplacement
Determines how the items are placed.
Definition: generator.hpp:51
GLboolean GLboolean g
Definition: glew.h:7319
const std::vector< std::string > items
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
#define SDLKey
Definition: compat.hpp:29
A left mouse button click event for a widget.
Definition: handler.hpp:84
A class inherited from ttext_box that displays its input as stars.
Definition: field-fwd.hpp:23
The user set the widget invisible, that means:
Definition: widget.hpp:103
This file contains the settings handling of the widget library.
friend class tinvalidate_layout_blocker
Definition: window.hpp:67
Send by a widget to notify others its contents or state are modified.
Definition: handler.hpp:133
virtual void place(const tpoint &origin, const tpoint &size) override
See twidget::place.
Definition: container.cpp:76
tevent
The event send to the dispatcher.
Definition: handler.hpp:54
void init()
Initializes the gui subsystems.
Definition: registry.cpp:482
GLuint GLuint GLsizei count
Definition: glew.h:1221
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
std::map< std::string, t_string > string_map
Definition: generator.hpp:23
#define DBG_GUI_E
Definition: log.hpp:35
GLuint index
Definition: glew.h:1782
size_t i
Definition: function.cpp:1057
static tgenerator_ * build(const bool has_minimum, const bool has_maximum, const tplacement placement, const bool select)
Create a new generator.
Definition: generator.cpp:802
GLsizeiptr size
Definition: glew.h:1649
GLenum GLenum GLvoid * row
Definition: glew.h:3805
cl_event event
Definition: glew.h:3070
#define LOG_HEADER
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
GLsizei const GLcharARB ** string
Definition: glew.h:4503