colobot/src/ui/list.cpp

802 lines
19 KiB
C++

// * This file is part of the COLOBOT source code
// * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch
// * Copyright (C) 2012 Polish Portal of Colobot (PPC)
// *
// * This program 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.
// *
// * This program 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 this program. If not, see http://www.gnu.org/licenses/.
// list.cpp
#include <ui/list.h>
namespace Ui {
const float MARGING = 4.0f;
// Object's constructor.
CList::CList() : CControl()
{
for (int i = 0; i < LISTMAXDISPLAY; i++)
m_button[i] = nullptr;
m_scroll = nullptr;
for (int i = 0; i < LISTMAXTOTAL; i++) {
m_text[i][0] = 0;
m_check[i] = false;
m_enable[i] = true;
}
for (int i = 0; i < 10; i++) {
m_tabs[i] = 0.0f;
m_justifs[i] = Gfx::TEXT_ALIGN_RIGHT;
}
m_totalLine = 0;
m_displayLine = 0;
m_selectLine = -1;
m_firstLine = 0;
m_bBlink = false;
m_bSelectCap = true;
m_blinkTime = 0.0f;
}
// Object's destructor.
CList::~CList()
{
for (int i = 0; i < LISTMAXDISPLAY; i++) {
if (m_button[i] != nullptr)
delete m_button[i];
}
if (m_scroll != nullptr)
delete m_scroll;
}
// Creates a new list.
bool CList::Create(Math::Point pos, Math::Point dim, int icon, EventType eventMsg, float expand)
{
m_expand = expand;
if (eventMsg == EVENT_NULL)
eventMsg = GetUniqueEventType();
CControl::Create(pos, dim, icon, eventMsg);
m_scroll = new CScroll();
m_scroll->Create(pos, dim, 0, EVENT_NULL);
m_eventScroll = m_scroll->GetEventType();
return MoveAdjust();
}
// Adjusted after a change of dimensions.
bool CList::MoveAdjust()
{
Math::Point ipos, idim, ppos, ddim;
float marging, h;
for (int i = 0; i < LISTMAXDISPLAY; i++) {
if (m_button[i] != nullptr) {
delete m_button[i];
m_button[i] = nullptr;
}
}
if (m_icon == 0)
marging = MARGING;
else
marging = 0.0f;
ipos.x = m_pos.x + marging / 640.f;
ipos.y = m_pos.y + marging / 480.f;
idim.x = m_dim.x - marging * 2.0f / 640.f;
idim.y = m_dim.y - marging * 2.0f / 480.f;
h = m_engine->GetText()->GetHeight(m_fontType, m_fontSize) * m_expand;
m_displayLine = static_cast<int>(idim.y / h);
if (m_displayLine == 0)
return false;
if (m_displayLine > LISTMAXDISPLAY)
m_displayLine = LISTMAXDISPLAY;
idim.y = h * m_displayLine;
m_dim.y = idim.y + marging * 2.0f / 480.f;
ppos.x = ipos.x;
ppos.y = ipos.y + idim.y - h;
ddim.x = idim.x - SCROLL_WIDTH;
ddim.y = h;
for (int i = 0; i < m_displayLine; i++) {
m_button[i] = new CButton();
m_button[i]->Create(ppos, ddim, -1, EVENT_NULL);
m_button[i]->SetTextAlign(Gfx::TEXT_ALIGN_RIGHT);
m_button[i]->SetState(STATE_SIMPLY);
m_button[i]->SetFontType(m_fontType);
m_button[i]->SetFontSize(m_fontSize);
ppos.y -= h;
m_eventButton[i] = m_button[i]->GetEventType();
}
if ( m_scroll != nullptr ) {
ppos.x = ipos.x + idim.x - SCROLL_WIDTH;
ppos.y = ipos.y;
ddim.x = SCROLL_WIDTH;
ddim.y = idim.y;
m_scroll->SetPos(ppos);
m_scroll->SetDim(ddim);
}
UpdateScroll();
UpdateButton();
return true;
}
// Returns the message of a button.
EventType CList::GetEventMsgButton(int i)
{
if (i < 0 || i >= m_displayLine)
return EVENT_NULL;
if (m_button[i] == nullptr)
return EVENT_NULL;
return m_button[i]->GetEventType();
}
// Returns the message from the elevator.
EventType CList::GetEventMsgScroll()
{
if (m_scroll == nullptr)
return EVENT_NULL;
return m_scroll->GetEventType();
}
void CList::SetPos(Math::Point pos)
{
CControl::SetPos(pos);
}
void CList::SetDim(Math::Point dim)
{
m_dim = dim;
MoveAdjust();
CControl::SetDim(dim);
}
bool CList::SetState(int state, bool bState)
{
if (state & STATE_ENABLE) {
for (int i = 0; i < m_displayLine; i++) {
if (m_button[i] != nullptr)
m_button[i]->SetState(state, bState);
}
if (m_scroll != nullptr)
m_scroll->SetState(state, bState);
}
return CControl::SetState(state, bState);
}
bool CList::SetState(int state)
{
if (state & STATE_ENABLE) {
for (int i = 0; i < m_displayLine; i++) {
if (m_button[i] != nullptr)
m_button[i]->SetState(state);
}
if (m_scroll != nullptr)
m_scroll->SetState(state);
}
return CControl::SetState(state);
}
bool CList::ClearState(int state)
{
if (state & STATE_ENABLE) {
for (int i = 0; i < m_displayLine; i++) {
if (m_button[i] != nullptr)
m_button[i]->ClearState(state);
}
if (m_scroll != nullptr)
m_scroll->ClearState(state);
}
return CControl::ClearState(state);
}
// Management of an event.
bool CList::EventProcess(const Event &event)
{
int i;
if (m_bBlink && event.type == EVENT_FRAME) {
i = m_selectLine-m_firstLine;
if (i >= 0 && i < 4 && m_button[i] != nullptr) {
m_blinkTime += event.rTime;
if (Math::Mod(m_blinkTime, 0.7f) < 0.3f) {
m_button[i]->ClearState(STATE_ENABLE);
m_button[i]->ClearState(STATE_CHECK);
} else {
m_button[i]->SetState(STATE_ENABLE);
m_button[i]->SetState(STATE_CHECK);
}
}
}
if ((m_state & STATE_VISIBLE) == 0)
return true;
if ((m_state & STATE_ENABLE) == 0)
return true;
if (event.type == EVENT_KEY_DOWN && event.mouseButton.button == 5 && Detect(event.pos)) {
if (m_firstLine > 0)
m_firstLine--;
UpdateScroll();
UpdateButton();
return true;
}
if (event.type == EVENT_KEY_DOWN && event.mouseButton.button == 4 && Detect(event.pos)) {
if (m_firstLine < m_totalLine - m_displayLine)
m_firstLine++;
UpdateScroll();
UpdateButton();
return true;
}
CControl::EventProcess(event);
if (event.type == EVENT_MOUSE_MOVE && Detect(event.pos)) {
m_engine->SetMouseType(Gfx::ENG_MOUSE_NORM);
for (i = 0; i < m_displayLine; i++) {
if (i + m_firstLine >= m_totalLine)
break;
if (m_button[i] != nullptr)
m_button[i]->EventProcess(event);
}
}
if (m_bSelectCap) {
for (i = 0; i < m_displayLine; i++) {
if (i + m_firstLine >= m_totalLine)
break;
if (m_button[i] != nullptr) {
if (!m_button[i]->EventProcess(event))
return false;
if (event.type == m_eventButton[i]) {
SetSelect(m_firstLine + i);
Event newEvent = event;
newEvent.type = m_eventType;
m_event->AddEvent(newEvent); // selected line changes
}
}
}
}
if (m_scroll != nullptr) {
if (!m_scroll->EventProcess(event))
return false;
if (event.type == m_eventScroll) {
MoveScroll();
UpdateButton();
}
}
return true;
}
// Draws the list.
void CList::Draw()
{
Math::Point uv1, uv2, corner, pos, dim, ppos, ddim;
float dp;
int i;
char text[100];
char *pb, *pe;
if ((m_state & STATE_VISIBLE) == 0)
return;
if (m_state & STATE_SHADOW)
DrawShadow(m_pos, m_dim);
dp = 0.5f / 256.0f;
if (m_icon != -1) {
dim = m_dim;
if (m_icon == 0) {
m_engine->SetTexture("button2.tga");
m_engine->SetState(Gfx::ENG_RSTATE_NORMAL);
uv1.x = 128.0f / 256.0f;
uv1.y = 64.0f / 256.0f; // u-v texture
uv2.x = 160.0f / 256.0f;
uv2.y = 96.0f / 256.0f;
} else {
m_engine->SetTexture("button2.tga");
m_engine->SetState(Gfx::ENG_RSTATE_NORMAL);
uv1.x = 132.0f / 256.0f;
uv1.y = 68.0f / 256.0f; // u-v texture
uv2.x = 156.0f / 256.0f;
uv2.y = 92.0f / 256.0f;
if (m_button[0] != nullptr) {
dim = m_button[0]->GetDim();
dim.y *= m_displayLine; // background sounds spot behind
}
}
uv1.x += dp;
uv1.y += dp;
uv2.x -= dp;
uv2.y -= dp;
corner.x = 10.0f / 640.0f;
corner.y = 10.0f / 480.0f;
DrawIcon(m_pos, dim, uv1, uv2, corner, 8.0f / 256.0f);
}
if ( m_totalLine < m_displayLine ) { // no buttons to the bottom?
i = m_totalLine;
if ( m_button[i] != 0 ) {
pos = m_button[i]->GetPos();
dim = m_button[i]->GetDim();
pos.y += dim.y * 1.1f;
dim.y *= 0.4f;
pos.y -= dim.y;
m_engine->SetTexture("button2.tga");
m_engine->SetState(Gfx::ENG_RSTATE_TTEXTURE_WHITE); // was D3DSTATETTw
uv1.x = 120.0f / 256.0f;
uv1.y = 64.0f / 256.0f;
uv2.x = 128.0f / 256.0f;
uv2.y = 48.0f / 256.0f;
uv1.x += dp;
uv1.y -= dp;
uv2.x -= dp;
uv2.y += dp;
DrawIcon(pos, dim, uv1, uv2); // ch'tite shadow cute (?)
}
}
for (i = 0; i < m_displayLine; i++) {
if ( i + m_firstLine >= m_totalLine )
break;
if ( m_button[i] != nullptr ) {
if ( !m_bBlink && i + m_firstLine < m_totalLine )
m_button[i]->SetState(STATE_ENABLE, m_enable[i+m_firstLine] && (m_state & STATE_ENABLE) );
m_button[i]->Draw(); // draws a box without text
// draws text in the box
pos = m_button[i]->GetPos();
dim = m_button[i]->GetDim();
if ( m_tabs[0] == 0.0f ) {
ppos.x = pos.x + dim.y * 0.5f;
ppos.y = pos.y + dim.y * 0.5f;
ppos.y -= m_engine->GetText()->GetHeight(m_fontType, m_fontSize) / 2.0f;
ddim.x = dim.x-dim.y;
DrawCase(m_text[i + m_firstLine], ppos, ddim.x, Gfx::TEXT_ALIGN_RIGHT);
} else {
ppos.x = pos.x + dim.y * 0.5f;
ppos.y = pos.y + dim.y * 0.5f;
ppos.y -= m_engine->GetText()->GetHeight(m_fontType, m_fontSize) / 2.0f;
pb = m_text[i + m_firstLine];
for (int j = 0; j < 10; j++) {
pe = strchr(pb, '\t');
if ( pe == 0 )
strcpy(text, pb);
else {
strncpy(text, pb, pe - pb);
text[pe - pb] = 0;
}
DrawCase(text, ppos, m_tabs[j], m_justifs[j]);
if ( pe == 0 )
break;
ppos.x += m_tabs[j];
pb = pe + 1;
}
}
if ( (m_state & STATE_EXTEND) && i < m_totalLine) {
pos = m_button[i]->GetPos();
dim = m_button[i]->GetDim();
pos.x += dim.x - dim.y * 0.75f;
dim.x = dim.y * 0.75f;
pos.x += 2.0f / 640.0f;
pos.y += 2.0f / 480.0f;
dim.x -= 4.0f / 640.0f;
dim.y -= 4.0f / 480.0f;
if ( m_check[i + m_firstLine] ) {
m_engine->SetTexture("button1.tga");
m_engine->SetState(Gfx::ENG_RSTATE_NORMAL);
uv1.x = 64.0f / 256.0f;
uv1.y = 0.0f / 256.0f;
uv2.x = 96.0f / 256.0f;
uv2.y = 32.0f / 256.0f;
uv1.x += dp;
uv1.y += dp;
uv2.x -= dp;
uv2.y -= dp;
DrawIcon(pos, dim, uv1, uv2); // square shape
m_engine->SetState(Gfx::ENG_RSTATE_TTEXTURE_WHITE); // was D3DSTATETTw
uv1.x = 0.0f / 256.0f; // v
uv1.y = 64.0f / 256.0f;
uv2.x = 32.0f / 256.0f;
uv2.y = 96.0f / 256.0f;
uv1.x += dp;
uv1.y += dp;
uv2.x -= dp;
uv2.y -= dp;
DrawIcon(pos, dim, uv1, uv2); // draws v
} else {
m_engine->SetTexture("button1.tga");
m_engine->SetState(Gfx::ENG_RSTATE_TTEXTURE_WHITE); // was D3DSTATETTw
if ( i + m_firstLine == m_selectLine ) {
uv1.x =224.0f / 256.0f; // <
uv1.y =192.0f / 256.0f;
uv2.x =256.0f / 256.0f;
uv2.y =224.0f / 256.0f;
} else {
uv1.x = 96.0f / 256.0f; // x
uv1.y = 32.0f / 256.0f;
uv2.x =128.0f / 256.0f;
uv2.y = 64.0f / 256.0f;
}
uv1.x += dp;
uv1.y += dp;
uv2.x -= dp;
uv2.y -= dp;
DrawIcon(pos, dim, uv1, uv2); // draws x
}
}
}
}
if ( m_scroll != 0 )
m_scroll->Draw(); // draws the lift
}
// Displays text in a box.
void CList::DrawCase(char *text, Math::Point pos, float width, Gfx::TextAlign justif)
{
if (justif == Gfx::TEXT_ALIGN_CENTER)
pos.x += width / 2.0f;
else if (justif == Gfx::TEXT_ALIGN_LEFT)
pos.x += width;
m_engine->GetText()->DrawText(std::string(text), m_fontType, m_fontSize, pos, width, justif, 0);
}
// Empty the list completely.
void CList::Flush()
{
m_totalLine = 0;
m_selectLine = -1;
m_firstLine = 0;
UpdateButton();
UpdateScroll();
}
// Specifies the total number of lines.
void CList::SetTotal(int i)
{
m_totalLine = i;
}
// Returns the total number of lines.
int CList::GetTotal()
{
return m_totalLine;
}
// Selects a line.
void CList::SetSelect(int i)
{
if ( m_bSelectCap )
m_selectLine = i;
else {
m_firstLine = i;
UpdateScroll();
}
UpdateButton();
}
// Returns the selected line.
int CList::GetSelect()
{
if ( m_bSelectCap )
return m_selectLine;
else
return m_firstLine;
}
// Management of capability has a select box.
void CList::SetSelectCap(bool bEnable)
{
m_bSelectCap = bEnable;
}
bool CList::GetSelectCap()
{
return m_bSelectCap;
}
// Blink a line.
void CList::SetBlink(bool bEnable)
{
int i;
m_bBlink = bEnable;
m_blinkTime = 0.0f;
i = m_selectLine-m_firstLine;
if (i >= 0 && i < 4 && m_button[i] != nullptr) {
if ( !bEnable ) {
m_button[i]->SetState(STATE_CHECK);
m_button[i]->ClearState(STATE_ENABLE);
}
}
}
bool CList::GetBlink()
{
return m_bBlink;
}
// Specifies the text of a line.
void CList::SetName(int i, char* name)
{
if ( i < 0 || i >= LISTMAXTOTAL )
return;
if ( i >= m_totalLine )
m_totalLine = i+1; // expands the list
if ( name[0] == 0 )
strcpy(m_text[i], " ");
else
strcpy(m_text[i], name);
UpdateButton();
UpdateScroll();
}
// Returns the text of a line.
char* CList::GetName(int i)
{
if ( i < 0 || i >= m_totalLine )
return 0;
return m_text[i];
}
// Specifies the bit "check" for a box.
void CList::SetCheck(int i, bool bMode)
{
if ( i < 0 || i >= m_totalLine )
return;
m_check[i] = bMode;
}
// Returns the bit "check" for a box.
bool CList::GetCheck(int i)
{
if ( i < 0 || i >= m_totalLine )
return false;
return m_check[i];
}
// Specifies the bit "enable" for a box.
void CList::SetEnable(int i, bool bMode)
{
if ( i < 0 || i >= m_totalLine )
return;
m_enable[i] = bMode;
}
// Returns the bit "enable" for a box.
bool CList::GetEnable(int i)
{
if ( i < 0 || i >= m_totalLine )
return false;
return m_enable[i];
}
// Management of the position of the tabs.
void CList::SetTabs(int i, float pos, Gfx::TextAlign justif)
{
if ( i < 0 || i >= 10 )
return;
m_tabs[i] = pos;
m_justifs[i] = justif;
}
float CList::GetTabs(int i)
{
if ( i < 0 || i >= 10 )
return 0.0f;
return m_tabs[i];
}
// Moves the lift to see the list of the selected line.
void CList::ShowSelect(bool bFixed)
{
int sel;
if ( bFixed && m_selectLine >= m_firstLine && m_selectLine < m_firstLine+m_displayLine )
return; // all good
sel = m_selectLine;
// Down from 1/2 * h.
sel += m_displayLine / 2;
if ( sel > m_totalLine - 1 )
sel = m_totalLine - 1;
// Back to h-1.
sel -= m_displayLine - 1;
if ( sel < 0 )
sel = 0;
m_firstLine = sel;
UpdateButton();
UpdateScroll();
}
// Updates all button names.
void CList::UpdateButton()
{
int state, i, j;
state = CControl::GetState();
j = m_firstLine;
for (i = 0; i < m_displayLine; i++) {
if (m_button[i] == nullptr)
continue;
m_button[i]->SetState(STATE_CHECK, (j == m_selectLine));
if ( j < m_totalLine ) {
//? m_button[i]->SetName(m_text[j]);
m_button[i]->SetName(" "); // blank button
m_button[i]->SetState(STATE_ENABLE, (state & STATE_ENABLE));
} else {
m_button[i]->SetName(" "); // blank button
m_button[i]->ClearState(STATE_ENABLE);
}
j ++;
}
}
// Updates the lift.
void CList::UpdateScroll()
{
float ratio, value, step;
if (m_scroll == nullptr)
return;
if (m_totalLine <= m_displayLine) {
ratio = 1.0f;
value = 0.0f;
step = 0.0f;
} else {
ratio = (float)m_displayLine / m_totalLine;
if ( ratio > 1.0f ) ratio = 1.0f;
value = (float)m_firstLine / (m_totalLine - m_displayLine);
if ( value < 0.0f )
value = 0.0f;
if ( value > 1.0f )
value = 1.0f;
step = (float)1.0f/ (m_totalLine - m_displayLine);
if ( step < 0.0f )
step = 0.0f;
}
m_scroll->SetVisibleRatio(ratio);
m_scroll->SetVisibleValue(value);
m_scroll->SetArrowStep(step);
}
// Update when the lift is moving.
void CList::MoveScroll()
{
float pos;
int n;
if ( m_scroll == 0 )
return;
n = m_totalLine - m_displayLine;
pos = m_scroll->GetVisibleValue();
pos += m_scroll->GetArrowStep() / 2.0f; // it's magic!
m_firstLine = (int)(pos * n);
if ( m_firstLine < 0 )
m_firstLine = 0;
if ( m_firstLine > n )
m_firstLine = n;
}
}