/* * 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("= (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, ""); 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(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(Flag)); IsChecked = Sip->flags[static_cast(Flag)]; } else if (Wip != nullptr) { Wip->wi_flags.toggle(static_cast(Flag)); IsChecked = Wip->wi_flags[static_cast(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; }