Mana
Loading...
Searching...
No Matches
table.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2008-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "gui/widgets/table.h"
23
24#include "gui/gui.h"
25#include "gui/sdlinput.h"
26
27#include "resources/theme.h"
28
29#include "utils/dtor.h"
30
31#include <guichan/actionlistener.hpp>
32#include <guichan/graphics.hpp>
33#include <guichan/key.hpp>
34
35class GuiTableActionListener : public gcn::ActionListener
36{
37public:
38 GuiTableActionListener(GuiTable *table, gcn::Widget *widget, int row, int column);
39 ~GuiTableActionListener() override;
40
41 void action(const gcn::ActionEvent& actionEvent) override;
42
43protected:
45 int mRow;
47 gcn::Widget *mWidget;
48};
49
50
51GuiTableActionListener::GuiTableActionListener(GuiTable *table, gcn::Widget *widget, int row, int column) :
52 mTable(table),
53 mRow(row),
54 mColumn(column),
55 mWidget(widget)
56{
57 if (widget)
58 {
59 widget->addActionListener(this);
60 widget->_setParent(table);
61 }
62}
63
65{
66 if (mWidget)
67 {
68 mWidget->removeActionListener(this);
69 mWidget->_setParent(nullptr);
70 }
71}
72
73void GuiTableActionListener::action(const gcn::ActionEvent& actionEvent)
74{
76 mTable->distributeActionEvent();
77}
78
79
80GuiTable::GuiTable(TableModel *initialModel, gcn::Color background,
81 bool opacity) :
82 mOpaque(opacity),
83 mBackgroundColor(background)
84{
85 setModel(initialModel);
86 setFocusable(true);
87
88 addMouseListener(this);
89 addKeyListener(this);
90}
91
97
99{
100 return mModel;
101}
102
104{
105 if (mModel)
106 {
108 mModel->removeListener(this);
109 }
110
111 mModel = new_model;
113
114 if (new_model)
115 {
116 new_model->installListener(this);
118 }
119}
120
122{
123 int rows_nr = mModel->getRows();
124 int columns_nr = mModel->getColumns();
125 int width = 0;
126 int height = 0;
127
128 if (mSelectedRow >= rows_nr)
129 mSelectedRow = rows_nr - 1;
130
131 if (mSelectedColumn >= columns_nr)
132 mSelectedColumn = columns_nr - 1;
133
134 for (int i = 0; i < columns_nr; i++)
135 width += getColumnWidth(i);
136
137 height = getRowHeight() * rows_nr;
138
139 setWidth(width);
140 setHeight(height);
141}
142
143void GuiTable::setSelected(int row, int column)
144{
145 mSelectedColumn = column;
146 mSelectedRow = row;
147}
148
150{
151 return mSelectedRow;
152}
153
155{
156 return mSelectedColumn;
157}
158
160{
161 mLinewiseMode = linewise;
162}
163
165{
166 if (!mModel)
167 return 0;
168 return mModel->getRowHeight() + 1; // border
169}
170
172{
173 if (!mModel)
174 return 0;
175 return mModel->getColumnWidth(i) + 1; // border
176}
177
178void GuiTable::setSelectedRow(int selected)
179{
180 if (!mModel)
181 {
182 mSelectedRow = -1;
183 }
184 else
185 {
186 if (selected < 0 && !mWrappingEnabled)
187 {
188 mSelectedRow = -1;
189 }
190 else if (selected >= mModel->getRows() && mWrappingEnabled)
191 {
192 mSelectedRow = 0;
193 }
194 else if ((selected >= mModel->getRows() && !mWrappingEnabled) ||
195 (selected < 0 && mWrappingEnabled))
196 {
197 mSelectedRow = mModel->getRows() - 1;
198 }
199 else
200 {
201 mSelectedRow = selected;
202 }
203 }
204}
205
207{
208 if (!mModel)
209 {
210 mSelectedColumn = -1;
211 }
212 else
213 {
214 if ((selected >= mModel->getColumns() && mWrappingEnabled) ||
215 (selected < 0 && !mWrappingEnabled))
216 {
217 mSelectedColumn = 0;
218 }
219 else if ((selected >= mModel->getColumns() && !mWrappingEnabled) ||
220 (selected < 0 && mWrappingEnabled))
221 {
223 }
224 else
225 {
226 mSelectedColumn = selected;
227 }
228 }
229}
230
236
238{
239 if (!mModel)
240 return;
241
242 int rows = mModel->getRows();
243 int columns = mModel->getColumns();
244
245 for (int row = 0; row < rows; ++row)
246 for (int column = 0; column < columns; ++column)
247 {
248 gcn::Widget *widget = mModel->getElementAt(row, column);
249 mActionListeners.push_back(new GuiTableActionListener(this, widget,
250 row, column));
251 }
252
253 _setFocusHandler(_getFocusHandler()); // propagate focus handler to widgets
254}
255
256// -- widget ops
257void GuiTable::draw(gcn::Graphics* graphics)
258{
259 if (!mModel)
260 return;
261
262 const auto guiAlpha = gui->getTheme()->getGuiAlpha();
263
264 if (mOpaque)
265 {
266 auto backgroundColor = Theme::getThemeColor(Theme::BACKGROUND);
267 backgroundColor.a = guiAlpha;
268 graphics->setColor(backgroundColor);
269 graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
270 }
271
272 auto highlightColor = Theme::getThemeColor(Theme::HIGHLIGHT);
273 highlightColor.a = guiAlpha;
274
275 // First, determine how many rows we need to draw, and where we should start.
276 int first_row = -(getY() / getRowHeight());
277
278 if (first_row < 0)
279 first_row = 0;
280
281 int rows_nr = 1 + (getHeight() / getRowHeight()); // May overestimate by one.
282
283 int max_rows_nr = mModel->getRows() - first_row; // clip if neccessary:
284 if (max_rows_nr < rows_nr)
285 rows_nr = max_rows_nr;
286
287 // Now determine the first and last column
288 // Take the easy way out; these are usually bounded and all visible.
289 int first_column = 0;
290 int last_column = mModel->getColumns() - 1;
291
292 // Set up everything for drawing
293 int height = getRowHeight();
294 int y_offset = first_row * height;
295
296 for (int r = first_row; r < first_row + rows_nr; ++r)
297 {
298 int x_offset = 0;
299
300 for (int c = first_column; c <= last_column; ++c)
301 {
302 gcn::Widget *widget = mModel->getElementAt(r, c);
303 int width = getColumnWidth(c);
304 if (widget)
305 {
306 gcn::Rectangle bounds(x_offset, y_offset, width, height);
307
308 if (widget == mTopWidget)
309 {
310 bounds.height = widget->getHeight();
311 bounds.width = widget->getWidth();
312 }
313
314 widget->setDimension(bounds);
315
316 graphics->setColor(highlightColor);
317
318 if (mLinewiseMode && r == mSelectedRow && c == 0)
319 {
320 graphics->fillRectangle(gcn::Rectangle(0, y_offset,
321 getWidth(), height));
322 }
323 else if (!mLinewiseMode &&
324 c == mSelectedColumn && r == mSelectedRow)
325 {
326 graphics->fillRectangle(gcn::Rectangle(x_offset, y_offset,
327 width, height));
328 }
329
330 graphics->pushClipArea(bounds);
331 widget->draw(graphics);
332 graphics->popClipArea();
333 }
334
335 x_offset += width;
336 }
337
338 y_offset += height;
339 }
340
341 if (mTopWidget)
342 {
343 gcn::Rectangle bounds = mTopWidget->getDimension();
344 graphics->pushClipArea(bounds);
345 mTopWidget->draw(graphics);
346 graphics->popClipArea();
347 }
348}
349
350void GuiTable::moveToTop(gcn::Widget *widget)
351{
352 gcn::Widget::moveToTop(widget);
353 mTopWidget = widget;
354}
355
356void GuiTable::moveToBottom(gcn::Widget *widget)
357{
358 gcn::Widget::moveToBottom(widget);
359 if (widget == mTopWidget)
360 mTopWidget = nullptr;
361}
362
364{
365 return gcn::Rectangle(0, 0, getWidth(), getHeight());
366}
367
368// -- KeyListener notifications
369void GuiTable::keyPressed(gcn::KeyEvent& keyEvent)
370{
371 gcn::Key key = keyEvent.getKey();
372
373 if (key.getValue() == Key::ENTER || key.getValue() == Key::SPACE)
374 {
375 distributeActionEvent();
376 keyEvent.consume();
377 }
378 else if (key.getValue() == Key::UP)
379 {
381 keyEvent.consume();
382 }
383 else if (key.getValue() == Key::DOWN)
384 {
386 keyEvent.consume();
387 }
388 else if (key.getValue() == Key::LEFT)
389 {
391 keyEvent.consume();
392 }
393 else if (key.getValue() == Key::RIGHT)
394 {
396 keyEvent.consume();
397 }
398 else if (key.getValue() == Key::HOME)
399 {
402 keyEvent.consume();
403 }
404 else if (key.getValue() == Key::END)
405 {
408 keyEvent.consume();
409 }
410}
411
412// -- MouseListener notifications
413void GuiTable::mousePressed(gcn::MouseEvent& mouseEvent)
414{
415 if (mouseEvent.getButton() == gcn::MouseEvent::LEFT)
416 {
417 int row = getRowForY(mouseEvent.getY());
418 int column = getColumnForX(mouseEvent.getX());
419
420 if (row > -1 && column > -1 &&
421 row < mModel->getRows() && column < mModel->getColumns())
422 {
423 mSelectedColumn = column;
424 mSelectedRow = row;
425 }
426
427 distributeActionEvent();
428 }
429}
430
431void GuiTable::mouseWheelMovedUp(gcn::MouseEvent& mouseEvent)
432{
433 if (isFocused())
434 {
435 if (getSelectedRow() > 0 || (getSelectedRow() == 0 && mWrappingEnabled))
436 {
438 }
439
440 mouseEvent.consume();
441 }
442}
443
444void GuiTable::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent)
445{
446 if (isFocused())
447 {
449
450 mouseEvent.consume();
451 }
452}
453
454void GuiTable::mouseDragged(gcn::MouseEvent& mouseEvent)
455{
456 if (mouseEvent.getButton() != gcn::MouseEvent::LEFT)
457 return;
458
459 // Make table selection update on drag
460 const int x = std::max(0, mouseEvent.getX());
461 const int y = std::max(0, mouseEvent.getY());
462
465}
466
467// -- TableModelListener notifications
468void GuiTable::modelUpdated(bool completed)
469{
470 if (completed)
471 {
474 }
475 else
476 { // before the update?
477 mTopWidget = nullptr; // No longer valid in general
479 }
480}
481
482gcn::Widget *GuiTable::getWidgetAt(int x, int y)
483{
484 int row = getRowForY(y);
485 int column = getColumnForX(x);
486
487 if (mTopWidget && mTopWidget->getDimension().isPointInRect(x, y))
488 return mTopWidget;
489
490 if (row > -1 && column > -1)
491 {
492 if (gcn::Widget *w = mModel->getElementAt(row, column))
493 if (w->isFocusable())
494 return w;
495 }
496
497 return nullptr; // Grab the event locally
498}
499
500int GuiTable::getRowForY(int y) const
501{
502 int row = -1;
503
504 if (getRowHeight() > 0)
505 row = y / getRowHeight();
506
507 if (row < 0 || row >= mModel->getRows())
508 return -1;
509
510 return row;
511}
512
514{
515 int column;
516 int delta = 0;
517
518 for (column = 0; column < mModel->getColumns(); column++)
519 {
520 delta += getColumnWidth(column);
521 if (x <= delta)
522 break;
523 }
524
525 if (column < 0 || column >= mModel->getColumns())
526 return -1;
527
528 return column;
529}
530
531void GuiTable::_setFocusHandler(gcn::FocusHandler* focusHandler)
532{
533 gcn::Widget::_setFocusHandler(focusHandler);
534
535 if (!mModel)
536 return;
537
538 for (int r = 0; r < mModel->getRows(); ++r)
539 {
540 for (int c = 0; c < mModel->getColumns(); ++c)
541 {
542 if (gcn::Widget *w = mModel->getElementAt(r, c))
543 w->_setFocusHandler(focusHandler);
544 }
545 }
546}
void setColor(const gcn::Color &color) override
Definition graphics.h:255
gcn::Widget * mWidget
Definition table.cpp:47
void action(const gcn::ActionEvent &actionEvent) override
Definition table.cpp:73
GuiTableActionListener(GuiTable *table, gcn::Widget *widget, int row, int column)
Definition table.cpp:51
~GuiTableActionListener() override
Definition table.cpp:64
GuiTable * mTable
Definition table.cpp:44
A table, with rows and columns made out of sub-widgets.
Definition table.h:47
bool mWrappingEnabled
Definition table.h:162
~GuiTable() override
Definition table.cpp:92
void installActionListeners()
Installs all action listeners on inner widgets.
Definition table.cpp:237
void mouseWheelMovedUp(gcn::MouseEvent &mouseEvent) override
Definition table.cpp:431
void setSelectedRow(int selected)
Definition table.cpp:178
int mSelectedRow
Definition table.h:172
void mousePressed(gcn::MouseEvent &mouseEvent) override
Definition table.cpp:413
void setSelectedColumn(int selected)
Definition table.cpp:206
gcn::Widget * mTopWidget
If someone moves a fresh widget to the top, we must display it.
Definition table.h:176
void setModel(TableModel *m)
Sets the table model.
Definition table.cpp:103
bool mOpaque
Definition table.h:163
int getSelectedColumn() const
Definition table.cpp:154
void modelUpdated(bool) override
Must be invoked by the TableModel whenever a global change is about to occur or has occurred (e....
Definition table.cpp:468
int getSelectedRow() const
Definition table.cpp:149
int getRowHeight() const
Definition table.cpp:164
int mSelectedColumn
Definition table.h:173
int getRowForY(int y) const
Definition table.cpp:500
friend class GuiTableActionListener
Definition table.h:49
int getColumnWidth(int i) const
Definition table.cpp:171
TableModel * getModel() const
Retrieves the active table model.
Definition table.cpp:98
bool mLinewiseMode
Definition table.h:161
gcn::Rectangle getChildrenArea() override
Definition table.cpp:363
void moveToBottom(gcn::Widget *child) override
Definition table.cpp:356
std::vector< GuiTableActionListener * > mActionListeners
Vector for compactness; used as a list in practice.
Definition table.h:179
void mouseDragged(gcn::MouseEvent &mouseEvent) override
Definition table.cpp:454
void mouseWheelMovedDown(gcn::MouseEvent &mouseEvent) override
Definition table.cpp:444
GuiTable(TableModel *initialModel=nullptr, gcn::Color background=0xffffff, bool opacity=true)
Definition table.cpp:80
void keyPressed(gcn::KeyEvent &keyEvent) override
Definition table.cpp:369
void recomputeDimensions()
Definition table.cpp:121
void setSelected(int row, int column)
Definition table.cpp:143
void setLinewiseSelection(bool linewise)
Toggle whether to use linewise selection mode, in which the table selects an entire line at a time,...
Definition table.cpp:159
void uninstallActionListeners()
Frees all action listeners on inner widgets.
Definition table.cpp:231
void _setFocusHandler(gcn::FocusHandler *focusHandler) override
Definition table.cpp:531
TableModel * mModel
Definition table.h:170
void draw(gcn::Graphics *graphics) override
Definition table.cpp:257
gcn::Widget * getWidgetAt(int x, int y) override
Definition table.cpp:482
void moveToTop(gcn::Widget *child) override
Definition table.cpp:350
int getColumnForX(int x) const
Definition table.cpp:513
Theme * getTheme() const
The global GUI theme.
Definition gui.h:138
A model for a regular table of widgets.
Definition tablemodel.h:50
virtual int getRows() const =0
Determines the number of rows (lines) in the table.
virtual int getRowHeight() const =0
Determines the height for each row.
void removeListener(TableModelListener *listener)
virtual int getColumns() const =0
Determines the number of columns in each row.
virtual gcn::Widget * getElementAt(int row, int column) const =0
Retrieves the widget stored at the specified location within the table.
virtual int getColumnWidth(int index) const =0
Determines the width of each individual column.
void installListener(TableModelListener *listener)
int getGuiAlpha() const
Get the current GUI alpha value.
Definition theme.h:321
static const gcn::Color & getThemeColor(int type)
Gets the color associated with the type in the default palette (0).
Definition theme.cpp:313
@ HIGHLIGHT
Definition theme.h:231
@ BACKGROUND
Definition theme.h:230
Graphics * graphics
Definition client.cpp:104
void delete_all(Container &c)
Definition dtor.h:46
Gui * gui
The GUI system.
Definition gui.cpp:50
@ ENTER
Definition sdlinput.h:78
@ SPACE
Definition sdlinput.h:76
@ LEFT
Definition sdlinput.h:119
@ HOME
Definition sdlinput.h:91
@ RIGHT
Definition sdlinput.h:120
@ UP
Definition sdlinput.h:121
@ DOWN
Definition sdlinput.h:122
@ END
Definition sdlinput.h:94