//---------------------------------------------------------------------------
#include "stdafx.h"
#include <cassert>
#include "panel.h"
#include "game.h"
#include "utils.h"
#include "sector.h"
#include "screen.h"
#include "image.h"
#include "sound.h"
using Gigalomania::Button;
using Gigalomania::ImageButton;
using Gigalomania::CycleButton;
using Gigalomania::MultiPanel;
//---------------------------------------------------------------------------
void registerClick() {
//LOG("registerClick()\n");
// call for gui items to be registered as a mouse click, rather than continuous press
playSample(game_g->s_guiclick, SOUND_CHANNEL_FX);
game_g->s_guiclick->setVolume(0.125f);
}
PanelPage::PanelPage(int offset_x,int offset_y) {
init_panelpage();
this->offset_x = offset_x;
this->offset_y = offset_y;
}
PanelPage::PanelPage(int offset_x,int offset_y,const char *infoLMB) {
init_panelpage();
this->offset_x = offset_x;
this->offset_y = offset_y;
this->setInfoLMB(infoLMB);
}
PanelPage::PanelPage(int offset_x,int offset_y,int w,int h) {
init_panelpage();
this->offset_x = offset_x;
this->offset_y = offset_y;
this->w = w;
this->h = h;
}
PanelPage::~PanelPage() {
if( owner != NULL )
owner->remove(this);
free(true);
delete children;
}
void PanelPage::init_panelpage() {
this->owner = NULL;
this->modal_child = NULL;
this->offset_x = 0;
this->offset_y = 0;
this->w = 0;
this->h = 0;
this->has_background = false;
for(int i=0;i<4;i++)
this->background[i] = 0;
this->tolerance = game_g->isMobileUI() ? 2 : 0;
this->visible = true;
this->enabled = true;
this->children = new vector<PanelPage *>();
this->popup_item = false;
this->popup_x = 0;
this->popup_y = 0;
this->is_inside_area = false;
this->inside_area_time = 0;
this->survive_owner = false;
this->helpTextOn = true;
}
void PanelPage::add(PanelPage *panel) {
this->children->push_back(panel);
panel->owner = this;
}
void PanelPage::remove(PanelPage *panel) {
if( panel == this->modal_child )
this->modal_child = NULL;
panel->owner = NULL;
remove_vec(this->children, panel);
}
int PanelPage::getLeft() const {
int parent_left = owner != NULL ? owner->getLeft() : 0;
return parent_left + offset_x;
}
int PanelPage::getTop() const {
int parent_top = owner != NULL ? owner->getTop() : 0;
return parent_top + offset_y;
}
int PanelPage::getRight() const {
return owner->getLeft() + offset_x + w;
}
int PanelPage::getXCentre() const {
return owner->getLeft() + offset_x + w/2;
}
int PanelPage::getYCentre() const {
return owner->getTop() + offset_y + h/2;
}
int PanelPage::getBottom() const {
return owner->getTop() + offset_y + h;
}
PanelPage *PanelPage::findById(const string &id) {
if( this->id == id )
return this;
for(int i=0;i<nChildren();i++) {
PanelPage *panel = get(i);
PanelPage *found = panel->findById(id);
if( found != NULL )
return found;
}
return NULL;
}
void PanelPage::setVisible(bool visible) {
this->visible = visible;
for(int i=0;i<nChildren();i++) {
PanelPage *panel = get(i);
panel->setVisible(visible);
}
}
void PanelPage::setEnabled(bool enabled) {
this->enabled = enabled;
for(int i=0;i<nChildren();i++) {
PanelPage *panel = get(i);
panel->setEnabled(enabled);
}
}
void PanelPage::setInfoLMB(const char *text) {
/*strncpy(infoLMB, text, GUI_MAX_STRING);
infoLMB[GUI_MAX_STRING] = '\0';*/
infoLMB = text;
}
void PanelPage::setInfoRMB(const char *text) {
/*strncpy(infoRMB, text, GUI_MAX_STRING);
infoRMB[GUI_MAX_STRING] = '\0';*/
infoRMB = text;
}
/*void PanelPage::setInfoBMB(const char *text) {
infoBMB = text;
}*/
const char *PanelPage::getInfoLMB() const {
//return ( *infoLMB == '\0' ) ? NULL : infoLMB;
return infoLMB.length() == 0 ? NULL : infoLMB.c_str();
}
const char *PanelPage::getInfoRMB() const {
//return ( *infoRMB == '\0' ) ? NULL : infoRMB;
return infoRMB.length() == 0 ? NULL : infoRMB.c_str();
}
/*const char *PanelPage::getInfoBMB() const {
return infoBMB.length() == 0 ? NULL : infoBMB.c_str();
}*/
void PanelPage::free(bool free_this) {
for(unsigned int i=0;i<children->size();i++) {
PanelPage *panel = children->at(i);
// panel should be non-NULL, but to satisfy VS Code Analysis...
if( panel != NULL && !panel->survive_owner ) {
if( free_this ) {
panel->owner = NULL; // this must be done before deletion!
delete panel;
}
else
panel->free(true);
}
}
if( free_this ) {
children->clear();
}
if( this->modal_child != NULL && free_this ) {
modal_child = NULL;
}
}
void PanelPage::drawPopups() {
bool touch_mode = game_g->isMobileUI() || game_g->getApplication()->isBlankMouse();
if( touch_mode ) {
return;
}
/*if( owner != NULL )
{
// DEBUG
int sx = (int)((owner->getLeft() + offset_x - tolerance) * game_g->getScaleWidth());
int sy = (int)((owner->getTop() + offset_y - tolerance) * game_g->getScaleHeight());
game_g->getScreen()->fillRect(sx, sy, (this->w+2*tolerance)*game_g->getScaleWidth(), (this->h+2*tolerance)*game_g->getScaleHeight(), 255, 0, 255);
}*/
// popup text
int m_x = 0, m_y = 0;
game_g->getScreen()->getMouseCoords(&m_x, &m_y);
PanelPage *panel = this;
const char *lmb_text = panel->getInfoLMB();
const char *rmb_text = panel->getInfoRMB();
//const char *bmb_text = panel->getInfoBMB();
if( is_inside_area ) {
if( !panel->mouseOver(m_x, m_y) ) {
is_inside_area = false;
inside_area_time = 0;
}
}
else {
if( panel->mouseOver(m_x, m_y) ) {
is_inside_area = true;
inside_area_time = game_g->getRealTime();
}
}
if( lmb_text != NULL || rmb_text != NULL /*|| bmb_text != NULL*/ ) {
if( panel->isHelpTextOn() && is_inside_area && game_g->getRealTime() < inside_area_time + 5000 ) {
if( !popup_item ) {
//LOG("create popup\n");
popup_item = true;
popup_x = (int)(m_x/game_g->getScaleWidth());
popup_y = (int)(m_y/game_g->getScaleHeight());
popup_x = (int)(popup_x*game_g->getScaleWidth());
popup_y = (int)(popup_y*game_g->getScaleHeight());
}
//LOG("draw popup\n");
{
int n_texts = 0;
/*const char *text[3] = {NULL, NULL, NULL};
int mice_indx[3] = {-1, -1, -1};*/
const char *text[3] = {NULL, NULL};
int mice_indx[3] = {-1, -1};
if( rmb_text != NULL ) {
text[n_texts] = rmb_text;
mice_indx[n_texts] = 1;
n_texts++;
}
if( lmb_text != NULL ) {
text[n_texts] = lmb_text;
mice_indx[n_texts] = 0;
n_texts++;
}
/*if( bmb_text != NULL ) {
text[n_texts] = bmb_text;
mice_indx[n_texts] = 2;
n_texts++;
}*/
int w = game_g->letters_small[0]->getScaledWidth();
int h = game_g->letters_small[0]->getScaledHeight() + 2;
int off_x = (int)(popup_x/game_g->getScaleWidth() + 24);
int gap_left = 20;
int gap_right = 4;
int gap_y = 4;
int between_lines_y = 2;
int between_texts_y = 4;
bool one_line[3];
int n_lines[3];
int total_lines = 0;
int max_wid = 0;
for(int j=0;j<n_texts;j++) {
int this_max_wid = 0;
textLines(&n_lines[j], &this_max_wid, text[j], w, w);
if( this_max_wid > max_wid )
max_wid = this_max_wid;
one_line[j] = n_lines[j] == 1;
if( one_line[j] )
n_lines[j] = 2;
total_lines += n_lines[j];
}
int rect_x = (int)(off_x * game_g->getScaleWidth());
int rect_y = (int)(popup_y - gap_y * game_g->getScaleHeight());
int rect_w = (int)(( max_wid + gap_left + gap_right ) * game_g->getScaleWidth());
int rect_h = (int)(( 2 * gap_y + h * total_lines +
between_lines_y * ( total_lines - 1 ) +
( between_texts_y - between_lines_y ) * ( n_texts - 1 ) ) * game_g->getScaleHeight());
if( rect_x + rect_w >= default_width_c * game_g->getScaleWidth() ) {
off_x = (int)(default_width_c - 8 - rect_w/game_g->getScaleWidth());
rect_x = (int)(off_x * game_g->getScaleWidth());
// also adjust y
int new_y = (int)(panel->getBottom() * game_g->getScaleHeight());
if( new_y + rect_h >= default_height_c * game_g->getScaleHeight() ) {
new_y = (int)(panel->getTop() * game_g->getScaleHeight() - rect_h);
}
popup_y += new_y - rect_y;
rect_y = new_y;
}
else if( rect_y + rect_h >= default_height_c * game_g->getScaleHeight() ) {
int new_y = (int)(( default_height_c - 1 ) * game_g->getScaleHeight() - rect_h);
popup_y += new_y - rect_y;
rect_y = new_y;
}
rect_x++;
rect_y++;
rect_w -= 2;
rect_h -= 2;
const unsigned char panel_background_r = 128;
const unsigned char panel_background_g = 128;
const unsigned char panel_background_b = 128;
const unsigned char panel_background_a = 160;
#if SDL_MAJOR_VERSION == 1
Image *fill_rect = Image::createBlankImage(rect_w, rect_h, 24);
fill_rect->fillRect(0, 0, rect_w, rect_h, panel_background_r, panel_background_g, panel_background_b);
fill_rect->convertToDisplayFormat();
fill_rect->drawWithAlpha(rect_x, rect_y, panel_background_a);
delete fill_rect;
#else
game_g->getScreen()->fillRectWithAlpha(rect_x, rect_y, rect_w, rect_h, panel_background_r, panel_background_g, panel_background_b, panel_background_a);
#endif
int py = (int)(popup_y/game_g->getScaleHeight());
for(int j=0;j<n_texts;j++) {
game_g->icon_mice[mice_indx[j]]->draw(off_x + 4, py);
Image::write(off_x + gap_left, py + (one_line[j] ? h/2 : 0), game_g->letters_small, text[j], Image::JUSTIFY_LEFT);
py += n_lines[j] * h + between_texts_y;
}
}
}
else
popup_item = false;
}
else
popup_item = false;
for(unsigned int i=0;i<children->size();i++) {
PanelPage *panel = children->at(i);
panel->drawPopups();
}
}
void PanelPage::drawBackground() {
if( this->has_background && this->visible ) {
int rect_x = (int)(game_g->getScaleWidth()*(owner->getLeft() + offset_x));
int rect_y = (int)(game_g->getScaleWidth()*(owner->getTop() + offset_y));
int rect_w = (int)(game_g->getScaleWidth()*this->w);
int rect_h = (int)(game_g->getScaleHeight()*this->h);
#if SDL_MAJOR_VERSION == 1
Image *fill_rect = Image::createBlankImage(rect_w, rect_h, 24);
fill_rect->fillRect(0, 0, rect_w, rect_h, background[0], background[1], background[2]);
fill_rect->convertToDisplayFormat();
fill_rect->drawWithAlpha(rect_x, rect_y, background[3]);
delete fill_rect;
#else
game_g->getScreen()->fillRectWithAlpha((short)rect_x, (short)rect_y, (short)rect_w, (short)rect_h, background[0], background[1], background[2], background[3]);
#endif
}
}
void PanelPage::drawForeground() {
for(unsigned int i=0;i<children->size();i++) {
PanelPage *panel = children->at(i);
panel->draw();
}
this->drawPopups();
}
void PanelPage::draw() {
this->drawBackground();
this->drawForeground();
}
bool PanelPage::mouseOver(int m_x,int m_y) const {
if( visible && enabled &&
m_x >= ( this->getLeft() - tolerance ) * game_g->getScaleWidth() &&
m_x < ( this->getLeft() + w + tolerance ) * game_g->getScaleWidth() &&
m_y >= ( this->getTop() - tolerance ) * game_g->getScaleHeight() &&
m_y < ( this->getTop() + h + tolerance ) * game_g->getScaleHeight() ) {
return true;
}
return false;
}
void PanelPage::input(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
// mouseOver check disabled, as PanelPages currently have 0 width and height
/*if( !mouseOver(m_x, m_y) ) {
return;
}*/
if( this->modal_child != NULL ) {
this->modal_child->input(m_x, m_y, m_left, m_middle, m_right, click);
return;
}
for(unsigned int i=0;i<children->size();i++) {
PanelPage *panel = children->at(i);
panel->input(m_x, m_y, m_left, m_middle, m_right, click);
}
}
/*Button::Button(int x,int y,Image *image) : PanelPage(x, y) {
init_button();
this->image = image;
this->w = image->getScaledWidth();
this->h = image->getScaledHeight();
}
Button::Button(int x,int y,Image *image,char *infoLMB) : PanelPage(x, y, infoLMB) {
init_button();
this->image = image;
this->w = image->getScaledWidth();
this->h = image->getScaledHeight();
}
Button::Button(int x,int y,int w, int h,Image *image) : PanelPage(x, y) {
init_button();
this->w = w;
this->h = h;
this->image = image;
}*/
Button::Button(int x,int y,const char *text,Gigalomania::Image *font[]) : PanelPage(x, y) {
this->text = text;
this->font = font;
this->w = font[0]->getScaledWidth() * this->text.length() + 2;
this->h = font[0]->getScaledHeight() + 2;
this->draw_offset_x = 0;
if( game_g->isMobileUI() ) {
this->tolerance += 4;
//this->h += 8; // useful for Android, where touches often seem to register lower than I seem to expect
}
}
Button::Button(int x,int y,int h,const char *text,Gigalomania::Image *font[]) : PanelPage(x, y) {
this->text = text;
this->font = font;
this->w = font[0]->getScaledWidth() * this->text.length() + 2;
this->h = h;
this->draw_offset_x = 0;
if( game_g->isMobileUI() ) {
this->tolerance += 4;
//this->h += 8; // useful for Android, where touches often seem to register lower than I seem to expect
}
}
Button::Button(int x,int y,int draw_offset_x,int h,const char *text,Gigalomania::Image *font[]) : PanelPage(x, y) {
this->text = text;
this->font = font;
this->w = draw_offset_x + font[0]->getScaledWidth() * this->text.length() + 2;
this->h = h;
this->draw_offset_x = draw_offset_x;
if( game_g->isMobileUI() ) {
this->tolerance += 4;
//this->h += 8; // useful for Android, where touches often seem to register lower than I seem to expect
}
}
Button::~Button() {
}
void Button::draw() {
this->drawBackground();
if( visible ) {
//LOG("write: %s\n", this->text.c_str());
/*{
// DEBUG
int sx = (int)((owner->getLeft() + offset_x - tolerance) * game_g->getScaleWidth());
int sy = (int)((owner->getTop() + offset_y - tolerance) * game_g->getScaleHeight());
game_g->getScreen()->fillRect(sx, sy, (this->w+2*tolerance)*game_g->getScaleWidth(), (this->h+2*tolerance)*game_g->getScaleHeight(), 255, 0, 255);
}*/
Image::writeMixedCase(owner->getLeft() + offset_x + draw_offset_x + 1, owner->getTop() + offset_y + 1, this->font, this->font, game_g->numbers_yellow, this->text.c_str(), Image::JUSTIFY_LEFT);
}
this->drawForeground();
}
ImageButton::ImageButton(int x,int y,const Gigalomania::Image *image) : PanelPage(x, y) {
ASSERT(image != NULL);
this->image = image;
/*this->has_alpha = false;
this->alpha = 255;*/
this->w = image->getScaledWidth();
this->h = image->getScaledHeight();
}
ImageButton::ImageButton(int x,int y,const Gigalomania::Image *image,const char *infoLMB) : PanelPage(x, y, infoLMB) {
ASSERT(image != NULL);
this->image = image;
/*this->has_alpha = false;
this->alpha = 255;*/
this->w = image->getScaledWidth();
this->h = image->getScaledHeight();
}
ImageButton::ImageButton(int x,int y,int w, int h,const Gigalomania::Image *image) : PanelPage(x, y) {
ASSERT(image != NULL);
this->image = image;
/*this->has_alpha = false;
this->alpha = 255;*/
this->w = w;
this->h = h;
}
ImageButton::ImageButton(int x,int y,int w, int h,const Gigalomania::Image *image,const char *infoLMB) : PanelPage(x, y, infoLMB) {
ASSERT(image != NULL);
this->image = image;
/*this->has_alpha = false;
this->alpha = 255;*/
this->w = w;
this->h = h;
}
ImageButton::~ImageButton() {
}
void ImageButton::draw() {
this->drawBackground();
if( visible && image != NULL ) {
//LOG("imagebutton: %d, %d\n", owner->getLeft() + offset_x, owner->getTop() + offset_y);
/*{
// DEBUG
int sx = (int)((owner->getLeft() + offset_x - tolerance) * game_g->getScaleWidth());
int sy = (int)((owner->getTop() + offset_y - tolerance) * game_g->getScaleHeight());
game_g->getScreen()->fillRect(sx, sy, (this->w+2*tolerance)*game_g->getScaleWidth(), (this->h+2*tolerance)*game_g->getScaleHeight(), 255, 0, 255);
}*/
/*if( has_alpha ) {
image->drawWithAlpha(owner->getLeft() + offset_x, owner->getTop() + offset_y, alpha);
}
else {
image->draw(owner->getLeft() + offset_x, owner->getTop() + offset_y, true);
}*/
image->draw(owner->getLeft() + offset_x, owner->getTop() + offset_y);
}
this->drawForeground();
}
CycleButton::CycleButton(int x,int y,const char *texts[],int n_texts,Gigalomania::Image *font[]) : PanelPage(x, y) {
ASSERT( n_texts > 0 );
this->n_texts = n_texts;
this->texts = new char *[n_texts];
int max_len = 0;
for(int i=0;i<n_texts;i++) {
int len = strlen(texts[i]);
if( len > max_len )
max_len = len;
this->texts[i] = new char[ len + 1 ];
strncpy(this->texts[i], texts[i], len);
(this->texts[i])[len] = '\0';
}
this->font = font;
this->w = font[0]->getScaledWidth() * max_len + 2;
this->h = font[0]->getScaledHeight() + 2;
this->active = 0;
if( game_g->isMobileUI() ) {
this->tolerance += 4;
//this->h += 8; // useful for Android, where touches often seem to register lower than I seem to expect
}
}
CycleButton::~CycleButton() {
for(int i=0;i<n_texts;i++) {
delete [] (texts[i]);
}
delete [] texts;
}
void CycleButton::draw() {
this->drawBackground();
if( visible ) {
/*{
// DEBUG
int sx = (int)((owner->getLeft() + offset_x - tolerance) * game_g->getScaleWidth());
int sy = (int)((owner->getTop() + offset_y - tolerance) * game_g->getScaleHeight());
game_g->getScreen()->fillRect(sx, sy, (this->w+2*tolerance)*game_g->getScaleWidth(), (this->h+2*tolerance)*game_g->getScaleHeight(), 255, 0, 255);
}*/
Gigalomania::Image::write(owner->getLeft() + offset_x + 1, owner->getTop() + offset_y + 1, this->font, this->texts[ this->active ], Image::JUSTIFY_LEFT);
}
this->drawForeground();
}
void CycleButton::setActive(int active) {
ASSERT( active >= 0 && active < n_texts );
this->active = active;
}
void CycleButton::input(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
if( this->mouseOver(m_x, m_y) && m_left && click ) {
registerClick();
this->active++;
if( this->active == this->n_texts )
this->active = 0;
}
PanelPage::input(m_x, m_y, m_left, m_middle, m_right, click);
}
MultiPanel::MultiPanel(int n_pages, int x, int y) : PanelPage(x, y) {
int i;
//this->n_pages = n_pages;
//this->pages = new PanelPage *[n_pages];
for(i=0;i<n_pages;i++) {
/*this->pages[i] = new PanelPage(0, 0);
this->add( this->pages[i] );*/
this->add( new PanelPage(0, 0) );
//this->get(i)->owner = this;
}
this->c_page = 0;
}
MultiPanel::~MultiPanel() {
this->free(true);
//delete [] this->pages;
}
/*void MultiPanel::free() {
for(int i=0;i<this->nChildren();i++)
this->get(i)->free();
}
*/
void MultiPanel::addToPanel(int page,PanelPage *panel) {
//this->pages[page]->add(panel);
this->get(page)->add(panel);
}
/*void MultiPanel::drawPopups() {
this->get(this->c_page)->drawPopups();
}*/
void MultiPanel::draw() {
//this->pages[this->c_page]->draw();
this->get(this->c_page)->draw();
}
void MultiPanel::input(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
//this->pages[this->c_page]->input(m_x, m_y, m_b);
this->get(this->c_page)->input(m_x, m_y, m_left, m_middle, m_right, click);
}