Bug Summary

File:ui_basic/table.cc
Location:line 162, column 3
Description:Called C++ object pointer is null

Annotated Source Code

1/*
2 * Copyright (C) 2002, 2006-2011 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#include "table.h"
21
22#include "graphic/font.h"
23#include "graphic/font_handler.h"
24#include "graphic/rendertarget.h"
25#include "graphic/surface.h"
26
27#include "button.h"
28#include "mouse_constants.h"
29#include "scrollbar.h"
30#include "wlapplication.h"
31
32#include "container_iterate.h"
33#include <boost/bind.hpp>
34
35namespace UI {
36
37/**
38 * Args: parent parent panel
39 * x coordinates of the Table
40 * y
41 * w dimensions, in pixels, of the Table
42 * h
43*/
44Table<void *>::Table
45 (Panel * const parent,
46 int32_t x, int32_t y, uint32_t w, uint32_t h,
47 const bool descending)
48:
49 Panel (parent, x, y, w, h),
50 m_total_width (0),
51 m_max_pic_width (0),
52 m_fontname (UI_FONT_NAME"DejaVuSerif.ttf"),
53 m_fontsize (UI_FONT_SIZE_SMALL14),
54 m_headerheight (15),
55 m_lineheight (g_fh->get_fontheight(m_fontname, m_fontsize)),
56 m_scrollbar (0),
57 m_scrollpos (0),
58 m_selection (no_selection_index()),
59 m_last_click_time (-10000),
60 m_last_selection (no_selection_index()),
61 m_sort_column (0),
62 m_sort_descending (descending)
63{
64 set_think(false);
65}
66
67
68/**
69 * Free allocated resources
70*/
71Table<void *>::~Table()
72{
73 container_iterate_const(Entry_Record_vector, m_entry_records, i)for (wl_const_range< Entry_Record_vector > i(m_entry_records
); i; ++i)
74 delete *i.current;
75}
76
77/// Add a new column to this table.
78void Table<void *>::add_column
79 (uint32_t const width,
80 std::string const & title,
81 Align const alignment,
82 bool const is_checkbox_column)
83{
84
85 // If there would be existing entries, they would not get the new column.
86 assert(size() == 0)((size() == 0) ? static_cast<void> (0) : __assert_fail (
"size() == 0", "/home/arch/widelands/src/ui_basic/table.cc", 86
, __PRETTY_FUNCTION__))
;
87
88 uint32_t complete_width = 0;
89 container_iterate_const(Columns, m_columns, i)for (wl_const_range< Columns > i(m_columns); i; ++i)
90 complete_width += i.current->width;
91
92 m_total_width += width;
93 set_desired_size(m_total_width, get_h());
94
95 {
96 Column c;
97 c.btn = 0;
98 if (title.size()) {
99 c.btn =
100 new Button
101 (this, title,
102 complete_width, 0, width, m_headerheight,
103 g_gr->imgcache().load(PicMod_UI, "pics/but3.png"),
104 title, "", true, false);
105 c.btn->sigclicked.connect
106 (boost::bind(&Table::header_button_clicked, boost::ref(*this), m_columns.size()));
107 c.btn->set_font(Font::get(m_fontname, m_fontsize));
108 }
109 c.width = width;
110 c.alignment = alignment;
111 c.is_checkbox_column = is_checkbox_column;
112
113 if (is_checkbox_column) {
114 c.compare = boost::bind
115 (&Table<void *>::default_compare_checkbox,
116 this, m_columns.size(), _1, _2);
117 } else {
118 c.compare = boost::bind
119 (&Table<void *>::default_compare_string,
120 this, m_columns.size(), _1, _2);
121 }
122
123 m_columns.push_back(c);
124 }
125 if (not m_scrollbar) {
126 m_scrollbar =
127 new Scrollbar
128 (get_parent(),
129 get_x() + get_w() - 24, get_y() + m_headerheight,
130 24, get_h() - m_headerheight,
131 false);
132 m_scrollbar->moved.connect(boost::bind(&Table::set_scrollpos, this, _1));
133 m_scrollbar->set_steps(1);
134 uint32_t const lineheight = g_fh->get_fontheight(m_fontname, m_fontsize);
135 m_scrollbar->set_singlestepsize(lineheight);
136 m_scrollbar->set_pagesize(get_h() - lineheight);
137 }
138}
139
140void Table<void *>::set_column_title
141 (uint8_t const col, std::string const & title)
142{
143 assert(col < m_columns.size())((col < m_columns.size()) ? static_cast<void> (0) : __assert_fail
("col < m_columns.size()", "/home/arch/widelands/src/ui_basic/table.cc"
, 143, __PRETTY_FUNCTION__))
;
144 Column & column = m_columns.at(col);
145 if (not column.btn and title.size()) { // no title before, but now
1
Taking false branch
146 uint32_t complete_width = 0;
147 for (uint8_t i = 0; i < col; ++i)
148 complete_width += m_columns.at(i).width;
149 column.btn =
150 new Button
151 (this, title,
152 complete_width, 0, column.width, m_headerheight,
153 g_gr->imgcache().load(PicMod_UI, "pics/but3.png"),
154 title, "", true, false);
155 column.btn->sigclicked.connect
156 (boost::bind(&Table::header_button_clicked, boost::ref(*this), col));
157 column.btn->set_font(Font::get(m_fontname, m_fontsize));
158 } else if (column.btn and title.empty()) { // had title before, not now
159 delete column.btn;
160 column.btn = 0;
161 } else
162 column.btn->set_title(title);
2
Called C++ object pointer is null
163}
164
165/**
166 * Set a custom comparison function for sorting of the given column.
167 */
168void Table<void *>::set_column_compare
169 (uint8_t col, const Table<void *>::CompareFn & fn)
170{
171 assert(col < m_columns.size())((col < m_columns.size()) ? static_cast<void> (0) : __assert_fail
("col < m_columns.size()", "/home/arch/widelands/src/ui_basic/table.cc"
, 171, __PRETTY_FUNCTION__))
;
172 Column & column = m_columns.at(col);
173 column.compare = fn;
174}
175
176void Table<void *>::Entry_Record::set_checked
177 (uint8_t const col, bool const checked)
178{
179 _data & cell = m_data.at(col);
180
181 cell.d_checked = checked;
182 cell.d_picture =
183 g_gr->imgcache().load
184 (PicMod_UI,
185 checked ? "pics/checkbox_checked.png" : "pics/checkbox_empty.png");
186}
187
188void Table<void *>::Entry_Record::toggle(uint8_t const col)
189{
190 set_checked(col, !is_checked(col));
191}
192
193
194bool Table<void *>::Entry_Record::is_checked(uint8_t const col) const {
195 _data const & cell = m_data.at(col);
196
197 return cell.d_checked;
198}
199
200Table<void *>::Entry_Record * Table<void *>::find
201 (const void * const entry) const
202 throw ()
203{
204 container_iterate_const(Entry_Record_vector, m_entry_records, i)for (wl_const_range< Entry_Record_vector > i(m_entry_records
); i; ++i)
205 if ((*i.current)->entry() == entry)
206 return *i.current;
207
208 return 0;
209}
210
211/**
212 * A header button has been clicked
213 */
214void Table<void *>::header_button_clicked(Columns::size_type const n) {
215 assert(m_columns.at(n).btn)((m_columns.at(n).btn) ? static_cast<void> (0) : __assert_fail
("m_columns.at(n).btn", "/home/arch/widelands/src/ui_basic/table.cc"
, 215, __PRETTY_FUNCTION__))
;
216 if (get_sort_colum() == n) {
217 set_sort_descending(not get_sort_descending()); // change sort direction
218 sort();
219 return;
220 }
221
222 set_sort_column(n);
223 sort();
224 return;
225}
226
227/**
228 * Remove all entries from the table
229*/
230void Table<void *>::clear()
231{
232 container_iterate_const(Entry_Record_vector, m_entry_records, i)for (wl_const_range< Entry_Record_vector > i(m_entry_records
); i; ++i)
233 delete *i.current;
234 m_entry_records.clear();
235
236 if (m_scrollbar)
237 m_scrollbar->set_steps(1);
238 m_scrollpos = 0;
239 m_selection = no_selection_index();
240 m_last_click_time = -10000;
241 m_last_selection = no_selection_index();
242}
243
244/**
245 * Redraw the table
246*/
247void Table<void *>::draw(RenderTarget & dst)
248{
249 // draw text lines
250 int32_t lineheight = get_lineheight();
251 uint32_t idx = m_scrollpos / lineheight;
252 int32_t y = 1 + idx * lineheight - m_scrollpos + m_headerheight;
253
254 dst.brighten_rect(Rect(Point(0, 0), get_w(), get_h()), ms_darken_value);
255
256 while (idx < m_entry_records.size()) {
257 if (y >= static_cast<int32_t>(get_h()))
258 return;
259
260 const Entry_Record & er = *m_entry_records[idx];
261
262 if (idx == m_selection) {
263 assert(2 <= get_eff_w())((2 <= get_eff_w()) ? static_cast<void> (0) : __assert_fail
("2 <= get_eff_w()", "/home/arch/widelands/src/ui_basic/table.cc"
, 263, __PRETTY_FUNCTION__))
;
264 dst.brighten_rect
265 (Rect(Point(1, y), get_eff_w() - 2, m_lineheight),
266 -ms_darken_value);
267 }
268
269 const RGBColor col = er.use_clr ? er.clr : UI_FONT_CLR_FGRGBColor(255, 255, 0);
270
271 Columns::size_type const nr_columns = m_columns.size();
272 for (uint32_t i = 0, curx = 0; i < nr_columns; ++i) {
273 Column const & column = m_columns[i];
274 uint32_t const curw = column.width;
275 Align const alignment = column.alignment;
276
277 const IPicture* entry_picture = er.get_picture(i);
278 std::string const & entry_string = er.get_string (i);
279 uint32_t picw = 0;
280 uint32_t pich = 0;
281 uint32_t stringw = 0;
282 uint32_t stringh = g_fh->get_fontheight(m_fontname, m_fontsize);
283 if (entry_picture) {
284 picw = entry_picture->get_w();
285 pich = entry_picture->get_h();
286 }
287 Point point =
288 Point(curx, y)
289 +
290 Point
291 (alignment & Align_Right ? curw - (picw + stringw) - 1 :
292 alignment & Align_HCenter ? (curw - (picw + stringw)) / 2 :
293 1,
294 0);
295 if (entry_picture)
296 dst.blit
297 (point +
298 Point
299 (0,
300 (static_cast<int32_t>(lineheight) -
301 static_cast<int32_t>(pich))
302 / 2),
303 entry_picture);
304
305 UI::g_fh->draw_text
306 (dst,
307 TextStyle::makebold(Font::get(m_fontname, m_fontsize), col),
308 point +
309 Point
310 (picw,
311 (static_cast<int32_t>(lineheight) -
312 static_cast<int32_t>(stringh))
313 / 2),
314 entry_string,
315 alignment);
316
317 curx += curw;
318 }
319
320 y += lineheight;
321 ++idx;
322 }
323}
324
325/**
326 * handle key presses
327 */
328bool Table<void *>::handle_key(bool down, SDL_keysym code)
329{
330 if (down) {
331 switch (code.sym) {
332 case SDLK_UP:
333 case SDLK_KP8:
334 move_selection(-1);
335 return true;
336
337 case SDLK_DOWN:
338 case SDLK_KP2:
339 move_selection(1);
340 return true;
341
342 default:
343 break; // not handled
344 }
345 }
346
347 return UI::Panel::handle_key(down, code);
348}
349
350/**
351 * Handle mouse presses: select the appropriate entry
352 */
353bool Table<void *>::handle_mousepress
354 (Uint8 const btn, int32_t x, int32_t const y)
355{
356 if (get_can_focus())
357 focus();
358
359 switch (btn) {
360 case SDL_BUTTON_WHEELDOWN5:
361 case SDL_BUTTON_WHEELUP4:
362 return m_scrollbar ? m_scrollbar->handle_mousepress(btn, 0, y) : false;
363 case SDL_BUTTON_LEFT1: {
364 int32_t const time = WLApplication::get()->get_time();
365
366 // This hick hack is needed if any of the callback functions calls clear
367 // to forget the last clicked time.
368 int32_t const real_last_click_time = m_last_click_time;
369
370 m_last_selection = m_selection;
371 m_last_click_time = time;
372
373 uint32_t const row =
374 (y + m_scrollpos - m_headerheight) / get_lineheight();
375 if (row < m_entry_records.size()) {
376 select(row);
377 Columns::size_type const nr_cols = m_columns.size();
378 for (uint8_t col = 0; col < nr_cols; ++col) {
379 Column const & column = m_columns.at(col);
380 x -= column.width;
381 if (x <= 0) {
382 if (column.is_checkbox_column) {
383 play_click();
384 m_entry_records.at(row)->toggle(col);
385 update(0, 0, get_eff_w(), get_h());
386 }
387 break;
388 }
389 }
390 }
391
392 if // check if doubleclicked
393 (time - real_last_click_time < DOUBLE_CLICK_INTERVAL500
394 and
395 m_last_selection == m_selection
396 and m_selection != no_selection_index())
397 double_clicked(m_selection);
398
399 return true;
400 }
401 default:
402 return false;
403 }
404}
405bool Table<void *>::handle_mouserelease(const Uint8 btn, int32_t, int32_t)
406{
407 return btn == SDL_BUTTON_LEFT1;
408}
409
410/**
411 * move the currently selected entry up or down.
412 * \param offset positive value move the selection down and
413 * negative values up.
414 */
415void Table<void *>::move_selection(const int32_t offset)
416{
417 if (!has_selection()) return;
418 int32_t new_selection = m_selection + offset;
419
420 if (new_selection < 0) new_selection = 0;
421 else if (static_cast<uint32_t>(new_selection) > m_entry_records.size() - 1)
422 new_selection = m_entry_records.size() - 1;
423
424 select(static_cast<uint32_t>(new_selection));
425
426 //scroll to newly selected entry
427 if (m_scrollbar)
428 {
429 int32_t scroll_offset = 0;
430 if (new_selection > 0) scroll_offset = -1;
431
432 m_scrollbar->set_scrollpos
433 ((new_selection + scroll_offset) * get_lineheight());
434 }
435}
436
437/**
438 * Change the currently selected entry
439 *
440 * Args: i the entry to select
441 */
442void Table<void *>::select(const uint32_t i)
443{
444 if (m_selection == i)
445 return;
446
447 m_selection = i;
448
449 selected(m_selection);
450 update(0, 0, get_eff_w(), get_h());
451}
452
453/**
454 * Add a new entry to the table.
455*/
456Table<void *>::Entry_Record & Table<void *>::add
457 (void * const entry, const bool do_select)
458{
459 int32_t entry_height = g_fh->get_fontheight(m_fontname, m_fontsize);
460 if (entry_height > m_lineheight)
461 m_lineheight = entry_height;
462
463 Entry_Record & result = *new Entry_Record(entry);
464 m_entry_records.push_back(&result);
465 result.m_data.resize(m_columns.size());
466 for
467 (wl_index_range<Columns::size_type> i(0, m_columns.size());
468 i; ++i)
469 if (m_columns.at(i.current).is_checkbox_column) {
470 result.m_data.at(i.current).d_picture =
471 g_gr->imgcache().load(PicMod_UI, "pics/checkbox_empty.png");
472 }
473
474 m_scrollbar->set_steps
475 (m_entry_records.size() * get_lineheight()
476 -
477 (get_h() - m_headerheight - 2));
478
479 if (do_select) {
480 select(m_entry_records.size() - 1);
481 m_scrollbar->set_scrollpos(std::numeric_limits<int32_t>::max());
482 }
483
484 update(0, 0, get_eff_w(), get_h());
485 return result;
486}
487
488/**
489 * Scroll to the given position, in pixels.
490*/
491void Table<void *>::set_scrollpos(int32_t const i)
492{
493 m_scrollpos = i;
494
495 update(0, 0, get_eff_w(), get_h());
496}
497
498/**
499 * Remove the table entry at the given (zero-based) index.
500 */
501void Table<void *>::remove(const uint32_t i) {
502 assert(i < m_entry_records.size())((i < m_entry_records.size()) ? static_cast<void> (0
) : __assert_fail ("i < m_entry_records.size()", "/home/arch/widelands/src/ui_basic/table.cc"
, 502, __PRETTY_FUNCTION__))
;
503
504 const Entry_Record_vector::iterator it = m_entry_records.begin() + i;
505 delete *it;
506 m_entry_records.erase(it);
507 if (m_selection == i)
508 m_selection = no_selection_index();
509 else if (m_selection > i && m_selection != no_selection_index())
510 m_selection--;
511
512 m_scrollbar->set_steps
513 (m_entry_records.size() * get_lineheight()
514 -
515 (get_h() - m_headerheight - 2));
516}
517
518bool Table<void *>::sort_helper(uint32_t a, uint32_t b)
519{
520 if (m_sort_descending)
521 return m_columns[m_sort_column].compare(b, a);
522 else
523 return m_columns[m_sort_column].compare(a, b);
524}
525
526/**
527 * Sort the table alphabetically. Make sure that the current selection stays
528 * valid (though it might scroll out of visibility).
529 * Only the subarea [start,end) is sorted.
530 * For example you might want to sort directories for themselves at the
531 * top of list and files at the bottom.
532 */
533void Table<void *>::sort(const uint32_t Begin, uint32_t End)
534{
535 assert(m_columns.at(m_sort_column).btn)((m_columns.at(m_sort_column).btn) ? static_cast<void> (
0) : __assert_fail ("m_columns.at(m_sort_column).btn", "/home/arch/widelands/src/ui_basic/table.cc"
, 535, __PRETTY_FUNCTION__))
;
536 assert(m_sort_column < m_columns.size())((m_sort_column < m_columns.size()) ? static_cast<void>
(0) : __assert_fail ("m_sort_column < m_columns.size()", "/home/arch/widelands/src/ui_basic/table.cc"
, 536, __PRETTY_FUNCTION__))
;
537
538 if (End > size())
539 End = size();
540
541 std::vector<uint32_t> indices;
542 std::vector<Entry_Record *> copy;
543
544 indices.reserve(End - Begin);
545 copy.reserve(End - Begin);
546 for (uint32_t i = Begin; i < End; ++i) {
547 indices.push_back(i);
548 copy.push_back(m_entry_records[i]);
549 }
550
551 std::stable_sort
552 (indices.begin(), indices.end(),
553 boost::bind(&Table<void *>::sort_helper, this, _1, _2));
554
555 uint32_t newselection = m_selection;
556 for (uint32_t i = Begin; i < End; ++i) {
557 uint32_t from = indices[i - Begin];
558 m_entry_records[i] = copy[from - Begin];
559 if (m_selection == from)
560 newselection = i;
561 }
562 m_selection = newselection;
563
564 update();
565}
566
567/**
568 * Default comparison for checkbox columns:
569 * checked items come before unchecked ones.
570 */
571bool Table<void *>::default_compare_checkbox
572 (uint32_t column, uint32_t a, uint32_t b)
573{
574 Entry_Record & ea = get_record(a);
575 Entry_Record & eb = get_record(b);
576 return ea.is_checked(column) && !eb.is_checked(column);
577}
578
579bool Table<void *>::default_compare_string
580 (uint32_t column, uint32_t a, uint32_t b)
581{
582 Entry_Record & ea = get_record(a);
583 Entry_Record & eb = get_record(b);
584 return ea.get_string(column) < eb.get_string(column);
585}
586
587Table<void *>::Entry_Record::Entry_Record(void * const e)
588 : m_entry(e), use_clr(false)
589{}
590
591void Table<void *>::Entry_Record::set_picture
592 (uint8_t const col, const IPicture* pic, std::string const & str)
593{
594 assert(col < m_data.size())((col < m_data.size()) ? static_cast<void> (0) : __assert_fail
("col < m_data.size()", "/home/arch/widelands/src/ui_basic/table.cc"
, 594, __PRETTY_FUNCTION__))
;
595
596 m_data.at(col).d_picture = pic;
597 m_data.at(col).d_string = str;
598}
599void Table<void *>::Entry_Record::set_string
600 (uint8_t const col, std::string const & str)
601{
602 assert(col < m_data.size())((col < m_data.size()) ? static_cast<void> (0) : __assert_fail
("col < m_data.size()", "/home/arch/widelands/src/ui_basic/table.cc"
, 602, __PRETTY_FUNCTION__))
;
603
604 m_data.at(col).d_picture = NULL__null;
605 m_data.at(col).d_string = str;
606}
607const IPicture* Table<void *>::Entry_Record::get_picture(uint8_t const col) const
608{
609 assert(col < m_data.size())((col < m_data.size()) ? static_cast<void> (0) : __assert_fail
("col < m_data.size()", "/home/arch/widelands/src/ui_basic/table.cc"
, 609, __PRETTY_FUNCTION__))
;
610
611 return m_data.at(col).d_picture;
612}
613const std::string & Table<void *>::Entry_Record::get_string
614 (uint8_t const col) const
615{
616 assert(col < m_data.size())((col < m_data.size()) ? static_cast<void> (0) : __assert_fail
("col < m_data.size()", "/home/arch/widelands/src/ui_basic/table.cc"
, 616, __PRETTY_FUNCTION__))
;
617
618 return m_data.at(col).d_string;
619}
620
621}