ManaPlus
guitable.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2008-2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2018 The ManaPlus Developers
6  *
7  * This file is part of The ManaPlus Client.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "gui/widgets/guitable.h"
24 
25 #include "settings.h"
26 
27 #include "gui/gui.h"
28 
29 #include "gui/models/tablemodel.h"
30 
32 
33 #include "render/graphics.h"
34 
35 #include "utils/delete2.h"
36 #include "utils/dtor.h"
37 
38 #include "debug.h"
39 
40 float GuiTable::mAlpha = 1.0;
41 
42 GuiTable::GuiTable(const Widget2 *const widget,
43  TableModel *const initial_model,
44  const Opaque opacity) :
45  Widget(widget),
46  MouseListener(),
47  KeyListener(),
49  mModel(nullptr),
50  mTopWidget(nullptr),
51  mActionListeners2(),
52  mHighlightColor(getThemeColor(ThemeColorId::HIGHLIGHT, 255U)),
53  mSelectedRow(-1),
54  mSelectedColumn(-1),
55  mLinewiseMode(false),
56  mWrappingEnabled(false),
57  mOpaque(opacity),
58  mSelectableGui(true)
59 {
60  mAllowLogic = false;
62 
63  setModel(initial_model);
64  setFocusable(true);
65 
66  addMouseListener(this);
67  addKeyListener(this);
68 }
69 
71 {
72  if (gui != nullptr)
73  gui->removeDragged(this);
74 
77 }
78 
80 {
81  return mModel;
82 }
83 
84 void GuiTable::setModel(TableModel *const new_model)
85 {
86  if (mModel != nullptr)
87  {
89  mModel->removeListener(this);
90  }
91 
92  mModel = new_model;
94 
95  new_model->installListener(this);
97 }
98 
100 {
101  if (mModel == nullptr)
102  return;
103 
104  const int rows_nr = mModel->getRows();
105  const int columns_nr = mModel->getColumns();
106  int width = 0;
107 
108  if (mSelectableGui)
109  {
110  if (mSelectedRow >= rows_nr)
111  mSelectedRow = rows_nr - 1;
112 
113  if (mSelectedColumn >= columns_nr)
114  mSelectedColumn = columns_nr - 1;
115  }
116 
117  for (int i = 0; i < columns_nr; i++)
118  width += getColumnWidth(i);
119 
120  setWidth(width);
121  setHeight(getRowHeight() * rows_nr);
122 }
123 
124 void GuiTable::setSelected(const int row, const int column)
125 {
126  mSelectedColumn = column;
127  mSelectedRow = row;
128 }
129 
131 {
132  return mSelectedRow;
133 }
134 
136 {
137  return mSelectedColumn;
138 }
139 
141 {
142  return mModel->getRowHeight() + 4; // border
143 }
144 
145 int GuiTable::getColumnWidth(const int i) const
146 {
147  return mModel->getColumnWidth(i) + 4; // border
148 }
149 
150 void GuiTable::setSelectedRow(const int selected)
151 {
152  if (!mSelectableGui)
153  {
154  mSelectedRow = -1;
155  }
156  else
157  {
158  const int rows = mModel->getRows();
159  if (selected < 0 && !mWrappingEnabled)
160  {
161  mSelectedRow = -1;
162  }
163  else if (selected >= rows && mWrappingEnabled)
164  {
165  mSelectedRow = 0;
166  }
167  else if ((selected >= rows && !mWrappingEnabled) ||
168  (selected < 0 && mWrappingEnabled))
169  {
170  mSelectedRow = rows - 1;
171  }
172  else
173  {
174  mSelectedRow = selected;
175  }
176  }
177 }
178 
179 void GuiTable::setSelectedColumn(const int selected)
180 {
181  const int columns = mModel->getColumns();
182  if ((selected >= columns && mWrappingEnabled) ||
183  (selected < 0 && !mWrappingEnabled))
184  {
185  mSelectedColumn = 0;
186  }
187  else if ((selected >= columns && !mWrappingEnabled) ||
188  (selected < 0 && mWrappingEnabled))
189  {
190  mSelectedColumn = columns - 1;
191  }
192  else
193  {
194  mSelectedColumn = selected;
195  }
196 }
197 
199 {
201  mActionListeners2.clear();
202 }
203 
205 {
206  const int rows = mModel->getRows();
207  const int columns = mModel->getColumns();
208 
209  for (int row = 0; row < rows; ++row)
210  {
211  for (int column = 0; column < columns; ++column)
212  {
213  Widget *const widget = mModel->getElementAt(row, column);
214  if (widget != nullptr)
215  {
217  this, widget, row, column));
218  }
219  }
220  }
221 
223 }
224 
225 // -- widget ops
226 void GuiTable::draw(Graphics *const graphics)
227 {
228  if (getRowHeight() == 0)
229  return;
230 
231  BLOCK_START("GuiTable::draw")
232  if (settings.guiAlpha != mAlpha)
234 
235  const Rect &rect = mDimension;
236  const int width = rect.width;
237  const int height = rect.height;
238  const int y = rect.y;
239  if (mOpaque == Opaque_true)
240  {
241  mBackgroundColor.a = CAST_U32(mAlpha * 255.0F);
242  graphics->setColor(mBackgroundColor);
243  graphics->fillRectangle(Rect(0, 0, width, height));
244  }
245 
246  // First, determine how many rows we need to draw,
247  // and where we should start.
248  int rHeight = getRowHeight();
249  if (rHeight == 0)
250  rHeight = 1;
251  int first_row = -(y / rHeight);
252 
253  if (first_row < 0)
254  first_row = 0;
255 
256  unsigned int rows_nr = CAST_U32(1 +
257  height / rHeight); // May overestimate by one.
258  unsigned int max_rows_nr;
259  if (mModel->getRows() < first_row)
260  {
261  max_rows_nr = 0U;
262  }
263  else
264  {
265  max_rows_nr = CAST_U32(
266  mModel->getRows() - first_row); // clip if neccessary:
267  }
268  if (max_rows_nr < rows_nr)
269  rows_nr = max_rows_nr;
270 
271  // Now determine the first and last column
272  // Take the easy way out; these are usually bounded and all visible.
273  const unsigned first_column = 0;
274  const unsigned last_column1 = CAST_U32(
275  mModel->getColumns());
276 
277  int y_offset = first_row * rHeight;
278 
279  for (unsigned int r = CAST_U32(first_row);
280  r < CAST_U32(first_row) + rows_nr;
281  ++r)
282  {
283  int x_offset = 0;
284 
285  for (unsigned c = first_column; c + 1 <= last_column1; ++c)
286  {
287  Widget *const widget = mModel->getElementAt(CAST_S32(r),
288  CAST_S32(c));
289  const int cWidth = CAST_S32(getColumnWidth(
290  CAST_S32(c)));
291  if (widget != nullptr)
292  {
293  Rect bounds(x_offset, y_offset, cWidth, rHeight);
294 
295  if (widget == mTopWidget)
296  {
297  bounds.height = widget->getHeight();
298  bounds.width = widget->getWidth();
299  }
300 
301  widget->setDimension(bounds);
302 
303  if (mSelectedRow > -1)
304  {
306  mAlpha * 255.0F);
307  graphics->setColor(mHighlightColor);
308 
309  if (mLinewiseMode && r == CAST_U32(
310  mSelectedRow) && c == 0)
311  {
312  graphics->fillRectangle(Rect(0, y_offset,
313  width, rHeight));
314  }
315  else if (!mLinewiseMode && mSelectedColumn > 0
316  && c == CAST_U32(mSelectedColumn)
317  && r == CAST_U32(mSelectedRow))
318  {
319  graphics->fillRectangle(Rect(
320  x_offset, y_offset, cWidth, rHeight));
321  }
322  }
323  graphics->pushClipArea(bounds);
324  widget->draw(graphics);
325  graphics->popClipArea();
326  }
327 
328  x_offset += cWidth;
329  }
330 
331  y_offset += rHeight;
332  }
333 
334  if (mTopWidget != nullptr)
335  {
336  const Rect &bounds = mTopWidget->getDimension();
337  graphics->pushClipArea(bounds);
338  mTopWidget->draw(graphics);
339  graphics->popClipArea();
340  }
341  BLOCK_END("GuiTable::draw")
342 }
343 
344 void GuiTable::safeDraw(Graphics *const graphics)
345 {
346  if (getRowHeight() == 0)
347  return;
348 
349  BLOCK_START("GuiTable::draw")
350  if (settings.guiAlpha != mAlpha)
352 
353  const Rect &rect = mDimension;
354  const int width = rect.width;
355  const int height = rect.height;
356  const int y = rect.y;
357  if (mOpaque == Opaque_true)
358  {
359  mBackgroundColor.a = CAST_U32(mAlpha * 255.0F);
360  graphics->setColor(mBackgroundColor);
361  graphics->fillRectangle(Rect(0, 0, width, height));
362  }
363 
364  // First, determine how many rows we need to draw,
365  // and where we should start.
366  int rHeight = getRowHeight();
367  if (rHeight == 0)
368  rHeight = 1;
369  int first_row = -(y / rHeight);
370 
371  if (first_row < 0)
372  first_row = 0;
373 
374  unsigned int rows_nr = CAST_U32(
375  1 + height / rHeight); // May overestimate by one.
376  unsigned int max_rows_nr;
377  if (mModel->getRows() < first_row)
378  {
379  max_rows_nr = 0;
380  }
381  else
382  {
383  max_rows_nr = CAST_U32(
384  mModel->getRows() - first_row); // clip if neccessary:
385  }
386  if (max_rows_nr < rows_nr)
387  rows_nr = max_rows_nr;
388 
389  // Now determine the first and last column
390  // Take the easy way out; these are usually bounded and all visible.
391  const unsigned int first_column = 0;
392  const unsigned int last_column1 = CAST_U32(
393  mModel->getColumns());
394 
395  int y_offset = first_row * rHeight;
396 
397  for (unsigned int r = CAST_U32(first_row);
398  r < CAST_U32(first_row + CAST_S32(rows_nr));
399  ++r)
400  {
401  int x_offset = 0;
402 
403  for (unsigned c = first_column; c + 1 <= last_column1; ++c)
404  {
405  Widget *const widget = mModel->getElementAt(CAST_S32(r),
406  CAST_S32(c));
407  const int cWidth = CAST_S32(getColumnWidth(
408  CAST_S32(c)));
409  if (widget != nullptr)
410  {
411  Rect bounds(x_offset, y_offset, cWidth, rHeight);
412 
413  if (widget == mTopWidget)
414  {
415  bounds.height = widget->getHeight();
416  bounds.width = widget->getWidth();
417  }
418 
419  widget->setDimension(bounds);
420 
421  if (mSelectedRow > -1)
422  {
424  mAlpha * 255.0F);
425  graphics->setColor(mHighlightColor);
426 
427  if (mLinewiseMode && r == CAST_U32(
428  mSelectedRow) && c == 0)
429  {
430  graphics->fillRectangle(Rect(0, y_offset,
431  width, rHeight));
432  }
433  else if (!mLinewiseMode && mSelectedColumn > 0
434  && c == CAST_U32(mSelectedColumn)
435  && r == CAST_U32(mSelectedRow))
436  {
437  graphics->fillRectangle(Rect(
438  x_offset, y_offset, cWidth, rHeight));
439  }
440  }
441  graphics->pushClipArea(bounds);
442  widget->safeDraw(graphics);
443  graphics->popClipArea();
444  }
445 
446  x_offset += cWidth;
447  }
448 
449  y_offset += rHeight;
450  }
451 
452  if (mTopWidget != nullptr)
453  {
454  const Rect &bounds = mTopWidget->getDimension();
455  graphics->pushClipArea(bounds);
456  mTopWidget->safeDraw(graphics);
457  graphics->popClipArea();
458  }
459  BLOCK_END("GuiTable::draw")
460 }
461 
462 void GuiTable::moveToTop(Widget *const widget)
463 {
464  Widget::moveToTop(widget);
465  mTopWidget = widget;
466 }
467 
468 void GuiTable::moveToBottom(Widget *const widget)
469 {
470  Widget::moveToBottom(widget);
471  if (widget == mTopWidget)
472  mTopWidget = nullptr;
473 }
474 
476 {
477  return Rect(0, 0, mDimension.width, mDimension.height);
478 }
479 
480 // -- KeyListener notifications
482 {
483  const InputActionT action = event.getActionId();
484 
485  if (action == InputAction::GUI_SELECT)
486  {
488  event.consume();
489  }
490  else if (action == InputAction::GUI_UP)
491  {
493  event.consume();
494  }
495  else if (action == InputAction::GUI_DOWN)
496  {
498  event.consume();
499  }
500  else if (action == InputAction::GUI_LEFT)
501  {
503  event.consume();
504  }
505  else if (action == InputAction::GUI_RIGHT)
506  {
508  event.consume();
509  }
510  else if (action == InputAction::GUI_HOME)
511  {
512  setSelectedRow(0);
514  event.consume();
515  }
516  else if (action == InputAction::GUI_END && (mModel != nullptr))
517  {
518  setSelectedRow(mModel->getRows() - 1);
520  event.consume();
521  }
522 }
523 
524 // -- MouseListener notifications
526 {
527  if (!mSelectableGui)
528  return;
529 
530  if (event.getButton() == MouseButton::LEFT)
531  {
532  const int row = getRowForY(event.getY());
533  const int column = getColumnForX(event.getX());
534 
535  if (row > -1 && column > -1 &&
536  row < mModel->getRows() && column < mModel->getColumns())
537  {
538  mSelectedColumn = column;
539  mSelectedRow = row;
540  event.consume();
541  }
542 
544  }
545 }
546 
548 {
549  if (isFocused())
550  {
551  const int selRow = getSelectedRow();
552  if (selRow > 0 || (selRow == 0 && mWrappingEnabled))
553  setSelectedRow(selRow - 1);
554  event.consume();
555  }
556 }
557 
559 {
560  if (isFocused())
561  {
563  event.consume();
564  }
565 }
566 
568 {
569  if (event.getButton() != MouseButton::LEFT)
570  return;
571 
572  // Make table selection update on drag
573  const int x = std::max(0, event.getX());
574  const int y = std::max(0, event.getY());
575 
578 }
579 
580 void GuiTable::modelUpdated(const bool completed)
581 {
582  if (completed)
583  {
586  }
587  else
588  { // before the update?
589  mTopWidget = nullptr; // No longer valid in general
591  }
592 }
593 
595 {
596  if (mModel == nullptr)
597  return nullptr;
598 
599  const int row = getRowForY(y);
600  const int column = getColumnForX(x);
601 
602  if (mTopWidget != nullptr &&
604  {
605  return mTopWidget;
606  }
607 
608  if (row > -1 && column > -1)
609  {
610  Widget *const w = mModel->getElementAt(row, column);
611  if (w != nullptr && w->isFocusable())
612  return w;
613  }
614  return nullptr;
615 }
616 
617 int GuiTable::getRowForY(const int y) const
618 {
619  int row = -1;
620 
621  const int rowHeight = getRowHeight();
622  if (rowHeight > 0)
623  row = y / rowHeight;
624 
625  if (row < 0 || row >= mModel->getRows())
626  return -1;
627  return row;
628 }
629 
630 int GuiTable::getColumnForX(const int x) const
631 {
632  int column;
633  int delta = 0;
634 
635  const int colnum = mModel->getColumns();
636  for (column = 0; column < colnum; column ++)
637  {
638  delta += getColumnWidth(column);
639  if (x <= delta)
640  break;
641  }
642 
643  if (column >= colnum)
644  return -1;
645  return column;
646 }
647 
648 void GuiTable::setFocusHandler(FocusHandler *const focusHandler)
649 {
650  // add check for focusHandler. may be need remove it?
651 
652  if (focusHandler == nullptr)
653  return;
654 
655  Widget::setFocusHandler(focusHandler);
656 
657  const int rows = mModel->getRows();
658  const int cols = mModel->getColumns();
659  for (int r = 0; r < rows; ++r)
660  {
661  for (int c = 0; c < cols ; ++c)
662  {
663  Widget *const w = mModel->getElementAt(r, c);
664  if (w != nullptr)
665  w->setFocusHandler(focusHandler);
666  }
667  }
668 }
669 
671 {
672  if (mFocusHandler == nullptr)
673  return;
675 }
#define CAST_U32
Definition: cast.h:30
std::vector< GuiTableActionListener * > mActionListeners2
Definition: guitable.h:191
int width
Definition: rect.h:218
int getWidth() const
Definition: widget.h:220
void keyPressed(KeyEvent &event)
Definition: guitable.cpp:481
void setWidth(const int width)
Definition: widget.cpp:132
void draw(Graphics *const graphics)
Definition: guitable.cpp:226
bool mSelectableGui
Definition: guitable.h:204
Widget * getWidgetAt(int x, int y)
Definition: guitable.cpp:594
Gui * gui
Definition: gui.cpp:110
virtual void draw(Graphics *const graphics)=0
unsigned int a
Definition: color.h:250
const TableModel * getModel() const
Definition: guitable.cpp:79
void mousePressed(MouseEvent &event)
Definition: guitable.cpp:525
virtual int getColumns() const =0
~GuiTable()
Definition: guitable.cpp:70
void setSelected(const int row, const int column)
Definition: guitable.cpp:124
MouseButtonT getButton() const
Definition: mouseevent.h:115
int getColumnWidth(const int i) const
Definition: guitable.cpp:145
Definition: rect.h:72
void mouseWheelMovedDown(MouseEvent &event)
Definition: guitable.cpp:558
int getRowHeight() const
Definition: guitable.cpp:140
virtual int getRowHeight() const =0
virtual void safeDraw(Graphics *const graphics)=0
#define BLOCK_START(name)
Definition: perfomance.h:78
bool isPointInRect(const int x_, const int y_) const
Definition: rect.h:196
void setDimension(const Rect &dimension)
Definition: widget.cpp:168
FocusHandler * getFocusHandler()
Definition: widget.h:473
GuiTable(const Widget2 *const widget, TableModel *const initial_model, const Opaque opacity)
Definition: guitable.cpp:42
void installActionListeners()
Definition: guitable.cpp:204
#define BLOCK_END(name)
Definition: perfomance.h:79
FocusHandler * mFocusHandler
Definition: widget.h:1115
virtual void popClipArea()
Definition: graphics.cpp:738
void moveToTop(Widget *const widget)
Definition: guitable.cpp:462
#define delete2(var)
Definition: delete2.h:24
Widget * mTopWidget
Definition: guitable.h:188
void recomputeDimensions()
Definition: guitable.cpp:99
virtual int getRows() const =0
virtual bool isFocused() const
Definition: widget.cpp:183
TableModel * mModel
Definition: guitable.h:185
void distributeActionEvent()
Definition: widget.cpp:492
Settings settings
Definition: settings.cpp:31
Color mBackgroundColor
Definition: widget.h:1090
virtual void setColor(const Color &color)
Definition: graphics.h:319
#define CAST_S32
Definition: cast.h:29
virtual void requestFocus()
Definition: widget.cpp:203
void delete_all(Container &c)
Definition: dtor.h:55
virtual void removeListener(TableModelListener *const listener)
Definition: tablemodel.cpp:39
void setFocusHandler(FocusHandler *const focusHandler)
Definition: guitable.cpp:648
void setSelectedColumn(const int selected)
Definition: guitable.cpp:179
int y
Definition: rect.h:213
int mSelectedRow
Definition: guitable.h:198
InputAction ::T InputActionT
Definition: inputaction.h:715
Color mHighlightColor
Definition: guitable.h:196
#define nullptr
Definition: localconsts.h:44
int getSelectedRow() const
Definition: guitable.cpp:130
bool mAllowLogic
Definition: widget.h:1159
void addMouseListener(MouseListener *const mouseListener)
Definition: widget.cpp:291
int height
Definition: rect.h:223
void addKeyListener(KeyListener *const keyListener)
Definition: widget.cpp:271
friend class GuiTableActionListener
Definition: guitable.h:54
const Rect & getDimension() const
Definition: widget.h:316
virtual void moveToBottom(Widget *widget)
Definition: widget.h:876
bool mWrappingEnabled
Definition: guitable.h:202
int mSelectedColumn
Definition: guitable.h:199
int getColumnForX(const int x) const
Definition: guitable.cpp:630
int getRowForY(const int y) const
Definition: guitable.cpp:617
virtual void moveToTop(Widget *widget)
Definition: widget.h:866
void setModel(TableModel *const m)
Definition: guitable.cpp:84
int getY() const
Definition: mouseevent.h:137
void mouseDragged(MouseEvent &event)
Definition: guitable.cpp:567
void setHeight(const int height)
Definition: widget.cpp:139
void mouseWheelMovedUp(MouseEvent &event)
Definition: guitable.cpp:547
virtual int getColumnWidth(const int index) const =0
virtual void fillRectangle(const Rect &rectangle)=0
void setFocusable(const bool focusable)
Definition: widget.cpp:191
void removeDragged(const Widget *const widget)
Definition: gui.cpp:1161
Definition: widget.h:97
void moveToBottom(Widget *const widget)
Definition: guitable.cpp:468
const bool Opaque_true
Definition: opaque.h:29
void requestFocus()
Definition: guitable.cpp:670
Opaque mOpaque
Definition: guitable.h:203
bool isFocusable() const
Definition: widget.cpp:198
int getHeight() const
Definition: widget.h:239
void modelUpdated(const bool completed)
Definition: guitable.cpp:580
virtual void pushClipArea(const Rect &area)
Definition: graphics.cpp:676
const Color & getThemeColor(const ThemeColorIdT type, const unsigned int alpha) const A_INLINE
Definition: widget2.h:44
int getX() const
Definition: mouseevent.h:126
void safeDraw(Graphics *const graphics)
Definition: guitable.cpp:344
virtual void setFocusHandler(FocusHandler *const focusHandler)
Definition: widget.cpp:237
virtual void installListener(TableModelListener *const listener)
Definition: tablemodel.cpp:33
void uninstallActionListeners()
Definition: guitable.cpp:198
bool mLinewiseMode
Definition: guitable.h:201
float guiAlpha
Definition: settings.h:129
void setSelectedRow(const int selected)
Definition: guitable.cpp:150
int getSelectedColumn() const
Definition: guitable.cpp:135
Rect mDimension
Definition: widget.h:1100
static float mAlpha
Definition: guitable.h:183
Rect getChildrenArea()
Definition: guitable.cpp:475
virtual Widget * getElementAt(const int row, const int column) const =0