mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
2797 lines
70 KiB
C++
2797 lines
70 KiB
C++
/*
|
|
* wmcgui.cpp
|
|
* created by WMCoolmon
|
|
*
|
|
* You may not sell or otherwise commercially exploit the source or things you
|
|
* create based on the source.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "freespace.h"
|
|
#include "graphics/2d.h"
|
|
#include "hud/hudbrackets.h"
|
|
#include "io/key.h"
|
|
#include "lab/wmcgui.h"
|
|
#include "localization/localize.h"
|
|
#include "parse/parselo.h"
|
|
|
|
//Gobals
|
|
GUISystem GUI_system;
|
|
|
|
//*****************************ClassInfoEntry*******************************
|
|
ClassInfoEntry::ClassInfoEntry()
|
|
{
|
|
CIEType = CIE_NONE;
|
|
|
|
for (uint i = 0; i < CIE_NUM_HANDLES; i++) {
|
|
IMG_HANDLE_SET_INVALID(Handles[i].Image);
|
|
}
|
|
|
|
Coords[0] = Coords[1] = INT_MAX;
|
|
}
|
|
ClassInfoEntry::~ClassInfoEntry()
|
|
{
|
|
//Unload image handles
|
|
if (CIEType == CIE_IMAGE_NMCSD || CIEType == CIE_IMAGE || CIEType == CIE_IMAGE_BORDER) {
|
|
int i = 0;
|
|
int num = sizeof(Handle)/sizeof(int);
|
|
for (; i < num; i++) {
|
|
if (IMG_HANDLE_IS_VALID(Handles[i].Image)) {
|
|
IMG_UNLOAD(Handles[i].Image);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ObjectClassInfoEntry::Parse()
|
|
{
|
|
//Do we actually have a tag?
|
|
//This is hackish, but it should get the job done
|
|
if (check_for_string("<") && !check_for_string("</")) {
|
|
char buf[NAME_LENGTH];
|
|
char buf2[NAME_LENGTH+3]; //for the end tag and name buffer
|
|
|
|
Assert( sizeof(buf2) >= (sizeof(buf) + 3) );
|
|
|
|
//Find the name of the thing we're parsing
|
|
parse_advance(1);
|
|
stuff_string(buf, F_NAME, sizeof(buf), ">");
|
|
parse_advance(1); //skip the end ">"
|
|
|
|
if (optional_string("+Name:")) {
|
|
stuff_string(buf2, F_NAME, sizeof(buf2));
|
|
Name = buf2;
|
|
}
|
|
if (optional_string("+Coords:")) {
|
|
stuff_int_list(Coords, 2, RAW_INTEGER_TYPE);
|
|
}
|
|
if (optional_string("+Size:")) {
|
|
stuff_int_list(&Coords[2], 2, RAW_INTEGER_TYPE);
|
|
}
|
|
|
|
//This is where you add specific classes to parse.
|
|
//Follow the current classes' example, this basically involves
|
|
//a !stricmp(), a resize() (to the number of entries) and multiple Entries[ID].Parse()
|
|
|
|
//=================================================
|
|
if (!stricmp(buf, "Window")) {
|
|
Object = GT_WINDOW;
|
|
Entries.resize(WCI_NUM_ENTRIES);
|
|
|
|
//Fill it out
|
|
Entries[WCI_CAPTION].Parse("Caption", CIE_IMAGE_NMCSD);
|
|
Entries[WCI_HIDE].Parse("Hider", CIE_IMAGE_NMCSD);
|
|
Entries[WCI_CLOSE].Parse("Closer", CIE_IMAGE_NMCSD);
|
|
Entries[WCI_BODY].Parse("Body", CIE_IMAGE);
|
|
Entries[WCI_BORDER].Parse("Border", CIE_IMAGE_BORDER);
|
|
} else if (!stricmp(buf, "Button")) {
|
|
Object = GT_BUTTON;
|
|
Entries.resize(BCI_NUM_ENTRIES);
|
|
|
|
//Fill it out
|
|
Entries[BCI_BUTTON].Parse("Button", CIE_IMAGE_NMCSD);
|
|
}
|
|
|
|
//=================================================
|
|
|
|
//Make sure that the next token isn't an end tag, but
|
|
//is a tag
|
|
ObjectClassInfoEntry temp_ocie;
|
|
while (temp_ocie.Parse()) {
|
|
Subentries.push_back(temp_ocie);
|
|
temp_ocie = ObjectClassInfoEntry();
|
|
}
|
|
|
|
//We MUST have the end tag
|
|
strcpy_s(buf2, "</");
|
|
strcat_s(buf2, buf);
|
|
strcat_s(buf2, ">");
|
|
|
|
required_string(buf2);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ScreenClassInfoEntry::Parse()
|
|
{
|
|
if (check_for_string("#") && !check_for_string("#End")) {
|
|
char buf[NAME_LENGTH];
|
|
|
|
//Find the name of the thing we're parsing
|
|
parse_advance(1);
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Name = buf;
|
|
|
|
//Parse all objects for this screen
|
|
ObjectClassInfoEntry temp_ocie;
|
|
while (temp_ocie.Parse()) {
|
|
Entries.push_back(temp_ocie);
|
|
temp_ocie = ObjectClassInfoEntry();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void GUISystem::ParseClassInfo(const char* filename)
|
|
{
|
|
if (ClassInfoParsed) {
|
|
Warning(LOCATION, "Class info is being parsed twice");
|
|
DestroyClassInfo();
|
|
}
|
|
|
|
try
|
|
{
|
|
read_file_text(filename);
|
|
reset_parse();
|
|
ScreenClassInfo.Parse();
|
|
|
|
bool flag;
|
|
do {
|
|
ScreenClassInfoEntry* sciep = new ScreenClassInfoEntry;
|
|
flag = sciep->Parse();
|
|
if (flag) {
|
|
list_append(&ScreenClassInfo, sciep);
|
|
} else {
|
|
delete sciep;
|
|
}
|
|
} while (flag);
|
|
|
|
ClassInfoParsed = true;
|
|
}
|
|
catch (const parse::ParseException& e)
|
|
{
|
|
mprintf(("WMCGUI: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ClassInfoEntry::Parse(const char* tag, int in_type)
|
|
{
|
|
char buf[MAX_FILENAME_LEN];
|
|
strcpy_s(buf, "+");
|
|
strcat_s(buf, tag);
|
|
if (in_type != CIE_IMAGE_BORDER) {
|
|
strcat_s(buf, ":");
|
|
}
|
|
|
|
if(optional_string(buf)) {
|
|
CIEType = in_type;
|
|
if (in_type == CIE_IMAGE || in_type == CIE_IMAGE_NMCSD) {
|
|
int num_frames;
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_N].Image = IMG_LOAD_ANIM(buf, &num_frames, NULL);
|
|
if (IMG_HANDLE_IS_VALID(Handles[CIE_HANDLE_N].Image) && num_frames) {
|
|
if (num_frames > 1) {
|
|
IMG_HANDLE_SET_FRAME(Handles[CIE_HANDLE_N].Image, Handles[CIE_HANDLE_M].Image, 1);
|
|
}
|
|
if (num_frames > 2) {
|
|
IMG_HANDLE_SET_FRAME(Handles[CIE_HANDLE_N].Image, Handles[CIE_HANDLE_C].Image, 2);
|
|
}
|
|
if (num_frames > 3) {
|
|
IMG_HANDLE_SET_FRAME(Handles[CIE_HANDLE_N].Image, Handles[CIE_HANDLE_S].Image, 3);
|
|
}
|
|
if (num_frames > 4) {
|
|
IMG_HANDLE_SET_FRAME(Handles[CIE_HANDLE_N].Image, Handles[CIE_HANDLE_D].Image, 4);
|
|
}
|
|
IMG_HANDLE_SET_FRAME(Handles[CIE_HANDLE_N].Image, Handles[CIE_HANDLE_N].Image, 0);
|
|
} else {
|
|
Handles[CIE_HANDLE_N].Image = IMG_LOAD(buf);
|
|
}
|
|
if (in_type == CIE_IMAGE_NMCSD) {
|
|
if (optional_string("+Mouseover:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_M].Image = IMG_LOAD(buf);
|
|
}
|
|
if (optional_string("+Clicked:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_C].Image = IMG_LOAD(buf);
|
|
}
|
|
if (optional_string("+Selected:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_S].Image = IMG_LOAD(buf);
|
|
}
|
|
if (optional_string("+Disabled:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_D].Image = IMG_LOAD(buf);
|
|
}
|
|
}
|
|
if(optional_string("+Coords:")) {
|
|
stuff_int_list(Coords, 2, RAW_INTEGER_TYPE);
|
|
}
|
|
} else if (in_type == CIE_IMAGE_BORDER) {
|
|
if (optional_string("+Top Left:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_TL].Image = IMG_LOAD(buf);
|
|
}
|
|
if (optional_string("+Top Mid:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_TM].Image = IMG_LOAD(buf);
|
|
}
|
|
if (optional_string("+Top Right:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_TR].Image = IMG_LOAD(buf);
|
|
}
|
|
if (optional_string("+Mid Left:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_ML].Image = IMG_LOAD(buf);
|
|
}
|
|
if (optional_string("+Mid Right:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_MR].Image = IMG_LOAD(buf);
|
|
}
|
|
if (optional_string("+Bottom left:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_BL].Image = IMG_LOAD(buf);
|
|
}
|
|
if (optional_string("+Bottom Mid:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_BM].Image = IMG_LOAD(buf);
|
|
}
|
|
if (optional_string("+Bottom Right:")) {
|
|
stuff_string(buf, F_NAME, sizeof(buf));
|
|
Handles[CIE_HANDLE_BR].Image = IMG_LOAD(buf);
|
|
}
|
|
} else if (in_type == CIE_COORDS) {
|
|
stuff_int_list(Coords, 2, RAW_INTEGER_TYPE);
|
|
}
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
//Do a little extra checking in debug mode
|
|
//This makes sure the skinnger knows if they did something bad
|
|
if (CIEType == CIE_IMAGE || CIEType == CIE_IMAGE_NMCSD) {
|
|
int w,h,cw,ch;
|
|
IMG_INFO(Handles[0].Image, &w, &h);
|
|
for (uint i = 1; i < CIE_NUM_HANDLES; i++) {
|
|
if (IMG_HANDLE_IS_VALID(Handles[i].Image)) {
|
|
IMG_INFO(Handles[i].Image, &cw, &ch);
|
|
if (cw != w || ch != h) {
|
|
Warning(LOCATION, "Grouped image size unequal; Handle number %d under $%s: has a different size than base image type", i, tag);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int ClassInfoEntry::GetCoords(int *x, int *y)
|
|
{
|
|
int rval = CIE_GC_NONE_SET;
|
|
if (Coords[0]!=INT_MAX && x!=NULL) {
|
|
*x=Coords[0];
|
|
rval |= CIE_GC_X_SET;
|
|
}
|
|
if (Coords[1]!=INT_MAX && y!=NULL) {
|
|
*y=Coords[1];
|
|
rval |= CIE_GC_Y_SET;
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
int ObjectClassInfoEntry::GetImageHandle(int id, int handle_num)
|
|
{
|
|
return Entries[id].GetImageHandle(handle_num);
|
|
}
|
|
|
|
int ObjectClassInfoEntry::GetCoords(int id, int *x, int *y)
|
|
{
|
|
return Entries[id].GetCoords(x, y);
|
|
}
|
|
|
|
int ObjectClassInfoEntry::GetObjectCoords(int *x, int *y, int *w, int *h)
|
|
{
|
|
int rval = CIE_GC_NONE_SET;
|
|
if (Coords[0] != INT_MAX && x != NULL) {
|
|
*x = Coords[0];
|
|
rval |= CIE_GC_X_SET;
|
|
}
|
|
if (Coords[1] != INT_MAX && y != NULL) {
|
|
*y = Coords[1];
|
|
rval |= CIE_GC_Y_SET;
|
|
}
|
|
if (Coords[2] != INT_MAX && w != NULL) {
|
|
*w = Coords[2];
|
|
rval |= CIE_GC_W_SET;
|
|
}
|
|
if (Coords[3] != INT_MAX && h != NULL) {
|
|
*h = Coords[3];
|
|
rval |= CIE_GC_H_SET;
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
//*****************************GUIScreen*******************************
|
|
GUIScreen::GUIScreen(const SCP_string &in_Name)
|
|
{
|
|
//Set the name
|
|
Name = in_Name;
|
|
|
|
//Setup the parent system
|
|
//This can actually be null; but you must attach a screen to a
|
|
//system for it to be functional.
|
|
//this may allow for saving of screens in the future. Incredibly useful for HUDs.
|
|
OwnerSystem = NULL;
|
|
|
|
//Get class info
|
|
if (OwnerSystem != NULL) {
|
|
OwnerSystem->PushScreen(this);
|
|
ScreenClassInfo = OwnerSystem->GetScreenClassInfo(Name);
|
|
} else {
|
|
ScreenClassInfo = NULL;
|
|
}
|
|
}
|
|
|
|
GUIScreen::~GUIScreen()
|
|
{
|
|
GUIObject* cgp = (GUIObject*)GET_FIRST(&Guiobjects);
|
|
GUIObject* cgp_next;
|
|
for (; cgp != END_OF_LIST(&Guiobjects); cgp = cgp_next) {
|
|
cgp_next = (GUIObject*)GET_NEXT(cgp);
|
|
delete cgp;
|
|
}
|
|
}
|
|
|
|
ObjectClassInfoEntry *GUIScreen::GetObjectClassInfo(GUIObject *cgp)
|
|
{
|
|
size_t len;
|
|
uint i;
|
|
|
|
if (cgp->Parent != NULL && cgp->Parent->InfoEntry != NULL) {
|
|
len = cgp->Parent->InfoEntry->Subentries.size();
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (cgp->Parent->InfoEntry->Subentries[i].Name == cgp->Name) {
|
|
return &cgp->Parent->InfoEntry->Subentries[i];
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if ( cgp->Parent->InfoEntry->Subentries[i].Name.empty()
|
|
&& cgp->Parent->InfoEntry->Subentries[i].Object == cgp->Type ) {
|
|
return &cgp->Parent->InfoEntry->Subentries[i];
|
|
}
|
|
}
|
|
} else if (cgp->Parent == NULL && ScreenClassInfo != NULL) {
|
|
len = ScreenClassInfo->Entries.size();
|
|
for (i = 0; i < len; i++) {
|
|
if (ScreenClassInfo->Entries[i].Name == cgp->Name) {
|
|
return &ScreenClassInfo->Entries[i];
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if( ScreenClassInfo->Entries[i].Name.empty() &&
|
|
ScreenClassInfo->Entries[i].Object == cgp->Type ) {
|
|
return &ScreenClassInfo->Entries[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
//Generic templates - ie if you want every single OK button to look the same,
|
|
//or every single window to look the same.
|
|
if (OwnerSystem != NULL) {
|
|
len = OwnerSystem->GetClassInfo()->Entries.size();
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (OwnerSystem->GetClassInfo()->Entries[i].Name == cgp->Name) {
|
|
return &OwnerSystem->GetClassInfo()->Entries[i];
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if( OwnerSystem->GetClassInfo()->Entries[i].Name.empty() &&
|
|
OwnerSystem->GetClassInfo()->Entries[i].Object == cgp->Type) {
|
|
return &OwnerSystem->GetClassInfo()->Entries[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//Adds objects to a screen
|
|
//IMPORTANT: Options added with the same name as
|
|
//older objects will make this function return
|
|
//a pointer to the original object with that name
|
|
GUIObject* GUIScreen::Add(GUIObject* new_gauge)
|
|
{
|
|
if(new_gauge == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Assert(this != NULL);
|
|
|
|
//First - do we have anything with the same name.
|
|
for (GUIObject *tgp = (GUIObject*) GET_FIRST(&Guiobjects); tgp != END_OF_LIST(&Guiobjects); tgp = (GUIObject*) GET_NEXT(tgp)) {
|
|
if (tgp->Name == new_gauge->Name) {
|
|
//Get rid of the new gauge
|
|
//We don't want it; breaks skinning
|
|
|
|
if (tgp->Type == new_gauge->Type) {
|
|
//If the type of the existing object is the same
|
|
//as the new one, we can safely pass this one off
|
|
delete new_gauge;
|
|
return tgp;
|
|
} else {
|
|
//This is icky; we might cast a pointer after this.
|
|
//So return NULL with a warning
|
|
Warning(LOCATION, "Attempt to create another object with name '%s'; new object type was %d, existing object type was %d. This may cause null pointer issues.", tgp->Name.c_str(), new_gauge->Type, tgp->Type);
|
|
delete new_gauge;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Add to the end of the list
|
|
list_append(&Guiobjects, new_gauge);
|
|
new_gauge->OwnerSystem = OwnerSystem;
|
|
new_gauge->OwnerScreen = this;
|
|
|
|
//For skinning
|
|
new_gauge->SetCIPointer();
|
|
//Set position and stuff
|
|
new_gauge->GetOIECoords(&new_gauge->Coords[0], &new_gauge->Coords[1], &new_gauge->Coords[2], &new_gauge->Coords[3]);
|
|
//In case we need to resize
|
|
new_gauge->OnRefreshSize();
|
|
|
|
return new_gauge;
|
|
}
|
|
|
|
void GUIScreen::DeleteObject(GUIObject* dgp)
|
|
{
|
|
DeletionCache.push_back(dgp);
|
|
}
|
|
|
|
int GUIScreen::OnFrame(float frametime, bool doevents)
|
|
{
|
|
GR_DEBUG_SCOPE("GUIScreen Frame");
|
|
|
|
GUIObject* cgp;
|
|
GUIObject* cgp_prev;
|
|
bool SomethingPressed = false;
|
|
|
|
if (NOT_EMPTY(&Guiobjects) && doevents) {
|
|
//Note that children WILL be changing this variable as they go along,
|
|
//as they use up statuses.
|
|
int status = OwnerSystem->GetStatus();
|
|
|
|
if ( OwnerSystem->GetFocusObject() != NULL ) {
|
|
GUIObject *csp = OwnerSystem->GetFocusObject();
|
|
csp->LastStatus = csp->Status;
|
|
csp->Status = status;
|
|
csp->OnFrame(frametime, &status);
|
|
SomethingPressed = true;
|
|
} else {
|
|
//Pass the status on
|
|
cgp = (GUIObject*)GET_LAST(&Guiobjects);
|
|
cgp->Status |= (status & GST_KEYBOARD_STATUS);
|
|
for (; cgp != END_OF_LIST(&Guiobjects); cgp = cgp_prev) {
|
|
//In case an object deletes itself
|
|
cgp_prev = (GUIObject*)GET_PREV(cgp);
|
|
cgp->LastStatus = cgp->Status;
|
|
cgp->Status = 0;
|
|
|
|
//If we are moving something, nothing else can get click events
|
|
if (OwnerSystem->GetGraspedObject() == NULL) {
|
|
if (OwnerSystem->GetMouseX() >= cgp->Coords[0]
|
|
&& OwnerSystem->GetMouseX() <= cgp->Coords[2]
|
|
&& OwnerSystem->GetMouseY() >= cgp->Coords[1]
|
|
&& OwnerSystem->GetMouseY() <= cgp->Coords[3]) {
|
|
cgp->Status |= (status & GST_MOUSE_STATUS);
|
|
if (status & GST_MOUSE_PRESS) {
|
|
SomethingPressed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
cgp->OnFrame(frametime, &status);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (size_t i = DeletionCache.size(); i > 0; i--) {
|
|
delete DeletionCache[i-1];
|
|
DeletionCache.pop_back();
|
|
}
|
|
|
|
// save zbuffer so that we can reset it after drawing (FIXME: this could probably be done better)
|
|
int saved_zbuf = gr_zbuffer_get();
|
|
gr_zbuffer_set(GR_ZBUFF_NONE);
|
|
|
|
//Draw now. This prevents problems from an object deleting itself or moving around in the list
|
|
cgp = (GUIObject*)GET_FIRST(&Guiobjects);
|
|
|
|
while (cgp != END_OF_LIST(&Guiobjects)) {
|
|
if ( !doevents ) {
|
|
cgp->Status = 0;
|
|
}
|
|
|
|
cgp->OnDraw(frametime);
|
|
|
|
cgp = (GUIObject*)GET_NEXT(cgp);
|
|
}
|
|
|
|
// reset zbuffer to saved value
|
|
gr_zbuffer_set(saved_zbuf);
|
|
|
|
if(SomethingPressed) {
|
|
return GSOF_SOMETHINGPRESSED;
|
|
} else {
|
|
return GSOF_NOTHINGPRESSED;
|
|
}
|
|
}
|
|
|
|
//*****************************GUISystem*******************************
|
|
GUISystem::GUISystem()
|
|
{
|
|
GraspedGuiobject=ActiveObject=NULL;
|
|
Status=LastStatus=0;
|
|
MouseX = MouseY = 0;
|
|
ClassInfoParsed = false;
|
|
}
|
|
|
|
GUISystem::~GUISystem()
|
|
{
|
|
GUIScreen* csp = (GUIScreen*)GET_FIRST(&Screens);
|
|
GUIScreen* csp_next;
|
|
for (; csp != END_OF_LIST(&Screens); csp = csp_next) {
|
|
csp_next = (GUIScreen*)GET_NEXT(csp);
|
|
delete csp;
|
|
}
|
|
DestroyClassInfo();
|
|
}
|
|
|
|
GUIScreen *GUISystem::PushScreen(GUIScreen *csp)
|
|
{
|
|
Assert(csp != NULL);
|
|
list_append(&Screens, csp);
|
|
csp->OwnerSystem = this;
|
|
csp->ScreenClassInfo = GetScreenClassInfo(csp->Name);
|
|
return csp;
|
|
}
|
|
|
|
//Removes a screen from a GUISystem
|
|
//Handy if you want to save window movements and such;
|
|
//just call this and it'll take care of everything
|
|
void GUISystem::PullScreen(GUIScreen *in_screen)
|
|
{
|
|
GUIScreen *csp_next;
|
|
for (GUIScreen *csp = (GUIScreen*)GET_FIRST(&Screens); csp != END_OF_LIST(&Screens); csp = csp_next) {
|
|
csp_next = (GUIScreen*)GET_NEXT(csp);
|
|
if (csp == in_screen) {
|
|
csp->prev->next = csp->next;
|
|
csp->next->prev = csp->prev;
|
|
csp->next = csp;
|
|
csp->prev = csp;
|
|
csp->OwnerSystem = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
ScreenClassInfoEntry *GUISystem::GetScreenClassInfo(const SCP_string & screen_name)
|
|
{
|
|
ScreenClassInfoEntry *sciep;
|
|
for (sciep = (ScreenClassInfoEntry*)GET_FIRST(&ScreenClassInfo); sciep != END_OF_LIST(&ScreenClassInfo); sciep = (ScreenClassInfoEntry*)GET_NEXT(sciep)) {
|
|
if (sciep->GetName() == screen_name) {
|
|
return sciep;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int GUISystem::OnFrame(float frametime, bool doevents, bool clearandflip)
|
|
{
|
|
GR_DEBUG_SCOPE("GUISystem Frame");
|
|
|
|
//Set the global status variables for this frame
|
|
LastStatus = Status;
|
|
Status = GST_MOUSE_OVER;
|
|
|
|
bool something_pressed = false;
|
|
|
|
if (clearandflip) {
|
|
gr_clear();
|
|
}
|
|
|
|
if (NOT_EMPTY(&Screens)) {
|
|
if (doevents) {
|
|
KeyPressed = game_check_key();
|
|
|
|
//Add keyboard event stuff
|
|
if (KeyPressed) {
|
|
Status |= GST_KEYBOARD_KEYPRESS;
|
|
|
|
if (KeyPressed & KEY_CTRLED) {
|
|
Status |= GST_KEYBOARD_CTRL;
|
|
}
|
|
if (KeyPressed & KEY_ALTED) {
|
|
Status |= GST_KEYBOARD_ALT;
|
|
}
|
|
if (KeyPressed & KEY_SHIFTED) {
|
|
Status |= GST_KEYBOARD_SHIFT;
|
|
}
|
|
}
|
|
|
|
//Add mouse event stuff
|
|
if (mouse_down(MOUSE_LEFT_BUTTON)) {
|
|
Status |= GST_MOUSE_LEFT_BUTTON;
|
|
} else if (mouse_down(MOUSE_MIDDLE_BUTTON)) {
|
|
Status |= GST_MOUSE_MIDDLE_BUTTON;
|
|
} else if (mouse_down(MOUSE_RIGHT_BUTTON)) {
|
|
Status |= GST_MOUSE_RIGHT_BUTTON;
|
|
}
|
|
|
|
mouse_get_pos(&MouseX, &MouseY);
|
|
|
|
//Handle any grasped object (we are in the process of moving this)
|
|
if (GraspedGuiobject != NULL) {
|
|
if (Status & GraspingButton) {
|
|
something_pressed = true;
|
|
GraspedGuiobject->SetPosition(MouseX - GraspedDiff[0], MouseY - GraspedDiff[1]);
|
|
GraspedGuiobject->Status = (Status & GST_MOUSE_STATUS);
|
|
} else {
|
|
GraspedGuiobject = NULL;
|
|
}
|
|
}
|
|
|
|
if ( FocusGuiObject != NULL ) {
|
|
if ( !( Status & GST_MOUSE_PRESS ) ) {
|
|
FocusGuiObject = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
GUIScreen *csp_prev; //so screens can delete themselves
|
|
for (GUIScreen* csp = (GUIScreen*)GET_LAST(&Screens); csp != END_OF_LIST(&Screens); csp = csp_prev) {
|
|
csp_prev = (GUIScreen*)GET_PREV(csp);
|
|
if (csp->OnFrame(frametime, doevents) == GSOF_SOMETHINGPRESSED) {
|
|
something_pressed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (clearandflip) {
|
|
gr_flip();
|
|
}
|
|
|
|
if (something_pressed) {
|
|
return GSOF_SOMETHINGPRESSED;
|
|
} else {
|
|
return GSOF_NOTHINGPRESSED;
|
|
}
|
|
}
|
|
|
|
void GUISystem::SetActiveObject(GUIObject *cgp)
|
|
{
|
|
if (cgp != NULL) {
|
|
ActiveObject = cgp;
|
|
//Move everything to the end of the list
|
|
//This way, it gets precedence
|
|
while (cgp->Parent != NULL) {
|
|
list_move_append(&cgp->Parent->Children, cgp);
|
|
cgp = cgp->Parent;
|
|
}
|
|
|
|
//Eventually, we make this last guy the last object in Guiobjects
|
|
list_move_append(&cgp->OwnerScreen->Guiobjects, cgp);
|
|
}
|
|
}
|
|
|
|
void GUISystem::SetGraspedObject(GUIObject *cgp, int button)
|
|
{
|
|
//Protect thyself!
|
|
if (cgp == NULL) {
|
|
return;
|
|
}
|
|
|
|
GraspedGuiobject = cgp;
|
|
GraspingButton = button;
|
|
GraspedDiff[0] = MouseX - cgp->Coords[0];
|
|
GraspedDiff[1] = MouseY - cgp->Coords[1];
|
|
}
|
|
|
|
void GUISystem::SetFocusObject(GUIObject *cgp)
|
|
{
|
|
FocusGuiObject = cgp;
|
|
}
|
|
|
|
void GUISystem::DestroyClassInfo()
|
|
{
|
|
ScreenClassInfoEntry *sciep, *next_sciep;
|
|
for (sciep = (ScreenClassInfoEntry*)GET_FIRST(&ScreenClassInfo); sciep != END_OF_LIST(&ScreenClassInfo); sciep = next_sciep) {
|
|
next_sciep = (ScreenClassInfoEntry*)GET_NEXT(sciep);
|
|
delete sciep;
|
|
}
|
|
ClassInfoParsed = false;
|
|
}
|
|
|
|
//*****************************GUIObject*******************************
|
|
GUIObject::GUIObject(const SCP_string &in_Name, int x_coord, int y_coord, int x_width, int y_height, int in_style)
|
|
{
|
|
//General stuff
|
|
LastStatus = Status = 0;
|
|
OwnerSystem = NULL;
|
|
OwnerScreen = NULL;
|
|
Parent = NULL;
|
|
CloseFunction = NULL;
|
|
|
|
list_init(this);
|
|
list_init(&Children);
|
|
|
|
//These would make no sense
|
|
//x_coord and y_coord of < 0 mean to let the parent handle placement.
|
|
if (x_width == 0 || y_height == 0) {
|
|
return;
|
|
}
|
|
|
|
//No! Bad!
|
|
if (in_Name.length() < 1) {
|
|
return;
|
|
}
|
|
|
|
Name = in_Name;
|
|
Coords[0] = x_coord;
|
|
Coords[1] = y_coord;
|
|
Coords[2] = x_coord + x_width;
|
|
Coords[3] = y_coord + y_height;
|
|
|
|
Style = in_style;
|
|
|
|
if (x_width > 0) {
|
|
Style |= GS_NOAUTORESIZEX;
|
|
}
|
|
if (y_height > 0) {
|
|
Style |= GS_NOAUTORESIZEY;
|
|
}
|
|
|
|
Type = GT_NONE;
|
|
InfoEntry = NULL;
|
|
}
|
|
|
|
GUIObject::~GUIObject()
|
|
{
|
|
if (CloseFunction != NULL) {
|
|
CloseFunction(this);
|
|
}
|
|
|
|
DeleteChildren();
|
|
|
|
// Set these properly
|
|
next->prev = prev;
|
|
prev->next = next;
|
|
}
|
|
|
|
void GUIObject::DeleteChildren(GUIObject* /*exception*/)
|
|
{
|
|
if ( EMPTY(&Children) ) {
|
|
return;
|
|
}
|
|
|
|
GUIObject* cgp = (GUIObject*)GET_FIRST(&Children);
|
|
GUIObject* cgp_temp;
|
|
|
|
while (cgp != END_OF_LIST(&Children)) {
|
|
cgp_temp = cgp;
|
|
cgp = (GUIObject*)GET_NEXT(cgp);
|
|
delete cgp_temp;
|
|
}
|
|
|
|
list_init(&Children);
|
|
}
|
|
|
|
GUIObject* GUIObject::AddChildInternal(GUIObject *cgp)
|
|
{
|
|
if (cgp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
cgp->Style |= GS_INTERNALCHILD;
|
|
|
|
//Add to end of child list
|
|
list_append(&Children, cgp);
|
|
|
|
cgp->Parent = this;
|
|
cgp->OwnerSystem = OwnerSystem;
|
|
cgp->OwnerScreen = OwnerScreen;
|
|
|
|
//Update coordinates (Should be relative x/y and width/height) to absolute coordinates
|
|
cgp->Coords[0] += Coords[0];
|
|
cgp->Coords[1] += Coords[1];
|
|
cgp->Coords[2] += Coords[0];
|
|
cgp->Coords[3] += Coords[1];
|
|
|
|
//For skinning
|
|
cgp->SetCIPointer();
|
|
//Check position
|
|
cgp->GetOIECoords(&cgp->Coords[0], &cgp->Coords[1], &cgp->Coords[2], &cgp->Coords[3]);
|
|
//In case we need to resize
|
|
cgp->OnRefreshSize();
|
|
|
|
return cgp;
|
|
}
|
|
|
|
GUIObject* GUIObject::AddChild(GUIObject* cgp)
|
|
{
|
|
if (cgp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//AddInternalChild must be used
|
|
if (cgp->Style & GS_INTERNALCHILD) {
|
|
return NULL;
|
|
}
|
|
|
|
//Add to end of child list
|
|
list_append(&Children, cgp);
|
|
|
|
cgp->Parent = this;
|
|
cgp->OwnerSystem = OwnerSystem;
|
|
cgp->OwnerScreen = OwnerScreen;
|
|
|
|
// Update coordinates (Should be relative x/y and width/height) to absolute coordinates
|
|
cgp->Coords[0] += ChildCoords[0];
|
|
cgp->Coords[1] += ChildCoords[1];
|
|
cgp->Coords[2] += ChildCoords[0];
|
|
cgp->Coords[3] += ChildCoords[1];
|
|
|
|
//For skinning
|
|
cgp->SetCIPointer();
|
|
//Check position
|
|
cgp->GetOIECoords(&cgp->Coords[0], &cgp->Coords[1], &cgp->Coords[2], &cgp->Coords[3]);
|
|
//In case we need to resize
|
|
cgp->OnRefreshSize();
|
|
|
|
return cgp;
|
|
}
|
|
|
|
void GUIObject::Delete()
|
|
{
|
|
if (OwnerScreen != NULL) {
|
|
OwnerScreen->DeleteObject(this);
|
|
} else {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
void GUIObject::OnDraw(float frametime)
|
|
{
|
|
DoDraw(frametime);
|
|
if (!(Style & GS_HIDDEN)) {
|
|
GUIObject *cgp_prev;
|
|
for (GUIObject* cgp = (GUIObject*)GET_LAST(&Children); cgp != END_OF_LIST(&Children); cgp = cgp_prev) {
|
|
cgp_prev = (GUIObject*)GET_PREV(cgp);
|
|
cgp->OnDraw(frametime);
|
|
}
|
|
}
|
|
}
|
|
|
|
int GUIObject::OnFrame(float frametime, int *unused_queue)
|
|
{
|
|
GR_DEBUG_SCOPE("GUIObject Frame");
|
|
|
|
int rval = OF_TRUE;
|
|
|
|
GUIObject *cgp_prev; //Elements will move themselves to the end of the list if they become active
|
|
GUIObject *cgp = (GUIObject*)GET_LAST(&Children);
|
|
|
|
for ( ; (cgp != NULL) && (cgp != END_OF_LIST(&Children)); cgp = cgp_prev) {
|
|
cgp_prev = (GUIObject*)GET_PREV(cgp);
|
|
cgp->LastStatus = cgp->Status;
|
|
cgp->Status = 0;
|
|
|
|
if (Status & GST_MOUSE_OVER) {
|
|
if (OwnerSystem->GetMouseX() >= cgp->Coords[0]
|
|
&& OwnerSystem->GetMouseX() <= cgp->Coords[2]
|
|
&& OwnerSystem->GetMouseY() >= cgp->Coords[1]
|
|
&& OwnerSystem->GetMouseY() <= cgp->Coords[3]) {
|
|
cgp->Status |= (Status & GST_MOUSE_STATUS);
|
|
}
|
|
}
|
|
if (cgp == GET_LAST(&Children)) {
|
|
cgp->Status |= (Status & GST_KEYBOARD_STATUS);
|
|
}
|
|
cgp->OnFrame(frametime, &Status);
|
|
}
|
|
|
|
if (Status) {
|
|
//MOUSE OVER
|
|
if (Status & GST_MOUSE_OVER) {
|
|
rval = DoMouseOver(frametime);
|
|
|
|
if (rval == OF_TRUE) {
|
|
(*unused_queue) &= ~GST_MOUSE_OVER;
|
|
}
|
|
}
|
|
|
|
//MOUSE DOWN
|
|
if (Status & GST_MOUSE_PRESS) {
|
|
rval = DoMouseDown(frametime);
|
|
OwnerSystem->SetFocusObject(this);
|
|
|
|
if (rval == OF_TRUE) {
|
|
(*unused_queue) &= ~(GST_MOUSE_LEFT_BUTTON | GST_MOUSE_RIGHT_BUTTON | GST_MOUSE_MIDDLE_BUTTON);
|
|
}
|
|
}
|
|
|
|
//MOUSE UP
|
|
if ((LastStatus & GST_MOUSE_PRESS) && !(OwnerSystem->GetStatus() & GST_MOUSE_PRESS)) {
|
|
rval = DoMouseUp(frametime);
|
|
|
|
if (rval == OF_TRUE) {
|
|
(*unused_queue) &= ~(GST_MOUSE_LEFT_BUTTON | GST_MOUSE_RIGHT_BUTTON | GST_MOUSE_MIDDLE_BUTTON);
|
|
}
|
|
}
|
|
|
|
//KEY STATE
|
|
if ((Status & GST_KEYBOARD_CTRL) || (Status & GST_KEYBOARD_ALT) || (Status & GST_KEYBOARD_SHIFT)) {
|
|
rval = DoKeyState(frametime);
|
|
|
|
if (rval == OF_TRUE) {
|
|
(*unused_queue) &= ~(GST_KEYBOARD_CTRL | GST_KEYBOARD_ALT | GST_KEYBOARD_SHIFT);
|
|
}
|
|
}
|
|
|
|
//KEYPRESS
|
|
if (Status & GST_KEYBOARD_KEYPRESS) {
|
|
rval = DoKeyPress(frametime);
|
|
|
|
if (rval == OF_TRUE) {
|
|
(*unused_queue) &= ~GST_KEYBOARD_KEYPRESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(Status & GST_MOUSE_OVER) && (LastStatus & GST_MOUSE_OVER)) {
|
|
rval = DoMouseOut(frametime);
|
|
}
|
|
|
|
return DoFrame(frametime);
|
|
}
|
|
|
|
void GUIObject::OnMove(int dx, int dy)
|
|
{
|
|
Coords[0]+=dx;
|
|
Coords[1]+=dy;
|
|
Coords[2]+=dx;
|
|
Coords[3]+=dy;
|
|
ChildCoords[0]+=dx;
|
|
ChildCoords[1]+=dy;
|
|
ChildCoords[2]+=dx;
|
|
ChildCoords[3]+=dy;
|
|
|
|
for (GUIObject *cgp = (GUIObject*)GET_FIRST(&Children); cgp != END_OF_LIST(&Children); cgp = (GUIObject*)GET_NEXT(cgp)) {
|
|
cgp->OnMove(dx, dy);
|
|
}
|
|
|
|
DoMove(dx, dy);
|
|
}
|
|
|
|
int GUIObject::GetCIECoords(int id, int *x, int *y)
|
|
{
|
|
if (InfoEntry!=NULL) {
|
|
int rv = InfoEntry->GetCoords(id, x, y);
|
|
if (rv & CIE_GC_X_SET) {
|
|
if (*x < 0) {
|
|
*x += Coords[2];
|
|
} else {
|
|
*x += Coords[0];
|
|
}
|
|
}
|
|
if (rv & CIE_GC_Y_SET) {
|
|
if (*y < 0) {
|
|
*y += Coords[3];
|
|
} else {
|
|
*y += Coords[1];
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
return CIE_GC_NONE_SET;
|
|
}
|
|
|
|
int GUIObject::GetOIECoords(int *x1, int *y1, int *x2, int *y2)
|
|
{
|
|
if (InfoEntry != NULL) {
|
|
int rv = InfoEntry->GetObjectCoords(x1, y1, x2, y2);
|
|
if (Parent != NULL) {
|
|
if (rv & CIE_GC_X_SET) {
|
|
if (*x1 < 0) {
|
|
*x1 += Parent->ChildCoords[2];
|
|
} else {
|
|
*x1 += Parent->ChildCoords[0];
|
|
}
|
|
}
|
|
if (rv & CIE_GC_Y_SET) {
|
|
if (*y1 < 0) {
|
|
*y1 += Parent->ChildCoords[3];
|
|
} else {
|
|
*y1 += Parent->ChildCoords[1];
|
|
}
|
|
}
|
|
} else {
|
|
if (rv & CIE_GC_X_SET) {
|
|
if (*x1 < 0) {
|
|
*x1 += gr_screen.clip_right;
|
|
} else {
|
|
*x1 += gr_screen.clip_left;
|
|
}
|
|
}
|
|
if (rv & CIE_GC_Y_SET) {
|
|
if (*y1 < 0) {
|
|
*y1 += gr_screen.clip_bottom;
|
|
} else {
|
|
*y1 += gr_screen.clip_top;
|
|
}
|
|
}
|
|
}
|
|
if (rv & CIE_GC_W_SET) {
|
|
*x2 = *x1 + *x2;
|
|
Style |= GS_NOAUTORESIZEX;
|
|
}
|
|
if (rv & CIE_GC_H_SET) {
|
|
*y2 = *y1 + *y2;
|
|
Style |= GS_NOAUTORESIZEY;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
return CIE_GC_NONE_SET;
|
|
}
|
|
|
|
//Call this after an object's type is set to enable the GUIObject CIE functions
|
|
void GUIObject::SetCIPointer()
|
|
{
|
|
if (OwnerScreen) {
|
|
InfoEntry=OwnerScreen->GetObjectClassInfo(this);
|
|
}
|
|
}
|
|
|
|
void GUIObject::SetPosition(int x, int y)
|
|
{
|
|
int dx, dy;
|
|
dx = x - Coords[0];
|
|
dy = y - Coords[1];
|
|
|
|
if (dx == 0 && dy == 0) {
|
|
return;
|
|
}
|
|
|
|
if (Parent != NULL) {
|
|
//Don't let stuff get moved outside client window
|
|
if (Coords[2] + dx > Parent->Coords[2]) {
|
|
dx = Parent->Coords[2] - Coords[2];
|
|
}
|
|
if (Coords[3] + dy > gr_screen.clip_bottom) {
|
|
dy = Parent->Coords[3] - Coords[3];
|
|
}
|
|
|
|
//Check these last, so we're sure we can move stuff around still
|
|
if (Coords[0] + dx < gr_screen.clip_left) {
|
|
dx = Parent->Coords[0] - Coords[0];
|
|
}
|
|
if (Coords[1] + dy < gr_screen.clip_top) {
|
|
dy = Parent->Coords[1] - Coords[1];
|
|
}
|
|
}
|
|
|
|
OnMove(dx, dy);
|
|
}
|
|
|
|
//*****************************Window*******************************
|
|
int Window::DoRefreshSize()
|
|
{
|
|
float num;
|
|
int w=0, h=0;
|
|
//Top left, top right, bottom left, bottom right
|
|
int CornerWidths[4];
|
|
|
|
//Determine left border's width
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_ML))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_ML), &BorderSizes[0], NULL);
|
|
} else {
|
|
BorderSizes[0] = W_BORDERHEIGHT;
|
|
}
|
|
|
|
//Determine right border's width
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_MR))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_MR), &BorderSizes[2], NULL);
|
|
} else {
|
|
BorderSizes[2] = W_BORDERWIDTH;
|
|
}
|
|
|
|
//Determine top border height
|
|
bool custom_top = true;
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TL))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TL), NULL, &BorderSizes[1]);
|
|
} else if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TR))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TR), NULL, &BorderSizes[1]);
|
|
} else if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TM))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TM), NULL, &BorderSizes[1]);
|
|
} else {
|
|
custom_top = false;
|
|
BorderSizes[1] = W_BORDERHEIGHT;
|
|
}
|
|
|
|
//Determine bottom border height
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BL))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BL), NULL, &BorderSizes[3]);
|
|
} else if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BR))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BR), NULL, &BorderSizes[3]);
|
|
} else if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BM))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BM), NULL, &BorderSizes[3]);
|
|
} else {
|
|
BorderSizes[3] = W_BORDERHEIGHT;
|
|
}
|
|
|
|
|
|
//Determine corner sizes
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TL))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BL), &CornerWidths[0], NULL);
|
|
} else {
|
|
CornerWidths[0] = BorderSizes[0];
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TR))){
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TR), &CornerWidths[1], NULL);
|
|
} else {
|
|
CornerWidths[1] = BorderSizes[2];
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BL))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BL), &CornerWidths[2], NULL);
|
|
} else {
|
|
CornerWidths[2] = BorderSizes[0];
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BR))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BL), &CornerWidths[3], NULL);
|
|
} else {
|
|
CornerWidths[3] = BorderSizes[2];
|
|
}
|
|
|
|
|
|
//Do child stuff
|
|
ChildCoords[0] = Coords[0] + BorderSizes[0];
|
|
if (custom_top || (Style & WS_NOTITLEBAR)) {
|
|
ChildCoords[1] = Coords[1] + BorderSizes[1]; //Set earlier on
|
|
} else {
|
|
ChildCoords[1] = Coords[1] + gr_get_font_height() + 5 + 3*W_BORDERHEIGHT;
|
|
}
|
|
ChildCoords[2] = Coords[2] - BorderSizes[2];
|
|
ChildCoords[3] = Coords[3] - BorderSizes[3];
|
|
|
|
if (!(Style & GS_NOAUTORESIZEX)) {
|
|
ChildCoords[2] = Coords[2] = 0;
|
|
}
|
|
if (!(Style & GS_NOAUTORESIZEY)) {
|
|
ChildCoords[3] = Coords[3] = 0;
|
|
}
|
|
|
|
for (GUIObject *cgp = (GUIObject*)GET_FIRST(&Children); cgp != END_OF_LIST(&Children); cgp = (GUIObject*)GET_NEXT(cgp)) {
|
|
//SetPosition children around so they're within the upper-left corner of dialog
|
|
if (cgp->Coords[0] < ChildCoords[0]) {
|
|
cgp->SetPosition(ChildCoords[0], cgp->Coords[1] + BorderSizes[0]);
|
|
}
|
|
if (cgp->Coords[1] < ChildCoords[1]) {
|
|
cgp->SetPosition(cgp->Coords[0], Coords[1] + BorderSizes[1]);
|
|
}
|
|
|
|
//Resize window to fit children
|
|
if (cgp->Coords[2] > ChildCoords[2] && !(Style & GS_NOAUTORESIZEX)) {
|
|
ChildCoords[2] = cgp->Coords[2];
|
|
Coords[2] = cgp->Coords[2] + BorderSizes[2];
|
|
}
|
|
if (cgp->Coords[3] > ChildCoords[3] && !(Style & GS_NOAUTORESIZEY)) {
|
|
ChildCoords[3] = cgp->Coords[3];
|
|
Coords[3] = cgp->Coords[3] + BorderSizes[3];
|
|
}
|
|
}
|
|
|
|
//Find caption coordinates
|
|
if (!(Style & WS_NOTITLEBAR)) {
|
|
int close_w = 0,close_h = 0;
|
|
int hide_w = 0, hide_h = 0;
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_CLOSE))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_CLOSE), &close_w, &close_h);
|
|
} else {
|
|
gr_get_string_size(&close_w, &close_h, "X");
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_HIDE))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_HIDE), &hide_w, &hide_h);
|
|
} else {
|
|
gr_get_string_size(&hide_w, &hide_h, "-");
|
|
}
|
|
|
|
int caption_min_size;
|
|
if (!Caption.empty()) {
|
|
gr_get_string_size(&w, &h, Caption.c_str());
|
|
caption_min_size = w + close_w + hide_w + 5;
|
|
} else {
|
|
caption_min_size = close_w + hide_w;
|
|
}
|
|
|
|
if ((Coords[2] - Coords[0]) < (caption_min_size)) {
|
|
Coords[2] = Coords[0] + caption_min_size + BorderSizes[0] + BorderSizes[2];
|
|
}
|
|
|
|
if (!Caption.empty()) {
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_CAPTION))) {
|
|
int cw, ch;
|
|
CaptionCoords[0] = Coords[0] + BorderSizes[0];
|
|
CaptionCoords[1] = Coords[1] + BorderSizes[1];
|
|
GetCIECoords(WCI_CAPTION, &CaptionCoords[0], &CaptionCoords[1]);
|
|
|
|
//do the image
|
|
IMG_INFO(GetCIEImageHandle(WCI_CAPTION), &cw, &ch);
|
|
num = (float) w / cw;
|
|
CaptionRectList = bitmap_rect_list(Coords[0] + CornerWidths[2], Coords[1], w, ch, 0, 0, 1.0f, 1.0f);
|
|
h = ch;
|
|
} else {
|
|
h += 5;
|
|
CaptionCoords[0] = Coords[0] + (((Coords[2]-Coords[0]) - w - (close_w + hide_w)) / 2);
|
|
CaptionCoords[1] = Coords[1] + BorderSizes[1];
|
|
}
|
|
CaptionCoords[2] = CaptionCoords[0] + w;
|
|
CaptionCoords[3] = CaptionCoords[1] + h;
|
|
}
|
|
|
|
//Find close coordinates now
|
|
CloseCoords[0] = Coords[2] - close_w;
|
|
CloseCoords[1] = Coords[1] + BorderSizes[1];
|
|
GetCIECoords(WCI_CLOSE, &CloseCoords[0], &CloseCoords[1]);
|
|
CloseCoords[2] = CloseCoords[0] + close_w;
|
|
CloseCoords[3] = CloseCoords[1] + close_h;
|
|
|
|
//Find hide coordinates now
|
|
HideCoords[0] = CloseCoords[0] - hide_w;
|
|
HideCoords[1] = CloseCoords[1];
|
|
GetCIECoords(WCI_HIDE, &HideCoords[0], &HideCoords[1]);
|
|
HideCoords[2] = HideCoords[0] + hide_w;
|
|
HideCoords[3] = HideCoords[1] + hide_h;
|
|
}
|
|
|
|
//Do bitmap stuff
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TM))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TM), &w, &h);
|
|
num = (float)((Coords[2]-CornerWidths[3])-(Coords[0]+CornerWidths[2])) / (float)w;
|
|
BorderRectLists[CIE_HANDLE_TM] = bitmap_rect_list(Coords[0] + CornerWidths[2], Coords[1], fl2i(w*num),h,0,0,num,1.0f);
|
|
//gr_bitmap_list(&BorderRectLists[CIE_HANDLE_BM], 1, GR_RESIZE_NONE);
|
|
}
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BM))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BM), &w, &h);
|
|
num = (float)((Coords[2]-CornerWidths[3])-(Coords[0]+CornerWidths[2])) / (float)w;
|
|
BorderRectLists[CIE_HANDLE_BM] = bitmap_rect_list(Coords[0] + CornerWidths[2], Coords[3]-h, fl2i(w*num),h,0,0,num,1.0f);
|
|
//gr_bitmap_list(&BorderRectLists[CIE_HANDLE_BM], 1, GR_RESIZE_NONE);
|
|
}
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_ML))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_ML), &w, &h);
|
|
num = (float)((Coords[3]-BorderSizes[3])-(Coords[1]+BorderSizes[1])) / (float)h;
|
|
BorderRectLists[CIE_HANDLE_ML] = bitmap_rect_list(Coords[0], Coords[1] + BorderSizes[1], w,fl2i(h*num),0,0,1.0f,num);
|
|
}
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_MR))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_MR), &w, &h);
|
|
num = (float)((Coords[3]-BorderSizes[3])-(Coords[1]+BorderSizes[1])) / (float)h;
|
|
BorderRectLists[CIE_HANDLE_MR] = bitmap_rect_list(Coords[2]-w, Coords[1] + BorderSizes[1], w,fl2i(h*num),0,0,1.0f,num);
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Window::DoMouseOver(float /*frametime*/)
|
|
{
|
|
if (OwnerSystem->GetMouseX() >= HideCoords[0]
|
|
&& OwnerSystem->GetMouseX() <= HideCoords[2]
|
|
&& OwnerSystem->GetMouseY() >= HideCoords[1]
|
|
&& OwnerSystem->GetMouseY() <= HideCoords[3]) {
|
|
HideHighlight = true;
|
|
} else {
|
|
HideHighlight = false;
|
|
}
|
|
|
|
if (OwnerSystem->GetMouseX() >= CloseCoords[0]
|
|
&& OwnerSystem->GetMouseX() <= CloseCoords[2]
|
|
&& OwnerSystem->GetMouseY() >= CloseCoords[1]
|
|
&& OwnerSystem->GetMouseY() <= CloseCoords[3]) {
|
|
CloseHighlight = true;
|
|
} else {
|
|
CloseHighlight = false;
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Window::DoMouseDown(float /*frametime*/)
|
|
{
|
|
OwnerSystem->SetActiveObject(this);
|
|
|
|
if (Style & WS_NONMOVEABLE) {
|
|
return OF_TRUE;
|
|
}
|
|
|
|
if ((OwnerSystem->GetGraspedObject() == NULL)
|
|
&& (Status & GST_MOUSE_LEFT_BUTTON)
|
|
&& OwnerSystem->GetMouseX() >= CaptionCoords[0]
|
|
&& ((OwnerSystem->GetMouseX() < CaptionCoords[2]) || ((Style & WS_NOTITLEBAR) && OwnerSystem->GetMouseX() <= Coords[2]))
|
|
&& OwnerSystem->GetMouseY() >= CaptionCoords[1]
|
|
&& ((OwnerSystem->GetMouseY() <= CaptionCoords[3]) || ((Style & WS_NOTITLEBAR) && OwnerSystem->GetMouseY() <= Coords[2])))
|
|
{
|
|
OwnerSystem->SetGraspedObject(this, GST_MOUSE_LEFT_BUTTON);
|
|
}
|
|
|
|
//All further movement of grasped objects is handled by GUISystem
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Window::DoMouseUp(float /*frametime*/)
|
|
{
|
|
if (Style & WS_NONMOVEABLE) {
|
|
return OF_TRUE;
|
|
}
|
|
|
|
if (CloseHighlight) {
|
|
Delete();
|
|
return OF_TRUE;
|
|
}
|
|
|
|
if (HideHighlight) {
|
|
if (Style & GS_HIDDEN) {
|
|
Coords[3] = UnhiddenHeight;
|
|
Style &= ~GS_HIDDEN;
|
|
} else {
|
|
UnhiddenHeight = Coords[3];
|
|
Coords[3] = Coords[1] + (CaptionCoords[3] - CaptionCoords[1]) + BorderSizes[1] + BorderSizes[3];
|
|
Style |= GS_HIDDEN;
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BM))) {
|
|
int h;
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BM), NULL, &h);
|
|
BorderRectLists[CIE_HANDLE_BM].screen_rect.y = Coords[3] - h;
|
|
}
|
|
}
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Window::DoMouseOut(float /*frametime*/)
|
|
{
|
|
HideHighlight = false;
|
|
CloseHighlight = false;
|
|
return OF_TRUE;
|
|
}
|
|
|
|
void Window::DoMove(int dx, int dy)
|
|
{
|
|
CloseCoords[0] += dx;
|
|
CloseCoords[1] += dy;
|
|
CloseCoords[2] += dx;
|
|
CloseCoords[3] += dy;
|
|
|
|
HideCoords[0] += dx;
|
|
HideCoords[1] += dy;
|
|
HideCoords[2] += dx;
|
|
HideCoords[3] += dy;
|
|
|
|
CaptionCoords[0] += dx;
|
|
CaptionCoords[1] += dy;
|
|
CaptionCoords[2] += dx;
|
|
CaptionCoords[3] += dy;
|
|
|
|
// change the hidden height too if needed
|
|
if (Style & GS_HIDDEN) {
|
|
UnhiddenHeight += dy;
|
|
}
|
|
|
|
//Handle moving the border around
|
|
uint i;
|
|
for (i = 0; i < 8; i++) {
|
|
BorderRectLists[i].screen_rect.x += dx;
|
|
BorderRectLists[i].screen_rect.y += dy;
|
|
}
|
|
|
|
//now the caption
|
|
CaptionRectList.screen_rect.x += dx;
|
|
CaptionRectList.screen_rect.y += dy;
|
|
}
|
|
|
|
void draw_open_rect(int x1, int y1, int x2, int y2, int resize_mode = GR_RESIZE_NONE)
|
|
{
|
|
gr_line(x1, y1, x2, y1, resize_mode);
|
|
gr_line(x2, y1, x2, y2, resize_mode);
|
|
gr_line(x2, y2, x1, y2, resize_mode);
|
|
gr_line(x1, y2, x1, y1, resize_mode);
|
|
}
|
|
|
|
void Window::DoDraw(float /*frametime*/)
|
|
{
|
|
int w, h;
|
|
|
|
// if the window has no title bar, and no content, then don't draw any of it
|
|
if ( (Style & WS_NOTITLEBAR) && !HasChildren() ) {
|
|
return;
|
|
}
|
|
|
|
// shade the background of the window so that it's just slightly transparent
|
|
color clr;
|
|
gr_init_alphacolor(&clr, WindowShade.r, WindowShade.g, WindowShade.b, WindowShade.c);
|
|
gr_set_color_fast(&clr);
|
|
|
|
w = Coords[2] - Coords[0];
|
|
h = Coords[3] - Coords[1];
|
|
gr_rect(Coords[0], Coords[1], w, h, GR_RESIZE_NONE);
|
|
|
|
gr_set_color_fast(&Color_text_normal);
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TL))) {
|
|
IMG_SET(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TL));
|
|
IMG_DRAW(Coords[0], Coords[1]);
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TR))) {
|
|
IMG_SET(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TR));
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TR), &w, NULL);
|
|
IMG_DRAW(Coords[2] - w, Coords[1]);
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BL))) {
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BL), NULL, &h);
|
|
IMG_SET(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BL));
|
|
IMG_DRAW(Coords[0], Coords[3] - h);
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BR))) {
|
|
IMG_SET(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BR));
|
|
IMG_INFO(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BR), &w, &h);
|
|
//IMG_DRAW(Coords[2] - w, Coords[3] - h, false);
|
|
IMG_DRAW(Coords[2]-w, Coords[3] - h);
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TM))) {
|
|
IMG_SET(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_TM));
|
|
gr_bitmap_list(&BorderRectLists[CIE_HANDLE_TM], 1, GR_RESIZE_NONE);
|
|
} else {
|
|
gr_line(Coords[0] + BorderSizes[0], Coords[1], Coords[2] - BorderSizes[2], Coords[1], GR_RESIZE_NONE);
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BM))) {
|
|
IMG_SET(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_BM));
|
|
gr_bitmap_list(&BorderRectLists[CIE_HANDLE_BM], 1, GR_RESIZE_NONE);
|
|
} else {
|
|
gr_line(Coords[0] + BorderSizes[0], Coords[3], Coords[2] - BorderSizes[2], Coords[3], GR_RESIZE_NONE);
|
|
}
|
|
|
|
if (!(Style & GS_HIDDEN)) {
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_ML))) {
|
|
IMG_SET(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_ML));
|
|
gr_bitmap_list(&BorderRectLists[CIE_HANDLE_ML], 1, GR_RESIZE_NONE);
|
|
} else {
|
|
gr_line(Coords[0], Coords[1] + BorderSizes[1], Coords[0], Coords[3] - BorderSizes[3], GR_RESIZE_NONE);
|
|
}
|
|
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_MR))) {
|
|
IMG_SET(GetCIEImageHandle(WCI_BORDER, CIE_HANDLE_MR));
|
|
gr_bitmap_list(&BorderRectLists[CIE_HANDLE_MR], 1, GR_RESIZE_NONE);
|
|
} else {
|
|
gr_line(Coords[2], Coords[1] + BorderSizes[1], Coords[2], Coords[3] - BorderSizes[3], GR_RESIZE_NONE);
|
|
}
|
|
}
|
|
|
|
if (!(Style & WS_NOTITLEBAR)) {
|
|
//Draw the caption background
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_CAPTION))) {
|
|
IMG_SET(GetCIEImageHandle(WCI_CAPTION));
|
|
gr_bitmap_list(&CaptionRectList, 1, GR_RESIZE_NONE);
|
|
} else {
|
|
draw_open_rect(Coords[0], Coords[1], Coords[2], CaptionCoords[3]);
|
|
}
|
|
|
|
//Close button
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_CLOSE))) {
|
|
if (CloseHighlight && IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_CLOSE, CIE_HANDLE_M))) {
|
|
IMG_SET(GetCIEImageHandle(WCI_CLOSE, CIE_HANDLE_M));
|
|
} else {
|
|
IMG_SET(GetCIEImageHandle(WCI_CLOSE));
|
|
}
|
|
IMG_DRAW(CloseCoords[0], CloseCoords[1]);
|
|
} else {
|
|
if (CloseHighlight) {
|
|
gr_set_color_fast(&Color_text_active);
|
|
} else {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
}
|
|
gr_string(CloseCoords[0], CloseCoords[1], "X", GR_RESIZE_NONE);
|
|
}
|
|
|
|
|
|
//Hide button
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_HIDE))) {
|
|
if (HideHighlight && IMG_HANDLE_IS_VALID(GetCIEImageHandle(WCI_HIDE, CIE_HANDLE_M))) {
|
|
IMG_SET(GetCIEImageHandle(WCI_HIDE, CIE_HANDLE_M));
|
|
} else {
|
|
IMG_SET(GetCIEImageHandle(WCI_HIDE));
|
|
}
|
|
IMG_DRAW(HideCoords[0], HideCoords[1]);
|
|
} else {
|
|
if (HideHighlight) {
|
|
gr_set_color_fast(&Color_text_active);
|
|
} else {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
}
|
|
gr_string(HideCoords[0], HideCoords[1], "-", GR_RESIZE_NONE);
|
|
}
|
|
|
|
//Caption text
|
|
gr_set_color_fast(&Color_text_normal);
|
|
|
|
gr_string(CaptionCoords[0], CaptionCoords[1], Caption.c_str(), GR_RESIZE_NONE);
|
|
}
|
|
}
|
|
|
|
void Window::ClearContent()
|
|
{
|
|
if ( !HasChildren() ) {
|
|
return;
|
|
}
|
|
|
|
LinkedList *cgp = GET_FIRST(&Children);
|
|
LinkedList *cgp_next;
|
|
|
|
while ( cgp && (cgp != END_OF_LIST(&Children)) ) {
|
|
cgp_next = GET_NEXT(cgp);
|
|
delete cgp;
|
|
cgp = cgp_next;
|
|
}
|
|
}
|
|
|
|
Window::Window(const SCP_string &in_caption, int x_coord, int y_coord, int x_width, int y_width, int in_style)
|
|
:GUIObject(in_caption, x_coord,y_coord,x_width,y_width,in_style)
|
|
{
|
|
Caption = in_caption;
|
|
|
|
CloseHighlight = false;
|
|
HideHighlight = false;
|
|
|
|
//Set the type
|
|
Type = GT_WINDOW;
|
|
|
|
// create a shader for the window
|
|
gr_create_shader(&WindowShade, 0, 0, 0, 200);
|
|
}
|
|
|
|
//*****************************Button*******************************
|
|
|
|
Button::Button(const SCP_string &in_caption, int x_coord, int y_coord, void (*in_function)(Button *caller), int x_width, int y_height, int in_style)
|
|
:GUIObject(in_caption, x_coord, y_coord, x_width, y_height, in_style)
|
|
{
|
|
Caption = in_caption;
|
|
|
|
function = in_function;
|
|
|
|
IsDown = false;
|
|
|
|
//Set the type
|
|
Type = GT_BUTTON;
|
|
}
|
|
|
|
int Button::DoRefreshSize()
|
|
{
|
|
int w, h;
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(BCI_BUTTON))) {
|
|
IMG_INFO(GetCIEImageHandle(BCI_BUTTON), &w, &h);
|
|
if (!(Style & GS_NOAUTORESIZEX)) {
|
|
Coords[2] = Coords[0] + w;
|
|
}
|
|
if (!(Style & GS_NOAUTORESIZEY)) {
|
|
Coords[3] = Coords[1] + h;
|
|
}
|
|
} else {
|
|
gr_get_string_size(&w, &h, Caption.c_str());
|
|
if (!(Style & GS_NOAUTORESIZEX)) {
|
|
Coords[2] = Coords[0] + w;
|
|
}
|
|
if (!(Style & GS_NOAUTORESIZEY)) {
|
|
Coords[3] = Coords[1] + h;
|
|
}
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
void Button::DoDraw(float /*frametime*/)
|
|
{
|
|
if (IMG_HANDLE_IS_VALID(GetCIEImageHandle(BCI_BUTTON))) {
|
|
IMG_SET(GetCIEImageHandle(BCI_BUTTON));
|
|
|
|
if ((Status & GST_MOUSE_OVER) && IMG_HANDLE_IS_VALID(GetCIEImageHandle(BCI_BUTTON, CIE_HANDLE_M))) {
|
|
IMG_SET(GetCIEImageHandle(BCI_BUTTON, CIE_HANDLE_M));
|
|
} else if ((Status & GST_MOUSE_LEFT_BUTTON) && IMG_HANDLE_IS_VALID(GetCIEImageHandle(BCI_BUTTON, CIE_HANDLE_C))) {
|
|
IMG_SET(GetCIEImageHandle(BCI_BUTTON, CIE_HANDLE_C));
|
|
} else if((Style & BS_STICKY) && IsDown && IMG_HANDLE_IS_VALID(GetCIEImageHandle(BCI_BUTTON, CIE_HANDLE_S))) {
|
|
IMG_SET(GetCIEImageHandle(BCI_BUTTON, CIE_HANDLE_S));
|
|
}
|
|
IMG_DRAW(Coords[0], Coords[1]);
|
|
} else {
|
|
if (Status == 0) {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
} else if (Status & GST_MOUSE_OVER) {
|
|
gr_set_color_fast(&Color_text_active);
|
|
} else {
|
|
gr_set_color_fast(&Color_text_selected);
|
|
}
|
|
draw_open_rect(Coords[0], Coords[1], Coords[2], Coords[3], GR_RESIZE_NONE);
|
|
|
|
int half_x, half_y;
|
|
gr_get_string_size(&half_x, &half_y, Caption.c_str());
|
|
half_x = Coords[0] +(((Coords[2]-Coords[0]) - half_x) / 2);
|
|
half_y = Coords[1] +(((Coords[3]-Coords[1]) - half_y) / 2);
|
|
gr_string(half_x, half_y + 1, Caption.c_str(), GR_RESIZE_NONE);
|
|
}
|
|
}
|
|
|
|
int Button::DoMouseDown(float /*frametime*/)
|
|
{
|
|
OwnerSystem->SetActiveObject(this);
|
|
|
|
if (!(Style & BS_STICKY)) {
|
|
IsDown = true;
|
|
}
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Button::DoMouseUp(float /*frametime*/)
|
|
{
|
|
if (function != NULL) {
|
|
function(this);
|
|
}
|
|
|
|
if (!(Style & BS_STICKY)) {
|
|
IsDown = false;
|
|
} else {
|
|
IsDown = !IsDown;
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Button::DoMouseOut(float /*frametime*/)
|
|
{
|
|
if (!(Style & BS_STICKY)) {
|
|
IsDown = false;
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
//*****************************Menu*******************************
|
|
TreeItem::TreeItem()
|
|
:LinkedList()
|
|
{
|
|
Function = NULL;
|
|
Data = 0;
|
|
ShowThis = true;
|
|
ShowChildren = false;
|
|
DeleteData = true;
|
|
Parent = NULL;
|
|
memset(Coords, 0, sizeof(Coords));
|
|
}
|
|
|
|
void TreeItem::ClearAllItems()
|
|
{
|
|
LinkedList* cgp = GET_FIRST(&Children);
|
|
LinkedList* cgp_next;
|
|
for (; cgp != END_OF_LIST(&Children); cgp = cgp_next) {
|
|
cgp_next = GET_NEXT(cgp);
|
|
delete cgp;
|
|
}
|
|
}
|
|
|
|
TreeItem::~TreeItem()
|
|
{
|
|
ClearAllItems();
|
|
}
|
|
|
|
void Tree::CalcItemsSize(TreeItem *items, int DrawData[4])
|
|
{
|
|
//DrawData
|
|
//0 - indent
|
|
//1 - largest width so far
|
|
//2 - total height so far
|
|
//3 - Are we done?
|
|
int w, h;
|
|
|
|
int temp_largest = DrawData[1];
|
|
for (TreeItem* tip = (TreeItem*)GET_FIRST(items); tip != END_OF_LIST(items); tip = (TreeItem*)GET_NEXT(tip)) {
|
|
if (!DrawData[3]) {
|
|
gr_get_string_size(&w, &h, tip->Name.c_str());
|
|
if ((w + DrawData[0]) > temp_largest) {
|
|
temp_largest = w + DrawData[0];
|
|
}
|
|
|
|
//This should really be moved to where we handle the scrolling code
|
|
//I'm not doing that right now. :)
|
|
//Or at least this should be called from that
|
|
if ( ((DrawData[2] + h) > Coords[3] && (Style & GS_NOAUTORESIZEY)) || (temp_largest > Coords[2] && (Style & GS_NOAUTORESIZEX)) ) {
|
|
DrawData[3] = 1;
|
|
tip->ShowThis = false;
|
|
} else {
|
|
tip->ShowThis = true;
|
|
}
|
|
|
|
//Do we care?
|
|
if (tip->ShowThis) {
|
|
if ((w + DrawData[0]) > DrawData[1]) {
|
|
DrawData[1] = w + DrawData[0];
|
|
}
|
|
|
|
tip->Coords[0] = Coords[0] + DrawData[0];
|
|
tip->Coords[1] = Coords[1] + DrawData[2];
|
|
tip->Coords[2] = Coords[0] + DrawData[0] + w;
|
|
tip->Coords[3] = Coords[1] + DrawData[2] + h;
|
|
|
|
DrawData[2] += h;
|
|
|
|
if (NOT_EMPTY(&tip->Children) && tip->ShowChildren) {
|
|
//Indent
|
|
DrawData[0] += TI_INDENT_PER_LEVEL;
|
|
CalcItemsSize((TreeItem*)&tip->Children, DrawData);
|
|
DrawData[0] -= TI_INDENT_PER_LEVEL;
|
|
}
|
|
}
|
|
}
|
|
if (DrawData[3] || !tip->ShowThis) {
|
|
tip->ShowThis = false;
|
|
tip->ShowChildren = false;
|
|
tip->Coords[0] = tip->Coords[1] = tip->Coords[2] = tip->Coords[3] = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
int Tree::DoRefreshSize()
|
|
{
|
|
//DrawData
|
|
//0 - indent
|
|
//1 - largest width so far
|
|
//2 - total height so far
|
|
//3 - Are we done? 0=no,1=yes
|
|
int DrawData[4] = {TI_INITIAL_INDENT,0,TI_INITIAL_INDENT_VERTICAL,0};
|
|
CalcItemsSize(&Items, DrawData);
|
|
|
|
//CalcItemsSize takes into account limited height/width
|
|
if (!(Style & GS_NOAUTORESIZEX)) {
|
|
Coords[2] = Coords[0] + DrawData[1] + TI_BORDER_WIDTH;
|
|
}
|
|
if (!(Style & GS_NOAUTORESIZEY)) {
|
|
Coords[3] = Coords[1] + DrawData[2] + TI_BORDER_HEIGHT;
|
|
if (Parent != NULL) {
|
|
if(DrawData[2] >= Parent->ChildCoords[3]) {
|
|
}
|
|
}
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
Tree::Tree(const SCP_string &in_name, int x_coord, int y_coord, void* in_associateditem, int x_width, int y_height, int in_style)
|
|
:GUIObject(in_name, x_coord, y_coord, x_width, y_height, in_style)
|
|
{
|
|
AssociatedItem = in_associateditem;
|
|
|
|
SelectedItem = NULL;
|
|
HighlightedItem = NULL;
|
|
|
|
//Set the type
|
|
Type = GT_MENU;
|
|
}
|
|
|
|
void Tree::DrawItems(TreeItem *items)
|
|
{
|
|
for (TreeItem* tip = (TreeItem*)GET_FIRST(items); tip != END_OF_LIST(items); tip = (TreeItem*)GET_NEXT(tip)) {
|
|
if (tip->ShowThis) {
|
|
if (SelectedItem == tip) {
|
|
gr_set_color_fast(&Color_text_selected);
|
|
} else if (HighlightedItem == tip) {
|
|
gr_set_color_fast(&Color_text_active);
|
|
} else {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
}
|
|
|
|
gr_string(tip->Coords[0], tip->Coords[1], tip->Name.c_str(), GR_RESIZE_NONE);
|
|
|
|
if (NOT_EMPTY(&tip->Children) && tip->ShowChildren) {
|
|
DrawItems((TreeItem*)&tip->Children);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Tree::DoDraw(float /*frametime*/)
|
|
{
|
|
|
|
if (EMPTY(&Items)) {
|
|
return;
|
|
}
|
|
|
|
DrawItems(&Items);
|
|
|
|
gr_set_color_fast(&Color_text_normal);
|
|
draw_open_rect(Coords[0], Coords[1], Coords[2], Coords[3]);
|
|
}
|
|
|
|
TreeItem* Tree::HitTest(TreeItem *items)
|
|
{
|
|
TreeItem *hti = NULL; //Highlighted item
|
|
|
|
for (TreeItem *tip = (TreeItem*)GET_FIRST(items);tip != END_OF_LIST(items); tip = (TreeItem*)GET_NEXT(tip)) {
|
|
if (OwnerSystem->GetMouseX() >= tip->Coords[0]
|
|
&& OwnerSystem->GetMouseX() <= tip->Coords[2]
|
|
&& OwnerSystem->GetMouseY() >= tip->Coords[1]
|
|
&& OwnerSystem->GetMouseY() <= tip->Coords[3]) {
|
|
|
|
hti = tip;
|
|
}
|
|
|
|
if (hti != NULL) {
|
|
return hti;
|
|
} else if (NOT_EMPTY(&tip->Children) && tip->ShowChildren) {
|
|
hti = HitTest((TreeItem*)&tip->Children);
|
|
}
|
|
|
|
if (hti != NULL) {
|
|
return hti;
|
|
}
|
|
}
|
|
return hti;
|
|
}
|
|
|
|
int Tree::DoMouseOver(float /*frametime*/)
|
|
{
|
|
HighlightedItem = HitTest(&Items);
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Tree::DoMouseDown(float /*frametime*/)
|
|
{
|
|
OwnerSystem->SetActiveObject(this);
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Tree::DoMouseUp(float /*frametime*/)
|
|
{
|
|
OwnerSystem->SetActiveObject(this);
|
|
|
|
if (HighlightedItem != nullptr) {
|
|
SetSelectedItem(HighlightedItem);
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
void Tree::MoveTreeItems(int dx, int dy, TreeItem *items)
|
|
{
|
|
for (TreeItem *tip = (TreeItem*)GET_FIRST(items);tip != END_OF_LIST(items); tip = (TreeItem*)GET_NEXT(tip)) {
|
|
if (tip->ShowThis) {
|
|
tip->Coords[0] += dx;
|
|
tip->Coords[1] += dy;
|
|
tip->Coords[2] += dx;
|
|
tip->Coords[3] += dy;
|
|
|
|
if (NOT_EMPTY(&tip->Children) && tip->ShowChildren) {
|
|
MoveTreeItems(dx, dy, (TreeItem*)&tip->Children);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Tree::DoMove(int dx, int dy)
|
|
{
|
|
MoveTreeItems(dx, dy, &Items);
|
|
}
|
|
|
|
TreeItem* Tree::AddItem(TreeItem *parent, const SCP_string &in_name, int in_data, bool in_delete_data, void (*in_function)(Tree* caller))
|
|
{
|
|
TreeItem *ni = new TreeItem;
|
|
|
|
ni->Parent = parent;
|
|
ni->Name = in_name;
|
|
ni->Data = in_data;
|
|
ni->DeleteData = in_delete_data;
|
|
ni->Function = in_function;
|
|
|
|
if (parent != NULL) {
|
|
list_append(&parent->Children, ni);
|
|
} else {
|
|
list_append(&Items, ni);
|
|
}
|
|
|
|
OnRefreshSize();
|
|
|
|
return ni;
|
|
}
|
|
|
|
void Tree::ClearItems()
|
|
{
|
|
Items.ClearAllItems();
|
|
|
|
LinkedList* cgp = GET_FIRST(&Items);
|
|
LinkedList* cgp_next;
|
|
for (; cgp != END_OF_LIST(&Items); cgp = cgp_next) {
|
|
cgp_next = GET_NEXT(cgp);
|
|
delete cgp;
|
|
}
|
|
}
|
|
|
|
void Tree::SetSelectedItem(TreeItem *item) {
|
|
SelectedItem = item;
|
|
SelectedItem->ShowChildren = !SelectedItem->ShowChildren;
|
|
if (NOT_EMPTY(&SelectedItem->Children)) {
|
|
OnRefreshSize(); //Unfortunately
|
|
}
|
|
if (SelectedItem->Function != nullptr) {
|
|
SelectedItem->Function(this);
|
|
}
|
|
}
|
|
|
|
//*****************************Text*******************************
|
|
int Text::DoRefreshSize()
|
|
{
|
|
int width;
|
|
if (Style & GS_NOAUTORESIZEX) {
|
|
width = Coords[2] - Coords[0];
|
|
} else if (Parent != NULL && (Parent->Style & GS_NOAUTORESIZEX)) {
|
|
width = Parent->ChildCoords[2] - Coords[0];
|
|
} else {
|
|
width = gr_screen.clip_right - Coords[0];
|
|
}
|
|
|
|
NumLines = split_str(Content.c_str(), width, LineLengths, LineStartPoints, MAX_TEXT_LINES);
|
|
|
|
//Find the shortest width we need to show all the shortened strings
|
|
int longest_width = 0;
|
|
int width_test;
|
|
for (int i = 0; i < NumLines; i++) {
|
|
gr_get_string_size(&width_test, NULL, LineStartPoints[i], LineLengths[i]);
|
|
if (width_test > longest_width) {
|
|
longest_width = width_test;
|
|
}
|
|
}
|
|
if (longest_width < width) {
|
|
width = longest_width;
|
|
}
|
|
|
|
//Resize along the Y axis
|
|
if (Style & GS_NOAUTORESIZEY) {
|
|
int new_height = (Coords[3] - Coords[1]) / gr_get_font_height();
|
|
|
|
if (new_height < NumLines) {
|
|
NumLines = new_height;
|
|
}
|
|
} else {
|
|
Coords[3] = Coords[1] + gr_get_font_height() * NumLines;
|
|
if (Parent != NULL) {
|
|
if (Coords[3] >= Parent->ChildCoords[3] && (Parent->Style & GS_NOAUTORESIZEY)) {
|
|
Coords[3] = Coords[1] + (Parent->ChildCoords[3] - Coords[1]);
|
|
NumLines = (Coords[3] - Coords[1]) / gr_get_font_height();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(Style & GS_NOAUTORESIZEX)) {
|
|
Coords[2] = Coords[0] + width;
|
|
}
|
|
|
|
if (Style & T_EDITTABLE) {
|
|
ChildCoords[0] = Coords[0] + 1;
|
|
ChildCoords[1] = Coords[1] + 1;
|
|
ChildCoords[2] = Coords[2] - 1;
|
|
ChildCoords[3] = Coords[3] - 1;
|
|
} else {
|
|
ChildCoords[0] = Coords[0];
|
|
ChildCoords[1] = Coords[1];
|
|
ChildCoords[2] = Coords[2];
|
|
ChildCoords[3] = Coords[3];
|
|
}
|
|
|
|
//Tell the parent to recalculate its size too
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Text::DoMouseDown(float /*frametime*/)
|
|
{
|
|
//Make this the active object
|
|
if (Style & T_EDITTABLE) {
|
|
OwnerSystem->SetActiveObject(this);
|
|
//For now, always set the cursor pos to the end
|
|
CursorPos = static_cast<int>(Content.size());
|
|
return OF_TRUE;
|
|
} else {
|
|
return OF_FALSE;
|
|
}
|
|
}
|
|
|
|
void Text::DoDraw(float /*frametime*/)
|
|
{
|
|
if (OwnerSystem->GetActiveObject() != this) {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
} else {
|
|
gr_set_color_fast(&Color_text_active);
|
|
}
|
|
|
|
if (Style & T_EDITTABLE) {
|
|
draw_open_rect(Coords[0], Coords[1], Coords[2], Coords[3]);
|
|
}
|
|
|
|
//Don't draw anything. Besides, it crashes
|
|
if (!Content.length()) {
|
|
return;
|
|
}
|
|
|
|
int font_height = gr_get_font_height();
|
|
|
|
for (int i = 0; i < NumLines; i++) {
|
|
gr_string(ChildCoords[0], ChildCoords[1] + (i*font_height) + 2, Content.substr(LineStartPoints[i] - Content.c_str(), LineLengths[i]).c_str(), GR_RESIZE_NONE);
|
|
}
|
|
}
|
|
|
|
extern int keypad_to_ascii(int c);
|
|
|
|
int Text::DoKeyPress(float /*frametime*/)
|
|
{
|
|
if (!(Style & T_EDITTABLE)) {
|
|
return OF_FALSE;
|
|
}
|
|
|
|
switch(OwnerSystem->GetKeyPressed()) {
|
|
case KEY_BACKSP:
|
|
Content = Content.substr(0, Content.length() - 1);
|
|
break;
|
|
|
|
case KEY_ENTER:
|
|
if (SaveType & T_ST_ONENTER) {
|
|
if (Save()) {
|
|
OwnerSystem->SetActiveObject(Parent);
|
|
} else {
|
|
Load();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KEY_ESC:
|
|
Load();
|
|
OwnerSystem->SetActiveObject(Parent);
|
|
break;
|
|
|
|
default: {
|
|
//Figure out what key, exactly, we have
|
|
int symbol = keypad_to_ascii(OwnerSystem->GetKeyPressed());
|
|
if (symbol == -1) {
|
|
symbol = key_to_ascii(OwnerSystem->GetKeyPressed());
|
|
}
|
|
//Is it ok with us?
|
|
if (iscntrl(symbol)) {
|
|
return OF_FALSE; //Guess not
|
|
} else if (SaveType & T_ST_INT) {
|
|
if (!isdigit(symbol) && symbol != '-') {
|
|
return OF_FALSE;
|
|
}
|
|
} else if (SaveType & T_ST_FLOAT) {
|
|
if (!isdigit(symbol) && symbol != '.' && symbol != '-') {
|
|
return OF_FALSE;
|
|
}
|
|
} else if (SaveType & T_ST_UBYTE) {
|
|
if (!isdigit(symbol)) {
|
|
return OF_FALSE;
|
|
}
|
|
} else if (!isalnum(symbol) && symbol != ' ') {
|
|
return OF_FALSE;
|
|
}
|
|
|
|
//We can add the letter to the box. Cheers.
|
|
Content += char(symbol);
|
|
if (SaveType & T_ST_REALTIME) {
|
|
Save();
|
|
}
|
|
}
|
|
}
|
|
OnRefreshSize();
|
|
return OF_TRUE;
|
|
}
|
|
|
|
Text::Text(const SCP_string &in_name, const SCP_string &in_content, int x_coord, int y_coord, int x_width, int y_height, int in_style)
|
|
:GUIObject(in_name, x_coord, y_coord, x_width, y_height, in_style)
|
|
{
|
|
Content = in_content;
|
|
NumLines = 0;
|
|
SaveType = T_ST_NONE;
|
|
CursorPos = -1; //Not editing
|
|
Type = GT_TEXT;
|
|
}
|
|
|
|
void Text::SetText(const SCP_string &in_content)
|
|
{
|
|
Content = in_content;
|
|
OnRefreshSize();
|
|
}
|
|
|
|
void Text::SetText(int the_int)
|
|
{
|
|
char buf[33];
|
|
sprintf(buf, "%d", the_int);
|
|
Content = buf;
|
|
OnRefreshSize();
|
|
}
|
|
|
|
void Text::SetText(float the_float)
|
|
{
|
|
char buf[32];
|
|
sprintf(buf, "%f", the_float);
|
|
Content = buf;
|
|
OnRefreshSize();
|
|
}
|
|
|
|
void Text::AddLine(const SCP_string &in_line)
|
|
{
|
|
Content += in_line;
|
|
OnRefreshSize();
|
|
}
|
|
|
|
|
|
|
|
void Text::SetSaveLoc(int *int_ptr, int save_method, int max_value, int min_value)
|
|
{
|
|
Assert(int_ptr != NULL); //Naughty.
|
|
Assert(min_value < max_value); //Mmm-hmm.
|
|
|
|
SaveType = T_ST_INT;
|
|
if (save_method == T_ST_CLOSE) {
|
|
SaveType |= T_ST_CLOSE;
|
|
} else if (save_method == T_ST_REALTIME) {
|
|
SaveType |= T_ST_REALTIME;
|
|
} else if (save_method == T_ST_ONENTER) {
|
|
SaveType |= T_ST_ONENTER;
|
|
}
|
|
|
|
iSavePointer = int_ptr;
|
|
SaveMax = max_value;
|
|
SaveMin = min_value;
|
|
}
|
|
|
|
void Text::SetSaveLoc(short int *sint_ptr, int save_method, short int max_value, short int min_value)
|
|
{
|
|
Assert(sint_ptr != NULL); //Naughty.
|
|
Assert(min_value < max_value); //Mmm-hmm.
|
|
|
|
SaveType = T_ST_SINT;
|
|
if (save_method == T_ST_CLOSE) {
|
|
SaveType |= T_ST_CLOSE;
|
|
} else if (save_method == T_ST_REALTIME) {
|
|
SaveType |= T_ST_REALTIME;
|
|
} else if (save_method == T_ST_ONENTER) {
|
|
SaveType |= T_ST_ONENTER;
|
|
}
|
|
|
|
siSavePointer = sint_ptr;
|
|
SaveMax = max_value;
|
|
SaveMin = min_value;
|
|
}
|
|
|
|
void Text::SetSaveLoc(float *ptr, int save_method, float max_value, float min_value)
|
|
{
|
|
Assert(ptr != NULL); //Naughty.
|
|
Assert(min_value < max_value); //Causes problems with floats
|
|
|
|
SaveType = T_ST_FLOAT;
|
|
if (save_method == T_ST_CLOSE) {
|
|
SaveType |= T_ST_CLOSE;
|
|
} else if (save_method == T_ST_REALTIME) {
|
|
SaveType |= T_ST_REALTIME;
|
|
} else if (save_method == T_ST_ONENTER) {
|
|
SaveType |= T_ST_ONENTER;
|
|
}
|
|
|
|
flSavePointer = ptr;
|
|
flSaveMax = max_value;
|
|
flSaveMin = min_value;
|
|
}
|
|
|
|
void Text::SetSaveLoc(char *ptr, int save_method, uint max_length, uint min_length)
|
|
{
|
|
Assert(ptr != NULL); //Naughty.
|
|
|
|
SaveType = T_ST_CHAR;
|
|
if (save_method == T_ST_CLOSE) {
|
|
SaveType |= T_ST_CLOSE;
|
|
} else if (save_method == T_ST_REALTIME) {
|
|
SaveType |= T_ST_REALTIME;
|
|
} else if (save_method == T_ST_ONENTER) {
|
|
SaveType |= T_ST_ONENTER;
|
|
}
|
|
|
|
chSavePointer = ptr;
|
|
uSaveMax = max_length;
|
|
uSaveMin = min_length;
|
|
}
|
|
|
|
void Text::SetSaveStringAlloc(char **ptr, int save_method, int mem_flags, uint max_length, uint min_length)
|
|
{
|
|
Assert(ptr != NULL); //Naughty.
|
|
|
|
if (mem_flags == T_ST_NEW) {
|
|
SaveType = T_ST_NEW;
|
|
} else if (mem_flags == T_ST_MALLOC) {
|
|
SaveType = T_ST_MALLOC;
|
|
} else {
|
|
return; //This MUST use some sort of mem allocation
|
|
}
|
|
|
|
SaveType |= T_ST_CHAR;
|
|
if (save_method == T_ST_CLOSE) {
|
|
SaveType |= T_ST_CLOSE;
|
|
} else if (save_method == T_ST_REALTIME) {
|
|
SaveType |= T_ST_REALTIME;
|
|
} else if (save_method == T_ST_ONENTER) {
|
|
SaveType |= T_ST_ONENTER;
|
|
}
|
|
|
|
chpSavePointer = ptr;
|
|
uSaveMax = max_length;
|
|
uSaveMin = min_length;
|
|
}
|
|
|
|
void Text::SetSaveLoc(ubyte *ptr, int save_method, int max_value, int min_value)
|
|
{
|
|
Assert(ptr != NULL); //Naughty.
|
|
Assert(min_value < max_value); //Mmm-hmm.
|
|
|
|
SaveType = T_ST_UBYTE;
|
|
if (save_method == T_ST_CLOSE) {
|
|
SaveType |= T_ST_CLOSE;
|
|
} else if (save_method == T_ST_REALTIME) {
|
|
SaveType |= T_ST_REALTIME;
|
|
} else if (save_method == T_ST_ONENTER) {
|
|
SaveType |= T_ST_ONENTER;
|
|
}
|
|
|
|
ubSavePointer = ptr;
|
|
SaveMax = max_value;
|
|
SaveMin = min_value;
|
|
}
|
|
|
|
bool Text::Save()
|
|
{
|
|
if (SaveType == T_ST_NONE) {
|
|
return false;
|
|
}
|
|
|
|
if (SaveType & T_ST_INT) {
|
|
int the_int = atoi(Content.c_str());
|
|
if (the_int <= SaveMax && the_int >= SaveMin) {
|
|
*iSavePointer = the_int;
|
|
return true;
|
|
}
|
|
} else if (SaveType & T_ST_SINT) {
|
|
short the_sint = (short)atoi(Content.c_str());
|
|
if (the_sint <= SaveMax && the_sint >= SaveMin) {
|
|
*siSavePointer = the_sint;
|
|
return true;
|
|
}
|
|
} else if (SaveType & T_ST_FLOAT) {
|
|
float the_float = (float)atof(Content.c_str());
|
|
if (the_float <= flSaveMax && the_float >= flSaveMin) {
|
|
*flSavePointer = the_float;
|
|
return true;
|
|
}
|
|
} else if (SaveType & T_ST_CHAR) {
|
|
size_t len = Content.length();
|
|
if (SaveType & T_ST_NEW && len < uSaveMax) {
|
|
if (*chpSavePointer != NULL) {
|
|
delete[] *chpSavePointer;
|
|
}
|
|
|
|
*chpSavePointer = new char[len + 1];
|
|
strcpy(*chpSavePointer, Content.c_str());
|
|
return true;
|
|
} else if (SaveType & T_ST_MALLOC && len < uSaveMax) {
|
|
if (*chpSavePointer != NULL) {
|
|
vm_free(*chpSavePointer);
|
|
}
|
|
|
|
*chpSavePointer = (char*)vm_malloc(sizeof(char) * (len + 1));
|
|
strcpy(*chpSavePointer, Content.c_str());
|
|
return true;
|
|
} else if (len < uSaveMax) {
|
|
strcpy(chSavePointer, Content.c_str());
|
|
return true;
|
|
}
|
|
|
|
} else if (SaveType & T_ST_UBYTE) {
|
|
int the_int = atoi(Content.c_str());
|
|
if (the_int <= SaveMax && the_int >= SaveMin) {
|
|
*ubSavePointer = (ubyte)the_int;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Text::Load()
|
|
{
|
|
if (SaveType == T_ST_NONE) {
|
|
return;
|
|
}
|
|
|
|
if (SaveType & T_ST_INT) {
|
|
SetText(*iSavePointer);
|
|
} else if (SaveType & T_ST_FLOAT) {
|
|
SetText(*flSavePointer);
|
|
} else if ((SaveType & T_ST_CHAR) && (SaveType & (T_ST_MALLOC | T_ST_NEW))) {
|
|
SetText(*chpSavePointer);
|
|
} else if (SaveType & T_ST_CHAR) {
|
|
SetText(chSavePointer);
|
|
} else if (SaveType & T_ST_UBYTE) {
|
|
SetText(*ubSavePointer);
|
|
} else {
|
|
Warning(LOCATION, "Unknown type (or no type) given in Text::Load() - nothing happened.");
|
|
}
|
|
}
|
|
|
|
//*****************************Checkbox*******************************
|
|
|
|
Checkbox::Checkbox(const SCP_string &in_label, int x_coord, int y_coord, void (*in_function)(Checkbox *caller), int x_width, int y_height, int in_style)
|
|
:GUIObject(in_label, x_coord, y_coord, x_width, y_height, in_style)
|
|
{
|
|
Label = in_label;
|
|
|
|
function = in_function;
|
|
|
|
IsChecked = false;
|
|
HighlightStatus = 0;
|
|
FlagPtr = NULL;
|
|
BoolFlagPtr = NULL;
|
|
|
|
//Set the type
|
|
Type = GT_CHECKBOX;
|
|
|
|
Sip = nullptr;
|
|
Wip = nullptr;
|
|
}
|
|
|
|
int Checkbox::DoRefreshSize()
|
|
{
|
|
int w, h,tw,th;
|
|
gr_get_string_size(&w, &h, Label.c_str());
|
|
tw = w;
|
|
th = h;
|
|
|
|
gr_get_string_size(&w, &h, "X");
|
|
tw += w;
|
|
th += h + CB_TEXTCHECKDIST; //Spacing b/t 'em
|
|
|
|
CheckCoords[0] = Coords[0];
|
|
CheckCoords[1] = Coords[1];
|
|
CheckCoords[2] = Coords[0] + w;
|
|
CheckCoords[3] = Coords[1] + h;
|
|
|
|
if (!(Style & GS_NOAUTORESIZEX)) {
|
|
Coords[2] = Coords[0] + tw;
|
|
}
|
|
|
|
if (!(Style & GS_NOAUTORESIZEY)) {
|
|
Coords[3] = Coords[1] + th;
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
void Checkbox::DoMove(int dx, int dy)
|
|
{
|
|
CheckCoords[0] += dx;
|
|
CheckCoords[2] += dx;
|
|
CheckCoords[1] += dy;
|
|
CheckCoords[3] += dy;
|
|
}
|
|
|
|
void Checkbox::DoDraw(float /*frametime*/)
|
|
{
|
|
if (HighlightStatus == 1) {
|
|
gr_set_color_fast(&Color_text_active);
|
|
} else if (HighlightStatus == 2) {
|
|
gr_set_color_fast(&Color_text_selected);
|
|
} else {
|
|
gr_set_color_fast(&Color_text_normal);
|
|
}
|
|
|
|
draw_open_rect(CheckCoords[0], CheckCoords[1], CheckCoords[2], CheckCoords[3], GR_RESIZE_NONE);
|
|
|
|
if ( (IsChecked && ((FlagPtr == NULL) && (BoolFlagPtr == NULL)))
|
|
|| ((FlagPtr != NULL) && ((*FlagPtr) & Flag))
|
|
|| ((BoolFlagPtr != NULL) && (*BoolFlagPtr)) ) {
|
|
gr_string(CheckCoords[0], CheckCoords[1], "X", GR_RESIZE_NONE);
|
|
}
|
|
|
|
gr_set_color_fast(&Color_text_normal);
|
|
gr_string(CheckCoords[2] + CB_TEXTCHECKDIST, CheckCoords[1], Label.c_str(), GR_RESIZE_NONE);
|
|
}
|
|
|
|
int Checkbox::DoMouseOver(float /*frametime*/)
|
|
{
|
|
if ( (OwnerSystem->GetMouseX() >= CheckCoords[0])
|
|
&& (OwnerSystem->GetMouseX() <= CheckCoords[2])
|
|
&& (OwnerSystem->GetMouseY() >= CheckCoords[1])
|
|
&& (OwnerSystem->GetMouseY() <= CheckCoords[3]) ) {
|
|
HighlightStatus = 1;
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Checkbox::DoMouseDown(float /*frametime*/)
|
|
{
|
|
OwnerSystem->SetActiveObject(this);
|
|
|
|
if ( (OwnerSystem->GetMouseX() >= CheckCoords[0])
|
|
&& (OwnerSystem->GetMouseX() <= CheckCoords[2])
|
|
&& (OwnerSystem->GetMouseY() >= CheckCoords[1])
|
|
&& (OwnerSystem->GetMouseY() <= CheckCoords[3]) ) {
|
|
HighlightStatus = 2;
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Checkbox::DoMouseUp(float /*frametime*/)
|
|
{
|
|
if ( (OwnerSystem->GetMouseX() >= CheckCoords[0])
|
|
&& (OwnerSystem->GetMouseX() <= CheckCoords[2])
|
|
&& (OwnerSystem->GetMouseY() >= CheckCoords[1])
|
|
&& (OwnerSystem->GetMouseY() <= CheckCoords[3]) ) {
|
|
HighlightStatus = 1;
|
|
|
|
if (function != NULL) {
|
|
function(this);
|
|
}
|
|
|
|
if (FlagPtr != NULL) {
|
|
if ( !(*FlagPtr & Flag) ) {
|
|
*FlagPtr |= Flag;
|
|
IsChecked = true;
|
|
} else {
|
|
*FlagPtr &= ~Flag;
|
|
IsChecked = false;
|
|
}
|
|
}
|
|
else if (BoolFlagPtr != NULL) {
|
|
*BoolFlagPtr = !(*BoolFlagPtr);
|
|
IsChecked = *BoolFlagPtr;
|
|
}
|
|
else if (Sip != nullptr)
|
|
{
|
|
Sip->flags.toggle(static_cast<Ship::Info_Flags>(Flag));
|
|
IsChecked = Sip->flags[static_cast<Ship::Info_Flags>(Flag)];
|
|
} else if (Wip != nullptr)
|
|
{
|
|
Wip->wi_flags.toggle(static_cast<Weapon::Info_Flags>(Flag));
|
|
IsChecked = Wip->wi_flags[static_cast<Weapon::Info_Flags>(Flag)];
|
|
} else {
|
|
IsChecked = !IsChecked;
|
|
}
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Checkbox::DoMouseOut(float /*frametime*/)
|
|
{
|
|
HighlightStatus = 0;
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
//*****************************ImageAnim*******************************
|
|
ImageAnim::ImageAnim(const SCP_string &in_name, const SCP_string & /*in_imagename*/, int x_coord, int y_coord, int x_width, int y_width, int in_style)
|
|
:GUIObject(in_name, x_coord, y_coord, x_width, y_width, in_style)
|
|
{
|
|
//Load the image
|
|
IsSet = false;
|
|
ImageHandle = -1;
|
|
ImageFlags = 0;
|
|
TotalFrames = 0;
|
|
FPS = 0;
|
|
Stop();
|
|
|
|
Type = GT_IMAGEANIM;
|
|
}
|
|
|
|
void ImageAnim::DoDraw(float frametime)
|
|
{
|
|
//Do nothing if nothing to draw
|
|
if (ImageHandle == -1) {
|
|
return;
|
|
}
|
|
|
|
if (PlayType != PT_STOPPED) {
|
|
if (PlayType == PT_PLAYING) {
|
|
ElapsedTime += frametime;
|
|
} else if (PlayType == PT_PLAYING_REVERSE) {
|
|
ElapsedTime -= frametime;
|
|
}
|
|
|
|
Progress = ElapsedTime/TotalTime;
|
|
}
|
|
|
|
int CurrentFrame = fl2i(Progress * TotalFrames);
|
|
if (Progress > 1.0f) {
|
|
if (ImageFlags & IF_BOUNCE) {
|
|
Progress = 1.0f;
|
|
CurrentFrame = TotalFrames;
|
|
PlayType = PT_PLAYING_REVERSE;
|
|
} else if (ImageFlags & IF_REPEAT) {
|
|
Progress = 0.0f;
|
|
CurrentFrame = 0;
|
|
PlayType = PT_PLAYING;
|
|
} else {
|
|
Progress = 1.0f;
|
|
CurrentFrame = TotalFrames;
|
|
PlayType = PT_STOPPED;
|
|
}
|
|
} else if (Progress < 1.0f) {
|
|
if (ImageFlags & IF_BOUNCE) {
|
|
Progress = 0.0f;
|
|
CurrentFrame = 0;
|
|
PlayType = PT_PLAYING;
|
|
} else if (ImageFlags & IF_REPEAT && ImageFlags & (IF_REVERSED)) {
|
|
Progress = 1.0f;
|
|
CurrentFrame = TotalFrames;
|
|
PlayType = PT_PLAYING_REVERSE;
|
|
} else {
|
|
Progress = 0.0f;
|
|
CurrentFrame = 0;
|
|
PlayType = PT_STOPPED;
|
|
}
|
|
}
|
|
|
|
//IMG_SET_FRAME(ImageHandle, CurrentFrame);
|
|
gr_set_bitmap( ImageHandle + CurrentFrame, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1-((Progress - (float)(CurrentFrame / TotalFrames)) / (float)(TotalFrames/CurrentFrame)));
|
|
IMG_DRAW(Coords[0], Coords[1]);
|
|
gr_set_bitmap( ImageHandle + CurrentFrame + 1, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, ((Progress - (float)(CurrentFrame / TotalFrames)) / (float)(TotalFrames/CurrentFrame)));
|
|
IMG_DRAW(Coords[0], Coords[1]);
|
|
}
|
|
|
|
int ImageAnim::DoRefreshSize()
|
|
{
|
|
//TODO: Fill this in
|
|
return OF_FALSE;
|
|
}
|
|
|
|
void ImageAnim::SetImage(const SCP_string &in_imagename)
|
|
{
|
|
if (in_imagename.size()) {
|
|
ImageHandle = IMG_LOAD_ANIM(in_imagename.c_str(), &TotalFrames, &FPS);
|
|
TotalTime = (float) (TotalFrames/FPS);
|
|
if (IMG_HANDLE_IS_INVALID(ImageHandle)) {
|
|
ImageHandle = IMG_LOAD(in_imagename.c_str());
|
|
if (IMG_HANDLE_IS_VALID(ImageHandle)) {
|
|
TotalFrames = 1;
|
|
} else {
|
|
TotalFrames = 0;
|
|
}
|
|
FPS = 0;
|
|
TotalTime = 0.0f;
|
|
}
|
|
}
|
|
|
|
//This takes care of setting up the play-flags
|
|
Stop();
|
|
|
|
IsSet = true;
|
|
OnRefreshSize();
|
|
}
|
|
|
|
void ImageAnim::Play(bool /*in_isreversed*/)
|
|
{
|
|
if (ImageFlags & IF_REVERSED) {
|
|
PlayType = PT_PLAYING;
|
|
}
|
|
}
|
|
|
|
void ImageAnim::Pause()
|
|
{
|
|
if (PlayType == PT_PLAYING) {
|
|
PlayType = PT_STOPPED;
|
|
} else {
|
|
PlayType = PT_STOPPED_REVERSE;
|
|
}
|
|
}
|
|
|
|
void ImageAnim::Stop()
|
|
{
|
|
if (ImageFlags & IF_REVERSED) {
|
|
PlayType = PT_STOPPED_REVERSE;
|
|
} else {
|
|
PlayType = PT_STOPPED;
|
|
}
|
|
|
|
Progress = 0.0f;
|
|
ElapsedTime = 0.0f;
|
|
}
|
|
|
|
//*****************************Slider*******************************
|
|
|
|
Slider::Slider(const SCP_string &in_label, float min, float max, int x_coord, int y_coord, void(*in_function)(Slider *caller), int x_width, int y_height, int in_style)
|
|
:GUIObject(in_label, x_coord, y_coord, x_width, y_height, in_style)
|
|
{
|
|
Label = in_label;
|
|
|
|
Min = min;
|
|
Max = max;
|
|
|
|
function = in_function;
|
|
|
|
BarWidth = x_width;
|
|
BarHeight = y_height;
|
|
|
|
SliderScale = 0.0f;
|
|
SliderWidth = 30;
|
|
SliderGrabbed = false;
|
|
|
|
gr_create_shader(&SliderShade, 50, 50, 50, 200);
|
|
|
|
//Set the type
|
|
Type = GT_SLIDER;
|
|
}
|
|
|
|
int Slider::DoRefreshSize()
|
|
{
|
|
int w, h;
|
|
|
|
// get the height of this label
|
|
gr_get_string_size(&w, &h, Label.c_str());
|
|
|
|
int bar_start_y = h + 3;
|
|
|
|
BarCoords[0] = Coords[0];
|
|
BarCoords[1] = Coords[1] + bar_start_y;
|
|
BarCoords[2] = Coords[0] + BarWidth;
|
|
BarCoords[3] = Coords[1] + bar_start_y + BarHeight;
|
|
|
|
Coords[2] = BarCoords[2];
|
|
Coords[3] = BarCoords[3];
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Slider::GetSliderOffset()
|
|
{
|
|
return fl2i(SliderScale * (BarCoords[2] - BarCoords[0] - SliderWidth) + BarCoords[0]);
|
|
}
|
|
|
|
float Slider::GetSliderPos(int x)
|
|
{
|
|
return i2fl(x - BarCoords[0]) / i2fl(BarCoords[2] - BarCoords[0] - SliderWidth);
|
|
}
|
|
|
|
void Slider::UpdateSlider(float x)
|
|
{
|
|
SliderScale = x;
|
|
|
|
CLAMP(SliderScale, 0.0, 1.0f);
|
|
|
|
if ( function != nullptr ) {
|
|
function(this);
|
|
}
|
|
}
|
|
|
|
float Slider::GetSliderValue()
|
|
{
|
|
return (SliderScale * (Max - Min) + Min);
|
|
}
|
|
|
|
void Slider::SetSliderValue(float raw_val)
|
|
{
|
|
UpdateSlider((raw_val - Min) / Max + Min);
|
|
}
|
|
|
|
void Slider::DoMove(int dx, int dy)
|
|
{
|
|
BarCoords[0] += dx;
|
|
BarCoords[2] += dx;
|
|
BarCoords[1] += dy;
|
|
BarCoords[3] += dy;
|
|
}
|
|
|
|
void Slider::DoDraw(float /*frametime*/)
|
|
{
|
|
gr_set_color_fast(&Color_text_normal);
|
|
|
|
gr_string(Coords[0], Coords[1], Label.c_str(), GR_RESIZE_NONE);
|
|
|
|
char value_txt[32];
|
|
sprintf(value_txt, "%.2f", GetSliderValue());
|
|
int w, h;
|
|
gr_get_string_size(&w, &h, value_txt);
|
|
gr_string(Coords[2] - 3 - w, Coords[1], value_txt, GR_RESIZE_NONE);
|
|
|
|
draw_open_rect(BarCoords[0], BarCoords[1], BarCoords[2], BarCoords[3], false);
|
|
|
|
auto sliderX = GetSliderOffset();
|
|
|
|
color clr;
|
|
gr_init_alphacolor(&clr, SliderShade.r, SliderShade.g, SliderShade.b, SliderShade.c);
|
|
gr_set_color_fast(&clr);
|
|
gr_rect(sliderX, BarCoords[1], SliderWidth, BarCoords[3] - BarCoords[1], false);
|
|
}
|
|
|
|
int Slider::DoMouseDown(float /*frametime*/)
|
|
{
|
|
OwnerSystem->SetActiveObject(this);
|
|
|
|
int x = OwnerSystem->GetMouseX();
|
|
int y = OwnerSystem->GetMouseY();
|
|
|
|
int sliderX = GetSliderOffset();
|
|
|
|
if ( SliderGrabbed ) {
|
|
UpdateSlider(GetSliderPos(x));
|
|
} else {
|
|
// first check if the mouse is within the slider bounds
|
|
if ( x >= sliderX
|
|
&& x <= sliderX + SliderWidth
|
|
&& y >= BarCoords[1]
|
|
&& y <= BarCoords[3] ) {
|
|
// start grabbing the slider
|
|
SliderGrabbed = true;
|
|
} else if ( x >= BarCoords[0]
|
|
&& x <= BarCoords[2]
|
|
&& y >= BarCoords[1]
|
|
&& y <= BarCoords[3]) {
|
|
// we've clicked on an area of the bar but didn't click the slider
|
|
// move the slider to the new position
|
|
UpdateSlider(GetSliderPos(x));
|
|
SliderGrabbed = true;
|
|
}
|
|
}
|
|
|
|
return OF_TRUE;
|
|
}
|
|
|
|
int Slider::DoMouseUp(float /*frametime*/)
|
|
{
|
|
SliderGrabbed = false;
|
|
|
|
return OF_TRUE;
|
|
}
|