diff options
-rw-r--r-- | common/module.mk | 3 | ||||
-rw-r--r-- | common/unzip.cpp | 1231 | ||||
-rw-r--r-- | common/unzip.h | 300 | ||||
-rw-r--r-- | graphics/imagedec.cpp | 174 | ||||
-rw-r--r-- | graphics/imagedec.h | 61 | ||||
-rw-r--r-- | graphics/imageman.cpp | 146 | ||||
-rw-r--r-- | graphics/imageman.h | 103 | ||||
-rw-r--r-- | graphics/module.mk | 4 | ||||
-rw-r--r-- | gui/EditTextWidget.cpp | 8 | ||||
-rw-r--r-- | gui/ListWidget.cpp | 19 | ||||
-rw-r--r-- | gui/PopUpWidget.cpp | 44 | ||||
-rw-r--r-- | gui/ScrollBarWidget.cpp | 75 | ||||
-rw-r--r-- | gui/TabWidget.cpp | 26 | ||||
-rw-r--r-- | gui/ThemeNew.cpp | 801 | ||||
-rw-r--r-- | gui/about.cpp | 71 | ||||
-rw-r--r-- | gui/about.h | 2 | ||||
-rw-r--r-- | gui/console.cpp | 54 | ||||
-rw-r--r-- | gui/console.h | 1 | ||||
-rw-r--r-- | gui/dialog.cpp | 20 | ||||
-rw-r--r-- | gui/dialog.h | 3 | ||||
-rw-r--r-- | gui/editable.cpp | 6 | ||||
-rw-r--r-- | gui/launcher.cpp | 1 | ||||
-rw-r--r-- | gui/module.mk | 4 | ||||
-rw-r--r-- | gui/newgui.cpp | 285 | ||||
-rw-r--r-- | gui/newgui.h | 62 | ||||
-rw-r--r-- | gui/theme.cpp | 516 | ||||
-rw-r--r-- | gui/theme.h | 361 | ||||
-rw-r--r-- | gui/themes/default-theme.zip | bin | 0 -> 3612 bytes | |||
-rw-r--r-- | gui/widget.cpp | 86 | ||||
-rw-r--r-- | gui/widget.h | 6 | ||||
-rw-r--r-- | scumm/dialogs.cpp | 16 | ||||
-rw-r--r-- | sky/debug.cpp | 6 |
32 files changed, 3868 insertions, 627 deletions
diff --git a/common/module.mk b/common/module.mk index 7fd5396457..dbd41485a4 100644 --- a/common/module.mk +++ b/common/module.mk @@ -13,7 +13,8 @@ MODULE_OBJS := \ common/savefile.o \ common/system.o \ common/scaler.o \ - common/scaler/thumbnail.o + common/scaler/thumbnail.o \ + common/unzip.o ifndef DISABLE_SCALERS MODULE_OBJS += \ diff --git a/common/unzip.cpp b/common/unzip.cpp new file mode 100644 index 0000000000..35158778c0 --- /dev/null +++ b/common/unzip.cpp @@ -0,0 +1,1231 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Header $ + */ + +/* unzip.c -- IO on .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Read unzip.h for more info +*/ + +#include "common/stdafx.h" +#include "common/scummsys.h" + +#ifdef USE_ZLIB + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <zlib.h> + +#include "common/unzip.h" +#include "common/file.h" + +#ifdef STDC +# include <stddef.h> +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include <errno.h> +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + + +#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ + !defined(CASESENSITIVITYDEFAULT_NO) +#define CASESENSITIVITYDEFAULT_NO +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +const char unz_copyright[] = + " unzip 0.15 Copyright 1998 Gilles Vollant "; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + Common::File *file; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + Common::File file; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ +} unz_s; + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +/*local int unzlocal_getByte(Common::File &fin, int *pi) +{ + unsigned char c = fin.readByte(); + *pi = (int)c; + return UNZ_OK; + } + else + { + if (fin.ioFailed()) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +}*/ + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort (Common::File &fin, uLong *pX) +{ + *pX = fin.readUint16LE(); + return UNZ_OK; +} + +local int unzlocal_getLong (Common::File &fin, uLong *pX) +{ + *pX = fin.readUint32LE(); + return UNZ_OK; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) { + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1<c2) + return -1; + if (c1>c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, const char* fileName2, int iCaseSensitivity) +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir(Common::File &fin) +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + fin.seek(0, SEEK_END); + if (fin.ioFailed()) + return 0; + + + uSizeFile = fin.pos(); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackRead<uMaxBack) + { + uLong uReadSize,uReadPos ; + int i; + if (uBackRead+BUFREADCOMMENT>uMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + fin.seek(uReadPos, SEEK_SET); + if (fin.ioFailed()) + break; + + if (fin.read(buf,(uInt)uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer + "zlib/zlib109.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen (const char *path) +{ + unz_s *us = new unz_s; + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + if (!us->file.open(path)) + return NULL; + + central_pos = unzlocal_SearchCentralDir(us->file); + if (central_pos==0) + err=UNZ_ERRNO; + + us->file.seek(central_pos, SEEK_SET); + if (us->file.ioFailed()) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(us->file,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(us->file,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(us->file,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(us->file,&us->gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(us->file,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us->gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(us->file,&us->size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(us->file,&us->offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(us->file,&us->gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pos<us->offset_central_dir+us->size_central_dir) && + (err==UNZ_OK)) + err=UNZ_BADZIPFILE; + + if (err!=UNZ_OK) + { + us->file.close(); + delete us; + return NULL; + } + + us->byte_before_the_zipfile = central_pos - + (us->offset_central_dir+us->size_central_dir); + us->central_pos = central_pos; + us->pfile_in_zip_read = NULL; + + unzGoToFirstFile((unzFile)us); + return (unzFile)us; +} + + +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + s->file.close(); + delete s; + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, + char *szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->file.seek(s->pos_in_central_dir+s->byte_before_the_zipfile, SEEK_SET); + if (s->file.ioFailed()) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename<fileNameBufferSize) + { + *(szFileName+file_info.size_filename)='\0'; + uSizeRead = file_info.size_filename; + } + else + uSizeRead = fileNameBufferSize; + + if ((file_info.size_filename>0) && (fileNameBufferSize>0)) + if (s->file.read(szFileName,(uInt)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extra<extraFieldBufferSize) + uSizeRead = file_info.size_file_extra; + else + uSizeRead = extraFieldBufferSize; + + if (lSeek!=0) + { + s->file.seek(lSeek, SEEK_CUR); + if (s->file.ioFailed()) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (s->file.read(extraField,(uInt)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_comment<commentBufferSize) + { + *(szComment+file_info.size_file_comment)='\0'; + uSizeRead = file_info.size_file_comment; + } + else + uSizeRead = commentBufferSize; + + if (lSeek!=0) + { + s->file.seek(lSeek, SEEK_CUR); + if (s->file.ioFailed()) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (s->file.read(szComment,(uInt)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (unz_s* s, uInt* piSizeVar, + uLong *poffset_local_extrafield, + uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + s->file.seek(s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile, SEEK_SET); + if (s->file.ioFailed()) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + int err=UNZ_OK; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=&s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressed<uReadThis) + uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + pfile_in_zip_read_info->file->seek(pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, SEEK_SET); + if (pfile_in_zip_read_info->file->ioFailed()) + return UNZ_ERRNO; + if (pfile_in_zip_read_info->file->read(pfile_in_zip_read_info->read_buffer,uReadThis)!=uReadThis) + return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { + uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;i<uDoCopy;i++) + *(pfile_in_zip_read_info->stream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + pfile_in_zip_read_info->file->seek(pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET); + if (pfile_in_zip_read_info->file->ioFailed()) + return UNZ_ERRNO; + + if (pfile_in_zip_read_info->file->read(buf,(uInt)size_to_read)!=size_to_read) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + s->file.seek(s->central_pos+22, SEEK_SET); + if (s->file.ioFailed()) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (s->file.read(szComment,(uInt)uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +#endif diff --git a/common/unzip.h b/common/unzip.h new file mode 100644 index 0000000000..5ca7505358 --- /dev/null +++ b/common/unzip.h @@ -0,0 +1,300 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Header $ + */ + +/* unzip.h -- IO for uncompress .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Copyright (C) 1998 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Encryption and multi volume ZipFile (span) are not supported. + Old compressions used by old PKZip 1.x are not supported + + THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE + CAN CHANGE IN FUTURE VERSION !! + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ +/* for more info about .ZIP format, see + ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip */ + +#ifndef _unz_H +#define _unz_H + +#include "common/stdafx.h" +#include "common/scummsys.h" + +#ifdef USE_ZLIB + +#ifdef __cplusplus +extern "C" { +#endif + +#include <zlib.h> + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer + "zlib/zlib111.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /* _unz_H */ diff --git a/graphics/imagedec.cpp b/graphics/imagedec.cpp new file mode 100644 index 0000000000..dd9bc588cd --- /dev/null +++ b/graphics/imagedec.cpp @@ -0,0 +1,174 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Header $ + */ + +#include "graphics/imagedec.h" + +#include "common/system.h" +#include "common/file.h" + +namespace Graphics { +// +// BMP Decoder +// +class BMPDecoder : public ImageDecoder { +public: + BMPDecoder() {} + virtual ~BMPDecoder() {} + + bool decodeable(Common::SeekableReadStream &stream); + Surface *decodeImage(Common::SeekableReadStream &stream); + + struct BitmapHeader { + uint16 type; + uint32 size; + uint16 res1; + uint16 res2; + uint32 imageOffset; + }; + + struct InfoHeader { + uint32 size; + uint32 width; + uint32 height; + uint16 planes; + uint16 bitsPerPixel; + uint32 compression; + uint32 imageSize; + uint32 pixelsPerMeterX; + uint32 pixelsPerMeterY; + uint32 colorsUsed; + uint32 colorsImportant; + }; +}; + +bool BMPDecoder::decodeable(Common::SeekableReadStream &stream) { + BitmapHeader header; + stream.seek(0); + header.type = stream.readUint16BE(); + header.size = stream.readUint32LE(); + + // TODO: maybe improve this detection + if (header.size == 0 || header.type != 'BM') + return false; + + return true; +} + +Surface *BMPDecoder::decodeImage(Common::SeekableReadStream &stream) { + if (!decodeable(stream)) { + return 0; + } + + BitmapHeader header; + InfoHeader info; + + stream.seek(0); + header.type = stream.readUint16BE(); + header.size = stream.readUint32LE(); + header.res1 = stream.readUint16LE(); + header.res2 = stream.readUint16LE(); + header.imageOffset = stream.readUint32LE(); + + if (header.size == 0 || header.type != 'BM') { + stream.seek(0); + return 0; + } + + info.size = stream.readUint32LE(); + info.width = stream.readUint32LE(); + info.height = stream.readUint32LE(); + info.planes = stream.readUint16LE(); + info.bitsPerPixel = stream.readUint16LE(); + info.compression = stream.readUint32LE(); + info.imageSize = stream.readUint32LE(); + info.pixelsPerMeterX = stream.readUint32LE(); + info.pixelsPerMeterY = stream.readUint32LE(); + info.colorsUsed = stream.readUint32LE(); + info.colorsImportant = stream.readUint32LE(); + + stream.seek(header.imageOffset); + + if (info.bitsPerPixel != 24) { + stream.seek(0); + return 0; + } + + uint8 r = 0, g = 0, b = 0; + Surface *newSurf = new Surface; + assert(newSurf); + newSurf->create(info.width, info.height, sizeof(OverlayColor)); + assert(newSurf->pixels); + OverlayColor *curPixel = (OverlayColor*)newSurf->pixels + (newSurf->h-1) * newSurf->w; + int pitchAdd = info.width % 4; + for (int i = 0; i < newSurf->h; ++i) { + for (int i2 = 0; i2 < newSurf->w; ++i2) { + b = stream.readByte(); + g = stream.readByte(); + r = stream.readByte(); + *curPixel = OSystem::instance().RGBToColor(r, g, b); + ++curPixel; + } + stream.seek(pitchAdd, SEEK_CUR); + curPixel -= newSurf->w*2; + } + + stream.seek(0); + return newSurf; +} + +#pragma mark - + +Surface *ImageDecoder::loadFile(const Common::String &name) { + Surface *newSurf = 0; + + Common::File imageFile; + if (imageFile.open(name.c_str())) { + newSurf = loadFile(imageFile); + } + + return newSurf; +} + +Surface *ImageDecoder::loadFile(Common::SeekableReadStream &stream) { + // TODO: implement support for bzipped memory + + // FIXME: this is not a very nice solution but it should work + // for the moment, we should use a different way to get all + // decoders + static BMPDecoder bmpDecoder; + static ImageDecoder *decoderList[] = { + &bmpDecoder, // for uncompressed .BMP files + 0 + }; + + ImageDecoder *decoder = 0; + for (int i = 0; decoderList[i] != 0; ++i) { + if (decoderList[i]->decodeable(stream)) { + decoder = decoderList[i]; + break; + } + } + + if (!decoder) + return 0; + + return decoder->decodeImage(stream); +} +} // end of namespace Graphics diff --git a/graphics/imagedec.h b/graphics/imagedec.h new file mode 100644 index 0000000000..c59a458ff3 --- /dev/null +++ b/graphics/imagedec.h @@ -0,0 +1,61 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Header $ + */ + +#ifndef GRAPHICS_IMAGEDEC_H +#define GRAPHICS_IMAGEDEC_H + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "common/str.h" +#include "common/stream.h" + +#include "graphics/surface.h" + +namespace Graphics { +class ImageDecoder { +public: + ImageDecoder() {} + virtual ~ImageDecoder() {} + + static Surface *loadFile(const Common::String &name); + static Surface *loadFile(Common::SeekableReadStream &stream); + + /** + * checks if the data can be decoded by this decoder + * + * @param stream memory read stream + * @return true if it can be decoded, otherwise false + */ + virtual bool decodeable(Common::SeekableReadStream &stream) = 0; + + /** + * decodes the data and returns an pointer to the resulting surface. + * Surface::free() must be called by the user also it must be deleted + * with delete; + * + * @param stream the memory stream which should be decoded + * @return returns a new surface if the image could be decoded, otherwise 0 + */ + virtual Surface *decodeImage(Common::SeekableReadStream &stream) = 0; +}; +} // end of namespace Graphics + +#endif + diff --git a/graphics/imageman.cpp b/graphics/imageman.cpp new file mode 100644 index 0000000000..7e78b52279 --- /dev/null +++ b/graphics/imageman.cpp @@ -0,0 +1,146 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Header $ + */ + +#include "graphics/imagedec.h" +#include "graphics/imageman.h" +#include "graphics/surface.h" + +DECLARE_SINGLETON(Graphics::ImageManager); + +namespace Graphics { +ImageManager::ImageManager() : _surfaces() +#ifdef USE_ZLIB +, _archives() +#endif +{ +} + +ImageManager::~ImageManager() { + for (Iterator pos = _surfaces.begin(); pos != _surfaces.end(); ++pos) { + (*pos)->surface->free(); + delete (*pos)->surface; + delete *pos; + *pos = 0; + } + _surfaces.clear(); +#ifdef USE_ZLIB + for (ZipIterator pos = _archives.begin(); pos != _archives.end(); ++pos) { + unzClose(*pos); + } + _archives.clear(); +#endif +} + +bool ImageManager::addArchive(const Common::String &name) { +#ifdef USE_ZLIB + unzFile newFile = unzOpen(name.c_str()); + if (!newFile) + return false; + _archives.push_back(newFile); +#endif + return true; +} + +bool ImageManager::registerSurface(const Common::String &name, Surface *surf) { + if (getSurface(name)) { + return false; + } + + Entry *newHandle = new Entry; + if (!newHandle) + return false; + + if (!surf) { + surf = ImageDecoder::loadFile(name); + if (!surf) { +#ifdef USE_ZLIB + ZipIterator file = _archives.end(); + for (ZipIterator pos = _archives.begin(); pos != _archives.end(); ++pos) { + if (unzLocateFile(*pos, name.c_str(), 2) == UNZ_OK) { + file = pos; + break; + } + } + + if (file == _archives.end()) + return false; + + unz_file_info fileInfo; + unzOpenCurrentFile(*file); + unzGetCurrentFileInfo(*file, &fileInfo, NULL, 0, NULL, 0, NULL, 0); + uint8 *buffer = new uint8[fileInfo.uncompressed_size]; + assert(buffer); + unzReadCurrentFile(*file, buffer, fileInfo.uncompressed_size); + unzCloseCurrentFile(*file); + Common::MemoryReadStream stream(buffer, fileInfo.uncompressed_size); + surf = ImageDecoder::loadFile(stream); + delete [] buffer; + + if (!surf) + return false; +#else + return false; +#endif + } + } + + newHandle->surface = surf; + newHandle->name = name; + _surfaces.push_back(newHandle); + + return true; +} + +bool ImageManager::unregisterSurface(const Common::String &name) { + Iterator pos = searchHandle(name); + if (pos == _surfaces.end()) { + // no surface handle it as success + return true; + } + + (*pos)->surface->free(); + delete (*pos)->surface; + delete *pos; + *pos = 0; + + _surfaces.erase(pos); + + return true; +} + +Surface * ImageManager::getSurface(const Common::String &name) { + Iterator pos = searchHandle(name); + if (pos == _surfaces.end()) { + // no surface handle it as success + return 0; + } + return (*pos)->surface; +} + +ImageManager::Iterator ImageManager::searchHandle(const Common::String &name) { + Iterator pos = _surfaces.begin(); + while (pos != _surfaces.end()) { + if ((*pos)->name == name) + break; + ++pos; + } + return pos; +} +} // end of namespace Graphics diff --git a/graphics/imageman.h b/graphics/imageman.h new file mode 100644 index 0000000000..ff48701cce --- /dev/null +++ b/graphics/imageman.h @@ -0,0 +1,103 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Header $ + */ + +#ifndef GRAPHICS_IMAGEMAN_H +#define GRAPHICS_IMAGEMAN_H + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "common/singleton.h" +#include "common/str.h" +#include "common/list.h" +#include "common/unzip.h" + +namespace Graphics { +struct Surface; + +class ImageManager : public Common::Singleton<ImageManager> { +public: + ~ImageManager(); + + /** + * adds an .zip archive to the pool there the ImagaManager searches + * for image files + * + * @param name the name of the archive + * @return true on success and false on failure + */ + bool addArchive(const Common::String &name); + + /** + * registers a surface to the ImageManager. + * surf->free(), also delete surf, will be called when the ImageManager will + * be destroyed or if ImageManager::unregisterSurface is called. + * if the parameter 'surf' is 0 the Manger will try to load an image with + * the filename 'name' + * + * @param name the name of the new handle + * @param surf the surface which should be associated to the given name + * @return returns true on success and false on failure + */ + bool registerSurface(const Common::String &name, Surface *surf); + + /** + * unregisters a surface, after this the returned surface from + * getSurface should NOT be used anymore + * + * @param name the handle + * @return true on success, false on failure + */ + bool unregisterSurface(const Common::String &name); + + /** + * gets a surface registered to a handle + * + * @param name the name of the handle + * @return returns an pointer to an Surface object or 0 on failure + */ + Surface *getSurface(const Common::String &name); +private: + friend class Common::Singleton<SingletonBaseType>; + ImageManager(); + + struct Entry { + Common::String name; + Surface *surface; + }; + typedef Common::List<Entry*>::iterator Iterator; +#ifdef USE_ZLIB + typedef Common::List<unzFile>::iterator ZipIterator; +#endif + + Iterator searchHandle(const Common::String &name); + + Common::List<Entry*> _surfaces; +#ifdef USE_ZLIB + Common::List<unzFile> _archives; +#endif +}; + +} // end of namespace Graphics + +/** Shortcut for accessing the font manager. */ +#define ImageMan (Graphics::ImageManager::instance()) + +#endif + diff --git a/graphics/module.mk b/graphics/module.mk index 4ff913d87c..66e63daf90 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -10,7 +10,9 @@ MODULE_OBJS := \ graphics/newfont_big.o \ graphics/primitives.o \ graphics/scummfont.o \ - graphics/surface.o + graphics/surface.o \ + graphics/imageman.o \ + graphics/imagedec.o MODULE_DIRS += \ graphics diff --git a/gui/EditTextWidget.cpp b/gui/EditTextWidget.cpp index c43c385a5f..d2e9f4da27 100644 --- a/gui/EditTextWidget.cpp +++ b/gui/EditTextWidget.cpp @@ -59,15 +59,11 @@ void EditTextWidget::handleMouseDown(int x, int y, int button, int clickCount) { void EditTextWidget::drawWidget(bool hilite) { - // Draw a thin frame around us. - g_gui.hLine(_x, _y, _x + _w - 1, g_gui._color); - g_gui.hLine(_x, _y + _h - 1, _x +_w - 1, g_gui._shadowcolor); - g_gui.vLine(_x, _y, _y + _h - 1, g_gui._color); - g_gui.vLine(_x + _w - 1, _y, _y + _h - 1, g_gui._shadowcolor); + g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x+_w, _y+_h), _hints, Theme::kWidgetBackgroundBorderSmall); // Draw the text adjustOffset(); - g_gui.drawString(_editString, _x + 2, _y + 2, getEditRect().width(), g_gui._textcolor, kTextAlignLeft, -_editScrollOffset, false); + g_gui.theme()->drawText(Common::Rect(_x+2,_y+2, _x+getEditRect().width()-2, _y+_h-2), _editString, Theme::kStateEnabled, Theme::kTextAlignLeft, false, -_editScrollOffset, false); } Common::Rect EditTextWidget::getEditRect() const { diff --git a/gui/ListWidget.cpp b/gui/ListWidget.cpp index 4e4af1ad12..9f3797af15 100644 --- a/gui/ListWidget.cpp +++ b/gui/ListWidget.cpp @@ -37,6 +37,7 @@ ListWidget::ListWidget(GuiObject *boss, int x, int y, int w, int h, WidgetSize w } _flags = WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_WANT_TICKLE; + setHints(THEME_HINT_SAVE_BACKGROUND); _type = kListWidget; _editMode = false; _numberingMode = kListNumberingOne; @@ -297,27 +298,25 @@ void ListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { } void ListWidget::drawWidget(bool hilite) { - NewGui *gui = &g_gui; int i, pos, len = _list.size(); Common::String buffer; int deltax; // Draw a thin frame around the list. - gui->hLine(_x, _y, _x + _w - 1, gui->_color); - gui->hLine(_x, _y + _h - 1, _x + _w - 1, gui->_shadowcolor); - gui->vLine(_x, _y, _y + _h - 1, gui->_color); + g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x+_w, _y+_h), _hints, Theme::kWidgetBackgroundBorderSmall); // Draw the list items for (i = 0, pos = _currentPos; i < _entriesPerPage && pos < len; i++, pos++) { - const OverlayColor textColor = (_selectedItem == pos && _hasFocus) ? gui->_bgcolor : gui->_textcolor; const int y = _y + 2 + kLineHeight * i; + const int fontHeight = kLineHeight; + bool inverted = false; // Draw the selected item inverted, on a highlighted background. if (_selectedItem == pos) { if (_hasFocus) - gui->fillRect(_x + 1, _y + 1 + kLineHeight * i, _w - 1, kLineHeight, gui->_textcolorhi); + inverted = true; else - gui->frameRect(_x + 1, _y + 1 + kLineHeight * i, _w - 1, kLineHeight, gui->_textcolorhi); + g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y + 1 + kLineHeight * i, _x+_w-1, y+fontHeight-1), _hints, Theme::kWidgetBackgroundBorderSmall); } // If in numbering mode, we first print a number prefix @@ -325,7 +324,7 @@ void ListWidget::drawWidget(bool hilite) { char temp[10]; sprintf(temp, "%2d. ", (pos + _numberingMode)); buffer = temp; - gui->drawString(buffer, _x + 2, y, _w - 4, textColor); + g_gui.theme()->drawText(Common::Rect(_x+2, y, _x+_w-2, y+fontHeight-1), buffer, Theme::kStateEnabled, Theme::kTextAlignLeft, inverted); } Common::Rect r(getEditRect()); @@ -335,11 +334,11 @@ void ListWidget::drawWidget(bool hilite) { adjustOffset(); deltax = -_editScrollOffset; - gui->drawString(buffer, _x + r.left, y, r.width(), textColor, kTextAlignLeft, deltax, false); + g_gui.theme()->drawText(Common::Rect(_x + r.left - deltax, y, _x+_w-2, y+fontHeight-1), buffer, Theme::kStateEnabled, Theme::kTextAlignLeft, inverted); } else { buffer = _list[pos]; deltax = 0; - gui->drawString(buffer, _x + r.left, y, r.width(), textColor); + g_gui.theme()->drawText(Common::Rect(_x + r.left, y, _x+_w-2, y+fontHeight-1), buffer, Theme::kStateEnabled, Theme::kTextAlignLeft, inverted); } } } diff --git a/gui/PopUpWidget.cpp b/gui/PopUpWidget.cpp index e09680f308..6edff1f927 100644 --- a/gui/PopUpWidget.cpp +++ b/gui/PopUpWidget.cpp @@ -140,13 +140,10 @@ PopUpDialog::PopUpDialog(PopUpWidget *boss, int clickX, int clickY, WidgetSize w void PopUpDialog::drawDialog() { // Draw the menu border - g_gui.hLine(_x, _y, _x+_w - 1, g_gui._color); - g_gui.hLine(_x, _y + _h - 1, _x + _w - 1, g_gui._shadowcolor); - g_gui.vLine(_x, _y, _y+_h - 1, g_gui._color); - g_gui.vLine(_x + _w - 1, _y, _y + _h - 1, g_gui._shadowcolor); + g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x+_w, _y+_h), THEME_HINT_FIRST_DRAW | THEME_HINT_SAVE_BACKGROUND, Theme::kWidgetBackgroundBorderSmall); - if (_twoColumns) - g_gui.vLine(_x + _w / 2, _y, _y + _h - 2, g_gui._color); + /*if (_twoColumns) + g_gui.vLine(_x + _w / 2, _y, _y + _h - 2, g_gui._color);*/ // Draw the entries int count = _popUpBoss->_entries.size(); @@ -155,11 +152,9 @@ void PopUpDialog::drawDialog() { } // The last entry may be empty. Fill it with black. - if (_twoColumns && (count & 1)) { + /*if (_twoColumns && (count & 1)) { g_gui.fillRect(_x + 1 + _w / 2, _y + 1 + kLineHeight * (_entriesPerColumn - 1), _w / 2 - 1, kLineHeight, g_gui._bgcolor); - } - - g_gui.addDirtyRect(_x, _y, _w, _h); + }*/ } void PopUpDialog::handleMouseUp(int x, int y, int button, int clickCount) { @@ -322,15 +317,13 @@ void PopUpDialog::drawMenuEntry(int entry, bool hilite) { Common::String &name = _popUpBoss->_entries[entry].name; - g_gui.fillRect(x, y, w, kLineHeight, hilite ? g_gui._textcolorhi : g_gui._bgcolor); if (name.size() == 0) { // Draw a separator - g_gui.hLine(x - 1, y + kLineHeight / 2, x + w, g_gui._shadowcolor); - g_gui.hLine(x, y + 1 + kLineHeight / 2, x + w, g_gui._color); + g_gui.theme()->drawLineSeparator(Common::Rect(x, y, x+w, y+kLineHeight)); } else { - g_gui.drawString(name, x + 1, y + 2, w - 2, hilite ? g_gui._bgcolor : g_gui._textcolor); + g_gui.theme()->drawText(Common::Rect(x+1, y+2, x+w-1, y+kLineHeight), name, hilite ? Theme::kStateHighlight : Theme::kStateEnabled, + Theme::kTextAlignLeft); } - g_gui.addDirtyRect(x, y, w, kLineHeight); } @@ -343,6 +336,7 @@ void PopUpDialog::drawMenuEntry(int entry, bool hilite) { PopUpWidget::PopUpWidget(GuiObject *boss, int x, int y, int w, int h, const String &label, uint labelWidth, WidgetSize ws) : Widget(boss, x, y - 1, w, h + 2), CommandSender(boss), _ws(ws), _label(label), _labelWidth(labelWidth) { _flags = WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS; + setHints(THEME_HINT_SAVE_BACKGROUND); _type = kPopUpWidget; _selectedItem = -1; @@ -397,22 +391,19 @@ void PopUpWidget::setSelectedTag(uint32 tag) { } void PopUpWidget::drawWidget(bool hilite) { - NewGui *gui = &g_gui; int x = _x + _labelWidth; int w = _w - _labelWidth; + // Draw a thin frame around us. + g_gui.theme()->drawWidgetBackground(Common::Rect(x, _y, x+w, _y+_h), _hints, Theme::kWidgetBackgroundBorderSmall); + // Draw the label, if any if (_labelWidth > 0) - gui->drawString(_label, _x, _y + 3, _labelWidth, isEnabled() ? gui->_textcolor : gui->_color, kTextAlignRight); - - // Draw a thin frame around us. - gui->hLine(x, _y, x + w - 1, gui->_color); - gui->hLine(x, _y +_h-1, x + w - 1, gui->_shadowcolor); - gui->vLine(x, _y, _y+_h-1, gui->_color); - gui->vLine(x + w - 1, _y, _y +_h - 1, gui->_shadowcolor); + g_gui.theme()->drawText(Common::Rect(_x+2,_y+3,_x+_labelWidth, _y+g_gui.theme()->getFontHeight()), _label, + isEnabled() ? Theme::kStateEnabled : Theme::kStateDisabled, Theme::kTextAlignRight); // Draw a set of arrows at the right end to signal this is a dropdown/popup - Common::Point p0, p1; + /*Common::Point p0, p1; p0 = Common::Point(x + w + 1 - _h / 2, _y + 4); p1 = Common::Point(x + w + 1 - _h / 2, _y + _h - 4); @@ -425,12 +416,13 @@ void PopUpWidget::drawWidget(bool hilite) { for (; p1.y - p0.y > 1; p0.y++, p0.x--, p1.y--, p1.x++) { surf.drawLine(p0.x, p0.y, p1.x, p0.y, color); surf.drawLine(p0.x, p1.y, p1.x, p1.y, color); - } + }*/ // Draw the selected entry, if any if (_selectedItem >= 0) { TextAlignment align = (g_gui.getStringWidth(_entries[_selectedItem].name) > w-6) ? kTextAlignRight : kTextAlignLeft; - gui->drawString(_entries[_selectedItem].name, x+2, _y+3, w-6, !isEnabled() ? gui->_color : gui->_textcolor, align); + g_gui.theme()->drawText(Common::Rect(x+2, _y+3, _x+w-6, _y+g_gui.theme()->getFontHeight()), _entries[_selectedItem].name, + isEnabled() ? Theme::kStateEnabled : Theme::kStateDisabled, g_gui.theme()->convertAligment(align)); } } diff --git a/gui/ScrollBarWidget.cpp b/gui/ScrollBarWidget.cpp index e373f04a1e..03182b35c0 100644 --- a/gui/ScrollBarWidget.cpp +++ b/gui/ScrollBarWidget.cpp @@ -179,73 +179,22 @@ void ScrollBarWidget::recalc() { } void ScrollBarWidget::drawWidget(bool hilite) { - NewGui *gui = &g_gui; - int bottomY = _y + _h - UP_DOWN_BOX_HEIGHT; - bool isSinglePage = (_numEntries <= _entriesPerPage); - OverlayColor color; - Graphics::Surface &surf = g_gui.getScreen(); - const int B = 3; - Common::Point p0, p1, p2; - - gui->frameRect(_x, _y, _w, _h, gui->_shadowcolor); - if (_draggingPart != kNoPart) _part = _draggingPart; - const int arrowSize = (_w / 2 - B + 1); - - // - // Up arrow - // - color = isSinglePage ? gui->_color : - (hilite && _part == kUpArrowPart) ? gui->_textcolorhi : gui->_textcolor; - gui->frameRect(_x, _y, _w, UP_DOWN_BOX_HEIGHT, gui->_color); - p0 = Common::Point(_x + _w / 2, _y + (UP_DOWN_BOX_HEIGHT - arrowSize - 1) / 2); - p1 = Common::Point(p0.x - arrowSize, p0.y + arrowSize); - p2 = Common::Point(p0.x + arrowSize, p0.y + arrowSize); -#if 0 - surf.drawLine(p0.x, p0.y, p1.x, p1.y, color); - surf.drawLine(p0.x, p0.y, p2.x, p2.y, color); -// surf.drawLine(p1.x, p1.y, p2.x, p2.y, color); -#else - // Evil HACK to draw filled triangle - for (; p1.x <= p2.x; ++p1.x) - surf.drawLine(p0.x, p0.y, p1.x, p1.y, color); -#endif - - // - // Down arrow - // - color = isSinglePage ? gui->_color : - (hilite && _part == kDownArrowPart) ? gui->_textcolorhi : gui->_textcolor; - gui->frameRect(_x, bottomY, _w, UP_DOWN_BOX_HEIGHT, gui->_color); - p0 = Common::Point(_x + _w / 2, bottomY + (UP_DOWN_BOX_HEIGHT + arrowSize + 1) / 2); - p1 = Common::Point(p0.x - arrowSize, p0.y - arrowSize); - p2 = Common::Point(p0.x + arrowSize, p0.y - arrowSize); - -#if 0 - surf.drawLine(p0.x, p0.y, p1.x, p1.y, color); - surf.drawLine(p0.x, p0.y, p2.x, p2.y, color); -// surf.drawLine(p1.x, p1.y, p2.x, p2.y, color); -#else - // Evil HACK to draw filled triangle - for (; p1.x <= p2.x; ++p1.x) - surf.drawLine(p0.x, p0.y, p1.x, p1.y, color); -#endif - - // - // Slider - // - if (!isSinglePage) { - gui->fillRect(_x, _y + _sliderPos, _w, _sliderHeight, - (hilite && _part == kSliderPart) ? gui->_textcolorhi : gui->_textcolor); - gui->frameRect(_x, _y + _sliderPos, _w, _sliderHeight, gui->_color); - int y = _y + _sliderPos + _sliderHeight / 2; - color = (hilite && _part == kSliderPart) ? gui->_color : gui->_shadowcolor; - gui->hLine(_x + 2, y - 2, _x + _w - 3, color); - gui->hLine(_x + 2, y, _x + _w - 3, color); - gui->hLine(_x + 2, y + 2, _x + _w-3, color); + Theme::kScrollbarState state = Theme::kScrollbarStateNo; + if (_numEntries <= _entriesPerPage) { + state = Theme::kScrollbarStateSinglePage; + } else if (_part == kUpArrowPart) { + state = Theme::kScrollbarStateUp; + } else if (_part == kDownArrowPart) { + state = Theme::kScrollbarStateDown; + } else if (_part == kSliderPart) { + state = Theme::kScrollbarStateSlider; } + + g_gui.theme()->drawScrollbar(Common::Rect(_x, _y, _x+_w, _y+_h), _sliderPos, _sliderHeight, state, + isEnabled() ? (hilite ? Theme::kStateHighlight : Theme::kStateEnabled) : Theme::kStateDisabled); } } // End of namespace GUI diff --git a/gui/TabWidget.cpp b/gui/TabWidget.cpp index 48b519938b..346a1d9c5d 100644 --- a/gui/TabWidget.cpp +++ b/gui/TabWidget.cpp @@ -127,7 +127,7 @@ bool TabWidget::handleKeyDown(uint16 ascii, int keycode, int modifiers) { return Widget::handleKeyDown(ascii, keycode, modifiers); } -static void box(int x, int y, int width, int height, OverlayColor colorA, OverlayColor colorB, bool omitBottom) { +/*static void box(int x, int y, int width, int height, OverlayColor colorA, OverlayColor colorB, bool omitBottom) { NewGui &gui = g_gui; gui.hLine(x + 1, y, x + width - 2, colorA); @@ -141,36 +141,18 @@ static void box(int x, int y, int width, int height, OverlayColor colorA, Overla } gui.vLine(x + width - 1, y + 1, y + height - (omitBottom ? 1 : 2), colorB); gui.vLine(x + width - 2, y + 1, y + height - (omitBottom ? 2 : 1), colorB); -} +}*/ void TabWidget::drawWidget(bool hilite) { - NewGui *gui = &g_gui; - - const int left1 = _x + 1; - const int right1 = _x + kTabLeftOffset + _activeTab * (_tabWidth + kTabSpacing); - const int left2 = right1 + _tabWidth; - const int right2 = _x + _w - 2; - - // Draw horizontal line - gui->hLine(left1, _y + _tabHeight - 2, right1, gui->_shadowcolor); - gui->hLine(left2, _y + _tabHeight - 2, right2, gui->_shadowcolor); - // Iterate over all tabs and draw them int i, x = _x + kTabLeftOffset; for (i = 0; i < (int)_tabs.size(); ++i) { - OverlayColor color = (i == _activeTab) ? gui->_color : gui->_shadowcolor; int yOffset = (i == _activeTab) ? 0 : 2; - box(x, _y + yOffset, _tabWidth, _tabHeight - yOffset, color, color, (i == _activeTab)); - gui->drawString(_tabs[i].title, x + kTabPadding, _y + yOffset / 2 + (_tabHeight - gui->getFontHeight() - 3), _tabWidth - 2 * kTabPadding, gui->_textcolor, kTextAlignCenter); + g_gui.theme()->drawTab(Common::Rect(x, _y+yOffset, x+_tabWidth, _y+_tabHeight), _tabs[i].title, (i == _activeTab)); x += _tabWidth + kTabSpacing; } - - // Draw more horizontal lines - gui->hLine(left1, _y + _tabHeight - 1, right1, gui->_color); - gui->hLine(left2, _y + _tabHeight - 1, right2, gui->_color); - gui->hLine(_x+1, _y + _h - 2, _x + _w - 2, gui->_shadowcolor); - gui->hLine(_x+1, _y + _h - 1, _x + _w - 2, gui->_color); + g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y+_tabHeight-2, _x+_w, _y+_h), _hints, Theme::kWidgetBackgroundBorderSmall); } Widget *TabWidget::findWidget(int x, int y) { diff --git a/gui/ThemeNew.cpp b/gui/ThemeNew.cpp new file mode 100644 index 0000000000..c707134d8a --- /dev/null +++ b/gui/ThemeNew.cpp @@ -0,0 +1,801 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Header $ + */ + +#include "gui/theme.h" + +#include "graphics/imageman.h" +#include "graphics/imagedec.h" + +#include "common/config-manager.h" +#include "common/file.h" + +#include "common/unzip.h" + +using Graphics::Surface; + +/** Specifies the currently active 16bit pixel format, 555 or 565. */ +extern int gBitFormat; + +static void getColorFromConfig(const Common::ConfigFile &cfg, const Common::String &value, OverlayColor &color) { + Common::String temp; + cfg.getKey(value, "colors", temp); + int r, g, b; + sscanf(temp.c_str(), "%d %d %d", &r, &g, &b); + color = OSystem::instance().RGBToColor(r, g, b); +} + +namespace GUI { +ThemeNew::ThemeNew(OSystem *system, Common::String stylefile) : Theme(), _system(system), _screen(), _initOk(false), +_forceRedraw(false), _font(0), _imageHandles(0), _images(0), _colors() { + _initOk = false; + memset(&_screen, 0, sizeof(_screen)); + memset(&_dialog, 0, sizeof(_dialog)); + memset(&_colors, 0, sizeof(_colors)); + + _screen.create(_system->getOverlayWidth(), _system->getOverlayHeight(), sizeof(OverlayColor)); + if (_screen.pixels) { + _initOk = true; + clearAll(); + if (_screen.w >= 400 && _screen.h >= 300) { + _font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); + } else { + _font = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont); + } + } + + if (ConfMan.hasKey("extrapath")) { + Common::File::addDefaultDirectory(ConfMan.get("extrapath")); + } + if (ConfMan.hasKey("themepath")) { + Common::File::addDefaultDirectory(ConfMan.get("themepath")); + } + ImageMan.addArchive(stylefile + ".zip"); + + if (!_configFile.loadFromFile(stylefile + ".ini")) { +#ifdef USE_ZLIB + // Maybe find a nicer solution to this + unzFile zipFile = unzOpen((stylefile + ".zip").c_str()); + if (zipFile == NULL) return; + if (unzLocateFile(zipFile, (stylefile + ".ini").c_str(), 2) == UNZ_OK) { + unz_file_info fileInfo; + unzOpenCurrentFile(zipFile); + unzGetCurrentFileInfo(zipFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0); + uint8 *buffer = new uint8[fileInfo.uncompressed_size]; + assert(buffer); + unzReadCurrentFile(zipFile, buffer, fileInfo.uncompressed_size); + unzCloseCurrentFile(zipFile); + Common::MemoryReadStream stream(buffer, fileInfo.uncompressed_size); + if (!_configFile.loadFromStream(stream)) { + warning("Can not find theme config file '%s'", (stylefile + ".ini").c_str()); + unzClose(zipFile); + return; + } + delete [] buffer; + buffer = 0; + } else { + warning("Can not find theme config file '%s'", (stylefile + ".ini").c_str()); + return; + } + unzClose(zipFile); +#else + warning("Can not find theme config file '%s'", (stylefile + ".ini").c_str()); + return; +#endif + } + + Common::String temp = ""; + _configFile.getKey("version", "theme", temp); + if (temp != "1") { + // TODO: improve this detection and handle it nicer + warning("Theme config uses a different version"); + return; + } + + static Common::String imageHandlesTable[kImageHandlesMax]; + _configFile.getKey("dialog_corner", "pixmaps", imageHandlesTable[kDialogBkgdCorner]); + _configFile.getKey("dialog_top", "pixmaps", imageHandlesTable[kDialogBkgdTop]); + _configFile.getKey("dialog_left", "pixmaps", imageHandlesTable[kDialogBkgdLeft]); + _configFile.getKey("dialog_bkgd", "pixmaps", imageHandlesTable[kDialogBkgd]); + _configFile.getKey("widget_corner", "pixmaps", imageHandlesTable[kWidgetBkgdCorner]); + _configFile.getKey("widget_top", "pixmaps", imageHandlesTable[kWidgetBkgdTop]); + _configFile.getKey("widget_left", "pixmaps", imageHandlesTable[kWidgetBkgdLeft]); + _configFile.getKey("widget_bkgd", "pixmaps", imageHandlesTable[kWidgetBkgd]); + _configFile.getKey("checkbox_empty", "pixmaps", imageHandlesTable[kCheckboxEmpty]); + _configFile.getKey("checkbox_checked", "pixmaps", imageHandlesTable[kCheckboxChecked]); + _configFile.getKey("widget_arrow", "pixmaps", imageHandlesTable[kWidgetArrow]); + + getColorFromConfig(_configFile, "main_dialog_start", _colors[kMainDialogStart]); + getColorFromConfig(_configFile, "main_dialog_end", _colors[kMainDialogEnd]); + + getColorFromConfig(_configFile, "dialog_start", _colors[kDialogStart]); + getColorFromConfig(_configFile, "dialog_end", _colors[kDialogEnd]); + + getColorFromConfig(_configFile, "color_state_disabled", _colors[kColorStateDisabled]); + getColorFromConfig(_configFile, "color_state_highlight", _colors[kColorStateHighlight]); + getColorFromConfig(_configFile, "color_state_enabled", _colors[kColorStateEnabled]); + getColorFromConfig(_configFile, "color_transparency", _colors[kColorTransparency]); + + getColorFromConfig(_configFile, "text_inverted_background", _colors[kTextInvertedBackground]); + getColorFromConfig(_configFile, "text_inverted_color", _colors[kTextInvertedColor]); + + getColorFromConfig(_configFile, "widget_bkgd_start", _colors[kWidgetBackgroundStart]); + getColorFromConfig(_configFile, "widget_bkgd_end", _colors[kWidgetBackgroundEnd]); + getColorFromConfig(_configFile, "widget_bkgd_small_start", _colors[kWidgetBackgroundSmallStart]); + getColorFromConfig(_configFile, "widget_bkgd_small_end", _colors[kWidgetBackgroundSmallEnd]); + + getColorFromConfig(_configFile, "button_bkgd_start", _colors[kButtonBackgroundStart]); + getColorFromConfig(_configFile, "button_bkgd_end", _colors[kButtonBackgroundEnd]); + getColorFromConfig(_configFile, "button_text_enabled", _colors[kButtonTextEnabled]); + getColorFromConfig(_configFile, "button_text_disabled", _colors[kButtonTextDisabled]); + getColorFromConfig(_configFile, "button_text_highlight", _colors[kButtonTextHighlight]); + + getColorFromConfig(_configFile, "slider_background_start", _colors[kSliderBackgroundStart]); + getColorFromConfig(_configFile, "slider_background_end", _colors[kSliderBackgroundEnd]); + getColorFromConfig(_configFile, "slider_start", _colors[kSliderStart]); + getColorFromConfig(_configFile, "slider_end", _colors[kSliderEnd]); + + getColorFromConfig(_configFile, "tab_background_start", _colors[kTabBackgroundStart]); + getColorFromConfig(_configFile, "tab_background_end", _colors[kTabBackgroundEnd]); + + getColorFromConfig(_configFile, "scrollbar_background_start", _colors[kScrollbarBackgroundStart]); + getColorFromConfig(_configFile, "scrollbar_background_end", _colors[kScrollbarBackgroundEnd]); + getColorFromConfig(_configFile, "scrollbar_button_start", _colors[kScrollbarButtonStart]); + getColorFromConfig(_configFile, "scrollbar_button_end", _colors[kScrollbarButtonEnd]); + getColorFromConfig(_configFile, "scrollbar_slider_start", _colors[kScrollbarSliderStart]); + getColorFromConfig(_configFile, "scrollbar_slider_end", _colors[kScrollbarSliderEnd]); + + getColorFromConfig(_configFile, "caret_color", _colors[kCaretColor]); + + _imageHandles = imageHandlesTable; + + _images = new const Graphics::Surface*[ARRAYSIZE(imageHandlesTable)]; + assert(_images); + + for (int i = 0; _imageHandles[i] != "\0"; ++i) { + ImageMan.registerSurface(_imageHandles[i], 0); + _images[i] = ImageMan.getSurface(_imageHandles[i]); + } +} + +ThemeNew::~ThemeNew() { + deinit(); + delete [] _images; + _images = 0; + if (_imageHandles) { + for (int i = 0; i < kImageHandlesMax; ++i) { + ImageMan.unregisterSurface(_imageHandles[i]); + } + } +} + +bool ThemeNew::init() { + if (!_images) + return false; + for (int i = 0; i < kImageHandlesMax; ++i) { + if (!_images[i]) { + return false; + } + } + + deinit(); + _screen.create(_system->getOverlayWidth(), _system->getOverlayHeight(), sizeof(OverlayColor)); + if (_screen.pixels) { + _initOk = true; + clearAll(); + if (_screen.w >= 400 && _screen.h >= 300) { + _font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); + } else { + _font = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont); + } + } + + return true; +} + +void ThemeNew::deinit() { + if (_initOk) { + _system->hideOverlay(); + _screen.free(); + _initOk = false; + } +} + +void ThemeNew::refresh() { + init(); + _system->showOverlay(); +} + +void ThemeNew::enable() { + _system->showOverlay(); + clearAll(); +} + +void ThemeNew::disable() { + _system->hideOverlay(); +} + +void ThemeNew::openDialog() { + if (!_dialog) { + _dialog = new DialogState; + assert(_dialog); + // first dialog + _dialog->screen.create(_screen.w, _screen.h, sizeof(OverlayColor)); + } + memcpy(_dialog->screen.pixels, _screen.pixels, _screen.pitch*_screen.h); +} + +void ThemeNew::closeDialog() { + if (_dialog) { + _dialog->screen.free(); + delete _dialog; + _dialog = 0; + } + _forceRedraw = true; +} + +void ThemeNew::clearAll() { + if (!_initOk) + return; + _system->clearOverlay(); + // FIXME: problem with the 'pitch' + _system->grabOverlay((OverlayColor*)_screen.pixels, _screen.w); +} + +void ThemeNew::drawAll() { + // TODO: see ThemeNew::addDirtyRect + _forceRedraw = false; +} + +void ThemeNew::resetDrawArea() { + if (_initOk) { + _drawArea = Common::Rect(0, 0, _screen.w, _screen.h); + } +} + +#define surface(x) (_images[x]) + +void ThemeNew::drawDialogBackground(const Common::Rect &r, kState state, bool mainDialog) { + if (!_initOk) + return; + + if (mainDialog) { + colorFade(r, _colors[kMainDialogStart], _colors[kMainDialogEnd]); + } else { + drawRectMasked(r, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), + 255, _colors[kDialogStart], _colors[kDialogEnd]); + } + + addDirtyRect(r, true); +} + +void ThemeNew::drawText(const Common::Rect &r, const Common::String &str, kState state, kTextAlign align, bool inverted, int deltax, bool useEllipsis) { + if (!_initOk) + return; + Common::Rect r2(r.left, r.top, r.right, r.top+_font->getFontHeight()); + + restoreBackground(r2); + + if (inverted) { + _screen.fillRect(r, _colors[kTextInvertedBackground]); + _font->drawString(&_screen, str, r.left, r.top, r.width(), _colors[kTextInvertedColor], convertAligment(align), deltax, useEllipsis); + addDirtyRect(r2); + return; + } else { + _font->drawString(&_screen, str, r.left, r.top, r.width(), getColor(state), convertAligment(align), deltax, useEllipsis); + } + + addDirtyRect(r2); +} + +void ThemeNew::drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, kState state) { + if (!_initOk) + return; + restoreBackground(r); + font->drawChar(&_screen, ch, r.left, r.top, getColor(state)); + addDirtyRect(r); +} + +void ThemeNew::drawWidgetBackground(const Common::Rect &r, uint16 hints, kWidgetBackground background, kState state) { + if (!_initOk) + return; + + if ((hints & THEME_HINT_SAVE_BACKGROUND) && !(hints & THEME_HINT_FIRST_DRAW) && !_forceRedraw) { + restoreBackground(r); + return; + } + + if (background == kWidgetBackgroundBorderSmall) { + drawRectMasked(r, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), + (state == kStateDisabled) ? 128 : 256, _colors[kWidgetBackgroundSmallStart], _colors[kWidgetBackgroundSmallEnd]); + } else { + drawRectMasked(r, surface(kWidgetBkgdCorner), surface(kWidgetBkgdTop), surface(kWidgetBkgdLeft), surface(kWidgetBkgd), + (state == kStateDisabled) ? 128 : 256, _colors[kWidgetBackgroundStart], _colors[kWidgetBackgroundEnd], 2); + } + + addDirtyRect(r, (hints & THEME_HINT_SAVE_BACKGROUND)); +} + +void ThemeNew::drawButton(const Common::Rect &r, const Common::String &str, kState state) { + if (!_initOk) + return; + + drawRectMasked(r, surface(kWidgetBkgdCorner), surface(kWidgetBkgdTop), surface(kWidgetBkgdLeft), surface(kWidgetBkgd), + 255, _colors[kButtonBackgroundStart], _colors[kButtonBackgroundEnd], 2); + + const int off = (r.height() - _font->getFontHeight()) / 2; + + OverlayColor col = 0; + switch (state) { + case kStateEnabled: + col = _colors[kButtonTextEnabled]; + break; + + case kStateHighlight: + col = _colors[kButtonTextHighlight]; + break; + + default: + col = _colors[kButtonTextDisabled]; + break; + }; + + _font->drawString(&_screen, str, r.left, r.top + off, r.width(), col, Graphics::kTextAlignCenter, 0, true); + + addDirtyRect(r); +} + +void ThemeNew::drawSurface(const Common::Rect &r, const Graphics::Surface &surface, kState state) { + if (!_initOk) + return; + Common::Rect rect(r.left, r.top, r.left + surface.w, r.top + surface.h); + rect.clip(_screen.w, _screen.h); + + if (!rect.isValidRect()) + return; + + assert(surface.bytesPerPixel == sizeof(OverlayColor)); + + OverlayColor *src = (OverlayColor *)surface.pixels; + OverlayColor *dst = (OverlayColor *)_screen.getBasePtr(rect.left, rect.top); + + int w = rect.width(); + int h = rect.height(); + + while (h--) { + memcpy(dst, src, surface.pitch); + src += w; + // FIXME: this should be pitch + dst += _screen.w; + } + addDirtyRect(r); +} + +void ThemeNew::drawSlider(const Common::Rect &r, int width, kState state) { + if (!_initOk) + return; + + drawRectMasked(r, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), 255, + _colors[kSliderBackgroundStart], _colors[kSliderBackgroundEnd]); + + Common::Rect r2 = r; + r2.left = r.left + 2; + r2.top = r.top + 2; + r2.bottom = r.bottom - 2; + r2.right = r2.left + width; + if (r2.right > r.right - 2) { + r2.right = r.right - 2; + } + + drawRectMasked(r2, surface(kWidgetBkgdCorner), surface(kWidgetBkgdTop), surface(kWidgetBkgdLeft), surface(kWidgetBkgd), + (state == kStateDisabled) ? 128 : 256, _colors[kSliderStart], _colors[kSliderEnd], 2); + + addDirtyRect(r); +} + +void ThemeNew::drawCheckbox(const Common::Rect &r, const Common::String &str, bool checked, kState state) { + if (!_initOk) + return; + Common::Rect r2 = r; + + const Graphics::Surface *checkBox = surface(checked ? kCheckboxChecked : kCheckboxEmpty); + int checkBoxSize = checkBox->w; + + drawSurface(Common::Rect(r.left, r.top, r.left+checkBox->w, r.top+checkBox->h), checkBox, false, false, (state == kStateDisabled) ? 128 : 256); + r2.left += checkBoxSize + 5; + _font->drawString(&_screen, str, r2.left, r2.top, r2.width(), getColor(state), Graphics::kTextAlignCenter, 0, false); + + addDirtyRect(r); +} + +void ThemeNew::drawTab(const Common::Rect &r, const Common::String &str, bool active, kState state) { + if (!_initOk) + return; + if (active) { + _font->drawString(&_screen, str, r.left, r.top+2, r.width(), getColor(kStateHighlight), Graphics::kTextAlignCenter, 0, true); + } else { + drawRectMasked(r, surface(kWidgetBkgdCorner), surface(kWidgetBkgdTop), surface(kWidgetBkgdLeft), surface(kWidgetBkgd), + (state == kStateDisabled) ? 128 : 256, _colors[kTabBackgroundStart], _colors[kTabBackgroundEnd], 2); + _font->drawString(&_screen, str, r.left, r.top+2, r.width(), getColor(state), Graphics::kTextAlignCenter, 0, true); + } + addDirtyRect(r); +} + +void ThemeNew::drawScrollbar(const Common::Rect &r, int sliderY, int sliderHeight, kScrollbarState, kState state) { + if (!_initOk) + return; + const int UP_DOWN_BOX_HEIGHT = r.width() + 1; + Common::Rect r2 = r; + + // draws the scrollbar background + drawRectMasked(r2, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), 255, + _colors[kScrollbarBackgroundStart], _colors[kScrollbarBackgroundEnd]); + + // draws the 'up' button + r2.bottom = r2.top + UP_DOWN_BOX_HEIGHT; + drawRectMasked(r2, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), 255, + _colors[kScrollbarButtonStart], _colors[kScrollbarButtonEnd]); + + const Graphics::Surface *arrow = surface(kWidgetArrow); + r2.left += 1 + (r2.width() - arrow->w) / 2; + r2.right = r2.left + arrow->w; + r2.top += (r2.height() - arrow->h) / 2; + r2.bottom = r2.top + arrow->h; + drawSurface(r2, arrow, false, false, 255); + + // draws the slider + r2 = r; + r2.left += 2; + r2.right -= 2; + r2.top += sliderY; + r2.bottom = r2.top + sliderHeight / 2 + surface(kWidgetBkgdCorner)->h + 4; + drawRectMasked(r2, surface(kWidgetBkgdCorner), surface(kWidgetBkgdTop), surface(kWidgetBkgdLeft), surface(kWidgetBkgd), 255, + _colors[kScrollbarSliderStart], _colors[kScrollbarSliderEnd]); + r2.top += sliderHeight / 2; + r2.bottom += sliderHeight / 2 - surface(kWidgetBkgdCorner)->h - 4; + drawRectMasked(r2, surface(kWidgetBkgdCorner), surface(kWidgetBkgdTop), surface(kWidgetBkgdLeft), surface(kWidgetBkgd), 255, + _colors[kScrollbarSliderEnd], _colors[kScrollbarSliderStart]); + + // draws the 'down' button + r2 = r; + r2.top = r2.bottom - UP_DOWN_BOX_HEIGHT; + drawRectMasked(r2, surface(kDialogBkgdCorner), surface(kDialogBkgdTop), surface(kDialogBkgdLeft), surface(kDialogBkgd), 255, + _colors[kScrollbarButtonStart], _colors[kScrollbarButtonEnd]); + + r2.left += 1 + (r2.width() - arrow->w) / 2; + r2.right = r2.left + arrow->w; + r2.top += (r2.height() - arrow->h) / 2; + r2.bottom = r2.top + arrow->h; + drawSurface(r2, arrow, true, false, 255); + + addDirtyRect(r); +} + +void ThemeNew::drawCaret(const Common::Rect &r, bool erase, kState state) { + if (!_initOk) + return; + + restoreBackground(Common::Rect(r.left, r.top, r.left+1, r.bottom)); + if (!erase) { + _screen.vLine(r.left, r.top, r.bottom, _colors[kCaretColor]); + } else { + // FIXME: hack to restore the caret background correctly + const OverlayColor search = _colors[kTextInvertedBackground]; + const OverlayColor *src = (const OverlayColor*)_screen.getBasePtr(r.left-1, r.top-1); + int height = r.height() + 2; + if (r.top + height > _screen.h) { + height = _screen.h - r.top; + } + bool drawInvBackground = false; + while (height--) { + if (src[0] == search || src[1] == search || src[2] == search) { + drawInvBackground = true; + } + src += _screen.w; + } + if (drawInvBackground) { + _screen.vLine(r.left, r.top, r.bottom, search); + } + } + addDirtyRect(r); +} + +void ThemeNew::drawLineSeparator(const Common::Rect &r, kState state) { + if (!_initOk) + return; + _screen.hLine(r.left - 1, r.top + r.height() / 2, r.right, _system->RGBToColor(0, 0, 0)); + addDirtyRect(r); +} + +#pragma mark - intern drawing + +void ThemeNew::restoreBackground(Common::Rect r) { + r.clip(_screen.w, _screen.h); + r.clip(_drawArea); + if (_dialog) { + if (!_dialog->screen.pixels) { + return; + } + const OverlayColor *src = (const OverlayColor*)_dialog->screen.getBasePtr(r.left, r.top); + OverlayColor *dst = (OverlayColor*)_screen.getBasePtr(r.left, r.top); + + int h = r.height(); + int w = r.width(); + while (h--) { + memcpy(dst, src, w*sizeof(OverlayColor)); + src += _dialog->screen.w; + dst += _screen.w; + } + } +} + +bool ThemeNew::addDirtyRect(Common::Rect r, bool backup) { + // TODO: implement proper dirty rect handling + // FIXME: problem with the 'pitch' + r.clip(_screen.w, _screen.h); + r.clip(_drawArea); + _system->copyRectToOverlay((OverlayColor*)_screen.getBasePtr(r.left, r.top), _screen.w, r.left, r.top, r.width(), r.height()); + if (_dialog && backup) { + if (_dialog->screen.pixels) { + OverlayColor *dst = (OverlayColor*)_dialog->screen.getBasePtr(r.left, r.top); + const OverlayColor *src = (const OverlayColor*)_screen.getBasePtr(r.left, r.top); + int h = r.height(); + while (h--) { + memcpy(dst, src, r.width()*sizeof(OverlayColor)); + dst += _dialog->screen.w; + src += _screen.w; + } + } + } + return true; +} + +inline uint8 calcColor(uint8 start, uint8 end, int pos, int max) { + int diff = ((int)end - (int)start) * pos / max; + return start + diff; +} + +OverlayColor calcColor(OverlayColor start, OverlayColor end, int pos, int max, uint factor = 1) { + pos *= factor; + if (pos > max) { + pos = max; + } + OverlayColor result = 0; + uint8 sr = 0, sg = 0, sb = 0; + uint8 er = 0, eg = 0, eb = 0; + if (gBitFormat == 565) { + sr = (start >> 11) & 0x1F; + sg = (start >> 5) & 0x3F; + sb = (start >> 0) & 0x1F; + + er = (end >> 11) & 0x1F; + eg = (end >> 5) & 0x3F; + eb = (end >> 0) & 0x1F; + } else { + sr = (start >> 10) & 0x1F; + sg = (start >> 5) & 0x1F; + sb = (start >> 0) & 0x1F; + + er = (end >> 10) & 0x1F; + eg = (end >> 5) & 0x1F; + eb = (end >> 0) & 0x1F; + } + uint8 cr = calcColor(sr, er, pos, max); + uint8 cg = calcColor(sg, eg, pos, max); + uint8 cb = calcColor(sb, eb, pos, max); + if (gBitFormat == 565) { + result = ((int)(cr & 0x1F) << 11) | ((int)(cg & 0x3F) << 5) | (int)(cb & 0x1F); + } else { + result = ((int)(cr & 0x1F) << 10) | ((int)(cg & 0x1F) << 5) | (int)(cb & 0x1F); + } + return result; +} + +void ThemeNew::colorFade(const Common::Rect &r, OverlayColor start, OverlayColor end) { + OverlayColor *ptr = (OverlayColor*)_screen.getBasePtr(r.left, r.top); + int h = r.height(); + while (h--) { + OverlayColor col = calcColor(start, end, r.height()-h, r.height()); + for (int i = 0; i < r.width(); ++i) { + ptr[i] = col; + } + ptr += _screen.w; + } +} + +void ThemeNew::drawRect(const Common::Rect &r, const Surface *corner, const Surface *top, const Surface *left, const Surface *fill, int alpha) { + // top left + drawRectMasked(r, corner, top, left, fill, alpha, _system->RGBToColor(255, 255, 255), _system->RGBToColor(255, 255, 255)); +} + +void ThemeNew::drawRectMasked(const Common::Rect &r, const Graphics::Surface *corner, const Graphics::Surface *top, + const Graphics::Surface *left, const Graphics::Surface *fill, int alpha, + OverlayColor start, OverlayColor end, uint factor) { + int drawWidth = MIN(corner->w, MIN(top->w, MIN(left->w, fill->w))); + int drawHeight = MIN(corner->h, MIN(top->h, MIN(left->h, fill->h))); + int partsH = r.height() / drawHeight; + int partsW = r.width() / drawWidth; + int yPos = r.top; + + int specialHeight = 0; + int specialWidth = 0; + + if (drawHeight*2 > r.height()) { + drawHeight = r.height() / 2; + partsH = 2; + } else { + specialHeight = r.height() % drawHeight; + if (specialHeight != 0) + ++partsH; + } + + if (drawWidth*2 > r.width()) { + drawWidth = r.width() / 2; + partsW = 2; + } else { + specialWidth = r.width() % drawWidth; + if (specialWidth != 0) + ++partsW; + } + + for (int y = 0; y < partsH; ++y) { + int xPos = r.left; + bool upDown = false; + if (y == partsH - 1) + upDown = true; + + // calculate the correct drawing height + int usedHeight = drawHeight; + if (specialHeight && y == 1) { + usedHeight = specialHeight; + } + + OverlayColor startCol = calcColor(start, end, yPos-r.top, r.height(), factor); + OverlayColor endCol = calcColor(start, end, yPos-r.top+usedHeight, r.height(), factor); + + for (int i = 0; i < partsW; ++i) { + + // calculate the correct drawing width + int usedWidth = drawWidth; + if (specialWidth && i == 1) { + usedWidth = specialWidth; + } + + // draw the right surface + if (!i) { + if (!y || y == partsH - 1) { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), corner, upDown, false, alpha, startCol, endCol); + } else { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), left, upDown, false, alpha, startCol, endCol); + } + } else if (i == partsW - 1) { + if (!y || y == partsH - 1) { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), corner, upDown, true, alpha, startCol, endCol); + } else { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), left, upDown, true, alpha, startCol, endCol); + } + } else if (!y || y == partsH - 1) { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), top, upDown, false, alpha, startCol, endCol); + } else { + drawSurfaceMasked(Common::Rect(xPos, yPos, xPos+usedWidth, yPos+usedHeight), fill, upDown, false, alpha, startCol, endCol); + } + xPos += usedWidth; + } + yPos += usedHeight; + } +} + +void ThemeNew::drawSurface(const Common::Rect &r, const Surface *surf, bool upDown, bool leftRight, int alpha) { + drawSurfaceMasked(r, surf, upDown, leftRight, alpha, _system->RGBToColor(255, 255, 255), _system->RGBToColor(255, 255, 255)); +} + +inline OverlayColor getColorAlpha(OverlayColor col1, OverlayColor col2, int alpha) { + if (alpha == 256) { + return col1; + } + uint8 r1, g1, b1; + uint8 r2, g2, b2; + OSystem::instance().colorToRGB(col1, r1, g1, b1); + OSystem::instance().colorToRGB(col2, r2, g2, b2); + uint8 r, g, b; + r = (alpha * (r1 - r2) >> 8) + r2; + g = (alpha * (g1 - g2) >> 8) + g2; + b = (alpha * (b1 - b2) >> 8) + b2; + return OSystem::instance().RGBToColor(r, g, b); +} + +void ThemeNew::drawSurfaceMasked(const Common::Rect &r, const Graphics::Surface *surf, bool upDown, bool leftRight, + int alpha, OverlayColor start, OverlayColor end, uint factor) { + OverlayColor *dst = (OverlayColor*)_screen.getBasePtr(r.left, r.top); + const OverlayColor *src = 0; + + const OverlayColor transparency = _colors[kColorTransparency]; + + if (upDown && !leftRight) { // upsidedown + src = (const OverlayColor*)surf->pixels + (surf->h - 1) * surf->w; + int drawWidth = (r.width() < surf->w) ? r.width() : surf->w; + for (int i = 0; i < r.height(); ++i) { + OverlayColor rowColor = calcColor(start, end, i, r.height(), factor); + for (int x = 0; x < drawWidth; ++x) { + if (src[x] != transparency && dst >= _screen.pixels) { + dst[x] = getColorAlpha(src[x], dst[x], alpha) & rowColor; + } + } + dst += _screen.w; + src -= surf->w; + } + } else if (upDown && leftRight) { // upsidedown + left right inverse + src = (const OverlayColor*)surf->pixels + (surf->h - 1) * surf->w; + int drawWidth = (r.width() < surf->w) ? r.width() : surf->w; + for (int i = 0; i < r.height(); ++i) { + OverlayColor rowColor = calcColor(start, end, i, r.height(), factor); + for (int x = 0; x < drawWidth; ++x) { + if (src[drawWidth-x-1] != transparency && dst >= _screen.pixels) { + dst[x] = getColorAlpha(src[drawWidth-x-1], dst[x], alpha) & rowColor; + } + } + dst += _screen.w; + src -= surf->w; + } + } else if (!upDown && leftRight) { // left right inverse + src = (const OverlayColor*)surf->pixels; + int drawWidth = (r.width() < surf->w) ? r.width() : surf->w; + for (int i = 0; i < r.height(); ++i) { + OverlayColor rowColor = calcColor(start, end, i, r.height(), factor); + for (int x = 0; x < drawWidth; ++x) { + if (src[drawWidth-x-1] != transparency && dst >= _screen.pixels) { + dst[x] = getColorAlpha(src[drawWidth-x-1], dst[x], alpha) & rowColor; + } + } + dst += _screen.w; + src += surf->w; + } + } else { // normal + src = (const OverlayColor*)surf->pixels; + int drawWidth = (r.width() < surf->w) ? r.width() : surf->w; + for (int i = 0; i < r.height(); ++i) { + OverlayColor rowColor = calcColor(start, end, i, r.height(), factor); + for (int x = 0; x < drawWidth; ++x) { + if (src[x] != transparency && dst >= _screen.pixels) { + dst[x] = getColorAlpha(src[x], dst[x], alpha) & rowColor; + } + } + dst += _screen.w; + src += surf->w; + } + } +} + +OverlayColor ThemeNew::getColor(kState state) { + switch (state) { + case kStateDisabled: + return _colors[kColorStateDisabled]; + break; + + case kStateHighlight: + return _colors[kColorStateHighlight]; + break; + + default: + break; + }; + return _colors[kColorStateEnabled]; +} + +} // end of namespace GUI diff --git a/gui/about.cpp b/gui/about.cpp index 372ffa5484..0043bd8518 100644 --- a/gui/about.cpp +++ b/gui/about.cpp @@ -73,7 +73,6 @@ static const char *credits_intro[] = { #include "gui/credits.h" - AboutDialog::AboutDialog() : Dialog(10, 20, 300, 174), _scrollPos(0), _scrollTime(0), _modifiers(0), _willClose(false) { @@ -111,7 +110,6 @@ AboutDialog::AboutDialog() } _w += 2*xOff; - for (i = 0; i < 1; i++) _lines.push_back(""); @@ -166,7 +164,7 @@ void AboutDialog::addLine(const char *str) { _lines.push_back(format); } else { Common::StringList wrappedLines; - g_gui.getFont().wordWrapText(str, _w - 2*xOff, wrappedLines); + g_gui.getFont().wordWrapText(str, _w - 2 * xOff, wrappedLines); for (Common::StringList::const_iterator i = wrappedLines.begin(); i != wrappedLines.end(); ++i) { _lines.push_back(format + *i); @@ -180,27 +178,17 @@ void AboutDialog::open() { _scrollPos = 0; _modifiers = 0; _willClose = false; - _canvas.pixels = NULL; Dialog::open(); } void AboutDialog::close() { - free(_canvas.pixels); Dialog::close(); } void AboutDialog::drawDialog() { - if (!_canvas.pixels) { - // Blend over the background. Since we can't afford to do that - // every time the text is updated (it's horribly CPU intensive) - // we do it just once and then use a copy of the result as our - // static background for the remainder of the credits. - g_gui.blendRect(_x, _y, _w, _h, g_gui._bgcolor); - g_gui.copyToSurface(&_canvas, _x, _y, _w, _h); - } - - g_gui.drawSurface(_canvas, _x, _y); + g_gui.theme()->setDrawArea(Common::Rect(_x, _y, _x+_w, _y+_h)); + Dialog::drawDialog(); // Draw text // TODO: Add a "fade" effect for the top/bottom text lines @@ -213,35 +201,37 @@ void AboutDialog::drawDialog() { for (int line = firstLine; line < lastLine; line++) { const char *str = _lines[line].c_str(); - Graphics::TextAlignment align = Graphics::kTextAlignCenter; - OverlayColor color = g_gui._textcolor; + Theme::kTextAlign align = Theme::kTextAlignCenter; + Theme::kState state = Theme::kStateEnabled; while (str[0] == '\\') { switch (str[1]) { case 'C': - align = Graphics::kTextAlignCenter; + align = Theme::kTextAlignCenter; break; case 'L': - align = Graphics::kTextAlignLeft; + align = Theme::kTextAlignLeft; break; case 'R': - align = Graphics::kTextAlignRight; + align = Theme::kTextAlignRight; break; case 'c': switch (str[2]) { case '0': - color = g_gui._textcolor; + state = Theme::kStateEnabled; break; case '1': - color = g_gui._textcolorhi; + state = Theme::kStateHighlight; break; case '2': - color = g_gui._color; + state = Theme::kStateDisabled; break; case '3': - color = g_gui._shadowcolor; + warning("Need state for color 3"); + // color = g_gui._shadowcolor; break; case '4': - color = g_gui._bgcolor; + warning("Need state for color 4"); + // color = g_gui._bgcolor; break; default: error("Unknown color type '%c'", str[2]); @@ -255,31 +245,17 @@ void AboutDialog::drawDialog() { str += 2; } // Trim leading whitespaces if center mode is on - if (align == Graphics::kTextAlignCenter) + if (align == Theme::kTextAlignCenter) while (*str && *str == ' ') str++; - g_gui.drawString(str, _x + xOff, y, _w - 2 * xOff, color, align, 0, false); + g_gui.theme()->drawText(Common::Rect(_x + xOff, y, _x + _w - xOff, y + g_gui.theme()->getFontHeight() + 4), str, state, align, false, 0, false); y += _lineHeight; } - - // Draw a border - g_gui.box(_x, _y, _w, _h, g_gui._color, g_gui._shadowcolor); - - // Finally blit it all to the screen - g_gui.addDirtyRect(_x, _y, _w, _h); + g_gui.theme()->resetDrawArea(); } - void AboutDialog::handleTickle() { - // We're in the process of doing a full redraw to re-create the - // background image for the text. That means we need to wait for the - // GUI itself to clear the overlay and call drawDialog() in all of the - // dialogs, otherwise we'll only redraw this one and it'll still have - // the remains of the old image, including the text that was on it. - if (!_canvas.pixels) - return; - const uint32 t = getMillis(); int scrollOffset = ((int)t - (int)_scrollTime) / kScrollMillisPerPixel; if (scrollOffset > 0) { @@ -302,16 +278,6 @@ void AboutDialog::handleTickle() { } } -void AboutDialog::handleScreenChanged() { - // The screen has changed. That means the overlay colors in the canvas - // may no longer be correct. Reset it, and issue a full redraw. - // TODO: We could check if the bit format has changed, like we do in - // the MPEG player. - free(_canvas.pixels); - _canvas.pixels = NULL; - draw(); -} - void AboutDialog::handleMouseUp(int x, int y, int button, int clickCount) { // Close upon any mouse click close(); @@ -329,5 +295,4 @@ void AboutDialog::handleKeyUp(uint16 ascii, int keycode, int modifiers) { close(); } - } // End of namespace GUI diff --git a/gui/about.h b/gui/about.h index a4c1977691..6f0900ea28 100644 --- a/gui/about.h +++ b/gui/about.h @@ -36,7 +36,6 @@ protected: uint32 _lineHeight; byte _modifiers; bool _willClose; - Graphics::Surface _canvas; int xOff, yOff; @@ -49,7 +48,6 @@ public: void close(); void drawDialog(); void handleTickle(); - void handleScreenChanged(); void handleMouseUp(int x, int y, int button, int clickCount); void handleKeyDown(uint16 ascii, int keycode, int modifiers); void handleKeyUp(uint16 ascii, int keycode, int modifiers); diff --git a/gui/console.cpp b/gui/console.cpp index 4f43b5a6df..57c374ddef 100644 --- a/gui/console.cpp +++ b/gui/console.cpp @@ -114,10 +114,7 @@ void ConsoleDialog::slideUpAndClose() { } void ConsoleDialog::open() { - // This dialog will be redrawn a lot, so we store a copy of the blended - // background in a separate "canvas", just like in the About dialog. - _canvas.pixels = NULL; - + // TODO: find a new way to do this // Initiate sliding the console down. We do a very simple trick to achieve // this effect: we simply move the console dialog just above (outside) the // visible screen area, then shift it down in handleTickle() over a @@ -135,36 +132,16 @@ void ConsoleDialog::open() { } void ConsoleDialog::close() { - free(_canvas.pixels); Dialog::close(); } void ConsoleDialog::drawDialog() { - if (!_canvas.pixels) { - // Blend over the background. Don't count the time used for - // this when timing the slide action. - - uint32 now = g_system->getMillis(); - uint32 delta; - - g_gui.blendRect(0, 0, _w, _h, g_gui._bgcolor, 2); - g_gui.copyToSurface(&_canvas, 0, 0, _w, _h); - - delta = g_system->getMillis() - now; - - if (_slideTime) - _slideTime += delta; - } - - g_gui.drawSurface(_canvas, 0, 0); - - // Draw a border - g_gui.hLine(_x, _y + _h - 1, _x + _w - 1, g_gui._color); - // Draw text int start = _scrollLine - _linesPerPage + 1; int y = _y + 2; + g_gui.theme()->drawDialogBackground(Common::Rect(_x, _y, _x+_w, _y+_h)); + for (int line = 0; line < _linesPerPage; line++) { int x = _x + 1; for (int column = 0; column < _lineWidth; column++) { @@ -174,7 +151,7 @@ void ConsoleDialog::drawDialog() { #else byte c = buffer((start + line) * _lineWidth + column); #endif - g_gui.drawChar(c, x, y, g_gui._textcolor, _font); + g_gui.theme()->drawChar(Common::Rect(x, y, x+kConsoleCharWidth, y+kConsoleLineHeight), c, _font); x += kConsoleCharWidth; } y += kConsoleLineHeight; @@ -182,21 +159,14 @@ void ConsoleDialog::drawDialog() { // Draw the scrollbar _scrollBar->draw(); - - // Finally blit it all to the screen - g_gui.addDirtyRect(_x, _y, _w, _h); } void ConsoleDialog::handleScreenChanged() { - free(_canvas.pixels); - _canvas.pixels = NULL; + Dialog::handleScreenChanged(); draw(); } void ConsoleDialog::handleTickle() { - if (!_canvas.pixels) - return; - uint32 time = g_system->getMillis(); if (_caretTime < time) { _caretTime = time + kCaretBlinkTime; @@ -219,7 +189,7 @@ void ConsoleDialog::handleTickle() { draw(); } else if (_slideMode == kUpSlideMode && _y <= -_h) { // End the slide - _slideMode = kNoSlideMode; + //_slideMode = kNoSlideMode; close(); } else draw(); @@ -614,6 +584,7 @@ void ConsoleDialog::print(const char *str) { } void ConsoleDialog::drawCaret(bool erase) { + // TODO: use code from EditableWidget::drawCaret here int line = _currentPos / _lineWidth; int displayLine = line - _scrollLine + _linesPerPage - 1; @@ -626,17 +597,8 @@ void ConsoleDialog::drawCaret(bool erase) { int x = _x + 1 + (_currentPos % _lineWidth) * kConsoleCharWidth; int y = _y + displayLine * kConsoleLineHeight; - char c = buffer(_currentPos); - if (erase) { - g_gui.fillRect(x, y, kConsoleCharWidth, kConsoleLineHeight, g_gui._bgcolor); - g_gui.drawChar(c, x, y + 2, g_gui._textcolor, _font); - } else { - g_gui.fillRect(x, y, kConsoleCharWidth, kConsoleLineHeight, g_gui._textcolor); - g_gui.drawChar(c, x, y + 2, g_gui._bgcolor, _font); - } - g_gui.addDirtyRect(x, y, kConsoleCharWidth, kConsoleLineHeight); - _caretVisible = !erase; + g_gui.theme()->drawCaret(Common::Rect(x, y, x+kConsoleCharWidth, y+kConsoleLineHeight), erase); } void ConsoleDialog::scrollToCurrent() { diff --git a/gui/console.h b/gui/console.h index 0bd0cb88d1..257ec95b4b 100644 --- a/gui/console.h +++ b/gui/console.h @@ -43,7 +43,6 @@ public: typedef bool (*CompletionCallbackProc)(ConsoleDialog* console, const char *input, char*& completion, void *refCon); protected: - Graphics::Surface _canvas; const Graphics::Font *_font; char _buffer[kBufferSize]; diff --git a/gui/dialog.cpp b/gui/dialog.cpp index fd8cec6a87..c6be54248d 100644 --- a/gui/dialog.cpp +++ b/gui/dialog.cpp @@ -40,7 +40,7 @@ namespace GUI { Dialog::Dialog(int x, int y, int w, int h) : GuiObject(x, y, w, h), - _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _visible(false) { + _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _visible(false), _mainDialog(false) { } Dialog::~Dialog() { @@ -88,6 +88,18 @@ void Dialog::close() { releaseFocus(); } +void Dialog::handleScreenChanged() { + // The screen has changed. That means the screen visual may also have + // changed, so any cached image may be invalid. The subsequent redraw + // should be treated as the very first draw. + + Widget *w = _firstWidget; + while (w) { + w->setHints(THEME_HINT_FIRST_DRAW); + w = w->_next; + } +} + void Dialog::releaseFocus() { if (_focusedWidget) { _focusedWidget->lostFocus(); @@ -104,8 +116,7 @@ void Dialog::drawDialog() { if (!isVisible()) return; - g_gui.blendRect(_x, _y, _w, _h, g_gui._bgcolor); - g_gui.box(_x, _y, _w, _h, g_gui._color, g_gui._shadowcolor); + g_gui.theme()->drawDialogBackground(Common::Rect(_x, _y, _x+_w, _y+_h), Theme::kStateEnabled, _mainDialog); // Draw all children Widget *w = _firstWidget; @@ -113,9 +124,6 @@ void Dialog::drawDialog() { w->draw(); w = w->_next; } - - // Flag the draw area as dirty - g_gui.addDirtyRect(_x, _y, _w, _h); } void Dialog::handleMouseDown(int x, int y, int button, int clickCount) { diff --git a/gui/dialog.h b/gui/dialog.h index 69d851f78a..5170e7c9b2 100644 --- a/gui/dialog.h +++ b/gui/dialog.h @@ -44,6 +44,7 @@ protected: Widget *_focusedWidget; Widget *_dragWidget; bool _visible; + bool _mainDialog; // FIXME: find a better solution for this and change the Theme class to handle it then private: int _result; @@ -73,7 +74,7 @@ protected: virtual void handleKeyUp(uint16 ascii, int keycode, int modifiers); virtual void handleMouseMoved(int x, int y, int button); virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); - virtual void handleScreenChanged() {} + void handleScreenChanged(); Widget *findWidget(int x, int y); // Find the widget at pos x,y if any diff --git a/gui/editable.cpp b/gui/editable.cpp index 07007583b5..3909acb423 100644 --- a/gui/editable.cpp +++ b/gui/editable.cpp @@ -143,7 +143,6 @@ void EditableWidget::drawCaret(bool erase) { Common::Rect editRect = getEditRect(); - int16 color = (erase ^ _caretInverse) ? g_gui._bgcolor : g_gui._textcolorhi; int x = editRect.left; int y = editRect.top + 1; @@ -155,9 +154,8 @@ void EditableWidget::drawCaret(bool erase) { x += getAbsX(); y += getAbsY(); - g_gui.vLine(x, y, y + editRect.height() - 2, color); - g_gui.addDirtyRect(x, y, 2, editRect.height() - 2); - + g_gui.theme()->drawCaret(Common::Rect(x, y, x+editRect.width(), y+editRect.height()-2), erase); + _caretVisible = !erase; } diff --git a/gui/launcher.cpp b/gui/launcher.cpp index 7d9cc18dea..68d5bdb7f2 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -490,6 +490,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat LauncherDialog::LauncherDialog(GameDetector &detector) : Dialog(0, 0, 320, 200), _detector(detector) { + _mainDialog = true; const int screenW = g_system->getOverlayWidth(); const int screenH = g_system->getOverlayHeight(); diff --git a/gui/module.mk b/gui/module.mk index e3b2af7727..9c6eb8faac 100644 --- a/gui/module.mk +++ b/gui/module.mk @@ -16,7 +16,9 @@ MODULE_OBJS := \ gui/PopUpWidget.o \ gui/ScrollBarWidget.o \ gui/TabWidget.o \ - gui/widget.o + gui/widget.o \ + gui/theme.o \ + gui/ThemeNew.o MODULE_DIRS += \ gui diff --git a/gui/newgui.cpp b/gui/newgui.cpp index 11f57fb167..91e7249d36 100644 --- a/gui/newgui.cpp +++ b/gui/newgui.cpp @@ -24,6 +24,8 @@ #include "gui/newgui.h" #include "gui/dialog.h" +#include "common/config-manager.h" + DECLARE_SINGLETON(GUI::NewGui); namespace GUI { @@ -55,7 +57,7 @@ enum { // Constructor NewGui::NewGui() : _needRedraw(false), - _stateIsSaved(false), _font(0), _cursorAnimateCounter(0), _cursorAnimateTimer(0) { + _stateIsSaved(false), _cursorAnimateCounter(0), _cursorAnimateTimer(0) { _system = &OSystem::instance(); @@ -65,32 +67,26 @@ NewGui::NewGui() : _needRedraw(false), // Reset key repeat _currentKeyDown.keycode = 0; - // updates the scaling factor - updateScaleFactor(); -} - -void NewGui::updateColors() { - // Setup some default GUI colors. - _bgcolor = _system->RGBToColor(0, 0, 0); - _color = _system->RGBToColor(104, 104, 104); - _shadowcolor = _system->RGBToColor(64, 64, 64); - _textcolor = _system->RGBToColor(32, 160, 32); - _textcolorhi = _system->RGBToColor(0, 255, 0); -} - -void NewGui::updateScaleFactor() { - const int screenW = g_system->getOverlayWidth(); - const int screenH = g_system->getOverlayHeight(); - - // TODO: Perhaps we should also set the widget size here. That'd allow - // us to remove much of the screen size checking from the individual - // widgets. - - if (screenW >= 400 && screenH >= 300) { - _font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); + ConfMan.registerDefault("gui_theme", "default-theme"); + Common::String style = ConfMan.get("gui_theme"); + if (scumm_stricmp(style.c_str(), "classic") == 0) { + _theme = new ThemeClassic(_system); } else { - _font = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont); + _theme = new ThemeNew(_system, style.c_str()); + } + assert(_theme); + + // Init the theme + if (!_theme->init()) { + warning("Could not initialize your preferred theme, falling back to classic style"); + delete _theme; + _theme = new ThemeClassic(_system); + assert(_theme); + if (!_theme->init()) { + error("Couldn't initialize classic theme"); + } } + _theme->resetDrawArea(); } void NewGui::runLoop() { @@ -101,36 +97,34 @@ void NewGui::runLoop() { if (activeDialog == 0) return; - // Setup some default GUI colors. Normally this will be done whenever an - // EVENT_SCREEN_CHANGED is received. However, not yet all backends support - // that event, so we also do it "manually" whenever a run loop is entered. - updateColors(); - updateScaleFactor(); - if (!_stateIsSaved) { saveState(); + _theme->enable(); didSaveState = true; } + _theme->openDialog(); + while (!_dialogStack.empty() && activeDialog == _dialogStack.top()) { activeDialog->handleTickle(); if (_needRedraw) { // Restore the overlay to its initial state, then draw all dialogs. // This is necessary to get the blending right. - _system->clearOverlay(); - _system->grabOverlay((OverlayColor *)_screen.pixels, _screenPitch); + _theme->clearAll(); + for (int i = 0; i < _dialogStack.size(); ++i) { + _theme->closeDialog(); + } for (int i = 0; i < _dialogStack.size(); i++) { - // For each dialog we draw we have to ensure the correct - // scaling mode is active. - updateScaleFactor(); + _theme->openDialog(); _dialogStack[i]->drawDialog(); } _needRedraw = false; } animateCursor(); + _theme->drawAll(); _system->updateScreen(); OSystem::Event event; @@ -191,8 +185,9 @@ void NewGui::runLoop() { _system->quit(); return; case OSystem::EVENT_SCREEN_CHANGED: - updateColors(); - updateScaleFactor(); + // reinit the whole theme + _theme->refresh(); + _needRedraw = true; activeDialog->handleScreenChanged(); break; } @@ -211,8 +206,12 @@ void NewGui::runLoop() { _system->delayMillis(10); } - if (didSaveState) + _theme->closeDialog(); + + if (didSaveState) { restoreState(); + _theme->disable(); + } } #pragma mark - @@ -221,20 +220,6 @@ void NewGui::saveState() { // Backup old cursor _oldCursorMode = _system->showMouse(true); - // Enable the overlay - _system->showOverlay(); - - // Create a screen buffer for the overlay data, and fill it with - // whatever is visible on the screen rught now. - _screen.h = _system->getOverlayHeight(); - _screen.w = _system->getOverlayWidth(); - _screen.bytesPerPixel = sizeof(OverlayColor); - _screen.pitch = _screen.w * _screen.bytesPerPixel; - _screenPitch = _screen.w; - _screen.pixels = (OverlayColor*)calloc(_screen.w * _screen.h, sizeof(OverlayColor)); - - _system->grabOverlay((OverlayColor *)_screen.pixels, _screenPitch); - _currentKeyDown.keycode = 0; _lastClick.x = _lastClick.y = 0; _lastClick.time = 0; @@ -246,12 +231,6 @@ void NewGui::saveState() { void NewGui::restoreState() { _system->showMouse(_oldCursorMode); - _system->hideOverlay(); - if (_screen.pixels) { - free(_screen.pixels); - _screen.pixels = 0; - } - _system->updateScreen(); _stateIsSaved = false; @@ -272,194 +251,6 @@ void NewGui::closeTopDialog() { _needRedraw = true; } -#pragma mark - - -const Graphics::Font &NewGui::getFont() const { - return *_font; -} - -OverlayColor *NewGui::getBasePtr(int x, int y) { - return (OverlayColor *)_screen.getBasePtr(x, y); -} - -void NewGui::box(int x, int y, int width, int height, OverlayColor colorA, OverlayColor colorB) { - hLine(x + 1, y, x + width - 2, colorA); - hLine(x, y + 1, x + width - 1, colorA); - vLine(x, y + 1, y + height - 2, colorA); - vLine(x + 1, y, y + height - 1, colorA); - - hLine(x + 1, y + height - 2, x + width - 1, colorB); - hLine(x + 1, y + height - 1, x + width - 2, colorB); - vLine(x + width - 1, y + 1, y + height - 2, colorB); - vLine(x + width - 2, y + 1, y + height - 1, colorB); -} - -void NewGui::hLine(int x, int y, int x2, OverlayColor color) { - _screen.hLine(x, y, x2, color); -} - -void NewGui::vLine(int x, int y, int y2, OverlayColor color) { - _screen.vLine(x, y, y2, color); -} - -void NewGui::copyToSurface(Graphics::Surface *s, int x, int y, int w, int h) { - Common::Rect rect(x, y, x + w, y + h); - rect.clip(_screen.w, _screen.h); - - if (!rect.isValidRect()) - return; - - s->w = rect.width(); - s->h = rect.height(); - s->bytesPerPixel = sizeof(OverlayColor); - s->pitch = s->w * s->bytesPerPixel; - s->pixels = (OverlayColor *)malloc(s->pitch * s->h); - - w = s->w; - h = s->h; - - OverlayColor *dst = (OverlayColor *)s->pixels; - OverlayColor *src = getBasePtr(rect.left, rect.top); - - while (h--) { - memcpy(dst, src, s->pitch); - src += _screenPitch; - dst += s->w; - } -} - -void NewGui::drawSurface(const Graphics::Surface &s, int x, int y) { - Common::Rect rect(x, y, x + s.w, y + s.h); - rect.clip(_screen.w, _screen.h); - - if (!rect.isValidRect()) - return; - - assert(s.bytesPerPixel == sizeof(OverlayColor)); - - OverlayColor *src = (OverlayColor *)s.pixels; - OverlayColor *dst = getBasePtr(rect.left, rect.top); - - int w = rect.width(); - int h = rect.height(); - - while (h--) { - memcpy(dst, src, s.pitch); - src += w; - dst += _screenPitch; - } -} - -void NewGui::blendRect(int x, int y, int w, int h, OverlayColor color, int level) { -#ifdef NEWGUI_256 - fillRect(x, y, w, h, color); -#else - Common::Rect rect(x, y, x + w, y + h); - rect.clip(_screen.w, _screen.h); - - if (!rect.isValidRect()) - return; - - if (_system->hasFeature(OSystem::kFeatureOverlaySupportsAlpha)) { - - int a, r, g, b; - uint8 aa, ar, ag, ab; - _system->colorToARGB(color, aa, ar, ag, ab); - a = aa*level/(level+1); - if (a < 1) - return; - r = ar * a; - g = ag * a; - b = ab * a; - - OverlayColor *ptr = getBasePtr(rect.left, rect.top); - - h = rect.height(); - w = rect.width(); - while (h--) { - for (int i = 0; i < w; i++) { - _system->colorToARGB(ptr[i], aa, ar, ag, ab); - int a2 = aa + a - (a*aa)/255; - ptr[i] = _system->ARGBToColor(a2, - ((255-a)*aa*ar/255+r)/a2, - ((255-a)*aa*ag/255+g)/a2, - ((255-a)*aa*ab/255+b)/a2); - } - ptr += _screenPitch; - } - - } else { - - int r, g, b; - uint8 ar, ag, ab; - _system->colorToRGB(color, ar, ag, ab); - r = ar * level; - g = ag * level; - b = ab * level; - - OverlayColor *ptr = getBasePtr(rect.left, rect.top); - - h = rect.height(); - w = rect.width(); - - while (h--) { - for (int i = 0; i < w; i++) { - _system->colorToRGB(ptr[i], ar, ag, ab); - ptr[i] = _system->RGBToColor((ar + r) / (level+1), - (ag + g) / (level+1), - (ab + b) / (level+1)); - } - ptr += _screenPitch; - } - - } -#endif -} - -void NewGui::fillRect(int x, int y, int w, int h, OverlayColor color) { - _screen.fillRect(Common::Rect(x, y, x + w, y + h), color); -} - -void NewGui::frameRect(int x, int y, int w, int h, OverlayColor color) { - _screen.frameRect(Common::Rect(x, y, x + w, y + h), color); -} - -void NewGui::addDirtyRect(int x, int y, int w, int h) { - Common::Rect rect(x, y, x + w, y + h); - rect.clip(_screen.w, _screen.h); - - if (!rect.isValidRect()) - return; - - // For now we don't keep yet another list of dirty rects but simply - // blit the affected area directly to the overlay. At least for our current - // GUI/widget/dialog code that is just fine. - OverlayColor *buf = getBasePtr(rect.left, rect.top); - _system->copyRectToOverlay(buf, _screenPitch, rect.left, rect.top, rect.width(), rect.height()); -} - -void NewGui::drawChar(byte chr, int xx, int yy, OverlayColor color, const Graphics::Font *font) { - if (font == 0) - font = &getFont(); - font->drawChar(&_screen, chr, xx, yy, color); -} - -int NewGui::getStringWidth(const String &str) const { - return getFont().getStringWidth(str); -} - -int NewGui::getCharWidth(byte c) const { - return getFont().getCharWidth(c); -} - -int NewGui::getFontHeight() const { - return getFont().getFontHeight(); -} - -void NewGui::drawString(const String &s, int x, int y, int w, OverlayColor color, TextAlignment align, int deltax, bool useEllipsis) { - getFont().drawString(&_screen, s, x, y, w, color, align, deltax, useEllipsis); -} - // // Draw the mouse cursor (animated). This is mostly ripped from the cursor code in gfx.cpp // We could plug in a different cursor here if we like to. diff --git a/gui/newgui.h b/gui/newgui.h index 51cc2af2ce..2307f90fc5 100644 --- a/gui/newgui.h +++ b/gui/newgui.h @@ -26,6 +26,7 @@ #include "common/stack.h" #include "common/str.h" #include "graphics/fontman.h" +#include "gui/theme.h" class OSystem; @@ -67,18 +68,23 @@ public: bool isActive() const { return ! _dialogStack.empty(); } + Theme *theme() { return _theme; } + + const Graphics::Font &getFont() const { return *(_theme->getFont()); } + int getFontHeight() const { return _theme->getFontHeight(); } + int getStringWidth(const Common::String &str) const { return _theme->getStringWidth(str); } + int getCharWidth(byte c) const { return _theme->getCharWidth(c); } + protected: OSystem *_system; - Graphics::Surface _screen; - int _screenPitch; + + Theme *_theme; bool _needRedraw; DialogStack _dialogStack; bool _stateIsSaved; - const Graphics::Font *_font; - // for continuous events (keyDown) struct { uint16 ascii; @@ -109,54 +115,6 @@ protected: void loop(); void animateCursor(); - void updateColors(); - - void updateScaleFactor(); - - OverlayColor *getBasePtr(int x, int y); - -public: - // Theme colors - OverlayColor _color, _shadowcolor; - OverlayColor _bgcolor; - OverlayColor _textcolor; - OverlayColor _textcolorhi; - - // Font - const Graphics::Font &getFont() const; - - // Screen surface - Graphics::Surface &getScreen() { return _screen; } - - // Drawing primitives - void box(int x, int y, int width, int height, OverlayColor colorA, OverlayColor colorB); - void hLine(int x, int y, int x2, OverlayColor color); - void vLine(int x, int y, int y2, OverlayColor color); - - /** - * Copy the specified screen rectangle into a new graphics surfaces. - * New memory for the GFX data is allocated via malloc; it is the - * callers responsibilty to free that data. - */ - void copyToSurface(Graphics::Surface *s, int x, int y, int w, int h); - - /** - * Draw the graphics contained in the given surface at the specified coordinates. - */ - void drawSurface(const Graphics::Surface &s, int x, int y); - - void blendRect(int x, int y, int w, int h, OverlayColor color, int level = 3); - void fillRect(int x, int y, int w, int h, OverlayColor color); - void frameRect(int x, int y, int w, int h, OverlayColor color); - - void drawChar(byte c, int x, int y, OverlayColor color, const Graphics::Font *font = 0); - void drawString(const String &str, int x, int y, int w, OverlayColor color, Graphics::TextAlignment align = Graphics::kTextAlignLeft, int deltax = 0, bool useEllipsis = true); - - int getStringWidth(const String &str) const; - int getCharWidth(byte c) const; - int getFontHeight() const; - - void addDirtyRect(int x, int y, int w, int h); }; } // End of namespace GUI diff --git a/gui/theme.cpp b/gui/theme.cpp new file mode 100644 index 0000000000..dfb8dcbebb --- /dev/null +++ b/gui/theme.cpp @@ -0,0 +1,516 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Header $ + */ + +#include "gui/theme.h" + +namespace GUI { +ThemeClassic::ThemeClassic(OSystem *system) : Theme() { + _system = system; + _initOk = false; + memset(&_screen, 0, sizeof(_screen)); +#ifdef OLDGUI_TRANSPARENCY + memset(&_dialog, 0, sizeof(_dialog)); +#endif + _font = 0; + + // Maybe change this filename + _configFile.loadFromFile("classic.ini"); +} + +ThemeClassic::~ThemeClassic() { + deinit(); +} + +bool ThemeClassic::init() { + deinit(); + _screen.create(_system->getOverlayWidth(), _system->getOverlayHeight(), sizeof(OverlayColor)); + if (_screen.pixels) { + _initOk = true; + clearAll(); + _bgcolor = _system->RGBToColor(0, 0, 0); + _color = _system->RGBToColor(104, 104, 104); + _shadowcolor = _system->RGBToColor(64, 64, 64); + _textcolor = _system->RGBToColor(32, 160, 32); + _textcolorhi = _system->RGBToColor(0, 255, 0); + if (_screen.w >= 400 && _screen.h >= 300) { + _font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); + } else { + _font = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont); + } + } + return true; +} + +void ThemeClassic::deinit() { + if (_initOk) { + _system->hideOverlay(); + _screen.free(); + _initOk = false; + } +} + +void ThemeClassic::refresh() { + init(); + _bgcolor = _system->RGBToColor(0, 0, 0); + _color = _system->RGBToColor(104, 104, 104); + _shadowcolor = _system->RGBToColor(64, 64, 64); + _textcolor = _system->RGBToColor(32, 160, 32); + _textcolorhi = _system->RGBToColor(0, 255, 0); + _system->showOverlay(); +} + +void ThemeClassic::enable() { + _system->showOverlay(); + clearAll(); +} + +void ThemeClassic::disable() { + _system->hideOverlay(); +} + +void ThemeClassic::openDialog() { +#ifdef OLDGUI_TRANSPARENCY + if (!_dialog) { + _dialog = new DialogState; + assert(_dialog); + // first dialog + _dialog->screen.create(_screen.w, _screen.h, sizeof(OverlayColor)); + } + memcpy(_dialog->screen.pixels, _screen.pixels, _screen.pitch*_screen.h); + blendScreenToDialog(); +#endif +} + +void ThemeClassic::closeDialog() { +#ifdef OLDGUI_TRANSPARENCY + if (_dialog) { + _dialog->screen.free(); + delete _dialog; + _dialog = 0; + } +#endif +} + +void ThemeClassic::clearAll() { + if (!_initOk) + return; + _system->clearOverlay(); + // FIXME: problem with the 'pitch' + _system->grabOverlay((OverlayColor*)_screen.pixels, _screen.w); +} + +void ThemeClassic::drawAll() { + if (!_initOk) + return; +} + +void ThemeClassic::resetDrawArea() { + if (_initOk) { + _drawArea = Common::Rect(0, 0, _screen.w, _screen.h); + } +} + +void ThemeClassic::drawDialogBackground(const Common::Rect &r, kState state, bool mainDialog) { + if (!_initOk) + return; + + restoreBackground(r); + box(r.left, r.top, r.width(), r.height(), _color, _shadowcolor); + addDirtyRect(r); +} + +void ThemeClassic::drawText(const Common::Rect &r, const Common::String &str, kState state, kTextAlign align, bool inverted, int deltax, bool useEllipsis) { + if (!_initOk) + return; + + if (!inverted) { + restoreBackground(r); + _font->drawString(&_screen, str, r.left, r.top, r.width(), getColor(state), convertAligment(align), deltax, useEllipsis); + } else { + _screen.fillRect(r, getColor(state)); + _font->drawString(&_screen, str, r.left, r.top, r.width(), _bgcolor, convertAligment(align), deltax, useEllipsis); + } + + addDirtyRect(r); +} + +void ThemeClassic::drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, kState state) { + if (!_initOk) + return; + restoreBackground(r); + font->drawChar(&_screen, ch, r.left, r.top, getColor(state)); + addDirtyRect(r); +} + +void ThemeClassic::drawWidgetBackground(const Common::Rect &r, uint16 hints, kWidgetBackground background, kState state) { + if (!_initOk || background == kWidgetBackgroundNo) + return; + + switch (background) { + case kWidgetBackgroundBorder: + restoreBackground(r); + box(r.left, r.top, r.width(), r.height(), _color, _shadowcolor); + break; + + case kWidgetBackgroundBorderSmall: + restoreBackground(r); + box(r.left, r.top, r.width(), r.height()); + break; + + case kWidgetBackgroundPlain: + restoreBackground(r); + break; + + default: + break; + }; + + addDirtyRect(r); +} + +void ThemeClassic::drawButton(const Common::Rect &r, const Common::String &str, kState state) { + if (!_initOk) + return; + restoreBackground(r); + + drawWidgetBackground(r, 0, kWidgetBackgroundBorder, state); + + const int off = (r.height() - _font->getFontHeight()) / 2; + _font->drawString(&_screen, str, r.left, r.top+off, r.width(), getColor(state), Graphics::kTextAlignCenter, 0, false); + + addDirtyRect(r); +} + +void ThemeClassic::drawSurface(const Common::Rect &r, const Graphics::Surface &surface, kState state) { + if (!_initOk) + return; + + Common::Rect rect(r.left, r.top, r.left + surface.w, r.top + surface.h); + rect.clip(_screen.w, _screen.h); + + if (!rect.isValidRect()) + return; + + assert(surface.bytesPerPixel == sizeof(OverlayColor)); + + OverlayColor *src = (OverlayColor *)surface.pixels; + OverlayColor *dst = (OverlayColor *)_screen.getBasePtr(rect.left, rect.top); + + int w = rect.width(); + int h = rect.height(); + + while (h--) { + memcpy(dst, src, surface.pitch); + src += w; + // FIXME: this should be pitch + dst += _screen.w; + } + addDirtyRect(r); +} + +void ThemeClassic::drawSlider(const Common::Rect &r, int width, kState state) { + if (!_initOk) + return; + Common::Rect r2 = r; + + restoreBackground(r); + + box(r.left, r.top, r.width(), r.height(), _color, _shadowcolor); + r2.left = r.left + 2; + r2.top = r.top + 2; + r2.bottom = r.bottom - 2; + r2.right = r2.left + width; + if (r2.right > r.right - 2) { + r2.right = r.right - 2; + } + + _screen.fillRect(r2, getColor(state)); + + addDirtyRect(r); +} + +void ThemeClassic::drawCheckbox(const Common::Rect &r, const Common::String &str, bool checked, kState state) { + if (!_initOk) + return; + + Common::Rect r2 = r; + int checkBoxSize = getFontHeight(); + if (checkBoxSize > r.height()) { + checkBoxSize = r.height(); + } + r2.bottom = r2.top + checkBoxSize; + + restoreBackground(r2); + + box(r.left, r.top, checkBoxSize, checkBoxSize, _color, _shadowcolor); + + if (checked) { + // TODO: implement old style + r2.top += 2; + r2.bottom = r.top + checkBoxSize - 2; + r2.left += 2; + r2.right = r.left + checkBoxSize - 2; + _screen.fillRect(r2, getColor(state)); + r2 = r; + } + + r2.left += checkBoxSize + 10; + _font->drawString(&_screen, str, r2.left, r2.top, r2.width(), getColor(state), Graphics::kTextAlignCenter, 0, true); + + addDirtyRect(r); +} + +void ThemeClassic::drawTab(const Common::Rect &r, const Common::String &str, bool active, kState state) { + if (!_initOk) + return; + restoreBackground(r); + box(r.left, r.top, r.width(), r.height(), _color, _shadowcolor); + _font->drawString(&_screen, str, r.left, r.top+2, r.width(), getColor(state), Graphics::kTextAlignCenter, 0, true); + addDirtyRect(r); +} + +void ThemeClassic::drawScrollbar(const Common::Rect &r, int sliderY, int sliderHeight, kScrollbarState scroll, kState state) { + if (!_initOk) + return; + restoreBackground(r); + Common::Rect r2 = r; + box(r.left, r.top, r.width(), r.height(), _color, _shadowcolor); + + const int UP_DOWN_BOX_HEIGHT = r.width() + 1; + const int B = 3; + const int arrowSize = (r.width() / 2 - B + 1); + + OverlayColor color = 0; + if (scroll == kScrollbarStateSinglePage) { + color = _color; + } else if (scroll == kScrollbarStateUp && state == kStateHighlight) { + color = _textcolorhi; + } else { + color = _textcolor; + } + + // draws the 'up' button + box(r.left, r.top, r.width(), UP_DOWN_BOX_HEIGHT, _color, _shadowcolor); + Common::Point p0 = Common::Point(r.left + r.width() / 2, r.top + (UP_DOWN_BOX_HEIGHT - arrowSize - 1) / 2); + Common::Point p1 = Common::Point(p0.x - arrowSize, p0.y + arrowSize); + Common::Point p2 = Common::Point(p0.x + arrowSize, p0.y + arrowSize); + for (; p1.x <= p2.x; ++p1.x) + _screen.drawLine(p0.x, p0.y, p1.x, p1.y, color); + + if (scroll != kScrollbarStateSinglePage) { + r2.top += sliderY; + r2.left += 2; + r2.right -= 2; + r2.bottom = r2.top + sliderHeight; + _screen.fillRect(r2, (state == kStateHighlight && scroll == kScrollbarStateSlider) ? _textcolorhi : _textcolor); + box(r2.left, r2.top, r2.width(), r2.height()); + int y = r2.top + sliderHeight / 2; + color = (state == kStateHighlight && scroll == kScrollbarStateSlider) ? _color : _bgcolor; + _screen.hLine(r2.left + 1, y - 2, r2.right - 2, color); + _screen.hLine(r2.left + 1, y, r2.right - 2, color); + _screen.hLine(r2.left + 1, y + 2, r2.right - 2, color); + r2 = r; + } + + r2.top = r2.bottom - UP_DOWN_BOX_HEIGHT; + if (scroll == kScrollbarStateSinglePage) { + color = _color; + } else if (scroll == kScrollbarStateDown && state == kStateHighlight) { + color = _textcolorhi; + } else { + color = _textcolor; + } + + // draws the 'down' button + box(r2.left, r2.top, r2.width(), UP_DOWN_BOX_HEIGHT, _color, _shadowcolor); + p0 = Common::Point(r2.left + r2.width() / 2, r2.top + (UP_DOWN_BOX_HEIGHT + arrowSize + 1) / 2); + p1 = Common::Point(p0.x - arrowSize, p0.y - arrowSize); + p2 = Common::Point(p0.x + arrowSize, p0.y - arrowSize); + for (; p1.x <= p2.x; ++p1.x) + _screen.drawLine(p0.x, p0.y, p1.x, p1.y, color); + + addDirtyRect(r); +} + +void ThemeClassic::drawCaret(const Common::Rect &r, bool erase, kState state) { + if (!_initOk) + return; + + OverlayColor color = 0; + if (erase) { + color = _bgcolor; + } else { + color = getColor(state); + } + + _screen.vLine(r.left, r.top, r.bottom - 2, color); + addDirtyRect(r); +} + +void ThemeClassic::drawLineSeparator(const Common::Rect &r, kState state) { + if (!_initOk) + return; + _screen.hLine(r.left - 1, r.top + r.height() / 2, r.right, _shadowcolor); + _screen.hLine(r.left, r.top + 1 + r.height() / 2, r.right, _color); + addDirtyRect(r); +} + +// intern drawing + +void ThemeClassic::restoreBackground(Common::Rect r) { + r.clip(_screen.w, _screen.h); +#ifndef OLDGUI_TRANSPARENCY + _screen.fillRect(r, _bgcolor); +#else + if (_dialog) { + if (!_dialog->screen.pixels) { + _screen.fillRect(r, _bgcolor); + return; + } + const OverlayColor *src = (const OverlayColor*)_dialog->screen.getBasePtr(r.left, r.top); + OverlayColor *dst = (OverlayColor*)_screen.getBasePtr(r.left, r.top); + + int h = r.height(); + int w = r.width(); + while (h--) { + memcpy(dst, src, w*sizeof(OverlayColor)); + src += _dialog->screen.w; + dst += _screen.w; + } + } else { + _screen.fillRect(r, _bgcolor); + } +#endif +} + +bool ThemeClassic::addDirtyRect(Common::Rect r) { + // TODO: implement proper dirty rect handling + // FIXME: problem with the 'pitch' + r.clip(_screen.w, _screen.h); + r.clip(_drawArea); + _system->copyRectToOverlay((OverlayColor*)_screen.getBasePtr(r.left, r.top), _screen.w, r.left, r.top, r.width(), r.height()); + return true; +} + +void ThemeClassic::box(int x, int y, int width, int height, OverlayColor colorA, OverlayColor colorB) { + if (y >= 0) { + _screen.hLine(x + 1, y, x + width - 2, colorA); + _screen.hLine(x, y + 1, x + width - 1, colorA); + } + int drawY = y; + if (drawY < 0) { + height += drawY; + drawY = 0; + } + _screen.vLine(x, drawY + 1, drawY + height - 2, colorA); + _screen.vLine(x + 1, drawY, drawY + height - 1, colorA); + + if (y + height >= 0) { + _screen.hLine(x + 1, drawY + height - 2, x + width - 1, colorB); + _screen.hLine(x + 1, drawY + height - 1, x + width - 2, colorB); + _screen.vLine(x + width - 1, drawY + 1, drawY + height - 2, colorB); + _screen.vLine(x + width - 2, drawY + 1, drawY + height - 1, colorB); + } +} + +void ThemeClassic::box(int x, int y, int w, int h) { + _screen.hLine(x, y, x + w - 1, _color); + _screen.hLine(x, y + h - 1, x +w - 1, _shadowcolor); + _screen.vLine(x, y, y + h - 1, _color); + _screen.vLine(x + w - 1, y, y + h - 1, _shadowcolor); +} + +OverlayColor ThemeClassic::getColor(kState state) { + OverlayColor usedColor = _color; + switch (state) { + case kStateEnabled: + usedColor = _textcolor; + break; + + case kStateHighlight: + usedColor = _textcolorhi; + break; + + default: + break; + } + return usedColor; +} + +#ifdef OLDGUI_TRANSPARENCY +void ThemeClassic::blendScreenToDialog() { + Common::Rect rect(0, 0, _screen.w, _screen.h); + + if (!rect.isValidRect()) + return; + + if (_system->hasFeature(OSystem::kFeatureOverlaySupportsAlpha)) { + int a, r, g, b; + uint8 aa, ar, ag, ab; + _system->colorToARGB(_bgcolor, aa, ar, ag, ab); + a = aa*3/(3+1); + if (a < 1) + return; + r = ar * a; + g = ag * a; + b = ab * a; + + OverlayColor *ptr = (OverlayColor*)_dialog->screen.getBasePtr(rect.left, rect.top); + + int h = rect.height(); + int w = rect.width(); + while (h--) { + for (int i = 0; i < w; i++) { + _system->colorToARGB(ptr[i], aa, ar, ag, ab); + int a2 = aa + a - (a*aa)/255; + ptr[i] = _system->ARGBToColor(a2, + ((255-a)*aa*ar/255+r)/a2, + ((255-a)*aa*ag/255+g)/a2, + ((255-a)*aa*ab/255+b)/a2); + } + ptr += _screen.w; + } + } else { + int r, g, b; + uint8 ar, ag, ab; + _system->colorToRGB(_bgcolor, ar, ag, ab); + r = ar * 3; + g = ag * 3; + b = ab * 3; + + OverlayColor *ptr = (OverlayColor*)_dialog->screen.getBasePtr(rect.left, rect.top); + + int h = rect.height(); + int w = rect.width(); + + while (h--) { + for (int i = 0; i < w; i++) { + _system->colorToRGB(ptr[i], ar, ag, ab); + ptr[i] = _system->RGBToColor((ar + r) / (3+1), + (ag + g) / (3+1), + (ab + b) / (3+1)); + } + ptr += _screen.w; + } + } +} +#endif +} // end of namespace GUI + diff --git a/gui/theme.h b/gui/theme.h new file mode 100644 index 0000000000..b6730199ef --- /dev/null +++ b/gui/theme.h @@ -0,0 +1,361 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Header $ + */ + +#ifndef GUI_THEME_H +#define GUI_THEME_H + +#include "common/stdafx.h" +#include "common/system.h" +#include "common/rect.h" +#include "common/str.h" +#include "common/config-file.h" + +#include "graphics/surface.h" +#include "graphics/fontman.h" + +namespace GUI { + +// Hints to the theme engine that the widget is used in a non-standard way. + +enum { + // Indicates that this is the first time the widget is drawn. + THEME_HINT_FIRST_DRAW = 1 << 0, + + // Indicates that the widget will be redrawn often, e.g. list widgets. + // It may therefore be a good idea to save the background so that it + // can be redrawn quickly. + THEME_HINT_SAVE_BACKGROUND = 1 << 1 +}; + +class Theme { +public: + Theme() : _drawArea(), _configFile() {} + virtual ~Theme() {} + + enum kTextAlign { + kTextAlignLeft, + kTextAlignCenter, + kTextAlignRight + }; + + enum kWidgetBackground { + kWidgetBackgroundNo, + kWidgetBackgroundPlain, + kWidgetBackgroundBorder, + kWidgetBackgroundBorderSmall + }; + + enum kState { + kStateDisabled, + kStateEnabled, + kStateHighlight + }; + + enum kScrollbarState { + kScrollbarStateNo, + kScrollbarStateUp, + kScrollbarStateDown, + kScrollbarStateSlider, + kScrollbarStateSinglePage + }; + + virtual bool init() = 0; + virtual void deinit() = 0; + + virtual void refresh() = 0; + + virtual void enable() = 0; + virtual void disable() = 0; + + virtual void openDialog() = 0; + virtual void closeDialog() = 0; + + virtual void clearAll() = 0; + virtual void drawAll() = 0; + + virtual void setDrawArea(const Common::Rect &r) { _drawArea = r; } + // resets the draw area to the screen size + virtual void resetDrawArea() = 0; + + virtual const Common::ConfigFile &getConfigFile() { return _configFile; } + + virtual const Graphics::Font *getFont() const = 0; + virtual int getFontHeight() const = 0; + virtual int getStringWidth(const Common::String &str) const = 0; + virtual int getCharWidth(byte c) const = 0; + + virtual void drawDialogBackground(const Common::Rect &r, kState state = kStateEnabled, bool mainDialog = false) = 0; + virtual void drawText(const Common::Rect &r, const Common::String &str, kState state = kStateEnabled, kTextAlign align = kTextAlignCenter, bool inverted = false, int deltax = 0, bool useEllipsis = true) = 0; + // this should ONLY be used by the debugger until we get a nicer solution + virtual void drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, kState state = kStateEnabled) = 0; + + virtual void drawWidgetBackground(const Common::Rect &r, uint16 hints, kWidgetBackground background = kWidgetBackgroundPlain, kState state = kStateEnabled) = 0; + virtual void drawButton(const Common::Rect &r, const Common::String &str, kState state = kStateEnabled) = 0; + virtual void drawSurface(const Common::Rect &r, const Graphics::Surface &surface, kState state = kStateEnabled) = 0; + virtual void drawSlider(const Common::Rect &r, int width, kState state = kStateEnabled) = 0; + virtual void drawCheckbox(const Common::Rect &r, const Common::String &str, bool checked, kState state = kStateEnabled) = 0; + virtual void drawTab(const Common::Rect &r, const Common::String &str, bool active, kState state = kStateEnabled) = 0; + virtual void drawScrollbar(const Common::Rect &r, int sliderY, int sliderHeight, kScrollbarState, kState state = kStateEnabled) = 0; + virtual void drawCaret(const Common::Rect &r, bool erase, kState state = kStateEnabled) = 0; + virtual void drawLineSeparator(const Common::Rect &r, kState state = kStateEnabled) = 0; + + Graphics::TextAlignment convertAligment(kTextAlign align) const { + switch (align) { + case kTextAlignLeft: + return Graphics::kTextAlignLeft; + break; + + case kTextAlignRight: + return Graphics::kTextAlignRight; + break; + + default: + break; + }; + return Graphics::kTextAlignCenter; + }; + + kTextAlign convertAligment(Graphics::TextAlignment align) const { + switch (align) { + case Graphics::kTextAlignLeft: + return kTextAlignLeft; + break; + + case Graphics::kTextAlignRight: + return kTextAlignRight; + break; + + default: + break; + } + return kTextAlignCenter; + } + +protected: + Common::Rect _drawArea; + Common::ConfigFile _configFile; +}; + +#define OLDGUI_TRANSPARENCY + +class ThemeClassic : public Theme { +public: + ThemeClassic(OSystem *system); + virtual ~ThemeClassic(); + + bool init(); + void deinit(); + + void refresh(); + + void enable(); + void disable(); + + void openDialog(); + void closeDialog(); + + void clearAll(); + void drawAll(); + + void resetDrawArea(); + + const Graphics::Font *getFont() const { return _font; } + int getFontHeight() const { if (_initOk) return _font->getFontHeight(); return 0; } + int getStringWidth(const Common::String &str) const { if (_initOk) return _font->getStringWidth(str); return 0; } + int getCharWidth(byte c) const { if (_initOk) return _font->getCharWidth(c); return 0; } + + void drawDialogBackground(const Common::Rect &r, kState state, bool mainDialog); + void drawText(const Common::Rect &r, const Common::String &str, kState state, kTextAlign align, bool inverted, int deltax, bool useEllipsis); + void drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, kState state); + + void drawWidgetBackground(const Common::Rect &r, uint16 hints, kWidgetBackground background, kState state); + void drawButton(const Common::Rect &r, const Common::String &str, kState state); + void drawSurface(const Common::Rect &r, const Graphics::Surface &surface, kState state); + void drawSlider(const Common::Rect &r, int width, kState state); + void drawCheckbox(const Common::Rect &r, const Common::String &str, bool checked, kState state); + void drawTab(const Common::Rect &r, const Common::String &str, bool active, kState state); + void drawScrollbar(const Common::Rect &r, int sliderY, int sliderHeight, kScrollbarState, kState state); + void drawCaret(const Common::Rect &r, bool erase, kState state); + void drawLineSeparator(const Common::Rect &r, kState state); +private: + void restoreBackground(Common::Rect r); + bool addDirtyRect(Common::Rect r); + + void box(int x, int y, int width, int height, OverlayColor colorA, OverlayColor colorB); + void box(int x, int y, int width, int height); + + OverlayColor getColor(kState state); + + OSystem *_system; + Graphics::Surface _screen; + +#ifdef OLDGUI_TRANSPARENCY + struct DialogState { + Graphics::Surface screen; + } *_dialog; + + void blendScreenToDialog(); +#endif + + bool _initOk; + + const Graphics::Font *_font; + OverlayColor _color, _shadowcolor; + OverlayColor _bgcolor; + OverlayColor _textcolor; + OverlayColor _textcolorhi; +}; + +class ThemeNew : public Theme { +public: + ThemeNew(OSystem *system, Common::String stylefile); + virtual ~ThemeNew(); + + bool init(); + void deinit(); + + void refresh(); + + void enable(); + void disable(); + + void openDialog(); + void closeDialog(); + + void clearAll(); + void drawAll(); + + void resetDrawArea(); + + const Graphics::Font *getFont() const { return _font; } + int getFontHeight() const { if (_font) return _font->getFontHeight(); return 0; } + int getStringWidth(const Common::String &str) const { if (_font) return _font->getStringWidth(str); return 0; } + int getCharWidth(byte c) const { if (_font) return _font->getCharWidth(c); return 0; } + + void drawDialogBackground(const Common::Rect &r, kState state, bool mainDialog); + void drawText(const Common::Rect &r, const Common::String &str, kState state, kTextAlign align, bool inverted, int deltax, bool useEllipsis); + void drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, kState state); + + void drawWidgetBackground(const Common::Rect &r, uint16 hints, kWidgetBackground background, kState state); + void drawButton(const Common::Rect &r, const Common::String &str, kState state); + void drawSurface(const Common::Rect &r, const Graphics::Surface &surface, kState state); + void drawSlider(const Common::Rect &r, int width, kState state); + void drawCheckbox(const Common::Rect &r, const Common::String &str, bool checked, kState state); + void drawTab(const Common::Rect &r, const Common::String &str, bool active, kState state); + void drawScrollbar(const Common::Rect &r, int sliderY, int sliderHeight, kScrollbarState, kState state); + void drawCaret(const Common::Rect &r, bool erase, kState state); + void drawLineSeparator(const Common::Rect &r, kState state); +private: + bool addDirtyRect(Common::Rect r, bool backup = false); + + void colorFade(const Common::Rect &r, OverlayColor start, OverlayColor end); + void drawRect(const Common::Rect &r, const Graphics::Surface *corner, + const Graphics::Surface *top, const Graphics::Surface *left, const Graphics::Surface *fill, int alpha); + void drawRectMasked(const Common::Rect &r, const Graphics::Surface *corner, const Graphics::Surface *top, + const Graphics::Surface *left, const Graphics::Surface *fill, int alpha, + OverlayColor start, OverlayColor end, uint factor = 1); + void drawSurface(const Common::Rect &r, const Graphics::Surface *surf, bool upDown, bool leftRight, int alpha); + void drawSurfaceMasked(const Common::Rect &r, const Graphics::Surface *surf, bool upDown, bool leftRight, int alpha, + OverlayColor start, OverlayColor end, uint factor = 1); + + OSystem *_system; + Graphics::Surface _screen; + + bool _initOk; + bool _forceRedraw; + + void restoreBackground(Common::Rect r); + OverlayColor getColor(kState state); + + struct DialogState { + Graphics::Surface screen; + } *_dialog; + + const Graphics::Font *_font; + + enum kImageHandles { + kDialogBkgdCorner = 0, + kDialogBkgdTop = 1, + kDialogBkgdLeft = 2, + kDialogBkgd = 3, + kWidgetBkgdCorner = 4, + kWidgetBkgdTop = 5, + kWidgetBkgdLeft = 6, + kWidgetBkgd = 7, + kCheckboxEmpty = 8, + kCheckboxChecked = 9, + kWidgetArrow = 10, + kImageHandlesMax + }; + + const Common::String *_imageHandles; + const Graphics::Surface **_images; + + enum kColorHandles { + kMainDialogStart = 0, + kMainDialogEnd = 1, + + kDialogStart = 2, + kDialogEnd = 3, + + kColorStateDisabled = 4, + kColorStateHighlight = 5, + kColorStateEnabled = 6, + kColorTransparency = 7, + + kTextInvertedBackground = 8, + kTextInvertedColor = 9, + + kWidgetBackgroundStart = 10, + kWidgetBackgroundEnd = 11, + kWidgetBackgroundSmallStart = 12, + kWidgetBackgroundSmallEnd = 13, + + kButtonBackgroundStart = 14, + kButtonBackgroundEnd = 15, + kButtonTextEnabled = 16, + kButtonTextDisabled = 17, + kButtonTextHighlight = 18, + + kSliderBackgroundStart = 19, + kSliderBackgroundEnd = 20, + kSliderStart = 21, + kSliderEnd = 22, + + kTabBackgroundStart = 23, + kTabBackgroundEnd = 24, + + kScrollbarBackgroundStart = 25, + kScrollbarBackgroundEnd = 26, + kScrollbarButtonStart = 27, + kScrollbarButtonEnd = 28, + kScrollbarSliderStart = 29, + kScrollbarSliderEnd = 30, + + kCaretColor = 31, + + kColorHandlesMax + }; + + OverlayColor _colors[kColorHandlesMax]; +}; +} // end of namespace GUI + +#endif // GUI_THEME_H diff --git a/gui/themes/default-theme.zip b/gui/themes/default-theme.zip Binary files differnew file mode 100644 index 0000000000..0f52429200 --- /dev/null +++ b/gui/themes/default-theme.zip diff --git a/gui/widget.cpp b/gui/widget.cpp index 3633715ce0..6608c4fd57 100644 --- a/gui/widget.cpp +++ b/gui/widget.cpp @@ -29,7 +29,7 @@ namespace GUI { Widget::Widget(GuiObject *boss, int x, int y, int w, int h) : GuiObject(x, y, w, h), _type(0), _boss(boss), - _id(0), _flags(0), _hasFocus(false) { + _id(0), _flags(0), _hints(THEME_HINT_FIRST_DRAW), _hasFocus(false) { // Insert into the widget list of the boss _next = _boss->_firstWidget; _boss->_firstWidget = this; @@ -52,16 +52,12 @@ void Widget::draw() { _y = getAbsY(); // Clear background (unless alpha blending is enabled) - if (_flags & WIDGET_CLEARBG) - gui->fillRect(_x, _y, _w, _h, gui->_bgcolor); + //if (_flags & WIDGET_CLEARBG) + // gui->fillRect(_x, _y, _w, _h, gui->_bgcolor); // Draw border if (_flags & WIDGET_BORDER) { - OverlayColor colorA = gui->_color; - OverlayColor colorB = gui->_shadowcolor; - if ((_flags & WIDGET_INV_BORDER) == WIDGET_INV_BORDER) - SWAP(colorA, colorB); - gui->box(_x, _y, _w, _h, colorA, colorB); + gui->theme()->drawWidgetBackground(Common::Rect(_x, _y, _x+_w, _y+_h), _hints, Theme::kWidgetBackgroundBorder); _x += 4; _y += 4; _w -= 8; @@ -79,9 +75,6 @@ void Widget::draw() { _h += 8; } - // Flag the draw area as dirty - gui->addDirtyRect(_x, _y, _w, _h); - _x = oldX; _y = oldY; @@ -91,6 +84,8 @@ void Widget::draw() { w->draw(); w = w->_next; } + + clearHints(THEME_HINT_FIRST_DRAW); } Widget *Widget::findWidgetInChain(Widget *w, int x, int y) { @@ -137,8 +132,9 @@ void StaticTextWidget::setAlign(TextAlignment align) { void StaticTextWidget::drawWidget(bool hilite) { - NewGui *gui = &g_gui; - gui->drawString(_label, _x, _y, _w, isEnabled() ? gui->_textcolor : gui->_color, _align); + g_gui.theme()->drawText(Common::Rect(_x, _y, _x+_w, _y+_h), _label, + isEnabled() ? Theme::kStateEnabled : Theme::kStateDisabled, + g_gui.theme()->convertAligment(_align)); } #pragma mark - @@ -146,7 +142,7 @@ void StaticTextWidget::drawWidget(bool hilite) { ButtonWidget::ButtonWidget(GuiObject *boss, int x, int y, int w, int h, const String &label, uint32 cmd, uint8 hotkey, WidgetSize ws) : StaticTextWidget(boss, x, y, w, h, label, kTextAlignCenter, ws), CommandSender(boss), _cmd(cmd), _hotkey(hotkey) { - _flags = WIDGET_ENABLED | WIDGET_BORDER | WIDGET_CLEARBG; + _flags = WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG; _type = kButtonWidget; } @@ -156,11 +152,7 @@ void ButtonWidget::handleMouseUp(int x, int y, int button, int clickCount) { } void ButtonWidget::drawWidget(bool hilite) { - NewGui *gui = &g_gui; - const int off = (_h - g_gui.getFontHeight()) / 2; - gui->drawString(_label, _x, _y + off, _w, - !isEnabled() ? gui->_color : - hilite ? gui->_textcolorhi : gui->_textcolor, _align); + g_gui.theme()->drawButton(Common::Rect(_x, _y, _x+_w, _y+_h), _label, isEnabled() ? (hilite ? Theme::kStateHighlight : Theme::kStateEnabled) : Theme::kStateDisabled); } #pragma mark - @@ -187,39 +179,8 @@ void CheckboxWidget::setState(bool state) { } void CheckboxWidget::drawWidget(bool hilite) { - NewGui *gui = &g_gui; - int fontHeight = gui->getFontHeight(); - - // Draw the box - gui->box(_x, _y, fontHeight + 4, fontHeight + 4, gui->_color, gui->_shadowcolor); - gui->fillRect(_x + 2, _y + 2, fontHeight, fontHeight, gui->_bgcolor); - - // If checked, draw cross inside the box - if (_state) { - Graphics::Surface &surf = gui->getScreen(); - Common::Point p0, p1, p2, p3; - OverlayColor color = isEnabled() ? gui->_textcolor : gui->_color; - - p0 = Common::Point(_x + 4, _y + 4); - p1 = Common::Point(_x + fontHeight - 1, _y + 4); - p2 = Common::Point(_x + 4, _y + fontHeight - 1); - p3 = Common::Point(_x + fontHeight - 1, _y + fontHeight - 1); - - if (_ws == kBigWidgetSize) { - surf.drawLine(p0.x + 1, p0.y, p3.x, p3.y - 1, color); - surf.drawLine(p0.x, p0.y + 1, p3.x - 1, p3.y, color); - surf.drawLine(p0.x + 1, p0.y + 1, p3.x - 1, p3.y - 1, color); - surf.drawLine(p2.x + 1, p2.y - 1, p1.x - 1, p1.y + 1, color); - surf.drawLine(p2.x + 1, p2.y, p1.x, p1.y + 1, color); - surf.drawLine(p2.x, p2.y - 1, p1.x - 1, p1.y, color); - } else { - surf.drawLine(p0.x, p0.y, p3.x, p3.y, color); - surf.drawLine(p2.x, p2.y, p1.x, p1.y, color); - } - } - - // Finally draw the label - gui->drawString(_label, _x + fontHeight + 10, _y + 3, _w, isEnabled() ? gui->_textcolor : gui->_color); + g_gui.theme()->drawCheckbox(Common::Rect(_x, _y, _x+_w, _y+_h), _label, _state, + isEnabled() ? Theme::kStateEnabled : Theme::kStateDisabled); } #pragma mark - @@ -262,15 +223,8 @@ void SliderWidget::handleMouseUp(int x, int y, int button, int clickCount) { } void SliderWidget::drawWidget(bool hilite) { - NewGui *gui = &g_gui; - - // Draw the box - gui->box(_x, _y, _w, _h, gui->_color, gui->_shadowcolor); - - // Draw the 'bar' - gui->fillRect(_x + 2, _y + 2, valueToPos(_value), _h - 4, - !isEnabled() ? gui->_color : - hilite ? gui->_textcolorhi : gui->_textcolor); + g_gui.theme()->drawSlider(Common::Rect(_x, _y, _x+_w, _y+_h), valueToPos(_value), + isEnabled() ? (hilite ? Theme::kStateHighlight : Theme::kStateEnabled) : Theme::kStateDisabled); } int SliderWidget::valueToPos(int value) { @@ -305,13 +259,9 @@ void GraphicsWidget::setGfx(const Graphics::Surface *gfx) { } void GraphicsWidget::drawWidget(bool hilite) { - if (sizeof(OverlayColor) != _gfx.bytesPerPixel || !_gfx.pixels) { - // FIXME: It doesn't really make sense to render this text here, since - // this widget might be used for other things than rendering savegame - // graphics/previews... - g_gui.drawString("No preview", _x, _y + _h / 2 - g_gui.getFontHeight() / 2, _w, g_gui._textcolor, Graphics::kTextAlignCenter); - } else - g_gui.drawSurface(_gfx, _x, _y); + if (sizeof(OverlayColor) == _gfx.bytesPerPixel && _gfx.pixels) { + g_gui.theme()->drawSurface(Common::Rect(_x, _y, _x+_w, _y+_h), _gfx); + } } } // End of namespace GUI diff --git a/gui/widget.h b/gui/widget.h index 6b5e7d5c98..737b96a3ee 100644 --- a/gui/widget.h +++ b/gui/widget.h @@ -45,7 +45,6 @@ enum { WIDGET_WANT_TICKLE = 1 << 7, WIDGET_TRACK_MOUSE = 1 << 8, WIDGET_RETAIN_FOCUS = 1 << 9 // Retain focus on mouse up. By default widgets lose focus on mouseup, but some widgets might want to retain it - widgets where you enter text, for instance - }; enum { @@ -93,6 +92,7 @@ protected: Widget *_next; uint16 _id; uint16 _flags; + uint16 _hints; bool _hasFocus; public: @@ -127,6 +127,10 @@ public: void clearFlags(int flags) { _flags &= ~flags; } int getFlags() const { return _flags; } + void setHints(int hints) { _hints |= hints; } + void clearHints(int hints) { _hints &= ~hints; } + int getHints() const { return _hints; } + void setEnabled(bool e) { if (e) setFlags(WIDGET_ENABLED); else clearFlags(WIDGET_ENABLED); } bool isEnabled() const { return _flags & WIDGET_ENABLED; } bool isVisible() const { return !(_flags & WIDGET_INVISIBLE); } diff --git a/scumm/dialogs.cpp b/scumm/dialogs.cpp index 65c85c050c..cddf4eeaf6 100644 --- a/scumm/dialogs.cpp +++ b/scumm/dialogs.cpp @@ -925,20 +925,10 @@ ValueDisplayDialog::ValueDisplayDialog(const Common::String& label, int minVal, } void ValueDisplayDialog::drawDialog() { - g_gui.blendRect(_x, _y, _w, _h, g_gui._bgcolor); - g_gui.box(_x, _y, _w, _h, g_gui._color, g_gui._shadowcolor); - const int labelWidth = _w - 8 - _percentBarWidth; - - // Draw the label - g_gui.drawString(_label, _x + 4, _y + 4, labelWidth, g_gui._textcolor); - - // Draw the percentage bar - g_gui.fillRect(_x + 4 + labelWidth, _y + 4, _percentBarWidth * (_value - _min) / (_max - _min), _h - 8, g_gui._textcolorhi); - g_gui.frameRect(_x + 4 + labelWidth, _y + 4, _percentBarWidth, _h - 8, g_gui._textcolor); - - // Flag the draw area as dirty - g_gui.addDirtyRect(_x, _y, _w, _h); + g_gui.theme()->drawDialogBackground(Common::Rect(_x, _y, _x+_w, _y+_h)); + g_gui.theme()->drawText(Common::Rect(_x+4, _y+4, _x+labelWidth+4, _y+g_gui.theme()->getFontHeight()+4), _label); + g_gui.theme()->drawSlider(Common::Rect(_x+4+labelWidth, _y+4, _x+_w-4, _y+_h-4), _percentBarWidth * (_value - _min) / (_max - _min)); } void ValueDisplayDialog::handleTickle() { diff --git a/sky/debug.cpp b/sky/debug.cpp index d16a66c55a..e7ac7c66e6 100644 --- a/sky/debug.cpp +++ b/sky/debug.cpp @@ -21,6 +21,8 @@ #include "common/stdafx.h" #include "common/util.h" +#include "common/debugger.cpp" + #include "sky/debug.h" #include "sky/grid.h" #include "sky/logic.h" @@ -30,8 +32,6 @@ #include "sky/struc.h" #include "sky/compact.h" -#include "common/debugger.cpp" - namespace Sky { static const char *section_0_compacts[] = { @@ -1278,7 +1278,7 @@ void Debug::mcode(uint32 mcode, uint32 a, uint32 b, uint32 c) { Debugger::Debugger(Logic *logic, Mouse *mouse, Screen *screen, SkyCompact *skyCompact) -: _logic(logic), _mouse(mouse), _screen(screen), _skyCompact(skyCompact), _showGrid(false) { +: Common::Debugger<Debugger>(), _logic(logic), _mouse(mouse), _screen(screen), _skyCompact(skyCompact), _showGrid(false) { DCmd_Register("exit", &Debugger::Cmd_Exit); DCmd_Register("help", &Debugger::Cmd_Help); DCmd_Register("info", &Debugger::Cmd_Info); |