/*
* Copyright 2010-2016 OpenXcom Developers.
*
* This file is part of OpenXcom.
*
* OpenXcom is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenXcom is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TextList.h"
#include <cstdarg>
#include <cmath>
#include <algorithm>
#include "../Engine/Action.h"
#include "../Engine/Font.h"
#include "../Engine/Palette.h"
#include "../Engine/Options.h"
#include "ArrowButton.h"
#include "ComboBox.h"
#include "ScrollBar.h"
#include "../fmath.h"
namespace OpenXcom
{
/**
* Sets up a blank list with the specified size and position.
* @param width Width in pixels.
* @param height Height in pixels.
* @param x X position in pixels.
* @param y Y position in pixels.
*/
TextList::TextList(int width, int height, int x, int y) : InteractiveSurface(width, height, x, y), _big(0), _small(0), _font(0), _scroll(0), _visibleRows(0), _selRow(0), _color(0), _dot(false), _selectable(false), _condensed(false), _contrast(false), _wrap(false), _flooding(false),
_bg(0), _selector(0), _margin(0), _scrolling(true), _arrowPos(-1), _scrollPos(4), _arrowType(ARROW_VERTICAL),
_leftClick(0), _leftPress(0), _leftRelease(0), _rightClick(0), _rightPress(0), _rightRelease(0), _arrowsLeftEdge(0), _arrowsRightEdge(0), _comboBox(0)
{
_up = new ArrowButton(ARROW_BIG_UP, 13, 14, getX() + getWidth() + _scrollPos, getY());
_up->setVisible(false);
_up->setTextList(this);
_down = new ArrowButton(ARROW_BIG_DOWN, 13, 14, getX() + getWidth() + _scrollPos, getY() + getHeight() - 14);
_down->setVisible(false);
_down->setTextList(this);
int h = std::max(_down->getY() - _up->getY() - _up->getHeight(), 1);
_scrollbar = new ScrollBar(_up->getWidth(), h, getX() + getWidth() + _scrollPos, _up->getY() + _up->getHeight());
_scrollbar->setVisible(false);
_scrollbar->setTextList(this);
}
/**
* Deletes all the stuff contained in the list.
*/
TextList::~TextList()
{
for (std::vector< std::vector<Text*> >::iterator u = _texts.begin(); u < _texts.end(); ++u)
{
for (std::vector<Text*>::iterator v = (*u).begin(); v < (*u).end(); ++v)
{
delete *v;
}
}
for (std::vector<ArrowButton*>::iterator i = _arrowLeft.begin(); i < _arrowLeft.end(); ++i)
{
delete *i;
}
for (std::vector<ArrowButton*>::iterator i = _arrowRight.begin(); i < _arrowRight.end(); ++i)
{
delete *i;
}
delete _selector;
delete _up;
delete _down;
delete _scrollbar;
}
/**
* Changes the position of the surface in the X axis.
* @param x X position in pixels.
*/
void TextList::setX(int x)
{
Surface::setX(x);
_up->setX(getX() + getWidth() + _scrollPos);
_down->setX(getX() + getWidth() + _scrollPos);
_scrollbar->setX(getX() + getWidth() + _scrollPos);
if (_selector != 0)
_selector->setX(getX());
}
/**
* Changes the position of the surface in the Y axis.
* @param y Y position in pixels.
*/
void TextList::setY(int y)
{
Surface::setY(y);
_up->setY(getY());
_down->setY(getY() + getHeight() - 14);
_scrollbar->setY(_up->getY() + _up->getHeight());
if (_selector != 0)
_selector->setY(getY());
}
/**
* Gets the arrowsLeftEdge.
* @return arrowsLeftEdge.
*/
int TextList::getArrowsLeftEdge()
{
return _arrowsLeftEdge;
}
/**
* Gets the arrowsRightEdge.
* @return arrowsRightEdge.
*/
int TextList::getArrowsRightEdge()
{
return _arrowsRightEdge;
}
/**
* Unpresses all the arrow buttons.
* @param state Pointer to running state.
*/
void TextList::unpress(State *state)
{
InteractiveSurface::unpress(state);
for (std::vector<ArrowButton*>::iterator i = _arrowLeft.begin(); i < _arrowLeft.end(); ++i)
{
(*i)->unpress(state);
}
for (std::vector<ArrowButton*>::iterator i = _arrowRight.begin(); i < _arrowRight.end(); ++i)
{
(*i)->unpress(state);
}
}
/**
* Changes the color of a specific Text object in the list.
* @param row Row number.
* @param column Column number.
* @param color Text color.
*/
void TextList::setCellColor(size_t row, size_t column, Uint8 color)
{
_texts[row][column]->setColor(color);
_redraw = true;
}
/**
* Changes the text color of a whole row in the list.
* @param row Row number.
* @param color Text color.
*/
void TextList::setRowColor(size_t row, Uint8 color)
{
for (std::vector<Text*>::iterator i = _texts[row].begin(); i < _texts[row].end(); ++i)
{
(*i)->setColor(color);
}
_redraw = true;
}
/**
* Returns the text of a specific Text object in the list.
* @param row Row number.
* @param column Column number.
* @return Text string.
*/
std::string TextList::getCellText(size_t row, size_t column) const
{
return _texts[row][column]->getText();
}
/**
* Changes the text of a specific Text object in the list.
* @param row Row number.
* @param column Column number.
* @param text Text string.
*/
void TextList::setCellText(size_t row, size_t column, const std::string &text)
{
_texts[row][column]->setText(text);
_redraw = true;
}
/**
* Returns the X position of a specific text column in the list.
* @param column Column number.
* @return X position in pixels.
*/
int TextList::getColumnX(size_t column) const
{
return getX() + _texts[0][column]->getX();
}
/**
* Returns the Y position of a specific text row in the list.
* @param row Row number.
* @return Y position in pixels.
*/
int TextList::getRowY(size_t row) const
{
return getY() + _texts[row][0]->getY();
}
/**
* Returns the height of a specific text row in the list.
* @param row Row number.
* @return height in pixels.
*/
int TextList::getTextHeight(size_t row) const
{
return _texts[row].front()->getTextHeight();
}
/**
* Returns the height of a specific text row in the list.
* @param row Row number.
* @return height in pixels.
*/
int TextList::getNumTextLines(size_t row) const
{
return _texts[row].front()->getNumLines();
}
/**
* Returns the amount of text rows stored in the list.
* @return Number of rows.
*/
size_t TextList::getTexts() const
{
return _texts.size();
}
/**
* Returns the amount of physical rows stored in the list.
* @return Number of rows.
*/
size_t TextList::getRows() const
{
return _rows.size();
}
/**
* Returns the amount of visible rows stored in the list.
* @return Number of rows.
*/
size_t TextList::getVisibleRows() const
{
return _visibleRows;
}
/**
* Adds a new row of text to the list, automatically creating
* the required Text objects lined up where they need to be.
* @param cols Number of columns.
* @param ... Text for each cell in the new row.
*/
void TextList::addRow(int cols, ...)
{
va_list args;
int ncols;
va_start(args, cols);
if (cols > 0)
{
ncols = cols;
}
else
{
ncols = 1;
}
std::vector<Text*> temp;
// Positions are relative to list surface.
int rowX = 0, rowY = 0, rows = 1, rowHeight = 0;
if (!_texts.empty())
{
rowY = _texts.back().front()->getY() + _texts.back().front()->getHeight() + _font->getSpacing();
}
for (int i = 0; i < ncols; ++i)
{
int width;
// Place text
if (_flooding)
{
width = 340;
}
else
{
width = _columns[i];
}
Text* txt = new Text(width, _font->getHeight(), _margin + rowX, rowY);
txt->setPalette(this->getPalette());
txt->initText(_big, _small, _lang);
txt->setColor(_color);
txt->setSecondaryColor(_color2);
if (_align[i])
{
txt->setAlign(_align[i]);
}
txt->setHighContrast(_contrast);
if (_font == _big)
{
txt->setBig();
}
else
{
txt->setSmall();
}
if (cols > 0)
txt->setText(va_arg(args, char*));
// grab this before we enable word wrapping so we can use it to calculate
// the total row height below
int vmargin = _font->getHeight() - txt->getTextHeight();
// Wordwrap text if necessary
if (_wrap && txt->getTextWidth() > txt->getWidth())
{
txt->setWordWrap(true, true);
rows = std::max(rows, txt->getNumLines());
}
rowHeight = std::max(rowHeight, txt->getTextHeight() + vmargin);
// Places dots between text
if (_dot && i < cols - 1)
{
std::string buf = txt->getText();
unsigned int w = txt->getTextWidth();
while (w < _columns[i])
{
if (_align[i] != ALIGN_RIGHT)
{
w += _font->getChar('.')->getCrop()->w + _font->getSpacing();
buf += '.';
}
if (_align[i] != ALIGN_LEFT)
{
w += _font->getChar('.')->getCrop()->w + _font->getSpacing();
buf.insert(0, 1, '.');
}
}
txt->setText(buf);
}
temp.push_back(txt);
if (_condensed)
{
rowX += txt->getTextWidth();
}
else
{
rowX += _columns[i];
}
}
// ensure all elements in this row are the same height
for (int i = 0; i < cols; ++i)
{
temp[i]->setHeight(rowHeight);
}
_texts.push_back(temp);
for (int i = 0; i < rows; ++i)
{
_rows.push_back(_texts.size() - 1);
}
// Place arrow buttons
// Position defined w.r.t. main window, NOT TextList.
if (_arrowPos != -1)
{
ArrowShape shape1, shape2;
if (_arrowType == ARROW_VERTICAL)
{
shape1 = ARROW_SMALL_UP;
shape2 = ARROW_SMALL_DOWN;
}
else
{
shape1 = ARROW_SMALL_LEFT;
shape2 = ARROW_SMALL_RIGHT;
}
ArrowButton *a1 = new ArrowButton(shape1, 11, 8, getX() + _arrowPos, getY());
a1->setListButton();
a1->setPalette(this->getPalette());
a1->setColor(_up->getColor());
a1->onMouseClick(_leftClick, 0);
a1->onMousePress(_leftPress);
a1->onMouseRelease(_leftRelease);
_arrowLeft.push_back(a1);
ArrowButton *a2 = new ArrowButton(shape2, 11, 8, getX() + _arrowPos + 12, getY());
a2->setListButton();
a2->setPalette(this->getPalette());
a2->setColor(_up->getColor());
a2->onMouseClick(_rightClick, 0);
a2->onMousePress(_rightPress);
a2->onMouseRelease(_rightRelease);
_arrowRight.push_back(a2);
}
_redraw = true;
va_end(args);
updateArrows();
}
/**
* Changes the columns that the list contains.
* While rows can be unlimited, columns need to be specified
* since they can have various widths for lining up the text.
* @param cols Number of columns.
* @param ... Width of each column.
*/
void TextList::setColumns(int cols, ...)
{
va_list args;
va_start(args, cols);
for (int i = 0; i < cols; ++i)
{
_columns.push_back(va_arg(args, int));
}
va_end(args);
}
/**
* Replaces a certain amount of colors in the palette of all
* the text contained in the list.
* @param colors Pointer to the set of colors.
* @param firstcolor Offset of the first color to replace.
* @param ncolors Amount of colors to replace.
*/
void TextList::setPalette(SDL_Color *colors, int firstcolor, int ncolors)
{
Surface::setPalette(colors, firstcolor, ncolors);
for (std::vector< std::vector<Text*> >::iterator u = _texts.begin(); u < _texts.end(); ++u)
{
for (std::vector<Text*>::iterator v = u->begin(); v < u->end(); ++v)
{
(*v)->setPalette(colors, firstcolor, ncolors);
}
}
for (std::vector<ArrowButton*>::iterator i = _arrowLeft.begin(); i < _arrowLeft.end(); ++i)
{
(*i)->setPalette(colors, firstcolor, ncolors);
}
for (std::vector<ArrowButton*>::iterator i = _arrowRight.begin(); i < _arrowRight.end(); ++i)
{
(*i)->setPalette(colors, firstcolor, ncolors);
}
if (_selector != 0)
{
_selector->setPalette(colors, firstcolor, ncolors);
}
_up->setPalette(colors, firstcolor, ncolors);
_down->setPalette(colors, firstcolor, ncolors);
_scrollbar->setPalette(colors, firstcolor, ncolors);
}
/**
* Changes the resources for the text in the list
* and calculates the selector and visible amount of rows.
* @param big Pointer to large-size font.
* @param small Pointer to small-size font.
* @param lang Pointer to current language.
*/
void TextList::initText(Font *big, Font *small, Language *lang)
{
_big = big;
_small = small;
_font = small;
_lang = lang;
delete _selector;
_selector = new Surface(getWidth(), _font->getHeight() + _font->getSpacing(), getX(), getY());
_selector->setPalette(getPalette());
_selector->setVisible(false);
updateVisible();
}
/**
* Changes the height of the text list.
* @param height New height in pixels.
*/
void TextList::setHeight(int height)
{
Surface::setHeight(height);
setY(getY());
int h = std::max(_down->getY() - _up->getY() - _up->getHeight(), 1);
_scrollbar->setHeight(h);
updateVisible();
}
/**
* Changes the color of the text in the list. This doesn't change
* the color of existing text, just the color of text added from then on.
* @param color Color value.
*/
void TextList::setColor(Uint8 color)
{
_color = color;
_up->setColor(color);
_down->setColor(color);
_scrollbar->setColor(color);
for (std::vector< std::vector<Text*> >::iterator u = _texts.begin(); u < _texts.end(); ++u)
{
for (std::vector<Text*>::iterator v = u->begin(); v < u->end(); ++v)
{
(*v)->setColor(color);
}
}
}
/**
* Returns the color of the text in the list.
* @return Color value.
*/
Uint8 TextList::getColor() const
{
return _color;
}
/**
* Changes the secondary color of the text in the list.
* @param color Color value.
*/
void TextList::setSecondaryColor(Uint8 color)
{
_color2 = color;
}
/**
* Returns the secondary color of the text in the list.
* @return Color value.
*/
Uint8 TextList::getSecondaryColor() const
{
return _color2;
}
/**
* Enables/disables text wordwrapping. When enabled, rows can
* take up multiple lines of the list, otherwise every row
* is restricted to one line.
* @param wrap Wordwrapping setting.
*/
void TextList::setWordWrap(bool wrap)
{
_wrap = wrap;
}
/**
* Enables/disables high contrast color. Mostly used for
* Battlescape text.
* @param contrast High contrast setting.
*/
void TextList::setHighContrast(bool contrast)
{
_contrast = contrast;
for (std::vector< std::vector<Text*> >::iterator u = _texts.begin(); u < _texts.end(); ++u)
{
for (std::vector<Text*>::iterator v = u->begin(); v < u->end(); ++v)
{
(*v)->setHighContrast(contrast);
}
}
_scrollbar->setHighContrast(contrast);
}
/**
* Changes the horizontal alignment of the text in the list. This doesn't change
* the alignment of existing text, just the alignment of text added from then on.
* @param align Horizontal alignment.
* @param col the column to set the alignment for (defaults to -1, meaning "all")
*/
void TextList::setAlign(TextHAlign align, int col)
{
if (col == -1)
{
for (size_t i = 0; i < _columns.size(); ++i)
{
_align[i] = align;
}
}
else
{
_align[col] = align;
}
}
/**
* If enabled, the text in different columns will be separated by dots.
* Otherwise, it will only be separated by blank space.
* @param dot True for dots, False for spaces.
*/
void TextList::setDot(bool dot)
{
_dot = dot;
}
/**
* If enabled, the list will respond to player input,
* highlighting selected rows and receiving clicks.
* @param selectable Selectable setting.
*/
void TextList::setSelectable(bool selectable)
{
_selectable = selectable;
}
/**
* Changes the text list to use the big-size font.
*/
void TextList::setBig()
{
_font = _big;
delete _selector;
_selector = new Surface(getWidth(), _font->getHeight() + _font->getSpacing(), getX(), getY());
_selector->setPalette(getPalette());
_selector->setVisible(false);
updateVisible();
}
/**
* Changes the text list to use the small-size font.
*/
void TextList::setSmall()
{
_font = _small;
delete _selector;
_selector = new Surface(getWidth(), _font->getHeight() + _font->getSpacing(), getX(), getY());
_selector->setPalette(getPalette());
_selector->setVisible(false);
updateVisible();
}
/**
* If enabled, the columns will match the text width.
* Otherwise, they will have a fixed width.
* @param condensed True for condensed layout, False for table layout.
*/
void TextList::setCondensed(bool condensed)
{
_condensed = condensed;
}
/**
* Returns the currently selected row if the text
* list is selectable.
* @return Selected row, -1 if none.
*/
unsigned int TextList::getSelectedRow() const
{
if (_rows.empty() || _selRow >= _rows.size())
{
return -1;
}
else
{
return _rows[_selRow];
}
}
/**
* Changes the surface used to draw the background of the selector.
* @param bg New background.
*/
void TextList::setBackground(Surface *bg)
{
_bg = bg;
_scrollbar->setBackground(_bg);
}
/**
* Changes the horizontal margin placed around the text.
* @param margin Margin in pixels.
*/
void TextList::setMargin(int margin)
{
_margin = margin;
}
/**
* Returns the margin of the text in the list.
* @return Margin in pixels.
*/
int TextList::getMargin() const
{
return _margin;
}
/**
* Changes the color of the arrow buttons in the list.
* @param color Color value.
*/
void TextList::setArrowColor(Uint8 color)
{
_up->setColor(color);
_down->setColor(color);
_scrollbar->setColor(color);
}
/**
* Sets the position of the column of arrow buttons
* in the text list.
* @param pos X in pixels (-1 to disable).
* @param type Arrow orientation type.
*/
void TextList::setArrowColumn(int pos, ArrowOrientation type)
{
_arrowPos = pos;
_arrowType = type;
_arrowsLeftEdge = getX() + _arrowPos;
_arrowsRightEdge = _arrowsLeftEdge + 12 + 11;
}
/**
* Sets a function to be called every time the left arrows are mouse clicked.
* @param handler Action handler.
*/
void TextList::onLeftArrowClick(ActionHandler handler)
{
_leftClick = handler;
for (std::vector<ArrowButton*>::iterator i = _arrowLeft.begin(); i < _arrowLeft.end(); ++i)
{
(*i)->onMouseClick(handler, 0);
}
}
/**
* Sets a function to be called every time the left arrows are mouse pressed.
* @param handler Action handler.
*/
void TextList::onLeftArrowPress(ActionHandler handler)
{
_leftPress = handler;
for (std::vector<ArrowButton*>::iterator i = _arrowLeft.begin(); i < _arrowLeft.end(); ++i)
{
(*i)->onMousePress(handler);
}
}
/**
* Sets a function to be called every time the left arrows are mouse released.
* @param handler Action handler.
*/
void TextList::onLeftArrowRelease(ActionHandler handler)
{
_leftRelease = handler;
for (std::vector<ArrowButton*>::iterator i = _arrowLeft.begin(); i < _arrowLeft.end(); ++i)
{
(*i)->onMouseRelease(handler);
}
}
/**
* Sets a function to be called every time the right arrows are mouse clicked.
* @param handler Action handler.
*/
void TextList::onRightArrowClick(ActionHandler handler)
{
_rightClick = handler;
for (std::vector<ArrowButton*>::iterator i = _arrowRight.begin(); i < _arrowRight.end(); ++i)
{
(*i)->onMouseClick(handler, 0);
}
}
/**
* Sets a function to be called every time the right arrows are mouse pressed.
* @param handler Action handler.
*/
void TextList::onRightArrowPress(ActionHandler handler)
{
_rightPress = handler;
for (std::vector<ArrowButton*>::iterator i = _arrowRight.begin(); i < _arrowRight.end(); ++i)
{
(*i)->onMousePress(handler);
}
}
/**
* Sets a function to be called every time the right arrows are mouse released.
* @param handler Action handler.
*/
void TextList::onRightArrowRelease(ActionHandler handler)
{
_rightRelease = handler;
for (std::vector<ArrowButton*>::iterator i = _arrowRight.begin(); i < _arrowRight.end(); ++i)
{
(*i)->onMouseRelease(handler);
}
}
/**
* Removes all the rows currently stored in the list.
*/
void TextList::clearList()
{
for (std::vector< std::vector<Text*> >::iterator u = _texts.begin(); u < _texts.end(); ++u)
{
for (std::vector<Text*>::iterator v = u->begin(); v < u->end(); ++v)
{
delete (*v);
}
u->clear();
}
scrollUp(true, false);
_texts.clear();
_rows.clear();
_redraw = true;
}
/**
* Scrolls the text in the list up by one row or to the top.
* @param toMax If true then scrolls to the top of the list. false => one row up
* @param scrollByWheel If true then use wheel scroll, otherwise scroll normally.
*/
void TextList::scrollUp(bool toMax, bool scrollByWheel)
{
if (!_scrolling)
return;
if (_rows.size() > _visibleRows && _scroll > 0)
{
if (toMax)
{
scrollTo(0);
}
else
{
if (scrollByWheel)
{
scrollTo(_scroll - std::min((size_t)(Options::mousewheelSpeed), _scroll));
}
else
{
scrollTo(_scroll - 1);
}
}
}
}
/**
* Scrolls the text in the list down by one row or to the bottom.
* @param toMax If true then scrolls to the bottom of the list. false => one row down
* @param scrollByWheel If true then use wheel scroll, otherwise scroll normally.
*/
void TextList::scrollDown(bool toMax, bool scrollByWheel)
{
if (!_scrolling)
return;
if (_rows.size() > _visibleRows && _scroll < _rows.size() - _visibleRows)
{
if (toMax)
{
scrollTo(_rows.size() - _visibleRows);
}
else
{
if (scrollByWheel)
{
scrollTo(_scroll + Options::mousewheelSpeed);
}
else
{
scrollTo(_scroll + 1);
}
}
}
}
/**
* Updates the visibility of the arrow buttons according to
* the current scroll position.
*/
void TextList::updateArrows()
{
_up->setVisible(_rows.size() > _visibleRows /*&& _scroll > 0*/);
_down->setVisible(_rows.size() > _visibleRows /*&& _scroll < _rows.size() - _visibleRows*/);
_scrollbar->setVisible(_rows.size() > _visibleRows);
_scrollbar->invalidate();
_scrollbar->blit(this);
}
/**
* Updates the amount of visible rows according to the
* current list and font size.
*/
void TextList::updateVisible()
{
_visibleRows = 0;
for (int y = 0; y < getHeight(); y += _font->getHeight() + _font->getSpacing())
{
_visibleRows++;
}
updateArrows();
}
/**
* Changes whether the list can be scrolled.
* @param scrolling True to allow scrolling, false otherwise.
* @param scrollPos Custom X position for the scroll buttons.
*/
void TextList::setScrolling(bool scrolling, int scrollPos)
{
_scrolling = scrolling;
if (scrollPos != _scrollPos)
{
_scrollPos = scrollPos;
_up->setX(getX() + getWidth() + _scrollPos);
_down->setX(getX() + getWidth() + _scrollPos);
_scrollbar->setX(getX() + getWidth() + _scrollPos);
}
}
/**
* Draws the text list and all the text contained within.
*/
void TextList::draw()
{
Surface::draw();
int y = 0;
if (!_rows.empty())
{
// for wrapped items, offset the draw height above the visible surface
// so that the correct row appears at the top
for (int row = _scroll; row > 0 && _rows[row] == _rows[row - 1]; --row)
{
y -= _font->getHeight() + _font->getSpacing();
}
for (size_t i = _rows[_scroll]; i < _texts.size() && i < _rows[_scroll] + _visibleRows; ++i)
{
for (std::vector<Text*>::iterator j = _texts[i].begin(); j < _texts[i].end(); ++j)
{
(*j)->setY(y);
(*j)->blit(this);
}
if (!_texts[i].empty())
{
y += _texts[i].front()->getHeight() + _font->getSpacing();
}
else
{
y += _font->getHeight() + _font->getSpacing();
}
}
}
}
/**
* Blits the text list and selector.
* @param surface Pointer to surface to blit onto.
*/
void TextList::blit(Surface *surface)
{
if (_visible && !_hidden)
{
_selector->blit(surface);
}
Surface::blit(surface);
if (_visible && !_hidden)
{
if (_arrowPos != -1 && !_rows.empty())
{
int y = getY();
for (int row = _scroll; row > 0 && _rows[row] == _rows[row - 1]; --row)
{
y -= _font->getHeight() + _font->getSpacing();
}
int maxY = getY() + getHeight();
for (size_t i = _rows[_scroll]; i < _texts.size() && i < _rows[_scroll] + _visibleRows && y < maxY; ++i)
{
_arrowLeft[i]->setY(y);
_arrowRight[i]->setY(y);
if (y >= getY())
{
// only blit arrows that belong to texts that have their first row on-screen
_arrowLeft[i]->blit(surface);
_arrowRight[i]->blit(surface);
}
if (!_texts[i].empty())
{
y += _texts[i].front()->getHeight() + _font->getSpacing();
}
else
{
y += _font->getHeight() + _font->getSpacing();
}
}
}
_up->blit(surface);
_down->blit(surface);
_scrollbar->blit(surface);
}
}
/**
* Passes events to arrow buttons.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void TextList::handle(Action *action, State *state)
{
InteractiveSurface::handle(action, state);
_up->handle(action, state);
_down->handle(action, state);
_scrollbar->handle(action, state);
if (_arrowPos != -1 && !_rows.empty())
{
size_t startArrowIdx = _rows[_scroll];
if (0 < _scroll && _rows[_scroll] == _rows[_scroll - 1])
{
// arrows for first partially-visible line of text are off-screen; don't process them
++startArrowIdx;
}
size_t endArrowIdx = _rows[_scroll] + 1;
size_t endRow = std::min(_rows.size(), _scroll + _visibleRows);
for (size_t i = _scroll + 1; i < endRow; ++i)
{
if (_rows[i] != _rows[i - 1])
{
++endArrowIdx;
}
}
for (size_t i = startArrowIdx; i < endArrowIdx; ++i)
{
_arrowLeft[i]->handle(action, state);
_arrowRight[i]->handle(action, state);
}
}
}
/**
* Passes ticks to arrow buttons.
*/
void TextList::think()
{
InteractiveSurface::think();
_up->think();
_down->think();
_scrollbar->think();
for (std::vector<ArrowButton*>::iterator i = _arrowLeft.begin(); i < _arrowLeft.end(); ++i)
{
(*i)->think();
}
for (std::vector<ArrowButton*>::iterator i = _arrowRight.begin(); i < _arrowRight.end(); ++i)
{
(*i)->think();
}
}
/**
* Ignores any mouse clicks that aren't on a row.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void TextList::mousePress(Action *action, State *state)
{
bool allowScroll = true;
if (Options::changeValueByMouseWheel != 0)
{
allowScroll = (action->getAbsoluteXMouse() < _arrowsLeftEdge || action->getAbsoluteXMouse() > _arrowsRightEdge);
}
if (allowScroll)
{
if (action->getDetails()->button.button == SDL_BUTTON_WHEELUP) scrollUp(false, true);
else if (action->getDetails()->button.button == SDL_BUTTON_WHEELDOWN) scrollDown(false, true);
}
if (_selectable)
{
if (_selRow < _rows.size())
{
InteractiveSurface::mousePress(action, state);
}
}
else
{
InteractiveSurface::mousePress(action, state);
}
}
/*
* Ignores any mouse clicks that aren't on a row.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void TextList::mouseRelease(Action *action, State *state)
{
if (_selectable)
{
if (_selRow < _rows.size())
{
InteractiveSurface::mouseRelease(action, state);
}
}
else
{
InteractiveSurface::mouseRelease(action, state);
}
}
/**
* Ignores any mouse clicks that aren't on a row.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void TextList::mouseClick(Action *action, State *state)
{
if (_selectable)
{
if (_selRow < _rows.size())
{
InteractiveSurface::mouseClick(action, state);
if (_comboBox && action->getDetails()->button.button == SDL_BUTTON_LEFT)
{
_comboBox->setSelected(_selRow);
_comboBox->toggle();
}
}
}
else
{
InteractiveSurface::mouseClick(action, state);
}
}
/**
* Selects the row the mouse is over.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void TextList::mouseOver(Action *action, State *state)
{
if (_selectable)
{
int rowHeight = _font->getHeight() + _font->getSpacing(); //theoretical line height
_selRow = std::max(0, (int)(_scroll + (int)floor(action->getRelativeYMouse() / (rowHeight * action->getYScale()))));
if (_selRow < _rows.size())
{
Text *selText = _texts[_rows[_selRow]].front();
int y = getY() + selText->getY();
int actualHeight = selText->getHeight() + _font->getSpacing(); //current line height
if (y < getY() || y + actualHeight > getY() + getHeight())
{
actualHeight /= 2;
}
if (y < getY())
{
y = getY();
}
if (_selector->getHeight() != actualHeight)
{
// resizing doesn't work, but recreating does, so let's do that!
delete _selector;
_selector = new Surface(getWidth(), actualHeight, getX(), y);
_selector->setPalette(getPalette());
}
_selector->setY(y);
_selector->copy(_bg);
if (_contrast)
{
_selector->offsetBlock(-5);
}
else if (_comboBox)
{
_selector->offset(+1, Palette::backPos);
}
else
{
_selector->offsetBlock(-10);
}
_selector->setVisible(true);
}
else
{
_selector->setVisible(false);
}
}
InteractiveSurface::mouseOver(action, state);
}
/**
* Deselects the row.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void TextList::mouseOut(Action *action, State *state)
{
if (_selectable)
{
_selector->setVisible(false);
}
InteractiveSurface::mouseOut(action, state);
}
/*
* get the scroll depth.
* @return scroll depth.
*/
size_t TextList::getScroll()
{
return _scroll;
}
/*
* set the scroll depth.
* @param scroll set the scroll depth to this.
*/
void TextList::scrollTo(size_t scroll)
{
if (!_scrolling)
return;
_scroll = Clamp(scroll, (size_t)(0), _rows.size() - _visibleRows);
draw(); // can't just set _redraw here because reasons
updateArrows();
}
/**
* Hooks up the button to work as part of an existing combobox,
* updating the selection when it's pressed.
* @param comboBox Pointer to combobox.
*/
void TextList::setComboBox(ComboBox *comboBox)
{
_comboBox = comboBox;
}
/**
* Gets the combobox that this list is attached to, if any.
* @return the attached combobox.
*/
ComboBox *TextList::getComboBox() const
{
return _comboBox;
}
void TextList::setBorderColor(Uint8 color)
{
_up->setColor(color);
_down->setColor(color);
_scrollbar->setColor(color);
}
int TextList::getScrollbarColor()
{
return _scrollbar->getColor();
}
void TextList::setFlooding(bool flooding)
{
_flooding = flooding;
}
}
↑ V768 The expression '_align[i]' is of enum type. It is odd that it is used as an expression of a Boolean-type.
↑ V1026 The 'y' variable is incremented in the loop. Undefined behavior will occur in case of signed integer overflow.
↑ V1026 The 'y' variable is incremented in the loop. Undefined behavior will occur in case of signed integer overflow.
↑ V1026 The 'y' variable is incremented in the loop. Undefined behavior will occur in case of signed integer overflow.
↑ V1026 The 'y' variable is incremented in the loop. Undefined behavior will occur in case of signed integer overflow.
↑ V524 It is odd that the body of 'setBorderColor' function is fully equivalent to the body of 'setArrowColor' function.