aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/module.mk3
-rw-r--r--common/unzip.cpp1231
-rw-r--r--common/unzip.h300
-rw-r--r--graphics/imagedec.cpp174
-rw-r--r--graphics/imagedec.h61
-rw-r--r--graphics/imageman.cpp146
-rw-r--r--graphics/imageman.h103
-rw-r--r--graphics/module.mk4
-rw-r--r--gui/EditTextWidget.cpp8
-rw-r--r--gui/ListWidget.cpp19
-rw-r--r--gui/PopUpWidget.cpp44
-rw-r--r--gui/ScrollBarWidget.cpp75
-rw-r--r--gui/TabWidget.cpp26
-rw-r--r--gui/ThemeNew.cpp801
-rw-r--r--gui/about.cpp71
-rw-r--r--gui/about.h2
-rw-r--r--gui/console.cpp54
-rw-r--r--gui/console.h1
-rw-r--r--gui/dialog.cpp20
-rw-r--r--gui/dialog.h3
-rw-r--r--gui/editable.cpp6
-rw-r--r--gui/launcher.cpp1
-rw-r--r--gui/module.mk4
-rw-r--r--gui/newgui.cpp285
-rw-r--r--gui/newgui.h62
-rw-r--r--gui/theme.cpp516
-rw-r--r--gui/theme.h361
-rw-r--r--gui/themes/default-theme.zipbin0 -> 3612 bytes
-rw-r--r--gui/widget.cpp86
-rw-r--r--gui/widget.h6
-rw-r--r--scumm/dialogs.cpp16
-rw-r--r--sky/debug.cpp6
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
new file mode 100644
index 0000000000..0f52429200
--- /dev/null
+++ b/gui/themes/default-theme.zip
Binary files differ
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);