Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F118504
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
83 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/Addons.cpp b/src/Addons.cpp
index cb83e91..39efe79 100644
--- a/src/Addons.cpp
+++ b/src/Addons.cpp
@@ -1,686 +1,681 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Addons.h"
#include "GameState.h"
#include "Functions.h"
#include "FileManager.h"
#include "Globals.h"
#include "Objects.h"
#include "GUIObject.h"
+#include "GUIOverlay.h"
#include "GUIScrollBar.h"
+#include "GUITextArea.h"
#include "GUIListBox.h"
#include "POASerializer.h"
#include "InputManager.h"
#include <string>
#include <sstream>
#include <iostream>
#include <SDL/SDL.h>
#ifdef __APPLE__
#include <SDL_ttf/SDL_ttf.h>
#else
#include <SDL/SDL_ttf.h>
#endif
using namespace std;
Addons::Addons(){
//Render the title.
title=TTF_RenderUTF8_Blended(fontTitle,_("Addons"),themeTextColor);
- //Load placeholder addon icons.
+ //Load placeholder addon icons and screenshot.
addonIcon[0]=loadImage(getDataPath()+"/gfx/addon1.png");
SDL_SetAlpha(addonIcon[0],0,0);
addonIcon[1]=loadImage(getDataPath()+"/gfx/addon2.png");
SDL_SetAlpha(addonIcon[1],0,0);
addonIcon[2]=loadImage(getDataPath()+"/gfx/addon3.png");
SDL_SetAlpha(addonIcon[2],0,0);
+
+ screenshot=loadImage(getDataPath()+"/gfx/screenshot.png");
FILE* addon=fopen((getUserPath(USER_CACHE)+"addons").c_str(),"wb");
- action=NONE;
addons=NULL;
selected=NULL;
//Clear the GUI if any.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Try to get(download) the addonsList.
if(getAddonsList(addon)==false) {
//It failed so we show the error message.
GUIObjectRoot=new GUIObject(0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
GUIObject* obj=new GUIObject(90,96,200,32,GUIObjectLabel,_("Unable to initialize addon menu:"));
obj->name="lbl";
GUIObjectRoot->addChild(obj);
obj=new GUIObject(120,130,200,32,GUIObjectLabel,error.c_str());
obj->name="lbl";
GUIObjectRoot->addChild(obj);
obj=new GUIObject(90,550,200,32,GUIObjectButton,_("Back"));
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
return;
}
//Now create the GUI.
createGUI();
}
Addons::~Addons(){
delete addons;
//Free the title surface.
SDL_FreeSurface(title);
//If the GUIObjectRoot exist delete it.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
void Addons::createGUI(){
//Downloaded the addons file now we can create the GUI.
GUIObjectRoot=new GUIObject(0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
//Create list of categories
GUISingleLineListBox *listTabs=new GUISingleLineListBox((SCREEN_WIDTH-360)/2,100,360,36);
listTabs->name="lstTabs";
listTabs->item.push_back(_("Levels"));
listTabs->item.push_back(_("Level Packs"));
listTabs->item.push_back(_("Themes"));
listTabs->value=0;
listTabs->eventCallback=this;
GUIObjectRoot->addChild(listTabs);
//Create the list for the addons.
//By default levels will be selected.
- list=new GUIListBox(SCREEN_WIDTH*0.1,160,SCREEN_WIDTH*0.8,SCREEN_HEIGHT-230);
+ list=new GUIListBox(SCREEN_WIDTH*0.1,160,SCREEN_WIDTH*0.8,SCREEN_HEIGHT-210);
addonsToList("levels");
list->name="lstAddons";
+ list->clickEvents=true;
list->eventCallback=this;
list->value=-1;
GUIObjectRoot->addChild(list);
type="levels";
- //And the buttons at the bottom of the screen.
- GUIObject* obj=new GUIObject(SCREEN_WIDTH*0.3,SCREEN_HEIGHT-50,-1,32,GUIObjectButton,_("Back"),0,true,true,GUIGravityCenter);
+ //The back button.
+ GUIObject* obj=new GUIObject(20,20,-1,32,GUIObjectButton,_("Back"));
obj->name="cmdBack";
obj->eventCallback=this;
GUIObjectRoot->addChild(obj);
- actionButton=new GUIObject(SCREEN_WIDTH*0.7,SCREEN_HEIGHT-50,-1,32,GUIObjectButton,_("Install"),0,false,true,GUIGravityCenter);
- actionButton->name="cmdInstall";
- actionButton->eventCallback=this;
- GUIObjectRoot->addChild(actionButton);
- updateButton=new GUIObject(SCREEN_WIDTH*0.5,SCREEN_HEIGHT-50,-1,32,GUIObjectButton,_("Update"),0,false,false,GUIGravityCenter);
- updateButton->name="cmdUpdate";
- updateButton->eventCallback=this;
- GUIObjectRoot->addChild(updateButton);
}
bool Addons::getAddonsList(FILE* file){
//First we download the file.
if(downloadFile(getSettings()->getValue("addon_url"),file)==false){
//NOTE: We keep the console output English so we put the string literal here twice.
cerr<<"ERROR: unable to download addons file!"<<endl;
error=_("ERROR: unable to download addons file!");
return false;
}
fclose(file);
//Load the downloaded file.
ifstream addonFile;
addonFile.open((getUserPath(USER_CACHE)+"addons").c_str());
if(!addonFile.good()) {
//NOTE: We keep the console output English so we put the string literal here twice.
cerr<<"ERROR: unable to load addon_list file!"<<endl;
/// TRANSLATORS: addon_list is the name of a file and should not be translated.
error=_("ERROR: unable to load addon_list file!");
return false;
}
//Parse the addonsfile.
TreeStorageNode obj;
{
POASerializer objSerializer;
if(!objSerializer.readNode(addonFile,&obj,true)){
//NOTE: We keep the console output English so we put the string literal here twice.
cerr<<"ERROR: Invalid file format of addons file!"<<endl;
error=_("ERROR: Invalid file format of addons file!");
return false;
}
}
//Also load the installed_addons file.
ifstream iaddonFile;
iaddonFile.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
if(!iaddonFile) {
//The installed_addons file doesn't exist, so we create it.
ofstream iaddons;
iaddons.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
iaddons<<" "<<endl;
iaddons.close();
//Also load the installed_addons file.
iaddonFile.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
if(!iaddonFile) {
//NOTE: We keep the console output English so we put the string literal here twice.
cerr<<"ERROR: Unable to create the installed_addons file."<<endl;
/// TRANSLATORS: installed_addons is the name of a file and should not be translated.
error=_("ERROR: Unable to create the installed_addons file.");
return false;
}
}
//And parse the installed_addons file.
TreeStorageNode obj1;
{
POASerializer objSerializer;
if(!objSerializer.readNode(iaddonFile,&obj1,true)){
//NOTE: We keep the console output English so we put the string literal here twice.
cerr<<"ERROR: Invalid file format of the installed_addons!"<<endl;
error=_("ERROR: Invalid file format of the installed_addons!");
return false;
}
}
//Fill the vector.
addons = new std::vector<Addon>;
fillAddonList(*addons,obj,obj1);
//Close the files.
iaddonFile.close();
addonFile.close();
return true;
}
void Addons::fillAddonList(std::vector<Addons::Addon> &list, TreeStorageNode &addons, TreeStorageNode &installed_addons){
//Loop through the blocks of the addons file.
//These should contain the types levels, levelpacks, themes.
for(unsigned int i=0;i<addons.subNodes.size();i++){
TreeStorageNode* block=addons.subNodes[i];
if(block==NULL) continue;
string type;
type=block->name;
//Now loop the entries(subNodes) of the block.
for(unsigned int i=0;i<block->subNodes.size();i++){
TreeStorageNode* entry=block->subNodes[i];
if(entry==NULL) continue;
if(entry->name=="entry" && entry->value.size()==1){
//The entry is valid so create a new Addon.
Addon addon = *(new Addon);
addon.icon=addon.screenshot=NULL;
addon.type=type;
addon.name=entry->value[0];
if(!entry->attributes["file"].empty())
addon.file=entry->attributes["file"][0];
if(!entry->attributes["folder"].empty())
addon.folder=entry->attributes["folder"][0];
if(!entry->attributes["author"].empty())
addon.author=entry->attributes["author"][0];
if(!entry->attributes["description"].empty())
addon.description=entry->attributes["description"][0];
if(entry->attributes["icon"].size()>1){
//There are (at least) two values, the url to the icon and its md5sum used for caching.
addon.icon=loadCachedImage(entry->attributes["icon"][0].c_str(),entry->attributes["icon"][1].c_str());
if(addon.icon)
SDL_SetAlpha(addon.icon,0,0);
}
if(entry->attributes["screenshot"].size()>1){
//There are (at least) two values, the url to the screenshot and its md5sum used for caching.
addon.screenshot=loadCachedImage(entry->attributes["screenshot"][0].c_str(),entry->attributes["screenshot"][1].c_str());
if(addon.screenshot)
SDL_SetAlpha(addon.screenshot,0,0);
}
if(!entry->attributes["version"].empty())
addon.version=atoi(entry->attributes["version"][0].c_str());
addon.upToDate=false;
addon.installed=false;
//Check if the addon is already installed.
for(unsigned int i=0;i<installed_addons.subNodes.size();i++){
TreeStorageNode* installed=installed_addons.subNodes[i];
if(installed==NULL) continue;
if(installed->name=="entry" && installed->value.size()==3){
if(addon.type.compare(installed->value[0])==0 && addon.name.compare(installed->value[1])==0) {
addon.installed=true;
addon.installedVersion=atoi(installed->value[2].c_str());
if(addon.installedVersion>=addon.version) {
addon.upToDate=true;
}
}
}
}
//Finally put him in the list.
list.push_back(addon);
}
}
}
}
void Addons::addonsToList(const std::string &type){
list->clearItems();
for(unsigned int i=0;i<addons->size();i++) {
//Check if the addon is from the right type.
if((*addons)[i].type==type) {
string entry = (*addons)[i].name + " by " + (*addons)[i].author;
if((*addons)[i].installed) {
if((*addons)[i].upToDate) {
entry += " *";
} else {
entry += " +";
}
}
SDL_Surface* surf=SDL_CreateRGBSurface(SDL_SWSURFACE,list->width,74,32,RMASK,GMASK,BMASK,AMASK);
//Check if there's an icon for the addon.
if((*addons)[i].icon){
applySurface(5,5,(*addons)[i].icon,surf,NULL);
}else{
if(type=="levels")
applySurface(5,5,addonIcon[0],surf,NULL);
else if(type=="levelpacks")
applySurface(5,5,addonIcon[1],surf,NULL);
else
applySurface(5,5,addonIcon[2],surf,NULL);
}
SDL_Color black={0,0,0,0};
SDL_Surface* nameSurf=TTF_RenderUTF8_Blended(fontGUI,(*addons)[i].name.c_str(),black);
SDL_SetAlpha(nameSurf,0,0xFF);
applySurface(74,-1,nameSurf,surf,NULL);
string authorLine = "by " + (*addons)[i].author;
SDL_Surface* authorSurf=TTF_RenderUTF8_Blended(fontText,authorLine.c_str(),black);
SDL_SetAlpha(authorSurf,0,0xFF);
applySurface(74,43,authorSurf,surf,NULL);
if((*addons)[i].installed){
if((*addons)[i].upToDate){
SDL_Surface* infoSurf=TTF_RenderUTF8_Blended(fontText,_("Installed"),black);
SDL_SetAlpha(infoSurf,0,0xFF);
applySurface(surf->w-infoSurf->w-32,(surf->h-infoSurf->h)/2,infoSurf,surf,NULL);
}else{
SDL_Surface* infoSurf=TTF_RenderUTF8_Blended(fontText,_("Update"),black);
SDL_SetAlpha(infoSurf,0,0xFF);
applySurface(surf->w-infoSurf->w-32,(surf->h-infoSurf->h)/2,infoSurf,surf,NULL);
}
}else{
SDL_Color grey={127,127,127};
SDL_Surface* infoSurf=TTF_RenderUTF8_Blended(fontText,_("Not installed"),grey);
SDL_SetAlpha(infoSurf,0,0xFF);
applySurface(surf->w-infoSurf->w-32,(surf->h-infoSurf->h)/2,infoSurf,surf,NULL);
}
list->addItem(entry,surf);
}
}
}
bool Addons::saveInstalledAddons(){
if(!addons) return false;
//Open the file.
ofstream iaddons;
iaddons.open((getUserPath(USER_CONFIG)+"installed_addons").c_str());
if(!iaddons) return false;
//Loop all the levels.
TreeStorageNode installed;
for(unsigned int i=0;i<addons->size();i++) {
//Check if the level is installed or not.
if((*addons)[i].installed) {
TreeStorageNode *entry=new TreeStorageNode;
entry->name="entry";
entry->value.push_back((*addons)[i].type);
entry->value.push_back((*addons)[i].name);
char version[64];
sprintf(version,"%d",(*addons)[i].installedVersion);
entry->value.push_back(version);
installed.subNodes.push_back(entry);
}
}
//And write away the file.
POASerializer objSerializer;
objSerializer.writeNode(&installed,iaddons,true,true);
return true;
}
SDL_Surface* Addons::loadCachedImage(const char* url,const char* md5sum){
//Check if the image is cached.
string imageFile=getUserPath(USER_CACHE)+"images/"+md5sum;
if(fileExists(imageFile.c_str())){
//It is, so load the image.
return loadImage(imageFile);
}else{
//Download the image.
FILE* file=fopen(imageFile.c_str(),"wb");
//Downloading failed.
if(!downloadFile(url,file)){
cerr<<"ERROR: Unable to download image from "<<url<<endl;
fclose(file);
return NULL;
}
fclose(file);
//Load the image.
return loadImage(imageFile);
}
}
void Addons::handleEvents(){
//Check if we should quit.
if(event.type==SDL_QUIT){
//Save the installed addons before exiting.
saveInstalledAddons();
setNextState(STATE_EXIT);
}
//Check if escape is pressed, if so return to the main menu.
if(inputMgr.isKeyUpEvent(INPUTMGR_ESCAPE)){
setNextState(STATE_MENU);
}
}
void Addons::logic(){}
void Addons::render(){
//Draw background.
objThemes.getBackground(true)->draw(screen);
//Draw the title.
applySurface((SCREEN_WIDTH-title->w)/2,40-TITLE_FONT_RAISE,title,screen,NULL);
}
void Addons::resize(){
//Delete the gui (if any).
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Now create a new one.
createGUI();
}
+void Addons::showAddon(){
+ //Make sure an addon is selected.
+ if(!selected)
+ return;
+
+ //Create a root object.
+ GUIObject* root=new GUIObject((SCREEN_WIDTH-600)/2,(SCREEN_HEIGHT-400)/2,600,400,GUIObjectFrame,selected->name.c_str());
+
+ //Create the 'by creator' label.
+ GUIObject* obj=new GUIObject(0,50,600,50,GUIObjectLabel,("by "+selected->author).c_str(),0,true,true,GUIGravityCenter);
+ root->addChild(obj);
+
+ //Create a back button.
+ obj=new GUIObject(0,0,150,50,GUIObjectButton,_("Back"));
+ obj->name="cmdCloseOverlay";
+ obj->eventCallback=this;
+ root->addChild(obj);
+
+ //Create the description text.
+ GUITextArea* description=new GUITextArea(10,100,370,200);
+ description->setString(selected->description.c_str());
+ description->editable=false;
+ description->resize();
+ root->addChild(description);
+
+ //Create the screenshot image.
+ obj=new GUIObject(390,100,200,150,GUIObjectImage);
+ obj->setImage(selected->screenshot?selected->screenshot:screenshot);
+ root->addChild(obj);
+
+ //Add buttons depending on the installed/update status.
+ if(selected->installed && !selected->upToDate){
+ obj=new GUIObject(0,350,300,50,GUIObjectButton,_("Remove"));
+ obj->name="cmdRemove";
+ obj->eventCallback=this;
+ root->addChild(obj);
+ obj=new GUIObject(300,350,300,50,GUIObjectButton,_("Update"));
+ obj->name="cmdUpdate";
+ obj->eventCallback=this;
+ root->addChild(obj);
+ }else{
+ if(!selected->installed){
+ obj=new GUIObject(0,350,600,50,GUIObjectButton,_("Install"));
+ obj->name="cmdInstall";
+ obj->eventCallback=this;
+ root->addChild(obj);
+ }
+ if(selected->upToDate){
+ obj=new GUIObject(0,350,600,50,GUIObjectButton,_("Remove"));
+ obj->name="cmdRemove";
+ obj->eventCallback=this;
+ root->addChild(obj);
+ }
+
+ }
+
+ new GUIOverlay(root);
+}
+
void Addons::GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType){
if(name=="lstTabs"){
if(obj->value==0){
addonsToList("levels");
type="levels";
}else if(obj->value==1){
addonsToList("levelpacks");
type="levelpacks";
}else{
addonsToList("themes");
type="themes";
}
list->value=0;
GUIEventCallback_OnEvent("lstAddons",list,GUIEventChange);
}else if(name=="lstAddons"){
- //Get the addon struct that belongs to it.
- Addon *addon=NULL;
- if(!list->item.empty()) {
- string entry = list->getItem(list->value);
- for(unsigned int i=0;i<addons->size();i++) {
- std::string prefix=(*addons)[i].name;
- if(!entry.compare(0, prefix.size(), prefix)) {
- addon=&(*addons)[i];
+ //Check which type of event.
+ if(eventType==GUIEventChange){
+ //Get the addon struct that belongs to it.
+ Addon* addon=NULL;
+ if(!list->item.empty()) {
+ string entry = list->getItem(list->value);
+ for(unsigned int i=0;i<addons->size();i++) {
+ std::string prefix=(*addons)[i].name;
+ if(!entry.compare(0, prefix.size(), prefix)) {
+ addon=&(*addons)[i];
+ }
}
}
+
+ selected=addon;
+ }else if(eventType==GUIEventClick){
+ //Make sure an addon is selected.
+ if(selected){
+ showAddon();
+ }
}
-
- selected=addon;
- updateActionButton();
- updateUpdateButton();
}else if(name=="cmdBack"){
saveInstalledAddons();
setNextState(STATE_MENU);
+ }else if(name=="cmdCloseOverlay"){
+ //We can safely delete the GUIObjectRoot, since it's handled by the GUIOverlay.
+ delete GUIObjectRoot;
+ GUIObjectRoot=NULL;
}else if(name=="cmdUpdate"){
-
//First remove the addon and then install it again.
if(type.compare("levels")==0) {
if(downloadFile(selected->file,(getUserPath(USER_DATA)+"/levels/"))!=false){
selected->upToDate=true;
selected->installedVersion=selected->version;
addonsToList("levels");
- updateActionButton();
- updateUpdateButton();
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
return;
}
}else if(type.compare("levelpacks")==0) {
if(!removeDirectory((getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/").c_str())){
cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/")<<"."<<endl;
return;
}
if(downloadFile(selected->file,(getUserPath(USER_CACHE)+"/tmp/"))!=false){
extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/levelpacks/"+selected->folder+"/");
selected->upToDate=true;
selected->installedVersion=selected->version;
addonsToList("levelpacks");
- updateActionButton();
- updateUpdateButton();
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
return;
}
}else if(type.compare("themes")==0) {
if(!removeDirectory((getUserPath(USER_DATA)+"themes/"+selected->folder+"/").c_str())){
cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"themes/"+selected->folder+"/")<<"."<<endl;
return;
}
if(downloadFile(selected->file,(getUserPath(USER_CACHE)+"/tmp/"))!=false){
extractFile((getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true)),(getUserPath(USER_DATA)+"/themes/"+selected->folder+"/"));
selected->upToDate=true;
selected->installedVersion=selected->version;
addonsToList("themes");
- updateActionButton();
- updateUpdateButton();
}else{
cerr<<"ERROR: Unable to download addon!"<<endl;
msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
return;
}
}
-
-
}else if(name=="cmdInstall"){
- switch(action) {
- case NONE:
- break;
- case INSTALL:
- //Download the addon.
- if(type.compare("levels")==0) {
- if(downloadFile(selected->file,getUserPath(USER_DATA)+"/levels/")!=false){
- selected->upToDate=true;
- selected->installed=true;
- selected->installedVersion=selected->version;
- addonsToList("levels");
- updateActionButton();
-
- //And add the level to the levels levelpack.
- LevelPack* levelsPack=getLevelPackManager()->getLevelPack("Levels");
- levelsPack->addLevel(getUserPath(USER_DATA)+"/levels/"+fileNameFromPath(selected->file));
- levelsPack->setLocked(levelsPack->getLevelCount()-1);
- }else{
- cerr<<"ERROR: Unable to download addon!"<<endl;
- msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
- return;
- }
- }else if(type.compare("levelpacks")==0) {
- if(downloadFile(selected->file,getUserPath(USER_CACHE)+"/tmp/")!=false){
- extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/levelpacks/"+selected->folder+"/");
- selected->upToDate=true;
- selected->installed=true;
- selected->installedVersion=selected->version;
- addonsToList("levelpacks");
- updateActionButton();
- updateUpdateButton();
-
- //And add the levelpack to the levelpackManager.
- getLevelPackManager()->loadLevelPack(getUserPath(USER_DATA)+"/levelpacks/"+selected->folder);
- }else{
- cerr<<"ERROR: Unable to download addon!"<<endl;
- msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
- return;
- }
- }else if(type.compare("themes")==0) {
- if(downloadFile(selected->file,getUserPath(USER_CACHE)+"/tmp/")!=false){
- extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/themes/"+selected->folder+"/");
- selected->upToDate=true;
- selected->installed=true;
- selected->installedVersion=selected->version;
- addonsToList("themes");
- updateActionButton();
- updateUpdateButton();
- }else{
- cerr<<"ERROR: Unable to download addon!"<<endl;
- msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
- return;
- }
- }
- break;
- case UNINSTALL:
- //Uninstall the addon.
- if(type.compare("levels")==0) {
- if(remove((getUserPath(USER_DATA)+"levels/"+fileNameFromPath(selected->file)).c_str())){
- cerr<<"ERROR: Unable to remove the file "<<(getUserPath(USER_DATA) + "levels/" + fileNameFromPath(selected->file))<<"."<<endl;
- return;
- }
-
- selected->upToDate=false;
- selected->installed=false;
+ //Download the addon.
+ if(type.compare("levels")==0) {
+ if(downloadFile(selected->file,getUserPath(USER_DATA)+"/levels/")!=false){
+ selected->upToDate=true;
+ selected->installed=true;
+ selected->installedVersion=selected->version;
addonsToList("levels");
- updateActionButton();
- updateUpdateButton();
- //And remove the level from the levels levelpack.
+ //And add the level to the levels levelpack.
LevelPack* levelsPack=getLevelPackManager()->getLevelPack("Levels");
- for(int i=0;i<levelsPack->getLevelCount();i++){
- if(levelsPack->getLevelFile(i)==(getUserPath(USER_DATA)+"levels/"+fileNameFromPath(selected->file))){
- //Remove the level and break out of the loop.
- levelsPack->removeLevel(i);
- break;
- }
- }
- }else if(type.compare("levelpacks")==0) {
- if(!removeDirectory((getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/").c_str())){
- cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/")<<"."<<endl;
- return;
- }
-
- selected->upToDate=false;
- selected->installed=false;
+ levelsPack->addLevel(getUserPath(USER_DATA)+"/levels/"+fileNameFromPath(selected->file));
+ levelsPack->setLocked(levelsPack->getLevelCount()-1);
+ }else{
+ cerr<<"ERROR: Unable to download addon!"<<endl;
+ msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
+ return;
+ }
+ }else if(type.compare("levelpacks")==0) {
+ if(downloadFile(selected->file,getUserPath(USER_CACHE)+"/tmp/")!=false){
+ extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/levelpacks/"+selected->folder+"/");
+ selected->upToDate=true;
+ selected->installed=true;
+ selected->installedVersion=selected->version;
addonsToList("levelpacks");
- updateActionButton();
- updateUpdateButton();
- //And remove the levelpack from the levelpack manager.
- getLevelPackManager()->removeLevelPack(selected->folder);
- }else if(type.compare("themes")==0) {
- if(!removeDirectory((getUserPath(USER_DATA)+"themes/"+selected->folder+"/").c_str())){
- cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"themes/"+selected->folder+"/")<<"."<<endl;
- return;
- }
-
- selected->upToDate=false;
- selected->installed=false;
+ //And add the levelpack to the levelpackManager.
+ getLevelPackManager()->loadLevelPack(getUserPath(USER_DATA)+"/levelpacks/"+selected->folder);
+ }else{
+ cerr<<"ERROR: Unable to download addon!"<<endl;
+ msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
+ return;
+ }
+ }else if(type.compare("themes")==0) {
+ if(downloadFile(selected->file,getUserPath(USER_CACHE)+"/tmp/")!=false){
+ extractFile(getUserPath(USER_CACHE)+"/tmp/"+fileNameFromPath(selected->file,true),getUserPath(USER_DATA)+"/themes/"+selected->folder+"/");
+ selected->upToDate=true;
+ selected->installed=true;
+ selected->installedVersion=selected->version;
addonsToList("themes");
- updateActionButton();
- updateUpdateButton();
+ }else{
+ cerr<<"ERROR: Unable to download addon!"<<endl;
+ msgBox(_("ERROR: Unable to download addon!"),MsgBoxOKOnly,_("ERROR:"));
+ return;
}
- break;
}
- }
-}
-
-void Addons::updateUpdateButton(){
- //some sanity check
- if(selected==NULL){
- updateButton->enabled=false;
- return;
- }
+ }else if(name=="cmdRemove"){
+ //Uninstall the addon.
+ if(type.compare("levels")==0) {
+ if(remove((getUserPath(USER_DATA)+"levels/"+fileNameFromPath(selected->file)).c_str())){
+ cerr<<"ERROR: Unable to remove the file "<<(getUserPath(USER_DATA) + "levels/" + fileNameFromPath(selected->file))<<"."<<endl;
+ return;
+ }
+
+ selected->upToDate=false;
+ selected->installed=false;
+ addonsToList("levels");
+
+ //And remove the level from the levels levelpack.
+ LevelPack* levelsPack=getLevelPackManager()->getLevelPack("Levels");
+ for(int i=0;i<levelsPack->getLevelCount();i++){
+ if(levelsPack->getLevelFile(i)==(getUserPath(USER_DATA)+"levels/"+fileNameFromPath(selected->file))){
+ //Remove the level and break out of the loop.
+ levelsPack->removeLevel(i);
+ break;
+ }
+ }
+ }else if(type.compare("levelpacks")==0) {
+ if(!removeDirectory((getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/").c_str())){
+ cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"levelpacks/"+selected->folder+"/")<<"."<<endl;
+ return;
+ }
+
+ selected->upToDate=false;
+ selected->installed=false;
+ addonsToList("levelpacks");
- //Check if the selected addon is installed.
- if(selected->installed){
- //It is installed, but is it uptodate?
- if(selected->upToDate){
- //The addon is installed and there is no need to show the button.
- updateButton->enabled=false;
- updateButton->visible=false;
- }else{
- //Otherwise show the button
- updateButton->enabled=true;
- updateButton->visible=true;
+ //And remove the levelpack from the levelpack manager.
+ getLevelPackManager()->removeLevelPack(selected->folder);
+ }else if(type.compare("themes")==0) {
+ if(!removeDirectory((getUserPath(USER_DATA)+"themes/"+selected->folder+"/").c_str())){
+ cerr<<"ERROR: Unable to remove the directory "<<(getUserPath(USER_DATA)+"themes/"+selected->folder+"/")<<"."<<endl;
+ return;
+ }
+
+ selected->upToDate=false;
+ selected->installed=false;
+ addonsToList("themes");
}
- }else{
- //The addon isn't installed so we can only install it.
- updateButton->enabled=false;
- }
-}
-
-
-
-void Addons::updateActionButton(){
- //some sanity check
- if(selected==NULL){
- actionButton->enabled=false;
- action=NONE;
- return;
}
- //Check if the selected addon is installed.
- if(selected->installed){
- //It is installed, but is it uptodate?
- actionButton->enabled=true;
- actionButton->caption=_("Uninstall");
- action=UNINSTALL;
- }else{
- //The addon isn't installed so we can only install it.
- actionButton->enabled=true;
- actionButton->caption=_("Install");
- action=INSTALL;
+ //NOTE: In case of install/remove/update we can delete the GUIObjectRoot, since it's managed by the GUIOverlay.
+ if(name=="cmdUpdate" || name=="cmdInstall" || name=="cmdRemove"){
+ delete GUIObjectRoot;
+ GUIObjectRoot=NULL;
}
}
diff --git a/src/Addons.h b/src/Addons.h
index 3b0889b..53e110e 100644
--- a/src/Addons.h
+++ b/src/Addons.h
@@ -1,153 +1,142 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ADDONS_H
#define ADDONS_H
#include "GameState.h"
#include "GameObjects.h"
#include "GUIObject.h"
#include "GUIListBox.h"
#include <vector>
#include <string>
#ifdef __APPLE__
#include <SDL_mixer/SDL_mixer.h>
#include <SDL_ttf/SDL_ttf.h>
#else
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_ttf.h>
#endif
//The addons menu.
class Addons: public GameState,public GUIEventCallback{
private:
//An addon entry.
struct Addon{
//The name of the addon.
string name;
//The type of addon. (Level, Levelpack, Theme)
string type;
//The link to the addon file.
string file;
//The folder to place the addon in, only for themes and levelpacks.
string folder;
//The name of the author.
string author;
//The description of the addon.
string description;
//Icon for the addon.
SDL_Surface* icon;
//Screenshot for the addon.
SDL_Surface* screenshot;
//The latest version of the addon.
int version;
//The version that the user has installed, if installed.
int installedVersion;
//Boolean if the addon is installed.
bool installed;
//Boolean if the addon is upToDate. (installedVersion==version)
bool upToDate;
};
//The title.
SDL_Surface* title;
//Placeholder icons for addons in case they don't provide custom icons.
SDL_Surface* addonIcon[3];
+
+ //Placeholder screenshot for addons in case they don't provide one.
+ SDL_Surface* screenshot;
//Vector containing all the addons.
std::vector<Addon>* addons;
//File pointing to the addon file in the userpath.
FILE* addon;
//String that should contain the error when something fails.
string error;
//The type of addon that is currently selected.
string type;
//Pointer to the addon that is selected.
Addon* selected;
//The list used for listing the addons.
GUIListBox* list;
- //The button that does install/uninstall.
- GUIObject* actionButton;
- //The button used for the updates
- GUIObject* updateButton;
-
- //The possible actions for the action button.
- //Note: Update was moved to an extra button.
- enum Action{
- NONE, INSTALL, UNINSTALL
- };
- //The current action.
- Action action;
public:
//Constructor.
Addons();
//Destructor.
~Addons();
//Method that will create the GUI.
void createGUI();
//Method that loads that downloads the addons list.
//file: Pointer to the file to download the list to.
//Returns: True if the file is downloaded successfuly.
bool getAddonsList(FILE* file);
//
void fillAddonList(std::vector<Addons::Addon> &list,TreeStorageNode &addons,TreeStorageNode &installed);
//Put all the addons of a given type in a vector.
//type: The type the addons must be.
//Returns: Vector containing the addons.
void addonsToList(const string &type);
//Method that will save the installed addons to the installed_addons file.
//Returns: True if the file is saved successfuly.
bool saveInstalledAddons();
//Method for loading a cached image and downloading if it isn't cached.
//url: The url to the image.
//md5sum: The md5sum used for caching.
//Returns: Pointer to the SDL_Surface.
SDL_Surface* loadCachedImage(const char* url,const char* md5sum);
+
+ //Method that will open a GUIOverlay with the an overview of the selected addon.
+ void showAddon();
//Inherited from GameState.
void handleEvents();
void logic();
void render();
void resize();
//Method used for GUI event handling.
//name: The name of the callback.
//obj: Pointer to the GUIObject that caused the event.
//eventType: The type of event: click, change, etc..
void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType);
-
- //Updates the text on the action button to the current action.
- void updateActionButton();
- //Switches enabled/disables of the update button
- void updateUpdateButton();
};
#endif
diff --git a/src/GUIListBox.cpp b/src/GUIListBox.cpp
index 285def0..275933a 100644
--- a/src/GUIListBox.cpp
+++ b/src/GUIListBox.cpp
@@ -1,445 +1,454 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GUIListBox.h"
using namespace std;
GUIListBox::GUIListBox(int left,int top,int width,int height,bool enabled,bool visible,int gravity):
-GUIObject(left,top,width,height,0,NULL,-1,enabled,visible,gravity),itemHeight(24),selectable(true){
+GUIObject(left,top,width,height,0,NULL,-1,enabled,visible,gravity),itemHeight(24),selectable(true),clickEvents(false){
//Set the state -1.
state=-1;
//Create the scrollbar and add it to the children.
scrollBar=new GUIScrollBar(width-16,0,16,height,1,0,0,0,1,1,true,true);
childControls.push_back(scrollBar);
}
GUIListBox::~GUIListBox(){
//Remove all items and cache.
clearItems();
//We need to delete every child we have.
for(unsigned int i=0;i<childControls.size();i++){
delete childControls[i];
}
//Deleted the childs now empty the childControls vector.
childControls.clear();
}
bool GUIListBox::handleEvents(int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when he and his parent are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left;
y+=top;
//Update the scrollbar.
if(scrollBar->visible)
b=b||scrollBar->handleEvents(x,y,enabled,visible,b);
//Set state negative.
state=-1;
//Check if the GUIListBox is visible, enabled and no event has been processed before.
if(enabled&&visible&&!b){
//The mouse location (x=i, y=j) and the mouse button (k).
int i,j;
SDL_GetMouseState(&i,&j);
//Convert the mouse location to a relative location.
i-=x+2;
j-=y+2;
//Check if the mouse is inside the GUIListBox.
if(i>=0&&i<width-4&&j>=0&&j<height-4){
//Calculate the y location with the scrollbar position.
int idx=j/itemHeight+scrollBar->value;
//If the entry isn't above the max we have an event.
if(idx>=0&&idx<(int)item.size()){
state=idx;
//Check if the left mouse button is pressed.
- if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT && value!=idx){
- value=idx;
-
- //If event callback is configured then add an event to the queue.
- if(eventCallback){
+ if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT){
+ //Check if the slected item changed.
+ if(value!=idx){
+ value=idx;
+
+ //If event callback is configured then add an event to the queue.
+ if(eventCallback){
+ GUIEvent e={eventCallback,name,this,GUIEventChange};
+ GUIEventQueue.push_back(e);
+ }
+ }
+
+ //After possibly a change event, there will always be a click event.
+ if(eventCallback && clickEvents){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
}
}
//Check for mouse wheel scrolling.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELDOWN && scrollBar->enabled){
scrollBar->value++;
if(scrollBar->value>scrollBar->maxValue)
scrollBar->value=scrollBar->maxValue;
}else if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELUP && scrollBar->enabled){
scrollBar->value--;
if(scrollBar->value<0)
scrollBar->value=0;
}
}
}
//Process child controls event except for the scrollbar.
//That's why i starts at one.
for(unsigned int i=1;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
void GUIListBox::render(int x,int y,bool draw){
if(updateScrollbar){
//Calculate the height of the content.
int maxY=0;
for(int i=0;i<images.size();i++){
maxY+=images.at(i)->h;
}
//Check if we need to show the scrollbar for many entries.
if(maxY<height){
scrollBar->maxValue=0;
scrollBar->value=0;
scrollBar->visible=false;
}else{
scrollBar->visible=true;
scrollBar->maxValue=item.size();
int yy=0;
for(int i=images.size()-1;i>0;i--){
yy+=images.at(i)->h;
if(yy>height)
break;
else
scrollBar->maxValue--;
}
scrollBar->largeChange=item.size()/4;
}
updateScrollbar=false;
}
//Rectangle the size of the GUIObject, used to draw borders.
//SDL_Rect r; //Unused local variable :/
//There's no need drawing the GUIObject when it's invisible.
if(!visible||!draw)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Draw the background box.
SDL_Rect r={x,y,width,height};
SDL_FillRect(screen,&r,0xFFFFFFFF);
//Loop through the entries and draw them.
for(int i=scrollBar->value,j=y+1;j>height,i<item.size();i++){
//Check if the current item is out side of the widget.
int yOver=images[i]->h;
if(j+images[i]->h>y+height)
yOver=y+height-j;
if(yOver>0){
if(selectable){
//Check if the mouse is hovering on current entry. If so draw borders around it.
if(state==i)
drawGUIBox(x,j-1,width,yOver+1,screen,0x00000000);
//Check if the current entry is selected. If so draw a gray background.
if(value==i)
drawGUIBox(x,j-1,width,yOver+1,screen,0xDDDDDDFF);
}
//Draw the image.
SDL_Rect clip;
clip.x=0;
clip.y=0;
clip.w=images[i]->w;
clip.h=yOver;
applySurface(x,j,images[i],screen,&clip);
}else{
break;
}
j+=images[i]->h;
}
//Draw borders around the whole thing.
drawGUIBox(x,y,width,height,screen,0x00000000);
//We now need to draw all the children of the GUIObject.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(x,y,draw);
}
}
void GUIListBox::clearItems(){
item.clear();
for(unsigned int i=0;i<images.size();i++){
SDL_FreeSurface(images[i]);
}
images.clear();
}
void GUIListBox::addItem(std::string name, SDL_Surface* image){
item.push_back(name);
if(image){
itemHeight=image->h;
images.push_back(image);
}else if(!image&&!name.empty()){
SDL_Color black={0,0,0,0};
SDL_Surface* tmp=TTF_RenderUTF8_Blended(fontText,name.c_str(),black);
images.push_back(tmp);
}
updateScrollbar=true;
}
void GUIListBox::updateItem(int index, std::string newText, SDL_Surface* newImage){
item.at(index)=newText;
if(newImage){
itemHeight=newImage->h;
SDL_FreeSurface(images.at(index));
images.at(index)=newImage;
}else if(!newImage&&!newText.empty()){
SDL_FreeSurface(images.at(index));
SDL_Color black={0,0,0,0};
SDL_Surface* tmp=TTF_RenderUTF8_Blended(fontText,newText.c_str(),black);
images.at(index)=tmp;
}
updateScrollbar=true;
}
std::string GUIListBox::getItem(int index){
return item.at(index);
}
GUISingleLineListBox::GUISingleLineListBox(int left,int top,int width,int height,bool enabled,bool visible,int gravity):
GUIObject(left,top,width,height,0,NULL,-1,enabled,visible,gravity),animation(0){}
bool GUISingleLineListBox::handleEvents(int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when he and his parent are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
state&=~0xF;
if(enabled&&visible){
//The mouse location (x=i, y=j) and the mouse button (k).
int i,j,k;
k=SDL_GetMouseState(&i,&j);
//Convert the mouse location to a relative location.
i-=x;
j-=y;
//The selected button.
//0=nothing 1=left 2=right.
int idx=0;
//Check which button the mouse is above.
if(i>=0&&i<width&&j>=0&&j<height){
if(i<26 && i<width/2){
//The left arrow.
idx=1;
}else if(i>=width-26){
//The right arrow.
idx=2;
}
}
//If idx is 0 it means the mous doesn't hover any arrow so reset animation.
if(idx==0)
animation=0;
//Check if there's a mouse button press or not.
if(k&SDL_BUTTON(1)){
if(((state>>4)&0xF)==idx)
state|=idx;
}else{
state|=idx;
}
//Check if there's a mouse press.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT && idx){
state=idx|(idx<<4);
}else if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT && idx && ((state>>4)&0xF)==idx){
int m=(int)item.size();
if(m>0){
if(idx==2){
idx=value+1;
if(idx<0||idx>=m) idx=0;
if(idx!=value){
value=idx;
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
}
}else if(idx==1){
idx=value-1;
if(idx<0||idx>=m) idx=m-1;
if(idx!=value){
value=idx;
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
}
}
}
}
if(event.type==SDL_MOUSEBUTTONUP) state&=0xF;
}else{
//Set state zero.
state=0;
}
//Also let the children handle their events.
for(unsigned int i=0;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
void GUISingleLineListBox::render(int x,int y,bool draw){
//Rectangle the size of the GUIObject, used to draw borders.
SDL_Rect r;
//There's no need drawing the GUIObject when it's invisible.
if(!visible)
return;
//NOTE: logic in the render method since it's the only part that gets called every frame.
if((state&0xF)==0x1 || (state&0xF)==0x2){
animation++;
if(animation>20)
animation=-20;
}
//Get the absolute x and y location.
x+=left;
y+=top;
if(gravity==GUIGravityCenter)
gravityX=int(width/2);
else if(gravity==GUIGravityRight)
gravityX=width;
x-=gravityX;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || item[value].compare(cachedCaption)!=0){
//Free the cache.
SDL_FreeSurface(cache);
cache=NULL;
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=item[value];
}
//Draw the text.
if(value>=0 && value<(int)item.size()){
//Get the text.
const char* lp=item[value].c_str();
//Check if the text is empty or not.
if(lp!=NULL && lp[0]){
if(!cache){
SDL_Color color;
if(inDialog)
color=themeTextColorDialog;
else
color=themeTextColor;
cache=TTF_RenderUTF8_Blended(fontGUI,lp,color);
//If the text is too wide then we change to smaller font (?)
//NOTE: The width is 32px smaller (2x16px for the arrows).
if(cache->w>width-32){
SDL_FreeSurface(cache);
cache=TTF_RenderUTF8_Blended(fontGUISmall,lp,color);
}
}
if(draw){
//Center the text both vertically as horizontally.
r.x=x+(width-cache->w)/2;
r.y=y+(height-cache->h)/2-GUI_FONT_RAISE;
//Draw the text and free the surface afterwards.
SDL_BlitSurface(cache,NULL,screen,&r);
}
}
}
if(draw){
//Draw the arrows.
SDL_Rect r2={48,0,16,16};
r.x=x;
if((state&0xF)==0x1)
r.x+=abs(animation/2);
r.y=y+4;
if(inDialog)
applySurface(r.x,r.y,arrowLeft2,screen,NULL);
else
applySurface(r.x,r.y,arrowLeft1,screen,NULL);
r.x=x+width-16;
if((state&0xF)==0x2)
r.x-=abs(animation/2);
if(inDialog)
applySurface(r.x,r.y,arrowRight2,screen,NULL);
else
applySurface(r.x,r.y,arrowRight1,screen,NULL);
}
//We now need to draw all the children of the GUIObject.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(x,y,draw);
}
}
diff --git a/src/GUIListBox.h b/src/GUIListBox.h
index ad1530d..642b2d5 100644
--- a/src/GUIListBox.h
+++ b/src/GUIListBox.h
@@ -1,124 +1,127 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GUILISTBOX_H
#define GUILISTBOX_H
#include "GUIObject.h"
#include "GUIScrollBar.h"
//GUIObject that displays a list.
//It extends GUIObject because it's a special GUIObject.
class GUIListBox:public GUIObject{
public:
//Vector containing the entries of the list.
std::vector<std::string> item;
std::vector<SDL_Surface*> images;
bool selectable;
+
+ //Boolean if the listbox should send click events.
+ bool clickEvents;
private:
//Scrollbar used when there are more entries than fit on the screen.
GUIScrollBar* scrollBar;
//Boolean if update for scrollbar is needed.
bool updateScrollbar;
//Integer for height of a single item.
//TODO: Remove this variable and because different items can be different size.
int itemHeight;
public:
//Constructor.
//left: The relative x location of the GUIListBox.
//top: The relative y location of the GUIListBox.
//witdh: The width of the GUIListBox.
//height: The height of the GUIListBox.
//enabled: Boolean if the GUIListBox is enabled or not.
//visible: Boolean if the GUIListBox is visisble or not.
GUIListBox(int left=0,int top=0,int width=0,int height=0,bool enabled=true,bool visible=true,int gravity=GUIGravityLeft);
//Destructor
~GUIListBox();
//Method to remove all items and clear cache.
void clearItems();
//Method to add an item to the widget.
//name: Text of the item.
//image: Custom image for the widget, if NULL the image will be generated from name string.
void addItem(std::string name, SDL_Surface* image=NULL);
//Method to update an item in the widget.
//index: index of the item.
//newName: New text for the item.
//newImage: New custom image for the widget, if NULL the image will be generated from newName string.
void updateItem(int index, std::string newText, SDL_Surface* newImage=NULL);
//Method used the get item names from the widget.
//index: index of the item.
std::string getItem(int index);
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIListBox.
//x: The x location to draw the GUIListBox. (x+left)
//y: The y location to draw the GUIListBox. (y+top)
virtual void render(int x=0,int y=0,bool draw=true);
};
//GUIObject that displays a list on only one line.
//Instead of clicking the entries of the list you iterate through them.
//It extends GUIObject because it's a special GUIObject.
class GUISingleLineListBox:public GUIObject{
public:
//Vector containing the entries of the list.
std::vector<std::string> item;
//Integer used for the animation of the arrow.
int animation;
public:
//Constructor.
//left: The relative x location of the GUIListBox.
//top: The relative y location of the GUIListBox.
//witdh: The width of the GUIListBox.
//height: The height of the GUIListBox.
//enabled: Boolean if the GUIListBox is enabled or not.
//visible: Boolean if the GUIListBox is visisble or not.
GUISingleLineListBox(int left=0,int top=0,int width=0,int height=0,bool enabled=true,bool visible=true,int gravity=GUIGravityLeft);
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIListBox.
//x: The x location to draw the GUIListBox. (x+left)
//y: The y location to draw the GUIListBox. (y+top)
virtual void render(int x=0,int y=0,bool draw=true);
};
#endif
diff --git a/src/GUIObject.cpp b/src/GUIObject.cpp
index eac6eea..bd9cd81 100644
--- a/src/GUIObject.cpp
+++ b/src/GUIObject.cpp
@@ -1,670 +1,684 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GUIObject.h"
#include <iostream>
#include <list>
using namespace std;
//Set the GUIObjectRoot to NULL.
GUIObject* GUIObjectRoot=NULL;
//Initialise the event queue.
list<GUIEvent> GUIEventQueue;
void GUIObjectHandleEvents(bool kill){
//Check if user resizes the window.
if(event.type==SDL_VIDEORESIZE){
//onVideoResize();
//Don't let other objects process this event (?)
return;
}
//Make sure that GUIObjectRoot isn't null.
if(GUIObjectRoot)
GUIObjectRoot->handleEvents();
//Check for SDL_QUIT.
if(event.type==SDL_QUIT && kill){
//We get a quit event so enter the exit state.
setNextState(STATE_EXIT);
delete GUIObjectRoot;
GUIObjectRoot=NULL;
return;
}
//Keep calling events until there are none left.
while(!GUIEventQueue.empty()){
//Get one event and remove it from the queue.
GUIEvent e=GUIEventQueue.front();
GUIEventQueue.pop_front();
//If an eventCallback exist call it.
if(e.eventCallback){
e.eventCallback->GUIEventCallback_OnEvent(e.name,e.obj,e.eventType);
}
}
//We empty the event queue just to be sure.
GUIEventQueue.clear();
}
GUIObject::~GUIObject(){
- if(cache){
+ //The cache is used as the actual image for GUIObjectImage and shouldn't be freed.
+ if(cache && type!=GUIObjectImage){
SDL_FreeSurface(cache);
cache=NULL;
}
//We need to delete every child we have.
for(unsigned int i=0;i<childControls.size();i++){
delete childControls[i];
}
//Deleted the childs now empty the childControls vector.
childControls.clear();
}
bool GUIObject::handleEvents(int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when he and his parent are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
//Type specific event handling.
switch(type){
case GUIObjectButton:
//Set state to 0.
state=0;
//Only check for events when the object is both enabled and visible.
if(enabled&&visible){
//The mouse location (x=i, y=j) and the mouse button (k).
int i,j,k;
k=SDL_GetMouseState(&i,&j);
//Check if the mouse is inside the GUIObject.
if(i>=x&&i<x+width&&j>=y&&j<y+height){
//We have hover so set state to one.
state=1;
//Check for a mouse button press.
if(k&SDL_BUTTON(1))
state=2;
//Check if there's a mouse press and the event hasn't been already processed.
if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT && !b){
//If event callback is configured then add an event to the queue.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
//Event has been processed.
b=true;
}
}
}
break;
case GUIObjectCheckBox:
//Set state to 0.
state=0;
//Only check for events when the object is both enabled and visible.
if(enabled&&visible){
//The mouse location (x=i, y=j) and the mouse button (k).
int i,j,k;
k=SDL_GetMouseState(&i,&j);
//Check if the mouse is inside the GUIObject.
if(i>=x&&i<x+width&&j>=y&&j<y+height){
//We have hover so set state to one.
state=1;
//Check for a mouse button press.
if(k&SDL_BUTTON(1))
state=2;
//Check if there's a mouse press and the event hasn't been already processed.
if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT && !b){
//It's a checkbox so toggle the value.
value=value?0:1;
//If event callback is configured then add an event to the queue.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
//Event has been processed.
b=true;
}
}
}
break;
case GUIObjectTextBox:
//NOTE: We don't reset the state to have a "focus" effect.
//Only check for events when the object is both enabled and visible.
if(enabled&&visible){
//Check if there's a key press and the event hasn't been already processed.
if(state==2 && event.type==SDL_KEYDOWN && !b){
//Get the keycode.
int key=(int)event.key.keysym.unicode;
//Check if the key is supported.
if(key>=32&&key<=126){
//Add the key to the text after the carrot.
caption.insert((size_t)value,1,char(key));
value=clamp(value+1,0,caption.length());
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}else if(event.key.keysym.sym==SDLK_BACKSPACE){
//We need to remove a character so first make sure that there is text.
if(caption.length()>0&&value>0){
//Remove the character before the carrot.
value=clamp(value-1,0,caption.length());
caption.erase((size_t)value,1);
this->key=SDLK_BACKSPACE;
keyHoldTime=0;
keyTime=5;
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
}else if(event.key.keysym.sym==SDLK_DELETE){
//We need to remove a character so first make sure that there is text.
if(caption.length()>0){
//Remove the character after the carrot.
value=clamp(value,0,caption.length());
caption.erase((size_t)value,1);
this->key=SDLK_DELETE;
keyHoldTime=0;
keyTime=5;
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
}else if(event.key.keysym.sym==SDLK_RIGHT){
value=clamp(value+1,0,caption.length());
this->key=SDLK_RIGHT;
keyHoldTime=0;
keyTime=5;
}else if(event.key.keysym.sym==SDLK_LEFT){
value=clamp(value-1,0,caption.length());
this->key=SDLK_LEFT;
keyHoldTime=0;
keyTime=5;
}
//The event has been processed.
b=true;
}else if(state==2 && event.type==SDL_KEYUP && !b){
//Check if released key is the same as the holded key.
if(event.key.keysym.sym==key){
//It is so stop the key.
key=-1;
}
}
//The mouse location (x=i, y=j) and the mouse button (k).
int i,j,k;
k=SDL_GetMouseState(&i,&j);
//Check if the mouse is inside the GUIObject.
if(i>=x&&i<x+width&&j>=y&&j<y+height){
//We can only increase our state. (nothing->hover->focus).
if(state!=2){
state=1;
}
//Also update the cursor type.
currentCursor=CURSOR_CARROT;
//Check for a mouse button press.
if(k&SDL_BUTTON(1)){
//We have focus.
state=2;
//Move carrot to the place clicked
int click=i-x;
if(!cache){
value=0;
}else if(click>cache->w){
value=caption.length();
}else{
unsigned int wid=0;
for(unsigned int i=0;i<caption.length();i++){
int advance;
TTF_GlyphMetrics(fontText,caption[i],NULL,NULL,NULL,NULL,&advance);
wid+=advance;
if(click<(int)wid-(int)advance/2){
value=i;
break;
}
}
}
}
}else{
//The mouse is outside the TextBox.
//If we don't have focus but only hover we lose it.
if(state==1){
state=0;
}
//If it's a click event outside the textbox then we blur.
if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT){
//Set state to 0.
state=0;
}
}
}
break;
}
//Also let the children handle their events.
for(unsigned int i=0;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
//Found on gmane.comp.lib.sdl mailing list, see: http://comments.gmane.org/gmane.comp.lib.sdl/33664
//Original code by "Patricia Curtis" and later modified by "Jason"
static void SetSurfaceTrans(SDL_Surface* Src,double PercentTrans){
Uint8 Sbpp = Src->format->BytesPerPixel;
Uint8 *Sbits;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
int amask = 0x000000ff;
int cmask = 0xffffff00;
#else
int amask = 0xff000000;
int cmask = 0x00ffffff;
int Shift = 24;
#endif
int x,y;
Uint32 Pixels;
Uint32 Alpha;
for(y=0;y<Src->h;y++)
{
for(x=0;x<Src->w;x++)
{
Sbits = ((Uint8 *)Src->pixels+(y*Src->pitch)+(x*Sbpp));
Pixels = *((Uint32 *)(Sbits));
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
Alpha = Pixels & mask;
#else
Alpha = (Pixels&amask)>>Shift;
#endif
Alpha*=PercentTrans;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
*((Uint32 *)(Sbits)) = (Pixels & cmask)|Alpha;
#else
*((Uint32 *)(Sbits)) = (Pixels & cmask)|(Alpha<<Shift);
#endif
}
}
}
void GUIObject::render(int x,int y,bool draw){
//Rectangle the size of the GUIObject, used to draw borders.
SDL_Rect r;
//There's no need drawing the GUIObject when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || caption.compare(cachedCaption)!=0 || width<=0){
//Free the cache.
SDL_FreeSurface(cache);
cache=NULL;
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget
if(autoWidth)
width=-1;
}
//Now do the type specific rendering.
switch(type){
case GUIObjectLabel:
{
//The rectangle is simple.
r.x=x;
r.y=y;
r.w=width;
r.h=height;
//We don't draw a background and/or border since that label is transparent.
//Get the caption and make sure it isn't empty.
const char* lp=caption.c_str();
if(lp!=NULL && lp[0]){
//Render the text using the small font.
if(cache==NULL){
SDL_Color color;
if(inDialog)
color=themeTextColorDialog;
else
color=themeTextColor;
cache=TTF_RenderUTF8_Blended(fontText,lp,color);
if(width<=0)
width=cache->w;
}
if(draw){
if(gravity==GUIGravityCenter)
gravityX=(width-cache->w)/2;
else if(gravity==GUIGravityRight)
gravityX=width-cache->w;
else
gravityX=0;
//Center the text vertically and draw it to the screen.
r.y=y+(height - cache->h)/2;
r.x+=gravityX;
SDL_BlitSurface(cache,NULL,screen,&r);
}
}
}
break;
case GUIObjectCheckBox:
{
//The rectangle is simple.
r.x=x;
r.y=y;
r.w=width;
r.h=height;
//Get the text.
const char* lp=caption.c_str();
//Make sure it isn't empty.
if(lp!=NULL && lp[0]){
//We render black text.
if(!cache){
SDL_Color color;
if(inDialog)
color=themeTextColorDialog;
else
color=themeTextColor;
cache=TTF_RenderUTF8_Blended(fontText,lp,color);
}
if(draw){
//Calculate the location, center it vertically.
r.x=x;
r.y=y+(height - cache->h)/2;
//Draw the text and free the surface.
SDL_BlitSurface(cache,NULL,screen,&r);
}
}
if(draw){
//Draw the check (or not).
SDL_Rect r1={0,0,16,16};
if(value==1||value==2)
r1.x=value*16;
r.x=x+width-20;
r.y=y+(height-16)/2;
SDL_BlitSurface(bmGUI,&r1,screen,&r);
}
}
break;
case GUIObjectButton:
{
//Get the text.
const char* lp=caption.c_str();
//Make sure the text isn't empty.
if(lp!=NULL && lp[0]){
if(!cache){
SDL_Color color;
if(inDialog)
color=themeTextColorDialog;
else
color=themeTextColor;
if(!smallFont)
cache=TTF_RenderUTF8_Blended(fontGUI,lp,color);
else
cache=TTF_RenderUTF8_Blended(fontGUISmall,lp,color);
if(!enabled)
SetSurfaceTrans(cache,0.5);
if(width<=0){
width=cache->w+50;
if(gravity==GUIGravityCenter){
gravityX=int(width/2);
}else if(gravity==GUIGravityRight){
gravityX=width;
}else{
gravityX=0;
}
}
}
if(draw){
//Center the text both vertically as horizontally.
r.x=x-gravityX+(width-cache->w)/2;
r.y=y+(height-cache->h)/2-GUI_FONT_RAISE;
//Check if the arrows don't fall of.
if(cache->w+32<=width){
//Create a rectangle that selects the right image from bmGUI,
SDL_Rect r2={64,0,16,16};
if(state==1){
if(inDialog){
applySurface(x-gravityX+(width-cache->w)/2+4+cache->w+5,y+2,arrowLeft2,screen,NULL);
applySurface(x-gravityX+(width-cache->w)/2-25,y+2,arrowRight2,screen,NULL);
}else{
applySurface(x-gravityX+(width-cache->w)/2+4+cache->w+5,y+2,arrowLeft1,screen,NULL);
applySurface(x-gravityX+(width-cache->w)/2-25,y+2,arrowRight1,screen,NULL);
}
}else if(state==2){
if(inDialog){
applySurface(x-gravityX+(width-cache->w)/2+4+cache->w,y+2,arrowLeft2,screen,NULL);
applySurface(x-gravityX+(width-cache->w)/2-20,y+2,arrowRight2,screen,NULL);
}else{
applySurface(x-gravityX+(width-cache->w)/2+4+cache->w,y+2,arrowLeft1,screen,NULL);
applySurface(x-gravityX+(width-cache->w)/2-20,y+2,arrowRight1,screen,NULL);
}
}
}
//Draw the text and free the surface.
SDL_BlitSurface(cache,NULL,screen,&r);
}
}
}
break;
case GUIObjectTextBox:
{
//FIXME: Logic in the render method since that is update constant.
if(key!=-1){
//Increase the key time.
keyHoldTime++;
//Make sure the deletionTime isn't to short.
if(keyHoldTime>=keyTime){
keyHoldTime=0;
keyTime--;
if(keyTime<1)
keyTime=1;
//Now check the which key it was.
switch(key){
case SDLK_BACKSPACE:
{
//Remove the character before the carrot.
value=clamp(value-1,0,caption.length());
caption.erase((size_t)value,1);
break;
}
case SDLK_DELETE:
{
//Remove the character after the carrot.
value=clamp(value,0,caption.length());
caption.erase((size_t)value,1);
break;
}
case SDLK_LEFT:
{
value=clamp(value-1,0,caption.length());
break;
}
case SDLK_RIGHT:
{
value=clamp(value+1,0,caption.length());
break;
}
}
}
}
if(draw){
//Default background opacity
int clr=50;
//If hovering or focused make background more visible.
if(state==1)
clr=128;
else if (state==2)
clr=100;
//Draw the box.
Uint32 color=0xFFFFFF00|clr;
drawGUIBox(x,y,width,height,screen,color);
}
//Get the text.
const char* lp=caption.c_str();
//Make sure it isn't empty.
if(lp!=NULL && lp[0]){
if(!cache){
//Draw the black text.
SDL_Color black={0,0,0,0};
cache=TTF_RenderUTF8_Blended(fontText,lp,black);
}
if(draw){
//Calculate the location, center it vertically.
r.x=x+2;
r.y=y+(height - cache->h)/2;
//Draw the text.
SDL_Rect tmp={0,0,width-2,25};
SDL_BlitSurface(cache,&tmp,screen,&r);
//Only draw the carrot when focus.
if(state==2){
r.x=x;
r.y=y+4;
r.w=2;
r.h=height-8;
int advance;
for(int n=0;n<value;n++){
TTF_GlyphMetrics(fontText,caption[n],NULL,NULL,NULL,NULL,&advance);
r.x+=advance;
}
//Make sure that the carrot is inside the textbox.
if(r.x<x+width)
SDL_FillRect(screen,&r,0);
}
}
}else{
//Only draw the carrot when focus.
if(state==2&&draw){
r.x=x+4;
r.y=y+4;
r.w=2;
r.h=height-8;
SDL_FillRect(screen,&r,0);
}
}
}
break;
case GUIObjectFrame:
{
if(draw){
//Create a rectangle the size of the button and fill it.
Uint32 color=0xDDDDDDFF;
drawGUIBox(x,y,width,height,screen,color);
}
//Get the title text.
const char* lp=caption.c_str();
//Make sure it isn't empty.
if(lp!=NULL && lp[0]){
if(!cache){
cache=TTF_RenderUTF8_Blended(fontGUI,lp,themeTextColorDialog);
}
if(draw){
//Calculate the location, center horizontally and vertically relative to the top.
r.x=x+(width-cache->w)/2;
r.y=y+6-GUI_FONT_RAISE;
//Draw the text and free the surface.
SDL_BlitSurface(cache,NULL,screen,&r);
}
}
}
break;
+ case GUIObjectImage:
+ {
+ r.x=x;
+ r.y=y;
+ r.w=width;
+ r.h=height;
+
+ //Make sure the image isn't null.
+ if(cache){
+ applySurface(r.x,r.y,cache,screen,NULL);
+ }
+ }
+ break;
}
//We now need to draw all the children of the GUIObject.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(x,y,draw);
}
}
diff --git a/src/GUIObject.h b/src/GUIObject.h
index ef5e7e4..0366f28 100644
--- a/src/GUIObject.h
+++ b/src/GUIObject.h
@@ -1,236 +1,248 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GUIOBJECT_H
#define GUIOBJECT_H
#include "Globals.h"
#include "Functions.h"
#include "FileManager.h"
#include <string>
#include <vector>
#include <list>
//Ids for the different GUIObject types.
//None is a special type, it has no visual form.
const int GUIObjectNone=0;
//A label used to dispaly text.
const int GUIObjectLabel=1;
//Button which will invoke an event when pressed.
const int GUIObjectButton=2;
//Checkbox which represents a boolean value and can be toggled.
const int GUIObjectCheckBox=3;
//A text box used to enter text.
const int GUIObjectTextBox=5;
//Frame which is like a container.
const int GUIObjectFrame=6;
+//Image that can be positioned, but won't cause any events.
+const int GUIObjectImage=7;
//Widget gravity properties
const int GUIGravityLeft=0;
const int GUIGravityCenter=1;
const int GUIGravityRight=2;
//The event id's.
//A click event used for e.g. buttons.
const int GUIEventClick=0;
//A change event used for e.g. textboxes.
const int GUIEventChange=1;
class GUIObject;
//Class that is used as event callback.
class GUIEventCallback{
public:
//This method is called when an event is fired.
//name: The name of the event.
//obj: Pointer to the GUIObject which caused this event.
//eventType: The type of event as defined above.
virtual void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType)=0;
};
//Class containing the
class GUIObject{
public:
//The relative x location of the GUIObject.
int left;
//The relative y location of the GUIObject.
int top;
//The width of the GUIObject.
int width;
//The height of the GUIObject.
int height;
//The type of the GUIObject.
int type;
//The value of the GUIObject.
//It depends on the type of GUIObject what it means.
int value;
//The name of the GUIObject.
std::string name;
//The caption of the GUIObject.
//It depends on the type of GUIObject what it is.
std::string caption;
//Boolean if the GUIObject is enabled.
bool enabled;
//Boolean if the GUIObject is visible.
bool visible;
//Vector containing the children of the GUIObject.
std::vector<GUIObject*> childControls;
//Event callback used to invoke events.
GUIEventCallback* eventCallback;
//Widget's gravity to centering
int gravity;
int gravityX;
bool autoWidth;
//Use small font
bool smallFont;
//Is the parent widget a dialog?
bool inDialog;
protected:
//The state of the GUIObject.
//It depends on the type of GUIObject where it's used for.
int state;
//Surface containing some gui images.
SDL_Surface* bmGUI;
//Surface that can be used to cache rendered text.
SDL_Surface* cache;
//String containing the old caption to detect if it changed.
std::string cachedCaption;
//Boolean containing the previous enabled state.
bool cachedEnabled;
//Integer containing the key that is holded.
int key;
//Integer containing the time the key is pressed.
int keyHoldTime;
//The time it takes to invoke the key action again.
int keyTime;
public:
//Constructor.
//left: The relative x location of the GUIObject.
//top: The relative y location of the GUIObject.
//witdh: The width of the GUIObject.
//height: The height of the GUIObject.
//type: The type of the GUIObject.
//caption: The text on the GUIObject.
//value: The value of the GUIObject.
//enabled: Boolean if the GUIObject is enabled or not.
//visible: Boolean if the GUIObject is visisble or not.
//gravity: The way the GUIObject needs to be aligned.
GUIObject(int left=0,int top=0,int width=0,int height=0,int type=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
left(left),top(top),width(width),height(height),
type(type),gravity(gravity),value(value),
enabled(enabled),visible(visible),
eventCallback(NULL),state(0),
cache(NULL),cachedEnabled(enabled),gravityX(0),smallFont(false),
key(-1),keyHoldTime(0),keyTime(5)
{
//Make sure that caption isn't NULL before setting it.
if(caption){
GUIObject::caption=caption;
//And set the cached caption.
cachedCaption=caption;
}
if(width<=0)
autoWidth=true;
else
autoWidth=false;
inDialog=false;
//Load the gui images.
bmGUI=loadImage(getDataPath()+"gfx/gui.png");
}
//Destructor.
virtual ~GUIObject();
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIObject.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Draw widget or just update it without drawing
virtual void render(int x=0,int y=0,bool draw=true);
void addChild(GUIObject* obj){
//Add widget add a child
childControls.push_back(obj);
//Check if parent (this class) is a dialog. If not copy value from parent.
if(type==GUIObjectFrame)
obj->inDialog=true;
else
obj->inDialog=inDialog;
}
//Method for getting a child from a GUIObject.
//NOTE: This method doesn't search recursively.
//name: The name of the child to return.
//Returns: Pointer to the requested child, NULL otherwise.
GUIObject* getChild(std::string name){
//Look for a child with the name.
for(unsigned int i=0;i<childControls.size();i++)
if(childControls[i]->name==name)
return childControls[i];
//Not found so return NULL.
return NULL;
}
+
+ //Method for setting the image of the GUIObjectImage.
+ //NOTE: This method should only be used for GUIObjectImages.
+ //image: SDL_Surface containing the image.
+ void setImage(SDL_Surface* image){
+ if(type!=GUIObjectImage)
+ return;
+
+ cache=image;
+ }
};
//Method used to handle the GUIEvents from the GUIEventQueue.
//kill: Boolean if an SDL_QUIT event may kill the GUIObjectRoot.
void GUIObjectHandleEvents(bool kill=false);
//A structure containing the needed variables to call an event.
struct GUIEvent{
//Event callback used to invoke the event.
GUIEventCallback* eventCallback;
//The name of the event.
std::string name;
//Pointer to the object which invoked the event.
GUIObject* obj;
//The type of event.
int eventType;
};
//List used to queue the gui events.
extern std::list<GUIEvent> GUIEventQueue;
#endif
diff --git a/src/GUITextArea.h b/src/GUITextArea.h
index 118cac9..19af7c5 100644
--- a/src/GUITextArea.h
+++ b/src/GUITextArea.h
@@ -1,114 +1,114 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GUITEXTAREA_H
#define GUITEXTAREA_H
#ifdef __APPLE__
#include <SDL_ttf/SDL_ttf.h>
#else
#include <SDL/SDL_ttf.h>
#endif
#include "GUIObject.h"
#include "GUIScrollBar.h"
//GUIObject based widget for multiline text input.
//It extends GUIObject because it's a special GUIObject.
class GUITextArea:public GUIObject{
private:
//Method that will remove the last character of the text.
void backspaceChar();
void deleteChar();
//Methods to move the carrot by one character/line.
void moveCarrotLeft();
void moveCarrotRight();
void moveCarrotUp();
void moveCarrotDown();
//Method to adjust view so carrot stays visible.
void adjustView();
//Pointer to the font used in the widget.
TTF_Font* widgetFont;
//Widget's text.
//One line per vector element.
std::vector<std::string> lines;
//Cache for rendered lines.
//Will be updated alongside with variable text.
std::vector<SDL_Surface*> linesCache;
//Variable for carrot position.
//NOTE: We will use variable "value" from GUIObject for position within the current line.
int currentLine;
//Height of the font.
int fontHeight;
//Scrollbar widget.
GUIScrollBar* scrollBar;
GUIScrollBar* scrollBarH;
public:
//Constructor.
- //left: The relative x location of the GUIListBox.
- //top: The relative y location of the GUIListBox.
- //witdh: The width of the GUIListBox.
- //height: The height of the GUIListBox.
- //enabled: Boolean if the GUIListBox is enabled or not.
- //visible: Boolean if the GUIListBox is visisble or not.
+ //left: The relative x location of the GUITextArea.
+ //top: The relative y location of the GUITextArea.
+ //witdh: The width of the GUITextArea.
+ //height: The height of the GUITextArea.
+ //enabled: Boolean if the GUITextArea is enabled or not.
+ //visible: Boolean if the GUITextArea is visisble or not.
GUITextArea(int left=0,int top=0,int width=0,int height=0,bool enabled=true,bool visible=true);
//Destructor
~GUITextArea();
//Method used to change the font.
//font: Pointer to the font
void setFont(TTF_Font* font);
//Method used to reposition scrollbars after a resize.
void resize();
//Method used to get widget's text in a single string.
std::string getString();
//Method used to set widget's text.
void setString(std::string input);
void setStringArray(std::vector<std::string> input);
//Bool if user can edit text in the widget.
bool editable;
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUITextArea.
//x: The x location to draw the GUITextArea. (x+left)
//y: The y location to draw the GUITextArea. (y+top)
virtual void render(int x=0,int y=0,bool draw=true);
};
#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, May 16, 8:39 AM (16 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
64142
Default Alt Text
(83 KB)
Attached To
Mode
R79 meandmyshadow
Attached
Detach File
Event Timeline