Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
344 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/util/file-system.cpp b/util/file-system.cpp
index 5f7f3b13..97d991ca 100644
--- a/util/file-system.cpp
+++ b/util/file-system.cpp
@@ -1,1884 +1,1884 @@
#ifdef USE_ALLEGRO
#include <allegro.h>
/* FIXME: replace with <winalleg.h> */
#ifdef _WIN32
#define BITMAP dummyBITMAP
#include <windows.h>
#undef BITMAP
#endif
#endif
#include <algorithm>
#include "funcs.h"
#include "file-system.h"
#include "thread.h"
#include "system.h"
#include "utf.h"
// #include "globals.h"
#include <dirent.h>
#include <sstream>
#include <exception>
#include <string>
#include <fstream>
#include <ostream>
#include "token.h"
#include "zip/unzip.h"
#include "zip/ioapi.h"
#include "7z/7zFile.h"
#include "7z/7z.h"
#include "7z/7zAlloc.h"
#include "7z/7zCrc.h"
#ifndef USE_ALLEGRO
/* some sfl symbols conflict with allegro */
#include "sfl/sfl.h"
#include "sfl/sfldir.h"
#endif
#ifdef _WIN32
// #define _WIN32_IE 0x400
#include <shlobj.h>
#endif
using namespace std;
/* some filesystem access can only be done by one thread at a time. specifically, sfl
* has its own allocator that is meant to be used in a single-threaded manner.
* rather than try to add locks to sfl we just wrap all sfl calls with a lock.
*
* initialize() must be called to initialize this lock
*/
// Util::Thread::Lock lock;
namespace Path{
/* remove extra path separators (/) */
string sanitize(string path){
size_t double_slash = path.find("//");
while (double_slash != string::npos){
path.erase(double_slash, 1);
double_slash = path.find("//");
}
/* Remove /./ from paths because its redundant */
size_t useless = path.find("/./");
while (useless != string::npos){
path.erase(useless, 2);
useless = path.find("/./");
}
return path;
}
static string removeEndSlashes(string path){
size_t last = path.rfind("/");
while (path.size() > 0 && last == path.size() - 1){
path.erase(last, 1);
last = path.rfind("/");
}
return path;
}
static int invert(int c){
if (c == '\\'){
return '/';
}
return c;
}
std::string invertSlashes(string str){
transform(str.begin(), str.end(), str.begin(), invert);
return str;
}
const string & Path::path() const {
return mypath;
}
const string Path::getExtension() const {
size_t dot = mypath.rfind('.');
if (dot == string::npos){
return "";
} else {
return mypath.substr(dot + 1);
}
}
bool Path::isEmpty() const {
return mypath.empty();
}
Path::~Path(){
}
Path::Path(){
}
Path::Path(const std::string & path):
mypath(sanitize(invertSlashes(path))){
}
Path::Path(const Path & path):
mypath(sanitize(invertSlashes(path.path()))){
}
RelativePath::RelativePath(){
}
RelativePath::RelativePath(const std::string & path):
Path(path){
if (! path.empty() && path[0] == '/'){
ostringstream out;
out << "Relative path '" << path << "' cannot start with a /. Only absolute paths can start with /";
throw Storage::IllegalPath(__FILE__, __LINE__, out.str());
}
}
/* a/b/c/d -> b/c/d */
std::string stripFirstDir(const std::string & str){
if (str.find("/") != std::string::npos || str.find( "\\") != std::string::npos){
std::string temp = str;
size_t rem = temp.find("/");
if (rem != std::string::npos){
return str.substr(rem+1,str.size());
}
rem = temp.find("\\");
if( rem != std::string::npos ){
return str.substr(rem+1,str.size());
}
}
return str;
}
static vector<string> splitPath(string path){
vector<string> all;
if (path.size() > 0 && path[0] == '/'){
all.push_back("/");
}
size_t found = path.find('/');
while (found != string::npos){
if (found > 0){
all.push_back(path.substr(0, found));
}
path.erase(0, found + 1);
found = path.find('/');
}
if (path.size() != 0){
all.push_back(path);
}
return all;
}
static string joinPath(const vector<string> & paths){
ostringstream out;
bool first = true;
for (vector<string>::const_iterator it = paths.begin(); it != paths.end(); it++){
if (!first){
out << '/';
}
out << *it;
first = false;
}
return out.str();
}
/* a/b/c/d -> a/b/c
* a/b/c/d/ -> a/b/c
*/
static string dirname(string path){
vector<string> paths = splitPath(path);
if (paths.size() > 1){
paths.pop_back();
return joinPath(paths);
} else if (paths.size() == 1){
if (paths[0] == "/"){
return "/";
}
return ".";
} else {
return ".";
}
/*
while (path.size() > 0 && path[path.size() - 1] == '/'){
path.erase(path.size() - 1);
}
if (path.find("/") != string::npos ||
path.find("\\") != string::npos){
size_t rem = path.find_last_of("/");
if (rem != string::npos){
return path.substr(0, rem + 1);
}
rem = path.find_last_of("\\");
if (rem != string::npos){
return path.substr(0, rem + 1);
}
}
return "";
*/
}
RelativePath::RelativePath(const RelativePath & path):
Path(path){
}
RelativePath RelativePath::removeFirstDirectory() const {
return RelativePath(stripFirstDir(path()));
}
bool RelativePath::isFile() const {
vector<string> paths = splitPath(path());
return paths.size() == 1;
}
RelativePath RelativePath::firstDirectory() const {
vector<string> paths = splitPath(path());
if (paths.size() > 1){
return RelativePath(paths[0]);
}
return RelativePath();
}
RelativePath RelativePath::getDirectory() const {
return RelativePath(dirname(path()));
}
RelativePath RelativePath::getFilename() const {
return RelativePath(stripDir(path()));
}
bool RelativePath::operator<(const RelativePath & path) const {
return this->path() < path.path();
}
bool RelativePath::operator==(const RelativePath & path) const {
return this->path() == path.path();
}
bool RelativePath::operator!=(const RelativePath & path) const {
return !(*this == path);
}
RelativePath RelativePath::join(const RelativePath & him) const {
return RelativePath(path() + "/" + him.path());
}
RelativePath & RelativePath::operator=(const RelativePath & copy){
setPath(copy.path());
return *this;
}
AbsolutePath::AbsolutePath(){
}
AbsolutePath::AbsolutePath(const std::string & path):
Path(path){
}
AbsolutePath::AbsolutePath(const AbsolutePath & path):
Path(path){
}
AbsolutePath & AbsolutePath::operator=(const AbsolutePath & copy){
setPath(copy.path());
return *this;
}
AbsolutePath AbsolutePath::removeFirstDirectory() const {
return AbsolutePath(stripFirstDir(path()));
}
bool AbsolutePath::operator==(const AbsolutePath & path) const {
return removeEndSlashes(this->path()) == removeEndSlashes(path.path());
}
bool AbsolutePath::operator!=(const AbsolutePath & path) const {
return !(*this == path);
}
bool AbsolutePath::operator<(const AbsolutePath & path) const {
return this->path() < path.path();
}
string AbsolutePath::firstDirectory() const {
vector<string> paths = splitPath(path());
if (paths.size() > 1){
return paths[0];
}
return removeEndSlashes(path());
}
bool AbsolutePath::isFile() const {
vector<string> paths = splitPath(path());
return path().find("/") == string::npos;
// paths.size() == 1;
}
RelativePath AbsolutePath::remove(const AbsolutePath & what) const {
string real = path();
real.erase(0, what.path().size());
while (real.find("/") == 0){
real.erase(0, 1);
}
return RelativePath(real);
}
AbsolutePath AbsolutePath::getDirectory() const {
return AbsolutePath(dirname(path()));
}
AbsolutePath AbsolutePath::getFilename() const {
return AbsolutePath(stripDir(path()));
}
std::string AbsolutePath::getLastComponent() const {
if (getFilename().path() == ""){
return stripDir(removeEndSlashes(path()));
}
return getFilename().path();
}
AbsolutePath AbsolutePath::join(const RelativePath & path) const {
return AbsolutePath(this->path() + "/" + path.path());
}
InsensitivePath::InsensitivePath(const Path & what):
Path(what){
}
bool InsensitivePath::operator==(const Path & path) const {
return Util::upperCaseAll(this->path()) == Util::upperCaseAll(path.path());
}
std::string removeExtension(const std::string & str){
if (str.find(".") != std::string::npos){
return str.substr(0, str.find_last_of("."));
}
return str;
}
AbsolutePath replaceExtension(const Filesystem::AbsolutePath & input, const std::string & extension){
return AbsolutePath(removeExtension(input.path()) + "." + extension);
}
/* a/b/c/d -> d */
std::string stripDir(const std::string & str){
if (str.find("/") != std::string::npos || str.find("\\") != std::string::npos){
std::string temp = str;
size_t rem = temp.find_last_of( "/" );
if (rem != std::string::npos){
return str.substr(rem+1,str.size());
}
rem = temp.find_last_of( "\\" );
if( rem != std::string::npos ){
return str.substr(rem+1,str.size());
}
}
return str;
}
/* a/b/c/d -> a/b/c/ */
std::string stripFilename(const std::string & str){
std::string temp = str;
if( str.find( "/") != std::string::npos || str.find( "\\") != std::string::npos ){
size_t rem = temp.find_last_of( "/" );
if( rem != std::string::npos ){
return str.substr(0,rem+1);
}
rem = temp.find_last_of( "\\" );
if( rem != std::string::npos ){
return str.substr(0,rem+1);
}
}
return "";
}
}
namespace Storage{
Exception::Exception(const std::string & where, int line, const std::string & file):
Exc::Base(where, line),
reason(file){
}
Exception::Exception(const std::string & where, int line, const Exc::Base & nested, const std::string & file):
Exc::Base(where, line, nested),
reason(file){
}
Exception::Exception(const Exception & copy):
Exc::Base(copy),
reason(copy.reason){
}
Exception::~Exception() throw (){
}
const std::string Exception::getReason() const {
return reason;
}
NotFound::NotFound(const std::string & where, int line, const std::string & file):
Exception(where, line, file){
}
NotFound::NotFound(const std::string & where, int line, const Exc::Base & nested, const std::string & file):
Exception(where, line, nested, file){
}
NotFound::NotFound(const NotFound & copy):
Exception(copy){
}
NotFound::~NotFound() throw (){
}
IllegalPath::IllegalPath(const std::string & where, int line, const std::string & file):
Exception(where, line, file){
}
IllegalPath::IllegalPath(const std::string & where, int line, const Exc::Base & nested, const std::string & file):
Exception(where, line, nested, file){
}
IllegalPath::IllegalPath(const IllegalPath & copy):
Exception(copy){
}
IllegalPath::~IllegalPath() throw(){
}
System::System(){
}
System::~System(){
}
vector<Filesystem::AbsolutePath> System::getFiles(const Filesystem::AbsolutePath & dataPath, const Filesystem::RelativePath & find, bool caseInsensitive){
if (find.isFile()){
return getFiles(dataPath, find.path(), caseInsensitive);
}
/* split the path into its consituent parts
* a/b/c -> a/b and c
* search for a/b, then search for c in the results
*/
Filesystem::RelativePath directory = find.getDirectory();
Filesystem::RelativePath file = find.getFilename();
vector<Filesystem::AbsolutePath> more = getFiles(dataPath, directory, caseInsensitive);
vector<Filesystem::AbsolutePath> out;
for (vector<Filesystem::AbsolutePath>::iterator it = more.begin(); it != more.end(); it++){
Filesystem::AbsolutePath path = *it;
/* if its not a directory then we can't keep searching */
if (::System::isDirectory(path.path())){
vector<Filesystem::AbsolutePath> findMore = getFiles(path, file, caseInsensitive);
out.insert(out.end(), findMore.begin(), findMore.end());
}
}
return out;
}
Filesystem::AbsolutePath System::findContainer(const RelativePath & dataPath){
try{
return find(RelativePath(dataPath.path() + ".zip"));
} catch (const NotFound & fail){
}
try{
return find(RelativePath(dataPath.path() + ".7z"));
} catch (const NotFound & fail){
}
throw NotFound(dataPath.path(), __LINE__, __FILE__);
}
vector<Filesystem::AbsolutePath> System::getContainerFilesRecursive(const Filesystem::AbsolutePath & dataPath){
vector<Filesystem::AbsolutePath> out;
vector<Filesystem::AbsolutePath> zips = getFilesRecursive(dataPath, "*.zip");
out.insert(out.end(), zips.begin(), zips.end());
zips = getFilesRecursive(dataPath, "*.7z");
out.insert(out.end(), zips.begin(), zips.end());
return out;
}
vector<Path::AbsolutePath> System::getContainerFiles(const AbsolutePath & dataPath){
vector<Filesystem::AbsolutePath> out;
vector<Filesystem::AbsolutePath> zips = getFiles(dataPath, "*.zip");
out.insert(out.end(), zips.begin(), zips.end());
zips = getFiles(dataPath, "*.7z");
out.insert(out.end(), zips.begin(), zips.end());
return out;
}
vector<Filesystem::AbsolutePath> System::getContainerFiles(const RelativePath & path){
vector<Filesystem::AbsolutePath> out;
vector<Filesystem::AbsolutePath> zips = getFiles(path, Filesystem::RelativePath("*.zip"), false);
out.insert(out.end(), zips.begin(), zips.end());
zips = getFiles(path, Filesystem::RelativePath("*.7z"), false);
out.insert(out.end(), zips.begin(), zips.end());
return out;
}
static Util::ReferenceCount<System> self;
System & instance(){
if (self != NULL){
return *self;
}
self = new Filesystem(Util::getDataPath2());
return *self;
}
File::File(){
}
File::~File(){
}
class NormalFile: public File {
public:
NormalFile(const Path::AbsolutePath & path, Access mode = Read):
path(path){
ios_base::openmode iosMode = fstream::in;
switch (mode){
case Read: iosMode = fstream::in; break;
case Write: iosMode = fstream::out; break;
case ReadWrite: iosMode = fstream::in | fstream::out; break;
}
in.open(path.path().c_str(), iosMode | fstream::binary);
in >> noskipws;
}
Token * location(){
Token * head = new Token();
*head << "file";
*head << path.path();
return head;
}
long getModificationTime(){
return ::System::getModificationTime(path.path());
}
bool canStream(){
return true;
}
long tell(){
return in.tellg();
}
void reset(){
in.clear();
}
off_t seek(off_t position, int whence){
switch (whence){
case SEEK_SET: in.seekg(position, ios::beg); break;
case SEEK_CUR: in.seekg(position, ios::cur); break;
case SEEK_END: in.seekg(position, ios::end); break;
}
return in.tellg();
}
bool eof(){
return in.eof();
}
int getSize(){
streampos here = in.tellg();
in.seekg(0, ios::end);
int length = in.tellg();
in.seekg(here, ios::beg);
return length;
}
bool good(){
return in.good();
}
File & operator>>(unsigned char & c){
in >> c;
return *this;
}
int readLine(char * output, int size){
in.read(output, size);
return in.gcount();
}
~NormalFile(){
in.close();
}
protected:
const Path::AbsolutePath path;
std::fstream in;
};
StringFile::StringFile(const std::string & start):
data(start),
stream(start){
stream >> noskipws;
}
Token * StringFile::location(){
Token * head = new Token();
*head << "<string file>";
return head;
}
long StringFile::getModificationTime(){
/* FIXME: maybe return INT_MAX or something? */
return 0;
}
void StringFile::reset(){
/* TODO or nothing..? */
}
int StringFile::readLine(char * output, int size){
stream.read(output, size);
return stream.gcount();
}
bool StringFile::canStream(){
return true;
}
int StringFile::getSize(){
return data.size();
}
long StringFile::tell(){
return stream.tellg();
}
off_t StringFile::seek(off_t position, int whence){
switch (whence){
case SEEK_SET: stream.seekg(position, ios::beg); break;
case SEEK_CUR: stream.seekg(position, ios::cur); break;
case SEEK_END: stream.seekg(position, ios::end); break;
}
return stream.tellg();
}
bool StringFile::eof(){
return stream.eof();
}
bool StringFile::good(){
return stream.good();
}
File & StringFile::operator>>(unsigned char & c){
stream >> c;
return *this;
}
StringFile::~StringFile(){
}
/* For 7z */
class LzmaContainer{
public:
LzmaContainer(const string & path, const Filesystem::AbsolutePath & start):
path(path),
start(start){
allocator.Alloc = SzAlloc;
allocator.Free = SzFree;
allocatorTemporary.Alloc = SzAllocTemp;
allocatorTemporary.Free = SzFreeTemp;
if (InFile_Open(&archiveStream.file, path.c_str())){
/* Error */
}
FileInStream_CreateVTable(&archiveStream);
LookToRead_CreateVTable(&lookStream, False);
lookStream.realStream = &archiveStream.s;
LookToRead_Init(&lookStream);
CrcGenerateTable();
SzArEx_Init(&database);
int ok = SzArEx_Open(&database, &lookStream.s, &allocator, &allocatorTemporary);
if (ok == SZ_OK){
/* Can read files */
UInt16 * name = NULL;
size_t tempSize = 0;
for (unsigned int index = 0; index < database.db.NumFiles; index++){
size_t offset = 0;
size_t outSizeProcessed = 0;
const CSzFileItem * file = database.db.Files + index;
size_t length;
if (file->IsDir){
continue;
}
length = SzArEx_GetFileNameUtf16(&database, index, NULL);
if (length > tempSize){
delete[] name;
name = new UInt16[length];
memset(name, 0, length);
tempSize = length;
}
SzArEx_GetFileNameUtf16(&database, index, name);
files[Utf::utf16_to_utf8(name)] = index;
}
delete[] name;
}
}
void readFile(const Path::AbsolutePath & path, unsigned char ** buffer, size_t * size){
Path::RelativePath find(path.remove(start));
*buffer = 0;
*size = 0;
if (files.find(find.path()) != files.end()){
int index = files[find.path()];
UInt32 block = 0;
size_t offset = 0;
size_t processed = 0;
int ok = SzArEx_Extract(&database, &lookStream.s, index,
&block, buffer, size, &offset, &processed,
&allocator, &allocatorTemporary);
if (ok != SZ_OK){
Global::debug(0) << "Could not read file from 7z archive: " << path.path() << endl;
return;
}
if (offset != 0){
memmove(*buffer, *buffer + offset, processed);
}
/* The original size is the size of the block buffer, but we only care
* about the size of the actual file.
*/
*size = processed;
}
}
vector<string> getFiles(){
vector<string> names;
for (map<string, int>::iterator it = files.begin(); it != files.end(); it++){
names.push_back(it->first);
}
return names;
}
string getPath() const {
return path;
}
string getMount() const {
return start.path();
}
long getModificationTime(const Path::AbsolutePath & path){
if (files.find(path.path()) != files.end()){
int index = files[path.path()];
const CSzFileItem * file = database.db.Files + index;
if (file->MTimeDefined){
CNtfsFileTime time = file->MTime;
/* Divide by 10 million here? */
return (time.Low | ((UInt64)time.High << 32)) / 10000000;
/*
unsigned year, mon, day, hour, min, sec;
UInt64 v64 = (time.Low | ((UInt64)time.High << 32)) / 10000000;
Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
unsigned t;
UInt32 v;
sec = (unsigned)(v64 % 60); v64 /= 60;
min = (unsigned)(v64 % 60); v64 /= 60;
hour = (unsigned)(v64 % 24); v64 /= 24;
v = (UInt32)v64;
const int PERIOD_4 = (4 * 365 + 1);
const int PERIOD_100 = (PERIOD_4 * 25 - 1);
const int PERIOD_400 = (PERIOD_100 * 4 + 1);
year = (unsigned)(1601 + v / PERIOD_400 * 400);
v %= PERIOD_400;
t = v / PERIOD_100; if (t == 4) t = 3; year += t * 100; v -= t * PERIOD_100;
t = v / PERIOD_4; if (t == 25) t = 24; year += t * 4; v -= t * PERIOD_4;
t = v / 365; if (t == 4) t = 3; year += t; v -= t * 365;
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)){
ms[1] = 29;
}
for (mon = 1; mon <= 12; mon++){
unsigned s = ms[mon - 1];
if (v < s)
break;
v -= s;
}
day = (unsigned)v + 1;
struct tm outTime;
memset(&outTime, 0, sizeof(outTime));
outTime.tm_sec = sec;
outTime.tm_min = min;
outTime.tm_hour = hour;
outTime.tm_mday = day;
outTime.tm_mon = mon;
/ * tm_year The number of years since 1900. * /
outTime.tm_year = year - 1900;
outTime.tm_isdst = -1;
return mktime(&outTime);
*/
}
}
return 0;
}
virtual ~LzmaContainer(){
SzArEx_Free(&database, &allocator);
File_Close(&archiveStream.file);
}
string path;
Path::AbsolutePath start;
CFileInStream archiveStream;
CLookToRead lookStream;
CSzArEx database;
ISzAlloc allocator;
ISzAlloc allocatorTemporary;
/* Map the filename to its index in the 7z database */
map<string, int> files;
};
/* overlays:
* add x/y.zip
* y.zip contains
* example.txt
* try to read x/example.txt, goto y.zip
* or y.zip could contain y/
*
* z.zip contains
* example.txt
*
* how can you prevent two zip files from providing the same files?
*/
class ZipContainer{
public:
ZipContainer(const string & path, const Filesystem::AbsolutePath & start):
path(path),
start(start),
locked(false){
zipFile = unzOpen(path.c_str());
if (zipFile == NULL){
throw Exception(__FILE__, __LINE__, "Could not open zip file");
}
if (unzGoToFirstFile(zipFile) != UNZ_OK){
throw Exception(__FILE__, __LINE__, "Could not get to first file");
}
do{
char filename[1024];
unz_file_info fileInfo;
unzGetCurrentFileInfo(zipFile, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0);
files.push_back(string(filename));
} while (unzGoToNextFile(zipFile) != UNZ_END_OF_LIST_OF_FILE);
/*
zlib_filefunc64_32_def functions;
functions.open_file_func = __real_open;
functions.tell_file_func = __real_lseek;
functions.seek_file_func = __real_lseek;
*/
/*
unzFile zip = unzOpen(path.c_str());
unz_global_info info;
if (unzGetGlobalInfo(zip, &info) == UNZ_OK){
Global::debug(0) << "Entries: " << info.number_entry << std::endl;
}
if (unzGoToFirstFile(zip) != UNZ_OK){
throw Exception(__FILE__, __LINE__, "Could not get to first file");
}
do{
char filename[1024];
unz_file_info fileInfo;
unzGetCurrentFileInfo(zip, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0);
Global::debug(0) << "Got file " << filename << std::endl;
Global::debug(0) << " Compressed " << fileInfo.compressed_size << " uncompressed " << fileInfo.uncompressed_size << std::endl;
} while (unzGoToNextFile(zip) != UNZ_END_OF_LIST_OF_FILE);
unzClose(zip);
*/
}
~ZipContainer(){
if (zipFile != NULL){
unzClose(zipFile);
}
}
string getPath() const {
return path;
}
string getMount() const {
return start.path();
}
long modificationTime(){
char filename[1024];
unz_file_info fileInfo;
unzGetCurrentFileInfo(zipFile, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0);
struct tm outTime;
memset(&outTime, 0, sizeof(outTime));
outTime.tm_sec = fileInfo.tmu_date.tm_sec;
outTime.tm_min = fileInfo.tmu_date.tm_min;
outTime.tm_hour = fileInfo.tmu_date.tm_hour;
outTime.tm_mday = fileInfo.tmu_date.tm_mday;
outTime.tm_mon = fileInfo.tmu_date.tm_mon;
/* tm_year The number of years since 1900. */
outTime.tm_year = fileInfo.tmu_date.tm_year - 1900;
outTime.tm_isdst = -1;
return mktime(&outTime);
}
void findFile(const Path::AbsolutePath & file){
Path::RelativePath find(file.remove(start));
if (unzLocateFile(zipFile, find.path().c_str(), 2) != UNZ_OK){
Global::debug(0) << "Could not find " << find.path() << std::endl;
} else {
Global::debug(1) << "Found " << find.path() << " in zip file " << path << std::endl;
}
}
int currentFileSize(){
char filename[1024];
unz_file_info fileInfo;
unzGetCurrentFileInfo(zipFile, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0);
return fileInfo.uncompressed_size;
}
int read(char * buffer, int size){
// Global::debug(0) << "offset before read " << unzGetOffset(zipFile) << " tell " << unztell(zipFile) << std::endl;
int got = unzReadCurrentFile(zipFile, buffer, size);
// Global::debug(0) << "offset after read " << unzGetOffset(zipFile) << " tell " << unztell(zipFile) << std::endl;
if (got <= 0){
throw Exception(__FILE__, __LINE__, "Could not read bytes from zip");
}
return got;
}
long tell(){
return unztell(zipFile);
}
void open(const Path::AbsolutePath & file){
if (locked){
std::ostringstream out;
char filename[1024];
unz_file_info fileInfo;
unzGetCurrentFileInfo(zipFile, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0);
out << "Could not open zip file entry " << file.path() << " because a zip file is already open: " << filename;
throw Exception(__FILE__, __LINE__, out.str());
}
findFile(file);
if (unzOpenCurrentFile(zipFile) != UNZ_OK){
std::ostringstream out;
out << "Could not open zip file entry " << file.path();
throw Exception(__FILE__, __LINE__, out.str());
}
locked = true;
}
void close(){
unzCloseCurrentFile(zipFile);
locked = false;
}
vector<string> getFiles() const {
return files;
}
protected:
/* Path to the zip file itself */
const string path;
/* Where overlay starts */
const Path::AbsolutePath start;
unzFile zipFile;
vector<string> files;
/* Only one file can be opened at a time, so if another file is opened
* while locked=true then throw an error
*/
bool locked;
};
class ZipFile: public File {
public:
ZipFile(const Path::AbsolutePath & path, const Util::ReferenceCount<ZipContainer> & zip):
path(path),
zip(zip),
atEof(false),
position(0){
zip->open(path);
}
virtual ~ZipFile(){
zip->close();
}
long getModificationTime(){
return zip->modificationTime();
}
Token * location(){
Token * head = new Token();
*head << "container";
/* container zipfile mount-point file-inside-zip */
*head << zip->getPath();
*head << zip->getMount();
*head << path.path();
return head;
}
off_t seek(off_t where, int whence){
/* It seems that minizip is not capable of seeking in a specific file in a zip
* container so we have to re-open the file and read `position' bytes to
* emulate the seek behavior.
*/
switch (whence){
case SEEK_SET: {
if (where >= position){
/* Read bytes until we hit the offset */
position += skipBytes(where - position);
} else {
zip->close();
zip->open(path);
/* re-open file and read where bytes */
position = skipBytes(where);
}
break;
}
case SEEK_CUR: {
position += skipBytes(where);
break;
}
case SEEK_END: {
return seek(getSize() + where, SEEK_SET);
break;
}
}
if (position > getSize()){
position = getSize();
}
atEof = position == getSize();
return position;
}
bool eof(){
return atEof;
}
long tell(){
return zip->tell();
}
void reset(){
/* TODO or nothing..? */
}
bool canStream(){
return false;
}
bool good(){
/* FIXME */
return true;
}
File & operator>>(unsigned char & c){
readLine((char*) &c, 1);
return *this;
}
int getSize(){
return zip->currentFileSize();
}
int readLine(char * output, int size){
try{
int read = zip->read(output, size);
position += read;
return read;
} catch (const Exception & nomore){
atEof = true;
return 0;
}
}
protected:
/* skips `bytes'.
* returns number of bytes skipped (may be less than bytes)
*/
int skipBytes(int bytes){
char dummy[1024];
int total = 0;
while (bytes > 0){
/* Calls the zip file directly here so we don't mess up position */
int read = zip->read(dummy, bytes > (int) sizeof(dummy) ? (int) sizeof(dummy) : bytes);
total += read;
if (read == 0){
return total;
}
bytes -= read;
}
return total;
}
const Path::AbsolutePath path;
const Util::ReferenceCount<ZipContainer> zip;
bool atEof;
/* keep track of bytes read so we can seek easier */
int position;
};
Descriptor::Descriptor(){
}
Descriptor::~Descriptor(){
}
class ZipDescriptor: public Descriptor {
public:
ZipDescriptor(const Path::AbsolutePath & path, const Util::ReferenceCount<ZipContainer> & container):
path(path),
container(container){
}
Path::AbsolutePath path;
Util::ReferenceCount<ZipContainer> container;
using Descriptor::open;
virtual Util::ReferenceCount<File> open(File::Access mode){
return Util::ReferenceCount<File>(new ZipFile(path, container));
}
virtual ~ZipDescriptor(){
}
};
/* Extracts an entire file from a 7z archive into memory */
class LzmaFile: public File {
public:
LzmaFile(const Path::AbsolutePath & path, const Util::ReferenceCount<LzmaContainer> & container):
path(path),
container(container),
position(0),
memory(NULL),
size(0){
container->readFile(path, &memory, &size);
}
virtual ~LzmaFile(){
SzFree(NULL, memory);
}
bool eof(){
return position >= (int) size;
}
virtual bool good(){
return memory != NULL;
}
virtual int getSize(){
return size;
}
virtual bool canStream(){
return true;
}
virtual void reset(){
/* nothing */
}
virtual long tell(){
return position;
}
virtual Token * location(){
Token * head = new Token();
*head << "container";
/* container zipfile mount-point file-inside-zip */
*head << container->getPath();
*head << container->getMount();
*head << path.path();
return head;
}
virtual long getModificationTime(){
return container->getModificationTime(path);
}
virtual off_t seek(off_t position, int whence){
switch (whence){
case SEEK_SET: this->position = position; break;
case SEEK_CUR: this->position += position; break;
case SEEK_END: this->position = this->size + position; break;
}
if (this->position < 0){
this->position = 0;
}
if (this->position > (int) size){
this->position = size;
}
return this->position;
}
virtual File & operator>>(unsigned char & out){
if (this->position < (int) size){
out = memory[position];
position += 1;
}
return *this;
}
virtual int readLine(char * output, int size){
if (size > (int) (this->size - this->position)){
size = this->size - this->position;
}
memmove(output, memory + this->position, size);
this->position += size;
return size;
}
protected:
const Path::AbsolutePath path;
const Util::ReferenceCount<LzmaContainer> container;
/* keep track of bytes read so we can seek easier */
int position;
unsigned char * memory;
size_t size;
};
class LzmaDescriptor: public Descriptor {
public:
LzmaDescriptor(const Path::AbsolutePath & path, const Util::ReferenceCount<LzmaContainer> & container):
path(path),
container(container){
}
Path::AbsolutePath path;
Util::ReferenceCount<LzmaContainer> container;
using Descriptor::open;
virtual Util::ReferenceCount<File> open(File::Access mode){
return Util::ReferenceCount<File>(new LzmaFile(path, container));
}
virtual ~LzmaDescriptor(){
}
};
/* Keep this updated with all the supported container types */
bool isContainer(const Path::AbsolutePath & path){
return path.getExtension() == "zip" ||
path.getExtension() == "7z";
}
vector<std::string> containerTypes(){
vector<string> types;
types.push_back("zip");
types.push_back("7z");
return types;
}
bool System::isDirectory(const AbsolutePath & path){
return virtualDirectory.isDirectory(path) || systemIsDirectory(path);
}
bool System::exists(const AbsolutePath & path){
return virtualDirectory.exists(path) || systemExists(path);
}
void System::overlayFile(const AbsolutePath & where, Util::ReferenceCount<LzmaContainer> container){
- virtualDirectory.addFile(where, Util::ReferenceCount<LzmaDescriptor>(new LzmaDescriptor(where, container)).convert<Descriptor>());
+ virtualDirectory.addFile(where, Util::ReferenceCount<LzmaDescriptor>(new LzmaDescriptor(where, container)));
}
void System::overlayFile(const AbsolutePath & where, Util::ReferenceCount<ZipContainer> zip){
- virtualDirectory.addFile(where, Util::ReferenceCount<ZipDescriptor>(new ZipDescriptor(where, zip)).convert<Descriptor>());
+ virtualDirectory.addFile(where, Util::ReferenceCount<ZipDescriptor>(new ZipDescriptor(where, zip)));
}
void System::unoverlayFile(const AbsolutePath & where){
virtualDirectory.removeFile(where);
}
vector<string> System::containerFileList(const AbsolutePath & container){
Util::ReferenceCount<ZipContainer> zip(new ZipContainer(container.path(), Filesystem::AbsolutePath()));
return zip->getFiles();
}
static bool isZipFile(const Filesystem::AbsolutePath & path){
return path.path().find(".zip") != string::npos;
}
static bool is7zFile(const Filesystem::AbsolutePath & path){
return path.path().find(".7z") != string::npos;
}
template <class Container>
static void addOverlayFiles(System & system, const Filesystem::AbsolutePath & where, const Util::ReferenceCount<Container> & container){
vector<string> files = container->getFiles();
for (vector<string>::const_iterator it = files.begin(); it != files.end(); it++){
string path = *it;
Global::debug(1) << "Add overlay to " << where.join(Filesystem::RelativePath(path)).path() << std::endl;
system.overlayFile(where.join(Filesystem::RelativePath(path)), container);
}
}
void System::addOverlay(const AbsolutePath & container, const AbsolutePath & where){
if (isZipFile(container)){
Global::debug(1) << "Opening zip file " << container.path() << std::endl;
addOverlayFiles(*this, where, Util::ReferenceCount<ZipContainer>(new ZipContainer(container.path(), where)));
} else if (is7zFile(container)){
addOverlayFiles(*this, where, Util::ReferenceCount<LzmaContainer>(new LzmaContainer(container.path(), where)));
}
}
void System::removeOverlay(const AbsolutePath & container, const AbsolutePath & where){
Util::ReferenceCount<ZipContainer> zip(new ZipContainer(container.path(), where));
vector<string> files = zip->getFiles();
for (vector<string>::const_iterator it = files.begin(); it != files.end(); it++){
string path = *it;
Global::debug(1) << "Remove overlay from " << where.join(Filesystem::RelativePath(path)).path() << std::endl;
unoverlayFile(where.join(Filesystem::RelativePath(path)));
}
}
Util::ReferenceCount<File> System::open(const AbsolutePath & path, File::Access mode){
Util::ReferenceCount<Descriptor> descriptor = virtualDirectory.lookup(path);
if (descriptor != NULL){
return descriptor->open(mode);
} else {
return Util::ReferenceCount<File>(new NormalFile(path, mode));
}
}
std::string readFile(const Path::AbsolutePath & path){
Util::ReferenceCount<Storage::File> file = Storage::instance().open(path);
if (file != NULL){
char * data = new char[file->getSize() + 1];
file->readLine(data, file->getSize());
data[file->getSize()] = 0;
std::string out(data);
delete[] data;
return out;
}
std::ostringstream out;
out << "Could not open file " << path.path();
throw Exception(__FILE__, __LINE__, out.str());
}
bool hasInstance(){
// return true;
return self != NULL;
}
System & setInstance(const Util::ReferenceCount<System> & what){
self = what;
return *self;
}
/* will read upto 'length' bytes unless a null byte is seen first */
string EndianReader::readStringX(int length){
ostringstream out;
uint8_t letter = readByte1();
while (letter != 0 && length > 0){
out << letter;
letter = readByte1();
length -= 1;
}
return out.str();
}
/* unconditionally reads 'length' bytes */
std::string EndianReader::readString2(int length){
ostringstream out;
vector<uint8_t> bytes = readBytes(length);
for (vector<uint8_t>::iterator it = bytes.begin(); it != bytes.end(); it++){
char byte = *it;
if (byte == 0){
break;
}
out << *it;
}
return out.str();
}
void EndianReader::seekEnd(streamoff where){
internal->seekEnd(where);
}
void EndianReader::seek(streampos where){
internal->seek(where);
}
int EndianReader::position(){
return internal->tell();
}
void EndianReader::readBytes(uint8_t * out, int length){
internal->read((char*) out, length);
}
vector<uint8_t> EndianReader::readBytes(int length){
vector<uint8_t> bytes;
for (int i = 0; i < length; i++){
uint8_t byte = 0;
internal->read((char*) &byte, 1);
if (internal->eof()){
throw Eof();
} else {
}
bytes.push_back(byte);
}
return bytes;
}
EndianReader::Internal::Internal(){
}
EndianReader::Internal::~Internal(){
}
bool EndianReader::StreamInternal::eof(){
return stream.eof();
}
int EndianReader::StreamInternal::read(char * data, int length){
stream.read(data, length);
return stream.gcount();
}
void EndianReader::StreamInternal::seekEnd(std::streamoff where){
stream.seekg(where, ios::end);
}
void EndianReader::StreamInternal::seek(std::streamoff where){
stream.seekg(where);
}
int EndianReader::StreamInternal::tell(){
return stream.tellg();
}
EndianReader::StreamInternal::~StreamInternal(){
}
bool EndianReader::FileInternal::eof(){
return file->eof();
}
void EndianReader::FileInternal::seekEnd(std::streamoff where){
file->seek(where, SEEK_END);
}
void EndianReader::FileInternal::seek(std::streamoff where){
file->seek(where, SEEK_SET);
}
int EndianReader::FileInternal::read(char * data, int length){
return file->readLine(data, length);
}
int EndianReader::FileInternal::tell(){
return file->tell();
}
EndianReader::FileInternal::~FileInternal(){
}
}
Filesystem::Filesystem(const AbsolutePath & path):
dataPath(path){
}
#ifdef _WIN32
Filesystem::AbsolutePath Filesystem::userDirectory(){
ostringstream str;
char path[MAX_PATH];
SHGetSpecialFolderPathA(0, path, CSIDL_APPDATA, false);
str << path << "/paintown/";
return Filesystem::AbsolutePath(str.str());
}
Filesystem::AbsolutePath Filesystem::configFile(){
ostringstream str;
char path[MAX_PATH];
SHGetSpecialFolderPathA(0, path, CSIDL_APPDATA, false);
str << path << "/paintown_configuration.txt";
return Filesystem::AbsolutePath(str.str());
}
#else
Filesystem::AbsolutePath Filesystem::configFile(){
ostringstream str;
/* what if HOME isn't set? */
str << getenv("HOME") << "/.paintownrc";
return Filesystem::AbsolutePath(str.str());
}
Filesystem::AbsolutePath Filesystem::userDirectory(){
ostringstream str;
char * home = getenv("HOME");
if (home == NULL){
str << "/tmp/paintown";
} else {
str << home << "/.paintown/";
}
return Filesystem::AbsolutePath(str.str());
}
#endif
Filesystem::AbsolutePath Filesystem::lookup(const RelativePath path){
vector<Filesystem::AbsolutePath> places;
#define push(x) try{ places.push_back(x); } catch (const Storage::IllegalPath & fail){ }
push(dataPath.join(path));
push(userDirectory().join(path));
push(Filesystem::AbsolutePath(path.path()));
#undef push
/* start error stuff early */
ostringstream out;
out << "Cannot find " << path.path() << ". I looked in ";
bool first = true;
for (vector<Filesystem::AbsolutePath>::iterator it = places.begin(); it != places.end(); it++){
const Filesystem::AbsolutePath & final = *it;
if (exists(final)){
return final;
}
if (!first){
out << ", ";
} else {
first = false;
}
out << "'" << final.path() << "'";
}
// out << "Cannot find " << path.path() << ". I looked in '" << dataPath.join(path).path() << "', '" << userDirectory().join(path).path() << "', and '" << path.path() << "'";
throw NotFound(__FILE__, __LINE__, out.str());
#if 0
/* first try the main data directory */
Filesystem::AbsolutePath final = dataPath.join(path);
if (::System::readable(final.path())){
return final;
}
/* then try the user directory, like ~/.paintown */
final = userDirectory().join(path);
if (::System::readable(final.path())){
return final;
}
/* then just look in the cwd */
if (::System::readable(path.path())){
return Filesystem::AbsolutePath(path.path());
}
ostringstream out;
out << "Cannot find " << path.path() << ". I looked in '" << dataPath.join(path).path() << "', '" << userDirectory().join(path).path() << "', and '" << path.path() << "'";
throw NotFound(__FILE__, __LINE__, out.str());
#endif
}
Filesystem::AbsolutePath Filesystem::lookupInsensitive(const Filesystem::AbsolutePath & directory, const Filesystem::RelativePath & path){
if (path.path() == ""){
throw NotFound(__FILE__, __LINE__, "Given empty path to lookup");
}
if (path.path() == "."){
return directory;
}
if (path.path() == ".."){
return directory.getDirectory();
}
if (path.isFile()){
vector<AbsolutePath> all = getFiles(directory, "*", true);
for (vector<AbsolutePath>::iterator it = all.begin(); it != all.end(); it++){
AbsolutePath & check = *it;
if (InsensitivePath(check.getFilename()) == path){
return check;
}
}
ostringstream out;
out << "Cannot find " << path.path() << " in " << directory.path();
throw NotFound(__FILE__, __LINE__, out.str());
} else {
return lookupInsensitive(lookupInsensitive(directory, path.firstDirectory()), path.removeFirstDirectory());
}
}
vector<Filesystem::AbsolutePath> Filesystem::findDirectoriesIn(const Filesystem::AbsolutePath & path){
vector<Filesystem::AbsolutePath> dirs = virtualDirectory.findDirectories(path, "*", false);
DIR * dir = opendir(path.path().c_str());
if (dir == NULL){
return dirs;
}
struct dirent * entry = readdir(dir);
while (entry != NULL){
if (string(entry->d_name) != "." && string(entry->d_name) != ".."){
string total = path.path() + "/" + entry->d_name;
if (::System::isDirectory(total)){
dirs.push_back(AbsolutePath(total));
}
}
entry = readdir(dir);
}
closedir(dir);
return dirs;
}
vector<Filesystem::AbsolutePath> Filesystem::findDirectories(const RelativePath & path){
typedef vector<AbsolutePath> Paths;
Paths dirs;
Paths main_dirs = findDirectoriesIn(dataPath.join(path));
Paths user_dirs = findDirectoriesIn(userDirectory().join(path));
Paths here_dirs = findDirectoriesIn(Filesystem::AbsolutePath(path.path()));
dirs.insert(dirs.end(), main_dirs.begin(), main_dirs.end());
dirs.insert(dirs.end(), user_dirs.begin(), user_dirs.end());
dirs.insert(dirs.end(), here_dirs.begin(), here_dirs.end());
return dirs;
}
/* a/b/c/ -> a/b/c */
static string removeTrailingSlash(string str){
while (str.size() > 0 && str[str.size() - 1] == '/'){
str = str.erase(str.size() - 1);
}
return str;
}
vector<Filesystem::AbsolutePath> Filesystem::getFiles(const AbsolutePath & dataPath, const string & find, bool caseInsensitive){
#ifdef USE_ALLEGRO
struct al_ffblk info;
vector<AbsolutePath> files;
if (al_findfirst((dataPath.path() + "/" + find).c_str(), &info, FA_ALL ) != 0){
return files;
}
files.push_back(AbsolutePath(dataPath.path() + "/" + string(info.name)));
while ( al_findnext( &info ) == 0 ){
files.push_back(AbsolutePath(dataPath.path() + "/" + string(info.name)));
}
al_findclose( &info );
return files;
#else
Util::Thread::ScopedLock scoped(lock);
vector<AbsolutePath> files;
vector<AbsolutePath> more = virtualDirectory.findFiles(dataPath, find, caseInsensitive);
files.insert(files.end(), more.begin(), more.end());
DIRST sflEntry;
// bool ok = open_dir(&sflEntry, removeTrailingSlash(dataPath.path()).c_str());
bool ok = open_dir(&sflEntry, dataPath.path().c_str());
if (!ok){
/* sfldir.c claims that you have to call close_dir even if
* open_dir fails but close_dir will do ASSERT(dir->dir_handle) which is sometimes
* NULL when open_dir fails so we first check if the dir_handle is non-NULL and
* then call close_dir.
*/
if (sflEntry._dir_handle != NULL){
close_dir(&sflEntry);
}
return files;
}
while (ok){
if (file_matches(sflEntry.file_name, find.c_str())){
files.push_back(AbsolutePath(dataPath.path() + "/" + string(sflEntry.file_name)));
}
ok = read_dir(&sflEntry);
}
close_dir(&sflEntry);
/*
for (map<AbsolutePath, Util::ReferenceCount<Storage::ZipContainer> >::iterator it = overlays.begin(); it != overlays.end(); it++){
AbsolutePath path = it->first;
if (it->second == NULL){
continue;
}
// Global::debug(0) << "Check " << path.path() << " (" << path.getDirectory().path() << ") vs directory " << dataPath.path() << " wildcard " << find << " to " << path.getFilename().path() << std::endl;
if (path.getDirectory() == dataPath &&
file_matches(path.getLastComponent().c_str(), find.c_str())){
// Global::debug(0) << "Found overlay " << path.path() << " in " << dataPath.path() << " for wildcard " << find << std::endl;
files.push_back(path);
}
}
*/
// Global::debug(0) << "Warning: Filesystem::getFiles() is not implemented yet for SDL" << endl;
return files;
#endif
}
std::vector<Filesystem::AbsolutePath> Filesystem::getFiles(const RelativePath & path, const RelativePath & find, bool caseInsensitive){
vector<AbsolutePath> directories;
directories.push_back(dataPath.join(path));
directories.push_back(userDirectory().join(path));
directories.push_back(Filesystem::AbsolutePath(path.path()));
vector<AbsolutePath> files;
for (vector<AbsolutePath>::iterator it = directories.begin(); it != directories.end(); it++){
Global::debug(0) << "Search for " << find.path() << " in " << it->path() << std::endl;
vector<AbsolutePath> found = getFiles(*it, find, caseInsensitive);
files.insert(files.end(), found.begin(), found.end());
}
return files;
}
template <class X>
static void append(vector<X> & destination, const vector<X> & source){
/*
for (typename vector<X>::const_iterator it = source.begin(); it != source.end(); it++){
destination.push_back(*it);
}
*/
copy(source.begin(), source.end(), back_insert_iterator<vector<X> >(destination));
}
vector<Filesystem::AbsolutePath> Filesystem::getAllDirectories(const AbsolutePath & path){
vector<AbsolutePath> all = findDirectoriesIn(path);
vector<AbsolutePath> final;
append(final, all);
for (vector<AbsolutePath>::iterator it = all.begin(); it != all.end(); it++){
vector<AbsolutePath> more = getAllDirectories(*it);
append(final, more);
}
return final;
}
vector<Filesystem::AbsolutePath> Filesystem::getFilesRecursive(const AbsolutePath & dataPath, const string & find, bool caseInsensitive){
if (!exists(dataPath)){
return vector<AbsolutePath>();
}
vector<AbsolutePath> directories = getAllDirectories(dataPath);
directories.push_back(dataPath);
vector<AbsolutePath> files;
for (vector<AbsolutePath>::iterator it = directories.begin(); it != directories.end(); it++){
vector<AbsolutePath> found = getFiles(*it, find, caseInsensitive);
append(files, found);
}
return files;
}
/*
std::string find(const std::string path){
if (path.length() == 0){
throw NotFound("No path given");
}
if (path[0] == '/'){
string str(path);
str.erase(0, 1);
string out = lookup(str);
if (System::isDirectory(out)){
return sanitize(out + "/");
}
return sanitize(out);
}
string out = lookup(path);
if (System::isDirectory(out)){
return sanitize(out + "/");
}
return sanitize(out);
}
*/
Filesystem::AbsolutePath Filesystem::find(const RelativePath & path){
if (path.isEmpty()){
throw NotFound(__FILE__, __LINE__, "No path given");
}
AbsolutePath out = lookup(path);
if (::System::isDirectory(out.path())){
return AbsolutePath(out.path() + "/");
}
return AbsolutePath(out.path());
}
Filesystem::AbsolutePath Filesystem::findInsensitive(const RelativePath & path){
try{
/* try sensitive lookup first */
return lookup(path);
} catch (const NotFound & fail){
}
/* get the base directory */
AbsolutePath directory = lookup(path.getDirectory());
return lookupInsensitive(directory, path.getFilename());
}
bool Filesystem::exists(const RelativePath & path){
try{
AbsolutePath absolute = find(path);
return true;
} catch (const NotFound & found){
return false;
}
}
bool Filesystem::systemIsDirectory(const AbsolutePath & path){
return ::System::isDirectory(path.path());
}
bool Filesystem::systemExists(const AbsolutePath & path){
return ::System::readable(path.path());
}
Filesystem::RelativePath Filesystem::cleanse(const AbsolutePath & path){
string str = path.path();
if (str.find(dataPath.path()) == 0){
str.erase(0, dataPath.path().length());
} else if (str.find(userDirectory().path()) == 0){
str.erase(0, userDirectory().path().length());
}
return RelativePath(str);
}
diff --git a/util/gui/animation.cpp b/util/gui/animation.cpp
index fd78e546..eafbf723 100644
--- a/util/gui/animation.cpp
+++ b/util/gui/animation.cpp
@@ -1,1233 +1,1233 @@
#include "animation.h"
#include <stdio.h>
#include <vector>
#include <math.h>
#include <sstream>
#include "util/token.h"
#include "util/graphics/bitmap.h"
#include "util/font.h"
#include "util/graphics/gradient.h"
#include "../debug.h"
#include "../funcs.h"
#include "../file-system.h"
#include "util/exceptions/load_exception.h"
using namespace std;
using namespace Gui;
// Temporary solution
static void renderSprite(const Graphics::Bitmap & bmp, const int x, const int y, const int alpha, const bool hflip, const bool vflip, const Graphics::Bitmap & work){
if (alpha != 255){
Graphics::Bitmap::transBlender( 0, 0, 0, alpha );
if (hflip && !vflip){
bmp.translucent().drawHFlip(x,y, work);
} else if (!hflip && vflip){
bmp.translucent().drawVFlip(x,y, work);
} else if (hflip && vflip){
bmp.translucent().drawHVFlip(x,y, work);
} else if (!hflip && !vflip){
bmp.translucent().draw(x,y, work);
}
} else {
if (hflip && !vflip){
bmp.drawHFlip(x,y, work);
} else if (!hflip && vflip){
bmp.drawVFlip(x,y, work);
} else if (hflip && vflip){
bmp.drawHVFlip(x,y, work);
} else if (!hflip && !vflip){
bmp.draw(x,y, work);
}
}
}
Element::Element(const Token * token):
time(50),
alpha(255){
if (token != NULL){
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
parseToken(tok);
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Gui::Animation::Element parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
}
Element::~Element(){
}
void Element::parseToken(const Token * token){
if (*token == "alpha"){
// get alpha
token->view() >> alpha;
} else if (*token == "offset"){
// Get the offset location it defaults to 0,0
double x=0, y=0;
try {
token->view() >> x >> y;
} catch (const TokenException & ex){
}
offset.set(x,y);
} else if (*token == "time"){
// time to display
token->view() >> time;
} else {
Global::debug( 3 ) << "Unhandled Gui::Animation::Element attribute: " << endl;
if (Global::getDebug() >= 3){
token->print(" ");
}
}
}
void Element::act(double xvel, double yvel){
scrollOffset.moveBy(xvel, yvel);
}
ImageFrame::ImageFrame(const Token *the_token, ImageMap &images, const string & baseDir):
Element(the_token),
bmp(NULL),
horizontalFlip(false),
verticalFlip(false){
/*
if (*the_token != "frame"){
throw LoadException(__FILE__, __LINE__, "Not an frame");
}
*/
/* The usual setup of an animation frame is
// use image -1 to not draw anything, it can be used to get a blinking effect
(frame (image NUM) (alpha NUM) (offset x y) (hflip 0|1) (vflip 0|1) (time NUM))
*/
TokenView view = the_token->view();
while (view.hasMore()){
try{
const Token * token;
view >> token;
parseToken(token, baseDir, images);
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu animation parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
void ImageFrame::parseToken(const Token * token, const string & baseDir, ImageMap & images){
if (*token == "image"){
// get the number
string maybeNumber;
token->view() >> maybeNumber;
if (Util::matchRegex(maybeNumber, Util::Regex("\\d+"))){
int num;
token->view() >> num;
if (images.find(num) == images.end()){
ostringstream out;
out << "No image for index " << num;
throw LoadException(__FILE__, __LINE__, out.str());
}
// now assign the bitmap
bmp = images[num];
} else {
bmp = Util::ReferenceCount<Graphics::Bitmap>(new Graphics::Bitmap(Storage::instance().find(Filesystem::RelativePath(baseDir + "/" + maybeNumber)).path()));
}
} else if (*token == "hflip"){
// horizontal flip
token->view() >> horizontalFlip;
} else if (*token == "vflip"){
// horizontal flip
token->view() >> verticalFlip;
} else {
Global::debug( 3 ) << "Unhandled Gui::Animation::ImageFrame attribute: "<<endl;
if (Global::getDebug() >= 3){
token->print(" ");
}
}
}
ImageFrame::ImageFrame(Util::ReferenceCount<Graphics::Bitmap> bmp):
Element(NULL),
bmp(bmp),
horizontalFlip(false),
verticalFlip(false){
time = -1;
}
ImageFrame::~ImageFrame(){
}
static bool closeFloat(double a, double b){
const double epsilon = 0.001;
return fabs(a-b) < epsilon;
}
void ImageFrame::act(double xvel, double yvel){
scrollOffset.moveBy(xvel, yvel);
if (bmp != NULL){
if (scrollOffset.getDistanceFromCenterX() >= bmp->getWidth()){
scrollOffset.setX(0);
} else if (scrollOffset.getDistanceFromCenterX() <= -(bmp->getWidth())){
scrollOffset.setX(0);
}
if (scrollOffset.getDistanceFromCenterY() >= bmp->getHeight()){
scrollOffset.setY(0);
} else if (scrollOffset.getDistanceFromCenterY() <= -(bmp->getHeight())){
scrollOffset.setY(0);
}
}
}
void ImageFrame::draw(const int xaxis, const int yaxis, const Graphics::Bitmap & work){
if (!bmp){
return;
}
if (!closeFloat(scrollOffset.getDistanceFromCenterX(), 0) || !closeFloat(scrollOffset.getDistanceFromCenterY(), 0)){
// Lets do some scrolling
// Graphics::Bitmap temp = Graphics::Bitmap::temporaryBitmap(bmp->getWidth(), bmp->getHeight());
//AnimationPoint loc;
AbsolutePoint loc;
if (scrollOffset.getRelativeX() < 0){
loc.setX(scrollOffset.getDistanceFromCenterX() + bmp->getWidth());
} else if (scrollOffset.getRelativeX() > 0){
loc.setX(scrollOffset.getDistanceFromCenterX() - bmp->getWidth());
}
if (scrollOffset.getRelativeY() < 0){
loc.setY(scrollOffset.getDistanceFromCenterY() + bmp->getHeight());
} else if (scrollOffset.getRelativeY() > 0){
loc.setY(scrollOffset.getDistanceFromCenterY() - bmp->getHeight());
}
/*
bmp->Blit((int) scrollOffset.getDistanceFromCenterX(), (int) scrollOffset.getDistanceFromCenterY(), temp);
bmp->Blit((int) scrollOffset.getDistanceFromCenterX(), (int) loc.getY(), temp);
bmp->Blit((int) loc.getX(), (int) scrollOffset.getDistanceFromCenterY(), temp);
bmp->Blit((int) loc.getX(), (int) loc.getY(), temp);
renderSprite(temp, (int)(xaxis+offset.getDistanceFromCenterX()), (int)(yaxis+offset.getDistanceFromCenterY()), alpha, horizontalFlip, verticalFlip, work);
*/
double x = xaxis+offset.getDistanceFromCenterX();
double y = yaxis+offset.getDistanceFromCenterY();
renderSprite(*bmp,
(int)(x + scrollOffset.getDistanceFromCenterX()),
(int)(y + scrollOffset.getDistanceFromCenterY()),
alpha, horizontalFlip, verticalFlip, work);
renderSprite(*bmp,
(int)(x + loc.getX()),
(int)(y + scrollOffset.getDistanceFromCenterY()),
alpha, horizontalFlip, verticalFlip, work);
renderSprite(*bmp,
(int)(x + scrollOffset.getDistanceFromCenterX()),
(int)(y + loc.getY()),
alpha, horizontalFlip, verticalFlip, work);
renderSprite(*bmp,
(int)(x + loc.getX()),
(int)(y + loc.getY()),
alpha, horizontalFlip, verticalFlip, work);
} else {
renderSprite(*bmp, (int)(xaxis+offset.getDistanceFromCenterX()), (int)(yaxis+offset.getDistanceFromCenterY()), alpha, horizontalFlip, verticalFlip, work);
}
}
/* This is mainly called from select-list to draw the cells. I'm not sure
* of the utility of stretching the bitmap to fit the cell dimensions. Most
* likely the cell will contain an image and the image will be the same
* size as the cell.
*/
void ImageFrame::draw(const Graphics::Bitmap & work){
Graphics::Bitmap temp(bmp->getWidth(), bmp->getHeight());
temp.clearToMask();
renderSprite(*bmp, 0, 0, alpha, horizontalFlip, verticalFlip, temp);
temp.drawStretched(work);
/* FIXME: This should work, but it doesn't.. */
/*
Graphics::StretchedBitmap show(bmp->getWidth(), bmp->getHeight(), work);
show.start();
renderSprite(*bmp, 0, 0, alpha, horizontalFlip, verticalFlip, show);
show.finish();
*/
}
void ImageFrame::reset(){
scrollOffset = RelativePoint();
}
void ImageFrame::setToEnd(const RelativePoint & end){
scrollOffset = end;
}
static const char * FRAME_TEXT = "Offset: ( %f, %f)\nScroll Offset: ( %f, %f)\nTime: %d\nHorizontal Flip: %d\nVertical Flip: %d\nAlpha: %d\n\n";
const std::string ImageFrame::getInfo(){
char info[255];
sprintf(info, FRAME_TEXT, offset.getRelativeX(), offset.getRelativeY(), scrollOffset.getRelativeX(), scrollOffset.getRelativeY(), time, horizontalFlip, verticalFlip, alpha);
return std::string(info);
}
TextFrame::TextFrame(const Token *token):
Element(token),
fontWidth(20),
fontHeight(20){
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * token;
view >> token;
parseToken(token);
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu animation parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
TextFrame::~TextFrame(){
}
void TextFrame::act(double xvel, double yvel){
gradient.forward();
Element::act(xvel, yvel);
}
static const Font & getFont(const string & font, int width, int height){
if (font != ""){
return Font::getFont(Filesystem::RelativePath(font), width, height);
}
return Font::getDefaultFont(width, height);
}
void TextFrame::draw(int xaxis, int yaxis, const Graphics::Bitmap & work){
double x = xaxis + offset.getDistanceFromCenterX() + scrollOffset.getDistanceFromCenterX();
double y = yaxis + offset.getDistanceFromCenterY() + scrollOffset.getDistanceFromCenterY();
const Font & font = getFont(this->font, fontWidth, fontHeight);
font.printf((int) x, (int) y, gradient.current(), work, "%s", 0, message.c_str());
}
void TextFrame::draw(const Graphics::Bitmap & work){
/* Probably don't need this.. but implement it if you do! */
}
void TextFrame::reset(){
scrollOffset = RelativePoint();
}
void TextFrame::setToEnd(const RelativePoint & point){
scrollOffset = point;
}
const std::string TextFrame::getInfo(){
return message;
}
void TextFrame::parseToken(const Token * token){
if (*token == "message"){
token->view() >> message;
} else if (*token == "font"){
TokenView view = token->view();
view >> font;
try{
view >> fontWidth >> fontHeight;
} catch (const TokenException & ignore){
}
if (fontWidth < 1){
fontWidth = 1;
}
if (fontHeight < 1){
fontHeight = 1;
}
} else if (*token == "color"){
int red, green, blue;
token->view() >> red >> green >> blue;
gradient = Effects::Gradient(Graphics::makeColor(red, green, blue));
} else if (*token == "gradient"){
gradient = Effects::Gradient(token);
}
}
Sequence::Sequence(){
}
Sequence::~Sequence(){
}
SequenceFrame::SequenceFrame(const Util::ReferenceCount<Element> & frame):
frame(frame),
ticks(0){
}
void SequenceFrame::draw(int xaxis, int yaxis, const Graphics::Bitmap & work) const {
frame->draw(xaxis, yaxis, work);
}
Util::ReferenceCount<Element> SequenceFrame::getCurrentFrame() const {
return frame;
}
int SequenceFrame::totalTicks() const {
return frame->getTime();
}
void SequenceFrame::setToEnd(){
ticks = frame->getTime();
}
bool SequenceFrame::forward(int tickCount, double velocityX, double velocityY){
frame->act(tickCount * velocityX, tickCount * velocityY);
ticks += tickCount;
return ticks > frame->getTime();
}
bool SequenceFrame::reverse(int tickCount, double velocityX, double velocityY){
frame->act(-tickCount * velocityX, -tickCount * velocityY);
ticks -= tickCount;
return ticks < 0;
}
void SequenceFrame::forwardFrame(){
/* nop */
}
void SequenceFrame::backFrame(){
/* nop */
}
void SequenceFrame::reset(){
ticks = 0;
frame->reset();
}
void SequenceFrame::resetTicks(){
ticks = 0;
}
SequenceLoop::SequenceLoop(int loops):
currentFrame(0),
currentLoop(loops),
loopTimes(loops){
}
Util::ReferenceCount<Element> SequenceLoop::getCurrentFrame() const {
if (currentFrame < frames.size()){
return frames[currentFrame]->getCurrentFrame();
} else {
/* Return the last frame */
if (frames.size() > 0){
return frames[frames.size() - 1]->getCurrentFrame();
}
}
return Util::ReferenceCount<Element>(NULL);
}
Util::ReferenceCount<Sequence> SequenceLoop::getCurrentSequence() const {
if (currentFrame < frames.size()){
return frames[currentFrame];
} else {
/* Return the last frame */
if (frames.size() > 0){
return frames[frames.size() - 1];
}
}
return Util::ReferenceCount<Sequence>(NULL);
}
void SequenceLoop::draw(int xaxis, int yaxis, const Graphics::Bitmap & work) const {
Util::ReferenceCount<Sequence> current = getCurrentSequence();
if (current != NULL){
current->draw(xaxis, yaxis, work);
}
}
void SequenceLoop::reset(){
currentFrame = 0;
currentLoop = loopTimes;
for (vector<Util::ReferenceCount<Sequence> >::iterator it = frames.begin(); it != frames.end(); it++){
Util::ReferenceCount<Sequence> & sequence = *it;
sequence->reset();
}
}
/* Does the same thing as reset except for calling resetTicks on children nodes */
void SequenceLoop::resetTicks(){
currentFrame = 0;
currentLoop = loopTimes;
for (vector<Util::ReferenceCount<Sequence> >::iterator it = frames.begin(); it != frames.end(); it++){
Util::ReferenceCount<Sequence> & sequence = *it;
sequence->resetTicks();
}
}
/* Similar to resetTicks but doesn't reset the current frame or loop */
void SequenceLoop::resetChildrenTicks(){
for (vector<Util::ReferenceCount<Sequence> >::iterator it = frames.begin(); it != frames.end(); it++){
Util::ReferenceCount<Sequence> & sequence = *it;
sequence->resetTicks();
}
}
void SequenceLoop::setToEnd(){
/* FIXME */
}
void SequenceLoop::addSequence(const Util::ReferenceCount<Sequence> & sequence){
frames.push_back(sequence);
}
static Util::ReferenceCount<Sequence> parseSequence(const Token * token, ImageMap & images, const string & baseDir){
if (*token == "frame"){
Util::ReferenceCount<Element> frame(new ImageFrame(token, images, baseDir));
return Util::ReferenceCount<Sequence>(new SequenceFrame(frame));
} else if (*token == "text"){
Util::ReferenceCount<Element> frame(new TextFrame(token));
return Util::ReferenceCount<Sequence>(new SequenceFrame(frame));
} else if (*token == "loop"){
int times;
token->view() >> times;
Util::ReferenceCount<SequenceLoop> loop(new SequenceLoop(times));
loop->parse(token, images, baseDir);
- return loop.convert<Sequence>();
+ return loop;
} else if (*token == "all"){
return Util::ReferenceCount<Sequence>(new SequenceAll(token, images, baseDir));
} else if (*token == "random"){
return Util::ReferenceCount<Sequence>(new SequenceRandom(token, images, baseDir));
}
return Util::ReferenceCount<Sequence>(NULL);
}
void SequenceLoop::parse(const Token * token, ImageMap & images, const string & baseDir){
TokenView view = token->view();
/* first ignore the number of times to loop */
int ignore;
view >> ignore;
while (view.hasMore()){
const Token * next;
view >> next;
Util::ReferenceCount<Sequence> sequence = parseSequence(next, images, baseDir);
if (sequence != NULL){
addSequence(sequence);
}
}
}
int SequenceLoop::totalTicks() const {
int total = 0;
for (vector<Util::ReferenceCount<Sequence> >::const_iterator it = frames.begin(); it != frames.end(); it++){
const Util::ReferenceCount<Sequence> & sequence = *it;
total += sequence->totalTicks();
}
return total * (1 + loopTimes);
}
bool SequenceLoop::forward(int tickCount, double velocityX, double velocityY){
Util::ReferenceCount<Sequence> current = getCurrentSequence();
if (current != NULL){
if (current->forward(tickCount, velocityX, velocityY)){
forwardFrame();
}
}
return currentFrame == frames.size();
}
bool SequenceLoop::reverse(int tickCount, double velocityX, double velocityY){
Util::ReferenceCount<Sequence> current = getCurrentSequence();
if (current != NULL){
if (current->reverse(tickCount, velocityX, velocityY)){
backFrame();
}
}
return currentFrame == frames.size();
}
void SequenceLoop::forwardFrame(){
currentFrame += 1;
/* do the loop */
if (currentFrame == frames.size() && currentLoop > 0){
currentLoop -= 1;
currentFrame = 0;
resetChildrenTicks();
}
}
void SequenceLoop::backFrame(){
currentFrame -= 1;
/* do the loop */
if (currentFrame <= 0 && currentLoop > 0){
currentLoop -= 1;
resetChildrenTicks();
if (frames.size() > 0){
currentFrame = frames.size() - 1;
} else {
currentFrame = 0;
}
}
}
SequenceAll::SequenceAll(const Token * token, ImageMap & images, const string & baseDir){
TokenView view = token->view();
while (view.hasMore()){
const Token * next;
view >> next;
Util::ReferenceCount<Sequence> sequence = parseSequence(next, images, baseDir);
if (sequence != NULL){
addSequence(sequence);
}
}
}
Util::ReferenceCount<Element> SequenceAll::getCurrentFrame() const {
return Util::ReferenceCount<Element>(NULL);
}
void SequenceAll::reset(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->reset();
}
}
void SequenceAll::resetTicks(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->resetTicks();
}
}
void SequenceAll::setToEnd(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->setToEnd();
}
}
void SequenceAll::addSequence(const Util::ReferenceCount<Sequence> & sequence){
sequences.push_back(sequence);
}
void SequenceAll::draw(int xaxis, int yaxis, const Graphics::Bitmap & work) const {
for (SequenceConstIterator it = sequences.begin(); it != sequences.end(); it++){
const Util::ReferenceCount<Sequence> & next = *it;
next->draw(xaxis, yaxis, work);
}
}
/* Maximum ticks of any sequence */
int SequenceAll::totalTicks() const {
int max = 0;
for (SequenceConstIterator it = sequences.begin(); it != sequences.end(); it++){
const Util::ReferenceCount<Sequence> & next = *it;
int what = next->totalTicks();
if (what > max){
max = what;
}
}
return max;
}
bool SequenceAll::forward(int tickCount, double velocityX, double velocityY){
bool go = false;
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
go = next->forward(tickCount, velocityX, velocityY) && go;
}
return go;
}
bool SequenceAll::reverse(int tickCount, double velocityX, double velocityY){
bool go = false;
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
go = next->reverse(tickCount, velocityX, velocityY) && go;
}
return go;
}
void SequenceAll::forwardFrame(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->forwardFrame();
}
}
void SequenceAll::backFrame(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->backFrame();
}
}
SequenceRandom::SequenceRandom(const Token * token, ImageMap & images, const std::string & baseDir):
current(0){
TokenView view = token->view();
while (view.hasMore()){
const Token * next;
view >> next;
Util::ReferenceCount<Sequence> sequence = parseSequence(next, images, baseDir);
if (sequence != NULL){
addSequence(sequence);
}
}
current = Util::rnd(sequences.size());
}
Util::ReferenceCount<Element> SequenceRandom::getCurrentFrame() const {
return Util::ReferenceCount<Element>(NULL);
}
void SequenceRandom::reset(){
current = Util::rnd(sequences.size());
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->reset();
}
}
void SequenceRandom::resetTicks(){
current = Util::rnd(sequences.size());
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->resetTicks();
}
}
void SequenceRandom::setToEnd(){
for (SequenceIterator it = sequences.begin(); it != sequences.end(); it++){
Util::ReferenceCount<Sequence> & next = *it;
next->setToEnd();
}
}
void SequenceRandom::addSequence(const Util::ReferenceCount<Sequence> & sequence){
sequences.push_back(sequence);
}
void SequenceRandom::draw(int xaxis, int yaxis, const Graphics::Bitmap & work) const {
if (current < sequences.size()){
Util::ReferenceCount<Sequence> now = sequences[current];
now->draw(xaxis, yaxis, work);
}
}
int SequenceRandom::totalTicks() const {
int max = 0;
for (SequenceConstIterator it = sequences.begin(); it != sequences.end(); it++){
const Util::ReferenceCount<Sequence> & next = *it;
int what = next->totalTicks();
if (what > max){
max = what;
}
}
return max;
}
bool SequenceRandom::forward(int tickCount, double velocityX, double velocityY){
if (current < sequences.size()){
Util::ReferenceCount<Sequence> now = sequences[current];
return now->forward(tickCount, velocityX, velocityY);
}
return true;
}
bool SequenceRandom::reverse(int tickCount, double velocityX, double velocityY){
if (current < sequences.size()){
Util::ReferenceCount<Sequence> now = sequences[current];
return now->reverse(tickCount, velocityX, velocityY);
}
return true;
}
void SequenceRandom::forwardFrame(){
if (current < sequences.size()){
sequences[current]->forwardFrame();
}
}
void SequenceRandom::backFrame(){
if (current < sequences.size()){
sequences[current]->backFrame();
}
}
static int CURRENT_ID = 0;
static int getNextId(){
return CURRENT_ID++;
}
Animation::Animation(const Token *the_token):
id(getNextId()),
depth(BackgroundBottom),
allowReset(true),
sequence(0){
/* Whats this for? */
images[-1] = 0;
std::string basedir = ".";
if ( *the_token != "anim" && *the_token != "animation" ){
throw LoadException(__FILE__, __LINE__, "Not an animation");
}
/* The usual setup of an animation is
The images must be listed prior to listing any frames, basedir can be used to set the directory where the images are located
loop will begin at the subsequent frame listed after loop
axis is the location in which the drawing must be placed
location *old* - used to render in background or foreground (0 == background [default]| 1 == foreground)
depth - used to render in background or foreground space (depth background bottom|middle|top) | (depth foreground bottom|midle|top)
reset - used to allow resetting of animation (0 == no | 1 == yes [default])
velocity - used to get a wrapping scroll effect while animating
window - area in which the item will be contained
(anim (id NUM)
(location NUM)
(scale NUM) ;; optional
(depth background|foreground NUM)
(basedir LOCATION)
(image NUM FILE [SCALE])
(velocity x y)
(axis x y)
(frame "Read comments above in constructor")
(loop 2)
(reset NUM)
(window x1 y1 x2 y2))
*/
double scale = 1.0;
bool scaleSet = false;
const Token & tok = *the_token;
TokenView view = tok.view();
while (view.hasMore()){
try{
const Token * token;
view >> token;
if (*token == "id"){
// get the id
token->view() >> id;
} else if (*token == "location"){
// translate location to depth
/*int location = 0;
token->view() >> location;
if (location == 0){
depth = BackgroundBottom;
} else if (location == 1){
depth = ForegroundBottom;
}*/
Global::debug(0) << "The option (" << token->getLineage() << ") in the file '" << token->getFileName() << "' is no longer valid and will be ignored. Consider using 'depth' instead." << std::endl;
} else if (*token == "scale"){
token->view() >> scale;
scaleSet = true;
} else if (*token == "depth"){
// get the depth
std::string name, level;
token->view() >> name >> level;
if (name == "background"){
if (level == "bottom"){
depth = BackgroundBottom;
} else if (level == "middle"){
depth = BackgroundMiddle;
} else if (level == "top"){
depth = BackgroundTop;
}
} else if (name == "foreground"){
if (level == "bottom"){
depth = ForegroundBottom;
} else if (level == "middle"){
depth = ForegroundMiddle;
} else if (level == "top"){
depth = ForegroundTop;
}
}
} else if (*token == "basedir"){
// set the base directory for loading images
token->view() >> basedir;
} else if (*token == "image"){
// add bitmaps by number to the map
int number;
std::string temp;
double localScale = 1;
bool localScaleSet = false;
TokenView view = token->view();
view >> number >> temp;
try{
view >> localScale;
localScaleSet = true;
} catch (const TokenException & fail){
}
Util::ReferenceCount<Graphics::Bitmap> bmp(new Graphics::Bitmap(*Storage::instance().open(Storage::instance().find(Filesystem::RelativePath(basedir + "/" + temp)))));
if (!bmp->getError()){
if (scaleSet || localScaleSet){
if (localScaleSet){
*bmp = bmp->scaleBy(localScale, localScale);
} else {
*bmp = bmp->scaleBy(scale, scale);
}
}
images[number] = bmp;
}
} else if (*token == "axis"){
// Get the axis location it defaults to 0,0
double x=0, y=0;
try {
token->view() >> x >> y;
} catch (const TokenException & ex){
}
axis.set(x,y);
} else if (*token == "window"){
// Windowed area where to clip if necessary otherwise it defaults to max
double x1=0,x2=0, y1=0,y2=0;
try {
token->view() >> x1 >> y1 >> x2 >> y2;
} catch (const TokenException & ex){
}
window.set(x1,y1,x2,y2);
} else if (*token == "velocity"){
// This allows the animation to get a wrapping scroll action going on
double x=0, y=0;
try {
token->view() >> x >> y;
} catch (const TokenException & ex){
}
velocity.set(x,y);
} else if (*token == "frame"){
Util::ReferenceCount<Element> frame(new ImageFrame(token, images, basedir));
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceFrame(frame)));
} else if (*token == "text"){
Util::ReferenceCount<Element> frame(new TextFrame(token));
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceFrame(frame)));
} else if (*token == "all"){
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceAll(token, images, basedir)));
} else if (*token == "random"){
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceRandom(token, images, basedir)));
} else if (*token == "loop"){
// start loop here
int times;
token->view() >> times;
Util::ReferenceCount<SequenceLoop> loop(new SequenceLoop(times));
loop->parse(token, images, basedir);
- sequence.addSequence(loop.convert<Sequence>());
+ sequence.addSequence(loop);
/*
if (l >= (int)frames.size()){
ostringstream out;
out << "Loop location is larger than the number of frames. Loop: " << loop << " Frames: " << frames.size();
throw LoadException(__FILE__, __LINE__, out.str());
}
*/
} else if (*token == "reset"){
// Allow reset of animation
token->view() >> allowReset;
} else {
Global::debug( 3 ) << "Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
token->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu animation parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
Animation::Animation(const std::string & background):
id(getNextId()),
depth(BackgroundBottom),
allowReset(true),
sequence(0){
// add bitmap
Util::ReferenceCount<Graphics::Bitmap> bmp(new Graphics::Bitmap(*Storage::instance().open(Storage::instance().find(Filesystem::RelativePath(background)))));
if (bmp->getError()){
throw LoadException(__FILE__,__LINE__, "Problem loading file: " + background);
} else {
images[0] = bmp;
}
Util::ReferenceCount<Element> frame(new ImageFrame(bmp));
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceFrame(frame)));
}
Animation::Animation(const Filesystem::AbsolutePath & path):
id(getNextId()),
depth(BackgroundBottom),
allowReset(true),
sequence(0){
// add bitmap
Util::ReferenceCount<Graphics::Bitmap> bmp(new Graphics::Bitmap(*Storage::instance().open(path)));
if (bmp->getError()){
throw LoadException(__FILE__,__LINE__, "Problem loading file: " + path.path());
} else {
images[0] = bmp;
}
Util::ReferenceCount<Element> frame(new ImageFrame(bmp));
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceFrame(frame)));
}
Animation::Animation(Util::ReferenceCount<Graphics::Bitmap> image):
id(getNextId()),
depth(BackgroundBottom),
allowReset(true),
sequence(0){
images[0] = image;
Util::ReferenceCount<Element> frame(new ImageFrame(image));
sequence.addSequence(Util::ReferenceCount<Sequence>(new SequenceFrame(frame)));
}
Animation::~Animation(){
}
int Animation::totalTicks() const {
return sequence.totalTicks();
}
void Animation::forward(int tickCount){
sequence.forward(tickCount, velocity.getRelativeX(), velocity.getRelativeY());
}
void Animation::reverse(int tickCount){
sequence.reverse(tickCount, velocity.getRelativeX(), velocity.getRelativeY());
}
void Animation::act(){
forward();
}
void Animation::draw(const Graphics::Bitmap & work) const {
/* FIXME: should use sub-bitmaps here */
/*const int x = window.getX();
const int y = window.getY();
const int width = window.getWidth();
const int height = window.getHeight();
Global::debug(0) << "Distance x2: " << width << " Distance y2: " << height << std::endl;
Graphics::Bitmap clipped(work, x, y, height, width);
frames[currentFrame]->draw(0, 0,clipped);*/
// Set clip from the axis default is 0,0,bitmap width, bitmap height
work.setClipRect(-window.getPosition().getDistanceFromCenterX(),
-window.getPosition().getDistanceFromCenterY(),
work.getWidth() - window.getPosition2().getDistanceFromCenterX(),
work.getHeight() - window.getPosition2().getDistanceFromCenterY());
sequence.draw(axis.getDistanceFromCenterX(), axis.getDistanceFromCenterY(), work);
work.setClipRect(0, 0, work.getWidth(), work.getHeight());
}
void Animation::draw(int x, int y, int width, int height, const Graphics::Bitmap & work) const {
const Util::ReferenceCount<Element> & frame = sequence.getCurrentFrame();
if (frame != NULL){
Graphics::Bitmap clipped(work, x, y, width, height);
frame->draw(clipped);
}
}
void Animation::forwardFrame(){
sequence.forwardFrame();
}
void Animation::backFrame(){
sequence.backFrame();
}
void Animation::reset(){
sequence.reset();
}
void Animation::resetAll(){
sequence.reset();
}
void Animation::setToEnd(){
/* FIXME */
sequence.setToEnd();
// currentFrame = frames.size()-1;
// currentLoop = 0;
// Set offsets
/*
for (std::vector<Util::ReferenceCount<Element> >::iterator i = frames.begin(); i != frames.end(); ++i){
Util::ReferenceCount<Element> frame = *i;
frame->setToEnd(RelativePoint(ticks * velocity.getRelativeX(), ticks * velocity.getRelativeY()));
}
*/
}
const std::string Animation::getInfo(){
/*
static const char * ANIMATION_TEXT = "Animation ID: %d\nTicks: %d\nFrame Index: %d\nLoop From: %d\nAxis: ( %f, %f)\nVelocity: ( %f, %f)\n";
char info[255];
sprintf(info, ANIMATION_TEXT, id, ticks, currentFrame, loop, axis.getRelativeX(), axis.getRelativeY(), velocity.getRelativeX(), velocity.getRelativeY());
return std::string(info) + frames[currentFrame]->getInfo();
*/
/* FIXME */
ostringstream out;
out << "Animation ID: " << id;
return out.str();
}
AnimationManager::AnimationManager(){
// Set the current id to 0 for each context
CURRENT_ID = 0;
}
AnimationManager::AnimationManager(const AnimationManager & copy):
animations(copy.animations){
// Set the current id to 0 for each context
CURRENT_ID = 0;
}
AnimationManager::~AnimationManager(){
}
const AnimationManager & AnimationManager::operator=(const AnimationManager & copy){
animations = copy.animations;
return *this;
}
void AnimationManager::forward(int tickCount){
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
animation->forward(tickCount);
}
}
}
}
void AnimationManager::reverse(int tickCount){
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
animation->reverse(tickCount);
}
}
}
}
void AnimationManager::act(){
forward();
}
void AnimationManager::render(const Gui::Animation::Depth & depth, const Graphics::Bitmap & work) const {
std::map< Gui::Animation::Depth, std::vector< Util::ReferenceCount<Gui::Animation> > >::const_iterator animation = animations.find(depth);
if (animation != animations.end()){
const vector<Util::ReferenceCount<Gui::Animation> > & all = animation->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::const_iterator i = all.begin(); i != all.end(); ++i){
Util::ReferenceCount<Gui::Animation> animation = *i;
if (animation != NULL){
animation->draw(work);
}
}
}
}
void AnimationManager::add(Util::ReferenceCount<Gui::Animation > animation){
animations[animation->getDepth()].push_back(animation);
}
void AnimationManager::reset(){
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
animation->resetAll();
}
}
}
}
int AnimationManager::totalTicks() const {
int count = 0;
for (map<Gui::Animation::Depth, vector<Util::ReferenceCount<Gui::Animation> > >::const_iterator it = animations.begin(); it != animations.end(); it++){
Gui::Animation::Depth depth = it->first;
const vector<Util::ReferenceCount<Gui::Animation> > & stuff = it->second;
int next = countTicks(stuff);
/* If any animation takes infinite time then the whole thing takes infinite time */
if (next == -1){
return -1;
}
if (next > count){
count = next;
}
}
return count;
}
int AnimationManager::countTicks(const vector<Util::ReferenceCount<Animation> > & toCount) const {
int total = 0;
for (vector<Util::ReferenceCount<Animation> >::const_iterator it = toCount.begin(); it != toCount.end(); it++){
const Util::ReferenceCount<Animation> & animation = *it;
int ticks = animation->totalTicks();
if (ticks == -1){
return ticks;
}
total += ticks;
}
return total;
}
void AnimationManager::setToEnd(){
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
animation->setToEnd();
}
}
}
}
const std::string AnimationManager::getInfo(int id, bool all){
std::string info("");
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
if (id == animation->getID() || all){
info += animation->getInfo();
}
}
}
}
return info;
}
const std::vector<int> AnimationManager::getIdList(){
std::vector<int> list;
for (std::map<Gui::Animation::Depth, std::vector<Util::ReferenceCount<Gui::Animation> > >::iterator i = animations.begin(); i != animations.end(); ++i){
std::vector<Util::ReferenceCount<Gui::Animation> > & animations = i->second;
for (std::vector<Util::ReferenceCount<Gui::Animation> >::iterator j = animations.begin(); j != animations.end(); ++j){
Util::ReferenceCount<Gui::Animation> animation = *j;
if (animation != NULL){
list.push_back(animation->getID());
}
}
}
return list;
}
diff --git a/util/gui/context-box.cpp b/util/gui/context-box.cpp
index f002c0bb..9566241d 100644
--- a/util/gui/context-box.cpp
+++ b/util/gui/context-box.cpp
@@ -1,359 +1,359 @@
#include "util/graphics/bitmap.h"
#include "context-box.h"
#include "util/font.h"
#include <math.h>
#include "util/token.h"
static const double FONT_SPACER = 1.3;
static const int GradientMax = 50;
static Graphics::Color selectedGradientStart(){
static Graphics::Color color = Graphics::makeColor(19, 167, 168);
return color;
}
static Graphics::Color selectedGradientEnd(){
static Graphics::Color color = Graphics::makeColor(27, 237, 239);
return color;
}
using namespace std;
namespace Gui{
Effects::Gradient standardGradient(int max){
Effects::Gradient standard(max, selectedGradientStart(), selectedGradientEnd());
return standard;
}
Effects::Gradient modifiedGradient(Graphics::Color low, Graphics::Color high, int max){
Effects::Gradient modified(max, low, high);
return modified;
}
ListValues::ListValues():
interpolate(true),
lowColor(selectedGradientStart()),
highColor(selectedGradientEnd()),
maxGradient(GradientMax),
selectedColor(selectedGradientStart()),
selectedAlpha(255),
otherColor(Graphics::makeColor(255, 255, 255)),
otherAlpha(255),
distanceFadeMultiplier(35),
fade(true){
}
ListValues::ListValues(const ListValues & copy):
interpolate(copy.interpolate),
lowColor(copy.lowColor),
highColor(copy.highColor),
maxGradient(copy.maxGradient),
selectedColor(copy.selectedColor),
selectedAlpha(copy.selectedAlpha),
otherColor(copy.otherColor),
otherAlpha(copy.otherAlpha),
distanceFadeMultiplier(copy.distanceFadeMultiplier),
fade(copy.fade){
}
ListValues::~ListValues(){
}
const ListValues & ListValues::operator=(const ListValues & copy){
interpolate = copy.interpolate;
lowColor = copy.lowColor;
highColor = copy.highColor;
maxGradient = copy.maxGradient;
selectedColor = copy.selectedColor;
selectedAlpha = copy.selectedAlpha;
otherColor = copy.otherColor;
otherAlpha = copy.otherAlpha;
distanceFadeMultiplier = copy.distanceFadeMultiplier;
fade = copy.fade;
return *this;
}
static int clamp(int in, int low = 0, int high = 255){
if (in < low){
return low;
}
if (in > high){
return high;
}
return in;
}
void ListValues::getValues(const Token * token){
TokenView view = token->view();
while (view.hasMore()){
const Token * token;
view >> token;
try{
int red = 0, green = 0, blue = 0, alpha = 0, gradient=1;
if (token->match("interpolate-selected", interpolate)){
} else if (token->match("color-low", red, green, blue)){
lowColor = Graphics::makeColor(clamp(red), clamp(green), clamp(blue));
} else if (token->match("color-high", red, green, blue)){
highColor = Graphics::makeColor(clamp(red), clamp(green), clamp(blue));
} else if (token->match("interpolate-distance", gradient)){
maxGradient = clamp(gradient, 1, 1000);
} else if (token->match("selected-color", red, green, blue)){
selectedColor = Graphics::makeColor(clamp(red), clamp(green), clamp(blue));
} else if (token->match("selected-color-alpha", alpha)){
selectedAlpha = clamp(alpha);
} else if (token->match("other-color", red, green, blue)){
otherColor = Graphics::makeColor(clamp(red), clamp(green), clamp(blue));
} else if (token->match("other-color-alpha", alpha)){
otherAlpha = clamp(alpha);
} else if (token->match("distance-fade-multiplier", distanceFadeMultiplier)){
} else if (token->match("distance-fade", fade)){
}
} catch (const TokenException & ex){
// Output something
}
}
}
ContextItem::ContextItem(const std::string & name, const ContextBox & parent):
text(name),
parent(parent){
}
ContextItem::~ContextItem(){
}
void ContextItem::draw(int x, int y, const Graphics::Bitmap & where, const Font & font, int distance) const {
if (distance == 0){
if (parent.getListValues().getInterpolate()){
Graphics::Bitmap::transBlender(0, 0, 0, parent.getFadeAlpha());
font.printf(x, y, parent.getSelectedColor(), where.translucent(), getText(), 0);
} else {
Graphics::Bitmap::transBlender(0, 0, 0, parent.getListValues().getSelectedAlpha());
font.printf(x, y, parent.getListValues().getSelectedColor(), where.translucent(), getText(), 0);
}
} else {
if (parent.getListValues().getDistanceFade()){
int alpha = parent.getListValues().getOtherAlpha() - fabs((double) distance) * parent.getListValues().getDistanceFadeMultiplier();
alpha = clamp(alpha);
Graphics::Bitmap::transBlender(0, 0, 0, alpha);
font.printf(x, y, parent.getListValues().getOtherColor(), where.translucent(), getText(), 0);
} else {
Graphics::Bitmap::transBlender(0, 0, 0, parent.getListValues().getOtherAlpha());
font.printf(x, y, parent.getListValues().getOtherColor(), where.translucent(), getText(), 0);
}
}
}
void ContextItem::setText(const LanguageString & t){
text = t;
}
int ContextItem::size(const Font & font) const {
return font.textLength(getText().c_str());
}
ContextBox::ContextBox():
fadeState(NotActive),
list(new ScrollList()),
fadeSpeed(12),
fadeAlpha(0),
cursorCenter(0),
cursorLocation(0),
scrollWait(4),
selectedGradient(standardGradient(GradientMax)),
renderOnlyText(false){
}
ContextBox::ContextBox( const ContextBox & copy ):
fadeState(NotActive),
list(new ScrollList()),
selectedGradient(standardGradient(GradientMax)),
renderOnlyText(false){
this->list = copy.list;
this->fadeSpeed = copy.fadeSpeed;
this->fadeAlpha = copy.fadeAlpha;
this->cursorCenter = copy.cursorCenter;
this->cursorLocation = copy.cursorLocation;
this->scrollWait = copy.scrollWait;
this->renderOnlyText = copy.renderOnlyText;
}
ContextBox::~ContextBox(){
}
ContextBox & ContextBox::operator=( const ContextBox & copy){
this->fadeState = NotActive;
this->list = copy.list;
this->fadeSpeed = copy.fadeSpeed;
this->fadeAlpha = copy.fadeAlpha;
this->cursorCenter = copy.cursorCenter;
this->cursorLocation = copy.cursorLocation;
this->scrollWait = copy.scrollWait;
this->renderOnlyText = copy.renderOnlyText;
return *this;
}
void ContextBox::act(const Font & font){
// update board
board.act(font);
// Calculate text info
list->act();
// do fade
doFade();
// Update gradient
selectedGradient.update();
}
void ContextBox::render(const Graphics::Bitmap & work){
}
void ContextBox::render(const Graphics::Bitmap & work, const Font & font){
if (!renderOnlyText){
board.render(work);
}
drawText(work, font);
}
bool ContextBox::next(const Font & font){
if (fadeState == FadeOut){
return false;
}
list->next();
return true;
}
bool ContextBox::previous(const Font & font){
if (fadeState == FadeOut){
return false;
}
list->previous();
return true;
}
Graphics::Color ContextBox::getSelectedColor() const {
return values.getInterpolate() ? selectedGradient.current() : values.getSelectedColor();
}
void ContextBox::setListValues(const Gui::ListValues & values){
this->values = values;
selectedGradient = modifiedGradient(values.getLowColor(), values.getHighColor(), values.getMaxGradient());
}
void ContextBox::adjustLeft(){
}
void ContextBox::adjustRight(){
}
void ContextBox::open(){
// Set the fade stuff
fadeState = FadeIn;
//board.position = position;
board.location = location;
board.transforms = transforms;
board.colors = colors;
board.open();
fadeAlpha = 0;
cursorLocation = 0;
}
void ContextBox::close(){
fadeState = FadeOut;
board.close();
fadeAlpha = 255;
cursorLocation = 480;
}
void ContextBox::doFade(){
switch (fadeState){
case FadeIn: {
if (fadeAlpha < 255){
fadeAlpha += (fadeSpeed+2);
}
if (fadeAlpha >= 255){
fadeAlpha = 255;
if (board.isActive()){
fadeState = Active;
}
}
break;
}
case FadeOut: {
if (fadeAlpha > 0){
fadeAlpha -= (fadeSpeed+2);
}
if (fadeAlpha <= 0){
fadeAlpha = 0;
if (!board.isActive()){
fadeState = NotActive;
}
}
break;
}
case Active:
case NotActive:
default:
break;
}
}
void ContextBox::setList(const std::vector<Util::ReferenceCount<ContextItem> > & list){
this->list->clearItems();
for (vector<Util::ReferenceCount<ContextItem> >::const_iterator it = list.begin(); it != list.end(); it++){
const Util::ReferenceCount<ContextItem> & item = *it;
- this->list->addItem(item.convert<ScrollItem>());
+ this->list->addItem(item);
}
}
void ContextBox::addItem(const Util::ReferenceCount<ContextItem> & item){
- this->list->addItem(item.convert<ScrollItem>());
+ this->list->addItem(item);
}
void ContextBox::setListType(const ListType & type){
switch (type){
case Normal:{
Util::ReferenceCount<ScrollListInterface> newList(new NormalList());
newList->addItems(list->getItems());
list = newList;
break;
}
case Scroll:{
Util::ReferenceCount<ScrollListInterface> newList(new ScrollList());
newList->addItems(list->getItems());
list = newList;
break;
}
default:
break;
}
}
void ContextBox::setListWrap(bool wrap){
list->setWrap(wrap);
}
void ContextBox::drawText(const Graphics::Bitmap & bmp, const Font & font){
const int x1 = board.getArea().getX()+(int)(board.getTransforms().getRadius()/2);
const int y1 = board.getArea().getY()+2;//(board.getArea().radius/2);
const int x2 = board.getArea().getX2()-(int)(board.getTransforms().getRadius()/2);
const int y2 = board.getArea().getY2()-2;//(board.getArea().radius/2);
if (x2 > x1 && y2 > y1){
Graphics::Bitmap area(bmp, x1, y1, x2 - x1, y2 - y1);
list->render(area, font);
}
}
}
diff --git a/util/loading.cpp b/util/loading.cpp
index 1ce54272..67d9c79a 100644
--- a/util/loading.cpp
+++ b/util/loading.cpp
@@ -1,556 +1,556 @@
#include "graphics/bitmap.h"
#include <math.h>
#include <iostream>
#include "messages.h"
#include "loading.h"
#include "file-system.h"
#include "font.h"
#include "funcs.h"
#include "version.h"
#include "graphics/gradient.h"
#include "parameter.h"
#include "thread.h"
#include <vector>
#include "thread.h"
#include "message-queue.h"
#include "init.h"
#include "events.h"
#include "input/input-map.h"
#include "input/input-manager.h"
using namespace std;
namespace Loader{
volatile bool done_loading = true;
typedef struct pair{
int x, y;
} ppair;
class MessageInfo{
public:
MessageInfo(){
MessageQueue::registerInfo(&messages);
}
bool transferMessages(Messages & box){
bool did = false;
while (messages.hasAny()){
const string & str = messages.get();
box.addMessage(str);
did = true;
}
return did;
}
~MessageInfo(){
MessageQueue::unregisterInfo(&messages);
}
private:
MessageQueue messages;
};
Info::Info(const string & message, const Filesystem::AbsolutePath & background):
x(-1),
y(-1),
_loadingMessage(message),
background(NULL),
_loadingBackground(background){
}
Info::Info(const Info & info){
this->x = info.x;
this->y = info.y;
this->_loadingMessage = info._loadingMessage;
this->background = info.background;
this->_loadingBackground = info._loadingBackground;
}
Info::~Info(){
}
void Info::setBackground(const Graphics::Bitmap * background){
this->background = background;
}
void Info::setLoadingMessage(const std::string & str){
this->_loadingMessage = str;
}
void Info::setPosition(int x, int y){
this->x = x;
this->y = y;
}
const Graphics::Bitmap * Info::getBackground() const {
return background;
}
const string & Info::loadingMessage() const {
return _loadingMessage;
}
const Filesystem::AbsolutePath & Info::loadingBackground() const {
return _loadingBackground;
}
int Info::getPositionX() const {
return x;
}
int Info::getPositionY() const {
return y;
}
void * loadingScreenSimple1(void * arg);
static void setupBackground(const Graphics::Bitmap & background, int load_x, int load_y, int load_width, int load_height, int infobox_x, int infobox_y, int infoWidth, int infoHeight, const Graphics::Bitmap & infoBackground, const Graphics::Bitmap & screen){
int startX = background.getWidth() - Font::getDefaultFont().textLength("Paintown version 9.9.9.9");
int startY = background.getHeight() - Font::getDefaultFont().getHeight() * 4;
int height = Font::getDefaultFont().getHeight();
Font::getDefaultFont().printf(startX, startY + height * 0, Graphics::makeColor(192, 192, 192), background, "Paintown version %s", 0, Version::getVersionString().c_str());
Font::getDefaultFont().printf(startX, startY + height * 1, Graphics::makeColor(192, 192, 192), background, "Made by Jon Rafkind", 0);
Font::getDefaultFont().printf(startX, startY + height * 2, Graphics::makeColor(192, 192, 192), background, "http://paintown.org", 0);
/* we have to blit to the screen object passed in because that is the bitmap
* that will be operated on in the draw() method of loadingScreen1.
* we also have to blit to the real screen because the screen object
* is not drawn in its entirety to the real screen, only the part
* that shows the 'Loading ...' message and the info box.
* drawing twice in Allegro5 is redundant because the screen object is the real
* screen but for Allegro4 and SDL we need to do this because the screen object
* is a buffer.
*/
background.Blit(screen);
background.BlitToScreen();
background.Blit(infobox_x, infobox_y, infoWidth, infoHeight, 0, 0, infoBackground);
}
/* converts a bitmap with some text on it into a sequence of points */
static vector<ppair> generateFontPixels(const Font & myFont, const string & message, int width, int height){
Graphics::Bitmap letters(width, height);
letters.fill(Graphics::MaskColor());
myFont.printf(0, 0, Graphics::makeColor(255, 255, 255), letters, message.c_str(), 0);
vector<ppair> pairs;
/* store every pixel we need to draw */
letters.lock();
for (int x = 0; x < letters.getWidth(); x++){
for (int y = 0; y < letters.getHeight(); y++){
Graphics::Color pixel = letters.getPixel(x, y);
if (pixel != Graphics::MaskColor()){
ppair p;
p.x = x;
p.y = y;
pairs.push_back(p);
}
}
}
letters.unlock();
// Graphics::resetDisplay();
return pairs;
}
/* shows time elapsed */
class TimeCounter{
public:
TimeCounter():
work(200, 40){
start = Global::second_counter;
last = 0;
}
void draw(int x, int y){
const Font & font = Font::getDefaultFont(24, 24);
if (Global::second_counter != last){
work.clear();
last = Global::second_counter;
font.printf(0, 0, Graphics::makeColor(192, 192, 192), work, "Waiting.. %d", 0, last - start);
work.BlitAreaToScreen(x, y);
}
}
Graphics::Bitmap work;
unsigned int start;
unsigned int last;
};
enum LoadingKeys{
Activate
};
static void loadingScreen1(LoadingContext & context, const Info & levelInfo){
int load_x = 80;
int load_y = 220;
const int infobox_width = 300;
const int infobox_height = 150;
const Font & myFont = Font::getDefaultFont(24, 24);
if (levelInfo.getPositionX() != -1){
load_x = levelInfo.getPositionX();
}
if (levelInfo.getPositionY() != -1){
load_y = levelInfo.getPositionY();
}
// const char * the_string = (arg != NULL) ? (const char *) arg : "Loading...";
int load_width = myFont.textLength(levelInfo.loadingMessage().c_str());
int load_height = myFont.getHeight(levelInfo.loadingMessage().c_str());
Global::debug(2) << "loading screen" << endl;
Messages infobox(infobox_width, infobox_height);
const int MAX_COLOR = 200;
/* blend from dark grey to light red */
Effects::Gradient gradient(MAX_COLOR, Graphics::makeColor(16, 16, 16), Graphics::makeColor(192, 8, 8));
TimeCounter counter;
struct State{
bool drawInfo;
};
class Logic: public Util::Logic {
public:
Logic(LoadingContext & context, State & state, Effects::Gradient & gradient, Messages & infoBox):
context(context),
state(state),
gradient(gradient),
infobox(infoBox),
active(false){
input.set(Keyboard::Key_SPACE, 0, true, Activate);
input.set(Keyboard::Key_ENTER, 0, true, Activate);
input.set(Joystick::Button1, 0, true, Activate);
}
LoadingContext & context;
State & state;
Effects::Gradient & gradient;
MessageInfo info;
Messages & infobox;
bool active;
InputMap<LoadingKeys> input;
void doInput(){
class Handler: public InputHandler<LoadingKeys> {
public:
Handler(bool & active):
active(active){
}
bool & active;
void press(const LoadingKeys & out, Keyboard::unicode_t unicode){
if (out == Activate){
/* the info box can't really be turned off because once
* its drawn it will remain there. the background would
* have to be drawn on top of it to remove the old
* info box. maybe do this in the future, if so use
* active = ! active
* to toggle it.
*/
active = ! active;
}
}
void release(const LoadingKeys & out, Keyboard::unicode_t unicode){
}
};
Handler handler(active);
InputManager::handleEvents(input, InputSource(true), handler);
}
void run(){
for (int i = 0; i < 3; i++){
gradient.backward();
}
doInput();
info.transferMessages(infobox);
// state.drawInfo = active && (info.transferMessages(infobox) || state.drawInfo);
state.drawInfo = active;
}
double ticks(double system){
return system * Global::ticksPerSecond(8);
}
bool done(){
return context.done();
}
};
class Draw: public Util::Draw {
public:
Draw(const Info & levelInfo, State & state, Messages & infobox, Effects::Gradient & gradient, int load_width, int load_height, int infobox_width, int infobox_height, int load_x, int load_y):
levelInfo(levelInfo),
gradient(gradient),
state(state),
infobox(infobox),
infobox_x(load_x),
infobox_y(load_y + load_height * 2),
infobox_width(infobox_width),
infobox_height(infobox_height),
load_x(load_x),
load_y(load_y),
load_width(load_width),
load_height(load_height){
if (levelInfo.loadingBackground() != Filesystem::AbsolutePath("")){
background = Graphics::Bitmap(levelInfo.loadingBackground().path());
}
const Font & myFont = Font::getDefaultFont(24, 24);
pairs = generateFontPixels(myFont, levelInfo.loadingMessage(), load_width, load_height);
}
const Info & levelInfo;
Effects::Gradient & gradient;
State & state;
Messages & infobox;
Graphics::Bitmap background;
// Graphics::Bitmap infoWork;
// Graphics::Bitmap infoBackground;
vector<ppair> pairs;
const int infobox_x;
const int infobox_y;
const int infobox_width;
const int infobox_height;
const int load_x;
const int load_y;
const int load_width;
const int load_height;
/*
void drawFirst(const Graphics::Bitmap & screen){
if (levelInfo.getBackground() != NULL){
setupBackground(*levelInfo.getBackground(), load_x, load_y, load_width, load_height, infobox_x, infobox_y, infoBackground.getWidth(), infoBackground.getHeight(), infoBackground, screen);
} else {
Graphics::Bitmap background;
if (levelInfo.loadingBackground() != Filesystem::AbsolutePath("")){
background = Graphics::Bitmap(levelInfo.loadingBackground().path());
} else {
background = Graphics::Bitmap(*Graphics::screenParameter.current(), true);
}
setupBackground(background, load_x, load_y, load_width, load_height, infobox_x, infobox_y, infoBackground.getWidth(), infoBackground.getHeight(), infoBackground, screen);
}
}
*/
void drawAuthorInfo(const Graphics::Bitmap & screen){
int startX = screen.getWidth() - Font::getDefaultFont().textLength("Paintown version 9.9.9.9");
int startY = screen.getHeight() - Font::getDefaultFont().getHeight() * 4;
int height = Font::getDefaultFont().getHeight();
Font::getDefaultFont().printf(startX, startY + height * 0, Graphics::makeColor(192, 192, 192), screen, "Paintown version %s", 0, Version::getVersionString().c_str());
Font::getDefaultFont().printf(startX, startY + height * 1, Graphics::makeColor(192, 192, 192), screen, "Made by Jon Rafkind", 0);
Font::getDefaultFont().printf(startX, startY + height * 2, Graphics::makeColor(192, 192, 192), screen, "http://paintown.org", 0);
}
void draw(const Graphics::Bitmap & screen){
if (!background.isEmpty()){
background.drawStretched(screen);
}
Graphics::Bitmap work(screen, load_x, load_y, load_width, load_height);
// work.lock();
for (vector< ppair >::iterator it = pairs.begin(); it != pairs.end(); it++){
Graphics::Color color = gradient.current(it->x);
work.putPixel(it->x, it->y, color);
}
// work.unlock();
if (state.drawInfo){
// infoBackground.Blit(infoWork);
const Font & infoFont = Font::getDefaultFont(24, 24);
/* cheesy hack to change the font size. the font
* should store the size and change it on its own
*/
Graphics::Bitmap infoWork(screen, infobox_x, infobox_y, infobox_width, infobox_height);
Font::getDefaultFont(13, 13);
infobox.draw(0, 0, infoWork, infoFont);
Font::getDefaultFont(24, 24);
infoWork.BlitAreaToScreen(infobox_x, infobox_y);
// infoWork.BlitToScreen();
// state.drawInfo = false;
}
drawAuthorInfo(screen);
/* work already contains the correct background */
// work.Blit( load_x, load_y, *Bitmap::Screen );
// work.BlitToScreen();
// work.BlitAreaToScreen(load_x, load_y);
}
};
State state;
- // state.drawInfo = true;
+ state.drawInfo = false;
Logic logic(context, state, gradient, infobox);
Draw draw(levelInfo, state, infobox, gradient, load_width, load_height, infobox_width, infobox_height, load_x, load_y);
Util::standardLoop(logic, draw);
}
static void loadingScreenSimpleX1(LoadingContext & context, const Info & levelInfo){
class Logic: public Util::Logic {
public:
Logic(LoadingContext & context, int & angle, int speed):
context(context),
speed(speed),
angle(angle){
}
LoadingContext & context;
/* speed of rotation */
const int speed;
int & angle;
double ticks(double system){
return system * Global::ticksPerSecond(10);
}
bool done(){
return context.done();
}
void run(){
angle += speed * 2;
angle = angle % 360;
}
};
class Draw: public Util::Draw {
public:
Draw(int & angle, const int speed):
angle(angle),
speed(speed){
/*
if (levelInfo.loadingBackground() != Filesystem::AbsolutePath("")){
background = Graphics::Bitmap(levelInfo.loadingBackground().path());
}
*/
colors[0] = Graphics::makeColor(0x18, 0x52, 0xa0);
colors[1] = Graphics::makeColor(0x00, 0x99, 0xff);
colors[2] = Graphics::makeColor(0xff, 0x22, 0x33);
colors[3] = Graphics::makeColor(0x44, 0x77, 0x33);
Graphics::Bitmap::transBlender(0, 0, 0, 64);
}
Graphics::Bitmap background;
int & angle;
const int speed;
/* the length of this array is the number of circles to show */
Graphics::Color colors[4];
virtual ~Draw(){
}
void draw(const Graphics::Bitmap & screen){
if (!background.isEmpty()){
background.drawStretched(screen);
}
Graphics::Bitmap work(screen, 0, 0, 40, 40);
int max = Util::array_size(colors);
double middleX = work.getWidth() / 2;
double middleY = work.getHeight() / 2;
int size = 15;
int radius = 3;
for (int i = 0; i < max; i++){
double new_angle = angle + 360.0 / (double) max * i;
int lag = 17;
double x = cos(Util::radians(new_angle - lag)) * size;
double y = sin(Util::radians(new_angle - lag)) * size;
/* ghost circle */
work.translucent(0, 0, 0, 128).circleFill(middleX + x, middleY + y, radius, colors[i]);
x = cos(Util::radians(new_angle)) * size;
y = sin(Util::radians(new_angle)) * size;
/* real circle */
work.circleFill(middleX + x, middleY + y, radius, colors[i]);
}
// work.BlitAreaToScreen(0, 0);
}
};
int angle = 0;
int speed = 10;
Logic logic(context, angle, speed);
Draw draw(angle, speed);
Util::standardLoop(logic, draw);
}
LoadingContext::LoadingContext():
finished(false){
Util::Thread::initializeLock(&lock);
}
LoadingContext::~LoadingContext(){
}
void LoadingContext::doLoad(){
this->load();
Util::Thread::acquireLock(&lock);
finished = true;
Util::Thread::releaseLock(&lock);
}
bool LoadingContext::done(){
bool ok = false;
Util::Thread::acquireLock(&lock);
ok = this->finished;
Util::Thread::releaseLock(&lock);
return ok;
}
void * LoadingContext::load_it(void * arg){
LoadingContext * context = (LoadingContext*) arg;
context->doLoad();
return NULL;
}
static void showLoadMessage(){
int screenX = 80;
int screenY = 50;
Graphics::Bitmap work(110, 50);
work.BlitFromScreen(screenX, screenY);
Graphics::Bitmap top(110, 50);
top.fill(Graphics::makeColor(0, 0, 0));
Font::getDefaultFont(25, 25).printf(10, 5, Graphics::makeColor(192, 192, 192), top, "Loading", 0);
Graphics::Bitmap::transBlender(0, 0, 0, 200);
top.translucent().draw(0, 0, work);
work.BlitAreaToScreen(screenX, screenY);
}
void loadScreen(LoadingContext & context, const Info & info, Kind kind){
Util::Thread::Id loadingThread;
bool created = Util::Thread::createThread(&loadingThread, NULL, (Util::Thread::ThreadFunction) LoadingContext::load_it, &context);
if (!created){
Global::debug(0) << "Could not create loading thread. Loading will occur in the main thread" << endl;
showLoadMessage();
LoadingContext::load_it(&context);
// throw LoadException(__FILE__, __LINE__, "Could not create loader thread");
} else {
InputManager::deferResizeEvents(true);
switch (kind){
case Default: loadingScreen1(context, info); break;
case SimpleCircle: loadingScreenSimpleX1(context, info); break;
default: loadingScreen1(context, info); break;
}
Util::Thread::joinThread(loadingThread);
InputManager::deferResizeEvents(false);
}
}
}
diff --git a/util/menu/menu.cpp b/util/menu/menu.cpp
index 792c58d6..471a02fa 100644
--- a/util/menu/menu.cpp
+++ b/util/menu/menu.cpp
@@ -1,1849 +1,1849 @@
#include "util/graphics/bitmap.h"
#include "menu.h"
#include "menu_option.h"
#include "util/version.h"
#include "util/funcs.h"
#include "util/sound/sound.h"
#include "util/font.h"
#include "util/token.h"
#include "util/events.h"
#include "util/tokenreader.h"
#include "util/file-system.h"
#include "util/resource.h"
#include "util/debug.h"
#include "util/init.h"
#include "util/configuration.h"
#include "util/sound/music.h"
#include "util/graphics/gradient.h"
#include "util/exceptions/shutdown_exception.h"
#include "util/exceptions/exception.h"
#include "optionfactory.h"
#include "actionfactory.h"
#include "util/input/input-manager.h"
#include "util/input/input-map.h"
#include "util/input/input-source.h"
#include "util/parameter.h"
#include <queue>
#include <map>
#include <ostream>
#include <sstream>
#include "util/gui/context-box.h"
using namespace std;
using namespace Gui;
/* the current font is a property of the dynamic execution. so it will
* be modified by various functions that call Parameter::push
*/
static const Font & currentFont(){
return Menu::menuFontParameter.current()->get();
}
Util::Parameter<Util::ReferenceCount<Menu::FontInfo> > Menu::menuFontParameter;
/*
static std::string sharedFont = "fonts/arial.ttf";
static int sharedFontWidth = 24;
static int sharedFontHeight = 24;
*/
Effects::Gradient Menu::standardGradient(){
return Gui::standardGradient(50);
}
Menu::Point::Point():
x(0),
y(0){
}
Menu::Point::Point(int x, int y):
x(x),
y(y){
}
Menu::Point::~Point(){
}
Menu::InfoBox::InfoBox():
state(NotActive),
fadeAlpha(0){
popup.setFadeSpeed(20);
}
Menu::InfoBox::~InfoBox(){
}
void Menu::InfoBox::act(const Font & font){
popup.act(font);
int speed = 9;
switch (state){
case Opening: {
if (fadeAlpha < 255){
fadeAlpha += speed;
}
if (fadeAlpha >= 255){
fadeAlpha = 255;
if (popup.isActive()){
state = Active;
}
}
break;
}
case Closing: {
if (fadeAlpha > 0){
fadeAlpha -= speed;
}
if (fadeAlpha <= 0){
fadeAlpha = 0;
if (!popup.isActive()){
state = NotActive;
}
}
break;
}
case Active:
case NotActive:
default:
break;
}
}
void Menu::InfoBox::render(const Graphics::Bitmap &){
throw MenuException(__FILE__, __LINE__, "Don't call the render(Bitmap) function");
}
void Menu::InfoBox::render(const Graphics::Bitmap & bmp, const Font & vFont){
popup.render(bmp);
// const Font & vFont = Configuration::getMenuFont()->get(*font);
const int x1 = popup.getArea().getX()+(int)(popup.getTransforms().getRadius()/2);
const int y1 = popup.getArea().getY()+2;
const int x2 = popup.getArea().getX2()-(int)(popup.getTransforms().getRadius()/2);
const int y2 = popup.getArea().getY2()-2;
bmp.setClipRect(x1, y1, x2, y2);
// FIXME height is proportionally wrong in a majority of the cases, this is perhaps due to ftalleg.
int sy = location.getY() - vFont.getHeight()/6;// - location.getHeight()/2 - vFont.getHeight()/2;
static Graphics::Color white = Graphics::makeColor(255,255,255);
unsigned int padding_index = 0;
for (vector<string>::iterator it = text.begin(); it != text.end(); it++){
string & str = *it;
if (fadeAlpha < 255){
Graphics::Bitmap::transBlender(0, 0, 0, fadeAlpha);
vFont.printf(location.getX() + padding[padding_index]/2, sy, white, bmp.translucent(), str, 0 );
} else {
vFont.printf(location.getX() + padding[padding_index]/2, sy, white, bmp, str, 0 );
}
sy += vFont.getHeight();
padding_index++;
}
bmp.setClipRect(0, 0, bmp.getWidth(), bmp.getHeight());
}
void Menu::InfoBox::open(){
state = Opening;
popup.location = location;
popup.transforms = transforms;
popup.colors = colors;
popup.open();
fadeAlpha = 0;
}
void Menu::InfoBox::close(){
state = Closing;
popup.close();
}
/* dimensions are computed lazily when we get a font, but only compute once per font */
void Menu::InfoBox::initialize(const Font & font){
int maxWidth = 0;
int height = 0;
for (vector<string>::iterator it = text.begin(); it != text.end(); it++){
// Add the padding
ostringstream pad;
pad << (*it)[0] << (*it)[(*it).size()-1];
padding.push_back(font.textLength(pad.str().c_str()));
int w = font.textLength((*it).c_str()) + padding.back();
if (w > maxWidth){
maxWidth = w;
}
height += font.getHeight();
}
location.setDimensions(maxWidth, height);
}
void Menu::InfoBox::setText(const std::string & info){
if (info.empty()){
return;
}
text.clear();
// const Font & vFont = Configuration::getMenuFont()->get(*font);
size_t start = 0;
size_t last = 0;
start = info.find("\n");
while (start != string::npos){
text.push_back(info.substr(last, start - last));
last = start + 1;
start = info.find("\n", last);
}
text.push_back(info.substr(last));
}
static std::vector<Util::ReferenceCount<ContextItem> > toContextList(const ContextBox & context, const std::vector<Util::ReferenceCount<MenuOption> > & list){
std::vector<Util::ReferenceCount<ContextItem> > contextItems;
for (std::vector<Util::ReferenceCount<MenuOption> >::const_iterator i = list.begin(); i != list.end(); ++i){
const Util::ReferenceCount<MenuOption> & option = *i;
- contextItems.push_back(option.convert<ContextItem>());
+ contextItems.push_back(option);
}
return contextItems;
}
static void tryPlaySound(const Filesystem::RelativePath & path){
Util::ReferenceCount<Sound> ok = Resource::getSound(path);
if (ok != NULL){
ok->play();
}
}
/*
* FIXME Exception handling for ValueHolder*/
Menu::MenuException::MenuException(const std::string & file, int line, const std::string reason ):
Exception::Base(file, line),
reason(reason){
}
Menu::MenuException::MenuException(const MenuException & copy):
Exception::Base(copy),
reason(copy.reason){
}
Menu::MenuException::MenuException(const Exception::Base & copy):
Exception::Base(copy),
reason("unknown"){
}
Menu::MenuException::~MenuException() throw(){
}
Exception::Base * Menu::MenuException::copy() const {
return new MenuException(*this);
}
Menu::Reload::Reload(const std::string & file, int line, const std::string reason):
MenuException(file, line, reason){
}
Menu::Reload::~Reload() throw() {
}
Exception::Base * Menu::Reload::copy() const {
return new Reload(*this);
}
Menu::ValueHolder::ValueHolder(const std::string & name):
name(name),
location(0){
}
Menu::ValueHolder::~ValueHolder(){
}
Menu::ValueHolder::ValueHolder(const ValueHolder & copy){
// reset position
this->location = 0;
this->name = copy.name;
this->values = copy.values;
}
Menu::ValueHolder & Menu::ValueHolder::operator=(const ValueHolder & copy){
// reset position
this->location = 0;
this->name = copy.name;
this->values = copy.values;
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(const std::string & val){
values.push_back(val);
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(bool val){
std::ostringstream o;
o << val;
return *this << o.str();
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(int val){
std::ostringstream o;
o << val;
return *this << o.str();
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(double val){
std::ostringstream o;
o << val;
return *this << o.str();
}
Menu::ValueHolder & Menu::ValueHolder::operator<<(TokenView & view){
std::string temp;
view >> temp;
return *this << temp;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(std::string & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
val = values[location];
next();
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(bool & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
std::istringstream i(values[location]);
i >> val;
next();
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(int & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
std::istringstream i(values[location]);
i >> val;
next();
return *this;
}
Menu::ValueHolder & Menu::ValueHolder::operator>>(double & val){
if (values[location].empty()){
throw MenuException(__FILE__, __LINE__, "Empty value.");
}
std::istringstream i(values[location]);
i >> val;
next();
return *this;
}
void Menu::ValueHolder::next(){
location++;
if (location >= values.size()){
location = 0;
}
}
const std::string Menu::ValueHolder::getValues() {
std::string temp;
for (std::vector<std::string>::iterator i = values.begin(); i != values.end(); ++i){
temp += *i + "; ";
}
return temp;
}
static bool parseDisplayList(const Token * token, ContextBox & menu){
if (*token == "display-list"){
TokenView view = token->view();
while (view.hasMore()){
const Token * tok;
view >> tok;
std::string type;
bool wrap = true;
if (tok->match("type", type)){
if (type == "normal"){
menu.setListType(ContextBox::Normal);
} else if (type == "scroll"){
menu.setListType(ContextBox::Scroll);
}
} else if (tok->match("wrap", wrap)){
menu.setListWrap(wrap);
} else if ( *tok == "items"){
Gui::ListValues values;
values.getValues(tok);
menu.setListValues(values);
}
}
return true;
}
return false;
}
Menu::Renderer::Renderer(){
}
Menu::Renderer::~Renderer(){
// Kill info boxes
for (std::vector< ::Menu::InfoBox *>::iterator i = info.begin(); i != info.end();++i){
if (*i){
delete *i;
}
}
}
/*
void Menu::Renderer::setFont(const Util::ReferenceCount<FontInfo> & font){
}
*/
void Menu::Renderer::addInfo(const std::string & text, const Gui::Widget & defaults, Context & context, const Font & font){
if (!info.empty()){
info.back()->close();
}
if (text.empty()){
return;
}
::Menu::InfoBox * temp = new ::Menu::InfoBox();
// temp->setFont(context.getFont());
temp->setText(text);
temp->initialize(font);
const int width = temp->location.getWidth();
const int height = temp->location.getHeight();
temp->location.setPosition(Gui::AbsolutePoint(context.getInfoLocation().getX() - width/2, context.getInfoLocation().getY() - height/2));
// have to pass the dimensions back in to correct proper placement
temp->location.setPosition2(Gui::AbsolutePoint(temp->location.getX() + width,temp->location.getY() + height));
temp->transforms.setRadius(defaults.transforms.getRadius());
temp->colors = defaults.colors;
temp->open();
info.push_back(temp);
}
void Menu::Renderer::actInfo(const Font & font){
for (std::vector< ::Menu::InfoBox *>::iterator i = info.begin(); i != info.end();){
::Menu::InfoBox *box = *i;
box->act(font);
if (!box->isActive()){
delete box;
i = info.erase(i);
} else {
i++;
}
}
}
void Menu::Renderer::renderInfo(const Graphics::Bitmap & work, const Font & font){
for (std::vector< ::Menu::InfoBox *>::iterator i = info.begin(); i != info.end(); ++i){
::Menu::InfoBox *box = *i;
box->render(work, font);
}
}
Menu::DefaultRenderer::DefaultRenderer():
hasOverride(false),
overrideIndex(0){
// Default the menu to a certain size and details
menu.transforms.setRadius(15);
menu.location.set(-.6, -.3, .6, .8);
menu.colors.body = Graphics::makeColor(0,0,0);
menu.colors.bodyAlpha = 128;
menu.colors.border = Graphics::makeColor(200,200,200);
menu.colors.borderAlpha = 255;
}
vector<Util::ReferenceCount<MenuOption> > Menu::DefaultRenderer::getOptions() const {
return options;
}
void Menu::DefaultRenderer::setPosition(const Gui::Coordinate & coordinate){
menu.location = coordinate;
}
void Menu::DefaultRenderer::invokeOverride(const Context & context){
if (hasOverride){
options[overrideIndex]->run(context);
throw Exception::Return(__FILE__, __LINE__);
}
}
Menu::DefaultRenderer::~DefaultRenderer(){
}
Menu::Renderer::Type Menu::DefaultRenderer::getType() const {
return Default;
}
bool Menu::DefaultRenderer::readToken(const Token * token, const OptionFactory & factory){
if( *token == "option" ) {
try{
MenuOption * temp = factory.getOption(menu, token);
if (temp){
// Check for info/name
{
TokenView view = token->view();
while (view.hasMore()){
const Token * tok;
view >> tok;
try{
if (*tok == "info"){
temp->addInfo(tok);
} else if (*tok == "name"){
temp->addName(tok);
}
} catch (const TokenException & ex){
// Output something
}
}
}
options.push_back(Util::ReferenceCount<MenuOption>(temp));
if (!hasOverride){
const Token * tok;
token->view() >> tok;
if (tok->findToken("_/override") != NULL){
overrideIndex = options.size()-1;
hasOverride = true;
}
}
}
} catch (const LoadException & le){
Global::debug(0) << "Could not read option: " << le.getTrace() << endl;
token->print(" ");
}
} else if ( *token == "position" ) {
// This handles the placement of the menu list and surrounding box
menu.setCoordinates(token);
} else if ( *token == "relative-position"){
menu.setCoordinates(token);
} else if ( *token == "coordinate"){
menu.setCoordinates(token);
} else if ( *token == "position-body" ) {
// This handles the body color of the menu box
menu.setColors(token);
} else if ( *token == "position-border" ) {
// This handles the border color of the menu box
menu.setColors(token);
} else if ( *token == "transforms" ) {
// This handles the border color of the menu box
menu.setTransforms(token);
} else if ( *token == "fade-speed" ) {
// Menu fade in speed
int speed;
token->view() >> speed;
menu.setFadeSpeed(speed);
} else if ( parseDisplayList(token, menu) ){
} else {
return false;
}
return true;
}
void Menu::DefaultRenderer::initialize(Context & context){
menu.setList(toContextList(menu, options));
menu.open();
// const Font & font = Configuration::getMenuFont()->get(context.getFont()->get());
const Font & font = currentFont();
// Menu info
if (!context.getMenuInfoText().empty()){
menuInfo.setText(context.getMenuInfoText());
menuInfo.initialize(font);
const int width = menuInfo.location.getWidth();
const int height = menuInfo.location.getHeight();
menuInfo.location.setPosition(Gui::AbsolutePoint(context.getMenuInfoLocation().getX() - width/2, context.getMenuInfoLocation().getY() - height/2));
// have to pass the dimensions back in to correct proper placement
menuInfo.location.setPosition2(Gui::AbsolutePoint(menuInfo.location.getX() + width, menuInfo.location.getY() + height));
menuInfo.transforms.setRadius(menu.transforms.getRadius());
menuInfo.colors = menu.colors;
}
menuInfo.open();
// Add first info option
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
void Menu::DefaultRenderer::finish(){
menu.close();
menuInfo.close();
}
bool Menu::DefaultRenderer::active(){
return menu.isActive();
}
void Menu::DefaultRenderer::act(const Context & context){
// const Font & font = Configuration::getMenuFont()->get(context.getFont()->get());
const Font & font = currentFont();
// FIXME find a better way to get options to update this is a waste
for (std::vector<Util::ReferenceCount<MenuOption> >::iterator i = options.begin(); i != options.end(); ++i){
Util::ReferenceCount<MenuOption> & option = *i;
option->logic();
}
menu.act(font);
menuInfo.act(font);
actInfo(font);
}
void Menu::DefaultRenderer::render(const Graphics::Bitmap & bmp, const Font & font){
menu.render(bmp, font);
menuInfo.render(bmp, font);
renderInfo(bmp, font);
}
void Menu::DefaultRenderer::addOption(MenuOption * opt){
this->options.push_back(Util::ReferenceCount<MenuOption>(opt));
}
void Menu::DefaultRenderer::doAction(const Actions & action, Context & context){
const Font & font = currentFont();
switch(action){
case Up:
if (menu.previous(font)){
context.playSound(Up);
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
break;
case Down:
if (menu.next(font)){
context.playSound(Down);
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
break;
case Left:
if (options[menu.getCurrentIndex()]->leftKey()){
// setFont(context.getFont());
context.playSound(Left);
}
break;
case Right:
if (options[menu.getCurrentIndex()]->rightKey()){
// setFont(context.getFont());
context.playSound(Right);
}
break;
case Select: {
if (options[menu.getCurrentIndex()]->isRunnable()){
try{
context.playSound(Select);
options[menu.getCurrentIndex()]->run(context);
} catch (const Reload & reload){
menu.open();
menuInfo.open();
}
// setFont(context.getFont());
context.playMusic();
/* font might have been recreated */
const Font & font = currentFont();
addInfo(options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
menuInfo.initialize(font);
}
break;
}
case Cancel:
context.playSound(Cancel);
throw Exception::Return(__FILE__, __LINE__);
break;
default:
break;
}
}
Menu::TabInfo::TabInfo(){
}
Menu::TabInfo::~TabInfo(){
}
void Menu::TabInfo::act(){
// Miguel: FIXME find a better way to get options to update this is a waste
// Jon: Whats wrong with it?
for (std::vector<Util::ReferenceCount<MenuOption> >::iterator i = options.begin(); i != options.end(); ++i){
Util::ReferenceCount<MenuOption> & option = *i;
option->logic();
}
}
Menu::TabRenderer::TabRenderer():
hasOverride(false),
overrideIndex(0){
// Default the menu to a certain size and details
//menu.transforms.setRadius(15);
menu.location.set(-.6, -.3, .6, .8);
menu.colors.body = Graphics::makeColor(0,0,0);
menu.colors.bodyAlpha = 128;
menu.colors.border = Graphics::makeColor(200,200,200);
menu.colors.borderAlpha = 255;
}
Menu::Renderer::Type Menu::TabRenderer::getType() const {
return Tabbed;
}
void Menu::TabRenderer::setPosition(const Gui::Coordinate & coordinate){
menu.location = coordinate;
}
vector<Util::ReferenceCount<MenuOption> > Menu::TabRenderer::getOptions() const {
vector<Util::ReferenceCount<MenuOption> > options;
for (vector<TabInfo *>::const_iterator it = tabs.begin(); it != tabs.end(); ++it){
options.insert(options.end(), (*it)->options.begin(), (*it)->options.end());
}
return options;
}
/* FIXME need to implement override for tabs */
void Menu::TabRenderer::invokeOverride(const Context & context){
if (hasOverride){
//options[overrideIndex]->run(context);
throw Exception::Return(__FILE__, __LINE__);
}
}
Menu::TabRenderer::~TabRenderer(){
// Kill tabs
for (std::vector<TabInfo *>::iterator i = tabs.begin(); i != tabs.end(); ++i){
if (*i){
delete *i;
}
}
}
bool Menu::TabRenderer::readToken(const Token * token, const OptionFactory & factory){
if (*token == "menu"){
TabInfo * tabInfo = new TabInfo();
Gui::Tab * tab = new Gui::Tab();
TokenView view = token->view();
while (view.hasMore()){
const Token * tok;
view >> tok;
try{
if (*tok == "name"){
tok->view() >> tabInfo->name;
tok->view() >> tab->name;
} else if (*tok == "info"){
tok->view() >> tabInfo->info;
} else if (*tok == "menuinfo"){
tok->view() >> tabInfo->menuInfo;
} else if (parseDisplayList(tok, tab->getContext())){
} else if (*tok == "option"){
try {
MenuOption *temp = factory.getOption(tab->getContext(), tok);
if (temp){
// Check for info/name
{
TokenView info = tok->view();
while (info.hasMore()){
const Token * check;
info >> check;
try{
if (*check == "info"){
temp->addInfo(check);
} else if (*check == "name"){
temp->addName(check);
}
} catch (const TokenException & ex){
// Output something
}
}
}
Util::ReferenceCount<MenuOption> ref(temp);
tabInfo->options.push_back(ref);
- tab->addOption(ref.convert<Gui::ContextItem>());
+ tab->addOption(ref);
// tab->addOption(ref->getAsScrollItem<ContextItem>(tab->getContext()));
}
} catch (const LoadException & le){
tok->print(" ");
}
}
} catch (const TokenException & ex){
// Output something
}
}
menu.addTab(tab);
tabs.push_back(tabInfo);
} else if ( *token == "position" ) {
// This handles the placement of the menu list and surrounding box
menu.setCoordinates(token);
} else if ( *token == "relative-position"){
menu.setCoordinates(token);
} else if ( *token == "coordinate"){
menu.setCoordinates(token);
} else if ( *token == "position-body" ) {
// This handles the body color of the menu box
menu.setColors(token);
} else if ( *token == "position-border" ) {
// This handles the border color of the menu box
menu.setColors(token);
} else if ( *token == "transforms" ) {
// This handles the border color of the menu box
menu.setTransforms(token);
} else if ( *token == "tab-body" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.tabColors.bodyAlpha;
menu.tabColors.body = Graphics::makeColor(r,g,b);
} else if ( *token == "tab-border" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.tabColors.borderAlpha;
menu.tabColors.border = Graphics::makeColor(r,g,b);
} else if ( *token == "selectedtab-body" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.selectedTabColors.bodyAlpha;
menu.selectedTabColors.body = Graphics::makeColor(r,g,b);
} else if ( *token == "selectedtab-border" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.selectedTabColors.borderAlpha;
menu.selectedTabColors.border = Graphics::makeColor(r,g,b);
} else if ( *token == "runningtab-body" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.runningTabColors.bodyAlpha;
menu.runningTabColors.body = Graphics::makeColor(r,g,b);
} else if ( *token == "runningtab-border" ) {
int r,g,b;
token->view() >> r >> g >> b >> menu.runningTabColors.borderAlpha;
menu.runningTabColors.border = Graphics::makeColor(r,g,b);
} else if ( *token == "font-color" ) {
int r,g,b;
token->view() >> r >> g >> b;
menu.setTabFontColor(Graphics::makeColor(r,g,b));
} else if ( *token == "selectedfont-color" ) {
int r,g,b;
token->view() >> r >> g >> b;
menu.setSelectedTabFontColor(Graphics::makeColor(r,g,b));
} else if ( *token == "runningfont-color" ) {
} else if ( *token == "fade-speed" ) {
// Menu fade in speed
int speed;
token->view() >> speed;
//menu.setFadeSpeed(speed);
} else {
return false;
}
return true;
}
void Menu::TabRenderer::initialize(Context & context){
const Font & font = currentFont();
// Menu info
if (!context.getMenuInfoText().empty()){
menuInfo.setText(context.getMenuInfoText());
menuInfo.initialize(font);
const int width = menuInfo.location.getWidth();
const int height = menuInfo.location.getHeight();
menuInfo.location.setPosition(Gui::AbsolutePoint(context.getMenuInfoLocation().getX() - width/2, context.getMenuInfoLocation().getY() - height/2));
// have to pass the dimensions back in to correct proper placement
menuInfo.location.setPosition2(Gui::AbsolutePoint(menuInfo.location.getX() + width,menuInfo.location.getY() + height));
menuInfo.transforms.setRadius(menu.transforms.getRadius());
menuInfo.colors = menu.colors;
}
menuInfo.open();
if (tabs.size() > 0 && tabs[menu.getCurrentTab()]->options.size() > menu.getCurrentIndex()){
// Add first info option
addInfo(tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
}
void Menu::TabRenderer::finish(){
//menu.close();
menuInfo.close();
}
bool Menu::TabRenderer::active(){
return true;//menu.isActive();
}
void Menu::TabRenderer::act(const Context & context){
const Font & font = currentFont();
// FIXME find a better way to get options to update this is a waste
for (std::vector<TabInfo *>::iterator i = tabs.begin(); i != tabs.end(); ++i){
TabInfo * tab = *i;
tab->act();
}
menu.act(font);
menuInfo.act(font);
actInfo(font);
}
void Menu::TabRenderer::render(const Graphics::Bitmap & bmp, const Font & font){
menu.render(bmp, font);
menuInfo.render(bmp, font);
renderInfo(bmp, font);
}
void Menu::TabRenderer::addOption(MenuOption * opt){
//this->options.push_back(opt);
}
void Menu::TabRenderer::doAction(const Actions & action, Context & context){
const Font & font = currentFont();
bool updateInfo = false;
switch (action){
case Up:
menu.up(font);
context.playSound(Up);
updateInfo = true;
break;
case Down:
menu.down(font);
context.playSound(Down);
updateInfo = true;
break;
case Left:
menu.left(font);
context.playSound(Up);
updateInfo = true;
break;
case Right:
menu.right(font);
context.playSound(Down);
updateInfo = true;
break;
case Select:
try{
if (!menu.isInTab()){
menu.toggleTabSelect();
} else {
if (menu.getCurrentTab() < tabs.size() &&
menu.getCurrentIndex() < tabs[menu.getCurrentTab()]->options.size() &&
tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->isRunnable()){
tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->run(context);
}
// tabs[menu.getCurrentTab()]->run(menu.getCurrentIndex(), context);
}
context.playSound(Select);
} catch (const Exception::Return & ex){
menuInfo.open();
}
context.playMusic();
updateInfo = true;
break;
case Cancel:
context.playSound(Cancel);
if (menu.isInTab()){
menu.toggleTabSelect();
} else {
throw Exception::Return(__FILE__, __LINE__);
}
break;
default:
break;
}
if (updateInfo){
if (tabs.size() > menu.getCurrentTab() &&
tabs[menu.getCurrentTab()]->options.size() > menu.getCurrentIndex()){
addInfo(tabs[menu.getCurrentTab()]->options[menu.getCurrentIndex()]->getInfoText(), menu, context, font);
}
}
}
Menu::Context::Context():
cleanup(true),
state(NotStarted),
fades(0),
font(NULL),
infoLocation(0, -.5),
menuInfoLocation(0,.95){
}
Menu::Context::Context(const Context & parent, const Context & child):
cleanup(false),
state(NotStarted),
fades(NULL),
font(NULL),
infoLocation(0,-.5),
menuInfoLocation(0,.95),
/* include child's languages?? */
languages(parent.getLanguages()){
// Update with parents info
fades = parent.fades;
background = parent.background;
sounds = parent.sounds;
music = parent.music;
// font = parent.font;
infoLocation = parent.infoLocation;
menuInfoLocation = parent.menuInfoLocation;
// Then overwrite with childs
if (child.fades != NULL){
fades = child.fades;
}
if (!child.background.empty()){
background = child.background;
}
if (!child.sounds.empty()){
sounds = child.sounds;
}
if (Storage::instance().exists(child.music)){
music = child.music;
}
if (child.hasFont()){
font = child.getFontInfo();
} else if (parent.hasFont()){
font = parent.getFontInfo();
}
/* what are these magic numbers -.5 and .95? */
if (child.infoLocation.getRelativeX() != 0 || child.infoLocation.getRelativeY() != -.5){
infoLocation = child.infoLocation;
}
if (child.menuInfoLocation.getRelativeX() != 0 || child.menuInfoLocation.getRelativeY() != .95){
menuInfoLocation = child.menuInfoLocation;
}
if (!child.menuInfo.empty()){
menuInfo = child.menuInfo;
}
}
Menu::Context::~Context(){
// Only delete if required
if (cleanup){
if (fades != NULL){
delete fades;
}
}
}
std::vector<std::string> Menu::Context::getLanguages() const {
return languages;
}
void Menu::Context::setLanguages(const std::vector<std::string> & languages){
this->languages = languages;
}
bool Menu::Context::hasFont() const {
return font != NULL;
}
void Menu::Context::parseToken(const Token * token){
if ( *token != "context" ){
throw LoadException(__FILE__, __LINE__, "Not a menu context");
} else if (!token->hasTokens()){
return;
}
// Token
const Token * tok;
token->view() >> tok;
TokenView view = tok->view();
while (view.hasMore()){
const Token * context;
view >> context;
if (*context == "fade"){
// Fade info
if (!fades){
fades = new Gui::FadeTool();
}
// Set fader default to white
fades->setFadeInColor(Graphics::makeColor(255,255,255));
fades->setFadeOutColor(Graphics::makeColor(255,255,255));
fades->setFadeInTime(25);
fades->setFadeOutTime(12);
// Load defaults
fades->parseDefaults(context);
} else if (*context == "animation" || *context == "background"){
// Backgrounds
addBackground(context);
} else if (*context == ""){
}
}
}
void Menu::Context::addBackground(const Token * token){
// Backgrounds
/*if (background == NULL){
background = new Gui::AnimationManager();
}*/
background.add(Util::ReferenceCount<Gui::Animation>(new Gui::Animation(token)));
}
void Menu::Context::addBackground(const Graphics::Bitmap & image){
/*if (background == NULL){
background = new Gui::AnimationManager();
}*/
background.add(Util::ReferenceCount<Gui::Animation>(new Gui::Animation(Util::ReferenceCount<Graphics::Bitmap>(new Graphics::Bitmap(image)))));
}
void Menu::Context::addBackground(const std::string & image){
// Backgrounds
/*if (background == NULL){
background = new Gui::AnimationManager();
}*/
background.add(Util::ReferenceCount<Gui::Animation>(new Gui::Animation(image)));
}
void Menu::Context::initialize(){
if (fades){
// state
state = Initializing;
// set fader state
fades->setState(FadeTool::FadeIn);
} else {
// Running
state = Running;
}
}
void Menu::Context::finish(){
if (fades){
// state
state = Finishing;
// set fader state
fades->setState(FadeTool::FadeOut);
} else {
// Completed
state = Completed;
}
}
void Menu::Context::playSound(const Actions & sound) const {
map<Actions, Filesystem::RelativePath>::const_iterator find = sounds.find(sound);
if (find != sounds.end() && Storage::instance().exists(find->second)){
tryPlaySound(find->second);
}
}
void Menu::Context::addSound(const Actions & sound, const Filesystem::RelativePath & path){
sounds[sound] = path;
}
void Menu::Context::playMusic(){
if (Storage::instance().exists(music)){
if (Music::loadSong(Storage::instance().find(music).path())){
Music::pause();
Music::play();
}
}
}
void Menu::Context::act(){
// fader
if (fades){
fades->act();
if (state == Initializing){
if(fades->getState() == Gui::FadeTool::NoFade){
state = Running;
}
} else if (state == Finishing){
if(fades->getState() == Gui::FadeTool::EndFade){
state = Completed;
}
}
}
// Backgrounds
background.act();
}
void Menu::Context::renderBackground(const Graphics::Bitmap & bmp) const {
if (!background.empty()){
// background
background.render(Gui::Animation::BackgroundBottom, bmp);
background.render(Gui::Animation::BackgroundMiddle, bmp);
background.render(Gui::Animation::BackgroundTop, bmp);
} else {
bmp.fill(Graphics::makeColor(0,0,0));
}
}
void Menu::Context::renderForeground(const Graphics::Bitmap & bmp) const {
if (!background.empty()){
// foreground
background.render(Gui::Animation::ForegroundBottom, bmp);
background.render(Gui::Animation::ForegroundMiddle, bmp);
background.render(Gui::Animation::ForegroundTop, bmp);
}
}
void Menu::Context::render(const Util::ReferenceCount<Renderer> & renderer, const Graphics::Bitmap & bmp) const {
renderBackground(bmp);
// Menu
if (renderer != NULL){
renderer->render(bmp, currentFont());
}
renderForeground(bmp);
// Fades
if (fades){
fades->draw(bmp);
}
}
void Menu::Context::setFadeTool(Gui::FadeTool *fade){
fades = fade;
}
/*
void Menu::Context::setBackground(Background *bg){
background = bg;
}
*/
/* New Menu */
// Utilizes default renderer
Menu::Menu::Menu(const Renderer::Type & type):
type(type){
setRenderer(type);
}
Menu::Menu::Menu(const Util::ReferenceCount<Renderer> & renderer):
renderer(renderer),
type(renderer->getType()){
}
Menu::Menu::Menu(const Filesystem::AbsolutePath & filename, const Renderer::Type & type):
type(type){
// Load up tokenizer
try{
Global::debug(1,"menu") << "Loading menu " << filename.path() << endl;
TokenReader tr;
Token * token = tr.readTokenFromFile(*Storage::instance().open(filename));
OptionFactory defaultFactory;
load(token, defaultFactory);
} catch (const TokenException & e){
throw LoadException(__FILE__, __LINE__, e, "Error loading menu");
}
}
Menu::Menu::Menu(const Filesystem::AbsolutePath & filename, const OptionFactory & factory, const Renderer::Type & type):
renderer(0),
type(type){
// Load up tokenizer
try{
Global::debug(1,"menu") << "Loading menu " << filename.path() << endl;
TokenReader tr;
Token * token = tr.readTokenFromFile(*Storage::instance().open(filename));
load(token, factory);
} catch (const TokenException & e){
throw LoadException(__FILE__, __LINE__, e, "Error loading menu");
}
}
Menu::Menu::Menu(const Token * token, const Renderer::Type & type):
renderer(0),
type(type){
OptionFactory defaultFactory;
load(token, defaultFactory);
}
Menu::Menu::Menu(const Token * token, const OptionFactory & factory, const Renderer::Type & type):
renderer(0),
type(type){
load(token, factory);
}
Menu::Menu::~Menu(){
// Kill values
for (std::map<string,ValueHolder *>::iterator i = data.begin(); i != data.end(); ++i){
if (i->second){
delete i->second;
}
}
}
void Menu::Menu::setPosition(const Gui::Coordinate & coordinate){
if (renderer != NULL){
renderer->setPosition(coordinate);
}
}
void Menu::Menu::setFont(const Util::ReferenceCount<FontInfo> & font){
context.setFont(font);
/*
if (renderer){
renderer->setFont(font);
}
*/
}
void Menu::Menu::load(const Token * token, const OptionFactory & factory){
// version info;
int major=0, minor=0, micro=0;
if (!token->hasTokens()){
throw LoadException(__FILE__, __LINE__, "Empty Menu");
} else {
const Token *ourToken = token->findToken("_/type");
if (ourToken != NULL){
try {
std::string menuType;
ourToken->view() >> menuType;
if (menuType == "default"){
type = Renderer::Default;
} else if (menuType == "tabbed"){
type = Renderer::Tabbed;
}
} catch (const TokenException & ex){
}
}
ourToken = token->findToken("_/version");
if (ourToken != NULL){
try {
ourToken->view() >> major >> minor >> micro;
} catch (const TokenException & ex){
}
} else {
Global::debug(0, "menu") << "No version indicated, assuming 3.3.1 or below." << endl;
major = 3;
minor = 3;
micro = 1;
}
}
setRenderer(type);
if (Version::getVersion(major, minor, micro) != Version::getVersion()){
// Do compatible translations if necessary
handleCompatibility(token, Version::getVersion(major, minor, micro), factory);
} else {
handleCurrentVersion(token);
}
}
typedef Menu::Menu MenuClass;
class LanguageMenu: public Menu::Menu {
public:
class LanguageOption: public MenuOption {
public:
LanguageOption(const Gui::ContextBox & box, const string & language):
MenuOption(box, NULL){
setText(language);
setInfoText(language);
}
virtual void logic(){
}
virtual void run(const ::Menu::Context & context){
Configuration::setLanguage(getText());
Configuration::saveConfiguration();
throw ::Menu::MenuException(__FILE__, __LINE__);
}
};
static vector<string> findLanguages(const MenuClass & original){
return original.getLanguages();
}
static vector<string> putEnglishFirst(vector<string> languages){
vector<string> out;
bool haveEnglish = false;
for (vector<string>::iterator it = languages.begin(); it != languages.end(); it++){
const string & name = *it;
if (name == "English"){
out.insert(out.begin(), name);
haveEnglish = true;
} else {
out.push_back(name);
}
}
/* We should always have at least english available */
if (!haveEnglish){
out.insert(out.begin(), "English");
}
return out;
}
LanguageMenu(const MenuClass & original){
- Util::ReferenceCount< ::Menu::DefaultRenderer > renderer = getRenderer().convert< ::Menu::DefaultRenderer>();
+ Util::ReferenceCount< ::Menu::DefaultRenderer > renderer = getRenderer();
vector<string> languages = putEnglishFirst(findLanguages(original));
for (vector<string>::iterator it = languages.begin(); it != languages.end(); it++){
addOption(new LanguageOption(renderer->getBox(), *it));
}
}
};
void Menu::Menu::setupDefaultLanguage(const Context & context, const MenuClass & parent){
LanguageMenu menu(parent);
menu.setFont(Util::ReferenceCount<FontInfo>(new RelativeFontInfo(Font::getDefaultFontPath(), 24, 24)));
Configuration::setLanguage("English");
try{
menu.run(context);
} catch (const ::Menu::MenuException & ignore){
}
}
void Menu::Menu::openOptions(){
vector<Util::ReferenceCount<MenuOption> > options = getRenderer()->getOptions();
for (vector<Util::ReferenceCount<MenuOption> >::iterator it = options.begin(); it != options.end(); it++){
Util::ReferenceCount<MenuOption> & option = *it;
option->open();
}
}
void Menu::Menu::closeOptions(){
vector<Util::ReferenceCount<MenuOption> > options = getRenderer()->getOptions();
for (vector<Util::ReferenceCount<MenuOption> >::iterator it = options.begin(); it != options.end(); it++){
Util::ReferenceCount<MenuOption> & option = *it;
option->close();
}
}
vector<string> Menu::Menu::getLanguages() const {
return languages;
}
void Menu::Menu::run(const Context & parentContext){
/* TODO: replace with Parameter */
Keyboard::pushRepeatState(true);
try{
// Setup context from parent and this menu and initialize
Context localContext(parentContext, context);
Util::Parameter<Util::ReferenceCount<FontInfo> > currentFont(menuFontParameter);
if (context.hasFont()){
currentFont.push(context.getFontInfo());
}
localContext.initialize();
/* Not sure if this is the right place to set the languages.
* For now the semantics is that if a sub-menu specifies a set
* of languages then we will use those, otherwise the
* languages will come from the parentContext.
*
* getLanguages() is supposed to hold at least one language
* which might be English, the default.
*
* This logic is sort of lame.. fix it.
*/
if (getLanguages().size() > 1 || (getLanguages().size() == 1 && getLanguages()[0] != "English")){
localContext.setLanguages(getLanguages());
}
// Setup menu fonts etc
if (renderer != NULL){
renderer->initialize(localContext);
// Invoke Override if available
renderer->invokeOverride(localContext);
}
//Play music
localContext.playMusic();
if (Configuration::getLanguage() == ""){
setupDefaultLanguage(localContext, *this);
}
/* do any lazy loading options want to do */
openOptions();
/* vi keys -- make these optional? */
input.set(Keyboard::Key_J, 0, true, Down);
input.set(Keyboard::Key_K, 0, true, Up);
input.set(Keyboard::Key_H, 0, true, Left);
input.set(Keyboard::Key_L, 0, true, Right);
/* regular keys */
input.set(Configuration::getUp(0), 0, true, Up);
input.set(Configuration::getDown(0), 0, true, Down);
input.set(Configuration::getLeft(0), 0, true, Left);
input.set(Configuration::getRight(0), 0, true, Right);
input.set(Configuration::getAttack1(0), 0, true, Select);
/* FIXME: use configuration keys */
input.set(Keyboard::Key_ENTER, 0, true, Select);
input.set(Keyboard::Key_ESC, 0, true, Cancel);
/* joystick */
input.set(Joystick::Up, 0, true, Up);
input.set(Joystick::Down, 0, true, Down);
input.set(Joystick::Left, 0, true, Left);
input.set(Joystick::Right, 0, true, Right);
/*! FIXME this should be changed to Select/Cancel buttons, all other buttons should be Select */
input.set(Joystick::Button1, 0, true, Select);
input.set(Joystick::Button2, 0, true, Select);
input.set(Joystick::Button3, 0, true, Select);
input.set(Joystick::Button4, 0, true, Select);
input.set(Joystick::Start, 0, true, Select);
input.set(Joystick::Quit, 0, true, Cancel);
class Logic: public Util::Logic {
public:
Logic(Menu & menu, Context & localContext, const Util::ReferenceCount<Renderer> & renderer):
menu(menu),
localContext(localContext),
renderer(renderer){
}
Menu & menu;
Context & localContext;
Util::ReferenceCount<Renderer> renderer;
void run(){
try {
menu.act(localContext);
} catch (const Exception::Return & ex){
// signaled to quit current menu, closing this one out
localContext.finish();
if (renderer != NULL){
renderer->finish();
}
}
}
double ticks(double system){
return system * Global::ticksPerSecond(90);
}
bool done(){
return localContext.getState() == Context::Completed ||
!(renderer != NULL && renderer->active());
}
};
class Draw: public Util::Draw {
public:
Draw(Menu & menu, Context & localContext):
menu(menu),
localContext(localContext){
}
Menu & menu;
Context & localContext;
void draw(const Graphics::Bitmap & buffer){
Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
Util::Parameter<Util::ReferenceCount<FontInfo> > currentFont(menuFontParameter);
if (Configuration::hasMenuFont()){
currentFont.push(Configuration::getMenuFont());
}
work.start();
menu.render(localContext, work);
work.finish();
// buffer.BlitToScreen();
}
};
Logic logic(*this, localContext, renderer);
Draw draw(*this, localContext);
Util::standardLoop(logic, draw);
closeOptions();
// FIXME Menu is finished, lets return. Is this even required anymore?
throw Exception::Return(__FILE__, __LINE__);
} catch (...){
Keyboard::popRepeatState();
throw;
}
}
void Menu::Menu::act(Context & ourContext){
InputSource source(true);
// Keys
vector<InputMap<Actions>::InputEvent> events = InputManager::getEvents(input, source);
for (vector<InputMap<Actions>::InputEvent>::iterator it = events.begin(); it != events.end(); it++){
InputMap<Actions>::InputEvent event = *it;
if (!event.enabled){
continue;
}
if (event.out == Cancel){
if (renderer != NULL){
InputManager::waitForRelease(input, source, Cancel);
renderer->doAction(Cancel, ourContext);
} else {
ourContext.playSound(Cancel);
InputManager::waitForRelease(input, source, Cancel);
throw Exception::Return(__FILE__, __LINE__);
}
}
if (renderer != NULL){
switch (event.out){
case Up: renderer->doAction(Up, ourContext); break;
case Down: renderer->doAction(Down, ourContext); break;
case Left: renderer->doAction(Left, ourContext); break;
case Right: renderer->doAction(Right, ourContext); break;
case Select: renderer->doAction(Select, ourContext); break;
default: break;
}
}
}
if (renderer != NULL){
Util::Parameter<Util::ReferenceCount<FontInfo> > currentFont(menuFontParameter);
if (Configuration::hasMenuFont()){
currentFont.push(Configuration::getMenuFont());
}
renderer->act(ourContext);
}
// Act context
ourContext.act();
}
void Menu::Menu::render(const Context & ourContext, const Graphics::Bitmap & bmp) const {
// Render context
ourContext.render(renderer, bmp);
}
std::string Menu::Menu::getName(){
std::string name;
try {
if (data["name"]){
*data["name"] >> name;
}
} catch (const MenuException & ex){
}
return name;
}
std::string Menu::Menu::getInfo(){
std::string name;
try {
if (data["info"]){
*data["info"] >> name;
}
} catch (const MenuException & ex){
}
return name;
}
void Menu::Menu::addData(ValueHolder * item){
std::pair<std::map<std::string,ValueHolder *>::iterator,bool> check;
check = data.insert( std::pair<std::string,ValueHolder *>(item->getName(),item) );
if (check.second == false){
Global::debug(0,"menu") << "Value \"" << check.first->second->getName() << "\" already exists - (" << check.first->second->getValues() << ")." << endl;
Global::debug(0,"menu") << "Replacing with value \"" << item->getName() << "\" - (" << item->getValues() << ")." << endl;
data[item->getName()] = item;
}
}
void Menu::Menu::handleCurrentVersion(const Token * token){
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
// Newer items
if (*tok == "val" || *tok == "value"){
const Token * val;
tok->view() >> val;
ValueHolder * value = new ValueHolder(val->getName());
TokenView valueView = val->view();
try {
while (true){
*value << valueView;
}
} catch (const TokenException & ex){
}
addData(value);
} else if (*tok == "context"){
context.parseToken(tok);
} else {
Global::debug(3,"menu") <<"Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch (const LoadException & ex){
throw ex;
} catch (const Filesystem::NotFound & ex){
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
}
}
}
void Menu::Menu::handleCompatibility(const Token * token, int version, const OptionFactory & factory){
Global::debug(1,"menu") << "Trying version: " << version << endl;
if (version <= Version::getVersion(3, 3, 1)){
const Token * languages = token->findToken("_/languages");
if (languages != NULL){
try{
TokenView view = languages->view();
while (true){
string language;
view >> language;
this->languages.push_back(language);
}
} catch (const TokenException & fail){
}
}
if (this->languages.size() == 0){
this->languages.push_back("English");
}
TokenView view = token->view();
while (view.hasMore()){
try {
const Token * tok;
view >> tok;
if ( *tok == "name" ){
ValueHolder * value = new ValueHolder("name");
TokenView nameView = tok->view();
*value << nameView;
addData(value);
} else if ( *tok == "music" ) {
ValueHolder * value = new ValueHolder("music");
TokenView musicView = tok->view();
*value << musicView;
addData(value);
try {
std::string music;
*value >> music;
context.setMusic(Filesystem::RelativePath(music));
} catch (const MenuException & ex){
}
} else if( *tok == "select-sound" ) {
ValueHolder * value = new ValueHolder("select-sound");
TokenView soundView = tok->view();
*value << soundView;
addData(value);
try{
std::string sound;
*value >> sound;
context.addSound(Up, Filesystem::RelativePath(sound));
context.addSound(Down, Filesystem::RelativePath(sound));
} catch (const MenuException & ex){
}
} else if (*tok == "back-sound"){
ValueHolder * value = new ValueHolder("back-sound");
TokenView soundView = tok->view();
*value << soundView;
addData(value);
try{
std::string sound;
*value >> sound;
context.addSound(Back,Filesystem::RelativePath(sound));
context.addSound(Cancel,Filesystem::RelativePath(sound));
} catch (const MenuException & ex){
}
} else if (*tok == "ok-sound"){
ValueHolder * value = new ValueHolder("ok-sound");
TokenView okView = tok->view();
*value << okView;
addData(value);
try{
std::string sound;
*value >> sound;
context.addSound(Select,Filesystem::RelativePath(sound));
} catch (const MenuException & ex){
}
} else if ( *tok == "background" ) {
std::string temp;
tok->view() >> temp;
context.addBackground(temp);
} else if (*tok == "anim"){
context.addBackground(tok);
} else if ( *tok == "clear-color" ) {
// Not necessary ignore
} else if (renderer != NULL && renderer->readToken(tok, factory)){
// Nothing checks compatible version of renderer
} else if ( *tok == "font" ) {
ValueHolder * value = new ValueHolder("font");
TokenView fontView = tok->view();
*value << fontView << fontView << fontView;
addData(value);
try {
std::string font;
int w = 24, h = 24;
*value >> font >> w >> h;
/*context.setFont(Filesystem::RelativePath(font));
context.setFontWidth(w);
context.setFontHeight(h);*/
context.setFont(Util::ReferenceCount<FontInfo>(new RelativeFontInfo(Filesystem::RelativePath(font), w, h)));
} catch (const MenuException & ex){
}
} else if ( *tok == "action"){
// Set speed
//ActionAct(tok);
} else if ( *tok == "info-position"){
ValueHolder * value = new ValueHolder("info-position");
TokenView infoView = tok->view();
*value << infoView << infoView;
addData(value);
try {
double x=0, y=-.5;
*value >> x >> y;
context.setInfoLocation(x,y);
} catch (const MenuException & ex){
}
} else if (*tok == "menuinfo"){
ValueHolder * value = new ValueHolder("menuinfo");
TokenView infoView = tok->view();
*value << infoView;
addData(value);
try {
std::string info;
*value >> info;
context.setMenuInfoText(info);
} catch (const MenuException & ex){
}
} else if (*tok == "menuinfo-position"){
ValueHolder * value = new ValueHolder("menuinfo-position");
TokenView infoView = tok->view();
*value << infoView << infoView;
addData(value);
try {
double x=0, y=.95;
*value >> x >> y;
context.setMenuInfoLocation(x,y);
} catch (const MenuException & ex){
}
} else {
Global::debug(3,"menu") <<"Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch (const LoadException & ex){
throw ex;
} catch (const Filesystem::NotFound & ex){
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
}
}
}
}
void Menu::Menu::setRenderer(const Renderer::Type & type){
renderer = rendererType(type);
}
void Menu::Menu::setRenderer(const Util::ReferenceCount<Renderer> & renderer){
this->renderer = renderer;
}
void Menu::Menu::addOption(MenuOption * opt){
if (renderer != NULL){
this->renderer->addOption(opt);
}
}
Util::ReferenceCount<Menu::Renderer> Menu::Menu::rendererType(const Renderer::Type & type){
switch (type){
case Renderer::Tabbed: {
return Util::ReferenceCount<Renderer>(new TabRenderer());
break;
}
case Renderer::Default:
default: {
return Util::ReferenceCount<Renderer>(new DefaultRenderer());
break;
}
}
return Util::ReferenceCount<Renderer>(NULL);
}
diff --git a/util/menu/options.cpp b/util/menu/options.cpp
index f31e097e..4a6e0a2e 100644
--- a/util/menu/options.cpp
+++ b/util/menu/options.cpp
@@ -1,3128 +1,3128 @@
#include "util/graphics/bitmap.h"
#include "options.h"
#include "util/token.h"
#include "util/input/input-source.h"
#include "util/parameter.h"
#include "util/tokenreader.h"
#include "menu.h"
#include "util/configuration.h"
#include "util/exceptions/load_exception.h"
#include "menu-exception.h"
#include "util/init.h"
#include "util/events.h"
#include "util/version.h"
#include "optionfactory.h"
#include "util/sound/music.h"
#include "util/input/keyboard.h"
#include "util/funcs.h"
#include "util/file-system.h"
#include "util/system.h"
#include "util/font_factory.h"
#include "util/exceptions/shutdown_exception.h"
#include "util/exceptions/exception.h"
#include "util/font.h"
#include "util/gui/box.h"
#include "util/thread.h"
#include "util/loading.h"
#include "util/input/input-map.h"
#include "util/input/input-manager.h"
#include <sstream>
#include <algorithm>
#include <time.h>
#include <math.h>
using namespace std;
using namespace Gui;
/* true if the arguments passed in match todays date.
* pass 0 for any argument that you don't care about (it will match any date)
*/
static bool todaysDate(int month, int day, int year){
time_t result = time(NULL);
struct tm * local = localtime(&result);
return (month == 0 || month == (local->tm_mon + 1)) &&
(day == 0 || day == local->tm_mday) &&
(year == 0 || year == local->tm_year + 1900);
}
static bool jonBirthday(){
return todaysDate(3, 25, 0);
}
static bool miguelBirthday(){
return todaysDate(8, 11, 0);
}
OptionCredits::Block::Block(const std::string & title):
title(title),
titleColorOverride(false),
titleColor(Graphics::makeColor(0,255,255)),
colorOverride(false),
color(Graphics::makeColor(255,255,255)),
spacing(0){
}
OptionCredits::Block::Block(const Token * token):
titleColorOverride(false),
titleColor(Graphics::makeColor(0,255,255)),
colorOverride(false),
color(Graphics::makeColor(255,255,255)),
topWidth(0),
topHeight(0),
bottomWidth(0),
bottomHeight(0),
spacing(0){
if ( *token != "block" ){
throw LoadException(__FILE__, __LINE__, "Not a credit block");
}
TokenView view = token->view();
while (view.hasMore()){
std::string match;
try{
const Token * tok;
view >> tok;
if ( *tok == "title" ) {
tok->view() >> title;
} else if (*tok == "credit"){
std::string credit;
tok->view() >> credit;
credits.push_back(credit);
} else if ( *tok == "titlecolor" ) {
try{
int r,b,g;
tok->view() >> r >> g >> b;
titleColor = Graphics::makeColor( r, g, b );
titleColorOverride = true;
} catch (const TokenException & ex){
}
} else if ( *tok == "color" ) {
try{
int r,b,g;
tok->view() >> r >> g >> b;
color = Graphics::makeColor( r, g, b );
colorOverride = true;
} catch (const TokenException & ex){
}
} else if ( *tok == "animation" ) {
TokenView animView = tok->view();
while (animView.hasMore()){
const Token * animTok;
animView >> animTok;
if (*animTok == "top"){
tok->match("_/width", topWidth);
tok->match("_/height", topHeight);
topAnimation = Util::ReferenceCount<Gui::Animation>(new Animation(tok));
} else if (*animTok == "bottom"){
tok->match("_/width", bottomWidth);
tok->match("_/height", bottomHeight);
bottomAnimation = Util::ReferenceCount<Gui::Animation>(new Animation(tok));
}
}
} else if (*tok == "spacing"){
tok->view() >> spacing;
} else {
Global::debug( 3 ) <<"Unhandled Credit Block attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Credit Block parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
}
OptionCredits::Block::Block(const OptionCredits::Block & copy):
title(copy.title),
credits(copy.credits),
titleColorOverride(copy.titleColorOverride),
titleColor(copy.titleColor),
colorOverride(copy.colorOverride),
color(copy.color),
topAnimation(copy.topAnimation),
topWidth(copy.topWidth),
topHeight(copy.topHeight),
bottomAnimation(copy.bottomAnimation),
bottomWidth(copy.bottomWidth),
bottomHeight(copy.bottomHeight),
spacing(copy.spacing){
}
OptionCredits::Block::~Block(){
}
const OptionCredits::Block & OptionCredits::Block::operator=(const OptionCredits::Block & copy){
title = copy.title;
credits = copy.credits;
titleColor = copy.titleColor;
titleColorOverride = copy.titleColorOverride;
color = copy.color;
colorOverride = copy.colorOverride;
topAnimation = copy.topAnimation;
topWidth =copy.topWidth;
topHeight = copy.topHeight;
bottomAnimation = copy.bottomAnimation;
bottomWidth = copy.bottomWidth;
bottomHeight = copy.bottomHeight;
spacing = copy.spacing;
return *this;
}
void OptionCredits::Block::addCredit(const std::string & credit){
credits.push_back(credit);
}
void OptionCredits::Block::act(){
// Top animation
if (topAnimation != NULL){
topAnimation->act();
}
// Bottom animation
if (bottomAnimation != NULL){
bottomAnimation->act();
}
}
int OptionCredits::Block::print(int x, int y, Graphics::Color defaultTitleColor, Graphics::Color defaultColor, const Font & font, const Graphics::Bitmap & work, const Justification & justification) const {
int currentY = y;
// Top animation
if (topAnimation != NULL){
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = topWidth/2;
break;
case Right:
xmod = topWidth;
break;
}
// FIXME temporary solution
const Graphics::Bitmap temp(topWidth, topHeight);
//topAnimation->draw(x - xmod, y, topWidth, topHeight, work);
topAnimation->draw(0, 0, topWidth, topHeight, temp);
temp.translucent().draw(x-xmod, y, work);
currentY += topHeight;
}
if (!title.empty()){
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = font.textLength(title.c_str())/2;
break;
case Right:
xmod = font.textLength(title.c_str());
break;
}
font.printf(x - xmod, currentY, (titleColorOverride ? titleColor : defaultTitleColor), work, title, 0);
currentY += font.getHeight();
}
for (std::vector<std::string>::const_iterator i = credits.begin(); i != credits.end(); ++i){
const std::string & credit = *i;
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = font.textLength(credit.c_str())/2;
break;
case Right:
xmod = font.textLength(credit.c_str());
break;
}
font.printf(x - xmod, currentY, (colorOverride ? color : defaultColor), work, credit, 0);
currentY += font.getHeight();
}
// Bottom animation
if (bottomAnimation != NULL){
int xmod = 0;
switch (justification){
default:
case Left:
xmod = 0;
break;
case Center:
xmod = bottomWidth/2;
break;
case Right:
xmod = bottomWidth;
break;
}
// FIXME temporary solution
const Graphics::Bitmap temp(topWidth, topHeight);
//bottomAnimation->draw(x - xmod, y, bottomWidth, bottomHeight, work);
bottomAnimation->draw(0, 0, bottomWidth, bottomHeight, temp);
temp.translucent().draw(x-xmod, y, work);
currentY += bottomHeight;
}
currentY += font.getHeight() + spacing;
return currentY;
}
const int OptionCredits::Block::size(const Font & font) const{
// Counts title and space in between
int total = 0;
if (topAnimation != NULL){
total += topHeight;
}
if (!title.empty()){
total+= font.getHeight();
}
total += credits.size() * font.getHeight();
if (bottomAnimation != NULL){
total += bottomHeight;
}
total += font.getHeight();
total += spacing;
return total;
}
OptionCredits::Sequence::Sequence(const Token * token):
type(Primary),
x(0),
y(0),
startx(0),
endx(0),
starty(0),
endy(0),
ticks(0),
duration(250),
speed(0),
alpha(0),
alphaMultiplier(0),
justification(Block::Center),
current(0),
done(false),
creditLength(0){
if ( *token != "sequence" ){
throw LoadException(__FILE__, __LINE__, "Not a credit sequence");
}
TokenView view = token->view();
while (view.hasMore()){
std::string match;
try{
const Token * tok;
view >> tok;
if (*tok == "type"){
std::string sequenceType;
tok->view() >> sequenceType;
if (sequenceType == "roll"){
type = Roll;
} else if (sequenceType == "primary"){
type = Primary;
}
} else if (*tok == "start-x"){
tok->view() >> startx;
} else if (*tok == "end-x"){
tok->view() >> endx;
} else if (*tok == "start-y"){
tok->view() >> starty;
} else if (*tok == "end-y"){
tok->view() >> endy;
} else if (*tok == "duration"){
tok->view() >> duration;
} else if (*tok == "speed"){
tok->view() >> speed;
} else if (*tok == "alpha-multiplier"){
tok->view() >> alphaMultiplier;
} else if ( *tok == "justification" ) {
std::string justify;
tok->view() >> justify;
if (justify == "left"){
justification = Block::Left;
} else if (justify == "center"){
justification = Block::Center;
} else if (justify == "right"){
justification = Block::Right;
}
} else if (*tok == "block"){
credits.push_back(Block(tok));
} else {
Global::debug( 3 ) <<"Unhandled Credit Sequence attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Credit Sequence parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
// Initial
reset();
for (std::vector<OptionCredits::Block>::const_iterator i = credits.begin(); i != credits.end(); ++i){
const OptionCredits::Block & block = *i;
creditLength += block.size(Menu::menuFontParameter.current()->get());
}
}
OptionCredits::Sequence::Sequence(const Sequence & copy):
type(copy.type),
x(copy.x),
y(copy.y),
startx(copy.startx),
endx(copy.endx),
starty(copy.starty),
endy(copy.endy),
ticks(copy.ticks),
duration(copy.duration),
speed(copy.speed),
alpha(copy.alpha),
alphaMultiplier(copy.alphaMultiplier),
justification(copy.justification),
credits(copy.credits),
current(copy.current),
done(false),
creditLength(copy.creditLength){
}
OptionCredits::Sequence::~Sequence(){
}
const OptionCredits::Sequence & OptionCredits::Sequence::operator=(const OptionCredits::Sequence & copy){
type = copy.type;
x = copy.x;
y = copy.y;
startx = copy.startx;
endx = copy.endx;
starty = copy.starty;
endy = copy.endy;
ticks = copy.ticks;
duration = copy.duration;
speed = copy.speed;
alpha = copy.alpha;
alphaMultiplier = copy.alphaMultiplier;
justification = copy.justification;
credits = copy.credits;
current = copy.current;
done = false;
creditLength = copy.creditLength;
return *this;
}
static int alphaClamp(int x, double multiplier){
int clamp = x * multiplier;
if (clamp < 0){
clamp = 0;
} else if (clamp > 255){
clamp = 255;
}
return clamp;
}
void OptionCredits::Sequence::act(){
if (!done && !credits.empty()){
if (type == Roll){
y += speed;
if (starty > endy){
if ((y + (creditLength * 1.1)) < endy){
done = true;
}
} else if (starty < endy){
if ((y * 1.1) > endy){
done = true;
}
}
} else if (type == Primary){
credits[current].act();
if (startx != endx){
x += speed;
if (startx > endx){
const double midpoint = (startx+endx)/2;
const int mid = x > midpoint ? startx -x : x - endx;
alpha = alphaClamp(mid, alphaMultiplier);
if (x < endx){
next();
}
} else if (startx < endx){
const double midpoint = (startx+endx)/2;
const int mid = x < midpoint ? x - startx : endx - x;
alpha = alphaClamp(mid, alphaMultiplier);
//Global::debug(0) << "alpha: " << alpha << " midpoint: " << midpoint << " mid: " << mid << std::endl;
if (x > endx){
next();
}
}
} else {
const double midpoint = duration/2;
const int mid = ticks < midpoint ? ticks : duration - ticks;
alpha = alphaClamp(mid, alphaMultiplier);
ticks++;
if (ticks >= duration){
ticks = 0;
next();
}
}
}
}
}
void OptionCredits::Sequence::draw(Graphics::Color title, Graphics::Color color, const Graphics::Bitmap & work){
if (!done && !credits.empty()){
if (type == Roll){
int rollY = (int) y;
for (std::vector<OptionCredits::Block>::const_iterator i = credits.begin(); i != credits.end(); ++i){
const OptionCredits::Block & block = *i;
rollY = block.print(x, rollY, title, color, Menu::menuFontParameter.current()->get(), work, justification);
}
} else if (type == Primary){
Graphics::Bitmap::transBlender(0, 0, 0, alpha);
credits[current].print(x, y, title, color, Menu::menuFontParameter.current()->get(), work.translucent(), justification);
}
}
}
void OptionCredits::Sequence::reset(){
done = false;
current = 0;
ticks = 0;
if (!credits.empty()){
if (type == Roll){
x = startx;
y = starty;
} else if (type == Primary){
x = startx;
y = starty - (credits[current].size(Menu::menuFontParameter.current()->get())/2);
}
}
}
void OptionCredits::Sequence::next(){
if (type == Primary){
if (current < credits.size()){
current++;
if (current == credits.size()){
done = true;
} else {
x = startx;
y = starty - (credits[current].size(Menu::menuFontParameter.current()->get())/2);
}
}
}
}
static std::string defaultPositions(){
const int width = Configuration::getScreenWidth();
const int height = Configuration::getScreenHeight();
std::ostringstream out;
out << "(start-x " << width/2.3 << ") (end-x " << width/1.8 << ") (start-y " << height/2 << ") ";
//out << "(start-x " << width/2 << ") (end-x " << width/2 << ") (start-y " << height/2 << ") (duration 250) ";
return out.str();
}
OptionCredits::OptionCredits(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token),
creditsContext(new Menu::Context()),
music(""),
color(Graphics::makeColor(255,255,255)),
title(Graphics::makeColor(0,255,255)),
clearColor(Graphics::makeColor(0,0,0)){
std::string defaultSequence = "(sequence (type primary) (speed 0.3) (alpha-multiplier 20) (justification center) " + defaultPositions();
/* Always */
if (jonBirthday()){
defaultSequence += "(block (title \"Happy birthday, Jon!\"))";
}
if (miguelBirthday()){
defaultSequence += "(block (title \"Happy birthday, Miguel!\"))";
}
if (Storage::instance().exists(Filesystem::RelativePath("sprites/paintown.png"))){
defaultSequence += "(block (animation (top) (width 350) (height 65) (image 0 \"sprites/paintown.png\") (frame (image 0) (time -1))) (credit \"Version " + Version::getVersionString() + "\"))";
} else {
defaultSequence += "(block (title \"PAINTOWN\") (credit \"Version " + Version::getVersionString() + "\"))";
}
defaultSequence += "(block (title \"Programming\") (credit \"Jon Rafkind\") (credit \"Miguel Gavidia\"))";
defaultSequence += "(block (title \"Level design\") (credit \"Jon Rafkind\") (credit \"Miguel Gavidia\"))";
defaultSequence += "(block (title \"Contact\") (credit \"Website: http://paintown.org\") (credit \"Email: jon@rafkind.com\")))";
TokenReader reader;
Sequence sequence(reader.readTokenFromString(defaultSequence));
sequences.push_back(sequence);
//Global::debug(0) << defaultSequence << std::endl;
if ( *token != "credits" ){
throw LoadException(__FILE__, __LINE__, "Not a credit menu");
}
readName(token);
TokenView view = token->view();
// NOTE Use this to handle legacy additional blocks for the time being
Block legacyAdditional("");
bool additionalTitle = true;
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
if ( *tok == "music" ) {
/* Set music for credits */
tok->view() >> music;
} else if ( *tok == "background" ) {
/* Create an image and push it back on to vector */
std::string temp;
tok->view() >> temp;
creditsContext->addBackground(temp);
} else if ( *tok == "anim" || *tok == "animation" ){
creditsContext->addBackground(tok);
} else if ( *tok == "additional" ) {
std::string str;
TokenView additionalView = tok->view();
while (additionalView.hasMore()){
additionalView >> str;
if (additionalTitle){
legacyAdditional = Block(str);
additionalTitle = false;
} else {
legacyAdditional.addCredit(str);
}
}
} else if (*tok == "sequence"){
sequences.push_back(OptionCredits::Sequence(tok));
} else if ( *tok == "titlecolor" ) {
int r,b,g;
tok->view() >> r >> g >> b;
title = Graphics::makeColor( r, g, b );
} else if ( *tok == "color" ) {
int r,b,g;
tok->view() >> r >> g >> b;
color = Graphics::makeColor( r, g, b );
} else if ( *tok == "clear-color" ) {
int r,b,g;
tok->view() >> r >> g >> b;
clearColor = Graphics::makeColor( r, g, b );
} else {
Global::debug( 3 ) <<"Unhandled menu attribute: "<<endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
throw ex;
}
}
if (!legacyAdditional.empty()){
//creditsRoll.push_back(legacyAdditional);
}
input.set(Keyboard::Key_ESC, 0, true, Exit);
input.set(Joystick::Button2, 0, true, Exit);
}
OptionCredits::~OptionCredits(){
}
void OptionCredits::logic(){
}
class CreditsLogicDraw : public Util::Logic, public Util::Draw {
public:
CreditsLogicDraw(std::vector<OptionCredits::Sequence> & sequences, Graphics::Color clearColor, Graphics::Color title, Graphics::Color color, const Font & font, InputMap<OptionCredits::CreditKey> & input, Menu::Context & context):
sequences(sequences),
clearColor(clearColor),
title(title),
color(color),
font(font),
input(input),
quit(false),
context(context),
current(0){
}
std::vector<OptionCredits::Sequence> & sequences;
Graphics::Color clearColor, title, color;
const Font & font;
InputMap<OptionCredits::CreditKey> & input;
bool quit;
Menu::Context & context;
unsigned int current;
void run(){
vector<InputMap<OptionCredits::CreditKey>::InputEvent> out = InputManager::getEvents(input, InputSource(true));
for (vector<InputMap<OptionCredits::CreditKey>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<OptionCredits::CreditKey>::InputEvent & event = *it;
if (event.enabled){
if (event.out == OptionCredits::Exit){
quit = true;
context.finish();
}
}
}
sequences[current].act();
if (sequences[current].isDone()){
sequences[current].reset();
current++;
if (current >= sequences.size()){
current = 0;
}
}
context.act();
}
bool done(){
return quit;
}
double ticks(double system){
return system * Global::ticksPerSecond(90);
}
void draw(const Graphics::Bitmap & buffer){
/* FIXME: hard coded resolution */
Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
work.fill(clearColor);
work.start();
//background.Blit(work);
context.render(Util::ReferenceCount<Menu::Renderer>(NULL), work);
sequences[current].draw(title, color, work);
work.finish();
// buffer.BlitToScreen();
}
};
void OptionCredits::run(const Menu::Context & context){
Menu::Context localContext(context, *creditsContext);
localContext.initialize();
if (!music.empty()){
if (Music::loadSong(Storage::instance().find(Filesystem::RelativePath(music)).path())){
Music::pause();
Music::play();
}
}
const Font & vFont = Menu::menuFontParameter.current()->get();
CreditsLogicDraw loop(sequences, clearColor, title, color, vFont, input, localContext);
Util::standardLoop(loop, loop);
InputManager::waitForRelease(input, InputSource(true), Exit);
throw Menu::Reload(__FILE__, __LINE__);
}
OptionDummy::OptionDummy(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token){
if ( *token != "dummy" ){
throw LoadException(__FILE__, __LINE__, "Not dummy option");
}
readName(token);
if (getText().empty()){
this->setText("Dummy");
}
setRunnable(false);
}
OptionDummy::OptionDummy(const Gui::ContextBox & parent, const std::string &name):
MenuOption(parent, 0){
if (name.empty()){
throw LoadException(__FILE__, __LINE__, "No name given to dummy");
}
this->setText(name);
setRunnable(false);
}
OptionDummy::~OptionDummy(){
}
void OptionDummy::logic(){
}
void OptionDummy::run(const Menu::Context & context){
}
OptionFullscreen::OptionFullscreen(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if ( *token != "fullscreen" )
throw LoadException(__FILE__, __LINE__, "Not fullscreen option");
readName(token);
}
OptionFullscreen::~OptionFullscreen()
{
// Nothing
}
std::string OptionFullscreen::getText() const {
ostringstream out;
out << MenuOption::getText() << ": " << (Configuration::getFullscreen() ? "Yes" : "No");
return out.str();
}
void OptionFullscreen::logic(){;
}
static void changeScreenMode(){
Configuration::setFullscreen(!Configuration::getFullscreen());
int gfx = (Configuration::getFullscreen() ? Global::FULLSCREEN : Global::WINDOWED);
Graphics::changeGraphicsMode(gfx, Graphics::Bitmap::getScreenWidth(), Graphics::Bitmap::getScreenHeight());
}
void OptionFullscreen::run(const Menu::Context & context){
changeScreenMode();
}
bool OptionFullscreen::leftKey(){
changeScreenMode();
return true;
}
bool OptionFullscreen::rightKey(){
changeScreenMode();
return true;
}
OptionFps::OptionFps(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
readName(token);
setRunnable(false);
}
void OptionFps::logic(){
}
void OptionFps::run(const Menu::Context & context){
}
std::string OptionFps::getText() const {
ostringstream out;
out << "Frames per second: " << Global::TICS_PER_SECOND;
return out.str();
}
bool OptionFps::leftKey(){
Global::setTicksPerSecond(Global::TICS_PER_SECOND - 1);
Configuration::setFps(Global::TICS_PER_SECOND);
return true;
}
bool OptionFps::rightKey(){
Global::setTicksPerSecond(Global::TICS_PER_SECOND + 1);
Configuration::setFps(Global::TICS_PER_SECOND);
return true;
}
OptionFps::~OptionFps(){
}
OptionQualityFilter::OptionQualityFilter(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
readName(token);
setRunnable(false);
}
std::string OptionQualityFilter::getText() const {
ostringstream out;
out << MenuOption::getText() << ": " << Configuration::getQualityFilter();
return out.str();
}
void OptionQualityFilter::logic(){
}
bool OptionQualityFilter::leftKey(){
string quality = Configuration::getQualityFilter();
if (quality == "none"){
quality = "hqx";
} else if (quality == "hqx"){
quality = "xbr";
} else if (quality == "xbr"){
quality = "none";
}
Configuration::setQualityFilter(quality);
return true;
}
bool OptionQualityFilter::rightKey(){
string quality = Configuration::getQualityFilter();
if (quality == "none"){
quality = "xbr";
} else if (quality == "hqx"){
quality = "none";
} else if (quality == "xbr"){
quality = "hqx";
}
Configuration::setQualityFilter(quality);
return true;
}
void OptionQualityFilter::run(const Menu::Context & context){
}
OptionQualityFilter::~OptionQualityFilter(){
}
#if 0
static OptionJoystick::JoystickType convertToKey(const std::string &k){
std::string temp = k;
for(unsigned int i=0;i<temp.length();i++){
temp[i] = tolower(temp[i]);
}
if (temp == "up") return OptionJoystick::Up;
if (temp == "down") return OptionJoystick::Down;
if (temp == "left") return OptionJoystick::Left;
/*
if (temp == "right") return OptionJoystick::Right;
if (temp == "jump") return OptionJoystick::Jump;
if (temp == "attack1") return OptionJoystick::Attack1;
if (temp == "attack2") return OptionJoystick::Attack2;
if (temp == "attack3") return OptionJoystick::Attack3;
if (temp == "attack4") return OptionJoystick::Attack4;
if (temp == "attack5") return OptionJoystick::Attack5;
if (temp == "attack6") return OptionJoystick::Attack6;
return OptionJoystick::Invalidkey;
}
static Configuration::JoystickInput getKey(int player, OptionJoystick::JoystickType k){
switch(k){
case OptionJoystick::Up:
return Joystick::Up;
case OptionJoystick::Down:
return Joystick::Down;
case OptionJoystick::Left:
return Joystick::Left;
case OptionJoystick::Right:
return Joystick::Right;
case OptionJoystick::Jump:
return Joystick::Button4;
case OptionJoystick::Attack1:
return Joystick::Button1;
case OptionJoystick::Attack2:
return Joystick::Button2;
case OptionJoystick::Attack3:
return Joystick::Button3;
case OptionJoystick::Attack4:
return Joystick::Button4;
case OptionJoystick::Attack5:
return Joystick::Button5;
case OptionJoystick::Attack6:
return Joystick::Button6;
default:
break;
}
return Joystick::Up;
}
static void setKey(int player, OptionJoystick::JoystickType k, Configuration::JoystickInput key){
/ *
switch(k){
case OptionJoystick::Up:
Configuration::setJoystickUp(player, key);
break;
case OptionJoystick::Down:
Configuration::setJoystickDown(player, key);
break;
case OptionJoystick::Left:
Configuration::setJoystickLeft(player, key);
break;
case OptionJoystick::Right:
Configuration::setJoystickRight(player, key);
break;
case OptionJoystick::Jump:
Configuration::setJoystickJump(player, key);
break;
case OptionJoystick::Attack1:
Configuration::setJoystickAttack1(player, key);
break;
case OptionJoystick::Attack2:
Configuration::setJoystickAttack2(player, key);
break;
case OptionJoystick::Attack3:
Configuration::setJoystickAttack3(player, key);
break;
case OptionJoystick::Attack4:
Configuration::setJoystickAttack4(player, key);
break;
case OptionJoystick::Attack5:
Configuration::setJoystickAttack5(player, key);
break;
case OptionJoystick::Attack6:
Configuration::setJoystickAttack6(player, key);
break;
default:
break;
}
*/
}
static Configuration::JoystickInput readJoystick(){
vector<Joystick::Key> keys;
keys.push_back(Joystick::Up);
keys.push_back(Joystick::Down);
keys.push_back(Joystick::Left);
keys.push_back(Joystick::Right);
keys.push_back(Joystick::Button1);
keys.push_back(Joystick::Button2);
keys.push_back(Joystick::Button3);
keys.push_back(Joystick::Button4);
keys.push_back(Joystick::Button5);
keys.push_back(Joystick::Button6);
InputMap<Joystick::Key> input;
for (vector<Joystick::Key>::iterator it = keys.begin(); it != keys.end(); it++){
input.set(*it, 0, true, *it);
}
input.set(Keyboard::Key_ESC, 0, true, Joystick::Invalid);
while (true){
InputManager::poll();
vector<InputMap<Joystick::Key>::InputEvent> out = InputManager::getEvents(input, InputSource());
for (vector<InputMap<Joystick::Key>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<Joystick::Key>::InputEvent & event = *it;
if (event.enabled){
Global::debug(1) << "Press: " << event.out << std::endl;
if (event.out == Joystick::Invalid){
InputManager::waitForRelease(input, InputSource(), Joystick::Invalid);
throw Exception::Return(__FILE__, __LINE__);
}
return event.out;
}
}
Util::rest(1);
}
/* control probably shouldn't get here.. */
return Joystick::Up;
}
OptionJoystick::OptionJoystick(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
player(-1),
type(Invalidkey),
keyCode(0){
if (*token != "joystick"){
throw LoadException(__FILE__, __LINE__, "Not joystick option");
}
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
if ( *tok == "name" ){
tok->view() >> name;
} else if ( *tok == "player" ) {
tok->view() >> player;
} else if ( *tok == "type" ) {
std::string temp;
tok->view() >> temp;
type = convertToKey(temp);
} else {
Global::debug( 3 ) <<"Unhandled menu attribute: "<<endl;
tok->print(" ");
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
// delete current;
throw ex;
}
}
if (name.empty()){
throw LoadException(__FILE__, __LINE__, "No name set, this option should have a name!");
}
if (type == Invalidkey){
throw LoadException(__FILE__, __LINE__, "Invalid joystick button, should be up, down, left, right, up, down, jump, attack1-6!");
}
if (player == -1){
throw LoadException(__FILE__, __LINE__, "Player not specified in joystick configuration");
}
ostringstream out;
out << name << ": " << Joystick::keyToName(getKey(player, type));
setText(out.str());
}
OptionJoystick::~OptionJoystick(){
// Nothing
}
void OptionJoystick::logic(){
/*
char temp[255];
sprintf( temp, "%s: %s", name.c_str(), Joystick::keyToName(getKey(player,type)));
setText(std::string(temp));
*/
}
void OptionJoystick::run(const Menu::Context & context){
/*
//int x, y, width, height;
const Font & vFont = Menu::menuFontParameter.current()->get();
const char * message = "Press a joystick button!";
const int width = vFont.textLength(message) + 10;
const int height = vFont.getHeight() + 10;
// const int x = (getParent()->getWork()->getWidth()/2) - (width/2);
// const int y = (getParent()->getWork()->getHeight()/2) - (height/2);
const int x = Menu::Menu::Width / 2 - width/2;
const int y = Menu::Menu::Height / 2 - height/2;
Box dialog;
dialog.location.setPosition(Gui::AbsolutePoint(0,0));
dialog.location.setDimensions(vFont.textLength(message) + 10, vFont.getHeight() + 10);
dialog.transforms.setRadius(0);
dialog.colors.body = Graphics::makeColor(0,0,0);
dialog.colors.bodyAlpha = 200;
dialog.colors.border = Graphics::makeColor(255,255,255);
dialog.colors.borderAlpha = 255;
Graphics::Bitmap temp = Graphics::Bitmap::temporaryBitmap(width,height);
dialog.render(temp, vFont);
vFont.printf( 5, 5, Graphics::makeColor(255,255,255), temp, message, -1);
temp.BlitToScreen(x,y);
setKey(player, type, readJoystick());
ostringstream out;
out << name << ": " << Joystick::keyToName(getKey(player, type));
setText(out.str());
*/
Graphics::Bitmap temp(Menu::Menu::Width, Menu::Menu::Height);
// Menu::Context tempContext = context;
Menu::Context tempContext(context);
tempContext.initialize();
Menu::InfoBox keyDialog;
// keyDialog.setFont(tempContext.getFont());
//keyDialog.location.set(-1,-1,1,1);
const int width = temp.getWidth();
const int height = temp.getHeight();
const Font & font = Menu::menuFontParameter.current()->get();
const int radius = 15;
keyDialog.setText("Press a joystick button!");
keyDialog.initialize(font);
keyDialog.location.setDimensions(font.textLength("Press a joystick button!") + radius, font.getHeight() + radius);
keyDialog.location.setCenterPosition(Gui::RelativePoint(0, 0));
// keyDialog.location.setPosition(Gui::AbsolutePoint((width/2)-(keyDialog.location.getWidth()/2), (height/2)-(keyDialog.location.getHeight()/2)));
// keyDialog.location.setPosition2(Gui::AbsolutePoint((
keyDialog.transforms.setRadius(radius);
keyDialog.colors.body = Graphics::makeColor(0,0,0);
keyDialog.colors.bodyAlpha = 180;
keyDialog.colors.border = Graphics::makeColor(255,255,255);
keyDialog.colors.borderAlpha = 255;
keyDialog.open();
InputManager::waitForClear();
while (!InputManager::anyInput() && keyDialog.isActive()){
InputManager::poll();
keyDialog.act(font);
/*
if (keyDialog.isActive()){
InputManager::poll();
}
*/
tempContext.act();
tempContext.render(0, temp);
keyDialog.render(temp, font);
temp.BlitToScreen();
}
tempContext.finish();
setKey(player, type, readJoystick());
InputManager::waitForClear();
ostringstream out;
out << name << ": " << Joystick::keyToName(getKey(player, type));
setText(out.str());
/*
Keyboard key;
keyCode = readKey(key);
setKey(player,type, keyCode);
*/
}
#endif
static OptionKey::keyType convertToKeyboardKey(const std::string &k){
std::string temp = k;
for(unsigned int i=0;i<temp.length();i++){
temp[i] = tolower(temp[i]);
}
if (temp == "up") return OptionKey::up;
if (temp == "down") return OptionKey::down;
if (temp == "left") return OptionKey::left;
if (temp == "right") return OptionKey::right;
if (temp == "jump") return OptionKey::jump;
if (temp == "attack1") return OptionKey::attack1;
if (temp == "attack2") return OptionKey::attack2;
if (temp == "attack3") return OptionKey::attack3;
if (temp == "attack4") return OptionKey::attack4;
if (temp == "attack5") return OptionKey::attack5;
if (temp == "attack6") return OptionKey::attack6;
return OptionKey::invalidkey;
}
static int getKey(int player, OptionKey::keyType k){
switch(k){
case OptionKey::up:
return Configuration::getUp(player);
break;
case OptionKey::down:
return Configuration::getDown(player);
break;
case OptionKey::left:
return Configuration::getLeft(player);
break;
case OptionKey::right:
return Configuration::getRight(player);
break;
case OptionKey::jump:
return Configuration::getJump(player);
break;
case OptionKey::attack1:
return Configuration::getAttack1(player);
break;
case OptionKey::attack2:
return Configuration::getAttack2(player);
break;
case OptionKey::attack3:
return Configuration::getAttack3(player);
break;
case OptionKey::attack4:
return Configuration::getAttack4(player);
case OptionKey::attack5:
return Configuration::getAttack5(player);
case OptionKey::attack6:
return Configuration::getAttack6(player);
default:
break;
}
return 0;
}
static void setKey(int player, OptionKey::keyType k, int key){
switch(k){
case OptionKey::up:
Configuration::setUp(player, key);
break;
case OptionKey::down:
Configuration::setDown(player, key);
break;
case OptionKey::left:
Configuration::setLeft(player, key);
break;
case OptionKey::right:
Configuration::setRight(player, key);
break;
case OptionKey::jump:
Configuration::setJump(player, key);
break;
case OptionKey::attack1:
Configuration::setAttack1(player, key);
break;
case OptionKey::attack2:
Configuration::setAttack2(player, key);
break;
case OptionKey::attack3:
Configuration::setAttack3(player, key);
break;
case OptionKey::attack4:
Configuration::setAttack4(player, key);
break;
case OptionKey::attack5:
Configuration::setAttack5(player, key);
break;
case OptionKey::attack6:
Configuration::setAttack6(player, key);
break;
default:
break;
}
}
/*
static int readKey( Keyboard & key ){
int k = key.readKey();
key.wait();
return k;
}
*/
OptionKey::OptionKey(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
player(-1),
type(invalidkey),
keyCode(0){
if ( *token != "key" )
throw LoadException(__FILE__, __LINE__, "Not key option");
TokenView view = token->view();
while (view.hasMore()) {
try {
const Token * tok;
view >> tok;
if ( *tok == "name" ) {
tok->view() >> name;
} else if ( *tok == "player" ) {
tok->view() >> player;
} else if ( *tok == "type" ) {
std::string temp;
tok->view() >> temp;
type = convertToKeyboardKey(temp);
} else {
Global::debug( 3 ) <<"Unhandled menu attribute: "<<endl;
tok->print(" ");
}
} catch ( const TokenException & ex ){
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
// delete current;
throw ex;
}
}
if(name.empty())throw LoadException(__FILE__, __LINE__, "No name set, this option should have a name!");
if(type == invalidkey)throw LoadException(__FILE__, __LINE__, "Invalid key, should be up, down, left, right, up, down, jump, attack1-6!");
if(player == -1)throw LoadException(__FILE__, __LINE__, "Player not specified in key configuration");
char temp[255];
sprintf( temp, "%s: %s", name.c_str(), Keyboard::keyToName(getKey(player,type)));
setText(std::string(temp));
}
OptionKey::~OptionKey(){
// Nothing
}
void OptionKey::logic(){
char temp[255];
sprintf( temp, "%s: %s", name.c_str(), Keyboard::keyToName(getKey(player,type)));
setText(std::string(temp));
}
void OptionKey::run(const Menu::Context & context){
// Do dialog
//Box::messageDialog(Menu::Menu::Width, Menu::Menu::Height, "Press a Key!",2);
class WaitForKey: public Util::Logic, public Util::Draw {
public:
WaitForKey(const Menu::Context & context):
font(Menu::menuFontParameter.current()->get()),
tempContext(context),
speed(30),
listener(*this),
quit(false),
out(Keyboard::Key_ESC),
startingTime(System::currentMilliseconds()),
maxTime(5000){
tempContext.initialize();
// keyDialog.setFont(tempContext.getFont());
//keyDialog.location.set(-1,-1,1,1);
const int width = 320;
const int height = 240;
const int radius = 15;
keyDialog.setText("Press a Key!");
keyDialog.initialize(font);
keyDialog.location.setDimensions(font.textLength("Press a Key!") + radius, font.getHeight() + radius);
keyDialog.location.setCenterPosition(Gui::RelativePoint(0, 0));
// keyDialog.location.setPosition(Gui::AbsolutePoint((width/2)-(keyDialog.location.getWidth()/2), (height/2)-(keyDialog.location.getHeight()/2)));
// keyDialog.location.setPosition2(Gui::AbsolutePoint((
keyDialog.transforms.setRadius(radius);
keyDialog.colors.body = Graphics::makeColor(0,0,0);
keyDialog.colors.bodyAlpha = 180;
keyDialog.colors.border = Graphics::makeColor(255,255,255);
keyDialog.colors.borderAlpha = 255;
keyDialog.open();
Keyboard::pushRepeatState(false);
Keyboard::addListener(&listener);
vector<Graphics::BlendPoint> points;
points.push_back(Graphics::BlendPoint(Graphics::makeColor(255, 0, 0), 30));
points.push_back(Graphics::BlendPoint(Graphics::makeColor(0, 255, 0), 25));
points.push_back(Graphics::BlendPoint(Graphics::makeColor(255, 255, 255), 0));
colors = Graphics::blend_palette(points);
}
virtual ~WaitForKey(){
Keyboard::removeListener(&listener);
Keyboard::popRepeatState();
}
class Listener: public KeyboardListener {
public:
Listener(WaitForKey & parent):
parent(parent){
}
WaitForKey & parent;
virtual void press(Keyboard * keyboard, Keyboard::KeyType key, Keyboard::unicode_t code){
parent.press(key);
}
virtual void release(Keyboard * keyboard, Keyboard::KeyType key){
parent.release(key);
}
};
const Font & font;
Menu::Context tempContext;
Menu::InfoBox keyDialog;
const int speed;
Listener listener;
bool quit;
Keyboard::KeyType out;
/* keep track of how much time we are in this loop */
uint64_t startingTime;
/* maximum number of milliseconds to wait */
const uint64_t maxTime;
vector<Graphics::Color> colors;
virtual void press(Keyboard::KeyType key){
quit = true;
if (key != Keyboard::Key_ESC){
this->out = key;
}
}
virtual void release(Keyboard::KeyType key){
}
virtual void run(){
for (int i = 0; i < 90 / speed; i++){
tempContext.act();
keyDialog.act(font);
}
/* exit after 5 seconds */
quit = quit || (System::currentMilliseconds() - startingTime > maxTime);
}
virtual bool done(){
return quit;
}
virtual double ticks(double system){
return system * Global::ticksPerSecond(speed);
}
virtual void showTimeLeft(const Graphics::Bitmap & screen){
int x1 = 1;
int y1 = 1;
int x2 = screen.getWidth() * (1 - (double) (System::currentMilliseconds() - startingTime) / (double) maxTime);
if (x2 < x1){
x2 = x1;
}
int y2 = 10;
int color = (1.0 - (double) (System::currentMilliseconds() - startingTime) / maxTime) * colors.size();
if (color < 0){
color = 0;
}
if (color >= colors.size()){
color = colors.size() - 1;
}
screen.rectangleFill(x1, y1, x2, y2, colors[color]);
}
virtual void draw(const Graphics::Bitmap & screen){
Graphics::StretchedBitmap work(640, 480, screen);
work.start();
tempContext.render(Util::ReferenceCount<Menu::Renderer>(NULL), work);
keyDialog.render(work, font);
showTimeLeft(work);
work.finish();
}
};
WaitForKey run(context);
Util::standardLoop(run, run);
Keyboard::KeyType key = run.out;
if (key != Keyboard::Key_ESC){
setKey(player, type, key);
}
/*
Keyboard key;
key.wait();
*/
/*
Graphics::Bitmap temp(Menu::Menu::Width, Menu::Menu::Height);
// Menu::Context tempContext = context;
Menu::Context tempContext(context);
tempContext.initialize();
Menu::InfoBox keyDialog;
// keyDialog.setFont(tempContext.getFont());
//keyDialog.location.set(-1,-1,1,1);
const int width = temp.getWidth();
const int height = temp.getHeight();
const Font & font = Menu::menuFontParameter.current()->get();
const int radius = 15;
keyDialog.setText("Press a Key!");
keyDialog.initialize(font);
keyDialog.location.setDimensions(font.textLength("Press a Key!") + radius, font.getHeight() + radius);
keyDialog.location.setCenterPosition(Gui::RelativePoint(0, 0));
// keyDialog.location.setPosition(Gui::AbsolutePoint((width/2)-(keyDialog.location.getWidth()/2), (height/2)-(keyDialog.location.getHeight()/2)));
// keyDialog.location.setPosition2(Gui::AbsolutePoint((
keyDialog.transforms.setRadius(radius);
keyDialog.colors.body = Graphics::makeColor(0,0,0);
keyDialog.colors.bodyAlpha = 180;
keyDialog.colors.border = Graphics::makeColor(255,255,255);
keyDialog.colors.borderAlpha = 255;
keyDialog.open();
InputManager::waitForClear();
while (!InputManager::anyInput() && keyDialog.isActive()){
InputManager::poll();
keyDialog.act(font);
/ *
if (keyDialog.isActive()){
InputManager::poll();
}
* /
tempContext.act();
tempContext.render(Util::ReferenceCount<Menu::Renderer>(NULL), temp);
keyDialog.render(temp, font);
temp.BlitToScreen();
}
tempContext.finish();
keyCode = InputManager::readKey();
setKey(player,type, keyCode);
InputManager::waitForClear();
*/
}
OptionLevel::OptionLevel(const Gui::ContextBox & parent, const Token *token, int * set, int value):
MenuOption(parent, token),
set(set),
value(value){
// Nothing
}
OptionLevel::~OptionLevel(){
}
void OptionLevel::logic(){
}
void OptionLevel::run(const Menu::Context & context){
*set = value;
throw Menu::MenuException(__FILE__, __LINE__);
}
OptionMenu::OptionMenu(const Gui::ContextBox & parent, const Token *token, const Menu::OptionFactory & factory):
MenuOption(parent, token),
menu(0){
if (*token != "menu"){
throw LoadException(__FILE__, __LINE__, "Not a menu");
}
if (token->numTokens() == 1){
std::string temp;
token->view() >> temp;
menu = new Menu::Menu(Storage::instance().find(Filesystem::RelativePath(temp)), factory);
} else {
menu = new Menu::Menu(token, factory);
}
this->setText(menu->getName());
this->setInfoText(menu->getInfo());
// Lets check if this menu is going bye bye
//if ( menu->checkRemoval() ) setForRemoval(true);
}
OptionMenu::~OptionMenu(){
// Delete our menu
if (menu){
delete menu;
}
}
void OptionMenu::logic(){
// Nothing
}
void OptionMenu::run(const Menu::Context & context){
// Do our new menu
try{
menu->run(context);
} catch (const Exception::Return ignore){
throw Menu::Reload(__FILE__, __LINE__);
}
}
OptionReturn::OptionReturn(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
if (*token != "return"){
throw LoadException(__FILE__, __LINE__, "Not a return option");
}
readName(token);
}
void OptionReturn::logic(){
}
/* maybe this option is misnamed, but its supposed to quit the current game
* and go back to the main menu
*/
void OptionReturn::run(const Menu::Context & context){
throw Exception::Quit(__FILE__, __LINE__);
}
OptionReturn::~OptionReturn(){
}
OptionContinue::OptionContinue(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
if (*token != "continue"){
throw LoadException(__FILE__, __LINE__, "Not a continue option");
}
readName(token);
}
void OptionContinue::logic(){
}
void OptionContinue::run(const Menu::Context & context){
throw Exception::Return(__FILE__, __LINE__);
}
OptionContinue::~OptionContinue(){
}
OptionQuit::OptionQuit(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token){
if ( *token != "quit" ){
throw LoadException(__FILE__, __LINE__, "Not quit option");
}
readName(token);
}
OptionQuit::OptionQuit(const Gui::ContextBox & parent, const std::string &name):
MenuOption(parent, 0){
if (name.empty()){
throw LoadException(__FILE__, __LINE__, "No name given to quit");
}
this->setText(name);
}
OptionQuit::~OptionQuit(){
}
void OptionQuit::logic(){
}
void OptionQuit::run(const Menu::Context & context){
throw ShutdownException();
}
#if defined(WINDOWS) && defined(doesnt_work_yet)
#include <windows.h>
#include <stdio.h>
/* contributed by Roy Underthump from allegro.cc */
static vector<ScreenSize> getScreenResolutions(){
HWND hwnd;
HDC hdc;
// int iPixelFormat;
int descerr;
int retval;
DEVMODE d;
PIXELFORMATDESCRIPTOR pfd;
hwnd = GetDesktopWindow();
hdc = GetDC(hwnd);
vector<ScreenSize> modes;
for (int i = 0;; i++){
retval = EnumDisplaySettings(0,i,&d);
if (!retval){
break;
}
descerr = DescribePixelFormat(hdc, i+1, sizeof(pfd), &pfd);
if(!descerr){
continue;
}
/*
printf("\n#%d bpp %d width %d height %d colorbits %d fps %d",i,d.dmBitsPerPel,
d.dmPelsWidth, d.dmPelsHeight,pfd.cColorBits,d.dmDisplayFrequency);
if(pfd.dwFlags & PFD_SUPPORT_OPENGL)printf(" OGL OK");
*/
modes.push_back(ScreenSize(d.dmPelsWidth, d.dmPelsHeight));
}
if (modes.empty()){
modes.push_back(ScreenSize(640,480));
}
return modes;
}
#else
static vector<ScreenSize> getScreenResolutions(){
vector<ScreenSize> modes;
modes.push_back(ScreenSize(320, 240));
modes.push_back(ScreenSize(640, 480));
modes.push_back(ScreenSize(800, 600));
modes.push_back(ScreenSize(960, 720));
modes.push_back(ScreenSize(1024, 768));
modes.push_back(ScreenSize(1280, 960));
modes.push_back(ScreenSize(1600, 1200));
return modes;
}
#endif
static bool doSort(const ScreenSize & a, const ScreenSize & b){
return (a.w * a.h) < (b.w * b.h);
}
static vector<ScreenSize> sortResolutions(const vector<ScreenSize> & modes){
vector<ScreenSize> copy(modes);
std::sort(copy.begin(), copy.end(), doSort);
return copy;
}
OptionScreenSize::OptionScreenSize(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
Global::debug(1) << "Get screen resolution" << endl;
modes = sortResolutions(getScreenResolutions());
if (Global::getDebug() >= 1){
for (vector<ScreenSize>::iterator it = modes.begin(); it != modes.end(); it++){
Global::debug(1) << "Screen size: " << it->w << " x " << it->h << endl;
}
}
if ( *token != "screen-size" ){
throw LoadException(__FILE__, __LINE__, "Not a screen-size");
}
readName(token);
}
OptionScreenSize::~OptionScreenSize(){
// Nothing
}
void OptionScreenSize::logic(){
ostringstream temp;
temp << "Screen size: " << Configuration::getScreenWidth() << " x " << Configuration::getScreenHeight();
setText(temp.str());
}
void OptionScreenSize::run(const Menu::Context & context){
}
void OptionScreenSize::setMode(int width, int height){
if (width != Configuration::getScreenWidth() ||
height != Configuration::getScreenHeight()){
Global::debug(1) << "Changing mode to " << width << " x " << height << endl;
int gfx = Configuration::getFullscreen() ? Global::FULLSCREEN : Global::WINDOWED;
int ok = Graphics::changeGraphicsMode(gfx, width, height);
if (ok == 0){
Global::debug(1) << "Success" << endl;
Configuration::setScreenWidth(width);
Configuration::setScreenHeight(height);
} else {
Global::debug(1) << "Fail" << endl;
int ok = Graphics::changeGraphicsMode(gfx, Configuration::getScreenWidth(), Configuration::getScreenHeight());
Global::debug(1) << "Set mode back " << ok << endl;
}
}
}
/*
static int modes[][2] = {{640,480}, {800,600}, {1024,768}, {1280,1024}, {1600,1200}};
// static int max_modes = sizeof(modes) / sizeof(int[]);
static int max_modes = 5;
*/
int OptionScreenSize::findMode(int width, int height){
for (int mode = 0; mode < (int) modes.size(); mode++){
if (modes[mode].w == width && modes[mode].h == height){
return mode;
}
}
return -1;
}
bool OptionScreenSize::leftKey(){
int mode = findMode(Configuration::getScreenWidth(), Configuration::getScreenHeight());
if (mode >= 1 && mode < (int)modes.size()){
mode -= 1;
} else {
mode = 0;
}
setMode(modes[mode].w, modes[mode].h);
lblue = lgreen = 0;
return true;
}
bool OptionScreenSize::rightKey(){
int mode = findMode(Configuration::getScreenWidth(), Configuration::getScreenHeight());
if (mode >= 0 && mode < (int)modes.size() - 1){
mode += 1;
} else {
mode = 0;
}
setMode(modes[mode].w, modes[mode].h);
rblue = rgreen = 0;
return true;
}
static string joinPaths(const vector<Filesystem::AbsolutePath> & strings, const string & middle){
ostringstream out;
for (vector<Filesystem::AbsolutePath>::const_iterator it = strings.begin(); it != strings.end(); it++){
out << (*it).path() << middle;
}
return out.str();
}
static bool sortInfo(const Util::ReferenceCount<Menu::FontInfo> & info1,
const Util::ReferenceCount<Menu::FontInfo> & info2){
string name1 = Util::lowerCaseAll(info1->getName());
string name2 = Util::lowerCaseAll(info2->getName());
return name1 < name2;
}
static bool isWindows(){
#ifdef WINDOWS
return true;
#else
return false;
#endif
}
static bool isOSX(){
#ifdef MACOSX
return true;
#else
return false;
#endif
}
template <class X>
static vector<X> operator+(const vector<X> & v1, const vector<X> & v2){
vector<X> out;
for (typename vector<X>::const_iterator it = v1.begin(); it != v1.end(); it++){
out.push_back(*it);
}
for (typename vector<X>::const_iterator it = v2.begin(); it != v2.end(); it++){
out.push_back(*it);
}
return out;
}
static vector<Filesystem::AbsolutePath> findSystemFonts(){
if (isWindows()){
const char * windows = getenv("windir");
if (windows != NULL){
return Storage::instance().getFilesRecursive(Filesystem::AbsolutePath(string(windows) + "/fonts"), "*.ttf");
}
return vector<Filesystem::AbsolutePath>();
} else if (isOSX()){
return Storage::instance().getFilesRecursive(Filesystem::AbsolutePath("/Library/Fonts"), "*.ttf");
} else {
/* assume unix/linux conventions */
return Storage::instance().getFilesRecursive(Filesystem::AbsolutePath("/usr/share/fonts/truetype"), "*.ttf") +
Storage::instance().getFilesRecursive(Filesystem::AbsolutePath("/usr/local/share/fonts/truetype"), "*.ttf");
}
}
static vector<Util::ReferenceCount<Menu::FontInfo> > findFonts(){
vector<Util::ReferenceCount<Menu::FontInfo> > fonts;
try{
Filesystem::AbsolutePath fontsDirectory = Storage::instance().find(Filesystem::RelativePath("fonts"));
Global::debug(1, "fonts") << "Font directory " << fontsDirectory.path() << endl;
vector<Filesystem::AbsolutePath> ttfFonts = Storage::instance().getFiles(fontsDirectory, "*.ttf");
Global::debug(1, "fonts") << "Found ttf fonts " << joinPaths(ttfFonts, ", ") << endl;
vector<Filesystem::AbsolutePath> otfFonts = Storage::instance().getFiles(fontsDirectory, "*.otf");
Global::debug(1, "fonts") << "Found otf fonts " << joinPaths(otfFonts, ", ") << endl;
for (vector<Filesystem::AbsolutePath>::iterator it = ttfFonts.begin(); it != ttfFonts.end(); it++){
fonts.push_back(Util::ReferenceCount<Menu::FontInfo>(new Menu::RelativeFontInfo(Storage::instance().cleanse(*it), Configuration::getMenuFontWidth(), Configuration::getMenuFontHeight())));
}
for (vector<Filesystem::AbsolutePath>::iterator it = otfFonts.begin(); it != otfFonts.end(); it++){
fonts.push_back(Util::ReferenceCount<Menu::FontInfo>(new Menu::RelativeFontInfo(Storage::instance().cleanse(*it), Configuration::getMenuFontWidth(), Configuration::getMenuFontHeight())));
}
/* linux specific fonts */
vector<Filesystem::AbsolutePath> systemFonts = findSystemFonts();
for (vector<Filesystem::AbsolutePath>::iterator it = systemFonts.begin(); it != systemFonts.end(); it++){
Global::debug(1) << "Adding system font `" << (*it).path() << "'" << endl;
fonts.push_back(Util::ReferenceCount<Menu::FontInfo>(new Menu::AbsoluteFontInfo(*it, Configuration::getMenuFontWidth(), Configuration::getMenuFontHeight())));
}
sort(fonts.begin(), fonts.end(), sortInfo);
// DEFAULT (blank)
// fonts.insert(fonts.begin(), new Menu::DefaultFontInfo());
fonts.insert(fonts.begin(), Util::ReferenceCount<Menu::FontInfo>(NULL));
} catch (const Filesystem::NotFound & e){
throw LoadException(__FILE__, __LINE__, e, "Could not load font");
}
return fonts;
}
OptionSelectFont::OptionSelectFont(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
typeAdjust(fontName),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if ( *token != "font-select" ){
throw LoadException(__FILE__, __LINE__, "Not a font selector");
}
TokenView view = token->view();
while (view.hasMore()){
try{
const Token * tok;
view >> tok;
if ( *tok == "adjust" ){
std::string temp;
tok->view() >> temp;
if ( temp == "name" ) typeAdjust = fontName;
else if ( temp == "width" ) typeAdjust = fontWidth;
else if ( temp == "height" ) typeAdjust = fontHeight;
else throw LoadException(__FILE__, __LINE__, "Incorrect value \"" + temp + "\" in font-select");
} else {
Global::debug(3) << "Unhandled menu attribute: " << endl;
if (Global::getDebug() >= 3){
tok->print(" ");
}
}
} catch ( const TokenException & ex ) {
throw LoadException(__FILE__, __LINE__, ex, "Menu parse error");
} catch ( const LoadException & ex ) {
// delete current;
throw ex;
}
}
}
void OptionSelectFont::open(){
// Find and set fonts now
if (typeAdjust == fontName){
fonts = findFonts();
}
}
void OptionSelectFont::close(){
if (typeAdjust == fontName){
/* the user probably loaded a bunch of different fonts that will
* never be used again, so clear the font cache
* TODO: dont clear the currently selected font
*/
FontFactory::clear();
}
}
OptionSelectFont::~OptionSelectFont(){
// Nothing
}
void OptionSelectFont::logic(){
/* FIXME Get current font and display info */
switch (typeAdjust){
case fontName:{
std::string name;
if (Configuration::hasMenuFont()){
name = Configuration::getMenuFont()->getName();
} else {
name = "Default";
}
setText("Current Font: " + name);
break;
}
case fontWidth:{
ostringstream temp;
temp << "Font Width: " << Configuration::getMenuFontWidth();
setText(temp.str());
break;
}
case fontHeight:{
ostringstream temp;
temp << "Font Height: " << Configuration::getMenuFontHeight();
setText(temp.str());
break;
}
default: break;
}
if (lblue < 255){
lblue += 5;
}
if (rblue < 255){
rblue += 5;
}
if (lgreen < 255){
lgreen += 5;
}
if (rgreen < 255){
rgreen += 5;
}
}
void OptionSelectFont::run(const Menu::Context & context){
// throw Menu::MenuException(__FILE__, __LINE__);
/* throw something to quit back to the previous menu */
}
bool OptionSelectFont::leftKey(){
switch (typeAdjust){
case fontName:
nextIndex(false);
break;
case fontWidth:
Configuration::setMenuFontWidth(Configuration::getMenuFontWidth() - 1);
break;
case fontHeight:
Configuration::setMenuFontHeight(Configuration::getMenuFontHeight() - 1);
break;
default:
break;
}
lblue = lgreen = 0;
return true;
}
bool OptionSelectFont::rightKey(){
switch (typeAdjust){
case fontName:
nextIndex(true);
break;
case fontWidth:
Configuration::setMenuFontWidth(Configuration::getMenuFontWidth() + 1);
break;
case fontHeight:
Configuration::setMenuFontHeight(Configuration::getMenuFontHeight() + 1);
break;
default:
break;
}
rblue = rgreen = 0;
return true;
}
static bool saneFont(const Util::ReferenceCount<Menu::FontInfo> & info){
class Context: public Loader::LoadingContext {
public:
Context(const Util::ReferenceCount<Menu::FontInfo> & info):
info(info),
isok(false){
}
bool ok(){
try{
const Font & font = info->get();
return font.textLength("A") != 0 &&
font.getHeight() != 0;
} catch (const Exception::Base & ignore){
return true;
}
}
virtual void load(){
isok = ok();
}
const Util::ReferenceCount<Menu::FontInfo> & info;
bool isok;
};
if (info == NULL){
return true;
}
Context context(info);
/* an empty Info object, we don't really care about it */
Loader::Info level("Loading Font", Filesystem::AbsolutePath());
Loader::loadScreen(context, level, Loader::SimpleCircle);
return context.isok;
}
void OptionSelectFont::nextIndex(bool forward){
if (fonts.size() == 0){
return;
}
int index = 0;
for (unsigned int i = 0 ; i < fonts.size() ; ++i){
if ((Configuration::getMenuFont() == NULL && fonts[i] == NULL) ||
((Configuration::getMenuFont() != NULL && fonts[i] != NULL) &&
(*Configuration::getMenuFont() == *fonts[i]))){
index = i;
}
}
if (forward){
index++;
if (index >= (int) fonts.size()){
index = 0;
}
} else {
index--;
if (index < 0){
index = (int)fonts.size()-1;
}
}
while (!saneFont(fonts[index])){
Global::debug(0) << "Warning: erasing font `" << fonts[index]->getName() << "'" << endl;
int where = 0;
vector<Util::ReferenceCount<Menu::FontInfo> >::iterator it;
for (it = fonts.begin(); it != fonts.end() && where != index; it++, where++){
}
fonts.erase(it);
if (index >= (int) fonts.size()){
index = fonts.size() - 1;
}
}
Configuration::setMenuFont(fonts[index]);
/* FIXME */
/*
if (fonts[index] == "Default"){
Configuration::setMenuFont("");
} else {
Configuration::setMenuFont(fonts[index]);
}
*/
}
OptionSpeed::OptionSpeed(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
name(""),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if ( *token != "speed" )
throw LoadException(__FILE__, __LINE__, "Not speed option");
readName(token);
}
OptionSpeed::~OptionSpeed(){
// Nothing
}
std::string OptionSpeed::getText() const {
ostringstream out;
out << MenuOption::getText() << ": " << Configuration::getGameSpeed();
return out.str();
}
void OptionSpeed::logic(){
/*
//ostringstream temp;
char temp[255];
sprintf( temp, "%s: %0.2f", name.c_str(), MenuGlobals::getGameSpeed() );
setText(std::string(temp));
*/
}
void OptionSpeed::run(const Menu::Context & context){
}
bool OptionSpeed::leftKey(){
Configuration::setGameSpeed(Configuration::getGameSpeed() - 0.05);
if (Configuration::getGameSpeed() < 0.1){
Configuration::setGameSpeed(0.1);
}
return false;
}
bool OptionSpeed::rightKey(){
Configuration::setGameSpeed(Configuration::getGameSpeed() + 0.05);
rblue = rgreen = 0;
return false;
}
OptionTabMenu::OptionTabMenu(const Gui::ContextBox & parent, const Token *token, const Menu::OptionFactory & factory):
MenuOption(parent, token),
menu(0){
if (token->numTokens() == 1){
std::string temp;
token->view() >> temp;
menu = new Menu::Menu(Storage::instance().find(Filesystem::RelativePath(temp)), factory, Menu::Renderer::Tabbed);
} else {
menu = new Menu::Menu(token, factory, Menu::Renderer::Tabbed);
}
// this->setText(menu->getName());
// token->print("Menu: ");
const Token * tok = token->findToken("_/name");
if (tok != NULL){
std::string name;
tok->view() >> name;
// Global::debug(0, "menu") << "Menu name: " << name << endl;
this->setText(name);
} else {
// No name?
throw LoadException(__FILE__, __LINE__, "Menu has no name");
}
}
OptionTabMenu::~OptionTabMenu(){
// Delete our menu
if (menu){
delete menu;
}
}
void OptionTabMenu::logic(){
// Nothing
}
void OptionTabMenu::run(const Menu::Context & context){
// Do our new menu
// menu->run(context);
try{
menu->run(context);
} catch (const Exception::Return ignore){
throw Menu::Reload(__FILE__, __LINE__);
}
}
OptionSound::OptionSound(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if (*token != "sound" ){
throw LoadException(__FILE__, __LINE__, "Not a sound option");
}
readName(token);
originalName = getName();
}
OptionSound::~OptionSound(){
}
void OptionSound::logic(){
ostringstream temp;
temp << originalName << ": " << Configuration::getSoundVolume();
setText(temp.str());
}
void OptionSound::run(const Menu::Context & context){
}
void OptionSound::changeSound(int much){
int volume = Configuration::getSoundVolume();
volume += much;
if (volume < 0){
volume = 0;
}
if (volume > 100){
volume = 100;
}
Configuration::setSoundVolume(volume);
}
bool OptionSound::leftKey(){
changeSound(-1);
lblue = lgreen = 0;
return true;
}
bool OptionSound::rightKey(){
changeSound(+1);
rblue = rgreen = 0;
return true;
}
OptionMusic::OptionMusic(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token),
lblue(255),
lgreen(255),
rblue(255),
rgreen(255){
setRunnable(false);
if (*token != "music" ){
throw LoadException(__FILE__, __LINE__, "Not a music option");
}
readName(token);
originalName = getName();
}
void OptionMusic::logic(){
ostringstream temp;
temp << originalName << ": " << Configuration::getMusicVolume();
setText(temp.str());
}
void OptionMusic::run(const Menu::Context & context){
}
void OptionMusic::changeMusic(int much){
int volume = Configuration::getMusicVolume();
volume += much;
if (volume < 0){
volume = 0;
}
if (volume > 100){
volume = 100;
}
Configuration::setMusicVolume(volume);
Music::setVolume((double) volume / 100.0);
}
bool OptionMusic::leftKey(){
changeMusic(-1);
lblue = lgreen = 0;
return true;
}
bool OptionMusic::rightKey(){
changeMusic(+1);
lblue = lgreen = 0;
return true;
}
OptionMusic::~OptionMusic(){
}
OptionLanguage::OptionLanguage(const Gui::ContextBox & parent, const Token * token):
MenuOption(parent, token){
readName(token);
#if 0
const Token * start = token->getRootParent();
vector<const Token*> tokens = start->findTokens("*/language");
vector<string> all;
for (vector<const Token*>::iterator it = tokens.begin(); it != tokens.end(); it++){
string language;
const Token * token = *it;
if (token->match("language", language)){
all.push_back(language);
}
}
sort(all.begin(), all.end());
unique_copy(all.begin(), all.end(), back_insert_iterator<vector<string> >(languages));
// Global::debug(0) << "Found " << languages.size() << " languages" << endl;
#endif
}
void OptionLanguage::run(const Menu::Context & context){
class LanguageOption: public MenuOption {
public:
LanguageOption(const Gui::ContextBox & parent, const string & language):
MenuOption(parent, NULL){
setText(language);
setInfoText(language);
}
virtual void logic(){
}
virtual void run(const ::Menu::Context & context){
Configuration::setLanguage(getText());
Configuration::saveConfiguration();
throw ::Menu::MenuException(__FILE__, __LINE__);
}
};
Util::NewReferenceCount<Menu::DefaultRenderer> renderer;
- Menu::Menu temp(renderer.convert<Menu::Renderer>());
+ Menu::Menu temp(renderer);
Util::ReferenceCount<Menu::FontInfo> info(new Menu::RelativeFontInfo(Font::getDefaultFontPath(), 24, 24));
temp.setFont(info);
const Gui::ContextBox & box = renderer->getBox();
vector<string> languages = context.getLanguages();
for (vector<string>::iterator it = languages.begin(); it != languages.end(); it++){
temp.addOption(new LanguageOption(box, *it));
}
try {
temp.run(context);
} catch (const Exception::Return & ignore){
} catch (const Menu::MenuException & ex){
}
throw Menu::Reload(__FILE__, __LINE__);
// throw Exception::Return(__FILE__, __LINE__);
}
void OptionLanguage::logic(){
}
OptionJoystick::OptionJoystick(const Gui::ContextBox & parent, const Token *token):
MenuOption(parent, token){
setRunnable(true);
if (*token != "joystick" ){
throw LoadException(__FILE__, __LINE__, "Not a joystick option");
}
readName(token);
}
void OptionJoystick::logic(){
}
class JoystickLogicDraw: public Util::Logic, public Util::Draw {
public:
enum Inputs{
Exit
};
static const int marginX = 20;
JoystickLogicDraw(int id, const Util::ReferenceCount<Joystick> & joystick, const ::Menu::Context & context):
id(id),
joystick(joystick),
quit(false),
context(context, Menu::Context()){
input.set(Keyboard::Key_ESC, Exit);
}
const int id;
Util::ReferenceCount<Joystick> joystick;
bool quit;
Menu::Context context;
InputMap<Inputs> input;
void doInput(){
vector<InputMap<Inputs>::InputEvent> out = InputManager::getEvents(input, InputSource(true));
for (vector<InputMap<Inputs>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<Inputs>::InputEvent & event = *it;
if (event.enabled){
if (event.out == Exit){
quit = true;
// context.finish();
}
}
}
}
virtual void run(){
doInput();
context.act();
}
bool done(){
return quit;
}
double ticks(double system){
return system * Global::ticksPerSecond(60);
}
void drawButtons(const Font & font, const Graphics::Bitmap & buffer, int y){
Graphics::Color color = Graphics::makeColor(255, 255, 255);
font.printf(marginX, y, color, buffer, "Up: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Down: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Left: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Right: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button1: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button2: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button3: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button4: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button5: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Button6: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Select: ", 0); y += font.getHeight() + 5;
font.printf(marginX, y, color, buffer, "Quit: ", 0); y += font.getHeight() + 5;
}
void draw(const Graphics::Bitmap & buffer){
const Font & font = Menu::menuFontParameter.current()->get();
Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
work.start();
context.renderBackground(work);
/* FIXME: scale the joystck name down to fit */
font.printf(marginX, 1, Graphics::makeColor(255, 255, 255), work, "Joystick %d: %s", 0, id, joystick->getName().c_str());
drawButtons(font, work, 1 + font.getHeight() + 5);
context.renderForeground(work);
work.finish();
}
};
namespace{
struct Axis{
Axis():
stick(0),
axis(0),
first(0),
set(false),
last(0),
lastMotion(0){
}
int stick;
int axis;
/* first value from this axis. we assume that
* the first value is sufficiently close to 'zero' which
* can be any value, but most likely will either be
* -1, 0, or 1
*/
double first;
/* true if first has been set */
bool set;
/* keep track of last value for this axis */
double last;
/* last time an event was produced (or at least last
* time we read it). it might be better to get the actual
* time from the event itself.
*/
uint64_t lastMotion;
};
}
static void runJoystickMenu(int joystickId, const Util::ReferenceCount<Joystick> & joystick, const ::Menu::Context & context){
Util::NewReferenceCount<Menu::DefaultRenderer> renderer;
- Menu::Menu menu(renderer.convert<Menu::Renderer>());
+ Menu::Menu menu(renderer);
Gui::ContextBox & box = renderer->getBox();
box.setListType(ContextBox::Normal);
Gui::ListValues attributes(box.getListValues());
attributes.setDistanceFade(false);
box.setListValues(attributes);
menu.setPosition(Gui::Coordinate(Gui::RelativePoint(-0.8, -0.3),
Gui::RelativePoint(0, 0.8)));
#define WAIT_TIME_MS (0.7 * 1000)
#define WAIT_TIME_AXIS_MS (1 * 1000)
#define AXIS_THRESHOLD 0.7
class JoystickButton: public MenuOption {
public:
JoystickButton(const Menu::Menu & menu, const Gui::ContextBox & parent, const Util::ReferenceCount<Joystick> & joystick, const string & name, Joystick::Key key):
MenuOption(parent, NULL),
menu(menu),
name(name),
joystick(joystick),
key(key){
setText(name);
setInfoText(name);
}
const Menu::Menu & menu;
string name;
Util::ReferenceCount<Joystick> joystick;
Joystick::Key key;
class ButtonListener: public JoystickListener {
public:
ButtonListener(const Util::ReferenceCount<Joystick> & joystick):
touched(false),
done(false),
chosen(-1),
chosenAxis(NULL){
map<int, map<int, double> > axisValues = joystick->getCurrentAxisValues();
for (map<int, map<int, double> >::iterator it = axisValues.begin(); it != axisValues.end(); it++){
int stick = it->first;
const map<int, double> & subMap = it->second;
for (map<int, double>::const_iterator it = subMap.begin(); it != subMap.end(); it++){
int axis = it->first;
double value = it->second;
Axis use;
use.stick = stick;
use.axis = axis;
use.first = value;
use.set = true;
use.last = value;
use.lastMotion = 0;
this->axis.push_back(use);
}
}
}
map<int, uint64_t> presses;
map<int, bool> pressed;
/* true if a button/axis is moved. flips to false when queried */
bool touched;
vector<Axis> axis;
bool done;
int chosen;
Axis * chosenAxis;
bool wasTouched(){
bool out = touched;
touched = false;
return out;
}
Axis & getAxis(int stick, int axis){
for (vector<Axis>::iterator it = this->axis.begin(); it != this->axis.end(); it++){
Axis & use = *it;
if (use.stick == stick && use.axis == axis){
return use;
}
}
Axis out;
out.stick = stick;
out.axis = axis;
this->axis.push_back(out);
return getAxis(stick, axis);
}
const vector<Axis> & getAllAxis() const {
return axis;
}
const map<int, uint64_t> & getPresses() const {
return presses;
}
int getButton() const {
return chosen;
}
Axis * getChosenAxis() const {
return chosenAxis;
}
virtual ~ButtonListener(){
}
bool isDone() const {
return (getButton() != -1 && !anyPressed()) ||
(getChosenAxis() != NULL);
}
void choose(){
uint64_t now = System::currentMilliseconds();
for (map<int, uint64_t>::const_iterator it = presses.begin(); it != presses.end(); it++){
uint64_t what = it->second;
if (what != 0 && now - what > WAIT_TIME_MS){
chosen = it->first;
}
}
for (vector<Axis>::iterator it = axis.begin(); it != axis.end(); it++){
Axis & use = *it;
if (fabs(use.last - use.first) > AXIS_THRESHOLD &&
now - use.lastMotion > WAIT_TIME_AXIS_MS){
chosenAxis = &use;
}
}
}
bool anyPressed() const {
for (map<int, bool>::const_iterator it = pressed.begin(); it != pressed.end(); it++){
if (it->second){
return true;
}
}
return false;
}
virtual void pressButton(Joystick * from, int button){
touched = true;
pressed[button] = true;
presses[button] = System::currentMilliseconds();
}
virtual void releaseButton(Joystick * from, int button){
presses[button] = 0;
pressed[button] = false;
}
/* either all increasing or all decreasing */
bool monotonic(const vector<double> & what){
if (what.size() == 0){
return true;
}
double first = what[0];
int direction = -1;
vector<double>::const_iterator it = what.begin();
it++;
for (/**/; it != what.end(); it++){
switch (direction){
case -1: {
if (*it < first){
direction = 1;
} else if (*it > first){
direction = 2;
}
first = *it;
break;
}
case 1: {
if (*it > first){
return false;
}
break;
}
case 2: {
if (*it < first){
return false;
}
break;
}
}
}
return true;
}
virtual void axisMotion(Joystick * from, int stick, int axis, double motion){
touched = true;
Axis & use = getAxis(stick, axis);
if (!use.set){
use.first = motion;
use.set = true;
}
use.last = motion;
use.lastMotion = System::currentMilliseconds();
/*
const double AXIS_THRESHOLD = 0.5;
Global::debug(0) << "stick " << stick << " axis " << axis << " first " << use.first << " last " << use.last << " diff " << fabs(use.last - use.first) << std::endl;
if (fabs(use.last - use.first) > AXIS_THRESHOLD){
Global::debug(0) << "stick " << stick << " axis " << axis << " motion " << motion << std::endl;
}
*/
}
virtual void hatMotion(Joystick * from, int motion){
}
};
void logic(){
ostringstream out;
int stick, axis;
double low, high;
if (joystick->getAxis(key, stick, axis, low, high)){
out << name << ": " << stick << "/" << axis << "/";
if (low < 0){
out << "-";
} else {
out << "+";
}
} else {
int button = joystick->getButton(key);
out << name << ": ";
if (button != -1){
out << joystick->getButton(key);
} else {
out << "unset";
}
}
setText(out.str());
}
void run(const Menu::Context & context){
class SetButton: public Util::Logic, public Util::Draw {
public:
SetButton(const Menu::Context & context, const Menu::Menu & menu, const string & name, const Util::ReferenceCount<Joystick> & joystick):
context(context),
menu(menu),
name(name),
listener(joystick),
joystick(joystick),
startingTime(System::currentMilliseconds()),
maxTime(5000){
input.set(Keyboard::Key_ESC, 0);
joystick->addListener(&listener);
vector<Graphics::BlendPoint> points;
points.push_back(Graphics::BlendPoint(Graphics::makeColor(255, 0, 0), 30));
points.push_back(Graphics::BlendPoint(Graphics::makeColor(0, 255, 0), 25));
points.push_back(Graphics::BlendPoint(Graphics::makeColor(255, 255, 255), 0));
colors = Graphics::blend_palette(points);
}
const Menu::Context & context;
const Menu::Menu & menu;
string name;
ButtonListener listener;
InputMap<int> input;
Util::ReferenceCount<Joystick> joystick;
vector<Graphics::Color> colors;
uint64_t startingTime;
const uint64_t maxTime;
virtual ~SetButton(){
joystick->removeListener(&listener);
}
virtual void showTimeLeft(const Graphics::Bitmap & screen){
int x1 = 1;
int y1 = 1;
int x2 = screen.getWidth() * (1 - (double) (System::currentMilliseconds() - startingTime) / (double) maxTime);
if (x2 < x1){
x2 = x1;
}
int y2 = 10;
int color = (1.0 - (double) (System::currentMilliseconds() - startingTime) / maxTime) * colors.size();
if (color < 0){
color = 0;
}
if (color >= colors.size()){
color = colors.size() - 1;
}
screen.rectangleFill(x1, y1, x2, y2, colors[color]);
}
void setButton(Joystick::Key key){
int button = listener.getButton();
if (button != -1){
Global::debug(1) << "Chosen button " << listener.getButton() << std::endl;
joystick->setCustomButton(listener.getButton(), key);
} else {
Axis * axis = listener.getChosenAxis();
double rangeLow = 0;
double rangeHigh = 0;
/* stick went negative and went to -1 */
if (axis->first <= 0 && axis->last < axis->first){
rangeLow = -1;
rangeHigh = -AXIS_THRESHOLD;
/* stick started at negative and went positive, possibly
* not above 0.
*/
} else if (axis->first < 0 && axis->last > axis->first){
rangeLow = 0;
rangeHigh = 1;
/* stick started positive and went to 1 */
} else if (axis->first >= 0 && axis->last > axis->first){
rangeLow = AXIS_THRESHOLD;
rangeHigh = 1;
/* stick started positive and went towards -1 */
} else if (axis->first > 0 && axis->last < axis->first){
rangeLow = -1;
rangeHigh = 0;
}
Global::debug(0) << "Set stick " << axis->stick << " axis " << axis->axis << " [" << rangeLow << ", " << rangeHigh << "]" << std::endl;
joystick->setCustomAxis(key, axis->stick, axis->axis, rangeLow, rangeHigh);
}
}
double ticks(double system){
return system * Global::ticksPerSecond(60);
}
bool done(){
return listener.isDone();
}
void run(){
vector<InputMap<int>::InputEvent> out = InputManager::getEvents(input, InputSource(true));
for (vector<InputMap<int>::InputEvent>::iterator it = out.begin(); it != out.end(); it++){
const InputMap<int>::InputEvent & event = *it;
if (event.enabled){
if (event.out == 0){
throw Exception::Return(__FILE__, __LINE__);
}
}
}
listener.choose();
if (listener.wasTouched()){
startingTime = System::currentMilliseconds();
}
if (System::currentMilliseconds() - startingTime >= maxTime){
throw Exception::Return(__FILE__, __LINE__);
}
}
void draw(const Graphics::Bitmap & buffer){
Graphics::StretchedBitmap work(640, 480, buffer, Graphics::StretchedBitmap::NoClear, Graphics::qualityFilterName(Configuration::getQualityFilter()));
work.start();
menu.render(context, work);
const Font & font = Menu::menuFontParameter.current()->get();
Gui::RelativePoint start(0.2, -0.3);
Gui::RelativePoint end(0.85, 0.8);
int x = start.getX();
int y = start.getY();
// font.printfWrap(x, y - font.getHeight() * 2 - 5, Graphics::makeColor(255, 255, 255), work, end.getX() - start.getX(), "Press and hold a button", 0);
font.printf(x, y - 18 - 5, 18, 18, Graphics::makeColor(255, 255, 255), work, "Press and hold a button", 0);
work.translucent(0, 0, 0, 128).rectangleFill(x, y, end.getX(), end.getY(), Graphics::makeColor(0, 0, 0));
uint64_t now = System::currentMilliseconds();
const map<int, uint64_t> & presses = listener.getPresses();
for (map<int, uint64_t>::const_iterator it = presses.begin(); it != presses.end(); it++){
int button = it->first;
uint64_t time = it->second;
if (time > 0){
int delta = now - time;
if (delta > WAIT_TIME_MS){
delta = WAIT_TIME_MS;
}
/* this shouldn't happen... */
if (delta < 0){
delta = 0;
}
Graphics::Color color;
color = Graphics::makeColor((int)(255.0 * (double) delta / (WAIT_TIME_MS)),
0, 255);
if (button == listener.getButton()){
color = Graphics::makeColor(255, 255, 255);
}
ostringstream text;
text << name << ": " << button;
font.printf(x, y, color, work, text.str(), 0);
y += font.getHeight() + 5;
}
}
const vector<Axis> & axis = listener.getAllAxis();
for (vector<Axis>::const_iterator it = axis.begin(); it != axis.end(); it++){
const Axis & use = *it;
if (fabs(use.last - use.first) > AXIS_THRESHOLD){
int delta = now - use.lastMotion;
if (delta > WAIT_TIME_AXIS_MS){
delta = WAIT_TIME_AXIS_MS;
}
/* this shouldn't happen... */
if (delta < 0){
delta = 0;
}
Graphics::Color color;
color = Graphics::makeColor((int)(255.0 * (double) delta / (WAIT_TIME_AXIS_MS)),
0, 255);
if (&use == listener.getChosenAxis()){
color = Graphics::makeColor(255, 255, 255);
}
ostringstream text;
text << name << ": stick " << use.stick << " axis " << use.axis;
if (use.last > use.first){
text << " +";
} else {
text << " -";
}
font.printf(x, y, 18, 18, color, work, text.str(), 0);
y += font.getHeight() + 5;
}
}
showTimeLeft(work);
work.finish();
}
void wait(){
while (listener.anyPressed()){
InputManager::poll();
Util::rest(1);
}
}
};
Global::debug(1) << "Set button " << getName() << std::endl;
SetButton set(context, menu, name, joystick);
try{
Util::standardLoop(set, set);
set.setButton(key);
} catch (const Exception::Return & quit){
}
set.wait();
}
};
menu.addOption(new JoystickButton(menu, box, joystick, "Up", Joystick::Up));
menu.addOption(new JoystickButton(menu, box, joystick, "Down", Joystick::Down));
menu.addOption(new JoystickButton(menu, box, joystick, "Left", Joystick::Left));
menu.addOption(new JoystickButton(menu, box, joystick, "Right", Joystick::Right));
menu.addOption(new JoystickButton(menu, box, joystick, "Button1", Joystick::Button1));
menu.addOption(new JoystickButton(menu, box, joystick, "Button2", Joystick::Button2));
menu.addOption(new JoystickButton(menu, box, joystick, "Button3", Joystick::Button3));
menu.addOption(new JoystickButton(menu, box, joystick, "Button4", Joystick::Button4));
menu.addOption(new JoystickButton(menu, box, joystick, "Button5", Joystick::Button5));
menu.addOption(new JoystickButton(menu, box, joystick, "Button6", Joystick::Button6));
menu.addOption(new JoystickButton(menu, box, joystick, "Start", Joystick::Start));
menu.addOption(new JoystickButton(menu, box, joystick, "Quit", Joystick::Quit));
try {
menu.run(context);
} catch (const Exception::Return & ignore){
} catch (const Menu::MenuException & ex){
}
}
void OptionJoystick::run(const Menu::Context & context){
class JoystickOption: public MenuOption {
public:
JoystickOption(const Gui::ContextBox & parent, int id, const Util::ReferenceCount<Joystick> & joystick):
MenuOption(parent, NULL),
joystick(joystick),
id(id){
ostringstream out;
out << "Joystick " << (id + 1);
setText(out.str());
setInfoText(joystick->getName());
}
const Util::ReferenceCount<Joystick> joystick;
const int id;
virtual void logic(){
}
virtual void run(const ::Menu::Context & context){
runJoystickMenu(id, joystick, context);
/*
JoystickLogicDraw mainLoop(id, joystick, context);
Util::standardLoop(mainLoop, mainLoop);
*/
throw ::Menu::MenuException(__FILE__, __LINE__);
}
};
Util::NewReferenceCount<Menu::DefaultRenderer> renderer;
- Menu::Menu menu(renderer.convert<Menu::Renderer>());
+ Menu::Menu menu(renderer);
/*
Util::ReferenceCount<Menu::FontInfo> info(new Menu::RelativeFontInfo(Global::DEFAULT_FONT, 24, 24));
temp.setFont(info);
*/
Gui::ContextBox & box = renderer->getBox();
box.setListType(ContextBox::Normal);
map<int, Util::ReferenceCount<Joystick> > joysticks = InputManager::getJoysticks();
for (map<int, Util::ReferenceCount<Joystick> >::iterator it = joysticks.begin(); it != joysticks.end(); it++){
menu.addOption(new JoystickOption(box, it->first, it->second));
}
if (joysticks.size() == 0){
menu.addOption(new OptionDummy(box, "No joysticks found!"));
}
try {
menu.run(context);
} catch (const Exception::Return & ignore){
} catch (const Menu::MenuException & ex){
}
}
OptionJoystick::~OptionJoystick(){
}
diff --git a/util/network/irc.cpp b/util/network/irc.cpp
index 0c93d38d..a6959a6e 100644
--- a/util/network/irc.cpp
+++ b/util/network/irc.cpp
@@ -1,1338 +1,1338 @@
#include "irc.h"
#include "util/funcs.h"
#include "util/font.h"
#include "util/regex.h"
#include "util/system.h"
#include "util/graphics/bitmap.h"
#include "util/configuration.h"
#include "util/gui/context-box.h"
#include "util/timedifference.h"
#include <stdexcept>
#include <queue>
namespace Network{
namespace IRC{
static Command::Type convertCommand(const std::string & cmd){
Command::Type command = Command::Unknown;
if (cmd == "PASS"){
command = Command::Pass;
} else if (cmd == "NICK"){
command = Command::Nick;
} else if (cmd == "USER"){
command = Command::User;
} else if (cmd == "USERHOST"){
command = Command::Userhost;
} else if (cmd == "SERVER"){
command = Command::Server;
} else if (cmd == "OPER"){
command = Command::Oper;
} else if (cmd == "QUIT"){
command = Command::Quit;
} else if (cmd == "SQUIT"){
command = Command::Squit;
} else if (cmd == "JOIN"){
command = Command::Join;
} else if (cmd == "PART"){
command = Command::Part;
} else if (cmd == "MODE"){
command = Command::Mode;
} else if (cmd == "TOPIC"){
command = Command::Topic;
} else if (cmd == "NAMES"){
command = Command::Names;
} else if (cmd == "LIST"){
command = Command::List;
} else if (cmd == "INVITE"){
command = Command::Invite;
} else if (cmd == "KICK"){
command = Command::Kick;
} else if (cmd == "VERSION"){
command = Command::Version;
} else if (cmd == "STATS"){
command = Command::Stats;
} else if (cmd == "LINKS"){
command = Command::Links;
} else if (cmd == "TIME"){
command = Command::Time;
} else if (cmd == "CONNECT"){
command = Command::Connect;
} else if (cmd == "TRACE"){
command = Command::Trace;
} else if (cmd == "ADMIN"){
command = Command::Admin;
} else if (cmd == "INFO"){
command = Command::Info;
} else if (cmd == "PRIVMSG"){
command = Command::PrivateMessage;
} else if (cmd == "NOTICE"){
command = Command::Notice;
} else if (cmd == "WHO"){
command = Command::Who;
} else if (cmd == "WHOIS"){
command = Command::Whois;
} else if (cmd == "WHOWAS"){
command = Command::Whowas;
} else if (cmd == "KILL"){
command = Command::Kill;
} else if (cmd == "PING"){
command = Command::Ping;
} else if (cmd == "PONG"){
command = Command::Pong;
} else if (cmd == "ERROR"){
command = Command::Error;
} else if (cmd == "433"){
command = Command::ErrorNickInUse;
} else if (cmd == "401"){
command = Command::ErrorNoSuchNick;
} else if (cmd == "403"){
command = Command::ErrorNoSuchChannel;
} else if (cmd == "461"){
command = Command::ErrorNeedMoreParams;
} else if (cmd == "473"){
command = Command::ErrorInviteOnlyChannel;
} else if (cmd == "474"){
command = Command::ErrorBannedFromChannel;
} else if (cmd == "475"){
command = Command::ErrorBadChannelKey;
} else if (cmd == "471"){
command = Command::ErrorChannelIsFull;
} else if (cmd == "302"){
command = Command::ReplyUserhost;
} else if (cmd == "331"){
command = Command::ReplyNoTopic;
} else if (cmd == "332"){
command = Command::ReplyTopic;
} else if (cmd == "333"){
command = Command::ReplyTopicAuthor;
} else if (cmd == "353"){
command = Command::ReplyNames;
} else if (cmd == "366"){
command = Command::ReplyNamesEndOf;
} else if (cmd == "372"){
command = Command::ReplyMOTD;
} else if (cmd == "375"){
command = Command::ReplyMOTDStart;
} else if (cmd == "376"){
command = Command::ReplyMOTDEndOf;
}
return command;
}
static std::string convertCommand(const Command::Type & cmd){
switch (cmd){
case Command::Pass: return "PASS";
case Command::Nick: return "NICK";
case Command::User: return "USER";
case Command::Userhost: return "USERHOST";
case Command::Server: return "SERVER";
case Command::Oper: return "OPER";
case Command::Quit: return "QUIT";
case Command::Squit: return "SQUIT";
case Command::Join: return "JOIN";
case Command::Part: return "PART";
case Command::Mode: return "MODE";
case Command::Topic: return "TOPIC";
case Command::Names: return "NAMES";
case Command::List: return "LIST";
case Command::Invite: return "INVITE";
case Command::Kick: return "KICK";
case Command::Version: return "VERSION";
case Command::Stats: return "STATS";
case Command::Links: return "LINKS";
case Command::Time: return "TIME";
case Command::Connect: return "CONNECT";
case Command::Trace: return "TRACE";
case Command::Admin: return "ADMIN";
case Command::Info: return "INFO";
case Command::PrivateMessage: return "PRIVMSG";
case Command::Notice: return "NOTICE";
case Command::Who: return "WHO";
case Command::Whois: return "WHOIS";
case Command::Whowas: return "WHOAS";
case Command::Kill: return "KILL";
case Command::Ping: return "PING";
case Command::Pong: return "PONG";
case Command::Error: return "ERROR";
case Command::Unknown:
default:
break;
}
return "";
}
static std::vector<std::string> split(std::string str, char splitter){
std::vector<std::string> strings;
size_t next = str.find(splitter);
while (next != std::string::npos){
strings.push_back(str.substr(0, next));
str = str.substr(next+1);
next = str.find(splitter);
}
if (str != ""){
strings.push_back(str);
}
return strings;
}
Command::Command(const std::string & message){
std::vector< std::string > messageSplit = split(message, ' ');
std::vector< std::string >::iterator current = messageSplit.begin();
if (Util::matchRegex(*current, Util::Regex("^:.*"))){
// Found owner (":") indicates the user, otherwise it's going to be the command
// Grab just the username, ignore everything else
try{
owner = split(*current, '!').at(0).substr(1);
} catch (const std::out_of_range & ex){
}
current++;
}
// Next is the actual command
type = convertCommand(*current);
if (type == Unknown){
Global::debug(0) << "Got unhandled response: " << message << std::endl;
}
current++;
// Parameters
bool foundCtcp = false;
bool concactenate = false;
std::string concactenated;
for (std::vector< std::string >::iterator i = current; i != messageSplit.end(); ++i){
const std::string & parameter = *i;
// If there is a colon in the parameter the rest of split string is the whole parameter rejoin
if (Util::matchRegex(parameter, Util::Regex("^:\001.*")) && !foundCtcp){
foundCtcp = true;
// Drop the ':\001'
ctcp.push_back(parameter.substr(2));
continue;
} else if (Util::matchRegex(parameter, Util::Regex(".*\001")) && foundCtcp){
foundCtcp = false;
// Drop the '\001'
ctcp.push_back(parameter.substr(0, parameter.size()-1));
continue;
} else if (Util::matchRegex(parameter, Util::Regex("^:.*")) && !concactenate){
concactenate = true;
// Drop the ':'
concactenated += parameter.substr(1) + " ";
continue;
} else if (Util::matchRegex(parameter, Util::Regex("=")) ||
Util::matchRegex(parameter, Util::Regex("@"))){
// Ignore
continue;
}
if (concactenate){
concactenated += parameter + " ";
} else {
if (!foundCtcp){
parameters.push_back(parameter);
} else {
ctcp.push_back(parameter);
}
}
}
if (concactenate){
parameters.push_back(concactenated);
}
}
Command::Command(const std::string & owner, const Type & type):
owner(owner),
type(type){
}
Command::Command(const Command & copy):
owner(copy.owner),
type(copy.type),
parameters(copy.parameters),
ctcp(copy.ctcp){
}
Command::~Command(){
}
const Command & Command::operator=(const Command & copy){
owner = copy.owner;
type = copy.type;
parameters = copy.parameters;
ctcp = copy.ctcp;
return *this;
}
std::string Command::getSendable() const {
std::string sendable;
// Name
if (!owner.empty()){
sendable += ":" + owner + " ";
}
// Command
sendable += convertCommand(type) + " ";
// Params
for (unsigned int i = 0; i < parameters.size(); ++i){
sendable += parameters[i] + (i < parameters.size()-1 ? " " : "");
}
// End
sendable += "\r\n";
return sendable;
}
std::string Command::getCTCPSendable() const {
std::string sendable;
// Name
if (!owner.empty()){
sendable += ":" + owner + " ";
}
// Command
sendable += convertCommand(type) + " ";
// Params
for (unsigned int i = 0; i < ctcp.size(); ++i){
sendable += ctcp[i] + (i < ctcp.size()-1 ? " " : "");
}
// End
sendable += "\r\n";
return sendable;
}
Channel::Channel(){
}
Channel::Channel(const std::string & name):
name(name){
}
Channel::Channel(const Channel & copy):
name(copy.name),
topic(copy.topic),
topicAuthor(copy.topicAuthor),
topicDate(copy.topicDate),
users(copy.users){
}
Channel::~Channel(){
}
const Channel & Channel::operator=(const Channel & copy){
name = copy.name;
topic = copy.topic;
topicAuthor = copy.topicAuthor;
topicDate = copy.topicDate;
users = copy.users;
return *this;
}
void Channel::addUser(const std::string & user){
// Can't add same user twice
for (std::vector<std::string>::iterator i = users.begin(); i != users.end(); ++i){
const std::string & name = *i;
if (name == user){
return;
}
}
users.push_back(user);
}
void Channel::removeUser(const std::string & user){
for (std::vector<std::string>::iterator i = users.begin(); i != users.end(); ++i){
const std::string & name = *i;
if (name == user){
users.erase(i);
break;
}
}
}
void Channel::replaceUser(const std::string & user, const std::string & newName){
for (std::vector<std::string>::iterator i = users.begin(); i != users.end(); ++i){
const std::string & name = *i;
if (name == user){
users.insert(users.erase(i), newName);
break;
}
}
}
void Channel::addUsers(const std::vector<std::string> & list){
for (std::vector<std::string>::const_iterator i = list.begin(); i != list.end(); ++i){
const std::string & name = *i;
addUser(name);
}
}
Client::Client(const std::string & hostname, int port):
previousUsername("AUTH"),
username("AUTH"),
previousActiveChannel(0),
currentChannel(0),
hostname(hostname),
port(port),
end(false),
disableMessages(true){
}
Client::~Client(){
}
void Client::connect(const std::string & name){
if (username.empty()){
throw NetworkException("Set username first.");
}
Global::debug(0) << "Connecting to " << hostname << " on port " << port << std::endl;
socket = Network::connectReliable(hostname, port);
start();
setName(name);
Command user("AUTH", Command::User);
user.setParameters(username, "*", "0", ":auth");
sendCommand(user);
// ^^^^^^^^ Should get a response from this crap!
Global::debug(0) << "Connected" << std::endl;
}
bool Client::hasCommands() const{
::Util::Thread::ScopedLock scope(lock);
return !commands.empty();
}
Command Client::nextCommand() const {
::Util::Thread::ScopedLock scope(lock);
Command command = commands.front();
commands.pop();
return command;
}
void Client::sendCommand(const Command & command){
const std::string & sendable = command.getSendable();
Network::sendBytes(socket, (uint8_t *) sendable.c_str(), sendable.size());
}
void Client::sendCommand(const Command::Type & type){
Command command(username, type);
sendCommand(command);
}
void Client::sendCommand(const Command::Type & type, const std::string & param1){
Command command(username, type);
command.setParameters(param1);
sendCommand(command);
}
void Client::sendCommand(const Command::Type & type, const std::string & param1, const std::string & param2){
Command command(username, type);
command.setParameters(param1, param2);
sendCommand(command);
}
void Client::sendCommand(const Command::Type & type, const std::string & param1, const std::string & param2, const std::string & param3){
Command command(username, type);
command.setParameters(param1, param2, param3);
sendCommand(command);
}
void Client::sendCommand(const Command::Type & type, const std::string & param1, const std::string & param2, const std::string & param3, const std::string & param4){
Command command(username, type);
command.setParameters(param1, param2, param3, param4);
sendCommand(command);
}
void Client::setName(const std::string & name){
previousUsername = username;
// Update channel list
for (std::vector< ChannelPointer >::iterator i = activeChannels.begin(); i != activeChannels.end(); ++i){
ChannelPointer activeChannel = *i;
activeChannel->removeUser(username);
activeChannel->addUser(name);
}
username = name;
sendCommand(Command::Nick, name);
}
void Client::joinChannel(const std::string & chan){
for (std::vector< ChannelPointer >::iterator i = activeChannels.begin(); i != activeChannels.end(); ++i){
ChannelPointer activeChannel = *i;
if (activeChannel->getName() == chan){
// Already belonging to this channel
return;
}
}
previousActiveChannel = currentChannel;
ChannelPointer newChannel = ChannelPointer(new Channel(chan));
activeChannels.push_back(newChannel);
currentChannel = activeChannels.size()-1;
sendCommand(Command::Join, getChannel()->getName());
}
void Client::partChannel(const std::string & channel){
for (std::vector< ChannelPointer >::iterator i = activeChannels.begin(); i != activeChannels.end(); ++i){
ChannelPointer activeChannel = *i;
if (activeChannel->getName() == channel){
// Remove channel
activeChannels.erase(i);
break;
}
}
currentChannel = previousActiveChannel;
sendCommand(Command::Part, channel);
}
std::string Client::channelListAsString(){
std::string list;
for (std::vector<ChannelPointer>::iterator i = activeChannels.begin(); i != activeChannels.end(); ++i){
ChannelPointer activeChannel = *i;
list += activeChannel->getName() + ", ";
}
return list.substr(0, list.size()-2);
}
unsigned int Client::getChannelIndex(const std::string & channel){
for (unsigned int i = 0; i < activeChannels.size(); ++i){
if (activeChannels[i]->getName() == channel){
return i;
}
}
return 0;
}
bool Client::isCurrentChannel(const std::string & channel){
return (activeChannels[currentChannel]->getName() == channel);
}
void Client::setChannel(unsigned int channel){
if (channel >= activeChannels.size()){
return;
}
currentChannel = channel;
}
void Client::nextChannel(){
currentChannel = (currentChannel + 1) % activeChannels.size();
}
void Client::previousChannel(){
if (currentChannel == 0){
currentChannel = activeChannels.size()-1;
} else {
currentChannel--;
}
}
void Client::removeChannel(const std::string & name){
for (std::vector< ChannelPointer >::iterator i = activeChannels.begin(); i != activeChannels.end(); ++i){
ChannelPointer activeChannel = *i;
if (activeChannel->getName() == name){
activeChannels.erase(i);
return;
}
}
}
ChannelPointer Client::findChannel(const std::string & name){
for (std::vector< ChannelPointer >::iterator i = activeChannels.begin(); i != activeChannels.end(); ++i){
ChannelPointer activeChannel = *i;
if (activeChannel->getName() == name){
return activeChannel;
}
}
return ChannelPointer(NULL);
}
void Client::sendMessage(const std::string & msg){
if (!disableMessages){
sendCommand(Command::PrivateMessage, getChannel()->getName(), ":" + msg);
}
}
void Client::sendPong(const Command & ping){
Command pong(username, Command::Pong);
pong.setParameters(ping.getParameters());
sendCommand(pong);
}
std::string Client::readMessage(){
std::string received;
bool foundReturn = false;
while (true){
try {
char nextCharacter = Network::read8(socket);
/* NOTE the latest RFC says that either \r or \n is the end of the message
* http://www.irchelp.org/irchelp/rfc/chapter8.html
*/
if (nextCharacter == '\r'){
// Found return
foundReturn = true;
continue;
} else if ((nextCharacter == '\n') && foundReturn){
// Should be the end of the message assuming \r is before it
break;
}
received += nextCharacter;
} catch (const Network::MessageEnd & ex){
// end of message get out
throw ex;
}
}
return received;
}
void Client::checkResponseAndHandle(const Command & command){
// Checks for username or channel errors
if (command.getType() == Command::ErrorNickInUse){
::Util::Thread::ScopedLock scope(lock);
// Change the username back to what it was
getChannel()->removeUser(username);
username = previousUsername;
getChannel()->addUser(username);
} else if (command.getType() == Command::ErrorBannedFromChannel ||
command.getType() == Command::ErrorInviteOnlyChannel ||
command.getType() == Command::ErrorBadChannelKey ||
command.getType() == Command::ErrorChannelIsFull ||
command.getType() == Command::ErrorNoSuchChannel){
::Util::Thread::ScopedLock scope(lock);
// Revert old channel
removeChannel(getChannel()->getName());
currentChannel = previousActiveChannel;
} else if (command.getType() == Command::ReplyTopic){
::Util::Thread::ScopedLock scope(lock);
// Set topic
getChannel()->setTopic(command.getParameters().at(2));
} else if (command.getType() == Command::ReplyTopicAuthor){
::Util::Thread::ScopedLock scope(lock);
// Set topic and author
const std::vector<std::string> & params = command.getParameters();
getChannel()->setTopicAuthor(split(params.at(1), '!').at(0), atoi(params.at(2).c_str()));
} else if (command.getType() == Command::ReplyNames){
// Add names
const std::vector<std::string> & params = command.getParameters();
const std::vector<std::string> & names = split(params.at(2), ' ');
::Util::Thread::ScopedLock scope(lock);
ChannelPointer update = findChannel(params.at(1));
if (update != NULL){
update->addUsers(names);
}
} else if (command.getType() == Command::Nick){
const std::vector<std::string> & params = command.getParameters();
::Util::Thread::ScopedLock scope(lock);
if (command.getOwner() != username){
// Replace in all valid channels
for (std::vector<ChannelPointer>::iterator i = activeChannels.begin(); i != activeChannels.end(); i++){
ChannelPointer update = *i;
if (update != NULL){
update->replaceUser(command.getOwner(), params.at(0));
}
}
}
} else if (command.getType() == Command::Join){
const std::vector<std::string> & params = command.getParameters();
::Util::Thread::ScopedLock scope(lock);
ChannelPointer update = findChannel(params.at(0));
if (update != NULL){
update->addUser(command.getOwner());
}
} else if (command.getType() == Command::Part){
const std::vector<std::string> & params = command.getParameters();
::Util::Thread::ScopedLock scope(lock);
ChannelPointer update = findChannel(params.at(0));
if (update != NULL){
update->removeUser(command.getOwner());
}
}
}
void Client::run(){
while (!end){
try {
const std::string & message = readMessage();
// Check if the message is empty it might be because of (\n)
if (!message.empty()){
Command command(message);
//Global::debug(0) << "Got message: " << command.getSendable() << std::endl;
::Util::Thread::ScopedLock scope(lock);
checkResponseAndHandle(command);
commands.push(command);
} else {
}
} catch (const Network::MessageEnd & ex){
end = true;
}
}
}
Message::EventInterface::EventInterface(){
}
Message::EventInterface::~EventInterface(){
}
void Message::EventInterface::EventInterface::localNotify(const std::string &){
}
void Message::EventInterface::EventInterface::localCommand(const std::vector<std::string> &){
}
void Message::EventInterface::EventInterface::remoteNotify(const std::string &){
}
void Message::EventInterface::EventInterface::remoteCommand(const Command &){
}
Message::HandlerInterface::HandlerInterface(){
}
Message::HandlerInterface::~HandlerInterface(){
}
Message::QueueInterface::QueueInterface(){
}
Message::QueueInterface::~QueueInterface(){
}
Message::QueueInterface::FontWrapper::FontWrapper(){
}
Message::QueueInterface::FontWrapper::~FontWrapper(){
}
void Message::QueueInterface::processMessages(Message::QueueInterface::FontWrapper & wrapper, int width, int height){
while (!messages.empty()){
const std::string message = messages.front();
messages.pop();
// Check message if it exceeds the length of the box so we can split it
if (wrapper.getWidth(message) > width-15){
unsigned int marker = 0;
unsigned int length = 0;
while ((marker+length) < message.size()){
//Global::debug(0) << "Substring: " << message.substr(marker, length) << " Marker: " << marker << " and Current length: " << length << std::endl;
if (wrapper.getWidth(message.substr(marker, length)) < width-15){
length++;
continue;
} else {
if (message[marker+length] == ' '){
buffer.push_front(message.substr(marker, length));
marker += length+1;
length = 0;
} else {
// Search for previous space
unsigned int cutoff = marker+length;
while ((marker+length) > marker){
if (message[marker+length] == ' '){
break;
}
length--;
}
if ((marker+length) > marker){
buffer.push_front(message.substr(marker, length));
marker += length+1;
length = 0;
} else {
buffer.push_front(message.substr(marker, cutoff));
marker = cutoff+1;
length = 0;
}
}
}
}
// Add last item
if ((marker+length) > marker){
buffer.push_front(message.substr(marker, length));
}
} else {
buffer.push_front(message);
}
// Drop out of sight
if ((buffer.size() * (wrapper.getHeight()+2)) > (unsigned int)height){
buffer.pop_back();
}
}
}
class UserItem : public Gui::ListItem{
public:
UserItem(const std::string & name, const Util::ReferenceCount<Client> client):
name(name),
client(client){
}
virtual ~UserItem(){
}
virtual bool operator==(const ListItem & comparable){
return (name == ((UserItem *)&comparable)->name);
}
virtual bool equals(Util::ReferenceCount<Gui::ListItem> comparable){
- return (name == comparable.convert<UserItem>()->name);
+ return (name == ((Util::ReferenceCount<UserItem>) comparable)->name);
}
bool equals(const std::string & comparable){
return (name == comparable);
}
virtual int compareTo(Util::ReferenceCount<Gui::ListItem> comparable){
- return name.compare(comparable.convert<UserItem>()->name);
+ return name.compare(((Util::ReferenceCount<UserItem>) comparable)->name);
}
void act(){
}
void draw(const Font & font, const Graphics::Bitmap & work){
if (client->getName() == name){
font.printf(0, 0, Graphics::makeColor(0,255,255), work, name, 0);
} else {
font.printf(0, 0, Graphics::makeColor(255,255,255), work, name, 0);
}
}
private:
const std::string name;
const Util::ReferenceCount<Client> client;
};
class ChannelTab: public Gui::TabItem, Message::QueueInterface{
public:
ChannelTab(const std::string & name):
TabItem(name),
width(0),
height(0),
changed(false){
}
virtual ~ChannelTab(){
}
void act(const Font & font){
class Wrapper: public Message::QueueInterface::FontWrapper{
public:
Wrapper(const Font & font):
font(font){
}
virtual ~Wrapper(){
}
const Font & font;
int getWidth(const std::string & text){
return font.textLength(text.c_str());
}
int getHeight(){
return font.getHeight();
}
};
Wrapper wrapper(font);
processMessages(wrapper, width, height);
}
void draw(const Font& font, const Graphics::Bitmap & work){
if (!isActive()){
return;
}
work.clear();
int mod = height-(font.getHeight()+2);
for (std::deque<std::string>::iterator i = buffer.begin(); i != buffer.end(); ++i){
const std::string & text = *i;
font.printf(0, mod, Graphics::makeColor(255,255,255), work, text, 0);
mod-=(font.getHeight()+2);
}
}
void addMessage(const std::string & message){
if (!isActive() && !changed){
changed = true;
}
messages.push(message);
}
void addMessage(const std::string & name, const std::string & message){
addMessage("<"+name+"> " + message);
}
void inspectBody(const Graphics::Bitmap & body){
width = body.getWidth();
height = body.getHeight();
}
void toggleActive(){
if (!active){
active = true;
changed = false;
} else {
active = false;
}
}
private:
int width;
int height;
bool changed;
};
static Util::ReferenceCount<ChannelTab> convertTab(ChatInterface * chat, std::string name = ""){
if (!name.empty()){
try{
- return chat->getTabByName(name).convert<ChannelTab>();
+ return chat->getTabByName(name);
} catch (const Gui::TabContainer::NoSuchTab & ex){
}
}
- return chat->getCurrentTab().convert<ChannelTab>();
+ return chat->getCurrentTab();
}
void ChatInterface::submit(void * interface){
ChatInterface * chat = (ChatInterface *)interface;
if (!chat->getInputBox().getText().empty()){
const std::string & text = chat->getInputBox().getText();
// check if it's a command
if (Util::matchRegex(text, Util::Regex("^/.*"))){
const std::vector<std::string> & command = Util::splitString(text.substr(1), ' ');
chat->localCommand(command);
} else {
convertTab(chat)->addMessage(chat->getClient()->getName(), text);
chat->getClient()->sendMessage(text);
chat->localNotify(text);
}
chat->getInputBox().clear();
}
}
ChatInterface::ChatInterface(const std::string & host, int port, const std::string & username):
host(host),
widthRatio(.8),
heightRatio(.95),
width(0),
height(0),
checkWidth(Configuration::getScreenWidth()),
checkHeight(Configuration::getScreenHeight()),
serverTab(Util::ReferenceCount<Gui::TabItem>(new ChannelTab(host))){
client = Util::ReferenceCount< Client >(new Client(host, port));
client->connect(username);
// Setup window size and chat list
updateDimensions();
// Add server tab
chatBox.add(serverTab);
}
ChatInterface::~ChatInterface(){
}
void ChatInterface::act(){
updateDimensions();
// Default size of fonts
const int size = height * (1 - (heightRatio + .01));
// Size is important
const Font & font = Font::getDefaultFont(size, size);
processRemoteCommands();
// Check user list
updateUserList();
chatBox.act(font);
inputBox.act(font);
listBox.act(font);
}
void ChatInterface::draw(const Graphics::Bitmap & work){
checkWidth = work.getWidth();
checkHeight = work.getHeight();
if (width == checkWidth && height == checkHeight){
const int size = height * (1 - (heightRatio + .01));
const Font & font = Font::getDefaultFont(size, size);
chatBox.draw(font, work);
inputBox.draw(font, work);
listBox.draw(font, work);
}
}
void ChatInterface::nextChannel(){
chatBox.next();
const std::string & name = chatBox.getCurrent()->getName();
if (name == host){
client->setMessagesDisabled(true);
listBox.clear();
} else {
client->setMessagesDisabled(false);
client->setChannel(client->getChannelIndex(name));
updateUserList();
}
}
void ChatInterface::previousChannel(){
chatBox.previous();
const std::string & name = chatBox.getCurrent()->getName();
if (chatBox.getCurrent()->getName() == host){
client->setMessagesDisabled(true);
listBox.clear();
} else {
client->setMessagesDisabled(false);
client->setChannel(client->getChannelIndex(name));
updateUserList();
}
}
void ChatInterface::gotoChannel(const std::string & channel){
chatBox.gotoTabByName(channel);
const std::string & name = chatBox.getCurrent()->getName();
if (chatBox.getCurrent()->getName() == host){
client->setMessagesDisabled(true);
listBox.clear();
} else {
client->setMessagesDisabled(false);
client->setChannel(client->getChannelIndex(name));
updateUserList();
}
}
Util::ReferenceCount<Client> ChatInterface::getClient(){
return client;
}
Util::ReferenceCount<Gui::TabItem> ChatInterface::getCurrentTab(){
return chatBox.getCurrent();
}
Util::ReferenceCount<Gui::TabItem> ChatInterface::getTabByName(const std::string & name){
return chatBox.getByName(name);
}
void ChatInterface::addMessageToTab(const std::string & message){
convertTab(this)->addMessage(message);
}
void ChatInterface::addMessageToTab(const std::string & name, const std::string & message){
convertTab(this)->addMessage(name, message);
}
void ChatInterface::updateDimensions(){
if (width == checkWidth && height == checkHeight){
return;
}
width = checkWidth;
height = checkHeight;
chatBox.transforms.setRadius(15);
Gui::ColorInfo colors;
colors.body = Graphics::makeColor(255,255,255);
colors.bodyAlpha = 128;
colors.border = Graphics::makeColor(0,0,255);
colors.borderAlpha = 255;
// chat panel widthRatio% heightRatio%
chatBox.location.setPosition(Gui::AbsolutePoint(0, 0));
chatBox.location.setDimensions(width * widthRatio, height * heightRatio);
chatBox.colors = colors;
// edit box widthRatio% remaining (heightRatio + .01)%
const double inputStart = heightRatio + .01;
inputBox.transforms.setRadius(15);
inputBox.location.setPosition(Gui::AbsolutePoint(0, height * inputStart));
inputBox.location.setDimensions(width * widthRatio, height * (1 - inputStart));
inputBox.addHook(Keyboard::Key_ENTER, submit, this);
inputBox.colors = colors;
// Set the location of user list width * widthRatio and height
const int listStart = width * widthRatio + .01;
listBox.transforms.setRadius(15);
listBox.location.setPosition(Gui::AbsolutePoint(listStart, 1));
listBox.location.setDimensions(width - listStart, height-2);
listBox.colors = colors;
}
void ChatInterface::remoteNotify(const std::string & message){
for (std::vector<NotifyCallback>::iterator i = callbacks.begin(); i != callbacks.end(); ++i){
(*i)(message);
}
for (std::vector<Message::EventInterface *>::iterator i = subscribers.begin(); i != subscribers.end(); ++i){
(*i)->localNotify(message);
}
}
void ChatInterface::processRemoteCommands(){
while (client->hasCommands()){
::Network::IRC::Command command = client->nextCommand();
const std::vector<std::string> & params = command.getParameters();
if (params.size() > 0){
//Global::debug(0) << "Got message: " << command.getSendable() << std::endl;
}
try {
if (command.getType() == ::Network::IRC::Command::Ping){
client->sendPong(command);
- serverTab.convert<ChannelTab>()->addMessage(command.getOwner(), "*** Ping!");
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage(command.getOwner(), "*** Ping!");
} else if (command.getType() == ::Network::IRC::Command::PrivateMessage){
// Check channel
const std::string & channel = params.at(0);
if (channel == client->getName()){
// Private message to user (not channel)
convertTab(this, channel)->addMessage("\""+command.getOwner() + "\" whispered: " + params.at(1));
} else {
// Username and message
convertTab(this, channel)->addMessage(command.getOwner(), params.at(1));
}
// notify
remoteNotify(params.at(1));
} else if (command.getType() == ::Network::IRC::Command::Notice){
// Username and message
- serverTab.convert<ChannelTab>()->addMessage(command.getOwner(), params.at(1));
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage(command.getOwner(), params.at(1));
// notify
remoteNotify(params.at(1));
} else if (command.getType() == ::Network::IRC::Command::ReplyMOTD ||
command.getType() == ::Network::IRC::Command::ReplyMOTDStart ||
command.getType() == ::Network::IRC::Command::ReplyMOTDEndOf){
- serverTab.convert<ChannelTab>()->addMessage("*** MOTD " + params.at(1));
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("*** MOTD " + params.at(1));
} else if (command.getType() == ::Network::IRC::Command::Nick){
// Check if user is the owner
if (command.getOwner() == client->getName()){
convertTab(this)->addMessage("*** Nick changed to " + params.at(0) + ".");
} else {
convertTab(this)->addMessage("*** " + command.getOwner() + " is now known as " + params.at(0) + ".");
}
changeUserName(command.getOwner(), params.at(0));
} else if (command.getType() == ::Network::IRC::Command::Join){
// Check if user is the owner otherwise display to that channel
if (command.getOwner() == client->getName()){
- serverTab.convert<ChannelTab>()->addMessage("*** You have joined the channel " + params.at(0) + ".");
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("*** You have joined the channel " + params.at(0) + ".");
chatBox.add(Util::ReferenceCount<Gui::TabItem>(new ChannelTab(params.at(0))));
gotoChannel(params.at(0));
} else {
convertTab(this, params.at(0))->addMessage("*** " + command.getOwner() + " has joined the channel.");
listBox.add(Util::ReferenceCount<Gui::ListItem>(new UserItem(command.getOwner(), client)));
}
} else if (command.getType() == ::Network::IRC::Command::Part){
// Check if user is the owner otherwise display to that channel
if (command.getOwner() == client->getName()){
- serverTab.convert<ChannelTab>()->addMessage("*** You have parted the channel " + params.at(0) + ".");
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("*** You have parted the channel " + params.at(0) + ".");
} else {
convertTab(this, params.at(0))->addMessage("*** " + command.getOwner() + " has parted the channel.");
removeUser(command.getOwner());
}
} else if (command.getType() == ::Network::IRC::Command::ReplyNoTopic){
- serverTab.convert<ChannelTab>()->addMessage("*** The channel " + params.at(1) + " has no topic set.");
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("*** The channel " + params.at(1) + " has no topic set.");
} else if (command.getType() == ::Network::IRC::Command::ReplyTopic){
- serverTab.convert<ChannelTab>()->addMessage("*** The channel topic for " + params.at(1) + " is: \"" + params.at(2) + "\".");
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("*** The channel topic for " + params.at(1) + " is: \"" + params.at(2) + "\".");
} else if (command.getType() == ::Network::IRC::Command::ReplyNames){
std::vector<std::string> names = split(params.at(2), ' ');
std::map<std::string, std::vector<std::string> >::iterator check = namesRequest.find(params.at(1));
if (check == namesRequest.end()){
namesRequest[params.at(1)] = std::vector<std::string>();
check = namesRequest.find(params.at(1));
}
check->second.insert(check->second.begin(), names.begin(), names.end());
} else if (command.getType() == ::Network::IRC::Command::ReplyNamesEndOf){
std::map<std::string, std::vector<std::string> >::iterator check = namesRequest.find(params.at(1));
if (check != namesRequest.end()){
- serverTab.convert<ChannelTab>()->addMessage("*** Current users on " + params.at(1) + " \"" + Util::joinStrings(check->second) + "\".");
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("*** Current users on " + params.at(1) + " \"" + Util::joinStrings(check->second) + "\".");
namesRequest.erase(check);
}
} else if (command.getType() == ::Network::IRC::Command::ErrorNickInUse){
- serverTab.convert<ChannelTab>()->addMessage("[Error] " + params.at(1) + ": Nick already in use.");
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("[Error] " + params.at(1) + ": Nick already in use.");
} else if (command.getType() == ::Network::IRC::Command::ErrorNoSuchNick){
- serverTab.convert<ChannelTab>()->addMessage("[Error] " + params.at(1) + ": No such nick.");
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("[Error] " + params.at(1) + ": No such nick.");
} else if (command.getType() == ::Network::IRC::Command::ErrorNoSuchChannel){
- serverTab.convert<ChannelTab>()->addMessage("[Error] " + params.at(1) + ": No such channel.");
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("[Error] " + params.at(1) + ": No such channel.");
} else if (command.getType() == ::Network::IRC::Command::Error){
Global::debug(0) << "Received Error: " << command.getSendable() << "... Aborting." << std::endl;
throw Exception::Return(__FILE__, __LINE__);
}
} catch (const std::out_of_range & ex){
}
// Check if we got any CTCP delimited messages
if (command.hasCtcp()){
const std::vector<std::string> & ctcp = command.getCtcp();
if (ctcp.size() > 0){
//Global::debug(0) << "Got CTCP: " << command.getCTCPSendable() << std::endl;
}
try {
if (ctcp.at(0) == "PING"){
// Lets check if there is an existing query otherwise send off request
if (command.getType() == ::Network::IRC::Command::Notice){
std::map<std::string, uint64_t>::iterator check = pingReply.find(command.getOwner());
if (check != pingReply.end()){
// there is an existing entry lets display our ping
std::ostringstream difference;
difference << (double)((System::currentMilliseconds() - check->second)/1000000);
- serverTab.convert<ChannelTab>()->addMessage("[CTCP] Received CTCP-PING reply from " + check->first + ": " + difference.str() + "second(s)" );
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("[CTCP] Received CTCP-PING reply from " + check->first + ": " + difference.str() + "second(s)" );
pingReply.erase(check);
}
} else if (command.getType() == ::Network::IRC::Command::PrivateMessage){
client->sendCommand(::Network::IRC::Command::Notice, command.getOwner(), ":\001PING " + ctcp.at(1) + "\001");
- serverTab.convert<ChannelTab>()->addMessage("[CTCP] Received CTCP-PING request from " + command.getOwner() + ", sending answer.");
+ ((Util::ReferenceCount<ChannelTab>) serverTab)->addMessage("[CTCP] Received CTCP-PING request from " + command.getOwner() + ", sending answer.");
}
}
} catch (const std::out_of_range & ex){
}
}
// Pass out the command to listeners
for (std::vector<Message::EventInterface *>::iterator i = subscribers.begin(); i != subscribers.end(); ++i){
(*i)->remoteCommand(command);
}
}
}
void ChatInterface::localNotify(const std::string & message){
for (std::vector<NotifyCallback>::iterator i = callbacks.begin(); i != callbacks.end(); ++i){
(*i)(message);
}
for (std::vector<Message::EventInterface *>::iterator i = subscribers.begin(); i != subscribers.end(); ++i){
(*i)->localNotify(message);
}
}
void ChatInterface::localCommand(const std::vector<std::string> & command){
if (command.at(0) == "help"){
convertTab(this)->addMessage("* commands: help, nick, whisper, ping, join, part, names, topic, previous, next, channels, quit");
} else if (command.at(0) == "nick"){
try {
const std::string & nick = command.at(1);
if (!nick.empty()){
client->setName(nick);
convertTab(this)->addMessage("* nick changed to " + nick);
}
} catch (const std::out_of_range & ex){
convertTab(this)->addMessage("* /nick [name]");
}
} else if (command.at(0) == "whisper"){
try {
const std::string & who = command.at(1);
const std::string & message = Util::joinStrings(command, 2);
if (!who.empty() && !message.empty()){
client->sendCommand(::Network::IRC::Command::PrivateMessage, who, ":" + message);
convertTab(this)->addMessage("-> " + who + " " + message);
}
} catch (const std::out_of_range & ex){
convertTab(this)->addMessage("* /whisper [nick] [message]");
}
} else if (command.at(0) == "ping"){
try {
const std::string & who = command.at(1);
if (!who.empty()){
std::ostringstream timestamp;
uint64_t time = System::currentMilliseconds();
timestamp << time;
client->sendCommand(::Network::IRC::Command::PrivateMessage, who, ":\001PING " + timestamp.str() + "\001");
convertTab(this)->addMessage("[CTCP] Sending CTCP-PING request to " + who);
// Log user
pingReply[who] = time;
}
} catch (const std::out_of_range & ex){
convertTab(this)->addMessage("* /ping [nick]");
}
} else if (command.at(0) == "join"){
try {
const std::string & channel = command.at(1);
if (!channel.empty()){
client->joinChannel(channel);
}
} catch (const std::out_of_range & ex){
convertTab(this)->addMessage("* /join [channel]");
}
} else if (command.at(0) == "part"){
if (client->getChannel() != NULL){
chatBox.removeCurrent();
gotoChannel(host);
client->partChannel(client->getChannel()->getName());
}
} else if (command.at(0) == "names"){
try {
const std::string & channel = command.at(1);
if (!channel.empty()){
client->sendCommand(::Network::IRC::Command::Names, channel);
namesRequest[channel] = std::vector<std::string>();
}
} catch (const std::out_of_range & ex){
convertTab(this)->addMessage("* /names [channel]");
}
} else if (command.at(0) == "topic"){
try {
const std::string & channel = command.at(1);
if (!channel.empty()){
client->sendCommand(::Network::IRC::Command::Topic, channel);
}
} catch (const std::out_of_range & ex){
client->sendCommand(::Network::IRC::Command::Topic, client->getChannel()->getName());
}
} else if (command.at(0) == "previous"){
client->previousChannel();
} else if (command.at(0) == "next"){
client->nextChannel();
} else if (command.at(0) == "channels"){
convertTab(this)->addMessage("* Current Channels: " + client->channelListAsString());
} else if (command.at(0) == "quit"){
const std::string & message = Util::joinStrings(command, 1);
if (!message.empty()){
client->sendCommand(::Network::IRC::Command::Quit, ":" + message);
Global::debug(0) << "Quit (" + message + "). Waiting for server to close connection..." << std::endl;
} else {
client->sendCommand(::Network::IRC::Command::Quit);
Global::debug(0) << "Quit. Waiting for server to close connection..." << std::endl;
}
} else {
//convertTab(this)->addMessage("* Uknown command.");
}
// pass local commands out to listeners
for (std::vector<Message::EventInterface *>::iterator i = subscribers.begin(); i != subscribers.end(); ++i){
(*i)->localCommand(command);
}
}
static bool inList(const std::vector< Util::ReferenceCount<Gui::ListItem> > & list, const std::string & name){
for (std::vector< Util::ReferenceCount<Gui::ListItem> >::const_iterator i = list.begin(); i != list.end(); i++){
- Util::ReferenceCount<UserItem> item = (*i).convert<UserItem>();
+ Util::ReferenceCount<UserItem> item = *i;
if (item->equals(name)){
return true;
}
}
return false;
}
void ChatInterface::updateUserList(){
const ChannelPointer channel = client->getChannel();
if (channel == NULL){
return;
}
const std::vector<std::string> & users = channel->getUsers();
if (listBox.getList().empty() && chatBox.getCurrent()->getName() != host){
// populate
for (std::vector<std::string>::const_iterator i = users.begin(); i != users.end(); i++){
listBox.add(Util::ReferenceCount<Gui::ListItem>(new UserItem(*i, client)));
}
} else if (chatBox.getCurrent()->getName() != host){
if (listBox.getList().size() != users.size()){
listBox.clear();
for (std::vector<std::string>::const_iterator i = users.begin(); i != users.end(); i++){
listBox.add(Util::ReferenceCount<Gui::ListItem>(new UserItem(*i, client)));
}
}
}
}
void ChatInterface::removeUser(const std::string & name){
const std::vector< Util::ReferenceCount<Gui::ListItem> > & userlist = listBox.getList();
for (std::vector< Util::ReferenceCount<Gui::ListItem> >::const_iterator i = userlist.begin(); i != userlist.end(); i++){
- Util::ReferenceCount<UserItem> user = (*i).convert<UserItem>();
+ Util::ReferenceCount<UserItem> user = *i;
if (user->equals(name)){
// remove him from list
listBox.remove(*i);
break;
}
}
}
void ChatInterface::changeUserName(const std::string & name, const std::string & newName){
const std::vector< Util::ReferenceCount<Gui::ListItem> > & userlist = listBox.getList();
for (std::vector< Util::ReferenceCount<Gui::ListItem> >::const_iterator i = userlist.begin(); i != userlist.end(); i++){
- Util::ReferenceCount<UserItem> user = (*i).convert<UserItem>();
+ Util::ReferenceCount<UserItem> user = *i;
if (user->equals(name)){
// remove him from list
listBox.remove(*i);
listBox.add(Util::ReferenceCount<Gui::ListItem>(new UserItem(newName, client)));
break;
}
}
}
}
}
diff --git a/util/pointer.h b/util/pointer.h
index 72cb9479..f38a5687 100644
--- a/util/pointer.h
+++ b/util/pointer.h
@@ -1,188 +1,200 @@
#ifndef _paintown_util_pointer_h
#define _paintown_util_pointer_h
#include <stdlib.h>
namespace Util{
/* Some helpful pointer classes, probably equivalent to stuff in boost
*/
template <class Data>
class ReferenceCount{
public:
explicit ReferenceCount(Data * what = NULL):
count(NULL),
data(what){
count = new int;
*count = 1;
}
ReferenceCount(const ReferenceCount<Data> & him){
data = him.data;
count = him.count;
*count += 1;
}
ReferenceCount & operator=(const ReferenceCount<Data> & him){
/* this object might have a link to `him' so if we release first
* then `him' will get deleted before we can capture his data.
* we need to increase his count first and then release our data
* to insure that `him' will still be alive.
*/
int * temp_count = him.count;
*temp_count += 1;
Data * temp_data = him.data;
release();
data = temp_data;
count = temp_count;
return *this;
}
ReferenceCount & operator=(Data * what){
release();
count = new int;
*count = 1;
data = what;
return *this;
}
/* use this if you need to convert between classes in a heirarchy.
* Base
* Child: public Base
* ReferenceCount<Base> base;
* ReferenceCount<Child> child;
* base = child; is illegal but
* base = child.convert<Base>(); is ok
*/
+ /*
template <class Convert>
ReferenceCount<Convert> convert() const {
ReferenceCount<Convert> what;
what.release();
what.count = count;
*what.count += 1;
what.data = (Convert*) data;
return what;
}
+ */
+
+ template <class Convert>
+ operator ReferenceCount<Convert>() const {
+ ReferenceCount<Convert> out;
+ out.release();
+ out.data = (Convert*) this->data;
+ out.count = this->count;
+ *out.count += 1;
+ return out;
+ }
bool operator!() const {
return !this->data;
}
Data * operator->() const {
return data;
}
Data & operator*() const {
return *data;
}
/* get the raw pointer -- be ultra careful! */
Data * raw() const {
return data;
}
bool operator<(const ReferenceCount<Data> & him) const {
return data < him.data;
}
bool operator==(const ReferenceCount<Data> & him) const {
return data == him.data;
}
bool operator!=(const ReferenceCount<Data> & him) const {
return !(*this == him);
}
bool operator==(const void * what) const {
return data == what;
}
bool operator!=(const void * what) const {
return !(*this == what);
}
virtual ~ReferenceCount(){
release();
}
/* this has to be public to make the convert() function work but pretend
* its private!
*/
public:
void release(){
*count -= 1;
if (*count == 0){
delete data;
delete count;
data = NULL;
count = NULL;
}
}
int * count;
Data * data;
};
/* Same as ReferenceCount but by default passes a new instance of Data to the constructor
* of ReferenceCount
*/
template <class Data>
class NewReferenceCount: public ReferenceCount<Data> {
public:
NewReferenceCount():
ReferenceCount<Data>(new Data()){
}
};
/* Initializes its pointer to NULL and deletes the data in the destructor.
* how is this different from the ReferenceCount class above? its basically
* the same thing but only allows one owner at a time.
*/
template <class Data>
class ClassPointer{
public:
ClassPointer():
data(NULL){
}
ClassPointer(Data * him):
data(him){
}
ClassPointer & operator=(Data * him){
if (data != NULL){
delete data;
}
data = him;
return *this;
}
Data & operator*() const {
return *data;
}
bool operator==(const void * what) const {
return data == what;
}
bool operator!=(const void * what) const {
return !(*this == what);
}
Data* operator->() const {
return data;
}
virtual ~ClassPointer(){
delete data;
}
private:
Data* data;
};
}
#endif

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jun 15, 11:33 PM (2 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70094
Default Alt Text
(344 KB)

Event Timeline